diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-04-28 22:07:25 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-04-28 22:07:25 +0200 |
commit | 899f6a5de2037364ce809e38970d7bcb0d4ab56b (patch) | |
tree | d99138bb22b0c623a71ee003d24a9146bc0c2549 /Kernel | |
parent | 89df887e1fbbd7027a120d2682bca6d791456068 (diff) | |
download | serenity-899f6a5de2037364ce809e38970d7bcb0d4ab56b.zip |
Ext2FS: Support shrinking inode to a smaller size.
Factor out inode resizing into a separate Ext2FSInode::resize() function.
This is then called both from write_bytes() and truncate().
This patch finally implements freeing of blocks when an inode shrinks.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/FileSystem/Ext2FileSystem.cpp | 79 | ||||
-rw-r--r-- | Kernel/FileSystem/Ext2FileSystem.h | 1 |
2 files changed, 50 insertions, 30 deletions
diff --git a/Kernel/FileSystem/Ext2FileSystem.cpp b/Kernel/FileSystem/Ext2FileSystem.cpp index 94bf18226a..a7c4559aa7 100644 --- a/Kernel/FileSystem/Ext2FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FileSystem.cpp @@ -216,6 +216,10 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in } if (inode_dirty) { dbgprintf("Ext2FS: Writing %u direct block(s) to i_block array of inode %u\n", min(EXT2_NDIR_BLOCKS, blocks.size()), inode_index); +#ifdef EXT2_DEBUG + for (int i = 0; i < min(EXT2_NDIR_BLOCKS, blocks.size()); ++i) + dbgprintf(" + %u\n", blocks[i]); +#endif write_ext2_inode(inode_index, e2inode); inode_dirty = false; } @@ -512,6 +516,40 @@ ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, byte* buffer, FileD return nread; } +bool Ext2FSInode::resize(qword new_size) +{ + qword block_size = fs().block_size(); + qword old_size = size(); + int blocks_needed_before = ceil_div(old_size, block_size); + int blocks_needed_after = ceil_div(new_size, block_size); + + dbgprintf("blocks needed before (size was %u): %d\n", old_size, blocks_needed_before); + dbgprintf("blocks needed after (size is %u): %d\n", new_size, blocks_needed_after); + + 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(new_block_index, true); + block_list.append(move(new_blocks)); + } else if (blocks_needed_after < blocks_needed_before) { + while (block_list.size() != blocks_needed_after) { + auto block_index = block_list.take_last(); + fs().set_block_allocation_state(block_index, false); + } + } + + bool success = fs().write_block_list_for_inode(index(), m_raw_inode, block_list); + if (!success) + return false; + + m_raw_inode.i_size = new_size; + set_metadata_dirty(true); + + m_block_list = move(block_list); + return true; +} + ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, FileDescriptor*) { ASSERT(offset >= 0); @@ -534,27 +572,16 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, } const ssize_t block_size = fs().block_size(); - size_t old_size = size(); - size_t new_size = max(static_cast<size_t>(offset) + count, size()); - - unsigned blocks_needed_before = ceil_div(size(), block_size); - unsigned blocks_needed_after = ceil_div(new_size, block_size); + qword old_size = size(); + qword new_size = max(static_cast<qword>(offset) + count, (qword)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(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(); - } + if (!resize(new_size)) + return -EIO; int first_block_logical_index = offset / block_size; int last_block_logical_index = (offset + count) / block_size; - if (last_block_logical_index >= block_list.size()) - last_block_logical_index = block_list.size() - 1; + if (last_block_logical_index >= m_block_list.size()) + last_block_logical_index = m_block_list.size() - 1; int offset_into_first_block = offset % block_size; @@ -575,9 +602,9 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, ByteBuffer block; if (offset_into_block != 0 || num_bytes_to_copy != block_size) { - block = fs().read_block(block_list[bi]); + block = fs().read_block(m_block_list[bi]); if (!block) { - kprintf("Ext2FSInode::write_bytes: read_block(%u) failed (lbi: %u)\n", block_list[bi], bi); + kprintf("Ext2FSInode::write_bytes: read_block(%u) failed (lbi: %u)\n", m_block_list[bi], bi); return -EIO; } } else @@ -595,9 +622,9 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, #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().write_block(block_list[bi], block); + bool success = fs().write_block(m_block_list[bi], block); if (!success) { - kprintf("Ext2FSInode::write_bytes: write_block(%u) failed (lbi: %u)\n", block_list[bi], bi); + kprintf("Ext2FSInode::write_bytes: write_block(%u) failed (lbi: %u)\n", m_block_list[bi], bi); ASSERT_NOT_REACHED(); return -EIO; } @@ -606,18 +633,10 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, 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; - fs().write_ext2_inode(index(), m_raw_inode); #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 - // NOTE: Make sure the cached block list is up to date! - m_block_list = move(block_list); - if (old_size != new_size) inode_size_changed(old_size, new_size); inode_contents_changed(offset, count, data); @@ -1330,7 +1349,7 @@ KResult Ext2FSInode::truncate(off_t size) LOCKER(m_lock); if ((off_t)m_raw_inode.i_size == size) return KSuccess; - m_raw_inode.i_size = size; + resize(size); set_metadata_dirty(true); return KSuccess; } diff --git a/Kernel/FileSystem/Ext2FileSystem.h b/Kernel/FileSystem/Ext2FileSystem.h index 6a88a60c9f..3443c77c9c 100644 --- a/Kernel/FileSystem/Ext2FileSystem.h +++ b/Kernel/FileSystem/Ext2FileSystem.h @@ -45,6 +45,7 @@ private: virtual KResult truncate(off_t) override; void populate_lookup_cache() const; + bool resize(qword); Ext2FS& fs(); const Ext2FS& fs() const; |