ext2/
superblock.rs

1use alloc::string::{FromUtf8Error, String};
2use core::fmt::{Debug, Display, Formatter};
3
4use bitflags::bitflags;
5
6#[derive(Debug, Eq, PartialEq)]
7pub struct Superblock {
8    pub num_inodes: u32,
9    pub num_blocks: u32,
10    pub num_superuser_reserved_blocks: u32,
11    pub num_unallocated_blocks: u32,
12    pub num_unallocated_inodes: u32,
13    pub superblock_block_number: u32,
14    pub block_size: u32,
15    pub fragment_size: u32,
16    pub blocks_per_group: u32,
17    pub fragments_per_group: u32,
18    pub inodes_per_group: u32,
19    pub last_mount_time: u32,
20    pub last_written_time: u32,
21    pub mounts_since_fsck: u16,
22    pub mounts_allowed_before_fsck: u16,
23    pub magic_number: u16,
24    pub state: State,
25    pub error_policy: ErrorPolicy,
26    pub version_minor: u16,
27    pub last_fsck: u32,
28    pub fsck_force_interval: u32,
29    pub os_id: u32,
30    pub version_major: u32,
31    pub uid_for_reserved_blocks: u16,
32    pub gid_for_reserved_blocks: u16,
33    pub extended: Option<SuperblockExtended>,
34}
35
36#[derive(Debug, Copy, Clone, Eq, PartialEq)]
37pub enum SuperblockDecodeError {
38    InvalidMagicNumber,
39    /// Indicates, that more bytes were expected than provided.
40    /// This is an internal error.
41    ShortRead,
42    InvalidBlockSize,
43    InvalidFragmentSize,
44    InvalidData,
45}
46
47impl Display for SuperblockDecodeError {
48    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
49        Debug::fmt(&self, f)
50    }
51}
52
53impl core::error::Error for SuperblockDecodeError {}
54
55impl From<ShortReadError> for SuperblockDecodeError {
56    fn from(_: ShortReadError) -> Self {
57        Self::ShortRead
58    }
59}
60
61impl From<FromUtf8Error> for SuperblockDecodeError {
62    fn from(_: FromUtf8Error) -> Self {
63        Self::InvalidData
64    }
65}
66
67impl TryFrom<[u8; 1024]> for Superblock {
68    type Error = SuperblockDecodeError;
69
70    fn try_from(value: [u8; 1024]) -> Result<Self, Self::Error> {
71        let num_inodes = read_u32_le(&value[0..4])?;
72        let num_blocks = read_u32_le(&value[4..8])?;
73        let num_superuser_reserved_blocks = read_u32_le(&value[8..12])?;
74        let num_unallocated_blocks = read_u32_le(&value[12..16])?;
75        let num_unallocated_inodes = read_u32_le(&value[16..20])?;
76        let superblock_block_number = read_u32_le(&value[20..24])?;
77        let log2_block_size = read_u32_le(&value[24..28])?;
78        let block_size = u32::checked_shl(1024, log2_block_size)
79            .ok_or(SuperblockDecodeError::InvalidBlockSize)?;
80        let log2_fragment_size = read_u32_le(&value[28..32])?;
81        let fragment_size = u32::checked_shl(1024, log2_fragment_size)
82            .ok_or(SuperblockDecodeError::InvalidFragmentSize)?;
83        let blocks_per_group = read_u32_le(&value[32..36])?;
84        let fragments_per_group = read_u32_le(&value[36..40])?;
85        let inodes_per_group = read_u32_le(&value[40..44])?;
86        let last_mount_time = read_u32_le(&value[44..48])?;
87        let last_written_time = read_u32_le(&value[48..52])?;
88        let mounts_since_fsck = read_u16_le(&value[52..54])?;
89        let mounts_allowed_before_fsck = read_u16_le(&value[54..56])?;
90        let magic_number = read_u16_le(&value[56..58])?;
91        let state = State::from_bits_truncate(read_u16_le(&value[58..60])?);
92        let error_policy = ErrorPolicy::from_bits_truncate(read_u16_le(&value[60..62])?);
93        let version_minor = read_u16_le(&value[62..64])?;
94        let last_fsck = read_u32_le(&value[64..68])?;
95        let fsck_force_interval = read_u32_le(&value[68..72])?;
96        let os_id = read_u32_le(&value[72..76])?;
97        let version_major = read_u32_le(&value[76..80])?;
98        let uid_for_reserved_blocks = read_u16_le(&value[80..82])?;
99        let gid_for_reserved_blocks = read_u16_le(&value[82..84])?;
100
101        // validation
102
103        if blocks_per_group < 1 || inodes_per_group < 1 {
104            return Err(SuperblockDecodeError::InvalidData);
105        }
106        let check1 = (num_blocks + blocks_per_group - 1) / blocks_per_group;
107        let check2 = (num_inodes + inodes_per_group - 1) / inodes_per_group;
108        if check1 != check2 {
109            return Err(SuperblockDecodeError::InvalidData);
110        }
111
112        if magic_number != 0xEF53 {
113            return Err(SuperblockDecodeError::InvalidMagicNumber);
114        }
115
116        let extended = if version_major >= 1 {
117            Some(SuperblockExtended::try_from(value)?)
118        } else {
119            None
120        };
121
122        Ok(Self {
123            num_inodes,
124            num_blocks,
125            num_superuser_reserved_blocks,
126            num_unallocated_blocks,
127            num_unallocated_inodes,
128            superblock_block_number,
129            block_size,
130            fragment_size,
131            blocks_per_group,
132            fragments_per_group,
133            inodes_per_group,
134            last_mount_time,
135            last_written_time,
136            mounts_since_fsck,
137            mounts_allowed_before_fsck,
138            magic_number,
139            state,
140            error_policy,
141            version_minor,
142            last_fsck,
143            fsck_force_interval,
144            os_id,
145            version_major,
146            uid_for_reserved_blocks,
147            gid_for_reserved_blocks,
148            extended,
149        })
150    }
151}
152
153#[derive(Debug, Eq, PartialEq)]
154pub struct Ext2FsId([u8; 16]);
155
156#[derive(Debug, Eq, PartialEq)]
157pub struct SuperblockExtended {
158    pub first_non_reserved_inode: u32,
159    pub inode_size: u16,
160    pub this_superblock_block_group: u16,
161    pub optional_features: OptionalFeatures,
162    pub required_features: RequiredFeatures,
163    pub write_required_features: ReadOnlyFeatures,
164    pub fsid: Ext2FsId,
165    pub volume_name: String,
166    pub last_mount_path: String,
167    pub compression: u32,
168    pub num_preallocate_blocks_file: u8,
169    pub num_preallocate_blocks_directory: u8,
170}
171
172impl TryFrom<[u8; 1024]> for SuperblockExtended {
173    type Error = SuperblockDecodeError;
174
175    fn try_from(value: [u8; 1024]) -> Result<Self, Self::Error> {
176        let first_non_reserved_inode = read_u32_le(&value[84..88])?;
177        let inode_size = read_u16_le(&value[88..90])?;
178        let this_superblock_block_group = read_u16_le(&value[90..92])?;
179        let optional_features = read_u32_le(&value[92..96])?;
180        let required_features = read_u32_le(&value[96..100])?;
181        let write_required_features = read_u32_le(&value[100..104])?;
182        let mut fsid = [0_u8; 16];
183        fsid.copy_from_slice(&value[104..120]);
184        let volume_name = read_c_str(&value[120..136])?;
185        let last_mount_path = read_c_str(&value[136..200])?;
186        let compression = read_u32_le(&value[200..204])?;
187        let num_preallocate_blocks_file = value[204];
188        let num_preallocate_blocks_directory = value[205];
189
190        Ok(Self {
191            first_non_reserved_inode,
192            inode_size,
193            this_superblock_block_group,
194            optional_features: OptionalFeatures::from_bits_truncate(optional_features),
195            required_features: RequiredFeatures::from_bits_truncate(required_features),
196            write_required_features: ReadOnlyFeatures::from_bits_truncate(write_required_features),
197            fsid: Ext2FsId(fsid),
198            volume_name,
199            last_mount_path,
200            compression,
201            num_preallocate_blocks_file,
202            num_preallocate_blocks_directory,
203        })
204    }
205}
206
207bitflags! {
208    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
209    pub struct OptionalFeatures: u32 {
210        const PREALLOCATE_FOR_DIRECTORY = 0x0001;
211        const AFS_SERVER_INODES_EXIST = 0x0002;
212        const HAS_JOURNAL = 0x0004;
213        const INODES_EXTENDED_ATTRIBUTES = 0x0008;
214        const CAN_RESIZE = 0x0010;
215        const DIRECTORIES_USE_HASH_INDEX = 0x0020;
216    }
217}
218
219bitflags! {
220    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
221    pub struct RequiredFeatures: u32 {
222        const COMPRESSION_USED = 0x0001;
223        const DIRECTORY_ENTRIES_HAVE_TYPE = 0x0002;
224        const NEEDS_JOURNAL_REPLAY = 0x0004;
225        const USES_JOURNAL_DEVICE = 0x0008;
226    }
227}
228
229bitflags! {
230    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
231    pub struct ReadOnlyFeatures: u32 {
232        const SPARSE_SUPERBLOCK_AND_GDTS = 0x0001;
233        const USE_64BIT_FILE_SIZE = 0x0002;
234        const DIRS_STORED_AS_BINARY_TREE = 0x0004;
235    }
236}
237
238bitflags! {
239    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
240    pub struct State: u16 {
241        const CLEAN = 1;
242        const ERRONOUS = 2;
243    }
244}
245
246bitflags! {
247    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
248    pub struct ErrorPolicy: u16 {
249        const IGNORE = 1;
250        const REMOUNT_READ_ONLY = 2;
251        const KERNEL_PANIC = 3;
252    }
253}
254
255#[derive(Debug)]
256struct ShortReadError;
257
258fn read_u32_le(data: &[u8]) -> Result<u32, ShortReadError> {
259    if data.len() < 4 {
260        return Err(ShortReadError);
261    }
262    Ok(u32::from_le_bytes(data[0..4].try_into().unwrap()))
263}
264
265fn read_u16_le(data: &[u8]) -> Result<u16, ShortReadError> {
266    if data.len() < 2 {
267        return Err(ShortReadError);
268    }
269    Ok(u16::from_le_bytes(data[0..2].try_into().unwrap()))
270}
271
272fn read_c_str(data: &[u8]) -> Result<String, FromUtf8Error> {
273    let termination_index = data.iter().position(|&b| b == 0).unwrap_or(data.len() + 1);
274    String::from_utf8(data[0..termination_index].into())
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use alloc::string::ToString;
281
282    #[test]
283    fn test_superblock_try_from() {
284        // bytes of a superblock generated by `mkfs.ext2`
285        let data = [
286            0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xca, 0x03,
287            0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288            0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x00,
289            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x5d, 0x7f, 0x64, 0x00, 0x00, 0xff, 0xff,
290            0x53, 0xef, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc5, 0x5d, 0x7f, 0x64, 0x00, 0x00,
291            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292            0x0b, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x00,
293            0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x77, 0x4b, 0x94, 0xce, 0x3d, 0x05, 0x4b, 0x71,
294            0x98, 0xfc, 0xfc, 0xf6, 0x37, 0xfd, 0x2b, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
296            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
297            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
301            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x38,
303            0x05, 0x06, 0x72, 0xb2, 0x44, 0xcd, 0xbb, 0x20, 0x4e, 0xa1, 0xcb, 0x69, 0xa6, 0x1d,
304            0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x5d,
305            0x7f, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
311            0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
328            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
349            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
358            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359            0x00, 0x00,
360        ];
361        let sb = Superblock::try_from(data).unwrap();
362
363        // result manually validated via `debugfs`
364        assert_eq!(
365            Superblock {
366                num_inodes: 128,
367                num_blocks: 1024,
368                num_superuser_reserved_blocks: 51,
369                num_unallocated_blocks: 970,
370                num_unallocated_inodes: 117,
371                superblock_block_number: 1,
372                block_size: 1024,
373                fragment_size: 1024,
374                blocks_per_group: 8192,
375                fragments_per_group: 8192,
376                inodes_per_group: 128,
377                last_mount_time: 0,
378                last_written_time: 1686068678,
379                mounts_since_fsck: 0,
380                mounts_allowed_before_fsck: 65535,
381                magic_number: 61267,
382                state: State::CLEAN,
383                error_policy: ErrorPolicy::IGNORE,
384                version_minor: 0,
385                last_fsck: 1686068677,
386                fsck_force_interval: 0,
387                os_id: 0,
388                version_major: 1,
389                uid_for_reserved_blocks: 0,
390                gid_for_reserved_blocks: 0,
391                extended: Some(SuperblockExtended {
392                    first_non_reserved_inode: 11,
393                    inode_size: 256,
394                    this_superblock_block_group: 0,
395                    optional_features: OptionalFeatures::INODES_EXTENDED_ATTRIBUTES
396                        | OptionalFeatures::CAN_RESIZE
397                        | OptionalFeatures::DIRECTORIES_USE_HASH_INDEX,
398                    required_features: RequiredFeatures::DIRECTORY_ENTRIES_HAVE_TYPE,
399                    write_required_features: ReadOnlyFeatures::SPARSE_SUPERBLOCK_AND_GDTS
400                        | ReadOnlyFeatures::USE_64BIT_FILE_SIZE,
401                    fsid: Ext2FsId([
402                        119, 75, 148, 206, 61, 5, 75, 113, 152, 252, 252, 246, 55, 253, 43, 72,
403                    ],),
404                    volume_name: "".to_string(),
405                    last_mount_path: "".to_string(),
406                    compression: 0,
407                    num_preallocate_blocks_file: 0,
408                    num_preallocate_blocks_directory: 0,
409                },),
410            },
411            sb
412        );
413    }
414
415    #[test]
416    fn test_read_u32_le() {
417        for (expected, data) in [
418            (1, [0x01, 0x00, 0x00, 0x00]),
419            (2, [0x02, 0x00, 0x00, 0x00]),
420            (255, [0xFF, 0x00, 0x00, 0x00]),
421            (65280, [0x00, 0xFF, 0x00, 0x00]),
422            (4294967040, [0x00, 0xFF, 0xFF, 0xFF]),
423            (4294967295, [0xFF, 0xFF, 0xFF, 0xFF]),
424        ] {
425            let actual = read_u32_le(&data).unwrap();
426            assert_eq!(expected, actual);
427        }
428    }
429
430    #[test]
431    fn test_read_u32_le_short() {
432        assert!(read_u32_le(&[]).is_err());
433        assert!(read_u32_le(&[0]).is_err());
434        assert!(read_u32_le(&[0, 0]).is_err());
435        assert!(read_u32_le(&[0, 0, 0]).is_err());
436        assert!(read_u32_le(&[0, 0, 0, 0]).is_ok_and(|i| i == 0));
437    }
438
439    #[test]
440    fn test_read_u16_le() {
441        for (expected, data) in [
442            (1, [0x01, 0x00]),
443            (2, [0x02, 0x00]),
444            (255, [0xFF, 0x00]),
445            (65280, [0x00, 0xFF]),
446            (65535, [0xFF, 0xFF]),
447        ] {
448            let actual = read_u16_le(&data).unwrap();
449            assert_eq!(expected, actual);
450        }
451    }
452
453    #[test]
454    fn test_read_u16_le_short() {
455        assert!(read_u16_le(&[]).is_err());
456        assert!(read_u16_le(&[0]).is_err());
457        assert!(read_u16_le(&[0, 0]).is_ok_and(|i| i == 0));
458    }
459}