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}