summaryrefslogtreecommitdiff
path: root/VirtualFileSystem
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2018-10-16 00:35:03 +0200
committerAndreas Kling <awesomekling@gmail.com>2018-10-16 00:35:03 +0200
commitf6086297047b639ede6fc0e058e4efcfc09ea46f (patch)
tree8d3487d2abc1c4b3d4e7eddf9d6a515ee070595a /VirtualFileSystem
parent5c50d02c2e6369e551eb67638c7e0b36366f1cca (diff)
downloadserenity-f6086297047b639ede6fc0e058e4efcfc09ea46f.zip
Implement creating a new directory.
Diffstat (limited to 'VirtualFileSystem')
-rw-r--r--VirtualFileSystem/Ext2FileSystem.cpp189
-rw-r--r--VirtualFileSystem/Ext2FileSystem.h17
-rw-r--r--VirtualFileSystem/FileSystem.h5
-rw-r--r--VirtualFileSystem/SyntheticFileSystem.cpp12
-rw-r--r--VirtualFileSystem/SyntheticFileSystem.h5
-rw-r--r--VirtualFileSystem/VirtualFileSystem.cpp9
-rw-r--r--VirtualFileSystem/VirtualFileSystem.h1
-rw-r--r--VirtualFileSystem/small.fsbin2048000 -> 2048000 bytes
-rw-r--r--VirtualFileSystem/test.cpp6
9 files changed, 224 insertions, 20 deletions
diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp
index 91295037c6..d73d859060 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<typename F>
+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::BlockIndex> 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<BlockIndex> 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<ext2_super_block*>(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<ext2_group_desc&>(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<DirectoryEntry> 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<ext2_group_desc&>(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<ext2_inode>();
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 8a07a81cba..f7381a029f 100644
--- a/VirtualFileSystem/Ext2FileSystem.h
+++ b/VirtualFileSystem/Ext2FileSystem.h
@@ -15,6 +15,10 @@ public:
virtual ~Ext2FileSystem() override;
private:
+ typedef unsigned BlockIndex;
+ typedef unsigned GroupIndex;
+ typedef unsigned InodeIndex;
+
explicit Ext2FileSystem(RetainPtr<BlockDevice>);
const ext2_super_block& superBlock() const;
@@ -39,11 +43,13 @@ private:
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) 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<BlockIndex> allocateBlocks(unsigned group, unsigned count);
unsigned groupIndexFromInode(unsigned) const;
Vector<unsigned> blockListForInode(const ext2_inode&) const;
@@ -51,12 +57,15 @@ private:
void dumpBlockBitmap(unsigned groupIndex) const;
void dumpInodeBitmap(unsigned groupIndex) const;
- template<typename F>
- void traverseInodeBitmap(unsigned groupIndex, F) const;
+ template<typename F> void traverseInodeBitmap(unsigned groupIndex, F) const;
+ template<typename F> void traverseBlockBitmap(unsigned groupIndex, F) const;
bool addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name, byte fileType);
bool writeDirectoryInode(unsigned directoryInode, Vector<DirectoryEntry>&&);
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 81132f0ba9..cde982323c 100644
--- a/VirtualFileSystem/FileSystem.h
+++ b/VirtualFileSystem/FileSystem.h
@@ -28,7 +28,7 @@ public:
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 @@ public:
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) 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 bc750b5546..1940d2f724 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<Unix::off_t>(count));
+ Unix::ssize_t nread = min(static_cast<Unix::off_t>(file.data.size() - offset), static_cast<Unix::off_t>(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 c59917cc32..2b698643f9 100644
--- a/VirtualFileSystem/SyntheticFileSystem.h
+++ b/VirtualFileSystem/SyntheticFileSystem.h
@@ -16,8 +16,9 @@ public:
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) 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 99397b016a..6495ce2406 100644
--- a/VirtualFileSystem/VirtualFileSystem.cpp
+++ b/VirtualFileSystem/VirtualFileSystem.cpp
@@ -337,7 +337,14 @@ OwnPtr<FileHandle> VirtualFileSystem::open(const String& path)
OwnPtr<FileHandle> 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<FileHandle> 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 2f968b7e38..f4b774fc06 100644
--- a/VirtualFileSystem/VirtualFileSystem.h
+++ b/VirtualFileSystem/VirtualFileSystem.h
@@ -53,6 +53,7 @@ public:
OwnPtr<FileHandle> open(const String& path);
OwnPtr<FileHandle> create(const String& path);
+ OwnPtr<FileHandle> mkdir(const String& path);
bool isRoot(InodeIdentifier) const;
diff --git a/VirtualFileSystem/small.fs b/VirtualFileSystem/small.fs
index 0f980f396d..74bf4393d6 100644
--- a/VirtualFileSystem/small.fs
+++ b/VirtualFileSystem/small.fs
Binary files differ
diff --git a/VirtualFileSystem/test.cpp b/VirtualFileSystem/test.cpp
index 4c704d991f..cc0a1286f3 100644
--- a/VirtualFileSystem/test.cpp
+++ b/VirtualFileSystem/test.cpp
@@ -38,10 +38,14 @@ 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;
if (!strcmp(v[0], "./vcat")) {