From 2b8ade8a1321f2d83e0317367a3bd107759bae40 Mon Sep 17 00:00:00 2001 From: tsatke Date: Sat, 22 Jun 2024 22:40:57 +0200 Subject: [PATCH] add support for growing files with writes that need to allocate new blocks --- ext2/src/bytefield.rs | 18 ++++++++++++++++++ ext2/src/inode.rs | 37 ++++++++++--------------------------- ext2/src/write.rs | 26 ++++++++++++++++++-------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/ext2/src/bytefield.rs b/ext2/src/bytefield.rs index 81bba2b..c2039d0 100644 --- a/ext2/src/bytefield.rs +++ b/ext2/src/bytefield.rs @@ -33,6 +33,18 @@ macro_rules! bytefield_field_read { t } }; + ([u32; $len:literal], $offset:literal, $source:expr) => { $crate::bytefield_field_read!(u32_array [u32; $len], $offset, $source) }; + (u32_array $typ:ty, $offset:literal, $source:expr) => { + { + check_is_implemented!($typ, Default); + let mut t: $typ = Default::default(); + let source_bytes = &$source[$offset..$offset + core::mem::size_of::<$typ>()]; + source_bytes.chunks_exact(4).enumerate().for_each(|(i, chunk)| { + t[i] = u32::from_le_bytes(chunk.try_into().unwrap()); + }); + t + } + }; } #[macro_export] @@ -51,6 +63,12 @@ macro_rules! bytefield_field_write { (u8_array $typ:ty, $field:expr, $offset:literal, $target:expr) => { $target[$offset..$offset + core::mem::size_of::<$typ>()].copy_from_slice(&$field); }; + ([u32; $len:literal], $field:expr, $offset:literal, $target:expr) => { $crate::bytefield_field_write!(u32_array [u32; $len], $field, $offset, $target) }; + (u32_array $typ:ty, $field:expr, $offset:literal, $target:expr) => { + let field = $field; + let aligned = unsafe { field.align_to::().1 }; + $target[$offset..$offset + core::mem::size_of::<$typ>()].copy_from_slice(&aligned); + }; } #[macro_export] diff --git a/ext2/src/inode.rs b/ext2/src/inode.rs index ffd3906..36afb4e 100644 --- a/ext2/src/inode.rs +++ b/ext2/src/inode.rs @@ -115,18 +115,7 @@ bytefield! { num_disk_sectors: u32 = 28, flags: u32 = 32, os_val_1: [u8; 4] = 36, - direct_block_ptr_0: u32 = 40, - direct_block_ptr_1: u32 = 44, - direct_block_ptr_2: u32 = 48, - direct_block_ptr_3: u32 = 52, - direct_block_ptr_4: u32 = 56, - direct_block_ptr_5: u32 = 60, - direct_block_ptr_6: u32 = 64, - direct_block_ptr_7: u32 = 68, - direct_block_ptr_8: u32 = 72, - direct_block_ptr_9: u32 = 76, - direct_block_ptr_10: u32 = 80, - direct_block_ptr_11: u32 = 84, + direct_block_ptr: [u32; 12] = 40, singly_indirect_block_ptr: u32 = 88, doubly_indirect_block_ptr: u32 = 92, triply_indirect_block_ptr: u32 = 96, @@ -160,21 +149,15 @@ impl Inode { } pub fn direct_ptr(&self, index: usize) -> Option { - BlockAddress::new(match index { - 0 => self.direct_block_ptr_0, - 1 => self.direct_block_ptr_1, - 2 => self.direct_block_ptr_2, - 3 => self.direct_block_ptr_3, - 4 => self.direct_block_ptr_4, - 5 => self.direct_block_ptr_5, - 6 => self.direct_block_ptr_6, - 7 => self.direct_block_ptr_7, - 8 => self.direct_block_ptr_8, - 9 => self.direct_block_ptr_9, - 10 => self.direct_block_ptr_10, - 11 => self.direct_block_ptr_11, - _ => panic!("direct pointer {} does not exist", index), - }) + BlockAddress::new(self.direct_block_ptr[index]) + } + + pub fn direct_ptrs(&self) -> impl Iterator> + '_ { + self.direct_block_ptr.iter().map(|&ptr| BlockAddress::new(ptr)) + } + + pub fn set_direct_ptr(&mut self, index: usize, ptr: Option) { + self.direct_block_ptr[index] = ptr.map_or(0, |v| v.into_u32()); } pub fn single_indirect_ptr(&self) -> Option { diff --git a/ext2/src/write.rs b/ext2/src/write.rs index 59d9363..baaa17a 100644 --- a/ext2/src/write.rs +++ b/ext2/src/write.rs @@ -36,14 +36,24 @@ where for (i, block) in (start_block..=end_block).enumerate() { if !self.is_block_allocated(file, block)? { - todo!("allocate block") - // // TODO: we don't need to allocate if the full content of this block would be zero if the fs allows sparse files - // let free_block_address = self.allocate_block()?; - // if free_block_address.is_none() { - // return Err(Error::NoSpace); - // } - // let free_block_address = free_block_address.unwrap(); - // // TODO: write the block address to the inode + // TODO: we don't need to allocate if the full content of this block would be zero if the fs allows sparse files + let free_block_address = self.allocate_block()?; + if free_block_address.is_none() { + return Err(Error::NoSpace); + } + let free_block_address = free_block_address.unwrap(); + + let inode = file.inode_mut(); + let free_slot = inode.direct_ptrs() + .enumerate() + .find(|(_, ptr)| ptr.is_none()) + .map(|(i, _)| i); + if let Some(free_slot) = free_slot { + inode.set_direct_ptr(free_slot, Some(free_block_address)); + self.write_inode(file.inode_address(), file)?; + } else { + todo!("allocate indirect block"); + } } }