diff --git a/src/fs/ext2/mod.rs b/src/fs/ext2/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..2dcff890a16e8bca412a6cb04b2277a3b539bd86 --- /dev/null +++ b/src/fs/ext2/mod.rs @@ -0,0 +1,4 @@ +//! Implementation of the Second Extended Filesystem (ext2fs) filesystem + +#[allow(clippy::same_name_method)] +mod superblock; diff --git a/src/fs/ext2/superblock.rs b/src/fs/ext2/superblock.rs new file mode 100644 index 0000000000000000000000000000000000000000..9ebe521b6b8863527d73e4c0b9c6aebc69788fdf --- /dev/null +++ b/src/fs/ext2/superblock.rs @@ -0,0 +1,274 @@ +//! Interface with the superblock +//! +//! See the [OSdev wiki](https://wiki.osdev.org/Ext2#Superblock) for more informations + +use bitflags::bitflags; +use log::error; + +/// 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, +} + +/// 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<u16> for State { + type Error = anyhow::Error; + + fn try_from(value: u16) -> Result<Self, Self::Error> { + match value { + 0x0001 => Ok(Self::Clean), + 0x0002 => Ok(Self::Errors), + _ => { + error!("ext2 superblock: tried to convert {:x} into a state (which can only be 1 or 2)", value); + Err(anyhow::Error::msg("ext2 superblock: tried to convert a value different than 1 or 2 into a state")) + }, + } + } +} + +impl From<State> for u16 { + 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<u16> for ErrorsHandling { + type Error = anyhow::Error; + + fn try_from(value: u16) -> Result<Self, Self::Error> { + match value { + 0x0001 => Ok(Self::Ignore), + 0x0002 => Ok(Self::Remount), + 0x0003 => Ok(Self::Panic), + _ => { + error!( + "ext2 superblock: tried to convert {:x} into an error handling method (which can only between 1 and 3)", + value + ); + Err(anyhow::Error::msg( + "ext2 superblock: tried to convert a value different than 1, 2 or 3 into an error handling method", + )) + }, + } + } +} + +impl From<ErrorsHandling> for u16 { + fn from(value: ErrorsHandling) -> Self { + value as Self + } +} + +bitflags! { + /// These are optional features for an implementation to support, but offer performance or reliability gains to + /// implementations that do support them. + pub struct OptionalFeatures: u16 { + /// Preallocate some number of (contiguous?) blocks (see byte 205 in the superblock) to a directory when creating + /// a new one (to reduce fragmentation?) + const PREALLOCATE_BLOCS = 0x0001; + + /// AFS server inodes exist + const AFS = 0x0002; + + /// File system has a journal (Ext3) + const JOURNAL_DEVICE = 0x0004; + + /// Inodes have extended attributes + const INODES_EXTENDED_ATTRIBUTES = 0x0008; + + /// File system can resize itself for larger partitions + const RESIZE_PARTITIONS = 0x0010; + + /// Directories use hash index + const HASH_INDEX = 0x0020; + } +} + +bitflags! { + /// These features if present on a file system are required to be supported by an implementation in order to correctly + /// read from or write to the file system + pub struct RequiredFeatures: u16 { + /// Compression is used + const COMPRESSION = 0x0001; + + /// Directory entries contain a type field + const TYPE_FIELD = 0x0002; + + /// File system needs to replay its journal + const JOURNAL_REPLAY = 0x0004; + + /// File system uses a journal device + const JOURNAL_DEVICE = 0x0008; + } +} + +bitflags! { + /// These features, if present on a file system, are required in order for an implementation to write to the file system, + /// but are not required to read from the file system. + pub struct ReadOnlyFeatures: u16 { + /// Sparse superblocks and group descriptor tables + const SPARSE_SUPERBLOCKS = 0x0001; + + /// File system uses a 64-bit file size + const SIZE_64_BITS = 0x0002; + + /// Directory contents are stored in the form of a Binary Tree + const BINARY_TREE = 0x0004; + } +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index f371830d6f57c04bc768c952d643fa9986e458a6..90153e532347a0c52fdf2c6e7cca4fd7ac4e934e 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,5 +2,7 @@ //! //! Contains modules for the Virtual File System (vfs) and an ext2 implementations +mod ext2; pub mod node; pub mod path; +pub mod vfs; diff --git a/src/fs/vfs.rs b/src/fs/vfs.rs new file mode 100644 index 0000000000000000000000000000000000000000..31f1e6a0850c2ab4f66118d9b5ea388fda782235 --- /dev/null +++ b/src/fs/vfs.rs @@ -0,0 +1,3 @@ +//! Virtual File System (vfs) +//! +//! Interface between the file system and the kernel/user