mirror of
https://github.com/RGBCube/serenity
synced 2025-07-07 12:27:35 +00:00
Ext2FS: Implement writing into inodes with arbitrary offset and length.
Okay, this is pretty cool. :^) There are some issues and limitations for sure but the basic functionality is there.
This commit is contained in:
parent
29dfb4ae13
commit
906685e238
8 changed files with 147 additions and 19 deletions
|
@ -15,10 +15,10 @@ DiskBackedFS::~DiskBackedFS()
|
||||||
|
|
||||||
bool DiskBackedFS::writeBlock(unsigned index, const ByteBuffer& data)
|
bool DiskBackedFS::writeBlock(unsigned index, const ByteBuffer& data)
|
||||||
{
|
{
|
||||||
ASSERT(data.size() == blockSize());
|
|
||||||
#ifdef DBFS_DEBUG
|
#ifdef DBFS_DEBUG
|
||||||
kprintf("DiskBackedFileSystem::writeBlock %u\n", index);
|
kprintf("DiskBackedFileSystem::writeBlock %u, size=%u\n", index, data.size());
|
||||||
#endif
|
#endif
|
||||||
|
ASSERT(data.size() == blockSize());
|
||||||
DiskOffset baseOffset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(blockSize());
|
DiskOffset baseOffset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(blockSize());
|
||||||
return device().write(baseOffset, blockSize(), data.pointer());
|
return device().write(baseOffset, blockSize(), data.pointer());
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ Ext2FS::BlockListShape Ext2FS::compute_block_list_shape(unsigned blocks)
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2inode, Vector<BlockIndex>&& blocks)
|
bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2inode, const Vector<BlockIndex>& blocks)
|
||||||
{
|
{
|
||||||
dbgprintf("Ext2FS: writing %u block(s) to i_block array\n", min((size_t)EXT2_NDIR_BLOCKS, blocks.size()));
|
dbgprintf("Ext2FS: writing %u block(s) to i_block array\n", min((size_t)EXT2_NDIR_BLOCKS, blocks.size()));
|
||||||
|
|
||||||
|
@ -188,6 +188,8 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in
|
||||||
Vector<BlockIndex> new_meta_blocks;
|
Vector<BlockIndex> new_meta_blocks;
|
||||||
if (new_shape.meta_blocks > old_shape.meta_blocks) {
|
if (new_shape.meta_blocks > old_shape.meta_blocks) {
|
||||||
new_meta_blocks = allocate_blocks(group_index_from_inode(inode_index), new_shape.meta_blocks - old_shape.meta_blocks);
|
new_meta_blocks = allocate_blocks(group_index_from_inode(inode_index), new_shape.meta_blocks - old_shape.meta_blocks);
|
||||||
|
for (auto bi : new_meta_blocks)
|
||||||
|
set_block_allocation_state(group_index_from_inode(inode_index), bi, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned output_block_index = 0;
|
unsigned output_block_index = 0;
|
||||||
|
@ -207,6 +209,7 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
dbgprintf("Ext2FS: Writing out indirect blockptr block for inode %u\n", inode_index);
|
||||||
auto block_contents = ByteBuffer::create_uninitialized(blockSize());
|
auto block_contents = ByteBuffer::create_uninitialized(blockSize());
|
||||||
BufferStream stream(block_contents);
|
BufferStream stream(block_contents);
|
||||||
ASSERT(new_shape.indirect_blocks <= EXT2_ADDR_PER_BLOCK(&super_block()));
|
ASSERT(new_shape.indirect_blocks <= EXT2_ADDR_PER_BLOCK(&super_block()));
|
||||||
|
@ -459,30 +462,135 @@ ssize_t Ext2FSInode::read_bytes(Unix::off_t offset, size_t count, byte* buffer,
|
||||||
return nread;
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ext2FSInode::write(const ByteBuffer& data)
|
ssize_t Ext2FSInode::write_bytes(Unix::off_t offset, size_t count, const byte* data, FileDescriptor*)
|
||||||
{
|
{
|
||||||
|
LOCKER(m_lock);
|
||||||
|
|
||||||
// FIXME: Support writing to symlink inodes.
|
// FIXME: Support writing to symlink inodes.
|
||||||
ASSERT(!is_symlink());
|
ASSERT(!is_symlink());
|
||||||
|
|
||||||
unsigned blocksNeededBefore = ceilDiv(size(), fs().blockSize());
|
ASSERT(offset >= 0);
|
||||||
unsigned blocksNeededAfter = ceilDiv((unsigned)data.size(), fs().blockSize());
|
|
||||||
|
|
||||||
// FIXME: Support growing or shrinking the block list.
|
const size_t block_size = fs().blockSize();
|
||||||
ASSERT(blocksNeededBefore == blocksNeededAfter);
|
size_t new_size = max(static_cast<size_t>(offset) + count, size());
|
||||||
|
|
||||||
auto list = fs().block_list_for_inode(m_raw_inode);
|
unsigned blocks_needed_before = ceilDiv(size(), block_size);
|
||||||
if (list.is_empty()) {
|
unsigned blocks_needed_after = ceilDiv(new_size, block_size);
|
||||||
kprintf("ext2fs: writeInode: empty block list for inode %u\n", index());
|
|
||||||
return false;
|
auto block_list = fs().block_list_for_inode(m_raw_inode);
|
||||||
|
if (blocks_needed_after > blocks_needed_before) {
|
||||||
|
auto new_blocks = fs().allocate_blocks(fs().group_index_from_inode(index()), blocks_needed_after - blocks_needed_before);
|
||||||
|
for (auto new_block_index : new_blocks)
|
||||||
|
fs().set_block_allocation_state(fs().group_index_from_inode(index()), new_block_index, true);
|
||||||
|
block_list.append(move(new_blocks));
|
||||||
|
} else if (blocks_needed_after < blocks_needed_before) {
|
||||||
|
// FIXME: Implement block list shrinking!
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < list.size(); ++i) {
|
dword first_block_logical_index = offset / block_size;
|
||||||
|
dword last_block_logical_index = (offset + count) / block_size;
|
||||||
|
if (last_block_logical_index >= block_list.size())
|
||||||
|
last_block_logical_index = block_list.size() - 1;
|
||||||
|
|
||||||
|
dword offset_into_first_block = offset % block_size;
|
||||||
|
|
||||||
|
ssize_t nwritten = 0;
|
||||||
|
size_t remaining_count = min((Unix::off_t)count, (Unix::off_t)new_size - offset);
|
||||||
|
const byte* in = data;
|
||||||
|
|
||||||
|
#ifdef EXT2_DEBUG
|
||||||
|
dbgprintf("Ext2FS::write_bytes: Writing %u bytes %d bytes into inode %u:%u from %p\n", count, offset, fsid(), index(), data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto buffer_block = ByteBuffer::create_uninitialized(block_size);
|
||||||
|
for (dword bi = first_block_logical_index; remaining_count && bi <= last_block_logical_index; ++bi) {
|
||||||
|
dword offset_into_block = (bi == first_block_logical_index) ? offset_into_first_block : 0;
|
||||||
|
dword num_bytes_to_copy = min(block_size - offset_into_block, remaining_count);
|
||||||
|
|
||||||
|
ByteBuffer block;
|
||||||
|
if (offset_into_block != 0) {
|
||||||
|
block = fs().readBlock(block_list[bi]);
|
||||||
|
if (!block) {
|
||||||
|
kprintf("Ext2FSInode::write_bytes: readBlock(%u) failed (lbi: %u)\n", block_list[bi], bi);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
block = buffer_block;
|
||||||
|
|
||||||
|
memcpy(block.pointer() + offset_into_block, in, num_bytes_to_copy);
|
||||||
|
if (offset_into_block == 0 && !num_bytes_to_copy)
|
||||||
|
memset(block.pointer() + num_bytes_to_copy, 0, block_size - num_bytes_to_copy);
|
||||||
|
#ifdef EXT2_DEBUG
|
||||||
|
dbgprintf("Ext2FSInode::write_bytes: writing block %u (offset_into_block: %u)\n", block_list[bi], offset_into_block);
|
||||||
|
#endif
|
||||||
|
bool success = fs().writeBlock(block_list[bi], block);
|
||||||
|
if (!success) {
|
||||||
|
kprintf("Ext2FSInode::write_bytes: writeBlock(%u) failed (lbi: %u)\n", block_list[bi], bi);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
remaining_count -= num_bytes_to_copy;
|
||||||
|
nwritten += num_bytes_to_copy;
|
||||||
|
in += num_bytes_to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = fs().write_block_list_for_inode(index(), m_raw_inode, block_list);
|
||||||
|
ASSERT(success);
|
||||||
|
|
||||||
|
m_raw_inode.i_size = new_size;
|
||||||
|
m_raw_inode.i_blocks = block_list.size() * (block_size / 512);
|
||||||
|
#ifdef EXT2_DEBUG
|
||||||
|
dbgprintf("Ext2FSInode::write_bytes: after write, i_size=%u, i_blocks=%u (%u blocks in list)\n", m_raw_inode.i_size, m_raw_inode.i_blocks, block_list.size());
|
||||||
|
#endif
|
||||||
|
flush_metadata();
|
||||||
|
|
||||||
|
// NOTE: Make sure the cached block list is up to date!
|
||||||
|
m_block_list = move(block_list);
|
||||||
|
return nwritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ext2FSInode::write(const ByteBuffer& data)
|
||||||
|
{
|
||||||
|
// FIXME: Lock this Inode during write. Inode should have a common locking mechanism.
|
||||||
|
// FIXME: Support writing to symlink inodes.
|
||||||
|
ASSERT(!is_symlink());
|
||||||
|
|
||||||
|
unsigned blocks_needed_before = ceilDiv(size(), fs().blockSize());
|
||||||
|
unsigned blocks_needed_after = ceilDiv((unsigned)data.size(), fs().blockSize());
|
||||||
|
|
||||||
|
|
||||||
|
auto block_list = fs().block_list_for_inode(m_raw_inode);
|
||||||
|
if (blocks_needed_after > blocks_needed_before) {
|
||||||
|
auto new_blocks = fs().allocate_blocks(fs().group_index_from_inode(index()), blocks_needed_after - blocks_needed_before);
|
||||||
|
for (auto new_block_index : new_blocks)
|
||||||
|
fs().set_block_allocation_state(fs().group_index_from_inode(index()), new_block_index, true);
|
||||||
|
block_list.append(move(new_blocks));
|
||||||
|
} else if (blocks_needed_after < blocks_needed_before) {
|
||||||
|
// FIXME: Implement block list shrinking!
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto padded_block = ByteBuffer::create_uninitialized(fs().blockSize());
|
||||||
|
for (unsigned i = 0; i < block_list.size(); ++i) {
|
||||||
auto section = data.slice(i * fs().blockSize(), fs().blockSize());
|
auto section = data.slice(i * fs().blockSize(), fs().blockSize());
|
||||||
//kprintf("section = %p (%u)\n", section.pointer(), section.size());
|
bool success;
|
||||||
bool success = fs().writeBlock(list[i], section);
|
dbgprintf("Ext2FS: write section = %p (%u)\n", section.pointer(), section.size());
|
||||||
|
if (section.size() == fs().blockSize())
|
||||||
|
success = fs().writeBlock(block_list[i], section);
|
||||||
|
else {
|
||||||
|
memcpy(padded_block.pointer(), section.pointer(), section.size());
|
||||||
|
memset(padded_block.pointer() + section.size(), 0, fs().blockSize() - section.size());
|
||||||
|
success = fs().writeBlock(block_list[i], padded_block);
|
||||||
|
}
|
||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = fs().write_block_list_for_inode(index(), m_raw_inode, block_list);
|
||||||
|
ASSERT(success);
|
||||||
|
|
||||||
|
m_raw_inode.i_size = data.size();
|
||||||
|
set_metadata_dirty(true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1043,7 +1151,7 @@ RetainPtr<Inode> Ext2FS::create_inode(InodeIdentifier parent_id, const String& n
|
||||||
else
|
else
|
||||||
initialLinksCount = 1;
|
initialLinksCount = 1;
|
||||||
|
|
||||||
auto timestamp = ktime(nullptr);
|
auto timestamp = RTC::now();
|
||||||
auto e2inode = make<ext2_inode>();
|
auto e2inode = make<ext2_inode>();
|
||||||
memset(e2inode.ptr(), 0, sizeof(ext2_inode));
|
memset(e2inode.ptr(), 0, sizeof(ext2_inode));
|
||||||
e2inode->i_mode = mode;
|
e2inode->i_mode = mode;
|
||||||
|
@ -1060,7 +1168,7 @@ RetainPtr<Inode> Ext2FS::create_inode(InodeIdentifier parent_id, const String& n
|
||||||
// FIXME: Implement writing out indirect blocks!
|
// FIXME: Implement writing out indirect blocks!
|
||||||
ASSERT(blocks.size() < EXT2_NDIR_BLOCKS);
|
ASSERT(blocks.size() < EXT2_NDIR_BLOCKS);
|
||||||
|
|
||||||
success = write_block_list_for_inode(inode_id, *e2inode, move(blocks));
|
success = write_block_list_for_inode(inode_id, *e2inode, blocks);
|
||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
|
|
||||||
dbgprintf("Ext2FS: writing initial metadata for inode %u\n", inode_id);
|
dbgprintf("Ext2FS: writing initial metadata for inode %u\n", inode_id);
|
||||||
|
|
|
@ -33,6 +33,7 @@ private:
|
||||||
virtual String reverse_lookup(InodeIdentifier) override;
|
virtual String reverse_lookup(InodeIdentifier) override;
|
||||||
virtual void flush_metadata() override;
|
virtual void flush_metadata() override;
|
||||||
virtual bool write(const ByteBuffer&) override;
|
virtual bool write(const ByteBuffer&) override;
|
||||||
|
virtual ssize_t write_bytes(Unix::off_t, size_t, const byte* data, FileDescriptor*) override;
|
||||||
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override;
|
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override;
|
||||||
virtual bool remove_child(const String& name, int& error) override;
|
virtual bool remove_child(const String& name, int& error) override;
|
||||||
virtual RetainPtr<Inode> parent() const override;
|
virtual RetainPtr<Inode> parent() const override;
|
||||||
|
@ -93,7 +94,7 @@ private:
|
||||||
unsigned group_index_from_inode(unsigned) const;
|
unsigned group_index_from_inode(unsigned) const;
|
||||||
|
|
||||||
Vector<unsigned> block_list_for_inode(const ext2_inode&, bool include_block_list_blocks = false) const;
|
Vector<unsigned> block_list_for_inode(const ext2_inode&, bool include_block_list_blocks = false) const;
|
||||||
bool write_block_list_for_inode(InodeIndex, ext2_inode&, Vector<BlockIndex>&&);
|
bool write_block_list_for_inode(InodeIndex, ext2_inode&, const Vector<BlockIndex>&);
|
||||||
|
|
||||||
void dump_block_bitmap(unsigned groupIndex) const;
|
void dump_block_bitmap(unsigned groupIndex) const;
|
||||||
void dump_inode_bitmap(unsigned groupIndex) const;
|
void dump_inode_bitmap(unsigned groupIndex) const;
|
||||||
|
|
|
@ -168,7 +168,7 @@ ssize_t FileDescriptor::write(Process& process, const byte* data, size_t size)
|
||||||
return m_device->write(process, data, size);
|
return m_device->write(process, data, size);
|
||||||
}
|
}
|
||||||
ASSERT(m_inode);
|
ASSERT(m_inode);
|
||||||
ssize_t nwritten = m_inode->write(ByteBuffer::wrap((byte*)data, size));
|
ssize_t nwritten = m_inode->write_bytes(m_current_offset, size, data, this);
|
||||||
m_current_offset += nwritten;
|
m_current_offset += nwritten;
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
virtual InodeIdentifier lookup(const String& name) = 0;
|
virtual InodeIdentifier lookup(const String& name) = 0;
|
||||||
virtual String reverse_lookup(InodeIdentifier) = 0;
|
virtual String reverse_lookup(InodeIdentifier) = 0;
|
||||||
virtual bool write(const ByteBuffer&) = 0;
|
virtual bool write(const ByteBuffer&) = 0;
|
||||||
|
virtual ssize_t write_bytes(Unix::off_t, size_t, const byte* data, FileDescriptor*) = 0;
|
||||||
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) = 0;
|
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) = 0;
|
||||||
virtual bool remove_child(const String& name, int& error) = 0;
|
virtual bool remove_child(const String& name, int& error) = 0;
|
||||||
virtual RetainPtr<Inode> parent() const = 0;
|
virtual RetainPtr<Inode> parent() const = 0;
|
||||||
|
|
|
@ -280,6 +280,17 @@ bool SynthFSInode::write(const ByteBuffer& data)
|
||||||
return m_write_callback(*this, data);
|
return m_write_callback(*this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t SynthFSInode::write_bytes(Unix::off_t offset, size_t size, const byte* buffer, FileDescriptor*)
|
||||||
|
{
|
||||||
|
if (!m_write_callback)
|
||||||
|
return -EPERM;
|
||||||
|
// FIXME: Being able to write into SynthFS at a non-zero offset seems like something we should support..
|
||||||
|
ASSERT(offset == 0);
|
||||||
|
bool success = m_write_callback(*this, ByteBuffer::wrap((byte*)buffer, size));
|
||||||
|
ASSERT(success);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool SynthFSInode::add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error)
|
bool SynthFSInode::add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error)
|
||||||
{
|
{
|
||||||
(void) child_id;
|
(void) child_id;
|
||||||
|
|
|
@ -61,6 +61,7 @@ private:
|
||||||
virtual String reverse_lookup(InodeIdentifier) override;
|
virtual String reverse_lookup(InodeIdentifier) override;
|
||||||
virtual void flush_metadata() override;
|
virtual void flush_metadata() override;
|
||||||
virtual bool write(const ByteBuffer&) override;
|
virtual bool write(const ByteBuffer&) override;
|
||||||
|
virtual ssize_t write_bytes(Unix::off_t, size_t, const byte* buffer, FileDescriptor*) override;
|
||||||
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override;
|
virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override;
|
||||||
virtual bool remove_child(const String& name, int& error) override;
|
virtual bool remove_child(const String& name, int& error) override;
|
||||||
virtual RetainPtr<Inode> parent() const override;
|
virtual RetainPtr<Inode> parent() const override;
|
||||||
|
|
|
@ -161,6 +161,12 @@ RetainPtr<FileDescriptor> VFS::create(const String& path, int& error, int option
|
||||||
{
|
{
|
||||||
(void) options;
|
(void) options;
|
||||||
error = -EWHYTHO;
|
error = -EWHYTHO;
|
||||||
|
|
||||||
|
if (!isSocket(mode) && !isFIFO(mode) && !isBlockDevice(mode) && !isCharacterDevice(mode)) {
|
||||||
|
// Turn it into a regular file. (This feels rather hackish.)
|
||||||
|
mode |= 0100000;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This won't work nicely across mount boundaries.
|
// FIXME: This won't work nicely across mount boundaries.
|
||||||
FileSystemPath p(path);
|
FileSystemPath p(path);
|
||||||
if (!p.is_valid()) {
|
if (!p.is_valid()) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue