diff --git a/kernel/driver/fs/ext2/src/allocation.rs b/kernel/driver/fs/ext2/src/allocation.rs index bc061473..dcf5ea0a 100644 --- a/kernel/driver/fs/ext2/src/allocation.rs +++ b/kernel/driver/fs/ext2/src/allocation.rs @@ -1,6 +1,7 @@ -use libk::error::Error; +use libk::{error::Error, time::real_time}; +use yggdrasil_abi::io::FileType; -use crate::{BlockGroupDescriptor, Ext2Fs, ExtendedSuperblock}; +use crate::{BlockGroupDescriptor, Ext2Fs, ExtendedSuperblock, Inode}; impl Ext2Fs { async fn allocate< @@ -80,6 +81,57 @@ impl Ext2Fs { Ok(ino) } + pub(crate) async fn free_inode(&self, ino: u32, inode: &mut Inode) -> Result<(), Error> { + if ino == 0 { + log::warn!("Tried to free a NULL inode"); + return Ok(()); + } + + let is_directory = inode + .mode + .node_type() + .map(|m| m == FileType::Directory) + .unwrap_or(false); + + // Free inode blocks + inode.resize(self, 0).await?; + + inode.dtime = real_time().seconds as u32; + + self.write_inode(ino, &inode).await?; + + let inode_index = ino - 1; + let group_index = inode_index / self.block_group_inode_count; + + let bitmap = self + .with_bgdt_entry_mut(group_index, |descriptor| { + if is_directory { + descriptor.directories = descriptor.directories.saturating_sub(1); + } + descriptor.unallocated_inodes += 1; + Ok(descriptor.inode_usage_bitmap) + }) + .await?; + self.with_block_mut(bitmap, size_of::(), |bitmap| { + let index = (inode_index / 8) as usize; + let bit = 1u8 << (inode_index % 8); + + if bitmap[index] & bit == 0 { + log::warn!("Trying to free an unallocated inode #{ino}"); + } + bitmap[index] &= !bit; + Ok(()) + }) + .await?; + self.with_superblock_mut(|sb| { + sb.total_unallocated_inodes += 1; + Ok(()) + }) + .await?; + + Ok(()) + } + pub(crate) async fn allocate_block(&self) -> Result { let no = self .allocate( @@ -101,4 +153,38 @@ impl Ext2Fs { Ok(no) } + + pub(crate) async fn free_block(&self, block_index: u32) -> Result<(), Error> { + if block_index == 0 { + log::warn!("Tried to free a NULL block"); + return Ok(()); + } + + let group_index = block_index / self.block_group_block_count; + let bitmap = self + .with_bgdt_entry_mut(group_index, |descriptor| { + descriptor.unallocated_blocks += 1; + Ok(descriptor.block_usage_bitmap) + }) + .await?; + self.with_block_mut(bitmap, size_of::(), |bitmap| { + let index = (block_index / 8) as usize; + let bit = 1u8 << (block_index % 8); + + if bitmap[index] & bit == 0 { + log::warn!("Freeing a block #{block_index}, but bitmap says it's not allocated"); + } + bitmap[index] &= !bit; + + Ok(()) + }) + .await?; + self.with_superblock_mut(|sb| { + sb.total_unallocated_blocks += 1; + Ok(()) + }) + .await?; + + Ok(()) + } } diff --git a/kernel/driver/fs/ext2/src/dir/mod.rs b/kernel/driver/fs/ext2/src/dir/mod.rs index 910eee86..5f9cf7ab 100644 --- a/kernel/driver/fs/ext2/src/dir/mod.rs +++ b/kernel/driver/fs/ext2/src/dir/mod.rs @@ -215,7 +215,16 @@ impl DirectoryNode { .await; match result { - Ok(_) => Ok(()), + Ok(_) => { + // Remove "." link + self.inode + .map_mut(|inode| { + inode.hard_links -= 1; + Ok(()) + }) + .await?; + Ok(()) + } Err(error) => { self.locked.store(false, Ordering::Release); Err(error) @@ -228,16 +237,28 @@ impl DirectoryNode { Arc::as_ptr(&child.filesystem().unwrap()).addr(), Arc::as_ptr(&self.fs).addr() ); - let child_ino = match child.data_as_any() { + let (child_ino, is_directory) = match child.data_as_any() { data if let Some(dir) = data.downcast_ref::() => { // Check that the directory is not empty dir.prepare_for_removal().await?; - dir.inode.ino() + (dir.inode.ino(), true) + } + data if let Some(file) = data.downcast_ref::() => { + (file.inode.ino(), false) } - data if let Some(file) = data.downcast_ref::() => file.inode.ino(), _ => return Err(Error::InvalidOperation), }; + if is_directory { + // child's .. points to this node + self.inode + .map_mut(|inode| { + inode.hard_links = inode.hard_links.saturating_sub(1); + Ok(()) + }) + .await?; + } + let ino = self .inode .map_blocks_mut(|_inode, _index, block| { @@ -257,7 +278,7 @@ impl DirectoryNode { child .map_mut(|inode| { inode.hard_links = inode.hard_links.saturating_sub(1); - inode.dtime = real_time().seconds as u32; + log::info!("#{ino} hard links = {}", inode.hard_links); Ok(()) }) .await?; diff --git a/kernel/driver/fs/ext2/src/dir/walk.rs b/kernel/driver/fs/ext2/src/dir/walk.rs index 12c6af7c..0a94cc4f 100644 --- a/kernel/driver/fs/ext2/src/dir/walk.rs +++ b/kernel/driver/fs/ext2/src/dir/walk.rs @@ -142,6 +142,9 @@ impl<'a> DirentIter<'a> { if entry_count >= entries.len() { break; } + if record.is_empty() { + continue; + } if let Some(name) = record .name() diff --git a/kernel/driver/fs/ext2/src/inode/cache.rs b/kernel/driver/fs/ext2/src/inode/cache.rs index f8a6d814..611bdeb1 100644 --- a/kernel/driver/fs/ext2/src/inode/cache.rs +++ b/kernel/driver/fs/ext2/src/inode/cache.rs @@ -48,7 +48,11 @@ impl InodeAccess { ) -> Result { let inode = self.inode_cache.get(self.ino).await?; let lock = inode.read(); - mapper(&lock.inode) + if lock.is_deleted() { + Err(Error::DoesNotExist) + } else { + mapper(&lock.inode) + } } pub async fn map_mut Result>( @@ -57,7 +61,11 @@ impl InodeAccess { ) -> Result { let inode = self.inode_cache.get(self.ino).await?; let mut lock = inode.write(); - let result = mapper(&mut lock.inode); + let result = if lock.is_deleted() { + Err(Error::DoesNotExist) + } else { + mapper(&mut lock.inode) + }; self.inode_cache.put(self.ino, &mut lock).await?; result } @@ -68,7 +76,11 @@ impl InodeAccess { ) -> Result { let inode = self.inode_cache.get(self.ino).await?; let lock = inode.read(); - mapper(&lock.inode).await + if lock.is_deleted() { + Err(Error::DoesNotExist) + } else { + mapper(&lock.inode).await + } } pub async fn amap_mut Result>( @@ -77,7 +89,11 @@ impl InodeAccess { ) -> Result { let inode = self.inode_cache.get(self.ino).await?; let mut lock = inode.write(); - let result = mapper(&mut lock.inode).await; + let result = if lock.is_deleted() { + Err(Error::DoesNotExist) + } else { + mapper(&mut lock.inode).await + }; self.inode_cache.put(self.ino, &mut lock).await?; result } @@ -257,11 +273,11 @@ impl InodeCache { ino: u32, inode: Arc>, ) -> Result<(), Error> { - let inode = inode.read(); + let mut inode = inode.write(); if inode.dirty { assert!(!self.synchronous); log::debug!("Flush dirty inode {ino}"); - self.fs.write_inode(ino, &inode.inode).await?; + self.fs.write_or_free_inode(ino, &mut inode.inode).await?; } Ok(()) } @@ -314,7 +330,7 @@ impl InodeCache { async fn put(&self, ino: u32, holder: &mut InodeHolder) -> Result<(), Error> { if self.synchronous { // Immediately write-back - self.fs.write_inode(ino, &holder.inode).await + self.fs.write_or_free_inode(ino, &mut holder.inode).await } else { // Mark dirty holder.dirty = true; diff --git a/kernel/driver/fs/ext2/src/inode/mod.rs b/kernel/driver/fs/ext2/src/inode/mod.rs index c5df947a..ec510959 100644 --- a/kernel/driver/fs/ext2/src/inode/mod.rs +++ b/kernel/driver/fs/ext2/src/inode/mod.rs @@ -12,18 +12,38 @@ pub mod cache; pub use cache::{InodeAccess, InodeCache, InodeHolder}; impl Inode { + #[inline(always)] + fn l0_capacity(blocks: u64) -> u64 { + blocks.min(DIRECT_BLOCK_COUNT as u64) + } + + #[inline(always)] + fn l1_capacity(fs: &Ext2Fs, blocks: u64) -> u64 { + blocks + .saturating_sub(DIRECT_BLOCK_COUNT as u64) + .min(fs.pointers_per_block as u64) + } + + #[inline(always)] + fn l2_capacity(fs: &Ext2Fs, blocks: u64) -> (u64, u64) { + let l2_l1 = blocks + .saturating_sub((DIRECT_BLOCK_COUNT + fs.pointers_per_block) as u64) + .min((fs.pointers_per_block * fs.pointers_per_block) as u64); + let l2_l0 = l2_l1.div_ceil(fs.pointers_per_block as u64); + (l2_l0, l2_l1) + } + async fn grow_direct( &mut self, fs: &Ext2Fs, old_capacity: u64, new_capacity: u64, ) -> Result<(), Error> { - let old_l0_capacity = old_capacity.min(DIRECT_BLOCK_COUNT as u64); - let new_l0_capacity = new_capacity.min(DIRECT_BLOCK_COUNT as u64); - - debug_assert!(old_l0_capacity <= new_l0_capacity); + let old_l0_capacity = Self::l0_capacity(old_capacity); + let new_l0_capacity = Self::l0_capacity(new_capacity); log::debug!("Grow L0: {old_l0_capacity} -> {new_l0_capacity}"); + debug_assert!(old_l0_capacity <= new_l0_capacity); for i in old_l0_capacity..new_l0_capacity { let i = i as usize; @@ -34,21 +54,37 @@ impl Inode { Ok(()) } + async fn shrink_direct( + &mut self, + fs: &Ext2Fs, + old_capacity: u64, + new_capacity: u64, + ) -> Result<(), Error> { + let old_l0_capacity = Self::l0_capacity(old_capacity); + let new_l0_capacity = Self::l0_capacity(new_capacity); + + log::debug!("Shrink L0: {old_l0_capacity} -> {new_l0_capacity}"); + debug_assert!(old_l0_capacity >= new_l0_capacity); + + for i in new_l0_capacity..old_l0_capacity { + let i = i as usize; + fs.free_block(self.blocks.direct_blocks[i]).await?; + self.blocks.direct_blocks[i] = 0; + } + + Ok(()) + } + async fn grow_l1( &mut self, fs: &Ext2Fs, old_capacity: u64, new_capacity: u64, ) -> Result<(), Error> { - let old_l1_capacity = old_capacity - .saturating_sub(DIRECT_BLOCK_COUNT as u64) - .min(fs.pointers_per_block as u64); - let new_l1_capacity = new_capacity - .saturating_sub(DIRECT_BLOCK_COUNT as u64) - .min(fs.pointers_per_block as u64); + let old_l1_capacity = Self::l1_capacity(fs, old_capacity); + let new_l1_capacity = Self::l1_capacity(fs, new_capacity); log::debug!("Grow L1: {old_l1_capacity} -> {new_l1_capacity}"); - debug_assert!(old_l1_capacity <= new_l1_capacity); if old_l1_capacity == 0 && new_l1_capacity != 0 { @@ -69,27 +105,48 @@ impl Inode { Ok(()) } + async fn shrink_l1( + &mut self, + fs: &Ext2Fs, + old_capacity: u64, + new_capacity: u64, + ) -> Result<(), Error> { + let old_l1_capacity = Self::l1_capacity(fs, old_capacity); + let new_l1_capacity = Self::l1_capacity(fs, new_capacity); + + log::debug!("Shrink L1: {old_l1_capacity} -> {new_l1_capacity}"); + debug_assert!(old_l1_capacity >= new_l1_capacity); + + for i in new_l1_capacity..old_l1_capacity { + debug_assert_ne!(self.blocks.indirect_block_l1, 0); + let i = i as usize; + let index = fs.remove_index(self.blocks.indirect_block_l1, i).await?; + fs.free_block(index).await?; + } + + if old_l1_capacity != 0 && new_l1_capacity == 0 { + // Free the L1 indirect block + fs.free_block(self.blocks.indirect_block_l1).await?; + self.blocks.indirect_block_l1 = 0; + } + + Ok(()) + } + async fn grow_l2( &mut self, fs: &Ext2Fs, old_capacity: u64, new_capacity: u64, ) -> Result<(), Error> { - let old_l2_capacity = (old_capacity as usize) - .saturating_sub(DIRECT_BLOCK_COUNT + fs.pointers_per_block) - .min(fs.pointers_per_block * fs.pointers_per_block); - let new_l2_capacity = (new_capacity as usize) - .saturating_sub(DIRECT_BLOCK_COUNT + fs.pointers_per_block) - .min(fs.pointers_per_block * fs.pointers_per_block); + let (old_l2_l0, old_l2_l1) = Self::l2_capacity(fs, old_capacity); + let (new_l2_l0, new_l2_l1) = Self::l2_capacity(fs, new_capacity); - let old_l2_l0 = old_l2_capacity.div_ceil(fs.pointers_per_block); - let new_l2_l0 = new_l2_capacity.div_ceil(fs.pointers_per_block); + log::debug!("Grow L2: {old_l2_l1} ({old_l2_l0} L2-1) -> {new_l2_l1} ({new_l2_l0} L2-1)"); + debug_assert!(old_l2_l1 <= new_l2_l1); + debug_assert!(old_l2_l0 <= new_l2_l0); - log::debug!( - "Grow L2: {old_l2_capacity} ({old_l2_l0} L2-1) -> {new_l2_capacity} ({new_l2_l0} L2-1)" - ); - - if old_l2_capacity == 0 && new_l2_capacity != 0 { + if old_l2_l1 == 0 && new_l2_l1 != 0 { // Allocate L2 indirect block let block = fs.allocate_block().await?; self.blocks.indirect_block_l2 = block; @@ -98,14 +155,16 @@ impl Inode { // Grow L2 direct-indirect block for i in old_l2_l0..new_l2_l0 { debug_assert_ne!(self.blocks.indirect_block_l2, 0); + let i = i as usize; let block = fs.allocate_block().await?; fs.write_index(self.blocks.indirect_block_l2, i, block) .await?; } // Grow L2 indirect-indirect blocks - for i in old_l2_capacity..new_l2_capacity { + for i in old_l2_l1..new_l2_l1 { debug_assert_ne!(self.blocks.indirect_block_l2, 0); + let i = i as usize; let l1i = i / fs.pointers_per_block; let l0i = i % fs.pointers_per_block; let indirect = fs.read_index(self.blocks.indirect_block_l2, l1i).await?; @@ -117,6 +176,48 @@ impl Inode { Ok(()) } + async fn shrink_l2( + &mut self, + fs: &Ext2Fs, + old_capacity: u64, + new_capacity: u64, + ) -> Result<(), Error> { + let (old_l2_l0, old_l2_l1) = Self::l2_capacity(fs, old_capacity); + let (new_l2_l0, new_l2_l1) = Self::l2_capacity(fs, new_capacity); + + log::debug!("Grow L2: {old_l2_l1} ({old_l2_l0} L2-1) -> {new_l2_l1} ({new_l2_l0} L2-1)"); + debug_assert!(old_l2_l1 >= new_l2_l1); + debug_assert!(old_l2_l0 >= new_l2_l0); + + // Shrink L2 indirect-indirect blocks + for i in new_l2_l1..old_l2_l1 { + debug_assert_ne!(self.blocks.indirect_block_l2, 0); + let i = i as usize; + let l1i = i / fs.pointers_per_block; + let l0i = i % fs.pointers_per_block; + let indirect = fs.read_index(self.blocks.indirect_block_l2, l1i).await?; + debug_assert_ne!(indirect, 0); + let block = fs.remove_index(indirect, l0i).await?; + fs.free_block(block).await?; + } + + // Shrink L2 direct-indirect block + for i in new_l2_l0..old_l2_l0 { + debug_assert_ne!(self.blocks.indirect_block_l2, 0); + let i = i as usize; + let block = fs.remove_index(self.blocks.indirect_block_l2, i).await?; + fs.free_block(block).await?; + } + + if new_l2_l1 == 0 && old_l2_l1 != 0 { + // Free L2 indirect block + fs.free_block(self.blocks.indirect_block_l2).await?; + self.blocks.indirect_block_l2 = 0; + } + + Ok(()) + } + fn set_size(&mut self, fs: &Ext2Fs, size: u64) { let block_count = size.div_ceil(fs.block_size as u64); @@ -164,7 +265,12 @@ impl Inode { self.grow_l2(fs, old_blocks, new_blocks).await?; } // Shrink - Ordering::Greater => todo!(), + Ordering::Greater => { + log::debug!("Shrink inode: {old_blocks} -> {new_blocks} blocks"); + self.shrink_l2(fs, old_blocks, new_blocks).await?; + self.shrink_l1(fs, old_blocks, new_blocks).await?; + self.shrink_direct(fs, old_blocks, new_blocks).await?; + } // No change Ordering::Equal => (), } diff --git a/kernel/driver/fs/ext2/src/lib.rs b/kernel/driver/fs/ext2/src/lib.rs index c077d7c5..5504a86c 100644 --- a/kernel/driver/fs/ext2/src/lib.rs +++ b/kernel/driver/fs/ext2/src/lib.rs @@ -4,6 +4,8 @@ extern crate alloc; +use core::mem; + use alloc::{boxed::Box, sync::Arc}; use async_trait::async_trait; use bytemuck::Zeroable; @@ -14,6 +16,7 @@ use inode::{InodeAccess, InodeCache}; use libk::{ device::block::{cache::DeviceMapper, BlockDevice}, error::Error, + time::real_time, vfs::{Filesystem, FilesystemMountOption, NodeRef}, }; use libk_util::OneTimeInit; @@ -343,6 +346,14 @@ impl Ext2Fs { Ok(()) } + pub async fn write_or_free_inode(&self, ino: u32, inode: &mut Inode) -> Result<(), Error> { + if inode.is_deleted() { + self.free_inode(ino, inode).await + } else { + self.write_inode(ino, inode).await + } + } + pub async fn read_inode_data( &self, inode: &Inode, @@ -426,6 +437,17 @@ impl Ext2Fs { .await } + async fn remove_index(&self, block_index: u32, index: usize) -> Result { + self.with_block_mut(block_index, size_of::(), |block| { + let indirect: &mut [u32] = unsafe { + core::slice::from_raw_parts_mut(block.as_mut_ptr().cast(), self.pointers_per_block) + }; + + Ok(mem::replace(&mut indirect[index], 0)) + }) + .await + } + async fn inode_block_index(&self, inode: &Inode, index: u32) -> Result { let mut index = index as usize; // L0 diff --git a/kernel/libk/src/vfs/node/ops.rs b/kernel/libk/src/vfs/node/ops.rs index 872e6425..7eb2ef9d 100644 --- a/kernel/libk/src/vfs/node/ops.rs +++ b/kernel/libk/src/vfs/node/ops.rs @@ -107,15 +107,12 @@ impl Node { let directory = self.as_directory()?; let node = directory.imp.create_node(self, info.ty)?; - // Fill out the node info - node.set_access( - Some(info.uid), - Some(info.gid), - Some(info.mode), - check.clone(), - )?; + self.create_node(node.clone(), info.name, check.clone())?; - self.create_node(node, info.name, check) + // Fill out the node info + node.set_access(Some(info.uid), Some(info.gid), Some(info.mode), check)?; + + Ok(node) } /// Attaches a pre-created node to its parent