diff --git a/src/error.rs b/src/error.rs index c6af128..5fb7990 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,8 +3,6 @@ use core::error; use core::fmt::{self, Display}; -use no_std_io::io; - use crate::dev::error::DevError; use crate::fs::FsError; use crate::path::PathError; @@ -19,9 +17,6 @@ pub enum Error { /// Filesystem error Fs(FsError), - /// I/O error - IO(io::Error), - /// Path error Path(PathError), @@ -35,7 +30,6 @@ impl Display for Error { match self { Self::Device(device_error) => write!(formatter, "Device Error: {device_error}"), Self::Fs(fs_error) => write!(formatter, "Filesystem Error: {fs_error}"), - Self::IO(io_error) => write!(formatter, "I/O Error: {io_error:?}"), Self::Path(path_error) => write!(formatter, "Path Error: {path_error}"), Self::Other(other_error) => write!(formatter, "{other_error}"), } @@ -43,10 +37,3 @@ impl Display for Error { } impl error::Error for Error {} - -impl From for Error { - #[inline] - fn from(value: io::Error) -> Self { - Self::IO(value) - } -} diff --git a/src/ext2/error.rs b/src/ext2/error.rs deleted file mode 100644 index 9d5b36d..0000000 --- a/src/ext2/error.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Errors related to Ext2 manipulation. - -use core::error; -use core::fmt::{self, Display}; - -/// Enumeration of possible errors encountered with Ext2's manipulation. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, PartialEq, Eq)] -pub enum Ext2Error { - /// A bad magic number has been found during the superblock parsing. - BadMagic(u16), -} - -impl Display for Ext2Error { - #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BadMagic(magic) => write!(formatter, "Bad Magic: {magic} has been found while TODO was expected"), - } - } -} - -impl error::Error for Ext2Error {} diff --git a/src/fs/ext2/error.rs b/src/fs/ext2/error.rs new file mode 100644 index 0000000..783fb0d --- /dev/null +++ b/src/fs/ext2/error.rs @@ -0,0 +1,41 @@ +//! Errors related to Ext2 manipulation. + +use core::error; +use core::fmt::{self, Display}; + +use super::superblock::EXT2_SIGNATURE; + +/// Enumeration of possible errors encountered with Ext2's manipulation. +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, PartialEq, Eq)] +pub enum Ext2Error { + /// A bad magic number has been found during the superblock parsing. + /// + /// See [this table](https://wiki.osdev.org/Ext2#Base_Superblock_Fields) for reference. + BadMagic(u16), + + /// Given code does not correspond to a valid file system state. + /// + /// See [this table](https://wiki.osdev.org/Ext2#File_System_States) for reference. + InvlidState(u16), + + /// Given code does not correspond to a valid error handling method. + /// + /// See [this table](https://wiki.osdev.org/Ext2#Error_Handling_Methods) for reference. + InvalidErrorHandlingMethod(u16), +} + +impl Display for Ext2Error { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BadMagic(magic) => write!(formatter, "Bad Magic: {magic} has been found while {EXT2_SIGNATURE} was expected"), + Self::InvlidState(state) => write!(formatter, "Invalid State: {state} has been found while 1 or 2 was expected"), + Self::InvalidErrorHandlingMethod(method) => { + write!(formatter, "Invalid Error Handling Method: {method} was found while 1, 2 or 3 was expected") + }, + } + } +} + +impl error::Error for Ext2Error {} diff --git a/src/ext2/mod.rs b/src/fs/ext2/mod.rs similarity index 92% rename from src/ext2/mod.rs rename to src/fs/ext2/mod.rs index e312d7b..6f6c21f 100644 --- a/src/ext2/mod.rs +++ b/src/fs/ext2/mod.rs @@ -3,3 +3,4 @@ //! See [its Wikipedia page](https://fr.wikipedia.org/wiki/Ext2) or [its OSDev page](https://wiki.osdev.org/Ext2) for more informations. pub mod error; +pub mod superblock; diff --git a/src/fs/ext2/superblock.rs b/src/fs/ext2/superblock.rs new file mode 100644 index 0000000..c9a95dd --- /dev/null +++ b/src/fs/ext2/superblock.rs @@ -0,0 +1,232 @@ +//! Interface with the ext2's superblock. +//! +//! See the [OSdev wiki](https://wiki.osdev.org/Ext2#Superblock) for more informations. + +use super::error::Ext2Error; + +/// Ext2 signature, used to help confirm the presence of an Ext2 volume. +pub const EXT2_SIGNATURE: u16 = 0xef53; + +/// Starting byte of the superblock in a Ext2 storage device. +pub const SUPERBLOCK_START_BYTE: usize = 1024; + +/// Superblock of the Ext2 filesystem. +/// +/// This implementation contains also the extended fields described [here](https://wiki.osdev.org/Ext2#Extended_Superblock_Fields). +#[repr(packed)] +#[derive(Debug)] +pub struct Superblock { + /// Total number of inodes in file system + pub total_inodes: u32, + + /// Total number of blocks in file system + pub total_blocks: u32, + + /// Number of blocks reserved for superuser (see offset 80) + pub number_blocks_superuser_reserved: u32, + + /// Total number of unallocated blocks + pub total_unallocated_blocks: u32, + + /// Total number of unallocated inodes + pub total_unallocated_inodes: u32, + + /// Block number of the block containing the superblock (also the starting block number, NOT always zero.) + pub superblock_block_number: u32, + + /// log2 (block size) - 10. (In other words, the number to shift 1,024 to the left by to obtain the block size) + pub shifted_block_size: u32, + + /// log2 (fragment size) - 10. (In other words, the number to shift 1,024 to the left by to obtain the fragment size) + pub shifted_fragment_size: u32, + + /// Number of blocks in each block group + pub blocks_per_group: u32, + + /// Number of fragments in each block group + pub fragments_per_group: u32, + + /// Number of inodes in each block group + pub inodes_per_group: u32, + + /// Last mount time (in POSIX time) + pub last_mount_time: u32, + + /// Last written time (in POSIX time) + pub last_written_time: u32, + + /// Number of times the volume has been mounted since its last consistency check (fsck) + pub nb_volumes_mounted_since_fsck: u16, + + /// Number of mounts allowed before a consistency check (fsck) must be done + pub nb_mounts_before_fsck: u16, + + /// Ext2 signature (0xef53), used to help confirm the presence of Ext2 on a volume + pub ext2_signature: u16, + + /// File system state + pub file_system_state: u16, + + /// What to do when an error is detected + pub error_handling_method: u16, + + /// Minor portion of version (combine with Major portion below to construct full version field) + pub version_minor: u16, + + /// POSIX time of last consistency check (fsck) + pub time_last_fsck: u32, + + /// Interval (in POSIX time) between forced consistency checks (fsck) + pub interval_forced_fsck: u32, + + /// Operating system ID from which the filesystem on this volume was created + pub os_id: u32, + + /// Major portion of version (combine with Minor portion above to construct full version field) + pub version_major: u32, + + /// User ID that can use reserved blocks + pub reserved_blocks_user_id: u16, + + /// Group ID that can use reserved blocks + pub reserved_blocks_group_id: u16, + + /// First non-reserved inode in file system. (In versions < 1.0, this is fixed as 11) + pub first_non_reserved_inode: u32, + + /// Size of each inode structure in bytes. (In versions < 1.0, this is fixed as 128) + pub inode_byte_size: u16, + + /// Block group that this superblock is part of (if backup copy) + pub superblock_block_number_backup_copy: u16, + + /// Optional features present (features that are not required to read or write, but usually result in a performance increase) + pub optional_features: u32, + + /// Required features present (features that are required to be supported to read or write) + pub required_features: u32, + + /// Features that if not supported, the volume must be mounted read-only + pub forced_read_only_features: u32, + + /// File system ID (what is output by blkid) + pub file_system_id: u128, + + /// Volume name (C-style string: characters terminated by a 0 byte) + pub volume_name: [u8; 16], + + /// Path volume was last mounted to (C-style string: characters terminated by a 0 byte) + pub path_last_mount: [u8; 64], + + /// Compression algorithms used (see Required features above) + pub compression_algorithms: u32, + + /// Number of blocks to preallocate for files + pub preallocated_blocks_per_file: u8, + + /// Number of blocks to preallocate for directories + pub preallocated_blocks_per_dir: u8, + + /// (Unused) + pub _unused: u16, + + /// Journal ID (same style as the File system ID above) + pub journal_id: u128, + + /// Journal inode + pub journal_inode: u32, + + /// Journal device + pub journal_device: u32, + + /// Head of orphan inode list + pub head_orphan_node_list: u32, +} + +impl Superblock { + /// Returns the number of block groups. + /// + /// It is equal to the round up of the total number of blocks divided by the number of blocks per block group. + #[inline] + #[must_use] + pub const fn total_block_groups(&self) -> usize { + self.total_blocks.div_ceil(self.blocks_per_group) as usize + } + + /// Returns the size of a block in the filesystem described by this superblock. + #[inline] + #[must_use] + pub const fn block_size(&self) -> usize { + 1024 << self.shifted_block_size + } +} + +/// File System States. +/// +/// See the [OSdev wiki](https://wiki.osdev.org/Ext2#File_System_States) for more informations. +#[repr(u16)] +#[derive(Debug)] +pub enum State { + /// File system is clean. + Clean = 0x0001, + + /// File system has errors. + Errors = 0x0002, +} + +impl TryFrom for State { + type Error = Ext2Error; + + #[inline] + fn try_from(value: u16) -> Result { + match value { + 0x0001 => Ok(Self::Clean), + 0x0002 => Ok(Self::Errors), + _ => Err(Ext2Error::InvlidState(value)), + } + } +} + +impl From for u16 { + #[inline] + fn from(value: State) -> Self { + value as Self + } +} + +/// Error Handling Methods. +/// +/// See the [OSdev wiki](https://wiki.osdev.org/Ext2#Error_Handling_Methods) for more informations. +#[repr(u16)] +#[derive(Debug)] +pub enum ErrorsHandling { + /// Ignore the error (continue on). + Ignore = 0x0001, + + /// Remount file system as read-only. + Remount = 0x0002, + + /// Kernel panic. + Panic = 0x03, +} + +impl TryFrom for ErrorsHandling { + type Error = Ext2Error; + + #[inline] + fn try_from(value: u16) -> Result { + match value { + 0x0001 => Ok(Self::Ignore), + 0x0002 => Ok(Self::Remount), + 0x0003 => Ok(Self::Panic), + _ => Err(Ext2Error::InvalidErrorHandlingMethod(value)), + } + } +} + +impl From for u16 { + #[inline] + fn from(value: ErrorsHandling) -> Self { + value as Self + } +} diff --git a/src/fs.rs b/src/fs/mod.rs similarity index 95% rename from src/fs.rs rename to src/fs/mod.rs index 1edc944..06e9b71 100644 --- a/src/fs.rs +++ b/src/fs/mod.rs @@ -10,12 +10,13 @@ use core::fmt::{self, Display}; use core::str::FromStr; use itertools::{Itertools, Position}; -use no_std_io::io; use crate::error::Error; use crate::file::{Directory, Type}; use crate::path::{Component, Path}; +pub mod ext2; + /// 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). @@ -27,16 +28,19 @@ pub const PATH_MAX: usize = 4_096; #[allow(clippy::module_name_repetitions)] #[derive(Debug)] pub enum FsError { - /// Indicates that the given [`Path`] is too long to be resolved + /// Indicates that the given [`Path`] is too long to be resolved. NameTooLong(String), - /// Indicates that the given filename is not a [`Directory`] + /// Indicates that the given filename is not a [`Directory`]. NotDir(String), - /// Indicates that the given filename is an symbolic link pointing at an empty string + /// Indicates that the given filename is an symbolic link pointing at an empty string. NoEnt(String), - /// Indicates that a loop has been encountered during the given path resolution + /// Indicates that the given filename has not been found. + NotFound(String), + + /// Indicates that a loop has been encountered during the given path resolution. Loop(String), } @@ -48,15 +52,16 @@ impl Display for FsError { 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"), + Self::NotFound(filename) => write!(formatter, "Not found: \"{filename}\" has not been found"), } } } impl error::Error for FsError {} -/// A filesystem +/// A filesystem. pub trait FileSystem { - /// Error type associated with the filesystem + /// Error type associated with the filesystem. type Error: error::Error; /// Returns the root directory of the filesystem. @@ -142,7 +147,7 @@ pub trait FileSystem { 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::Error::new(io::ErrorKind::NotFound, "File not found"))); + return Err(Error::Fs(FsError::NotFound(filename.to_string()))); }; #[allow(clippy::wildcard_enum_match_arm)] diff --git a/src/lib.rs b/src/lib.rs index 70312bf..f3965a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,12 +70,9 @@ extern crate core; #[cfg(not(no_std))] extern crate std; +pub mod dev; pub mod error; pub mod file; pub mod fs; pub mod path; pub mod types; - -pub mod dev; - -pub mod ext2;