Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FsWrite: Writing Files

FsWrite provides write operations for files.

The Trait

#![allow(unused)]
fn main() {
pub trait FsWrite: Send + Sync {
    /// Write content to a file, creating it if it doesn't exist.
    fn write(&self, path: &Path, content: &[u8]) -> Result<(), FsError>;

    /// Remove a file.
    fn remove_file(&self, path: &Path) -> Result<(), FsError>;
}
}

Implementation

write - Write File Contents

#![allow(unused)]
fn main() {
use anyfs_backend::{FsWrite, FsError, FileType};

impl FsWrite for TutorialFs {
    fn write(&self, path: &Path, content: &[u8]) -> Result<(), FsError> {
        let path = Self::normalize_path(path);
        let mut inner = self.inner.write().unwrap();

        // Check parent directory exists
        if let Some(parent) = path.parent() {
            let parent = Self::normalize_path(parent);
            match inner.nodes.get(&parent) {
                None => {
                    return Err(FsError::NotFound { path: parent });
                }
                Some(node) if node.file_type != FileType::Directory => {
                    return Err(FsError::NotADirectory { path: parent });
                }
                _ => {}
            }
        }

        // Can't write to a directory
        if let Some(existing) = inner.nodes.get(&path) {
            if existing.file_type == FileType::Directory {
                return Err(FsError::IsADirectory { path });
            }
        }

        // Create or update the file
        let inode = if let Some(existing) = inner.nodes.get(&path) {
            existing.inode  // Reuse existing inode
        } else {
            Self::alloc_inode(&mut inner)
        };

        let mut node = FsNode::new_file(content.to_vec(), inode);
        node.modified = SystemTime::now();

        inner.inode_to_path.insert(inode, path.clone());
        inner.nodes.insert(path, node);

        Ok(())
    }
    // ...
}
}

Key points:

  • Verify parent directory exists before creating file
  • Reuse inode if file already exists (overwrite)
  • Update modification timestamp

remove_file - Delete a File

#![allow(unused)]
fn main() {
    fn remove_file(&self, path: &Path) -> Result<(), FsError> {
        let path = Self::normalize_path(path);
        let mut inner = self.inner.write().unwrap();

        let node = inner.nodes.get(&path)
            .ok_or_else(|| FsError::NotFound { path: path.clone() })?;

        // Can't remove directories with remove_file
        if node.file_type == FileType::Directory {
            return Err(FsError::IsADirectory { path });
        }

        let inode = node.inode;
        inner.nodes.remove(&path);
        inner.inode_to_path.remove(&inode);

        Ok(())
    }
}

Error Handling

SituationError
Parent directory doesn’t existFsError::NotFound { path: parent }
Parent path is a file, not directoryFsError::NotADirectory { path: parent }
Target path is a directoryFsError::IsADirectory { path }
File to remove doesn’t existFsError::NotFound { path }

Testing

#![allow(unused)]
fn main() {
#[test]
fn test_write_creates_file() {
    let fs = TutorialFs::new();
    
    fs.write(Path::new("/hello.txt"), b"Hello!").unwrap();
    
    let content = fs.read(Path::new("/hello.txt")).unwrap();
    assert_eq!(content, b"Hello!");
}

#[test]
fn test_write_overwrites_existing() {
    let fs = TutorialFs::new();
    
    fs.write(Path::new("/file.txt"), b"First").unwrap();
    fs.write(Path::new("/file.txt"), b"Second").unwrap();
    
    let content = fs.read(Path::new("/file.txt")).unwrap();
    assert_eq!(content, b"Second");
}

#[test]
fn test_write_to_nonexistent_parent_fails() {
    let fs = TutorialFs::new();
    
    let result = fs.write(Path::new("/no/such/dir/file.txt"), b"data");
    assert!(matches!(result, Err(FsError::NotFound { .. })));
}

#[test]
fn test_remove_file() {
    let fs = TutorialFs::new();
    
    fs.write(Path::new("/temp.txt"), b"temp").unwrap();
    assert!(fs.exists(Path::new("/temp.txt")));
    
    fs.remove_file(Path::new("/temp.txt")).unwrap();
    assert!(!fs.exists(Path::new("/temp.txt")));
}
}

Summary

FsWrite provides:

  • write() - Create or overwrite file contents
  • remove_file() - Delete a file

Next, we’ll implement FsDir →