Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
feat(fs): update file traits to support write operations
Browse files Browse the repository at this point in the history
  • Loading branch information
RatCornu committed Jan 26, 2024
1 parent dba6601 commit 24cff84
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl_device!(&mut [T]);
impl_device!(Vec<T>);
impl_device!(Box<[T]>);

impl<E: core::error::Error, T: Base<Error = E> + Read + Write + Seek> Device<u8, E> for RefCell<T> {
impl<E: core::error::Error, T: Base<IOError = E> + Read + Write + Seek> Device<u8, E> for RefCell<T> {
#[inline]
fn size(&self) -> Size {
let mut device = self.borrow_mut();
Expand Down
41 changes: 35 additions & 6 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ pub struct DirectoryEntry<'path, Dir: Directory> {
/// Defined in [this POSIX definition](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_129).
pub trait Directory: Sized + File {
/// Type of the regular files in the [`FileSystem`](crate::fs::FileSystem) this directory belongs to.
type Regular: Regular;
type Regular: Regular<Error = Self::Error>;

/// Type of the symbolic links in the [`FileSystem`](crate::fs::FileSystem) this directory belongs to.
type SymbolicLink: SymbolicLink;
type SymbolicLink: SymbolicLink<Error = Self::Error>;

/// Type of the other files (if any) in the [`FileSystem`](crate::fs::FileSystem) this directory belongs to.
type File: File;
type File: File<Error = Self::Error>;

/// Returns the directory entries contained.
///
Expand All @@ -120,9 +120,27 @@ pub trait Directory: Sized + File {
/// # Errors
///
/// Returns an [`DevError`](crate::dev::error::DevError) if the device on which the directory is located could not be read.
#[allow(clippy::type_complexity)]
fn entries(&self) -> Result<Vec<DirectoryEntry<Self>>, Error<Self::Error>>;

/// Adds a new empty entry to the directory, meaning that a new file will be created.
///
/// # Errors
///
/// Returns an [`EntryAlreadyExist`](crate::fs::error::FsError::EntryAlreadyExist) error if the entry already exist.
///
/// Returns an [`DevError`](crate::dev::error::DevError) if the device on which the directory is located could not be written.
fn add_entry(&mut self, entry: DirectoryEntry<Self>) -> Result<(), Error<Self::Error>>;

/// Removes an entry from the directory.
///
/// # Errors
///
/// Returns an [`NotFound`](crate::fs::error::FsError::NotFound) error if there is no entry with the given name in this
/// directory.
///
/// Returns an [`DevError`](crate::dev::error::DevError) if the device on which the directory is located could not be written.
fn remove_entry(&mut self, name: UnixStr) -> Result<(), Error<Self::Error>>;

/// Returns the entry with the given name.
///
/// # Errors
Expand Down Expand Up @@ -156,8 +174,19 @@ pub trait Directory: Sized + File {
///
/// Defined in [this POSIX definition](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_381).
pub trait SymbolicLink: File {
/// Returns the string stored in this symbolic link
fn pointed_file(&self) -> &str;
/// Returns the string stored in this symbolic link.
///
/// # Errors
///
/// Returns an [`DevError`](crate::dev::error::DevError) if the device on which the directory is located could not be read.
fn get_pointed_file(&self) -> Result<&str, Error<Self::Error>>;

/// Sets the pointed file in this symbolic link.
///
/// # Errors
///
/// Returns an [`DevError`](crate::dev::error::DevError) if the device on which the directory is located could not be written.
fn set_pointed_file(&mut self, pointed_file: &str) -> Result<(), Error<Self::Error>>;
}

/// Enumeration of possible file types in a standard UNIX-like filesystem.
Expand Down
4 changes: 4 additions & 0 deletions src/fs/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use crate::file::Type;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub enum FsError<E: core::error::Error> {
/// Indicates that the given [`File`](crate::file::File) already exist in the given directory.
EntryAlreadyExist(String),

/// Indicates that the given [`Path`](crate::path::Path) is too long to be resolved.
NameTooLong(String),

Expand Down Expand Up @@ -38,6 +41,7 @@ impl<E: core::error::Error> Display for FsError<E> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EntryAlreadyExist(file) => write!(formatter, "Entry Already Exist: \"{file}\" already exist in given directory"),
Self::Loop(path) => write!(formatter, "Loop: a loop has been encountered during the resolution of \"{path}\""),
Self::NameTooLong(path) => write!(formatter, "Name too long: \"{path}\" is too long to be resolved"),
Self::NotDir(filename) => write!(formatter, "Not a Directory: \"{filename}\" is not a directory"),
Expand Down
2 changes: 1 addition & 1 deletion src/fs/ext2/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl<Dev: Device<u8, Ext2Error>> From<Block<Dev>> for u32 {
}

impl<Dev: Device<u8, Ext2Error>> Base for Block<Dev> {
type Error = Ext2Error;
type IOError = Ext2Error;
}

impl<Dev: Device<u8, Ext2Error>> Read for Block<Dev> {
Expand Down
108 changes: 81 additions & 27 deletions src/fs/ext2/file.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! Interface to manipulate UNIX files on an ext2 filesystem.

use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::Debug;
use core::mem::size_of;
use core::ops::{AddAssign, SubAssign};
use core::ptr::addr_of;
use core::ptr::{addr_of, addr_of_mut};
use core::slice::from_raw_parts;

use itertools::Itertools;
Expand Down Expand Up @@ -110,6 +111,35 @@ impl<D: Device<u8, Ext2Error>> File<D> {

Ok(())
}

/// General implementation of [`truncate`](file::Regular::truncate) for ext2's [`File`].
///
/// # Errors
///
/// Same as [`truncate`](file::Regular::truncate).
#[inline]
pub fn truncate(&mut self, size: u64) -> Result<(), Error<Ext2Error>> {
if u64::from(self.inode.size) <= size {
return Ok(());
}

let fs = self.filesystem.borrow();

let mut new_inode = self.inode;
// SAFETY: the result is smaller than `u32::MAX`
new_inode.size = unsafe { u32::try_from(u64::from(u32::MAX) & size).unwrap_unchecked() };

let starting_addr = Inode::starting_addr(&fs.device, fs.superblock(), self.inode_number)?;

// SAFETY: this writes an inode at the starting address of the inode
unsafe {
fs.device.borrow_mut().write_at(starting_addr, new_inode)?;
};

drop(fs);

self.update_inner_inode()
}
}

impl<D: Device<u8, Ext2Error>> file::File for File<D> {
Expand Down Expand Up @@ -160,12 +190,12 @@ pub struct Regular<D: Device<u8, Ext2Error>> {
}

impl<D: Device<u8, Ext2Error>> Base for File<D> {
type Error = Ext2Error;
type IOError = Ext2Error;
}

impl<D: Device<u8, Ext2Error>> Read for File<D> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::IOError>> {
let filesystem = self.filesystem.borrow();
self.inode
.read_data(&filesystem.device, &filesystem.superblock, buf, self.io_offset)
Expand Down Expand Up @@ -203,7 +233,7 @@ impl<D: Device<u8, Ext2Error>> Write for File<D> {
#[inline]
#[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)] // TODO: make this understandable for a human
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::IOError>> {
/// Writes the given `blocks` number in the indirect block with the number `block_number`.
fn write_indirect_block<D: Device<u8, Ext2Error>>(
filesystem: &Celled<Ext2<D>>,
Expand Down Expand Up @@ -783,7 +813,7 @@ impl<D: Device<u8, Ext2Error>> file::File for Regular<D> {
}

impl<D: Device<u8, Ext2Error>> Base for Regular<D> {
type Error = Ext2Error;
type IOError = Ext2Error;
}

impl<D: Device<u8, Ext2Error>> Read for Regular<D> {
Expand Down Expand Up @@ -815,26 +845,7 @@ impl<D: Device<u8, Ext2Error>> Seek for Regular<D> {
impl<D: Device<u8, Ext2Error>> file::Regular for Regular<D> {
#[inline]
fn truncate(&mut self, size: u64) -> Result<(), Error<<Self as file::File>::Error>> {
if u64::from(self.file.inode.size) <= size {
return Ok(());
}

let fs = self.file.filesystem.borrow();

let mut new_inode = self.file.inode;
// SAFETY: the result is smaller than `u32::MAX`
new_inode.size = unsafe { u32::try_from(u64::from(u32::MAX) & size).unwrap_unchecked() };

let starting_addr = Inode::starting_addr(&fs.device, fs.superblock(), self.file.inode_number)?;

// SAFETY: this writes an inode at the starting address of the inode
unsafe {
fs.device.borrow_mut().write_at(starting_addr, new_inode)?;
};

drop(fs);

self.file.update_inner_inode()
self.file.truncate(size)
}
}

Expand Down Expand Up @@ -908,6 +919,16 @@ impl<Dev: Device<u8, Ext2Error>> file::Directory for Directory<Dev> {

Ok(entries)
}

#[inline]
fn add_entry(&mut self, entry: DirectoryEntry<Self>) -> Result<(), Error<Self::Error>> {
todo!()
}

#[inline]
fn remove_entry(&mut self, entry_name: crate::path::UnixStr) -> Result<(), Error<Self::Error>> {
todo!()
}
}

/// Interface for ext2's symbolic links.
Expand Down Expand Up @@ -972,8 +993,41 @@ impl<D: Device<u8, Ext2Error>> file::File for SymbolicLink<D> {

impl<D: Device<u8, Ext2Error>> file::SymbolicLink for SymbolicLink<D> {
#[inline]
fn pointed_file(&self) -> &str {
&self.pointed_file
fn get_pointed_file(&self) -> Result<&str, Error<Self::Error>> {
Ok(&self.pointed_file)
}

#[inline]
fn set_pointed_file(&mut self, pointed_file: &str) -> Result<(), Error<Self::Error>> {
let bytes = pointed_file.as_bytes();

if bytes.len() > PATH_MAX {
Err(Error::Fs(FsError::NameTooLong(pointed_file.to_owned())))
} else if bytes.len() > SYMBOLIC_LINK_INODE_STORE_LIMIT {
self.file.seek(SeekFrom::Start(0))?;
self.file.write(bytes)?;
self.file.truncate(bytes.len() as u64)
} else {
let mut new_inode = self.file.inode;
// SAFETY: `bytes.len() < PATH_MAX << u32::MAX`
new_inode.size = unsafe { u32::try_from(bytes.len()).unwrap_unchecked() };

let data_ptr = addr_of_mut!(new_inode.blocks).cast::<u8>();
// SAFETY: there are `SYMBOLIC_LINK_INODE_STORE_LIMIT` bytes available to store the data
let data_slice = unsafe { core::slice::from_raw_parts_mut(data_ptr, bytes.len()) };
data_slice.clone_from_slice(bytes);

let fs = self.file.filesystem.borrow();
let starting_addr = Inode::starting_addr(&fs.device, fs.superblock(), self.file.inode_number)?;
// SAFETY: the starting address correspond to the one of this inode
unsafe {
fs.device.borrow_mut().write_at(starting_addr, new_inode)?;
};

drop(fs);

self.file.update_inner_inode()
}
}
}

Expand Down
12 changes: 3 additions & 9 deletions src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use core::str::FromStr;
use itertools::{Itertools, Position};

use crate::error::Error;
use crate::file::{Directory, File, Regular, SymbolicLink, TypeWithFile};
use crate::file::{Directory, SymbolicLink, TypeWithFile};
use crate::fs::error::FsError;
use crate::path::{Component, Path};

Expand Down Expand Up @@ -71,13 +71,7 @@ pub trait FileSystem<Dir: Directory> {
/// Auxiliary function used to store the visited symlinks during the pathname resolution to detect loops caused bt symbolic
/// links.
#[inline]
fn path_resolution<
E: core::error::Error,
R: Regular,
SL: SymbolicLink,
F: File,
D: Directory<Regular = R, SymbolicLink = SL, File = F, Error = E>,
>(
fn path_resolution<E: core::error::Error, D: Directory<Error = E>>(
fs: &impl FileSystem<D>,
path: &Path,
mut current_dir: D,
Expand Down Expand Up @@ -145,7 +139,7 @@ pub trait FileSystem<Dir: Directory> {
|| !trailing_blackslash
|| !symlink_resolution =>
{
let pointed_file = symlink.pointed_file().to_owned();
let pointed_file = symlink.get_pointed_file()?.to_owned();
if pointed_file.is_empty() {
return Err(Error::Fs(FsError::NoEnt(filename.to_string())));
};
Expand Down
Loading

0 comments on commit 24cff84

Please sign in to comment.