keos_project5/ffs/
types.rs

1//! Core type definitions for the filesystem.
2//!
3//! This module defines fundamental data types used throughout the
4//! filesystem implementation. These types encapsulate low-level
5//! representations of on-disk metadata and provide utility methods
6//! for interpreting and manipulating filesystem structures.
7//!
8//! Most types here are simple wrappers around integers or enums, often with
9//! utility methods to convert between disk layout positions and internal
10//! structures.
11use super::FastFileSystemInner;
12use core::{iter::Step, num::NonZeroU64};
13use keos::{KernelError, fs::Sector};
14
15/// Represents the type of a file in the filesystem.
16///
17/// This enum is used to distinguish between different kinds of inodes,
18/// such as regular files and directories. It is stored on disk as part of
19/// the inode metadata to identify how the data associated with the inode
20/// should be interpreted.
21#[derive(Debug, Eq, PartialEq, Copy, Clone)]
22#[repr(u32)]
23pub enum FileType {
24    /// A regular file, containing user data.
25    ///
26    /// This type represents standard files used to store arbitrary user
27    /// content (e.g., text, binaries, etc.). The file’s data blocks are
28    /// directly mapped to its contents.
29    RegularFile = 0,
30
31    /// A directory, which stores a list of file entries.
32    ///
33    /// A directory maps file names to inode numbers. Its contents are
34    /// typically a structured list of directory entries that allow for
35    /// hierarchical navigation within the filesystem.
36    Directory = 1,
37}
38
39impl TryFrom<u32> for FileType {
40    type Error = KernelError;
41    fn try_from(value: u32) -> Result<Self, Self::Error> {
42        match value {
43            0 => Ok(Self::RegularFile),
44            1 => Ok(Self::Directory),
45            _ => Err(KernelError::FilesystemCorrupted("Invalid inode type")),
46        }
47    }
48}
49
50/// Represents a logical block address (LBA) on disk.
51///
52/// This structure stores the block index used to locate data blocks
53/// on a physical storage device. The actual size of a block depends
54/// on the filesystem's configuration.
55#[repr(transparent)]
56#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
57pub struct LogicalBlockAddress(NonZeroU64);
58const_assert!(core::mem::size_of::<Option<LogicalBlockAddress>>() == 8);
59
60impl LogicalBlockAddress {
61    /// Creates a [`LogicalBlockAddress`] if the given value is not zero.
62    pub const fn new(n: u64) -> Option<Self> {
63        if let Some(v) = NonZeroU64::new(n) {
64            Some(Self(v))
65        } else {
66            None
67        }
68    }
69
70    /// Converts a logical block address (LBA) to a sector number.
71    ///
72    /// In the Fast File System (FFS), logical blocks are 4 KiB (`0x1000`) in
73    /// size, while sectors are 512 bytes. Since LBA numbering starts at `1`
74    /// (with `0` being invalid), this function calculates the corresponding
75    /// sector.
76    ///
77    /// # Returns
78    /// - A [`Sector`] corresponding to the given logical block.
79    pub const fn into_sector(self) -> Sector {
80        // LBA is starting from 1. Zero represents the invalid LBA.
81        Sector(((self.0.get() - 1) * (0x1000 / 512)) as usize)
82    }
83
84    /// Converts this logical block address into the corresponding location in
85    /// the block allocation bitmap.
86    ///
87    /// # Arguments
88    /// - `fs`: Reference to the file system's internal metadata layout.
89    ///
90    /// # Returns
91    /// - `Some((lba, index))`: if the block address is valid, which includes:
92    ///   - `lba`: the logical block address of the bitmap block that tracks
93    ///     this data block.
94    ///   - `index`: the index within the bitmap block where the relevant bit
95    ///     resides.
96    /// - `None`: if the logical block address is out of bounds or outside the
97    ///   data region.
98    pub fn into_bitmap_lba_offset(
99        self,
100        ffs: &FastFileSystemInner,
101    ) -> Option<(LogicalBlockAddress, usize)> {
102        if self >= ffs.data_block_start() && self < ffs.data_block_start() + ffs.block_count {
103            Some((
104                ffs.block_bitmap().start + (self.0.get() as usize / 0x8000),
105                self.0.get() as usize % 0x8000,
106            ))
107        } else {
108            None
109        }
110    }
111
112    /// Returns the contained value as a u64.
113    #[inline]
114    pub fn into_u64(&self) -> u64 {
115        self.0.get()
116    }
117}
118
119// Sugars for LBA and FBN.
120impl Step for LogicalBlockAddress {
121    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
122        if start.0 <= end.0 {
123            let steps = (end.0.get() - start.0.get()) as usize;
124            (steps, Some(steps))
125        } else {
126            (0, None)
127        }
128    }
129
130    fn forward_checked(start: Self, count: usize) -> Option<Self> {
131        start.0.get().checked_add(count as u64).and_then(Self::new)
132    }
133
134    fn backward_checked(start: Self, count: usize) -> Option<Self> {
135        start.0.get().checked_sub(count as u64).and_then(Self::new)
136    }
137}
138
139impl core::ops::Add<usize> for LogicalBlockAddress {
140    type Output = Self;
141
142    fn add(self, rhs: usize) -> Self {
143        Self(NonZeroU64::new(self.0.get() + rhs as u64).unwrap())
144    }
145}