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 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 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 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 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}