diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp index 91295037c6d333..d73d859060a356 100644 --- a/VirtualFileSystem/Ext2FileSystem.cpp +++ b/VirtualFileSystem/Ext2FileSystem.cpp @@ -63,7 +63,7 @@ const ext2_group_desc& Ext2FileSystem::blockGroupDescriptor(unsigned groupIndex) ASSERT(groupIndex <= m_blockGroupCount); if (!m_cachedBlockGroupDescriptorTable) { - unsigned blocksToRead = ceilDiv(m_blockGroupCount * sizeof(ext2_group_desc), blockSize()); + unsigned blocksToRead = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); printf("[ext2fs] block group count: %u, blocks-to-read: %u\n", m_blockGroupCount, blocksToRead); unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; printf("[ext2fs] first block of BGDT: %u\n", firstBlockOfBGDT); @@ -350,7 +350,7 @@ bool Ext2FileSystem::writeInode(InodeIdentifier inode, const ByteBuffer& data) ASSERT(!isSymbolicLink(e2inode->i_mode)); unsigned blocksNeededBefore = ceilDiv(e2inode->i_size, blockSize()); - unsigned blocksNeededAfter = ceilDiv(data.size(), blockSize()); + unsigned blocksNeededAfter = ceilDiv((unsigned)data.size(), blockSize()); // FIXME: Support growing or shrinking the block list. ASSERT(blocksNeededBefore == blocksNeededAfter); @@ -604,6 +604,38 @@ void Ext2FileSystem::traverseInodeBitmap(unsigned groupIndex, F callback) const } } +template +void Ext2FileSystem::traverseBlockBitmap(unsigned groupIndex, F callback) const +{ + ASSERT(groupIndex <= m_blockGroupCount); + auto& bgd = blockGroupDescriptor(groupIndex); + + unsigned blocksInGroup = min(blocksPerGroup(), superBlock().s_blocks_count); + unsigned blockCount = ceilDiv(blocksInGroup, 8u); + + for (unsigned i = 0; i < blockCount; ++i) { + auto block = readBlock(bgd.bg_block_bitmap + i); + ASSERT(block); + bool shouldContinue = callback(i * (blockSize() / 8) + 1, Bitmap::wrap(block.pointer(), blocksInGroup)); + if (!shouldContinue) + break; + } +} + +bool Ext2FileSystem::modifyLinkCount(InodeIndex inode, int delta) +{ + ASSERT(inode); + auto e2inode = lookupExt2Inode(inode); + if (!e2inode) + return false; + + auto newLinkCount = e2inode->i_links_count + delta; + printf("changing inode %u link count from %u to %u\n", inode, e2inode->i_links_count, newLinkCount); + e2inode->i_links_count = newLinkCount; + + return writeExt2Inode(inode, *e2inode); +} + bool Ext2FileSystem::setModificationTime(InodeIdentifier inode, dword timestamp) { ASSERT(inode.fileSystemID() == id()); @@ -637,6 +669,36 @@ bool Ext2FileSystem::isDirectoryInode(unsigned inode) const return false; } +Vector Ext2FileSystem::allocateBlocks(unsigned group, unsigned count) +{ + printf("[ext2fs] allocateBlocks(group: %u, count: %u)\n", group, count); + + auto& bgd = blockGroupDescriptor(group); + if (bgd.bg_free_blocks_count < count) { + printf("[ext2fs] allocateBlocks can't allocate out of group %u, wanted %u but only %u available\n", group, count, bgd.bg_free_blocks_count); + return { }; + } + + // FIXME: Implement a scan that finds consecutive blocks if possible. + Vector blocks; + traverseBlockBitmap(group, [&blocks, count] (unsigned firstBlockInBitmap, const Bitmap& bitmap) { + for (unsigned i = 0; i < bitmap.size(); ++i) { + if (!bitmap.get(i)) { + blocks.append(firstBlockInBitmap + i); + if (blocks.size() == count) + return false; + } + } + return true; + }); + printf("[ext2fs] allocateBlock found these blocks:\n"); + for (auto& bi : blocks) { + printf(" > %u\n", bi); + } + + return blocks; +} + unsigned Ext2FileSystem::allocateInode(unsigned preferredGroup, unsigned expectedSize) { printf("[ext2fs] allocateInode(preferredGroup: %u, expectedSize: %u)\n", preferredGroup, expectedSize); @@ -736,14 +798,97 @@ bool Ext2FileSystem::setInodeAllocationState(unsigned inode, bool newState) ++mutableBGD.bg_free_inodes_count; printf("[ext2fs] group free inode count %u -> %u\n", bgd.bg_free_inodes_count, bgd.bg_free_inodes_count - 1); - unsigned blocksToWrite = ceilDiv(m_blockGroupCount * sizeof(ext2_group_desc), blockSize()); + unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); + unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; + writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cachedBlockGroupDescriptorTable); + + return true; +} + +bool Ext2FileSystem::setBlockAllocationState(GroupIndex group, BlockIndex bi, bool newState) +{ + auto& bgd = blockGroupDescriptor(group); + + // Update block bitmap + unsigned blocksPerBitmapBlock = blockSize() * 8; + unsigned bitmapBlockIndex = (bi - 1) / blocksPerBitmapBlock; + unsigned bitIndex = (bi - 1) % blocksPerBitmapBlock; + auto block = readBlock(bgd.bg_block_bitmap + bitmapBlockIndex); + ASSERT(block); + auto bitmap = Bitmap::wrap(block.pointer(), block.size()); + bool currentState = bitmap.get(bitIndex); + printf("[ext2fs] setBlockAllocationState(%u) %u -> %u\n", block, currentState, newState); + + if (currentState == newState) + return true; + + bitmap.set(bitIndex, newState); + writeBlock(bgd.bg_block_bitmap + bitmapBlockIndex, block); + + // Update superblock + auto& sb = *reinterpret_cast(m_cachedSuperBlock.pointer()); + printf("[ext2fs] superblock free block count %u -> %u\n", sb.s_free_blocks_count, sb.s_free_blocks_count - 1); + if (newState) + --sb.s_free_blocks_count; + else + ++sb.s_free_blocks_count; + writeSuperBlock(sb); + + // Update BGD + auto& mutableBGD = const_cast(bgd); + if (newState) + --mutableBGD.bg_free_blocks_count; + else + ++mutableBGD.bg_free_blocks_count; + printf("[ext2fs] group free block count %u -> %u\n", bgd.bg_free_blocks_count, bgd.bg_free_blocks_count - 1); + + unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cachedBlockGroupDescriptorTable); return true; } -InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode) +InodeIdentifier Ext2FileSystem::makeDirectory(InodeIdentifier parentInode, const String& name, Unix::mode_t mode) +{ + ASSERT(parentInode.fileSystemID() == id()); + ASSERT(isDirectoryInode(parentInode.index())); + + // Fix up the mode to definitely be a directory. + // FIXME: This is a bit on the hackish side. + mode &= ~0170000; + mode |= 0040000; + + // NOTE: When creating a new directory, make the size 1 block. + // There's probably a better strategy here, but this works for now. + auto inode = createInode(parentInode, name, mode, blockSize()); + if (!inode.isValid()) + return { }; + + printf("[ext2fs] makeDirectory: created new directory named '%s' with inode %u\n", name.characters(), inode.index()); + + Vector entries; + entries.append({ ".", inode, EXT2_FT_DIR }); + entries.append({ "..", parentInode, EXT2_FT_DIR }); + + bool success = writeDirectoryInode(inode.index(), std::move(entries)); + ASSERT(success); + + success = modifyLinkCount(parentInode.index(), 1); + ASSERT(success); + + auto& bgd = const_cast(blockGroupDescriptor(groupIndexFromInode(inode.index()))); + ++bgd.bg_used_dirs_count; + printf("[ext2fs] incremented bg_used_dirs_count %u -> %u\n", bgd.bg_used_dirs_count - 1, bgd.bg_used_dirs_count); + + unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); + unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; + writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cachedBlockGroupDescriptorTable); + + return inode; +} + +InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const String& name, Unix::mode_t mode, unsigned size) { ASSERT(parentInode.fileSystemID() == id()); ASSERT(isDirectoryInode(parentInode.index())); @@ -754,6 +899,16 @@ InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const S // NOTE: This doesn't commit the inode allocation just yet! auto inode = allocateInode(0, 0); + if (!inode) { + printf("[ext2fs] createInode: allocateInode failed\n"); + return { }; + } + + auto blocks = allocateBlocks(groupIndexFromInode(inode), ceilDiv(size, blockSize())); + if (blocks.isEmpty()) { + printf("[ext2fs] createInode: allocateBlocks failed\n"); + return { }; + } byte fileType = 0; if (isRegularFile(mode)) @@ -782,19 +937,39 @@ InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const S success = setInodeAllocationState(inode, true); ASSERT(success); + for (auto bi : blocks) { + success = setBlockAllocationState(groupIndexFromInode(inode), bi, true); + ASSERT(success); + } + + unsigned initialLinksCount; + if (isDirectory(mode)) + initialLinksCount = 2; // (parent directory + "." entry in self) + else + initialLinksCount = 1; + auto timestamp = time(nullptr); auto e2inode = make(); memset(e2inode.ptr(), 0, sizeof(ext2_inode)); e2inode->i_mode = mode; e2inode->i_uid = 0; - e2inode->i_size = 0; + e2inode->i_size = size; e2inode->i_atime = timestamp; e2inode->i_ctime = timestamp; e2inode->i_mtime = timestamp; e2inode->i_dtime = 0; e2inode->i_gid = 0; - e2inode->i_links_count = 1; - e2inode->i_blocks = 0; + e2inode->i_links_count = initialLinksCount; + e2inode->i_blocks = blocks.size() * (blockSize() / 512); + + // FIXME: Implement writing out indirect blocks! + ASSERT(blocks.size() < EXT2_NDIR_BLOCKS); + + printf("[XXX] writing %u blocks to i_block array\n", min((unsigned)EXT2_NDIR_BLOCKS, blocks.size())); + for (unsigned i = 0; i < min((unsigned)EXT2_NDIR_BLOCKS, blocks.size()); ++i) { + e2inode->i_block[i] = blocks[i]; + } + e2inode->i_flags = 0; success = writeExt2Inode(inode, *e2inode); ASSERT(success); diff --git a/VirtualFileSystem/Ext2FileSystem.h b/VirtualFileSystem/Ext2FileSystem.h index 8a07a81cbafa1e..f7381a029f2e40 100644 --- a/VirtualFileSystem/Ext2FileSystem.h +++ b/VirtualFileSystem/Ext2FileSystem.h @@ -15,6 +15,10 @@ class Ext2FileSystem final : public DeviceBackedFileSystem { virtual ~Ext2FileSystem() override; private: + typedef unsigned BlockIndex; + typedef unsigned GroupIndex; + typedef unsigned InodeIndex; + explicit Ext2FileSystem(RetainPtr); const ext2_super_block& superBlock() const; @@ -39,11 +43,13 @@ class Ext2FileSystem final : public DeviceBackedFileSystem { virtual bool enumerateDirectoryInode(InodeIdentifier, std::function) const override; virtual InodeMetadata inodeMetadata(InodeIdentifier) const override; virtual bool setModificationTime(InodeIdentifier, dword timestamp) override; - virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override; - virtual ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, size_t count, byte* buffer) const override; + virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, Unix::mode_t, unsigned size) override; + virtual Unix::ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, Unix::size_t count, byte* buffer) const override; + virtual InodeIdentifier makeDirectory(InodeIdentifier parentInode, const String& name, Unix::mode_t) override; bool isDirectoryInode(unsigned) const; unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize); + Vector allocateBlocks(unsigned group, unsigned count); unsigned groupIndexFromInode(unsigned) const; Vector blockListForInode(const ext2_inode&) const; @@ -51,12 +57,15 @@ class Ext2FileSystem final : public DeviceBackedFileSystem { void dumpBlockBitmap(unsigned groupIndex) const; void dumpInodeBitmap(unsigned groupIndex) const; - template - void traverseInodeBitmap(unsigned groupIndex, F) const; + template void traverseInodeBitmap(unsigned groupIndex, F) const; + template void traverseBlockBitmap(unsigned groupIndex, F) const; bool addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name, byte fileType); bool writeDirectoryInode(unsigned directoryInode, Vector&&); bool setInodeAllocationState(unsigned inode, bool); + bool setBlockAllocationState(GroupIndex, BlockIndex, bool); + + bool modifyLinkCount(InodeIndex, int delta); unsigned m_blockGroupCount { 0 }; diff --git a/VirtualFileSystem/FileSystem.h b/VirtualFileSystem/FileSystem.h index 81132f0ba9e49c..cde982323c8478 100644 --- a/VirtualFileSystem/FileSystem.h +++ b/VirtualFileSystem/FileSystem.h @@ -28,7 +28,7 @@ class FileSystem : public Retainable { virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0; virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0; - virtual ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, size_t count, byte* buffer) const = 0; + virtual Unix::ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, Unix::size_t count, byte* buffer) const = 0; struct DirectoryEntry { String name; @@ -38,7 +38,8 @@ class FileSystem : public Retainable { virtual bool enumerateDirectoryInode(InodeIdentifier, std::function) const = 0; virtual bool setModificationTime(InodeIdentifier, dword timestamp) = 0; - virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) = 0; + virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, Unix::mode_t, unsigned size) = 0; + virtual InodeIdentifier makeDirectory(InodeIdentifier parentInode, const String& name, Unix::mode_t) = 0; InodeIdentifier childOfDirectoryInodeWithName(InodeIdentifier, const String& name) const; ByteBuffer readEntireInode(InodeIdentifier) const; diff --git a/VirtualFileSystem/SyntheticFileSystem.cpp b/VirtualFileSystem/SyntheticFileSystem.cpp index bc750b55463ef2..1940d2f724c35a 100644 --- a/VirtualFileSystem/SyntheticFileSystem.cpp +++ b/VirtualFileSystem/SyntheticFileSystem.cpp @@ -97,7 +97,7 @@ bool SyntheticFileSystem::setModificationTime(InodeIdentifier, dword timestamp) return false; } -InodeIdentifier SyntheticFileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode) +InodeIdentifier SyntheticFileSystem::createInode(InodeIdentifier parentInode, const String& name, Unix::mode_t mode, unsigned size) { (void) parentInode; (void) name; @@ -112,7 +112,7 @@ bool SyntheticFileSystem::writeInode(InodeIdentifier, const ByteBuffer&) return false; } -ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier inode, Unix::off_t offset, Unix::size_t count, byte* buffer) const +Unix::ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier inode, Unix::off_t offset, Unix::size_t count, byte* buffer) const { ASSERT(inode.fileSystemID() == id()); #ifdef SYNTHFS_DEBUG @@ -124,7 +124,13 @@ ssize_t SyntheticFileSystem::readInodeBytes(InodeIdentifier inode, Unix::off_t o ASSERT(buffer); auto& file = *m_files[inode.index() - 1]; - Unix::ssize_t nread = min(file.data.size() - offset, static_cast(count)); + Unix::ssize_t nread = min(static_cast(file.data.size() - offset), static_cast(count)); memcpy(buffer, file.data.pointer() + offset, nread); return nread; } + +InodeIdentifier SyntheticFileSystem::makeDirectory(InodeIdentifier parentInode, const String& name, Unix::mode_t) +{ + printf("FIXME: Implement SyntheticFileSystem::makeDirectory().\n"); + return { }; +} diff --git a/VirtualFileSystem/SyntheticFileSystem.h b/VirtualFileSystem/SyntheticFileSystem.h index c59917cc32b4ef..2b698643f9edd4 100644 --- a/VirtualFileSystem/SyntheticFileSystem.h +++ b/VirtualFileSystem/SyntheticFileSystem.h @@ -16,8 +16,9 @@ class SyntheticFileSystem final : public FileSystem { virtual bool enumerateDirectoryInode(InodeIdentifier, std::function) const override; virtual InodeMetadata inodeMetadata(InodeIdentifier) const override; virtual bool setModificationTime(InodeIdentifier, dword timestamp) override; - virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override; - virtual ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, size_t count, byte* buffer) const override; + virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, Unix::mode_t, unsigned size) override; + virtual Unix::ssize_t readInodeBytes(InodeIdentifier, Unix::off_t offset, Unix::size_t count, byte* buffer) const override; + virtual InodeIdentifier makeDirectory(InodeIdentifier parentInode, const String& name, Unix::mode_t) override; private: SyntheticFileSystem(); diff --git a/VirtualFileSystem/VirtualFileSystem.cpp b/VirtualFileSystem/VirtualFileSystem.cpp index 99397b016a494e..6495ce24061343 100644 --- a/VirtualFileSystem/VirtualFileSystem.cpp +++ b/VirtualFileSystem/VirtualFileSystem.cpp @@ -337,7 +337,14 @@ OwnPtr VirtualFileSystem::open(const String& path) OwnPtr VirtualFileSystem::create(const String& path) { // FIXME: Do the real thing, not just this fake thing! - m_rootNode->fileSystem()->createInode(m_rootNode->fileSystem()->rootInode(), "empty", 0100644); + m_rootNode->fileSystem()->createInode(m_rootNode->fileSystem()->rootInode(), "empty", 0100644, 0); + return nullptr; +} + +OwnPtr VirtualFileSystem::mkdir(const String& path) +{ + // FIXME: Do the real thing, not just this fake thing! + m_rootNode->fileSystem()->makeDirectory(m_rootNode->fileSystem()->rootInode(), "mydir", 0400755); return nullptr; } diff --git a/VirtualFileSystem/VirtualFileSystem.h b/VirtualFileSystem/VirtualFileSystem.h index 2f968b7e38f270..f4b774fc068b06 100644 --- a/VirtualFileSystem/VirtualFileSystem.h +++ b/VirtualFileSystem/VirtualFileSystem.h @@ -53,6 +53,7 @@ class VirtualFileSystem { OwnPtr open(const String& path); OwnPtr create(const String& path); + OwnPtr mkdir(const String& path); bool isRoot(InodeIdentifier) const; diff --git a/VirtualFileSystem/small.fs b/VirtualFileSystem/small.fs index 0f980f396da362..74bf4393d6d823 100644 Binary files a/VirtualFileSystem/small.fs and b/VirtualFileSystem/small.fs differ diff --git a/VirtualFileSystem/test.cpp b/VirtualFileSystem/test.cpp index 4c704d991f8369..cc0a1286f3e9b9 100644 --- a/VirtualFileSystem/test.cpp +++ b/VirtualFileSystem/test.cpp @@ -38,9 +38,13 @@ int main(int c, char** v) return 1; } -#if 1 +#if 0 auto newFile = vfs.create("/empty"); printf("vfs.create: %p\n", newFile.ptr()); +#endif +#if 1 + auto newDir = vfs.mkdir("/mydir"); + printf("vfs.mkdir: %p\n", newDir.ptr()); #endif //return 0;