Skip to content

Commit

Permalink
Implement creating a new directory.
Browse files Browse the repository at this point in the history
  • Loading branch information
awesomekling committed Oct 15, 2018
1 parent 5c50d02 commit f608629
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 20 deletions.
189 changes: 182 additions & 7 deletions VirtualFileSystem/Ext2FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()));
Expand All @@ -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))
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 13 additions & 4 deletions VirtualFileSystem/Ext2FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockDevice>);

const ext2_super_block& superBlock() const;
Expand All @@ -39,24 +43,29 @@ class Ext2FileSystem final : public DeviceBackedFileSystem {
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;

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 };

Expand Down
5 changes: 3 additions & 2 deletions VirtualFileSystem/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class FileSystem : public Retainable<FileSystem> {
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;
Expand All @@ -38,7 +38,8 @@ class FileSystem : public Retainable<FileSystem> {
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;
Expand Down
12 changes: 9 additions & 3 deletions VirtualFileSystem/SyntheticFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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 { };
}
5 changes: 3 additions & 2 deletions VirtualFileSystem/SyntheticFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ class SyntheticFileSystem final : public FileSystem {
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();
Expand Down
9 changes: 8 additions & 1 deletion VirtualFileSystem/VirtualFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions VirtualFileSystem/VirtualFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class VirtualFileSystem {

OwnPtr<FileHandle> open(const String& path);
OwnPtr<FileHandle> create(const String& path);
OwnPtr<FileHandle> mkdir(const String& path);

bool isRoot(InodeIdentifier) const;

Expand Down
Binary file modified VirtualFileSystem/small.fs
Binary file not shown.
Loading

0 comments on commit f608629

Please sign in to comment.