// Ext2OpenDirectory.cpp // Author: Josh Holtrop // Date: 12/26/05 // Modified: 12/26/05 extern "C" { #include "display/kout.h" #include "lang/lang.h" } #include "fs/vfs.h" #include "Ext2OpenDirectory.h" #include "Ext2OpenFile.h" #include "ext2.h" Ext2OpenDirectory::Ext2OpenDirectory(Ext2fs *fs, u32_t inum, int mode) { myPosition = 0; myFS = fs; myInum = inum; myMode = mode; myCache = new Ext2BlockCache(fs, inum); fs->readInode(inum, &myInode); u32_t size = myInode.i_size; u32_t logBlockSize = fs->getSuper()->s_log_block_size; myBlockSize = 1024 << logBlockSize; if (size % myBlockSize) size += myBlockSize - (size % myBlockSize); myBlocks = size >> (10 + logBlockSize); myBuffer = new u8_t[size]; if ((mode & VFS_MODE_RW_MASK) == VFS_MODE_READ) readDirectory(); } /* destroy an open ext2 directory object */ Ext2OpenDirectory::~Ext2OpenDirectory() { delete myCache; if ((myMode & VFS_MODE_RW_MASK) == VFS_MODE_READ) delete[] myBuffer; } void Ext2OpenDirectory::readDirectory() { /* read in the entire directory */ for (u32_t i = 0; i < myBlocks; i++) myCache->readBlock(i, myBuffer + i * myBlockSize); } /* seek absolute or relative, only positive direction */ int Ext2OpenDirectory::seek(int pos, int mode) { if (mode == SEEK_ABSOLUTE) myPosition = 0; if (pos > 0) { while (myPosition < myInode.i_size) { ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition); myPosition += de->length; pos--; if (pos < 1) return 0; } return -1; } return -2; } /* read a directory entry from the directory */ int Ext2OpenDirectory::read(vfs_dir_entry_t *ent) { if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_READ) return -1; if (myPosition >= myInode.i_size) return EOF; ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition); ent->inum = de->inode; memcpy(ent->name, de->name, de->name_length); ent->name[de->name_length] = 0; myPosition += de->length; return 0; } int Ext2OpenDirectory::create(char *name, int mode, u32_t permissions, u32_t dev) { if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE) return -1; readDirectory(); myPosition = 0; u32_t name_length = strlen(name); while (myPosition < myInode.i_size) { ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition); if (name_length == de->name_length && !strncmp(name, de->name, name_length)) return -2; /* entry already exists */ myPosition += de->length; } u32_t newInode = myFS->allocInode(); if (newInode == 0) return -3; /* no free inodes! */ /* initialize the inode */ ext2_inode_t in; memset(&in, 0, sizeof(ext2_inode_t)); u32_t ext2_mode; switch(mode) { case VFS_FT_DIR: ext2_mode = EXT2_I_MODE_DIR; break; case VFS_FT_CHAR: ext2_mode = EXT2_I_MODE_CHAR; in.i_block[0] = dev; break; case VFS_FT_BLOCK: ext2_mode = EXT2_I_MODE_BLOCK; in.i_block[0] = dev; break; case VFS_FT_FIFO: ext2_mode = EXT2_I_MODE_FIFO; break; case VFS_FT_SOCK: ext2_mode = EXT2_I_MODE_SOCK; break; case VFS_FT_SYMLINK: ext2_mode = EXT2_I_MODE_SYM; break; case VFS_FT_FILE: default: ext2_mode = EXT2_I_MODE_FILE; break; } in.i_mode = ext2_mode | (permissions & EXT2_I_MODE_ATTR_MASK); in.i_links_count = 1; myFS->writeInode(newInode, &in); u32_t entryLength = name_length + 8; if (entryLength & 0x3) entryLength = (entryLength + 4) & 0xFFFFFFFC; /* multiple of 4 bytes */ myPosition = 0; for (;;) { ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition); u32_t thisEntryLength = de->name_length + 8; if (thisEntryLength & 0x3) thisEntryLength = (thisEntryLength + 4) & 0xFFFFFFFC; if (de->length - thisEntryLength >= entryLength) { /* There is room in this entry for the new one */ u32_t newLength = de->length - entryLength; de->length = entryLength; u32_t firstPosition = myPosition; myPosition += de->length; de = (ext2_dir_entry_t *) (myBuffer + myPosition); de->length = newLength; de->name_length = name_length; de->inode = newInode; memcpy(de->name, name, name_length); writeInclusiveRange(firstPosition, myPosition + entryLength - 1); return 0; } if (myPosition + de->length >= myInode.i_size) { if (myFS->getSuper()->s_free_blocks_count < 2) return -4; /* out of space on device */ /* This is the last entry, we need to add a block */ myBlocks++; u8_t *newBuffer = new u8_t[myBlockSize * myBlocks]; memcpy(newBuffer, myBuffer, myBlockSize * (myBlocks - 1)); delete[] myBuffer; myBuffer = newBuffer; de = (ext2_dir_entry_t *) (myBuffer + myPosition); u32_t thisEntryLength = de->name_length + 8; if (thisEntryLength & 0x3) thisEntryLength = (thisEntryLength + 4) & 0xFFFFFFFC; u32_t leftovers = de->length - thisEntryLength; de->length -= leftovers; u32_t firstPosition = myPosition; myPosition += thisEntryLength; de = (ext2_dir_entry_t *) (myBuffer + myPosition); de->length = myBlockSize + leftovers; de->name_length = name_length; de->inode = newInode; memcpy(de->name, name, name_length); writeInclusiveRange(firstPosition, myPosition + myBlockSize + leftovers - 1); return 0; } myPosition += de->length; } } void Ext2OpenDirectory::writeInclusiveRange(u32_t first, u32_t last) { u32_t firstBlock = first / myBlockSize; u32_t lastBlock = last / myBlockSize; for (u32_t i = firstBlock; i <= lastBlock; i++) myCache->writeBlock(i, myBuffer + i * myBlockSize); } int Ext2OpenDirectory::unlink(char *name) { if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE) return -1; readDirectory(); myPosition = 0; u32_t lastPosition = 0; u32_t name_length = strlen(name); while (myPosition < myInode.i_size) { ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition); if (name_length == de->name_length && !strncmp(name, de->name, name_length)) { ext2_inode_t inode; myFS->readInode(de->inode, &inode); if ((inode.i_mode & EXT2_I_MODE_TYPE_MASK) == EXT2_I_MODE_DIR) { if (myFS->attemptRemoveDir(de->inode)) return -3; /* can't unlink non-empty directory */ } /* found entry to unlink add the space to entry at lastPosition */ myFS->unlink(de->inode); ext2_dir_entry_t *lde = (ext2_dir_entry_t *) (myBuffer + lastPosition); lde->length += de->length; writeInclusiveRange(lastPosition, lastPosition + 8); return 0; } lastPosition = myPosition; myPosition += de->length; } return -2; /* entry not found */ }