1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 10:57:35 +00:00

Import all this stuff into a single repo called Serenity.

This commit is contained in:
Andreas Kling 2018-10-10 11:53:07 +02:00
commit 5a30055157
67 changed files with 8836 additions and 0 deletions

4
VirtualFileSystem/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
test
*.o
*.swp
AK

View file

@ -0,0 +1,9 @@
#include "BlockDevice.h"
BlockDevice::BlockDevice()
{
}
BlockDevice::~BlockDevice()
{
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <AK/Retainable.h>
#include <AK/Types.h>
class BlockDevice : public Retainable<BlockDevice> {
public:
virtual ~BlockDevice();
virtual unsigned blockSize() const = 0;
virtual bool readBlock(unsigned index, byte*) const = 0;
virtual bool writeBlock(unsigned index, const byte*) = 0;
virtual const char* className() const = 0;
virtual bool read(qword offset, unsigned length, byte*) const = 0;
virtual bool write(qword offset, unsigned length, const byte*) = 0;
protected:
BlockDevice();
};

View file

@ -0,0 +1,78 @@
#include "DeviceBackedFileSystem.h"
//#define DBFS_DEBUG
DeviceBackedFileSystem::DeviceBackedFileSystem(RetainPtr<BlockDevice>&& device)
: m_device(std::move(device))
{
ASSERT(m_device);
}
DeviceBackedFileSystem::~DeviceBackedFileSystem()
{
}
bool DeviceBackedFileSystem::writeBlock(unsigned index, const ByteBuffer& data)
{
ASSERT(data.size() == blockSize());
#ifdef DBFS_DEBUG
printf("DeviceBackedFileSystem::writeBlock %u\n", index);
#endif
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
return device().write(baseOffset, blockSize(), data.pointer());
}
bool DeviceBackedFileSystem::writeBlocks(unsigned index, unsigned count, const ByteBuffer& data)
{
#ifdef DBFS_DEBUG
printf("DeviceBackedFileSystem::writeBlocks %u x%u\n", index, count);
#endif
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
return device().write(baseOffset, count * blockSize(), data.pointer());
}
ByteBuffer DeviceBackedFileSystem::readBlock(unsigned index) const
{
#ifdef DBFS_DEBUG
printf("DeviceBackedFileSystem::readBlock %u\n", index);
#endif
auto buffer = ByteBuffer::createUninitialized(blockSize());
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
auto* bufferPointer = buffer.pointer();
device().read(baseOffset, blockSize(), bufferPointer);
ASSERT(buffer.size() == blockSize());
return buffer;
}
ByteBuffer DeviceBackedFileSystem::readBlocks(unsigned index, unsigned count) const
{
if (!count)
return nullptr;
if (count == 1)
return readBlock(index);
auto blocks = ByteBuffer::createUninitialized(count * blockSize());
byte* out = blocks.pointer();
for (unsigned i = 0; i < count; ++i) {
auto block = readBlock(index + i);
if (!block)
return nullptr;
memcpy(out, block.pointer(), block.size());
out += blockSize();
}
return blocks;
}
void DeviceBackedFileSystem::setBlockSize(unsigned blockSize)
{
if (blockSize == m_blockSize)
return;
m_blockSize = blockSize;
invalidateCaches();
}
void DeviceBackedFileSystem::invalidateCaches()
{
// FIXME: Implement block cache.
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "FileSystem.h"
#include <AK/ByteBuffer.h>
class DeviceBackedFileSystem : public FileSystem {
public:
virtual ~DeviceBackedFileSystem() override;
BlockDevice& device() { return *m_device; }
const BlockDevice& device() const { return *m_device; }
unsigned blockSize() const { return m_blockSize; }
protected:
explicit DeviceBackedFileSystem(RetainPtr<BlockDevice>&&);
void setBlockSize(unsigned);
void invalidateCaches();
ByteBuffer readBlock(unsigned index) const;
ByteBuffer readBlocks(unsigned index, unsigned count) const;
bool writeBlock(unsigned index, const ByteBuffer&);
bool writeBlocks(unsigned index, unsigned count, const ByteBuffer&);
private:
unsigned m_blockSize { 0 };
RetainPtr<BlockDevice> m_device;
};

View file

@ -0,0 +1,746 @@
#include "Ext2FileSystem.h"
#include "ext2_fs.h"
#include <AK/Bitmap.h>
#include <AK/StdLib.h>
#include <cstdio>
#include <cstring>
#include "kmalloc.h"
//#define EXT2_DEBUG
RetainPtr<Ext2FileSystem> Ext2FileSystem::create(RetainPtr<BlockDevice> device)
{
return adopt(*new Ext2FileSystem(std::move(device)));
}
Ext2FileSystem::Ext2FileSystem(RetainPtr<BlockDevice> device)
: DeviceBackedFileSystem(std::move(device))
{
}
Ext2FileSystem::~Ext2FileSystem()
{
}
ByteBuffer Ext2FileSystem::readSuperBlock() const
{
auto buffer = ByteBuffer::createUninitialized(1024);
device().readBlock(2, buffer.pointer());
device().readBlock(3, buffer.offsetPointer(512));
return buffer;
}
bool Ext2FileSystem::writeSuperBlock(const ext2_super_block& sb)
{
const byte* raw = (const byte*)&sb;
bool success;
success = device().writeBlock(2, raw);
ASSERT(success);
success = device().writeBlock(3, raw + 512);
ASSERT(success);
// FIXME: This is an ugly way to refresh the superblock cache. :-|
superBlock();
return true;
}
unsigned Ext2FileSystem::firstBlockOfGroup(unsigned groupIndex) const
{
return superBlock().s_first_data_block + (groupIndex * superBlock().s_blocks_per_group);
}
const ext2_super_block& Ext2FileSystem::superBlock() const
{
if (!m_cachedSuperBlock)
m_cachedSuperBlock = readSuperBlock();
return *reinterpret_cast<ext2_super_block*>(m_cachedSuperBlock.pointer());
}
const ext2_group_desc& Ext2FileSystem::blockGroupDescriptor(unsigned groupIndex) const
{
// FIXME: Should this fail gracefully somehow?
ASSERT(groupIndex <= m_blockGroupCount);
if (!m_cachedBlockGroupDescriptorTable) {
unsigned blocksToRead = ceilDiv(m_blockGroupCount * 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);
m_cachedBlockGroupDescriptorTable = readBlocks(firstBlockOfBGDT, blocksToRead);
}
return reinterpret_cast<ext2_group_desc*>(m_cachedBlockGroupDescriptorTable.pointer())[groupIndex - 1];
}
bool Ext2FileSystem::initialize()
{
auto& superBlock = this->superBlock();
printf("[ext2fs] super block magic: %04x (super block size: %u)\n", superBlock.s_magic, sizeof(ext2_super_block));
if (superBlock.s_magic != EXT2_SUPER_MAGIC)
return false;
printf("[ext2fs] %u inodes, %u blocks\n", superBlock.s_inodes_count, superBlock.s_blocks_count);
printf("[ext2fs] block size = %u\n", EXT2_BLOCK_SIZE(&superBlock));
printf("[ext2fs] first data block = %u\n", superBlock.s_first_data_block);
printf("[ext2fs] inodes per block = %u\n", inodesPerBlock());
printf("[ext2fs] inodes per group = %u\n", inodesPerGroup());
printf("[ext2fs] free inodes = %u\n", superBlock.s_free_inodes_count);
printf("[ext2fs] desc per block = %u\n", EXT2_DESC_PER_BLOCK(&superBlock));
printf("[ext2fs] desc size = %u\n", EXT2_DESC_SIZE(&superBlock));
setBlockSize(EXT2_BLOCK_SIZE(&superBlock));
m_blockGroupCount = ceilDiv(superBlock.s_blocks_count, superBlock.s_blocks_per_group);
if (m_blockGroupCount == 0) {
printf("[ext2fs] no block groups :(\n");
return false;
}
for (unsigned i = 1; i <= m_blockGroupCount; ++i) {
auto& group = blockGroupDescriptor(i);
printf("[ext2fs] group[%u] { block_bitmap: %u, inode_bitmap: %u, inode_table: %u }\n",
i,
group.bg_block_bitmap,
group.bg_inode_bitmap,
group.bg_inode_table);
}
return true;
}
const char* Ext2FileSystem::className() const
{
return "ext2fs";
}
InodeIdentifier Ext2FileSystem::rootInode() const
{
return { id(), EXT2_ROOT_INO };
}
#ifdef EXT2_DEBUG
static void dumpExt2Inode(const ext2_inode& inode)
{
printf("Dump of ext2_inode:\n");
printf(" i_size: %u\n", inode.i_size);
printf(" i_mode: %u\n", inode.i_mode);
printf(" i_blocks: %u\n", inode.i_blocks);
printf(" i_uid: %u\n", inode.i_uid);
printf(" i_gid: %u\n", inode.i_gid);
}
#endif
ByteBuffer Ext2FileSystem::readBlockContainingInode(unsigned inode, unsigned& blockIndex, unsigned& offset) const
{
auto& superBlock = this->superBlock();
if (inode != EXT2_ROOT_INO && inode < EXT2_FIRST_INO(&superBlock))
return { };
if (inode > superBlock.s_inodes_count)
return { };
auto& bgd = blockGroupDescriptor(groupIndexFromInode(inode));
offset = ((inode - 1) % inodesPerGroup()) * inodeSize();
blockIndex = bgd.bg_inode_table + (offset >> EXT2_BLOCK_SIZE_BITS(&superBlock));
offset &= blockSize() - 1;
return readBlock(blockIndex);
}
OwnPtr<ext2_inode> Ext2FileSystem::lookupExt2Inode(unsigned inode) const
{
unsigned blockIndex;
unsigned offset;
auto block = readBlockContainingInode(inode, blockIndex, offset);
if (!block)
return nullptr;
auto* e2inode = reinterpret_cast<ext2_inode*>(kmalloc(inodeSize()));
memcpy(e2inode, reinterpret_cast<ext2_inode*>(block.offsetPointer(offset)), inodeSize());
#ifdef EXT2_DEBUG
dumpExt2Inode(*e2inode);
#endif
return OwnPtr<ext2_inode>(e2inode);
}
InodeMetadata Ext2FileSystem::inodeMetadata(InodeIdentifier inode) const
{
ASSERT(inode.fileSystemID() == id());
auto e2inode = lookupExt2Inode(inode.index());
if (!e2inode)
return InodeMetadata();
InodeMetadata metadata;
metadata.inode = inode;
metadata.size = e2inode->i_size;
metadata.mode = e2inode->i_mode;
metadata.uid = e2inode->i_uid;
metadata.gid = e2inode->i_gid;
metadata.linkCount = e2inode->i_links_count;
metadata.atime = e2inode->i_atime;
metadata.ctime = e2inode->i_ctime;
metadata.mtime = e2inode->i_mtime;
metadata.dtime = e2inode->i_dtime;
return metadata;
}
Vector<unsigned> Ext2FileSystem::blockListForInode(const ext2_inode& e2inode) const
{
unsigned entriesPerBlock = EXT2_ADDR_PER_BLOCK(&superBlock());
// NOTE: i_blocks is number of 512-byte blocks, not number of fs-blocks.
unsigned blockCount = e2inode.i_blocks / (blockSize() / 512);
unsigned blocksRemaining = blockCount;
Vector<unsigned> list;
list.ensureCapacity(blocksRemaining);
unsigned directCount = min(blockCount, (unsigned)EXT2_NDIR_BLOCKS);
for (unsigned i = 0; i < directCount; ++i) {
list.append(e2inode.i_block[i]);
--blocksRemaining;
}
if (!blocksRemaining)
return list;
auto processBlockArray = [&] (unsigned arrayBlockIndex, std::function<void(unsigned)> callback) {
auto arrayBlock = readBlock(arrayBlockIndex);
ASSERT(arrayBlock);
auto* array = reinterpret_cast<const __u32*>(arrayBlock.pointer());
unsigned count = min(blocksRemaining, entriesPerBlock);
for (unsigned i = 0; i < count; ++i) {
if (!array[i]) {
blocksRemaining = 0;
return;
}
callback(array[i]);
--blocksRemaining;
}
};
processBlockArray(e2inode.i_block[EXT2_IND_BLOCK], [&] (unsigned entry) {
list.append(entry);
});
if (!blocksRemaining)
return list;
processBlockArray(e2inode.i_block[EXT2_DIND_BLOCK], [&] (unsigned entry) {
processBlockArray(entry, [&] (unsigned entry) {
list.append(entry);
});
});
if (!blocksRemaining)
return list;
processBlockArray(e2inode.i_block[EXT2_TIND_BLOCK], [&] (unsigned entry) {
processBlockArray(entry, [&] (unsigned entry) {
processBlockArray(entry, [&] (unsigned entry) {
list.append(entry);
});
});
});
return list;
}
ByteBuffer Ext2FileSystem::readInode(InodeIdentifier inode) const
{
ASSERT(inode.fileSystemID() == id());
auto e2inode = lookupExt2Inode(inode.index());
if (!e2inode) {
printf("[ext2fs] readInode: metadata lookup for inode %u failed\n", inode.index());
return nullptr;
}
// Symbolic links shorter than 60 characters are store inline inside the i_block array.
// This avoids wasting an entire block on short links. (Most links are short.)
static const unsigned maxInlineSymlinkLength = 60;
if (isSymbolicLink(e2inode->i_mode) && e2inode->i_size < maxInlineSymlinkLength) {
auto inlineSymlink = ByteBuffer::createUninitialized(e2inode->i_size);
memcpy(inlineSymlink.pointer(), e2inode->i_block, e2inode->i_size);
return inlineSymlink;
}
auto list = blockListForInode(*e2inode);
if (list.isEmpty()) {
printf("[ext2fs] readInode: empty block list for inode %u\n", inode.index());
return nullptr;
}
auto contents = ByteBuffer::createUninitialized(list.size() * blockSize());
auto* out = contents.pointer();
for (unsigned i = 0; i < list.size(); ++i) {
auto block = readBlock(list[i]);
if (!block) {
printf("[ext2fs] readInode: readBlock(%u) failed\n", list[i]);
return nullptr;
}
memcpy(out, block.pointer(), block.size());
out += blockSize();
}
#ifdef EXT2_DEBUG
printf("trim from %u to %u\n", contents.size(), e2inode->i_size);
#endif
contents.trim(e2inode->i_size);
return contents;
}
bool Ext2FileSystem::writeInode(InodeIdentifier inode, const ByteBuffer& data)
{
ASSERT(inode.fileSystemID() == id());
auto e2inode = lookupExt2Inode(inode.index());
if (!e2inode) {
printf("[ext2fs] writeInode: metadata lookup for inode %u failed\n", inode.index());
return false;
}
// FIXME: Support writing to symlink inodes.
ASSERT(!isSymbolicLink(e2inode->i_mode));
unsigned blocksNeededBefore = ceilDiv(e2inode->i_size, blockSize());
unsigned blocksNeededAfter = ceilDiv(data.size(), blockSize());
// FIXME: Support growing or shrinking the block list.
ASSERT(blocksNeededBefore == blocksNeededAfter);
auto list = blockListForInode(*e2inode);
if (list.isEmpty()) {
printf("[ext2fs] writeInode: empty block list for inode %u\n", inode.index());
return false;
}
for (unsigned i = 0; i < list.size(); ++i) {
auto section = data.slice(i * blockSize(), blockSize());
printf("section = %p (%u)\n", section.pointer(), section.size());
bool success = writeBlock(list[i], section);
ASSERT(success);
}
return true;
}
bool Ext2FileSystem::enumerateDirectoryInode(InodeIdentifier inode, std::function<bool(const DirectoryEntry&)> callback) const
{
ASSERT(inode.fileSystemID() == id());
ASSERT(isDirectoryInode(inode.index()));
#ifdef EXT2_DEBUG
printf("[ext2fs] Enumerating directory contents of inode %u:\n", inode.index());
#endif
auto buffer = readInode(inode);
ASSERT(buffer);
auto* entry = reinterpret_cast<ext2_dir_entry_2*>(buffer.pointer());
char namebuf[EXT2_NAME_LEN + 1];
while (entry < buffer.endPointer()) {
if (entry->inode != 0) {
memcpy(namebuf, entry->name, entry->name_len);
namebuf[entry->name_len] = 0;
#ifdef EXT2_DEBUG
printf("inode: %u, name_len: %u, rec_len: %u, name: %s\n", entry->inode, entry->name_len, entry->rec_len, namebuf);
#endif
if (!callback({ namebuf, { id(), entry->inode }, entry->file_type }))
break;
}
entry = (ext2_dir_entry_2*)((char*)entry + entry->rec_len);
}
return true;
}
bool Ext2FileSystem::addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name)
{
auto e2inodeForDirectory = lookupExt2Inode(directoryInode);
ASSERT(e2inodeForDirectory);
ASSERT(isDirectory(e2inodeForDirectory->i_mode));
//#ifdef EXT2_DEBUG
printf("[ext2fs] Adding inode %u with name '%s' to directory %u\n", inode, name.characters(), directoryInode);
//#endif
Vector<DirectoryEntry> entries;
bool nameAlreadyExists = false;
enumerateDirectoryInode({ id(), directoryInode }, [&] (const DirectoryEntry& entry) {
if (entry.name == name) {
nameAlreadyExists = true;
return false;
}
entries.append(entry);
return true;
});
if (nameAlreadyExists) {
printf("[ext2fs] Name '%s' already exists in directory inode %u\n", name.characters(), directoryInode);
return false;
}
entries.append({ name, { id(), inode } });
return writeDirectoryInode(directoryInode, std::move(entries));
}
class BufferStream {
public:
explicit BufferStream(ByteBuffer& buffer)
: m_buffer(buffer)
{
}
void operator<<(byte value)
{
m_buffer[m_offset++] = value & 0xffu;
}
void operator<<(word value)
{
m_buffer[m_offset++] = value & 0xffu;
m_buffer[m_offset++] = (byte)(value >> 8) & 0xffu;
}
void operator<<(dword value)
{
m_buffer[m_offset++] = value & 0xffu;
m_buffer[m_offset++] = (byte)(value >> 8) & 0xffu;
m_buffer[m_offset++] = (byte)(value >> 16) & 0xffu;
m_buffer[m_offset++] = (byte)(value >> 24) & 0xffu;
}
void operator<<(const String& value)
{
for (unsigned i = 0; i < value.length(); ++i)
m_buffer[m_offset++] = value[i];
}
void fillToEnd(byte ch)
{
while (m_offset < m_buffer.size())
m_buffer[m_offset++] = ch;
}
private:
ByteBuffer& m_buffer;
size_t m_offset { 0 };
};
bool Ext2FileSystem::writeDirectoryInode(unsigned directoryInode, Vector<DirectoryEntry>&& entries)
{
printf("[ext2fs] New directory inode %u contents to write:\n", directoryInode);
unsigned directorySize = 0;
for (auto& entry : entries) {
printf(" - %08u %s\n", entry.inode.index(), entry.name.characters());
directorySize += EXT2_DIR_REC_LEN(entry.name.length());
}
unsigned blocksNeeded = ceilDiv(directorySize, blockSize());
unsigned occupiedSize = blocksNeeded * blockSize();
printf("[ext2fs] directory size: %u (occupied: %u)\n", directorySize, occupiedSize);
auto directoryData = ByteBuffer::createUninitialized(occupiedSize);
BufferStream stream(directoryData);
for (unsigned i = 0; i < entries.size(); ++i) {
auto& entry = entries[i];
unsigned recordLength = EXT2_DIR_REC_LEN(entry.name.length());
if (i == entries.size() - 1)
recordLength += occupiedSize - directorySize;
printf("* inode: %u", entry.inode.index());
printf(", name_len: %u", word(entry.name.length()));
printf(", rec_len: %u", word(recordLength));
printf(", name: %s\n", entry.name.characters());
stream << dword(entry.inode.index());
stream << word(recordLength);
stream << byte(entry.name.length());
stream << byte(entry.fileType);
stream << entry.name;
unsigned padding = recordLength - entry.name.length() - 8;
printf(" *** pad %u bytes\n", padding);
for (unsigned j = 0; j < padding; ++j) {
stream << byte(0);
}
}
stream.fillToEnd(0);
#if 0
printf("data to write (%u):\n", directoryData.size());
for (unsigned i = 0; i < directoryData.size(); ++i) {
printf("%02x ", directoryData[i]);
if ((i + 1) % 8 == 0)
printf(" ");
if ((i + 1) % 16 == 0)
printf("\n");
}
printf("\n");
#endif
writeInode({ id(), directoryInode }, directoryData);
return true;
}
unsigned Ext2FileSystem::inodesPerBlock() const
{
return EXT2_INODES_PER_BLOCK(&superBlock());
}
unsigned Ext2FileSystem::inodesPerGroup() const
{
return EXT2_INODES_PER_GROUP(&superBlock());
}
unsigned Ext2FileSystem::inodeSize() const
{
return EXT2_INODE_SIZE(&superBlock());
}
unsigned Ext2FileSystem::blocksPerGroup() const
{
return EXT2_BLOCKS_PER_GROUP(&superBlock());
}
void Ext2FileSystem::dumpBlockBitmap(unsigned groupIndex) const
{
ASSERT(groupIndex <= m_blockGroupCount);
auto& bgd = blockGroupDescriptor(groupIndex);
unsigned blocksInGroup = min(blocksPerGroup(), superBlock().s_blocks_count);
unsigned blockCount = ceilDiv(blocksInGroup, 8u);
auto bitmapBlocks = readBlocks(bgd.bg_block_bitmap, blockCount);
ASSERT(bitmapBlocks);
printf("[ext2fs] group[%u] block bitmap (bitmap occupies %u blocks):\n", groupIndex, blockCount);
auto bitmap = Bitmap::wrap(bitmapBlocks.pointer(), blocksInGroup);
for (unsigned i = 0; i < blocksInGroup; ++i) {
printf("%c", bitmap.get(i) ? '1' : '0');
}
printf("\n");
}
void Ext2FileSystem::dumpInodeBitmap(unsigned groupIndex) const
{
traverseInodeBitmap(groupIndex, [] (unsigned, const Bitmap& bitmap) {
for (unsigned i = 0; i < bitmap.size(); ++i)
printf("%c", bitmap.get(i) ? '1' : '0');
return true;
});
}
template<typename F>
void Ext2FileSystem::traverseInodeBitmap(unsigned groupIndex, F callback) const
{
ASSERT(groupIndex <= m_blockGroupCount);
auto& bgd = blockGroupDescriptor(groupIndex);
unsigned inodesInGroup = min(inodesPerGroup(), superBlock().s_inodes_count);
unsigned blockCount = ceilDiv(inodesInGroup, 8u);
for (unsigned i = 0; i < blockCount; ++i) {
auto block = readBlock(bgd.bg_inode_bitmap + i);
ASSERT(block);
bool shouldContinue = callback(i * (blockSize() / 8), Bitmap::wrap(block.pointer(), inodesInGroup));
if (!shouldContinue)
break;
}
}
bool Ext2FileSystem::setModificationTime(InodeIdentifier inode, dword timestamp)
{
ASSERT(inode.fileSystemID() == id());
auto e2inode = lookupExt2Inode(inode.index());
if (!e2inode)
return false;
printf("changing inode %u mtime from %u to %u\n", inode.index(), e2inode->i_mtime, timestamp);
e2inode->i_mtime = timestamp;
return writeExt2Inode(inode.index(), *e2inode);
}
bool Ext2FileSystem::writeExt2Inode(unsigned inode, const ext2_inode& e2inode)
{
unsigned blockIndex;
unsigned offset;
auto block = readBlockContainingInode(inode, blockIndex, offset);
if (!block)
return false;
memcpy(reinterpret_cast<ext2_inode*>(block.offsetPointer(offset)), &e2inode, inodeSize());
writeBlock(blockIndex, block);
return true;
}
bool Ext2FileSystem::isDirectoryInode(unsigned inode) const
{
if (auto e2inode = lookupExt2Inode(inode))
return isDirectory(e2inode->i_mode);
return false;
}
unsigned Ext2FileSystem::allocateInode(unsigned preferredGroup, unsigned expectedSize)
{
printf("[ext2fs] allocateInode(preferredGroup: %u, expectedSize: %u)\n", preferredGroup, expectedSize);
unsigned neededBlocks = ceilDiv(expectedSize, blockSize());
printf("[ext2fs] minimum needed blocks: %u\n", neededBlocks);
unsigned groupIndex = 0;
auto isSuitableGroup = [this, neededBlocks] (unsigned groupIndex) {
auto& bgd = blockGroupDescriptor(groupIndex);
return bgd.bg_free_inodes_count && bgd.bg_free_blocks_count >= neededBlocks;
};
if (preferredGroup && isSuitableGroup(preferredGroup)) {
groupIndex = preferredGroup;
} else {
for (unsigned i = 1; i <= m_blockGroupCount; ++i) {
if (isSuitableGroup(i))
groupIndex = i;
}
}
if (!groupIndex) {
printf("[ext2fs] allocateInode: no suitable group found for new inode with %u blocks needed :(\n", neededBlocks);
return 0;
}
printf("[ext2fs] allocateInode: found suitable group [%u] for new inode with %u blocks needed :^)\n", groupIndex, neededBlocks);
unsigned firstFreeInodeInGroup = 0;
traverseInodeBitmap(groupIndex, [&firstFreeInodeInGroup] (unsigned firstInodeInBitmap, const Bitmap& bitmap) {
for (unsigned i = 0; i < bitmap.size(); ++i) {
if (!bitmap.get(i)) {
firstFreeInodeInGroup = firstInodeInBitmap + i;
return false;
}
}
return true;
});
if (!firstFreeInodeInGroup) {
printf("[ext2fs] firstFreeInodeInGroup returned no inode, despite bgd claiming there are inodes :(\n");
return 0;
}
unsigned inode = firstFreeInodeInGroup;
printf("[ext2fs] found suitable inode %u\n", inode);
// FIXME: allocate blocks if needed!
return inode;
}
unsigned Ext2FileSystem::groupIndexFromInode(unsigned inode) const
{
if (!inode)
return 0;
return (inode - 1) / inodesPerGroup() + 1;
}
bool Ext2FileSystem::setInodeAllocationState(unsigned inode, bool newState)
{
auto& bgd = blockGroupDescriptor(groupIndexFromInode(inode));
// Update inode bitmap
unsigned inodesPerBitmapBlock = blockSize() * 8;
unsigned bitmapBlockIndex = (inode - 1) / inodesPerBitmapBlock;
unsigned bitIndex = (inode - 1) % inodesPerBitmapBlock;
auto block = readBlock(bgd.bg_inode_bitmap + bitmapBlockIndex);
ASSERT(block);
auto bitmap = Bitmap::wrap(block.pointer(), block.size());
bool currentState = bitmap.get(bitIndex);
printf("[ext2fs] setInodeAllocationState(%u) %u -> %u\n", inode, currentState, newState);
if (currentState == newState)
return true;
bitmap.set(bitIndex, newState);
writeBlock(bgd.bg_inode_bitmap + bitmapBlockIndex, block);
// Update superblock
auto& sb = *reinterpret_cast<ext2_super_block*>(m_cachedSuperBlock.pointer());
printf("[ext2fs] superblock free inode count %u -> %u\n", sb.s_free_inodes_count, sb.s_free_inodes_count - 1);
if (newState)
--sb.s_free_inodes_count;
else
++sb.s_free_inodes_count;
writeSuperBlock(sb);
// Update BGD
auto& mutableBGD = const_cast<ext2_group_desc&>(bgd);
if (newState)
--mutableBGD.bg_free_inodes_count;
else
++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 firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1;
writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cachedBlockGroupDescriptorTable);
return true;
}
InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode)
{
ASSERT(parentInode.fileSystemID() == id());
ASSERT(isDirectoryInode(parentInode.index()));
//#ifdef EXT2_DEBUG
printf("[ext2fs] Adding inode '%s' (mode %o) to parent directory %u:\n", name.characters(), mode, parentInode.index());
//#endif
// NOTE: This doesn't commit the inode allocation just yet!
auto inode = allocateInode(0, 0);
// Try adding it to the directory first, in case the name is already in use.
bool success = addInodeToDirectory(parentInode.index(), inode, name);
if (!success) {
printf("[ext2fs] failed to add inode to directory :(\n");
return { };
}
// Looks like we're good, time to update the inode bitmap and group+global inode counters.
success = setInodeAllocationState(inode, true);
ASSERT(success);
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_atime = timestamp;
e2inode->i_ctime = timestamp;
e2inode->i_mtime = timestamp;
e2inode->i_dtime = 0;
e2inode->i_gid = 0;
e2inode->i_links_count = 2;
e2inode->i_blocks = 0;
e2inode->i_flags = 0;
success = writeExt2Inode(inode, *e2inode);
ASSERT(success);
return { id(), inode };
}

View file

@ -0,0 +1,65 @@
#pragma once
#include "DeviceBackedFileSystem.h"
#include <AK/Buffer.h>
#include <AK/OwnPtr.h>
struct ext2_group_desc;
struct ext2_inode;
struct ext2_super_block;
class Ext2FileSystem final : public DeviceBackedFileSystem {
public:
static RetainPtr<Ext2FileSystem> create(RetainPtr<BlockDevice>);
virtual ~Ext2FileSystem() override;
private:
explicit Ext2FileSystem(RetainPtr<BlockDevice>);
const ext2_super_block& superBlock() const;
const ext2_group_desc& blockGroupDescriptor(unsigned groupIndex) const;
unsigned firstBlockOfGroup(unsigned groupIndex) const;
unsigned inodesPerBlock() const;
unsigned inodesPerGroup() const;
unsigned blocksPerGroup() const;
unsigned inodeSize() const;
OwnPtr<ext2_inode> lookupExt2Inode(unsigned) const;
bool writeExt2Inode(unsigned, const ext2_inode&);
ByteBuffer readBlockContainingInode(unsigned inode, unsigned& blockIndex, unsigned& offset) const;
ByteBuffer readSuperBlock() const;
bool writeSuperBlock(const ext2_super_block&);
virtual bool initialize() override;
virtual const char* className() const override;
virtual InodeIdentifier rootInode() const override;
virtual ByteBuffer readInode(InodeIdentifier) const override;
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) override;
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;
bool isDirectoryInode(unsigned) const;
unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize);
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;
bool addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name);
bool writeDirectoryInode(unsigned directoryInode, Vector<DirectoryEntry>&&);
bool setInodeAllocationState(unsigned inode, bool);
unsigned m_blockGroupCount { 0 };
mutable ByteBuffer m_cachedSuperBlock;
mutable ByteBuffer m_cachedBlockGroupDescriptorTable;
};

View file

@ -0,0 +1,82 @@
#define _FILE_OFFSET_BITS 64
#include "FileBackedBlockDevice.h"
#include <cstring>
#include <sys/stat.h>
//#define FBBD_DEBUG
#define IGNORE_FILE_LENGTH // Useful for e.g /dev/hda2
RetainPtr<FileBackedBlockDevice> FileBackedBlockDevice::create(String&& imagePath, unsigned blockSize)
{
return adopt(*new FileBackedBlockDevice(std::move(imagePath), blockSize));
}
FileBackedBlockDevice::FileBackedBlockDevice(String&& imagePath, unsigned blockSize)
: m_imagePath(std::move(imagePath))
, m_blockSize(blockSize)
{
struct stat st;
int result = stat(m_imagePath.characters(), &st);
ASSERT(result != -1);
m_fileLength = st.st_size;
m_file = fopen(m_imagePath.characters(), "r+");
}
FileBackedBlockDevice::~FileBackedBlockDevice()
{
}
unsigned FileBackedBlockDevice::blockSize() const
{
return m_blockSize;
}
bool FileBackedBlockDevice::readBlock(unsigned index, byte* out) const
{
qword offset = index * m_blockSize;
return read(offset, blockSize(), out);
}
bool FileBackedBlockDevice::writeBlock(unsigned index, const byte* data)
{
qword offset = index * m_blockSize;
return write(offset, blockSize(), data);
}
bool FileBackedBlockDevice::read(qword offset, unsigned length, byte* out) const
{
#ifndef IGNORE_FILE_LENGTH
if (offset + length >= m_fileLength)
return false;
#endif
#ifdef FBBD_DEBUG
printf("[FileBackedBlockDevice] Read device @ offset %llx, length %u\n", offset, length);
#endif
fseeko(m_file, offset, SEEK_SET);
unsigned nread = fread(out, sizeof(byte), length, m_file);
ASSERT(nread == length);
return true;
}
bool FileBackedBlockDevice::write(qword offset, unsigned length, const byte* data)
{
#ifndef IGNORE_FILE_LENGTH
if (offset + length >= m_fileLength)
return false;
#endif
#ifdef FBBD_DEBUG
printf("[FileBackedBlockDevice] Write device @ offset %llx, length %u\n", offset, length);
#endif
fseeko(m_file, offset, SEEK_SET);
// size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
unsigned nwritten = fwrite(data, sizeof(byte), length, m_file);
ASSERT(nwritten == length);
return true;
}
const char* FileBackedBlockDevice::className() const
{
return "FileBackedBlockDevice";
}

View file

@ -0,0 +1,32 @@
#pragma once
#include "BlockDevice.h"
#include <AK/RetainPtr.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <stdio.h>
class FileBackedBlockDevice final : public BlockDevice {
public:
static RetainPtr<FileBackedBlockDevice> create(String&& imagePath, unsigned blockSize);
virtual ~FileBackedBlockDevice() override;
bool isValid() const { return m_file; }
virtual unsigned blockSize() const override;
virtual bool readBlock(unsigned index, byte* out) const override;
virtual bool writeBlock(unsigned index, const byte*) override;
virtual bool read(qword offset, unsigned length, byte* out) const override;
virtual bool write(qword offset, unsigned length, const byte* data) override;
private:
virtual const char* className() const override;
FileBackedBlockDevice(String&& imagePath, unsigned blockSize);
String m_imagePath;
FILE* m_file { nullptr };
qword m_fileLength { 0 };
unsigned m_blockSize { 0 };
};

View file

@ -0,0 +1,17 @@
#include "FileHandle.h"
#include "FileSystem.h"
FileHandle::FileHandle(RetainPtr<VirtualFileSystem::Node>&& vnode)
: m_vnode(std::move(vnode))
{
}
FileHandle::~FileHandle()
{
}
ByteBuffer FileHandle::read() const
{
return m_vnode->fileSystem()->readInode(m_vnode->inode);
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "VirtualFileSystem.h"
#include <AK/ByteBuffer.h>
class FileHandle {
public:
explicit FileHandle(RetainPtr<VirtualFileSystem::Node>&&);
~FileHandle();
ByteBuffer read() const;
private:
friend class VirtualFileSystem;
RetainPtr<VirtualFileSystem::Node> m_vnode;
};

View file

@ -0,0 +1,45 @@
#include <AK/Assertions.h>
#include <AK/HashMap.h>
#include "FileSystem.h"
static dword s_lastFileSystemID = 0;
static HashMap<dword, FileSystem*>& fileSystems()
{
static auto* map = new HashMap<dword, FileSystem*>();
return *map;
}
FileSystem::FileSystem()
: m_id(++s_lastFileSystemID)
{
fileSystems().set(m_id, this);
}
FileSystem::~FileSystem()
{
// FIXME: Needs HashMap::remove()!
//fileSystems().remove(m_id);
}
FileSystem* FileSystem::fromID(dword id)
{
auto it = fileSystems().find(id);
if (it != fileSystems().end())
return (*it).value;
return nullptr;
}
InodeIdentifier FileSystem::childOfDirectoryInodeWithName(InodeIdentifier inode, const String& name)
{
InodeIdentifier foundInode;
enumerateDirectoryInode(inode, [&] (const DirectoryEntry& entry) {
if (entry.name == name) {
foundInode = entry.inode;
return false;
}
return true;
});
return foundInode;
}

View file

@ -0,0 +1,81 @@
#pragma once
#include "BlockDevice.h"
#include "InodeIdentifier.h"
#include "InodeMetadata.h"
#include <AK/ByteBuffer.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
#include <AK/String.h>
#include <functional>
static const dword mepoch = 476763780;
class FileSystem : public Retainable<FileSystem> {
public:
virtual ~FileSystem();
dword id() const { return m_id; }
static FileSystem* fromID(dword);
virtual bool initialize() = 0;
virtual const char* className() const = 0;
virtual InodeIdentifier rootInode() const = 0;
virtual ByteBuffer readInode(InodeIdentifier) const = 0;
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0;
virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0;
struct DirectoryEntry {
String name;
InodeIdentifier inode;
byte fileType { 0 };
};
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;
InodeIdentifier childOfDirectoryInodeWithName(InodeIdentifier, const String& name);
protected:
FileSystem();
private:
dword m_id { 0 };
};
inline FileSystem* InodeIdentifier::fileSystem()
{
return FileSystem::fromID(m_fileSystemID);
}
inline const FileSystem* InodeIdentifier::fileSystem() const
{
return FileSystem::fromID(m_fileSystemID);
}
inline InodeMetadata InodeIdentifier::metadata() const
{
if (!isValid())
return InodeMetadata();
return fileSystem()->inodeMetadata(*this);
}
inline bool InodeIdentifier::isRootInode() const
{
return (*this) == fileSystem()->rootInode();
}
namespace AK {
template<>
struct Traits<InodeIdentifier> {
// FIXME: This is a shitty hash.
static unsigned hash(const InodeIdentifier& inode) { return Traits<unsigned>::hash(inode.fileSystemID()) + Traits<unsigned>::hash(inode.index()); }
static void dump(const InodeIdentifier& inode) { printf("%02u:%08u", inode.fileSystemID(), inode.index()); }
};
}

View file

@ -0,0 +1,9 @@
#include "InodeIdentifier.h"
#include "FileSystem.h"
ByteBuffer InodeIdentifier::readEntireFile() const
{
if (!fileSystem())
return { };
return fileSystem()->readInode(*this);
}

View file

@ -0,0 +1,40 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Types.h>
class FileSystem;
struct InodeMetadata;
class InodeIdentifier {
public:
InodeIdentifier() { }
InodeIdentifier(dword fileSystemID, dword inode)
: m_fileSystemID(fileSystemID)
, m_index(inode)
{
}
bool isValid() const { return m_fileSystemID != 0 && m_index != 0; }
dword fileSystemID() const { return m_fileSystemID; }
dword index() const { return m_index; }
FileSystem* fileSystem();
const FileSystem* fileSystem() const;
bool operator==(const InodeIdentifier& other) const
{
return m_fileSystemID == other.m_fileSystemID && m_index == other.m_index;
}
InodeMetadata metadata() const;
bool isRootInode() const;
ByteBuffer readEntireFile() const;
private:
dword m_fileSystemID { 0 };
dword m_index { 0 };
};

View file

@ -0,0 +1,42 @@
#pragma once
#include "InodeIdentifier.h"
inline bool isDirectory(word mode) { return (mode & 0170000) == 0040000; }
inline bool isCharacterDevice(word mode) { return (mode & 0170000) == 0020000; }
inline bool isBlockDevice(word mode) { return (mode & 0170000) == 0060000; }
inline bool isRegularFile(word mode) { return (mode & 0170000) == 0100000; }
inline bool isFIFO(word mode) { return (mode & 0170000) == 0010000; }
inline bool isSymbolicLink(word mode) { return (mode & 0170000) == 0120000; }
inline bool isSocket(word mode) { return (mode & 0170000) == 0140000; }
inline bool isSticky(word mode) { return mode & 01000; }
inline bool isSetUID(word mode) { return mode & 04000; }
inline bool isSetGID(word mode) { return mode & 02000; }
struct InodeMetadata {
bool isValid() const { return inode.isValid(); }
bool isDirectory() const { return ::isDirectory(mode); }
bool isCharacterDevice() const { return ::isCharacterDevice(mode); }
bool isBlockDevice() const { return ::isBlockDevice(mode); }
bool isRegularFile() const { return ::isRegularFile(mode); }
bool isFIFO() const { return ::isFIFO(mode); }
bool isSymbolicLink() const { return ::isSymbolicLink(mode); }
bool isSocket() const { return ::isSocket(mode); }
bool isSticky() const { return ::isSticky(mode); }
bool isSetUID() const { return ::isSetUID(mode); }
bool isSetGID() const { return ::isSetGID(mode); }
InodeIdentifier inode;
dword size { 0 };
word mode { 0 };
dword uid { 0 };
dword gid { 0 };
dword linkCount { 0 };
time_t atime { 0 };
time_t ctime { 0 };
time_t mtime { 0 };
time_t dtime { 0 };
};

View file

@ -0,0 +1,39 @@
PROGRAM = test
AK_OBJS = \
../AK/String.o \
../AK/StringImpl.o \
../AK/MappedFile.o \
../AK/TemporaryFile.o \
../AK/SimpleMalloc.o \
../AK/kmalloc.o
VFS_OBJS = \
BlockDevice.o \
FileBackedBlockDevice.o \
FileSystem.o \
Ext2FileSystem.o \
VirtualFileSystem.o \
FileHandle.o \
DeviceBackedFileSystem.o \
SyntheticFileSystem.o \
InodeIdentifier.o \
test.o
OBJS = $(AK_OBJS) $(VFS_OBJS)
CXXFLAGS = -std=c++17 -Os -W -Wall -Wextra -Wconversion -I. -I.. -ggdb3 -Wno-class-memaccess
#test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h
all: $(PROGRAM)
.cpp.o:
$(CXX) $(CXXFLAGS) -o $@ -c $<
clean:
rm -f $(OBJS) $(PROGRAM)
$(PROGRAM): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS)

View file

@ -0,0 +1,123 @@
#include "SyntheticFileSystem.h"
RetainPtr<SyntheticFileSystem> SyntheticFileSystem::create()
{
return adopt(*new SyntheticFileSystem);
}
SyntheticFileSystem::SyntheticFileSystem()
{
}
SyntheticFileSystem::~SyntheticFileSystem()
{
}
bool SyntheticFileSystem::initialize()
{
// Add a File for the root directory.
// FIXME: This needs work.
auto rootDir = make<File>();
rootDir->metadata.inode = { id(), 1 };
rootDir->metadata.mode = 0040555;
rootDir->metadata.uid = 0;
rootDir->metadata.gid = 0;
rootDir->metadata.size = 0;
rootDir->metadata.mtime = mepoch;
m_files.append(std::move(rootDir));
addFile(createTextFile("file", "I'm a synthetic file!\n"));
addFile(createTextFile("message", "Hey! This isn't my bottle!\n"));
return true;
}
auto SyntheticFileSystem::createTextFile(String&& name, String&& text) -> OwnPtr<File>
{
auto file = make<File>();
file->data = text.toByteBuffer();
file->name = std::move(name);
file->metadata.size = file->data.size();
file->metadata.uid = 100;
file->metadata.gid = 200;
file->metadata.mode = 0100644;
file->metadata.mtime = mepoch;
return file;
}
void SyntheticFileSystem::addFile(OwnPtr<File>&& file)
{
ASSERT(file);
file->metadata.inode = { id(), m_files.size() + 1 };
m_files.append(std::move(file));
}
const char* SyntheticFileSystem::className() const
{
return "synthfs";
}
InodeIdentifier SyntheticFileSystem::rootInode() const
{
return { id(), 1 };
}
ByteBuffer SyntheticFileSystem::readInode(InodeIdentifier inode) const
{
ASSERT(inode.fileSystemID() == id());
#ifdef SYNTHFS_DEBUG
printf("[synthfs] readInode %u\n", inode.index());
#endif
ASSERT(inode.index() != 1);
ASSERT(inode.index() <= m_files.size());
return m_files[inode.index() - 1]->data;
}
bool SyntheticFileSystem::enumerateDirectoryInode(InodeIdentifier inode, std::function<bool(const DirectoryEntry&)> callback) const
{
ASSERT(inode.fileSystemID() == id());
#ifdef SYNTHFS_DEBUG
printf("[synthfs] enumerateDirectoryInode %u\n", inode.index());
#endif
if (inode.index() != 1)
return false;
callback({ ".", m_files[0]->metadata.inode });
callback({ "..", m_files[0]->metadata.inode });
for (unsigned i = 1; i < m_files.size(); ++i)
callback({ m_files[i]->name, m_files[i]->metadata.inode });
return true;
}
InodeMetadata SyntheticFileSystem::inodeMetadata(InodeIdentifier inode) const
{
ASSERT(inode.fileSystemID() == id());
#ifdef SYNTHFS_DEBUG
printf("[synthfs] inodeMetadata(%u)\n", inode.index);
#endif
if (inode.index() == 0 || inode.index() > m_files.size())
return InodeMetadata();
return m_files[inode.index() - 1]->metadata;
}
bool SyntheticFileSystem::setModificationTime(InodeIdentifier, dword timestamp)
{
(void) timestamp;
printf("FIXME: Implement SyntheticFileSystem::setModificationTime().\n");
return false;
}
InodeIdentifier SyntheticFileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode)
{
(void) parentInode;
(void) name;
(void) mode;
printf("FIXME: Implement SyntheticFileSystem::createDirectoryInode().\n");
return { };
}
bool SyntheticFileSystem::writeInode(InodeIdentifier, const ByteBuffer&)
{
printf("FIXME: Implement SyntheticFileSystem::writeInode().\n");
return false;
}

View file

@ -0,0 +1,35 @@
#pragma once
#include "FileSystem.h"
#include <AK/HashMap.h>
class SyntheticFileSystem final : public FileSystem {
public:
virtual ~SyntheticFileSystem() override;
static RetainPtr<SyntheticFileSystem> create();
virtual bool initialize() override;
virtual const char* className() const override;
virtual InodeIdentifier rootInode() const override;
virtual ByteBuffer readInode(InodeIdentifier) const override;
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) override;
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;
private:
SyntheticFileSystem();
struct File {
String name;
InodeMetadata metadata;
ByteBuffer data;
};
OwnPtr<File> createTextFile(String&& name, String&& text);
void addFile(OwnPtr<File>&&);
Vector<OwnPtr<File>> m_files;
};

View file

@ -0,0 +1,408 @@
#include "VirtualFileSystem.h"
#include "FileHandle.h"
#include "FileSystem.h"
#include <cstdio>
#include <cstdlib>
#include "kmalloc.h"
//#define VFS_DEBUG
VirtualFileSystem::VirtualFileSystem()
{
m_maxNodeCount = 16;
m_nodes = reinterpret_cast<Node*>(kmalloc(sizeof(Node) * maxNodeCount()));
memset(m_nodes, 0, sizeof(Node) * maxNodeCount());
for (unsigned i = 0; i < m_maxNodeCount; ++i)
m_nodeFreeList.append(&m_nodes[i]);
}
VirtualFileSystem::~VirtualFileSystem()
{
printf("[VFS] ~VirtualFileSystem with %u nodes allocated\n", allocatedNodeCount());
}
auto VirtualFileSystem::makeNode(InodeIdentifier inode) -> RetainPtr<Node>
{
auto metadata = inode.metadata();
if (!metadata.isValid())
return nullptr;
auto vnode = allocateNode();
ASSERT(vnode);
FileSystem* fileSystem = inode.fileSystem();
fileSystem->retain();
vnode->inode = inode;
#ifdef VFS_DEBUG
printf("makeNode: inode=%u, size=%u, mode=%o, uid=%u, gid=%u\n", inode.index(), metadata.size, metadata.mode, metadata.uid, metadata.gid);
#endif
m_inode2vnode.set(inode, vnode.ptr());
return vnode;
}
auto VirtualFileSystem::getOrCreateNode(InodeIdentifier inode) -> RetainPtr<Node>
{
auto it = m_inode2vnode.find(inode);
if (it != m_inode2vnode.end())
return (*it).value;
return makeNode(inode);
}
bool VirtualFileSystem::mount(RetainPtr<FileSystem>&& fileSystem, const String& path)
{
ASSERT(fileSystem);
auto inode = resolvePath(path);
if (!inode.isValid()) {
printf("[VFS] mount can't resolve mount point '%s'\n", path.characters());
return false;
}
printf("mounting %s{%p} at %s (inode: %u)\n", fileSystem->className(), fileSystem.ptr(), path.characters(), inode.index());
// FIXME: check that this is not already a mount point
auto mount = make<Mount>(inode, std::move(fileSystem));
m_mounts.append(std::move(mount));
return true;
}
bool VirtualFileSystem::mountRoot(RetainPtr<FileSystem>&& fileSystem)
{
if (m_rootNode) {
printf("[VFS] mountRoot can't mount another root\n");
return false;
}
auto mount = make<Mount>(InodeIdentifier(), std::move(fileSystem));
auto node = makeNode(mount->guest());
if (!node->inUse()) {
printf("[VFS] root inode for / is not in use :(\n");
return false;
}
if (!node->inode.metadata().isDirectory()) {
printf("[VFS] root inode for / is not in use :(\n");
return false;
}
m_rootNode = std::move(node);
printf("[VFS] mountRoot mounted %s{%p}\n",
m_rootNode->fileSystem()->className(),
m_rootNode->fileSystem());
m_mounts.append(std::move(mount));
return true;
}
auto VirtualFileSystem::allocateNode() -> RetainPtr<Node>
{
if (m_nodeFreeList.isEmpty()) {
printf("[VFS] allocateNode has no nodes left\n");
return nullptr;
}
auto* node = m_nodeFreeList.takeLast();
ASSERT(node->retainCount == 0);
node->retainCount = 1;
node->vfs = this;
return adopt(*node);
}
void VirtualFileSystem::freeNode(Node* node)
{
ASSERT(node);
ASSERT(node->inUse());
node->inode.fileSystem()->release();
node->inode = InodeIdentifier();
m_nodeFreeList.append(std::move(node));
// FIXME: Need HashMap::remove.
//m_inode2vnode.remove(node);
}
bool VirtualFileSystem::isDirectory(const String& path)
{
auto inode = resolvePath(path);
if (!inode.isValid())
return false;
return inode.metadata().isDirectory();
}
auto VirtualFileSystem::findMountForHost(InodeIdentifier inode) -> Mount*
{
for (auto& mount : m_mounts) {
if (mount->host() == inode)
return mount.ptr();
}
return nullptr;
}
auto VirtualFileSystem::findMountForGuest(InodeIdentifier inode) -> Mount*
{
for (auto& mount : m_mounts) {
if (mount->guest() == inode)
return mount.ptr();
}
return nullptr;
}
bool VirtualFileSystem::isRoot(InodeIdentifier inode) const
{
return inode == m_rootNode->inode;
}
template<typename F>
void VirtualFileSystem::enumerateDirectoryInode(InodeIdentifier directoryInode, F func)
{
if (!directoryInode.isValid())
return;
directoryInode.fileSystem()->enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
InodeIdentifier resolvedInode;
if (auto mount = findMountForHost(entry.inode))
resolvedInode = mount->guest();
else
resolvedInode = entry.inode;
if (directoryInode.isRootInode() && !isRoot(directoryInode) && entry.name == "..") {
auto mount = findMountForGuest(entry.inode);
ASSERT(mount);
resolvedInode = mount->host();
}
func({ entry.name, resolvedInode });
return true;
});
}
void VirtualFileSystem::listDirectory(const String& path)
{
auto directoryInode = resolvePath(path);
if (!directoryInode.isValid())
return;
printf("[VFS] ls %s -> %s %02u:%08u\n", path.characters(), directoryInode.fileSystem()->className(), directoryInode.fileSystemID(), directoryInode.index());
enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
const char* nameColorBegin = "";
const char* nameColorEnd = "";
auto metadata = entry.inode.metadata();
ASSERT(metadata.isValid());
if (metadata.isDirectory()) {
nameColorBegin = "\033[34;1m";
nameColorEnd = "\033[0m";
} else if (metadata.isSymbolicLink()) {
nameColorBegin = "\033[36;1m";
nameColorEnd = "\033[0m";
}
if (metadata.isSticky()) {
nameColorBegin = "\033[42;30m";
nameColorEnd = "\033[0m";
}
printf("%02u:%08u ",
metadata.inode.fileSystemID(),
metadata.inode.index());
if (metadata.isDirectory())
printf("d");
else if (metadata.isSymbolicLink())
printf("l");
else if (metadata.isBlockDevice())
printf("b");
else if (metadata.isCharacterDevice())
printf("c");
else if (metadata.isSocket())
printf("s");
else if (metadata.isFIFO())
printf("f");
else if (metadata.isRegularFile())
printf("-");
else
printf("?");
printf("%c%c%c%c%c%c%c%c",
metadata.mode & 00400 ? 'r' : '-',
metadata.mode & 00200 ? 'w' : '-',
metadata.mode & 00100 ? 'x' : '-',
metadata.mode & 00040 ? 'r' : '-',
metadata.mode & 00020 ? 'w' : '-',
metadata.mode & 00010 ? 'x' : '-',
metadata.mode & 00004 ? 'r' : '-',
metadata.mode & 00002 ? 'w' : '-'
);
if (metadata.isSticky())
printf("t");
else
printf("%c", metadata.mode & 00001 ? 'x' : '-');
printf("%12u ", metadata.size);
printf("\033[30;1m");
auto tm = *localtime(&metadata.mtime);
printf("%04u-%02u-%02u %02u:%02u:%02u ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec);
printf("\033[0m");
printf("%s%s%s",
nameColorBegin,
entry.name.characters(),
nameColorEnd);
if (metadata.isDirectory()) {
printf("/");
} else if (metadata.isSymbolicLink()) {
auto symlinkContents = directoryInode.fileSystem()->readInode(metadata.inode);
printf(" -> %s", String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
}
printf("\n");
return true;
});
}
void VirtualFileSystem::listDirectoryRecursively(const String& path)
{
auto directory = resolvePath(path);
if (!directory.isValid())
return;
printf("%s\n", path.characters());
enumerateDirectoryInode(directory, [&] (const FileSystem::DirectoryEntry& entry) {
auto metadata = entry.inode.metadata();
if (metadata.isDirectory()) {
if (entry.name != "." && entry.name != "..") {
char buf[4096];
sprintf(buf, "%s/%s", path.characters(), entry.name.characters());
listDirectoryRecursively(buf);
}
} else {
printf("%s/%s\n", path.characters(), entry.name.characters());
}
});
}
bool VirtualFileSystem::touch(const String& path)
{
auto inode = resolvePath(path);
if (!inode.isValid())
return false;
return inode.fileSystem()->setModificationTime(inode, time(nullptr));
}
OwnPtr<FileHandle> VirtualFileSystem::open(const String& path)
{
auto inode = resolvePath(path);
if (!inode.isValid())
return nullptr;
auto vnode = getOrCreateNode(inode);
if (!vnode)
return nullptr;
return make<FileHandle>(std::move(vnode));
}
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);
return nullptr;
}
InodeIdentifier VirtualFileSystem::resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode)
{
auto symlinkContents = symlinkInode.readEntireFile();
if (!symlinkContents)
return { };
char buf[4096];
sprintf(buf, "/%s/%s", basePath.characters(), String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
return resolvePath(buf);
}
InodeIdentifier VirtualFileSystem::resolvePath(const String& path)
{
auto parts = path.split('/');
InodeIdentifier inode = m_rootNode->inode;
for (unsigned i = 0; i < parts.size(); ++i) {
auto& part = parts[i];
auto metadata = inode.metadata();
if (!metadata.isValid()) {
#ifdef VFS_DEBUG
printf("invalid metadata\n");
#endif
return InodeIdentifier();
}
if (!metadata.isDirectory()) {
#ifdef VFS_DEBUG
printf("not directory\n");
#endif
return InodeIdentifier();
}
inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, part);
if (!inode.isValid()) {
#ifdef VFS_DEBUG
printf("bad child\n");
#endif
return InodeIdentifier();
}
#ifdef VFS_DEBUG
printf("<%s> %02u:%08u\n", part.characters(), inode.fileSystemID(), inode.index());
#endif
if (auto mount = findMountForHost(inode)) {
#ifdef VFS_DEBUG
printf(" -- is host\n");
#endif
inode = mount->guest();
}
if (inode.isRootInode() && !isRoot(inode) && part == "..") {
#ifdef VFS_DEBUG
printf(" -- is guest\n");
#endif
auto mount = findMountForGuest(inode);
inode = mount->host();
inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, "..");
}
metadata = inode.metadata();
if (metadata.isSymbolicLink()) {
char buf[4096] = "";
char* p = buf;
for (unsigned j = 0; j < i; ++j) {
p += sprintf(p, "/%s", parts[j].characters());
}
inode = resolveSymbolicLink(buf, inode);
if (!inode.isValid()) {
printf("Symbolic link resolution failed :(\n");
return { };
}
}
}
return inode;
}
void VirtualFileSystem::Node::retain()
{
++retainCount;
}
void VirtualFileSystem::Node::release()
{
ASSERT(retainCount);
if (--retainCount == 0) {
vfs->freeNode(this);
}
}
VirtualFileSystem::Mount::Mount(InodeIdentifier host, RetainPtr<FileSystem>&& guestFileSystem)
: m_host(host)
, m_guest(guestFileSystem->rootInode())
, m_fileSystem(std::move(guestFileSystem))
{
}

View file

@ -0,0 +1,93 @@
#pragma once
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/RetainPtr.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include "InodeIdentifier.h"
class FileHandle;
class FileSystem;
class VirtualFileSystem {
public:
struct Node {
InodeIdentifier inode;
bool inUse() const { return inode.isValid(); }
void retain();
void release();
FileSystem* fileSystem() { return inode.fileSystem(); }
const FileSystem* fileSystem() const { return inode.fileSystem(); }
private:
friend class VirtualFileSystem;
VirtualFileSystem* vfs { nullptr };
unsigned retainCount { 0 };
};
VirtualFileSystem();
~VirtualFileSystem();
bool isDirectory(const String& path);
void listDirectory(const String& path);
void listDirectoryRecursively(const String& path);
unsigned maxNodeCount() const { return m_maxNodeCount; }
unsigned allocatedNodeCount() const { return m_maxNodeCount - m_nodeFreeList.size(); }
Node* root() { return m_rootNode.ptr(); }
const Node* root() const { return m_rootNode.ptr(); }
bool mountRoot(RetainPtr<FileSystem>&&);
bool mount(RetainPtr<FileSystem>&&, const String& path);
OwnPtr<FileHandle> open(const String& path);
OwnPtr<FileHandle> create(const String& path);
bool isRoot(InodeIdentifier) const;
bool touch(const String&path);
private:
template<typename F> void enumerateDirectoryInode(InodeIdentifier, F func);
InodeIdentifier resolvePath(const String& path);
InodeIdentifier resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode);
RetainPtr<Node> allocateNode();
void freeNode(Node*);
RetainPtr<Node> makeNode(InodeIdentifier);
RetainPtr<Node> getOrCreateNode(InodeIdentifier);
class Mount {
public:
Mount(InodeIdentifier host, RetainPtr<FileSystem>&&);
InodeIdentifier host() const { return m_host; }
InodeIdentifier guest() const { return m_guest; }
private:
InodeIdentifier m_host;
InodeIdentifier m_guest;
RetainPtr<FileSystem> m_fileSystem;
};
Mount* findMountForHost(InodeIdentifier);
Mount* findMountForGuest(InodeIdentifier);
HashMap<InodeIdentifier, Node*> m_inode2vnode;
Vector<OwnPtr<Mount>> m_mounts;
unsigned m_maxNodeCount { 0 };
Node* m_nodes { nullptr };
Vector<Node*> m_nodeFreeList;
RetainPtr<Node> m_rootNode;
};

750
VirtualFileSystem/ext2_fs.h Normal file
View file

@ -0,0 +1,750 @@
/*
* linux/include/linux/ext2_fs.h
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/include/linux/minix_fs.h
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#ifndef _LINUX_EXT2_FS_H
#define _LINUX_EXT2_FS_H
#include "ext2_types.h" /* Changed from linux/types.h */
/*
* The second extended filesystem constants/structures
*/
/*
* Define EXT2FS_DEBUG to produce debug messages
*/
#undef EXT2FS_DEBUG
/*
* Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
*/
#define EXT2_PREALLOCATE
#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
/*
* The second extended file system version
*/
#define EXT2FS_DATE "95/08/09"
#define EXT2FS_VERSION "0.5b"
/*
* Special inode numbers
*/
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
#define EXT2_JOURNAL_INO 8 /* Journal inode */
/* First non-reserved inode for old ext2 filesystems */
#define EXT2_GOOD_OLD_FIRST_INO 11
/*
* The second extended file system magic number
*/
#define EXT2_SUPER_MAGIC 0xEF53
#ifdef __KERNEL__
#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
#else
/* Assume that user mode programs are passing in an ext2fs superblock, not
* a kernel struct super_block. This will allow us to call the feature-test
* macros from user land. */
#define EXT2_SB(sb) (sb)
#endif
/*
* Maximal count of links to a file
*/
#define EXT2_LINK_MAX 65000
/*
* Macro-instructions used to manage several block sizes
*/
#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
#ifdef __KERNEL__
#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits)
#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size)
#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino)
#else
#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
#endif
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
/*
* Macro-instructions used to manage fragments
*/
#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
#ifdef __KERNEL__
# define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block)
#else
# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
#endif
/*
* ACL structures
*/
struct ext2_acl_header /* Header of Access Control Lists */
{
__u32 aclh_size;
__u32 aclh_file_count;
__u32 aclh_acle_count;
__u32 aclh_first_acle;
};
struct ext2_acl_entry /* Access Control List Entry */
{
__u32 acle_size;
__u16 acle_perms; /* Access permissions */
__u16 acle_type; /* Type of entry */
__u16 acle_tag; /* User or group identity */
__u16 acle_pad1;
__u32 acle_next; /* Pointer on next entry for the */
/* same inode or on next free entry */
};
/*
* Structure of a blocks group descriptor
*/
struct ext2_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_flags;
__u32 bg_reserved[2];
__u16 bg_itable_unused; /* Unused inodes count */
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
};
struct ext4_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_flags;
__u32 bg_reserved[2];
__u16 bg_itable_unused; /* Unused inodes count */
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
__u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */
__u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */
__u32 bg_inode_table_hi; /* Inodes table block MSB */
__u16 bg_free_blocks_count_hi;/* Free blocks count MSB */
__u16 bg_free_inodes_count_hi;/* Free inodes count MSB */
__u16 bg_used_dirs_count_hi; /* Directories count MSB */
__u16 bg_pad;
__u32 bg_reserved2[3];
};
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
/*
* Data structures used by the directory indexing feature
*
* Note: all of the multibyte integer fields are little endian.
*/
/*
* Note: dx_root_info is laid out so that if it should somehow get
* overlaid by a dirent the two low bits of the hash version will be
* zero. Therefore, the hash version mod 4 should never be 0.
* Sincerely, the paranoia department.
*/
struct ext2_dx_root_info {
__u32 reserved_zero;
__u8 hash_version; /* 0 now, 1 at release */
__u8 info_length; /* 8 */
__u8 indirect_levels;
__u8 unused_flags;
};
#define EXT2_HASH_LEGACY 0
#define EXT2_HASH_HALF_MD4 1
#define EXT2_HASH_TEA 2
#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */
#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */
#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */
#define EXT2_HASH_FLAG_INCOMPAT 0x1
struct ext2_dx_entry {
__u32 hash;
__u32 block;
};
struct ext2_dx_countlimit {
__u16 limit;
__u16 count;
};
/*
* Macro-instructions used to manage group descriptors
*/
#define EXT2_MIN_DESC_SIZE 32
#define EXT2_MIN_DESC_SIZE_64BIT 64
#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE
#define EXT2_DESC_SIZE(s) \
((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \
(s)->s_desc_size : EXT2_MIN_DESC_SIZE)
#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
#ifdef __KERNEL__
#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
#else
#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s))
#endif
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
/*
* Inode flags
*/
#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
#define EXT2_UNRM_FL 0x00000002 /* Undelete */
#define EXT2_COMPR_FL 0x00000004 /* Compress file */
#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
/* Reserved for compression usage... */
#define EXT2_DIRTY_FL 0x00000100
#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
/* End compression flags --- maybe not all used */
#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
#define EXT2_IMAGIC_FL 0x00002000
#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
/*
* ioctl commands
*/
/* Used for online resize */
struct ext2_new_group_input {
__u32 group; /* Group number for this data */
__u32 block_bitmap; /* Absolute block number of block bitmap */
__u32 inode_bitmap; /* Absolute block number of inode bitmap */
__u32 inode_table; /* Absolute block number of inode table start */
__u32 blocks_count; /* Total number of blocks in this group */
__u16 reserved_blocks; /* Number of reserved blocks in this group */
__u16 unused; /* Number of reserved GDT blocks in group */
};
struct ext4_new_group_input {
__u32 group; /* Group number for this data */
__u64 block_bitmap; /* Absolute block number of block bitmap */
__u64 inode_bitmap; /* Absolute block number of inode bitmap */
__u64 inode_table; /* Absolute block number of inode table start */
__u32 blocks_count; /* Total number of blocks in this group */
__u16 reserved_blocks; /* Number of reserved blocks in this group */
__u16 unused;
};
#ifdef __GNU__ /* Needed for the Hurd */
#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0)
#endif
#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long)
#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long)
#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input)
#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input)
/*
* Structure of an inode on the disk
*/
struct ext2_inode {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Inode change time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_version; /* was l_i_reserved1 */
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u16 l_i_blocks_hi;
__u16 l_i_file_acl_high;
__u16 l_i_uid_high; /* these 2 fields */
__u16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
} osd2; /* OS dependent 2 */
};
/*
* Permanent part of an large inode on the disk
*/
struct ext2_inode_large {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Inode Change time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_version; /* was l_i_reserved1 */
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u16 l_i_blocks_hi;
__u16 l_i_file_acl_high;
__u16 l_i_uid_high; /* these 2 fields */
__u16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
} osd2; /* OS dependent 2 */
__u16 i_extra_isize;
__u16 i_pad1;
__u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
__u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */
__u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
__u32 i_crtime; /* File creation time */
__u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
__u32 i_version_hi; /* high 32 bits for 64-bit version */
};
#define i_size_high i_dir_acl
#if defined(__KERNEL__) || defined(__linux__)
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_frag osd2.linux2.l_i_frag
#define i_fsize osd2.linux2.l_i_fsize
#define i_uid_low i_uid
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
#define i_gid_high osd2.linux2.l_i_gid_high
#define i_reserved2 osd2.linux2.l_i_reserved2
#else
#if defined(__GNU__)
#define i_translator osd1.hurd1.h_i_translator
#define i_frag osd2.hurd2.h_i_frag;
#define i_fsize osd2.hurd2.h_i_fsize;
#define i_uid_high osd2.hurd2.h_i_uid_high
#define i_gid_high osd2.hurd2.h_i_gid_high
#define i_author osd2.hurd2.h_i_author
#endif /* __GNU__ */
#endif /* defined(__KERNEL__) || defined(__linux__) */
#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16)
#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16)
#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x))
#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x))
/*
* File system states
*/
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */
/*
* Misc. filesystem flags
*/
#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */
#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */
#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */
/*
* Mount flags
*/
#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
EXT2_MOUNT_##opt)
/*
* Maximal mount counts between two filesystem checks
*/
#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
/*
* Behaviour when detecting errors
*/
#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
#define EXT2_ERRORS_PANIC 3 /* Panic */
#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
/*
* Structure of the super block
*/
struct ext2_super_block {
__u32 s_inodes_count; /* Inodes count */
__u32 s_blocks_count; /* Blocks count */
__u32 s_r_blocks_count; /* Reserved blocks count */
__u32 s_free_blocks_count; /* Free blocks count */
__u32 s_free_inodes_count; /* Free inodes count */
__u32 s_first_data_block; /* First Data Block */
__u32 s_log_block_size; /* Block size */
__s32 s_log_frag_size; /* Fragment size */
__u32 s_blocks_per_group; /* # Blocks per group */
__u32 s_frags_per_group; /* # Fragments per group */
__u32 s_inodes_per_group; /* # Inodes per group */
__u32 s_mtime; /* Mount time */
__u32 s_wtime; /* Write time */
__u16 s_mnt_count; /* Mount count */
__s16 s_max_mnt_count; /* Maximal mount count */
__u16 s_magic; /* Magic signature */
__u16 s_state; /* File system state */
__u16 s_errors; /* Behaviour when detecting errors */
__u16 s_minor_rev_level; /* minor revision level */
__u32 s_lastcheck; /* time of last check */
__u32 s_checkinterval; /* max. time between checks */
__u32 s_creator_os; /* OS */
__u32 s_rev_level; /* Revision level */
__u16 s_def_resuid; /* Default uid for reserved blocks */
__u16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT2_DYNAMIC_REV superblocks only.
*
* Note: the difference between the compatible feature set and
* the incompatible feature set is that if there is a bit set
* in the incompatible feature set that the kernel doesn't
* know about, it should refuse to mount the filesystem.
*
* e2fsck's requirements are more strict; if it doesn't know
* about a feature in either the compatible or incompatible
* feature set, it must abort and not try to meddle with
* things it doesn't understand...
*/
__u32 s_first_ino; /* First non-reserved inode */
__u16 s_inode_size; /* size of inode structure */
__u16 s_block_group_nr; /* block group # of this superblock */
__u32 s_feature_compat; /* compatible feature set */
__u32 s_feature_incompat; /* incompatible feature set */
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__u32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
*/
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__u16 s_reserved_gdt_blocks; /* Per group table for online growth */
/*
* Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
*/
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
__u32 s_hash_seed[4]; /* HTREE hash seed */
__u8 s_def_hash_version; /* Default hash version to use */
__u8 s_jnl_backup_type; /* Default type of journal backup */
__u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */
__u32 s_default_mount_opts;
__u32 s_first_meta_bg; /* First metablock group */
__u32 s_mkfs_time; /* When the filesystem was created */
__u32 s_jnl_blocks[17]; /* Backup of the journal inode */
__u32 s_blocks_count_hi; /* Blocks count high 32bits */
__u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/
__u32 s_free_blocks_hi; /* Free blocks count */
__u16 s_min_extra_isize; /* All inodes have at least # bytes */
__u16 s_want_extra_isize; /* New inodes should reserve # bytes */
__u32 s_flags; /* Miscellaneous flags */
__u16 s_raid_stride; /* RAID stride */
__u16 s_mmp_interval; /* # seconds to wait in MMP checking */
__u64 s_mmp_block; /* Block for multi-mount protection */
__u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
__u8 s_reserved_char_pad;
__u16 s_reserved_pad; /* Padding to next 32bits */
__u32 s_reserved[162]; /* Padding to the end of the block */
};
/*
* Codes for operating systems
*/
#define EXT2_OS_LINUX 0
#define EXT2_OS_HURD 1
#define EXT2_OBSO_OS_MASIX 2
#define EXT2_OS_FREEBSD 3
#define EXT2_OS_LITES 4
/*
* Revision levels
*/
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
#define EXT2_GOOD_OLD_INODE_SIZE 128
/*
* Journal inode backup types
*/
#define EXT3_JNL_BACKUP_BLOCKS 1
/*
* Feature set definitions
*/
#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_compat & (mask) )
#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
( EXT2_SB(sb)->s_feature_incompat & (mask) )
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
/*
* Default values for user and/or group using reserved blocks
*/
#define EXT2_DEF_RESUID 0
#define EXT2_DEF_RESGID 0
/*
* Default mount options
*/
#define EXT2_DEFM_DEBUG 0x0001
#define EXT2_DEFM_BSDGROUPS 0x0002
#define EXT2_DEFM_XATTR_USER 0x0004
#define EXT2_DEFM_ACL 0x0008
#define EXT2_DEFM_UID16 0x0010
#define EXT3_DEFM_JMODE 0x0060
#define EXT3_DEFM_JMODE_DATA 0x0020
#define EXT3_DEFM_JMODE_ORDERED 0x0040
#define EXT3_DEFM_JMODE_WBACK 0x0060
/*
* Structure of a directory entry
*/
#define EXT2_NAME_LEN 255
struct ext2_dir_entry {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u16 name_len; /* Name length */
char name[EXT2_NAME_LEN]; /* File name */
};
/*
* The new version of the directory entry. Since EXT2 structures are
* stored in intel byte order, and the name_len field could never be
* bigger than 255 chars, it's safe to reclaim the extra byte for the
* file_type field.
*/
struct ext2_dir_entry_2 {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */
__u8 file_type;
char name[EXT2_NAME_LEN]; /* File name */
};
/*
* Ext2 directory file types. Only the low 3 bits are used. The
* other bits are reserved for now.
*/
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
#define EXT2_FT_MAX 8
/*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
*/
#define EXT2_DIR_PAD 4
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
/*
* This structure will be used for multiple mount protection. It will be
* written into the block number saved in the s_mmp_block field in the
* superblock.
*/
#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */
#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */
#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */
struct mmp_struct {
__u32 mmp_magic;
__u32 mmp_seq;
__u64 mmp_time;
char mmp_nodename[64];
char mmp_bdevname[32];
__u16 mmp_interval;
__u16 mmp_pad1;
__u32 mmp_pad2;
};
/*
* Interval in number of seconds to update the MMP sequence number.
*/
#define EXT2_MMP_DEF_INTERVAL 5
#endif /* _LINUX_EXT2_FS_H */

View file

@ -0,0 +1,145 @@
/*
* If linux/types.h is already been included, assume it has defined
* everything we need. (cross fingers) Other header files may have
* also defined the types that we need.
*/
#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \
!defined(_EXT2_TYPES_H))
#define _EXT2_TYPES_H
#define __S8_TYPEDEF __signed__ char
#define __U8_TYPEDEF unsigned char
#define __S16_TYPEDEF __signed__ short
#define __U16_TYPEDEF unsigned short
#define __S32_TYPEDEF __signed__ int
#define __U32_TYPEDEF unsigned int
#define __S64_TYPEDEF __signed__ long long
#define __U64_TYPEDEF unsigned long long
#ifdef __U8_TYPEDEF
typedef __U8_TYPEDEF __u8;
#else
typedef unsigned char __u8;
#endif
#ifdef __S8_TYPEDEF
typedef __S8_TYPEDEF __s8;
#else
typedef signed char __s8;
#endif
#ifdef __U16_TYPEDEF
typedef __U16_TYPEDEF __u16;
#else
#if (4 == 2)
typedef unsigned int __u16;
#else
#if (2 == 2)
typedef unsigned short __u16;
#else
?==error: undefined 16 bit type
#endif /* SIZEOF_SHORT == 2 */
#endif /* SIZEOF_INT == 2 */
#endif /* __U16_TYPEDEF */
#ifdef __S16_TYPEDEF
typedef __S16_TYPEDEF __s16;
#else
#if (4 == 2)
typedef int __s16;
#else
#if (2 == 2)
typedef short __s16;
#else
?==error: undefined 16 bit type
#endif /* SIZEOF_SHORT == 2 */
#endif /* SIZEOF_INT == 2 */
#endif /* __S16_TYPEDEF */
#ifdef __U32_TYPEDEF
typedef __U32_TYPEDEF __u32;
#else
#if (4 == 4)
typedef unsigned int __u32;
#else
#if (4 == 4)
typedef unsigned long __u32;
#else
#if (2 == 4)
typedef unsigned short __u32;
#else
?== error: undefined 32 bit type
#endif /* SIZEOF_SHORT == 4 */
#endif /* SIZEOF_LONG == 4 */
#endif /* SIZEOF_INT == 4 */
#endif /* __U32_TYPEDEF */
#ifdef __S32_TYPEDEF
typedef __S32_TYPEDEF __s32;
#else
#if (4 == 4)
typedef int __s32;
#else
#if (4 == 4)
typedef long __s32;
#else
#if (2 == 4)
typedef short __s32;
#else
?== error: undefined 32 bit type
#endif /* SIZEOF_SHORT == 4 */
#endif /* SIZEOF_LONG == 4 */
#endif /* SIZEOF_INT == 4 */
#endif /* __S32_TYPEDEF */
#ifdef __U64_TYPEDEF
typedef __U64_TYPEDEF __u64;
#else
#if (4 == 8)
typedef unsigned int __u64;
#else
#if (4 == 8)
typedef unsigned long __u64;
#else
#if (8 == 8)
typedef unsigned long long __u64;
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __U64_TYPEDEF */
#ifdef __S64_TYPEDEF
typedef __S64_TYPEDEF __s64;
#else
#if (4 == 8)
typedef int __s64;
#else
#if (4 == 8)
typedef long __s64;
#else
#if (8 == 8)
#if defined(__GNUC__)
typedef __signed__ long long __s64;
#else
typedef signed long long __s64;
#endif /* __GNUC__ */
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __S64_TYPEDEF */
#undef __S8_TYPEDEF
#undef __U8_TYPEDEF
#undef __S16_TYPEDEF
#undef __U16_TYPEDEF
#undef __S32_TYPEDEF
#undef __U32_TYPEDEF
#undef __S64_TYPEDEF
#undef __U64_TYPEDEF
#endif /* _*_TYPES_H */
/* These defines are needed for the public ext2fs.h header file */
#define HAVE_SYS_TYPES_H 1
#undef WORDS_BIGENDIAN

BIN
VirtualFileSystem/small.fs Normal file

Binary file not shown.

158
VirtualFileSystem/test.cpp Normal file
View file

@ -0,0 +1,158 @@
#include "Ext2FileSystem.h"
#include "FileBackedBlockDevice.h"
#include "VirtualFileSystem.h"
#include "FileHandle.h"
#include "SyntheticFileSystem.h"
#include <cstring>
#include <AK/SimpleMalloc.h>
#include <AK/kmalloc.h>
static RetainPtr<FileSystem> makeFileSystem(const char* imagePath);
int main(int c, char** v)
{
const char* filename = "small.fs";
if (c >= 2)
filename = v[1];
VirtualFileSystem vfs;
if (!vfs.mountRoot(makeFileSystem(filename))) {
printf("Failed to mount root :(\n");
return 1;
}
auto newFile = vfs.create("/empty");
printf("vfs.create: %p\n", newFile.ptr());
//return 0;
if (!strcmp(v[0], "./vcat")) {
auto handle = vfs.open(v[2]);
if (!handle) {
printf("failed to open %s inside fs image\n", v[2]);
return 1;
}
auto contents = handle->read();
FILE* fout = fopen(v[3], "w");
if (!fout) {
printf("failed to open %s for output\n", v[3]);
return 1;
}
fwrite(contents.pointer(), sizeof(char), contents.size(), fout);
fclose(fout);
return 0;
}
auto synthfs = SyntheticFileSystem::create();
bool success = static_cast<FileSystem&>(*synthfs).initialize();
printf("synth->initialize(): returned %u\n", success);
vfs.mount(std::move(synthfs), "/syn");
vfs.listDirectory("/");
printf("list /syn:\n");
vfs.listDirectory("/syn");
#if 0
auto handle = vfs.open("/home/andreas/../../home/./andreas/./file2");
printf("handle = %p\n", handle.ptr());
ASSERT(handle);
auto contents = handle->read();
ASSERT(contents);
printf("contents: '%s'\n", contents->pointer());
#endif
String currentDirectory = "/";
while (true) {
char cmdbuf[256];
printf("::>");
fflush(stdout);
fgets(cmdbuf, sizeof(cmdbuf), stdin);
if (cmdbuf[strlen(cmdbuf) - 1] == '\n')
cmdbuf[strlen(cmdbuf) - 1] = '\0';
String command = cmdbuf;
auto parts = command.split(' ');
if (parts.isEmpty())
continue;
String cmd = parts[0];
if (cmd == "q")
break;
if (cmd == "pwd") {
printf("%s\n", currentDirectory.characters());
continue;
}
if (cmd == "ls") {
vfs.listDirectory(currentDirectory);
continue;
}
if (cmd == "lr") {
vfs.listDirectoryRecursively(currentDirectory);
continue;
}
if (cmd == "cd" && parts.size() > 1) {
char buf[1024];
sprintf(buf, "%s/%s", currentDirectory.characters(), parts[1].characters());
if (vfs.isDirectory(buf)) {
currentDirectory = buf;
} else {
printf("No such directory: %s\n", buf);
}
continue;
}
if (cmd == "mt" && parts.size() > 1) {
char buf[1024];
sprintf(buf, "%s/%s", currentDirectory.characters(), parts[1].characters());
vfs.touch(buf);
continue;
}
if (cmd == "cat" && parts.size() > 1) {
char pathbuf[1024];
sprintf(pathbuf, "%s/%s", currentDirectory.characters(), parts[1].characters());
auto handle = vfs.open(pathbuf);
if (!handle) {
printf("failed to open %s\n", pathbuf);
continue;
}
auto contents = handle->read();
fwrite(contents.pointer(), sizeof(char), contents.size(), stdout);
continue;
}
if (cmd == "ma") {
SimpleMalloc::dump();
continue;
}
}
return 0;
}
RetainPtr<FileSystem> makeFileSystem(const char* imagePath)
{
auto fsImage = FileBackedBlockDevice::create(imagePath, 512);
if (!fsImage->isValid()) {
fprintf(stderr, "Failed to open fs image file '%s'\n", imagePath);
exit(1);
}
auto ext2 = Ext2FileSystem::create(std::move(fsImage));
bool success = static_cast<FileSystem&>(*ext2).initialize();
printf("ext2->initialize(): returned %u\n", success);
return ext2;
}