This repository has been archived by the owner on Mar 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implementation of
pathname_resolution
- Loading branch information
Showing
8 changed files
with
632 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,24 @@ | ||
[package] | ||
name = "efs" | ||
version = "0.1.0" | ||
edition = "2021" | ||
authors = ["RatCornu <[email protected]>"] | ||
license = "MIT OR Apache-2.0" | ||
edition = "2021" | ||
description = "An OS and architecture independent implementation of some Unix filesystems in Rust." | ||
documentation = "https://ratcornu.github.io/efs/" | ||
readme = "README.md" | ||
homepage = "https://github.com/RatCornu/efs" | ||
repository = "https://github.com/RatCornu/efs" | ||
license = "MIT OR Apache-2.0" | ||
keywords = ["filesystem", "no-std"] | ||
categories = ["filesystem", "no-std"] | ||
|
||
[lib] | ||
name = "efs" | ||
path = "src/lib.rs" | ||
|
||
[dependencies] | ||
derive_more = "0.99" | ||
itertools = { version = "0.11", default-features = false, features = ["use_alloc"] } | ||
no_std_io = "0.6.0" | ||
once_cell = "1" | ||
regex = "1" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,16 @@ | ||
[![Build][build-badge]][build-link] | ||
[![Documentation][documentation-badge]][documentation-link] | ||
[![crates.io-badge]][crates.io-link] | ||
|
||
[build-badge]: https://github.com/RatCornu/efs/workflows/Build/badge.svg | ||
[documentation-badge]: https://github.com/RatCornu/efs/workflows/Documentation/badge.svg | ||
|
||
[build-link]: https://github.com/RatCornu/efs/actions?query=workflow:"Build" | ||
|
||
[documentation-badge]: https://github.com/RatCornu/efs/workflows/Documentation/badge.svg | ||
[documentation-link]: https://github.com/RatCornu/efs/actions?query=workflow:"Documentation" | ||
|
||
[crates.io-badge]: https://img.shields.io/crates/v/efs.svg | ||
[crates.io-link]: https://crates.io/crates/efs | ||
|
||
# Extended fs | ||
|
||
An OS and architecture independent implementation of some Unix filesystems in Rust. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
buildInputs = with pkgs; [ | ||
rust | ||
cargo | ||
cargo-deny | ||
clippy | ||
rustfmt | ||
alejandra | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
//! Interface for `efs` possible errors | ||
|
||
use core::error; | ||
use core::fmt::{self, Display}; | ||
|
||
use no_std_io::io; | ||
|
||
use crate::fs::FsError; | ||
use crate::path::PathError; | ||
|
||
/// Enumeration of possible sources of error | ||
#[allow(clippy::error_impl_error)] | ||
#[derive(Debug)] | ||
pub enum Error { | ||
/// I/O error | ||
IO(io::ErrorKind), | ||
|
||
/// Path error | ||
Path(PathError), | ||
|
||
/// Filesystem error | ||
Fs(FsError), | ||
} | ||
|
||
impl Display for Error { | ||
#[inline] | ||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::IO(io_error) => write!(formatter, "I/O Error: {io_error:?}"), | ||
Self::Path(path_error) => write!(formatter, "Path Error: {path_error}"), | ||
Self::Fs(fs_error) => write!(formatter, "Filesystem Error: {fs_error}"), | ||
} | ||
} | ||
} | ||
|
||
impl error::Error for Error {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,198 @@ | ||
//! General interface for filesystems | ||
|
||
use alloc::borrow::ToOwned; | ||
use alloc::boxed::Box; | ||
use alloc::string::{String, ToString}; | ||
use alloc::vec; | ||
use alloc::vec::Vec; | ||
use core::fmt::{self, Display}; | ||
use core::str::FromStr; | ||
|
||
use no_std_io::io::Result; | ||
use itertools::{Itertools, Position}; | ||
use no_std_io::io; | ||
|
||
use crate::file::{Directory, File}; | ||
use crate::path::Path; | ||
use crate::error::Error; | ||
use crate::file::{Directory, Type}; | ||
use crate::path::{Component, Path}; | ||
|
||
/// Maximal length for a path. | ||
/// | ||
/// This is defined in [this POSIX definition](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13). | ||
/// | ||
/// This value is the same as the one defined in [the linux's `limits.h` header](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/linux/limits.h?h=v6.5.8#n13). | ||
pub const PATH_MAX: usize = 4_096; | ||
|
||
/// Enumeration of possible errors encountered with [`FileSystem`]s' manipulation. | ||
#[allow(clippy::module_name_repetitions)] | ||
#[derive(Debug)] | ||
pub enum FsError { | ||
/// Indicates that the given [`Path`] is too long to be resolved | ||
NameTooLong(String), | ||
|
||
/// Indicates that the given filename is not a [`Directory`] | ||
NotDir(String), | ||
|
||
/// Indicates that the given filename is not a [`Directory`] | ||
NoEnt(String), | ||
|
||
/// Indicates that a loop has been encountered during the given path resolution | ||
Loop(String), | ||
} | ||
|
||
impl Display for FsError { | ||
#[inline] | ||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
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"), | ||
Self::NoEnt(filename) => write!(formatter, "No entry: \"{filename}\" is an symbolic link pointing at an empty string"), | ||
} | ||
} | ||
} | ||
|
||
/// A filesystem | ||
pub trait FileSystem { | ||
/// Returns the root directory of the filesystem. | ||
fn root(&self) -> Box<dyn Directory>; | ||
|
||
/// Returns the double slash root directory of the filesystem. | ||
/// | ||
/// If you do not have any idea of what this is, you are probably looking for [`root`](trait.FileSystem.html#tymethod.root). | ||
/// | ||
/// See [`Component::DoubleSlashRootDir`] and [`Path`] for more informations. | ||
fn double_slash_root(&self) -> Box<dyn Directory>; | ||
|
||
/// Performs a pathname resolution as described in [this POSIX definition](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13). | ||
/// | ||
/// Returns the file of this filesystem corresponding to the given [`Path`]. | ||
/// Returns the file of this filesystem corresponding to the given `path`, starting at the `current_dir`. | ||
/// | ||
/// `symlink_resolution` indicates whether the function calling this method is required to act on the symbolic link itself, or | ||
/// certain arguments direct that the function act on the symbolic link itself. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Returns an [`NotFound`](no_std_io::io::ErrorKind) if the given path does not leed to an existing path. | ||
/// Returns an [`NotFound`](no_std_io::io::ErrorKind) error if the given path does not leed to an existing path. | ||
/// | ||
/// Returns an [`NotDir`](enum.FsError.html#variant.NotDir) error if one of the components of the file is not a directory. | ||
#[inline] | ||
fn pathname_resolution(&self, _path: &Path) -> Result<Box<dyn File>> { | ||
todo!() | ||
fn pathname_resolution(&self, path: &Path, current_dir: Box<dyn Directory>, symlink_resolution: bool) -> Result<Type, Error> | ||
where | ||
Self: Sized, | ||
{ | ||
/// Auxiliary function used to store the visited symlinks during the pathname resolution to detect loops caused bt symbolic | ||
/// links. | ||
#[inline] | ||
fn inner_resolution( | ||
fs: &impl FileSystem, | ||
path: &Path, | ||
mut current_dir: Box<dyn Directory>, | ||
symlink_resolution: bool, | ||
mut visited_symlinks: Vec<String>, | ||
) -> Result<Type, Error> { | ||
let canonical_path = path.canonical(); | ||
let trailing_blackslash = canonical_path.as_unix_str().has_trailing_backslash(); | ||
let mut symlink_encountered = None; | ||
|
||
let mut components = canonical_path.components(); | ||
|
||
for (pos, comp) in components.with_position() { | ||
match comp { | ||
Component::RootDir => { | ||
if pos == Position::First || pos == Position::Only { | ||
current_dir = fs.root(); | ||
} else { | ||
unreachable!("The root directory cannot be encountered during the pathname resolution"); | ||
} | ||
}, | ||
Component::DoubleSlashRootDir => { | ||
if pos == Position::First || pos == Position::Only { | ||
current_dir = fs.double_slash_root(); | ||
} else { | ||
unreachable!("The double slash root directory cannot be encountered during the pathname resolution"); | ||
} | ||
}, | ||
Component::CurDir => {}, | ||
Component::ParentDir => { | ||
current_dir = current_dir.parent(); | ||
}, | ||
Component::Normal(filename) => { | ||
let children = current_dir.entries(); | ||
let Some(entry) = children.into_iter().find(|entry| entry.filename == filename).map(|entry| entry.file) | ||
else { | ||
return Err(Error::IO(io::ErrorKind::NotFound)); | ||
}; | ||
|
||
#[allow(clippy::wildcard_enum_match_arm)] | ||
match entry { | ||
Type::Directory(dir) => { | ||
current_dir = dir; | ||
}, | ||
// This case is the symbolic link resolution, which is the one described as **not** being the one | ||
// explained in the following paragraph from the POSIX definition of the | ||
// pathname resolution: | ||
// | ||
// If a symbolic link is encountered during pathname resolution, the behavior shall depend on whether | ||
// the pathname component is at the end of the pathname and on the function | ||
// being performed. If all of the following are true, then pathname | ||
// resolution is complete: | ||
// 1. This is the last pathname component of the pathname. | ||
// 2. The pathname has no trailing <slash>. | ||
// 3. The function is required to act on the symbolic link itself, or certain arguments direct that | ||
// the | ||
// function act on the symbolic link itself. | ||
Type::SymbolicLink(symlink) | ||
if (pos != Position::Last && pos != Position::Only) | ||
|| !trailing_blackslash | ||
|| !symlink_resolution => | ||
{ | ||
let pointed_file = symlink.pointed_file().to_owned(); | ||
if pointed_file.is_empty() { | ||
return Err(Error::Fs(FsError::NoEnt(filename.to_string()))); | ||
}; | ||
|
||
symlink_encountered = Some(pointed_file); | ||
break; | ||
}, | ||
_ => { | ||
return if (pos == Position::Last || pos == Position::Only) && !trailing_blackslash { | ||
Ok(entry) | ||
} else { | ||
Err(Error::Fs(FsError::NotDir(filename.to_string()))) | ||
}; | ||
}, | ||
} | ||
}, | ||
} | ||
} | ||
|
||
match symlink_encountered { | ||
None => Ok(Type::Directory(current_dir)), | ||
Some(pointed_file) => { | ||
if visited_symlinks.contains(&pointed_file) { | ||
return Err(Error::Fs(FsError::Loop(pointed_file))); | ||
} | ||
visited_symlinks.push(pointed_file.clone()); | ||
|
||
let pointed_path = match Path::from_str(&pointed_file) { | ||
Ok(path) => path, | ||
Err(path_error) => return Err(Error::Path(path_error)), | ||
}; | ||
|
||
let complete_path = match TryInto::<Path>::try_into(&components) { | ||
Ok(remaining_path) => pointed_path.join(&remaining_path), | ||
Err(_) => pointed_path, | ||
}; | ||
|
||
if complete_path.len() >= PATH_MAX { | ||
Err(Error::Fs(FsError::NameTooLong(complete_path.to_string()))) | ||
} else { | ||
inner_resolution(fs, &complete_path, current_dir, symlink_resolution, visited_symlinks) | ||
} | ||
}, | ||
} | ||
} | ||
|
||
inner_resolution(self, path, current_dir, symlink_resolution, vec![]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.