Better support for L1 indirection in ext2 driver

This commit is contained in:
Mark
2020-01-15 14:03:45 +02:00
parent b7c8ce1d2b
commit bf9bef247a
2 changed files with 110 additions and 47 deletions
+53 -27
View File
@@ -142,9 +142,7 @@ int ext2_free_block(struct fs *ext2, uint32_t block_no) {
}
int ext2_inode_alloc_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
if (index >= 12) {
panic("Not implemented\n");
}
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
int res;
uint32_t block_no;
@@ -154,35 +152,63 @@ int ext2_inode_alloc_block(struct fs *ext2, struct ext2_inode *inode, uint32_t i
return res;
}
// Write direct block list entry
inode->direct_blocks[index] = block_no;
if (index < 12) {
// Write direct block list entry
inode->direct_blocks[index] = block_no;
} else if (index < 12 + (sb->block_size / 4)) {
// Also allocate L1 indirection block if needed
char buf[sb->block_size];
if (!inode->l1_indirect_block) {
uint32_t l1_block;
if ((res = ext2_alloc_block(ext2, &l1_block)) < 0) {
return res;
}
inode->l1_indirect_block = l1_block;
_assert(inode->l1_indirect_block);
memset(buf, 0, sb->block_size);
} else {
// Read indirection block
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf)) < 0) {
return res;
}
}
((uint32_t *) buf)[index - 12] = block_no;
// Write the block back
if ((res = ext2_write_block(ext2, inode->l1_indirect_block, buf)) < 0) {
return res;
}
} else {
panic("Inode block index has too high indirection level\n");
}
// Flush changes to the device
return ext2_write_inode(ext2, inode, ino);
}
int ext2_free_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
if (index >= 12) {
panic("Not implemented\n");
}
// All sanity checks regarding whether the block is present
// at all are left to the caller
int res;
uint32_t block_no;
// Get block number
block_no = inode->direct_blocks[index];
// Free the block
if ((res = ext2_free_block(ext2, block_no)) < 0) {
return res;
}
// Write updated inode
inode->direct_blocks[index] = 0;
return ext2_write_inode(ext2, inode, ino);
}
//int ext2_free_inode_block(struct fs *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
// if (index >= 12) {
// panic("Not implemented\n");
// }
// // All sanity checks regarding whether the block is present
// // at all are left to the caller
//
// int res;
// uint32_t block_no;
//
// // Get block number
// block_no = inode->direct_blocks[index];
//
// // Free the block
// if ((res = ext2_free_block(ext2, block_no)) < 0) {
// return res;
// }
//
// // Write updated inode
// inode->direct_blocks[index] = 0;
// return ext2_write_inode(ext2, inode, ino);
//}
int ext2_free_inode(struct fs *ext2, uint32_t ino) {
_assert(ino);
+57 -20
View File
@@ -141,8 +141,13 @@ static int ext2_vnode_open(struct ofile *fd, int opt) {
_assert(fd->vnode);
_assert(fd->vnode->type == VN_REG);
// TODO: O_APPEND should place fd->pos at the end of the file
fd->pos = 0;
if (opt & O_APPEND) {
struct ext2_inode *inode = (struct ext2_inode *) fd->vnode->fs_data;
_assert(inode);
fd->pos = inode->size_lower;
} else {
fd->pos = 0;
}
return 0;
}
@@ -312,7 +317,7 @@ static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) {
for (size_t i = 0; i < nblocks; ++i) {
if (ext2_read_inode_block(vn->fs, inode, i + block_number, block_buffer) < 0) {
kerror("Failed to read inode %d block #%zu\n", vn->ino, i + block_number);
kerror("Failed to read inode %d block #%u\n", vn->ino, i + block_number);
return -EIO;
}
if (i == 0) {
@@ -358,7 +363,7 @@ static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count)
size_t pos_in_block = fd->pos % sb->block_size;
size_t need_write = MIN(remaining, sb->block_size - pos_in_block);
kdebug("Write %zuB to block %zu offset %zu\n", need_write, block_index, pos_in_block);
kdebug("Write %uB to block %u offset %u\n", need_write, block_index, pos_in_block);
if (need_write == sb->block_size) {
// Can write block without reading it
// TODO: implement this
@@ -447,17 +452,33 @@ static int ext2_vnode_truncate(struct vnode *vn, size_t length) {
ssize_t delta_blocks = now_blocks - was_blocks;
if (delta_blocks < 0) {
int ind1 = 0;
char buf1[sb->block_size];
// Free truncated blocks
// XXX: reverse the loop
for (size_t i = now_blocks; i < was_blocks; ++i) {
// Modify inode right here because ext2_free_inode_block will
// flush these changes to disk so we don't have to write it
// twice
inode->size_lower -= sb->block_size;
if ((res = ext2_free_inode_block(ext2, inode, vn->ino, i)) < 0) {
// Put the block size back, couldn't free it
inode->size_lower += sb->block_size;
return res;
if (i < 12) {
if ((res = ext2_free_block(ext2, inode->direct_blocks[i])) < 0) {
panic("Failed to release inode block\n");
}
inode->direct_blocks[i] = 0;
} else if (i < 12 + (sb->block_size / 4)) {
if (!ind1) {
// Read indirection block only once
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf1)) < 0) {
// TODO: rollback inode struct and return error
panic("Failed to read indirection block\n");
}
ind1 = 1;
}
if ((res = ext2_free_block(ext2, ((uint32_t *) buf1)[i - 12])) < 0) {
panic("Failed to release inode block\n");
}
((uint32_t *) buf1)[i - 12] = 0;
} else {
panic("Inode block index has too high indirection level\n");
}
}
@@ -654,18 +675,34 @@ static int ext2_vnode_unlink(struct vnode *node) {
// Free blocks used by the inode - truncate the file to zero
size_t nblocks = (inode->size_lower + sb->block_size - 1) / sb->block_size;
int ind1 = 0;
char buf1[sb->block_size];
inode->size_lower = nblocks * sb->block_size;
for (ssize_t i = nblocks - 1; i >= 0; --i) {
inode->size_lower -= sb->block_size;
if ((res = ext2_free_inode_block(ext2, inode, ino, i)) < 0) {
return res;
for (size_t i = 0; i < nblocks; ++i) {
if (i < 12) {
if ((res = ext2_free_block(ext2, inode->direct_blocks[i])) < 0) {
panic("Failed to release inode block\n");
}
inode->direct_blocks[i] = 0;
} else if (i < 12 + (sb->block_size / 4)) {
if (!ind1) {
// Read indirection block only once
if ((res = ext2_read_block(ext2, inode->l1_indirect_block, buf1)) < 0) {
// TODO: rollback inode struct and return error
panic("Failed to read indirection block\n");
}
ind1 = 1;
}
if ((res = ext2_free_block(ext2, ((uint32_t *) buf1)[i - 12])) < 0) {
panic("Failed to release inode block\n");
}
((uint32_t *) buf1)[i - 12] = 0;
} else {
panic("Inode block index has too high indirection level\n");
}
}
// inode->size_lower is now 0
_assert(inode->size_lower == 0);
// Free the inode itself
if ((res = ext2_free_inode(ext2, ino)) < 0) {
return res;