diff --git a/trickfs/src/lib.rs b/trickfs/src/lib.rs index 9602c4d9..1a89a6c0 100644 --- a/trickfs/src/lib.rs +++ b/trickfs/src/lib.rs @@ -3,7 +3,7 @@ use std::{ ffi::{OsStr, OsString}, fmt, path::Path, - sync::LazyLock, + sync::{atomic::AtomicBool, Arc, LazyLock, Mutex}, time::{Duration, UNIX_EPOCH}, u64, }; @@ -302,10 +302,13 @@ pub struct Trick { /// Note that inodes are 1-based and this vector is 0-based. inodes: Vec, freelist: Vec, + trigger_enospc: Arc, } impl Trick { - pub fn new() -> Self { + fn new() -> (Self, TrickHandle) { + let trigger_enospc = Arc::new(AtomicBool::new(false)); + let tree = Tree::new(); let inodes = Vec::new(); let freelist = Vec::new(); @@ -313,13 +316,19 @@ impl Trick { tree, inodes, freelist, + trigger_enospc: trigger_enospc.clone(), }; // Initialize the root directory. Parent of the ROOT is ROOT. fs.register_inode(InodeData::new_dir(Inode::ROOT)); fs.tree .ino_to_container .insert(Inode::ROOT, Container::new()); - fs + + let handle = TrickHandle { + bg_sess: None.into(), + trigger_enospc, + }; + (fs, handle) } fn lookup_inode(&self, ino: Inode) -> Option<&InodeData> { @@ -474,6 +483,13 @@ impl fuser::Filesystem for Trick { ) { // we don't really care about these parameters. let _ = (mode, umask, flags); + if self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed) + { + reply.error(libc::ENOSPC); + return; + } let Some(parent_container) = self.tree.ino_to_container.get(&Inode(parent)) else { log::trace!("parent inode doesn't exist"); reply.error(libc::ENOENT); @@ -564,6 +580,9 @@ impl fuser::Filesystem for Trick { reply: fuser::ReplyWrite, ) { let _ = (fh, flags, write_flags, lock_owner); + let trigger_enospc = self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed); let Some(inode_data) = self.lookup_inode_mut(Inode(ino)) else { reply.error(libc::ENOENT); return; @@ -576,7 +595,12 @@ impl fuser::Filesystem for Trick { reply.error(libc::EFBIG); return; } + // Extension: if the file size is less than the new size, we should extend the file. if inode_data.size() < new_file_sz as u64 { + if trigger_enospc { + reply.error(libc::ENOSPC); + return; + } inode_data.set_size(new_file_sz as u64); } inode_data.content_mut()[offset..offset + len].copy_from_slice(data); @@ -593,6 +617,13 @@ impl fuser::Filesystem for Trick { reply: fuser::ReplyEntry, ) { let _ = (mode, umask); + if self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed) + { + reply.error(libc::ENOSPC); + return; + } let Some(inode_data) = self.lookup_inode_mut(Inode(parent)) else { reply.error(libc::ENOENT); return; @@ -753,14 +784,21 @@ impl fuser::Filesystem for Trick { reply: fuser::ReplyEmpty, ) { let _ = (fh, mode); + let trigger_enospc = self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed); let Some(inode_data) = self.lookup_inode_mut(Inode(ino)) else { reply.error(libc::ENOENT); return; }; // fallocate should preallocate stuff. We here just gon pretend that we are preallocating. - // we should set the length though. + // Here we should extend the file if the offset + length is greater than the current file. let new_size = u64::try_from(offset).unwrap() + u64::try_from(length).unwrap(); if inode_data.size() < new_size { + if trigger_enospc { + reply.error(libc::ENOSPC); + return; + } inode_data.set_size(new_size); } reply.ok(); @@ -776,6 +814,13 @@ impl fuser::Filesystem for Trick { ) { // fsync doesn't do anything since we are working in-memory, so just return OK. let _ = (ino, fh, datasync); + if self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed) + { + reply.error(libc::ENOSPC); + return; + } reply.ok(); } @@ -789,6 +834,13 @@ impl fuser::Filesystem for Trick { ) { // just like fsync, fsyncdir doesn't do anything. let _ = (req, ino, fh, datasync); + if self + .trigger_enospc + .load(std::sync::atomic::Ordering::Relaxed) + { + reply.error(libc::ENOSPC); + return; + } reply.ok(); } } @@ -798,13 +850,21 @@ fn has_odirect(flags: i32) -> bool { } pub struct TrickHandle { - bg_sess: fuser::BackgroundSession, + bg_sess: Mutex>, + trigger_enospc: Arc, } impl TrickHandle { /// Sets whether the file system should return ENOSPC on the subsequent write operations. + pub fn set_trigger_enospc(&self, on: bool) { + self.trigger_enospc + .store(on, std::sync::atomic::Ordering::Relaxed); + } + pub fn unmount_and_join(self) { - self.bg_sess.join(); + if let Some(bg_sess) = self.bg_sess.lock().unwrap().take() { + bg_sess.join(); + } } } @@ -818,9 +878,9 @@ pub fn spawn_trick>(mountpoint: P) -> std::io::Result