mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 12:38:12 +00:00
Kernel: Implement triply indirect block support in Ext2FSInode
This commit is contained in:
parent
facd18113b
commit
800dca3834
4 changed files with 276 additions and 148 deletions
|
@ -78,6 +78,10 @@
|
||||||
#cmakedefine01 EXEC_DEBUG
|
#cmakedefine01 EXEC_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef EXT2_BLOCKLIST_DEBUG
|
||||||
|
#cmakedefine01 EXT2_BLOCKLIST_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EXT2_DEBUG
|
#ifndef EXT2_DEBUG
|
||||||
#cmakedefine01 EXT2_DEBUG
|
#cmakedefine01 EXT2_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -229,6 +229,193 @@ Ext2FS::BlockListShape Ext2FS::compute_block_list_shape(unsigned blocks) const
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KResult Ext2FSInode::write_indirect_block(BlockBasedFS::BlockIndex block, Span<BlockBasedFS::BlockIndex> blocks_indexes)
|
||||||
|
{
|
||||||
|
const auto entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
||||||
|
VERIFY(blocks_indexes.size() <= entries_per_block);
|
||||||
|
|
||||||
|
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
||||||
|
OutputMemoryStream stream { block_contents };
|
||||||
|
auto buffer = UserOrKernelBuffer::for_kernel_buffer(stream.data());
|
||||||
|
|
||||||
|
VERIFY(blocks_indexes.size() <= EXT2_ADDR_PER_BLOCK(&fs().super_block()));
|
||||||
|
for (unsigned i = 0; i < blocks_indexes.size(); ++i)
|
||||||
|
stream << blocks_indexes[i].value();
|
||||||
|
stream.fill_to_end(0);
|
||||||
|
|
||||||
|
return fs().write_block(block, buffer, stream.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult Ext2FSInode::grow_doubly_indirect_block(BlockBasedFS::BlockIndex block, size_t old_blocks_length, Span<BlockBasedFS::BlockIndex> blocks_indexes, Vector<Ext2FS::BlockIndex>& new_meta_blocks, unsigned& meta_blocks)
|
||||||
|
{
|
||||||
|
const auto entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
||||||
|
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block;
|
||||||
|
const auto old_indirect_blocks_length = divide_rounded_up(old_blocks_length, entries_per_block);
|
||||||
|
const auto new_indirect_blocks_length = divide_rounded_up(blocks_indexes.size(), entries_per_block);
|
||||||
|
VERIFY(blocks_indexes.size() > 0);
|
||||||
|
VERIFY(blocks_indexes.size() > old_blocks_length);
|
||||||
|
VERIFY(blocks_indexes.size() <= entries_per_doubly_indirect_block);
|
||||||
|
|
||||||
|
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
||||||
|
auto* block_as_pointers = (unsigned*)block_contents.data();
|
||||||
|
OutputMemoryStream stream { block_contents };
|
||||||
|
auto buffer = UserOrKernelBuffer::for_kernel_buffer(stream.data());
|
||||||
|
|
||||||
|
if (old_blocks_length > 0) {
|
||||||
|
auto result = fs().read_block(block, &buffer, fs().block_size());
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow the doubly indirect block.
|
||||||
|
for (unsigned i = 0; i < old_indirect_blocks_length; i++)
|
||||||
|
stream << block_as_pointers[i];
|
||||||
|
for (unsigned i = old_indirect_blocks_length; i < new_indirect_blocks_length; i++) {
|
||||||
|
auto new_block = new_meta_blocks.take_last().value();
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: grow_doubly_indirect_block() allocating indirect block {} at index {}", new_block, i);
|
||||||
|
stream << new_block;
|
||||||
|
meta_blocks++;
|
||||||
|
}
|
||||||
|
stream.fill_to_end(0);
|
||||||
|
|
||||||
|
// Write out the indirect blocks.
|
||||||
|
for (unsigned i = old_blocks_length / entries_per_block; i < new_indirect_blocks_length; i++) {
|
||||||
|
const auto offset_block = i * entries_per_block;
|
||||||
|
auto result = write_indirect_block(block_as_pointers[i], blocks_indexes.slice(offset_block, min(blocks_indexes.size() - offset_block, entries_per_block)));
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the doubly indirect block.
|
||||||
|
return fs().write_block(block, buffer, stream.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult Ext2FSInode::shrink_doubly_indirect_block(BlockBasedFS::BlockIndex block, size_t old_blocks_length, size_t new_blocks_length, unsigned& meta_blocks)
|
||||||
|
{
|
||||||
|
const auto entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
||||||
|
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block;
|
||||||
|
const auto old_indirect_blocks_length = divide_rounded_up(old_blocks_length, entries_per_block);
|
||||||
|
const auto new_indirect_blocks_length = divide_rounded_up(new_blocks_length, entries_per_block);
|
||||||
|
VERIFY(old_blocks_length > 0);
|
||||||
|
VERIFY(old_blocks_length >= new_blocks_length);
|
||||||
|
VERIFY(new_blocks_length <= entries_per_doubly_indirect_block);
|
||||||
|
|
||||||
|
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
||||||
|
auto* block_as_pointers = (unsigned*)block_contents.data();
|
||||||
|
auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast<u8*>(block_as_pointers));
|
||||||
|
auto result = fs().read_block(block, &buffer, fs().block_size());
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Free the unused indirect blocks.
|
||||||
|
for (unsigned i = new_indirect_blocks_length; i < old_indirect_blocks_length; i++) {
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: shrink_doubly_indirect_block() freeing indirect block {} at index {}", block_as_pointers[i], i);
|
||||||
|
auto result = fs().set_block_allocation_state(block_as_pointers[i], false);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
meta_blocks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the doubly indirect block if no longer needed.
|
||||||
|
if (new_blocks_length == 0) {
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: shrink_doubly_indirect_block() freeing doubly indirect block {}", block);
|
||||||
|
auto result = fs().set_block_allocation_state(block, false);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
meta_blocks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult Ext2FSInode::grow_triply_indirect_block(BlockBasedFS::BlockIndex block, size_t old_blocks_length, Span<BlockBasedFS::BlockIndex> blocks_indexes, Vector<Ext2FS::BlockIndex>& new_meta_blocks, unsigned& meta_blocks)
|
||||||
|
{
|
||||||
|
const auto entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
||||||
|
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block;
|
||||||
|
const auto entries_per_triply_indirect_block = entries_per_block * entries_per_block;
|
||||||
|
const auto old_doubly_indirect_blocks_length = divide_rounded_up(old_blocks_length, entries_per_doubly_indirect_block);
|
||||||
|
const auto new_doubly_indirect_blocks_length = divide_rounded_up(blocks_indexes.size(), entries_per_doubly_indirect_block);
|
||||||
|
VERIFY(blocks_indexes.size() > 0);
|
||||||
|
VERIFY(blocks_indexes.size() > old_blocks_length);
|
||||||
|
VERIFY(blocks_indexes.size() <= entries_per_triply_indirect_block);
|
||||||
|
|
||||||
|
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
||||||
|
auto* block_as_pointers = (unsigned*)block_contents.data();
|
||||||
|
OutputMemoryStream stream { block_contents };
|
||||||
|
auto buffer = UserOrKernelBuffer::for_kernel_buffer(stream.data());
|
||||||
|
|
||||||
|
if (old_blocks_length > 0) {
|
||||||
|
auto result = fs().read_block(block, &buffer, fs().block_size());
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow the triply indirect block.
|
||||||
|
for (unsigned i = 0; i < old_doubly_indirect_blocks_length; i++)
|
||||||
|
stream << block_as_pointers[i];
|
||||||
|
for (unsigned i = old_doubly_indirect_blocks_length; i < new_doubly_indirect_blocks_length; i++) {
|
||||||
|
auto new_block = new_meta_blocks.take_last().value();
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: grow_triply_indirect_block() allocating doubly indirect block {} at index {}", new_block, i);
|
||||||
|
stream << new_block;
|
||||||
|
meta_blocks++;
|
||||||
|
}
|
||||||
|
stream.fill_to_end(0);
|
||||||
|
|
||||||
|
// Write out the doubly indirect blocks.
|
||||||
|
for (unsigned i = old_blocks_length / entries_per_doubly_indirect_block; i < new_doubly_indirect_blocks_length; i++) {
|
||||||
|
const auto processed_blocks = i * entries_per_doubly_indirect_block;
|
||||||
|
const auto old_doubly_indirect_blocks_length = min(old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block);
|
||||||
|
const auto new_doubly_indirect_blocks_length = min(blocks_indexes.size() > processed_blocks ? blocks_indexes.size() - processed_blocks : 0, entries_per_doubly_indirect_block);
|
||||||
|
auto result = grow_doubly_indirect_block(block_as_pointers[i], old_doubly_indirect_blocks_length, blocks_indexes.slice(processed_blocks, new_doubly_indirect_blocks_length), new_meta_blocks, meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the triply indirect block.
|
||||||
|
return fs().write_block(block, buffer, stream.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
KResult Ext2FSInode::shrink_triply_indirect_block(BlockBasedFS::BlockIndex block, size_t old_blocks_length, size_t new_blocks_length, unsigned& meta_blocks)
|
||||||
|
{
|
||||||
|
const auto entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
||||||
|
const auto entries_per_doubly_indirect_block = entries_per_block * entries_per_block;
|
||||||
|
const auto entries_per_triply_indirect_block = entries_per_doubly_indirect_block * entries_per_block;
|
||||||
|
const auto old_triply_indirect_blocks_length = divide_rounded_up(old_blocks_length, entries_per_doubly_indirect_block);
|
||||||
|
const auto new_triply_indirect_blocks_length = new_blocks_length / entries_per_doubly_indirect_block;
|
||||||
|
VERIFY(old_blocks_length > 0);
|
||||||
|
VERIFY(old_blocks_length >= new_blocks_length);
|
||||||
|
VERIFY(new_blocks_length <= entries_per_triply_indirect_block);
|
||||||
|
|
||||||
|
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
||||||
|
auto* block_as_pointers = (unsigned*)block_contents.data();
|
||||||
|
auto buffer = UserOrKernelBuffer::for_kernel_buffer(reinterpret_cast<u8*>(block_as_pointers));
|
||||||
|
auto result = fs().read_block(block, &buffer, fs().block_size());
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Shrink the doubly indirect blocks.
|
||||||
|
for (unsigned i = new_triply_indirect_blocks_length; i < old_triply_indirect_blocks_length; i++) {
|
||||||
|
const auto processed_blocks = i * entries_per_doubly_indirect_block;
|
||||||
|
const auto old_doubly_indirect_blocks_length = min(old_blocks_length > processed_blocks ? old_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block);
|
||||||
|
const auto new_doubly_indirect_blocks_length = min(new_blocks_length > processed_blocks ? new_blocks_length - processed_blocks : 0, entries_per_doubly_indirect_block);
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: shrink_triply_indirect_block() shrinking doubly indirect block {} at index {}", block_as_pointers[i], i);
|
||||||
|
auto result = shrink_doubly_indirect_block(block_as_pointers[i], old_doubly_indirect_blocks_length, new_doubly_indirect_blocks_length, meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the triply indirect block if no longer needed.
|
||||||
|
if (new_blocks_length == 0) {
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: shrink_triply_indirect_block() freeing triply indirect block {}", block);
|
||||||
|
auto result = fs().set_block_allocation_state(block, false);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
meta_blocks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
KResult Ext2FSInode::flush_block_list()
|
KResult Ext2FSInode::flush_block_list()
|
||||||
{
|
{
|
||||||
LOCKER(m_lock);
|
LOCKER(m_lock);
|
||||||
|
@ -241,10 +428,10 @@ KResult Ext2FSInode::flush_block_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not.
|
// NOTE: There is a mismatch between i_blocks and blocks.size() since i_blocks includes meta blocks and blocks.size() does not.
|
||||||
auto old_block_count = ceil_div(static_cast<size_t>(m_raw_inode.i_size), fs().block_size());
|
const auto old_block_count = ceil_div(static_cast<size_t>(m_raw_inode.i_size), fs().block_size());
|
||||||
|
|
||||||
auto old_shape = fs().compute_block_list_shape(old_block_count);
|
auto old_shape = fs().compute_block_list_shape(old_block_count);
|
||||||
auto new_shape = fs().compute_block_list_shape(m_block_list.size());
|
const auto new_shape = fs().compute_block_list_shape(m_block_list.size());
|
||||||
|
|
||||||
Vector<Ext2FS::BlockIndex> new_meta_blocks;
|
Vector<Ext2FS::BlockIndex> new_meta_blocks;
|
||||||
if (new_shape.meta_blocks > old_shape.meta_blocks) {
|
if (new_shape.meta_blocks > old_shape.meta_blocks) {
|
||||||
|
@ -255,11 +442,14 @@ KResult Ext2FSInode::flush_block_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_raw_inode.i_blocks = (m_block_list.size() + new_shape.meta_blocks) * (fs().block_size() / 512);
|
m_raw_inode.i_blocks = (m_block_list.size() + new_shape.meta_blocks) * (fs().block_size() / 512);
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() old shape=({};{};{};{}:{}), new shape=({};{};{};{}:{})", old_shape.direct_blocks, old_shape.indirect_blocks, old_shape.doubly_indirect_blocks, old_shape.triply_indirect_blocks, old_shape.meta_blocks, new_shape.direct_blocks, new_shape.indirect_blocks, new_shape.doubly_indirect_blocks, new_shape.triply_indirect_blocks, new_shape.meta_blocks);
|
||||||
bool inode_dirty = false;
|
|
||||||
|
|
||||||
unsigned output_block_index = 0;
|
unsigned output_block_index = 0;
|
||||||
unsigned remaining_blocks = m_block_list.size();
|
unsigned remaining_blocks = m_block_list.size();
|
||||||
|
|
||||||
|
// Deal with direct blocks.
|
||||||
|
bool inode_dirty = false;
|
||||||
|
VERIFY(new_shape.direct_blocks <= EXT2_NDIR_BLOCKS);
|
||||||
for (unsigned i = 0; i < new_shape.direct_blocks; ++i) {
|
for (unsigned i = 0; i < new_shape.direct_blocks; ++i) {
|
||||||
if (m_raw_inode.i_block[i] != m_block_list[output_block_index])
|
if (m_raw_inode.i_block[i] != m_block_list[output_block_index])
|
||||||
inode_dirty = true;
|
inode_dirty = true;
|
||||||
|
@ -274,160 +464,88 @@ KResult Ext2FSInode::flush_block_list()
|
||||||
dbgln(" + {}", m_block_list[i]);
|
dbgln(" + {}", m_block_list[i]);
|
||||||
}
|
}
|
||||||
set_metadata_dirty(true);
|
set_metadata_dirty(true);
|
||||||
inode_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!remaining_blocks)
|
// Deal with indirect blocks.
|
||||||
return KSuccess;
|
if (old_shape.indirect_blocks != new_shape.indirect_blocks) {
|
||||||
|
if (new_shape.indirect_blocks > old_shape.indirect_blocks) {
|
||||||
|
// Write out the indirect block.
|
||||||
|
if (old_shape.indirect_blocks == 0) {
|
||||||
|
auto new_block = new_meta_blocks.take_last().value();
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() allocating indirect block: {}", new_block);
|
||||||
|
m_raw_inode.i_block[EXT2_IND_BLOCK] = new_block;
|
||||||
|
set_metadata_dirty(true);
|
||||||
|
old_shape.meta_blocks++;
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned entries_per_block = EXT2_ADDR_PER_BLOCK(&fs().super_block());
|
auto result = write_indirect_block(m_raw_inode.i_block[EXT2_IND_BLOCK], m_block_list.span().slice(output_block_index, new_shape.indirect_blocks));
|
||||||
|
if (result.is_error())
|
||||||
bool ind_block_new = !m_raw_inode.i_block[EXT2_IND_BLOCK];
|
|
||||||
if (ind_block_new) {
|
|
||||||
Ext2FS::BlockIndex new_indirect_block = new_meta_blocks.take_last();
|
|
||||||
if (m_raw_inode.i_block[EXT2_IND_BLOCK] != new_indirect_block)
|
|
||||||
inode_dirty = true;
|
|
||||||
m_raw_inode.i_block[EXT2_IND_BLOCK] = new_indirect_block.value();
|
|
||||||
if (inode_dirty) {
|
|
||||||
dbgln_if(EXT2_DEBUG, "Ext2FS: Adding the indirect block to i_block array of inode {}", index());
|
|
||||||
set_metadata_dirty(true);
|
|
||||||
inode_dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_shape.indirect_blocks == new_shape.indirect_blocks) {
|
|
||||||
// No need to update the singly indirect block array.
|
|
||||||
remaining_blocks -= new_shape.indirect_blocks;
|
|
||||||
output_block_index += new_shape.indirect_blocks;
|
|
||||||
} else {
|
|
||||||
auto block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
|
||||||
OutputMemoryStream stream { block_contents };
|
|
||||||
|
|
||||||
VERIFY(new_shape.indirect_blocks <= entries_per_block);
|
|
||||||
for (unsigned i = 0; i < new_shape.indirect_blocks; ++i) {
|
|
||||||
stream << m_block_list[output_block_index++].value();
|
|
||||||
--remaining_blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.fill_to_end(0);
|
|
||||||
|
|
||||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(stream.data());
|
|
||||||
auto result = fs().write_block(m_raw_inode.i_block[EXT2_IND_BLOCK], buffer, stream.size());
|
|
||||||
if (result.is_error())
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!remaining_blocks)
|
|
||||||
return KSuccess;
|
|
||||||
|
|
||||||
bool dind_block_dirty = false;
|
|
||||||
|
|
||||||
bool dind_block_new = !m_raw_inode.i_block[EXT2_DIND_BLOCK];
|
|
||||||
if (dind_block_new) {
|
|
||||||
Ext2FS::BlockIndex new_dindirect_block = new_meta_blocks.take_last();
|
|
||||||
if (m_raw_inode.i_block[EXT2_DIND_BLOCK] != new_dindirect_block)
|
|
||||||
inode_dirty = true;
|
|
||||||
m_raw_inode.i_block[EXT2_DIND_BLOCK] = new_dindirect_block.value();
|
|
||||||
if (inode_dirty) {
|
|
||||||
dbgln_if(EXT2_DEBUG, "Ext2FS: Adding the doubly-indirect block to i_block array of inode {}", index());
|
|
||||||
set_metadata_dirty(true);
|
|
||||||
inode_dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_shape.doubly_indirect_blocks == new_shape.doubly_indirect_blocks) {
|
|
||||||
// No need to update the doubly indirect block data.
|
|
||||||
remaining_blocks -= new_shape.doubly_indirect_blocks;
|
|
||||||
output_block_index += new_shape.doubly_indirect_blocks;
|
|
||||||
} else {
|
|
||||||
unsigned indirect_block_count = divide_rounded_up(new_shape.doubly_indirect_blocks, entries_per_block);
|
|
||||||
|
|
||||||
auto dind_block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
|
||||||
if (dind_block_new) {
|
|
||||||
dind_block_contents.zero_fill();
|
|
||||||
dind_block_dirty = true;
|
|
||||||
} else {
|
|
||||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(dind_block_contents.data());
|
|
||||||
auto result = fs().read_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], &buffer, fs().block_size());
|
|
||||||
if (result.is_error()) {
|
|
||||||
dbgln("Ext2FS: write_block_list_for_inode had error: {}", result.error());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
} else if ((new_shape.indirect_blocks == 0) && (old_shape.indirect_blocks != 0)) {
|
||||||
}
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() freeing indirect block: {}", m_raw_inode.i_block[EXT2_IND_BLOCK]);
|
||||||
auto* dind_block_as_pointers = (unsigned*)dind_block_contents.data();
|
auto result = fs().set_block_allocation_state(m_raw_inode.i_block[EXT2_IND_BLOCK], false);
|
||||||
|
if (result.is_error())
|
||||||
VERIFY(indirect_block_count <= entries_per_block);
|
return result;
|
||||||
for (unsigned i = 0; i < indirect_block_count; ++i) {
|
old_shape.meta_blocks--;
|
||||||
bool ind_block_dirty = false;
|
|
||||||
|
|
||||||
Ext2FS::BlockIndex indirect_block_index = dind_block_as_pointers[i];
|
|
||||||
|
|
||||||
bool ind_block_new = !indirect_block_index;
|
|
||||||
if (ind_block_new) {
|
|
||||||
indirect_block_index = new_meta_blocks.take_last();
|
|
||||||
dind_block_as_pointers[i] = indirect_block_index.value();
|
|
||||||
dind_block_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ind_block_contents = ByteBuffer::create_uninitialized(fs().block_size());
|
|
||||||
if (ind_block_new) {
|
|
||||||
ind_block_contents.zero_fill();
|
|
||||||
ind_block_dirty = true;
|
|
||||||
} else {
|
|
||||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(ind_block_contents.data());
|
|
||||||
auto result = fs().read_block(indirect_block_index, &buffer, fs().block_size());
|
|
||||||
if (result.is_error()) {
|
|
||||||
dbgln("Ext2FS: write_block_list_for_inode had error: {}", result.error());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto* ind_block_as_pointers = (unsigned*)ind_block_contents.data();
|
|
||||||
|
|
||||||
unsigned entries_to_write = new_shape.doubly_indirect_blocks - (i * entries_per_block);
|
|
||||||
if (entries_to_write > entries_per_block)
|
|
||||||
entries_to_write = entries_per_block;
|
|
||||||
|
|
||||||
VERIFY(entries_to_write <= entries_per_block);
|
|
||||||
for (unsigned j = 0; j < entries_to_write; ++j) {
|
|
||||||
Ext2FS::BlockIndex output_block = m_block_list[output_block_index++];
|
|
||||||
if (ind_block_as_pointers[j] != output_block) {
|
|
||||||
ind_block_as_pointers[j] = output_block.value();
|
|
||||||
ind_block_dirty = true;
|
|
||||||
}
|
|
||||||
--remaining_blocks;
|
|
||||||
}
|
|
||||||
for (unsigned j = entries_to_write; j < entries_per_block; ++j) {
|
|
||||||
if (ind_block_as_pointers[j] != 0) {
|
|
||||||
ind_block_as_pointers[j] = 0;
|
|
||||||
ind_block_dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ind_block_dirty) {
|
|
||||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(ind_block_contents.data());
|
|
||||||
int err = fs().write_block(indirect_block_index, buffer, fs().block_size());
|
|
||||||
VERIFY(err >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (unsigned i = indirect_block_count; i < entries_per_block; ++i) {
|
|
||||||
if (dind_block_as_pointers[i] != 0) {
|
|
||||||
dind_block_as_pointers[i] = 0;
|
|
||||||
dind_block_dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dind_block_dirty) {
|
|
||||||
auto buffer = UserOrKernelBuffer::for_kernel_buffer(dind_block_contents.data());
|
|
||||||
int err = fs().write_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], buffer, fs().block_size());
|
|
||||||
VERIFY(err >= 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remaining_blocks -= new_shape.indirect_blocks;
|
||||||
|
output_block_index += new_shape.indirect_blocks;
|
||||||
|
|
||||||
|
if (old_shape.doubly_indirect_blocks != new_shape.doubly_indirect_blocks) {
|
||||||
|
// Write out the doubly indirect block.
|
||||||
|
if (new_shape.doubly_indirect_blocks > old_shape.doubly_indirect_blocks) {
|
||||||
|
if (old_shape.doubly_indirect_blocks == 0) {
|
||||||
|
auto new_block = new_meta_blocks.take_last().value();
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() allocating doubly indirect block: {}", new_block);
|
||||||
|
m_raw_inode.i_block[EXT2_DIND_BLOCK] = new_block;
|
||||||
|
set_metadata_dirty(true);
|
||||||
|
old_shape.meta_blocks++;
|
||||||
|
}
|
||||||
|
auto result = grow_doubly_indirect_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], old_shape.doubly_indirect_blocks, m_block_list.span().slice(output_block_index, new_shape.doubly_indirect_blocks), new_meta_blocks, old_shape.meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
auto result = shrink_doubly_indirect_block(m_raw_inode.i_block[EXT2_DIND_BLOCK], old_shape.doubly_indirect_blocks, new_shape.doubly_indirect_blocks, old_shape.meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining_blocks -= new_shape.doubly_indirect_blocks;
|
||||||
|
output_block_index += new_shape.doubly_indirect_blocks;
|
||||||
|
|
||||||
|
if (old_shape.triply_indirect_blocks != new_shape.triply_indirect_blocks) {
|
||||||
|
// Write out the triply indirect block.
|
||||||
|
if (new_shape.triply_indirect_blocks > old_shape.triply_indirect_blocks) {
|
||||||
|
if (old_shape.triply_indirect_blocks == 0) {
|
||||||
|
auto new_block = new_meta_blocks.take_last().value();
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() allocating triply indirect block: {}", new_block);
|
||||||
|
m_raw_inode.i_block[EXT2_TIND_BLOCK] = new_block;
|
||||||
|
set_metadata_dirty(true);
|
||||||
|
old_shape.meta_blocks++;
|
||||||
|
}
|
||||||
|
auto result = grow_triply_indirect_block(m_raw_inode.i_block[EXT2_TIND_BLOCK], old_shape.triply_indirect_blocks, m_block_list.span().slice(output_block_index, new_shape.triply_indirect_blocks), new_meta_blocks, old_shape.meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
auto result = shrink_triply_indirect_block(m_raw_inode.i_block[EXT2_TIND_BLOCK], old_shape.triply_indirect_blocks, new_shape.triply_indirect_blocks, old_shape.meta_blocks);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining_blocks -= new_shape.triply_indirect_blocks;
|
||||||
|
output_block_index += new_shape.triply_indirect_blocks;
|
||||||
|
|
||||||
|
dbgln_if(EXT2_BLOCKLIST_DEBUG, "Ext2FS: flush_block_list() new meta blocks count at {}, expecting {}", old_shape.meta_blocks, new_shape.meta_blocks);
|
||||||
|
VERIFY(new_meta_blocks.size() == 0);
|
||||||
|
VERIFY(old_shape.meta_blocks == new_shape.meta_blocks);
|
||||||
if (!remaining_blocks)
|
if (!remaining_blocks)
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
|
|
||||||
// FIXME: Implement!
|
dbgln("we don't know how to write qind ext2fs blocks, they don't exist anyway!");
|
||||||
dbgln("we don't know how to write tind ext2fs blocks yet!");
|
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,11 @@ private:
|
||||||
KResult write_directory(const Vector<Ext2FSDirectoryEntry>&);
|
KResult write_directory(const Vector<Ext2FSDirectoryEntry>&);
|
||||||
bool populate_lookup_cache() const;
|
bool populate_lookup_cache() const;
|
||||||
KResult resize(u64);
|
KResult resize(u64);
|
||||||
|
KResult write_indirect_block(BlockBasedFS::BlockIndex, Span<BlockBasedFS::BlockIndex>);
|
||||||
|
KResult grow_doubly_indirect_block(BlockBasedFS::BlockIndex, size_t, Span<BlockBasedFS::BlockIndex>, Vector<BlockBasedFS::BlockIndex>&, unsigned&);
|
||||||
|
KResult shrink_doubly_indirect_block(BlockBasedFS::BlockIndex, size_t, size_t, unsigned&);
|
||||||
|
KResult grow_triply_indirect_block(BlockBasedFS::BlockIndex, size_t, Span<BlockBasedFS::BlockIndex>, Vector<BlockBasedFS::BlockIndex>&, unsigned&);
|
||||||
|
KResult shrink_triply_indirect_block(BlockBasedFS::BlockIndex, size_t, size_t, unsigned&);
|
||||||
KResult flush_block_list();
|
KResult flush_block_list();
|
||||||
Vector<BlockBasedFS::BlockIndex> compute_block_list() const;
|
Vector<BlockBasedFS::BlockIndex> compute_block_list() const;
|
||||||
Vector<BlockBasedFS::BlockIndex> compute_block_list_with_meta_blocks() const;
|
Vector<BlockBasedFS::BlockIndex> compute_block_list_with_meta_blocks() const;
|
||||||
|
|
|
@ -120,6 +120,7 @@ set(EVENT_DEBUG ON)
|
||||||
set(EVENTLOOP_DEBUG ON)
|
set(EVENTLOOP_DEBUG ON)
|
||||||
set(EXEC_DEBUG ON)
|
set(EXEC_DEBUG ON)
|
||||||
set(EXT2_DEBUG ON)
|
set(EXT2_DEBUG ON)
|
||||||
|
set(EXT2_BLOCKLIST_DEBUG ON)
|
||||||
set(EXT2_VERY_DEBUG ON)
|
set(EXT2_VERY_DEBUG ON)
|
||||||
set(GEMINI_DEBUG ON)
|
set(GEMINI_DEBUG ON)
|
||||||
set(GEMINIJOB_DEBUG ON)
|
set(GEMINIJOB_DEBUG ON)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue