fix(ext4): review fixes — bounds checks, delete safety, blocks_count, multi-extent dirs

This commit is contained in:
2026-10-18 23:28:34 +02:00
parent 73661e925d
commit 9be67e403b
2 changed files with 85 additions and 69 deletions

View File

@@ -249,34 +249,7 @@ impl<B: BlockDevice> Ext4<B> {
fn find_in_dir(&mut self, dir_inode: u32, name: &[u8]) -> Option<u32> {
let inode = self.read_inode(dir_inode)?;
if !inode.is_dir() { return None; }
let extent = self.read_extents(&inode)?;
let mut block_buf = [0u8; 4096];
for blk in 0..extent.length as u64 {
read_block(
&mut self.device,
extent.physical_block + blk,
self.sb.sectors_per_block(),
&mut block_buf,
);
let mut pos = 0;
while pos + 8 <= self.sb.block_size {
let entry_inode = read_u32(&block_buf, pos);
let rec_len = read_u16(&block_buf, pos + 4) as usize;
let name_len = block_buf[pos + 6] as usize;
if rec_len == 0 { break; }
if entry_inode != 0 && name_len == name.len() {
if &block_buf[pos + 8..pos + 8 + name_len] == name {
return Some(entry_inode);
}
}
pos += rec_len;
}
}
None
dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &inode, name)
}
fn resolve_path(&mut self, path: &[u8]) -> Option<u32> {
@@ -338,6 +311,19 @@ impl<B: BlockDevice> Ext4<B> {
true
}
fn count_extent_blocks(block_data: &[u8; 60]) -> u32 {
let magic = read_u16(block_data, 0);
if magic != EXTENT_MAGIC { return 0; }
let entries = read_u16(block_data, 2);
let mut total = 0u32;
for i in 0..entries as usize {
let off = 12 + i * 12;
if off + 12 > block_data.len() { break; }
total += read_u16(block_data, off + 4) as u32;
}
total
}
fn free_all_blocks(&mut self, inode: &Inode) {
if !inode.uses_extents() { return; }
let hdr = &inode.block_data;
@@ -373,6 +359,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
}
fn read(&mut self, handle: FileHandle, buf: &mut [u8]) -> Option<usize> {
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
let file = *self.open_files[handle.raw() as usize].as_ref()?;
if file.offset >= file.size { return Some(0); }
@@ -405,6 +392,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
}
fn write(&mut self, handle: FileHandle, buf: &[u8]) -> Option<usize> {
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
let file = *self.open_files[handle.raw() as usize].as_ref()?;
let bs = self.sb.block_size;
let mut written = 0;
@@ -424,7 +412,10 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
let phys_block = match self.map_logical_block(&tmp_inode, logical_block) {
Some(p) => p,
None => {
let new_block = self.alloc_block()?;
let new_block = match self.alloc_block() {
Some(b) => b,
None => break,
};
if !Self::append_extent(&mut block_data, logical_block, new_block as u64, 1) {
self.free_block(new_block);
break;
@@ -460,6 +451,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
};
inode.size = size as u64;
inode.block_data = block_data;
inode.blocks_count = Self::count_extent_blocks(&block_data) * (bs as u32 / 512);
self.write_inode(inode_num, &inode);
}
@@ -467,6 +459,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
}
fn close(&mut self, handle: FileHandle) {
if handle.raw() as usize >= MAX_OPEN_FILES { return; }
if let Some(file) = self.open_files[handle.raw() as usize].take() {
if let Some(mut inode) = self.read_inode(file.inode_num) {
inode.size = file.size as u64;
@@ -484,45 +477,15 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
};
let inode = self.read_inode(dir_inode_num)?;
if !inode.is_dir() { return None; }
let extent = self.read_extents(&inode)?;
let mut block_buf = [0u8; 4096];
let mut count = 0;
for blk in 0..extent.length as u64 {
read_block(
&mut self.device,
extent.physical_block + blk,
self.sb.sectors_per_block(),
&mut block_buf,
);
let mut pos = 0;
while pos + 8 <= self.sb.block_size && count < entries.len() {
let entry_inode = read_u32(&block_buf, pos);
let rec_len = read_u16(&block_buf, pos + 4) as usize;
let name_len = block_buf[pos + 6] as usize;
let file_type = block_buf[pos + 7];
if rec_len == 0 { break; }
if entry_inode != 0 && name_len > 0 {
let name = &block_buf[pos + 8..pos + 8 + name_len];
if name != b"." && name != b".." {
let child_size = if file_type == 1 {
self.read_inode(entry_inode)
.map(|i| i.size as usize)
.unwrap_or(0)
} else { 0 };
entries[count] = DirEntry {
name: FileName::from_bytes(name),
size: child_size,
is_dir: file_type == 2,
};
count += 1;
let count = dir::list_entries(&mut self.device, &self.sb, &inode, entries)?;
for i in 0..count {
if !entries[i].is_dir {
let name = entries[i].name;
if let Some(child_ino) = dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &inode, name.as_bytes()) {
if let Some(child) = self.read_inode(child_ino) {
entries[i].size = child.size as usize;
}
}
pos += rec_len;
}
}
Some(count)
@@ -568,21 +531,41 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
None => return false,
};
let child_inode_num = match dir::remove_entry(
&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename,
) {
let child_inode_num = match dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename) {
Some(n) => n,
None => return false,
};
if let Some(child_inode) = self.read_inode(child_inode_num) {
if child_inode.is_dir() {
return false;
}
}
let parent_inode = match self.read_inode(parent_inode_num) {
Some(i) => i,
None => return false,
};
if dir::remove_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, filename).is_none() {
return false;
}
if let Some(child_inode) = self.read_inode(child_inode_num) {
self.free_all_blocks(&child_inode);
}
self.free_inode(child_inode_num);
for slot in self.open_files.iter_mut() {
if let Some(f) = slot {
if f.inode_num == child_inode_num {
*slot = None;
}
}
}
true
}
fn file_size(&mut self, handle: FileHandle) -> Option<usize> {
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
self.open_files[handle.raw() as usize].as_ref().map(|f| f.size)
}