mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +00:00
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.
This commit is contained in:
parent
89df887e1f
commit
899f6a5de2
2 changed files with 50 additions and 30 deletions
|
@ -216,6 +216,10 @@ bool Ext2FS::write_block_list_for_inode(InodeIndex inode_index, ext2_inode& e2in
|
||||||
}
|
}
|
||||||
if (inode_dirty) {
|
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);
|
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);
|
write_ext2_inode(inode_index, e2inode);
|
||||||
inode_dirty = false;
|
inode_dirty = false;
|
||||||
}
|
}
|
||||||
|
@ -512,6 +516,40 @@ ssize_t Ext2FSInode::read_bytes(off_t offset, ssize_t count, byte* buffer, FileD
|
||||||
return nread;
|
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*)
|
ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data, FileDescriptor*)
|
||||||
{
|
{
|
||||||
ASSERT(offset >= 0);
|
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();
|
const ssize_t block_size = fs().block_size();
|
||||||
size_t old_size = size();
|
qword old_size = size();
|
||||||
size_t new_size = max(static_cast<size_t>(offset) + count, size());
|
qword new_size = max(static_cast<qword>(offset) + count, (qword)size());
|
||||||
|
|
||||||
unsigned blocks_needed_before = ceil_div(size(), block_size);
|
if (!resize(new_size))
|
||||||
unsigned blocks_needed_after = ceil_div(new_size, block_size);
|
return -EIO;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
int first_block_logical_index = offset / block_size;
|
int first_block_logical_index = offset / block_size;
|
||||||
int last_block_logical_index = (offset + count) / block_size;
|
int last_block_logical_index = (offset + count) / block_size;
|
||||||
if (last_block_logical_index >= block_list.size())
|
if (last_block_logical_index >= m_block_list.size())
|
||||||
last_block_logical_index = block_list.size() - 1;
|
last_block_logical_index = m_block_list.size() - 1;
|
||||||
|
|
||||||
int offset_into_first_block = offset % block_size;
|
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;
|
ByteBuffer block;
|
||||||
if (offset_into_block != 0 || num_bytes_to_copy != block_size) {
|
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) {
|
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;
|
return -EIO;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -595,9 +622,9 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const byte* data,
|
||||||
#ifdef EXT2_DEBUG
|
#ifdef EXT2_DEBUG
|
||||||
dbgprintf("Ext2FSInode::write_bytes: writing block %u (offset_into_block: %u)\n", block_list[bi], offset_into_block);
|
dbgprintf("Ext2FSInode::write_bytes: writing block %u (offset_into_block: %u)\n", block_list[bi], offset_into_block);
|
||||||
#endif
|
#endif
|
||||||
bool success = fs().write_block(block_list[bi], block);
|
bool success = fs().write_block(m_block_list[bi], block);
|
||||||
if (!success) {
|
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();
|
ASSERT_NOT_REACHED();
|
||||||
return -EIO;
|
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;
|
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
|
#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());
|
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
|
#endif
|
||||||
|
|
||||||
// NOTE: Make sure the cached block list is up to date!
|
|
||||||
m_block_list = move(block_list);
|
|
||||||
|
|
||||||
if (old_size != new_size)
|
if (old_size != new_size)
|
||||||
inode_size_changed(old_size, new_size);
|
inode_size_changed(old_size, new_size);
|
||||||
inode_contents_changed(offset, count, data);
|
inode_contents_changed(offset, count, data);
|
||||||
|
@ -1330,7 +1349,7 @@ KResult Ext2FSInode::truncate(off_t size)
|
||||||
LOCKER(m_lock);
|
LOCKER(m_lock);
|
||||||
if ((off_t)m_raw_inode.i_size == size)
|
if ((off_t)m_raw_inode.i_size == size)
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
m_raw_inode.i_size = size;
|
resize(size);
|
||||||
set_metadata_dirty(true);
|
set_metadata_dirty(true);
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ private:
|
||||||
virtual KResult truncate(off_t) override;
|
virtual KResult truncate(off_t) override;
|
||||||
|
|
||||||
void populate_lookup_cache() const;
|
void populate_lookup_cache() const;
|
||||||
|
bool resize(qword);
|
||||||
|
|
||||||
Ext2FS& fs();
|
Ext2FS& fs();
|
||||||
const Ext2FS& fs() const;
|
const Ext2FS& fs() const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue