diff --git a/VirtualFileSystem/DiskBackedFileSystem.cpp b/VirtualFileSystem/DiskBackedFileSystem.cpp index 8f17f859ea..974ade6fe6 100644 --- a/VirtualFileSystem/DiskBackedFileSystem.cpp +++ b/VirtualFileSystem/DiskBackedFileSystem.cpp @@ -15,10 +15,10 @@ DiskBackedFS::~DiskBackedFS() bool DiskBackedFS::writeBlock(unsigned index, const ByteBuffer& data) { - ASSERT(data.size() == blockSize()); #ifdef DBFS_DEBUG - kprintf("DiskBackedFileSystem::writeBlock %u\n", index); + kprintf("DiskBackedFileSystem::writeBlock %u, size=%u\n", index, data.size()); #endif + ASSERT(data.size() == blockSize()); DiskOffset baseOffset = static_cast(index) * static_cast(blockSize()); return device().write(baseOffset, blockSize(), data.pointer()); } diff --git a/VirtualFileSystem/Ext2FileSystem.cpp b/VirtualFileSystem/Ext2FileSystem.cpp index 90ba3f2f17..59c9fdc8fa 100644 --- a/VirtualFileSystem/Ext2FileSystem.cpp +++ b/VirtualFileSystem/Ext2FileSystem.cpp @@ -178,7 +178,7 @@ Ext2FS::BlockListShape Ext2FS::compute_block_list_shape(unsigned blocks) return shape; } -bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2inode, Vector&& blocks) +bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2inode, const Vector& blocks) { 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 new_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); + for (auto bi : new_meta_blocks) + set_block_allocation_state(group_index_from_inode(inode_index), bi, true); } 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()); BufferStream stream(block_contents); 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; } -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. ASSERT(!is_symlink()); - unsigned blocksNeededBefore = ceilDiv(size(), fs().blockSize()); - unsigned blocksNeededAfter = ceilDiv((unsigned)data.size(), fs().blockSize()); + ASSERT(offset >= 0); - // FIXME: Support growing or shrinking the block list. - ASSERT(blocksNeededBefore == blocksNeededAfter); + const size_t block_size = fs().blockSize(); + size_t new_size = max(static_cast(offset) + count, size()); - auto list = fs().block_list_for_inode(m_raw_inode); - if (list.is_empty()) { - kprintf("ext2fs: writeInode: empty block list for inode %u\n", index()); - return false; + unsigned blocks_needed_before = ceilDiv(size(), block_size); + unsigned blocks_needed_after = ceilDiv(new_size, block_size); + + 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()); - //kprintf("section = %p (%u)\n", section.pointer(), section.size()); - bool success = fs().writeBlock(list[i], section); + bool success; + 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); } + 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; } @@ -1043,7 +1151,7 @@ RetainPtr Ext2FS::create_inode(InodeIdentifier parent_id, const String& n else initialLinksCount = 1; - auto timestamp = ktime(nullptr); + auto timestamp = RTC::now(); auto e2inode = make(); memset(e2inode.ptr(), 0, sizeof(ext2_inode)); e2inode->i_mode = mode; @@ -1060,7 +1168,7 @@ RetainPtr Ext2FS::create_inode(InodeIdentifier parent_id, const String& n // FIXME: Implement writing out indirect 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); dbgprintf("Ext2FS: writing initial metadata for inode %u\n", inode_id); diff --git a/VirtualFileSystem/Ext2FileSystem.h b/VirtualFileSystem/Ext2FileSystem.h index 9881a3f2c8..a9819588ee 100644 --- a/VirtualFileSystem/Ext2FileSystem.h +++ b/VirtualFileSystem/Ext2FileSystem.h @@ -33,6 +33,7 @@ private: virtual String reverse_lookup(InodeIdentifier) override; virtual void flush_metadata() 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 remove_child(const String& name, int& error) override; virtual RetainPtr parent() const override; @@ -93,7 +94,7 @@ private: unsigned group_index_from_inode(unsigned) const; Vector block_list_for_inode(const ext2_inode&, bool include_block_list_blocks = false) const; - bool write_block_list_for_inode(InodeIndex, ext2_inode&, Vector&&); + bool write_block_list_for_inode(InodeIndex, ext2_inode&, const Vector&); void dump_block_bitmap(unsigned groupIndex) const; void dump_inode_bitmap(unsigned groupIndex) const; diff --git a/VirtualFileSystem/FileDescriptor.cpp b/VirtualFileSystem/FileDescriptor.cpp index 15d2a79b68..9668af33fa 100644 --- a/VirtualFileSystem/FileDescriptor.cpp +++ b/VirtualFileSystem/FileDescriptor.cpp @@ -168,7 +168,7 @@ ssize_t FileDescriptor::write(Process& process, const byte* data, size_t size) return m_device->write(process, data, size); } 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; return nwritten; } diff --git a/VirtualFileSystem/FileSystem.h b/VirtualFileSystem/FileSystem.h index 110fd313e7..1dd0fee94f 100644 --- a/VirtualFileSystem/FileSystem.h +++ b/VirtualFileSystem/FileSystem.h @@ -84,6 +84,7 @@ public: virtual InodeIdentifier lookup(const String& name) = 0; virtual String reverse_lookup(InodeIdentifier) = 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 remove_child(const String& name, int& error) = 0; virtual RetainPtr parent() const = 0; diff --git a/VirtualFileSystem/SyntheticFileSystem.cpp b/VirtualFileSystem/SyntheticFileSystem.cpp index eec8f6ec24..95558236c9 100644 --- a/VirtualFileSystem/SyntheticFileSystem.cpp +++ b/VirtualFileSystem/SyntheticFileSystem.cpp @@ -280,6 +280,17 @@ bool SynthFSInode::write(const ByteBuffer& 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) { (void) child_id; diff --git a/VirtualFileSystem/SyntheticFileSystem.h b/VirtualFileSystem/SyntheticFileSystem.h index ee3744e9df..daefa3cf46 100644 --- a/VirtualFileSystem/SyntheticFileSystem.h +++ b/VirtualFileSystem/SyntheticFileSystem.h @@ -61,6 +61,7 @@ private: virtual String reverse_lookup(InodeIdentifier) override; virtual void flush_metadata() 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 remove_child(const String& name, int& error) override; virtual RetainPtr parent() const override; diff --git a/VirtualFileSystem/VirtualFileSystem.cpp b/VirtualFileSystem/VirtualFileSystem.cpp index e5663068b0..e1c62ff89f 100644 --- a/VirtualFileSystem/VirtualFileSystem.cpp +++ b/VirtualFileSystem/VirtualFileSystem.cpp @@ -161,6 +161,12 @@ RetainPtr VFS::create(const String& path, int& error, int option { (void) options; 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. FileSystemPath p(path); if (!p.is_valid()) {