summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-04-28 22:07:25 +0200
committerAndreas Kling <awesomekling@gmail.com>2019-04-28 22:07:25 +0200
commit899f6a5de2037364ce809e38970d7bcb0d4ab56b (patch)
treed99138bb22b0c623a71ee003d24a9146bc0c2549 /Kernel
parent89df887e1fbbd7027a120d2682bca6d791456068 (diff)
downloadserenity-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.cpp79
-rw-r--r--Kernel/FileSystem/Ext2FileSystem.h1
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;