1use crate::constants::*;
8use std::time::SystemTime;
9
10#[inline]
15fn get_u8(buf: &[u8], off: usize) -> u8 {
16 buf[off]
17}
18
19#[inline]
20fn put_u8(buf: &mut [u8], off: usize, v: u8) {
21 buf[off] = v;
22}
23
24#[inline]
25fn get_u16(buf: &[u8], off: usize) -> u16 {
26 u16::from_le_bytes([buf[off], buf[off + 1]])
27}
28
29#[inline]
30fn put_u16(buf: &mut [u8], off: usize, v: u16) {
31 let b = v.to_le_bytes();
32 buf[off] = b[0];
33 buf[off + 1] = b[1];
34}
35
36#[inline]
37fn get_u32(buf: &[u8], off: usize) -> u32 {
38 u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]])
39}
40
41#[inline]
42fn put_u32(buf: &mut [u8], off: usize, v: u32) {
43 let b = v.to_le_bytes();
44 buf[off..off + 4].copy_from_slice(&b);
45}
46
47#[inline]
48fn get_u64(buf: &[u8], off: usize) -> u64 {
49 u64::from_le_bytes([
50 buf[off],
51 buf[off + 1],
52 buf[off + 2],
53 buf[off + 3],
54 buf[off + 4],
55 buf[off + 5],
56 buf[off + 6],
57 buf[off + 7],
58 ])
59}
60
61#[inline]
62fn put_u64(buf: &mut [u8], off: usize, v: u64) {
63 let b = v.to_le_bytes();
64 buf[off..off + 8].copy_from_slice(&b);
65}
66
67#[inline]
68fn get_bytes<const N: usize>(buf: &[u8], off: usize) -> [u8; N] {
69 let mut out = [0u8; N];
70 out.copy_from_slice(&buf[off..off + N]);
71 out
72}
73
74#[inline]
75fn put_bytes(buf: &mut [u8], off: usize, src: &[u8]) {
76 buf[off..off + src.len()].copy_from_slice(src);
77}
78
79pub fn timestamp_now() -> (u32, u32) {
89 let dur = SystemTime::now()
90 .duration_since(SystemTime::UNIX_EPOCH)
91 .unwrap_or_default();
92
93 let secs = dur.as_secs();
94 let nanos = dur.subsec_nanos();
95
96 let lo = secs as u32; let epoch_bits = ((secs >> 32) & 0x3) as u32; let extra = epoch_bits | (nanos << 2);
99
100 (lo, extra)
101}
102
103pub const SUPERBLOCK_SIZE: usize = 1024;
108
109#[derive(Debug, Clone)]
110pub struct SuperBlock {
111 pub inodes_count: u32,
113 pub blocks_count_lo: u32,
114 pub r_blocks_count_lo: u32,
115 pub free_blocks_count_lo: u32,
116 pub free_inodes_count: u32,
117 pub first_data_block: u32,
118 pub log_block_size: u32,
119 pub log_cluster_size: u32,
120 pub blocks_per_group: u32,
121 pub clusters_per_group: u32,
122 pub inodes_per_group: u32,
123 pub mtime: u32,
124 pub wtime: u32,
125 pub mount_count: u16,
126 pub max_mount_count: u16,
127 pub magic: u16,
128 pub state: u16,
129 pub errors: u16,
130 pub minor_rev_level: u16,
131 pub lastcheck: u32,
132 pub check_interval: u32,
133 pub creator_os: u32,
134 pub rev_level: u32,
135 pub def_resuid: u16,
136 pub def_resgid: u16,
137
138 pub first_ino: u32,
140 pub inode_size: u16,
141 pub block_group_nr: u16,
142 pub feature_compat: u32,
143 pub feature_incompat: u32,
144 pub feature_ro_compat: u32,
145 pub uuid: [u8; 16],
146 pub volume_name: [u8; 16],
147 pub last_mounted: [u8; 64],
148 pub algorithm_usage_bitmap: u32,
149
150 pub prealloc_blocks: u8,
152 pub prealloc_dir_blocks: u8,
153 pub reserved_gdt_blocks: u16,
154
155 pub journal_uuid: [u8; 16],
157 pub journal_inum: u32,
158 pub journal_dev: u32,
159 pub last_orphan: u32,
160 pub hash_seed: [u32; 4],
161 pub def_hash_version: u8,
162 pub journal_backup_type: u8,
163 pub desc_size: u16,
164 pub default_mount_opts: u32,
165 pub first_meta_bg: u32,
166 pub mkfs_time: u32,
167 pub journal_blocks: [u32; 17],
168
169 pub blocks_count_hi: u32,
171 pub r_blocks_count_hi: u32,
172 pub free_blocks_count_hi: u32,
173 pub min_extra_isize: u16,
174 pub want_extra_isize: u16,
175 pub flags: u32,
176 pub raid_stride: u16,
177 pub mmp_interval: u16,
178 pub mmp_block: u64,
179 pub raid_stripe_width: u32,
180 pub log_groups_per_flex: u8,
181 pub checksum_type: u8,
182 pub reserved_pad: u16,
183 pub kbytes_written: u64,
184
185 pub snapshot_inum: u32,
187 pub snapshot_id: u32,
188 pub snapshot_r_blocks_count: u64,
189 pub snapshot_list: u32,
190
191 pub error_count: u32,
193 pub first_error_time: u32,
194 pub first_error_ino: u32,
195 pub first_error_block: u64,
196 pub first_error_func: [u8; 32],
197 pub first_error_line: u32,
198 pub last_error_time: u32,
199 pub last_error_ino: u32,
200 pub last_error_line: u32,
201 pub last_error_block: u64,
202 pub last_error_func: [u8; 32],
203
204 pub mount_opts: [u8; 64],
206
207 pub usr_quota_inum: u32,
209 pub grp_quota_inum: u32,
210 pub overhead_blocks: u32,
211 pub backup_bgs: [u32; 2],
212 pub encrypt_algos: [u8; 4],
213 pub encrypt_pw_salt: [u8; 16],
214 pub lpf_ino: u32,
215 pub prj_quota_inum: u32,
216 pub checksum_seed: u32,
217
218 pub wtime_hi: u8,
220 pub mtime_hi: u8,
221 pub mkfs_time_hi: u8,
222 pub lastcheck_hi: u8,
223 pub first_error_time_hi: u8,
224 pub last_error_time_hi: u8,
225 pub pad: [u8; 2],
226
227 pub reserved: [u32; 96],
229
230 pub checksum: u32,
232}
233
234impl Default for SuperBlock {
235 fn default() -> Self {
236 Self {
237 inodes_count: 0,
238 blocks_count_lo: 0,
239 r_blocks_count_lo: 0,
240 free_blocks_count_lo: 0,
241 free_inodes_count: 0,
242 first_data_block: 0,
243 log_block_size: 0,
244 log_cluster_size: 0,
245 blocks_per_group: 0,
246 clusters_per_group: 0,
247 inodes_per_group: 0,
248 mtime: 0,
249 wtime: 0,
250 mount_count: 0,
251 max_mount_count: 0,
252 magic: 0,
253 state: 0,
254 errors: 0,
255 minor_rev_level: 0,
256 lastcheck: 0,
257 check_interval: 0,
258 creator_os: 0,
259 rev_level: 0,
260 def_resuid: 0,
261 def_resgid: 0,
262 first_ino: 0,
263 inode_size: 0,
264 block_group_nr: 0,
265 feature_compat: 0,
266 feature_incompat: 0,
267 feature_ro_compat: 0,
268 uuid: [0; 16],
269 volume_name: [0; 16],
270 last_mounted: [0; 64],
271 algorithm_usage_bitmap: 0,
272 prealloc_blocks: 0,
273 prealloc_dir_blocks: 0,
274 reserved_gdt_blocks: 0,
275 journal_uuid: [0; 16],
276 journal_inum: 0,
277 journal_dev: 0,
278 last_orphan: 0,
279 hash_seed: [0; 4],
280 def_hash_version: 0,
281 journal_backup_type: 0,
282 desc_size: 0,
283 default_mount_opts: 0,
284 first_meta_bg: 0,
285 mkfs_time: 0,
286 journal_blocks: [0; 17],
287 blocks_count_hi: 0,
288 r_blocks_count_hi: 0,
289 free_blocks_count_hi: 0,
290 min_extra_isize: 0,
291 want_extra_isize: 0,
292 flags: 0,
293 raid_stride: 0,
294 mmp_interval: 0,
295 mmp_block: 0,
296 raid_stripe_width: 0,
297 log_groups_per_flex: 0,
298 checksum_type: 0,
299 reserved_pad: 0,
300 kbytes_written: 0,
301 snapshot_inum: 0,
302 snapshot_id: 0,
303 snapshot_r_blocks_count: 0,
304 snapshot_list: 0,
305 error_count: 0,
306 first_error_time: 0,
307 first_error_ino: 0,
308 first_error_block: 0,
309 first_error_func: [0; 32],
310 first_error_line: 0,
311 last_error_time: 0,
312 last_error_ino: 0,
313 last_error_line: 0,
314 last_error_block: 0,
315 last_error_func: [0; 32],
316 mount_opts: [0; 64],
317 usr_quota_inum: 0,
318 grp_quota_inum: 0,
319 overhead_blocks: 0,
320 backup_bgs: [0; 2],
321 encrypt_algos: [0; 4],
322 encrypt_pw_salt: [0; 16],
323 lpf_ino: 0,
324 prj_quota_inum: 0,
325 checksum_seed: 0,
326 wtime_hi: 0,
327 mtime_hi: 0,
328 mkfs_time_hi: 0,
329 lastcheck_hi: 0,
330 first_error_time_hi: 0,
331 last_error_time_hi: 0,
332 pad: [0; 2],
333 reserved: [0; 96],
334 checksum: 0,
335 }
336 }
337}
338
339impl SuperBlock {
340 pub const SIZE: usize = SUPERBLOCK_SIZE;
341
342 pub fn read_from(buf: &[u8]) -> Self {
344 debug_assert!(buf.len() >= Self::SIZE);
345
346 let mut hash_seed = [0u32; 4];
347 for i in 0..4 {
348 hash_seed[i] = get_u32(buf, 0xEC + i * 4);
349 }
350
351 let mut journal_blocks = [0u32; 17];
352 for i in 0..17 {
353 journal_blocks[i] = get_u32(buf, 0x10C + i * 4);
354 }
355
356 let mut backup_bgs = [0u32; 2];
357 backup_bgs[0] = get_u32(buf, 0x24C);
358 backup_bgs[1] = get_u32(buf, 0x250);
359
360 let mut reserved = [0u32; 96];
361 for i in 0..96 {
362 reserved[i] = get_u32(buf, 0x27C + i * 4);
363 }
364
365 Self {
366 inodes_count: get_u32(buf, 0x00),
367 blocks_count_lo: get_u32(buf, 0x04),
368 r_blocks_count_lo: get_u32(buf, 0x08),
369 free_blocks_count_lo: get_u32(buf, 0x0C),
370 free_inodes_count: get_u32(buf, 0x10),
371 first_data_block: get_u32(buf, 0x14),
372 log_block_size: get_u32(buf, 0x18),
373 log_cluster_size: get_u32(buf, 0x1C),
374 blocks_per_group: get_u32(buf, 0x20),
375 clusters_per_group: get_u32(buf, 0x24),
376 inodes_per_group: get_u32(buf, 0x28),
377 mtime: get_u32(buf, 0x2C),
378 wtime: get_u32(buf, 0x30),
379 mount_count: get_u16(buf, 0x34),
380 max_mount_count: get_u16(buf, 0x36),
381 magic: get_u16(buf, 0x38),
382 state: get_u16(buf, 0x3A),
383 errors: get_u16(buf, 0x3C),
384 minor_rev_level: get_u16(buf, 0x3E),
385 lastcheck: get_u32(buf, 0x40),
386 check_interval: get_u32(buf, 0x44),
387 creator_os: get_u32(buf, 0x48),
388 rev_level: get_u32(buf, 0x4C),
389 def_resuid: get_u16(buf, 0x50),
390 def_resgid: get_u16(buf, 0x52),
391 first_ino: get_u32(buf, 0x54),
392 inode_size: get_u16(buf, 0x58),
393 block_group_nr: get_u16(buf, 0x5A),
394 feature_compat: get_u32(buf, 0x5C),
395 feature_incompat: get_u32(buf, 0x60),
396 feature_ro_compat: get_u32(buf, 0x64),
397 uuid: get_bytes(buf, 0x68),
398 volume_name: get_bytes(buf, 0x78),
399 last_mounted: get_bytes(buf, 0x88),
400 algorithm_usage_bitmap: get_u32(buf, 0xC8),
401 prealloc_blocks: get_u8(buf, 0xCC),
402 prealloc_dir_blocks: get_u8(buf, 0xCD),
403 reserved_gdt_blocks: get_u16(buf, 0xCE),
404 journal_uuid: get_bytes(buf, 0xD0),
405 journal_inum: get_u32(buf, 0xE0),
406 journal_dev: get_u32(buf, 0xE4),
407 last_orphan: get_u32(buf, 0xE8),
408 hash_seed,
409 def_hash_version: get_u8(buf, 0xFC),
410 journal_backup_type: get_u8(buf, 0xFD),
411 desc_size: get_u16(buf, 0xFE),
412 default_mount_opts: get_u32(buf, 0x100),
413 first_meta_bg: get_u32(buf, 0x104),
414 mkfs_time: get_u32(buf, 0x108),
415 journal_blocks,
416 blocks_count_hi: get_u32(buf, 0x150),
417 r_blocks_count_hi: get_u32(buf, 0x154),
418 free_blocks_count_hi: get_u32(buf, 0x158),
419 min_extra_isize: get_u16(buf, 0x15C),
420 want_extra_isize: get_u16(buf, 0x15E),
421 flags: get_u32(buf, 0x160),
422 raid_stride: get_u16(buf, 0x164),
423 mmp_interval: get_u16(buf, 0x166),
424 mmp_block: get_u64(buf, 0x168),
425 raid_stripe_width: get_u32(buf, 0x170),
426 log_groups_per_flex: get_u8(buf, 0x174),
427 checksum_type: get_u8(buf, 0x175),
428 reserved_pad: get_u16(buf, 0x176),
429 kbytes_written: get_u64(buf, 0x178),
430 snapshot_inum: get_u32(buf, 0x180),
431 snapshot_id: get_u32(buf, 0x184),
432 snapshot_r_blocks_count: get_u64(buf, 0x188),
433 snapshot_list: get_u32(buf, 0x190),
434 error_count: get_u32(buf, 0x194),
435 first_error_time: get_u32(buf, 0x198),
436 first_error_ino: get_u32(buf, 0x19C),
437 first_error_block: get_u64(buf, 0x1A0),
438 first_error_func: get_bytes(buf, 0x1A8),
439 first_error_line: get_u32(buf, 0x1C8),
440 last_error_time: get_u32(buf, 0x1CC),
441 last_error_ino: get_u32(buf, 0x1D0),
442 last_error_line: get_u32(buf, 0x1D4),
443 last_error_block: get_u64(buf, 0x1D8),
444 last_error_func: get_bytes(buf, 0x1E0),
445 mount_opts: get_bytes(buf, 0x200),
446 usr_quota_inum: get_u32(buf, 0x240),
447 grp_quota_inum: get_u32(buf, 0x244),
448 overhead_blocks: get_u32(buf, 0x248),
449 backup_bgs,
450 encrypt_algos: get_bytes(buf, 0x254),
451 encrypt_pw_salt: get_bytes(buf, 0x258),
452 lpf_ino: get_u32(buf, 0x268),
453 prj_quota_inum: get_u32(buf, 0x26C),
454 checksum_seed: get_u32(buf, 0x270),
455 wtime_hi: get_u8(buf, 0x274),
456 mtime_hi: get_u8(buf, 0x275),
457 mkfs_time_hi: get_u8(buf, 0x276),
458 lastcheck_hi: get_u8(buf, 0x277),
459 first_error_time_hi: get_u8(buf, 0x278),
460 last_error_time_hi: get_u8(buf, 0x279),
461 pad: get_bytes(buf, 0x27A),
462 reserved,
463 checksum: get_u32(buf, 0x3FC),
464 }
465 }
466
467 pub fn write_to(&self, buf: &mut [u8]) {
469 debug_assert!(buf.len() >= Self::SIZE);
470
471 buf[..Self::SIZE].fill(0);
473
474 put_u32(buf, 0x00, self.inodes_count);
475 put_u32(buf, 0x04, self.blocks_count_lo);
476 put_u32(buf, 0x08, self.r_blocks_count_lo);
477 put_u32(buf, 0x0C, self.free_blocks_count_lo);
478 put_u32(buf, 0x10, self.free_inodes_count);
479 put_u32(buf, 0x14, self.first_data_block);
480 put_u32(buf, 0x18, self.log_block_size);
481 put_u32(buf, 0x1C, self.log_cluster_size);
482 put_u32(buf, 0x20, self.blocks_per_group);
483 put_u32(buf, 0x24, self.clusters_per_group);
484 put_u32(buf, 0x28, self.inodes_per_group);
485 put_u32(buf, 0x2C, self.mtime);
486 put_u32(buf, 0x30, self.wtime);
487 put_u16(buf, 0x34, self.mount_count);
488 put_u16(buf, 0x36, self.max_mount_count);
489 put_u16(buf, 0x38, self.magic);
490 put_u16(buf, 0x3A, self.state);
491 put_u16(buf, 0x3C, self.errors);
492 put_u16(buf, 0x3E, self.minor_rev_level);
493 put_u32(buf, 0x40, self.lastcheck);
494 put_u32(buf, 0x44, self.check_interval);
495 put_u32(buf, 0x48, self.creator_os);
496 put_u32(buf, 0x4C, self.rev_level);
497 put_u16(buf, 0x50, self.def_resuid);
498 put_u16(buf, 0x52, self.def_resgid);
499 put_u32(buf, 0x54, self.first_ino);
500 put_u16(buf, 0x58, self.inode_size);
501 put_u16(buf, 0x5A, self.block_group_nr);
502 put_u32(buf, 0x5C, self.feature_compat);
503 put_u32(buf, 0x60, self.feature_incompat);
504 put_u32(buf, 0x64, self.feature_ro_compat);
505 put_bytes(buf, 0x68, &self.uuid);
506 put_bytes(buf, 0x78, &self.volume_name);
507 put_bytes(buf, 0x88, &self.last_mounted);
508 put_u32(buf, 0xC8, self.algorithm_usage_bitmap);
509 put_u8(buf, 0xCC, self.prealloc_blocks);
510 put_u8(buf, 0xCD, self.prealloc_dir_blocks);
511 put_u16(buf, 0xCE, self.reserved_gdt_blocks);
512 put_bytes(buf, 0xD0, &self.journal_uuid);
513 put_u32(buf, 0xE0, self.journal_inum);
514 put_u32(buf, 0xE4, self.journal_dev);
515 put_u32(buf, 0xE8, self.last_orphan);
516 for i in 0..4 {
517 put_u32(buf, 0xEC + i * 4, self.hash_seed[i]);
518 }
519 put_u8(buf, 0xFC, self.def_hash_version);
520 put_u8(buf, 0xFD, self.journal_backup_type);
521 put_u16(buf, 0xFE, self.desc_size);
522 put_u32(buf, 0x100, self.default_mount_opts);
523 put_u32(buf, 0x104, self.first_meta_bg);
524 put_u32(buf, 0x108, self.mkfs_time);
525 for i in 0..17 {
526 put_u32(buf, 0x10C + i * 4, self.journal_blocks[i]);
527 }
528 put_u32(buf, 0x150, self.blocks_count_hi);
529 put_u32(buf, 0x154, self.r_blocks_count_hi);
530 put_u32(buf, 0x158, self.free_blocks_count_hi);
531 put_u16(buf, 0x15C, self.min_extra_isize);
532 put_u16(buf, 0x15E, self.want_extra_isize);
533 put_u32(buf, 0x160, self.flags);
534 put_u16(buf, 0x164, self.raid_stride);
535 put_u16(buf, 0x166, self.mmp_interval);
536 put_u64(buf, 0x168, self.mmp_block);
537 put_u32(buf, 0x170, self.raid_stripe_width);
538 put_u8(buf, 0x174, self.log_groups_per_flex);
539 put_u8(buf, 0x175, self.checksum_type);
540 put_u16(buf, 0x176, self.reserved_pad);
541 put_u64(buf, 0x178, self.kbytes_written);
542 put_u32(buf, 0x180, self.snapshot_inum);
543 put_u32(buf, 0x184, self.snapshot_id);
544 put_u64(buf, 0x188, self.snapshot_r_blocks_count);
545 put_u32(buf, 0x190, self.snapshot_list);
546 put_u32(buf, 0x194, self.error_count);
547 put_u32(buf, 0x198, self.first_error_time);
548 put_u32(buf, 0x19C, self.first_error_ino);
549 put_u64(buf, 0x1A0, self.first_error_block);
550 put_bytes(buf, 0x1A8, &self.first_error_func);
551 put_u32(buf, 0x1C8, self.first_error_line);
552 put_u32(buf, 0x1CC, self.last_error_time);
553 put_u32(buf, 0x1D0, self.last_error_ino);
554 put_u32(buf, 0x1D4, self.last_error_line);
555 put_u64(buf, 0x1D8, self.last_error_block);
556 put_bytes(buf, 0x1E0, &self.last_error_func);
557 put_bytes(buf, 0x200, &self.mount_opts);
558 put_u32(buf, 0x240, self.usr_quota_inum);
559 put_u32(buf, 0x244, self.grp_quota_inum);
560 put_u32(buf, 0x248, self.overhead_blocks);
561 put_u32(buf, 0x24C, self.backup_bgs[0]);
562 put_u32(buf, 0x250, self.backup_bgs[1]);
563 put_bytes(buf, 0x254, &self.encrypt_algos);
564 put_bytes(buf, 0x258, &self.encrypt_pw_salt);
565 put_u32(buf, 0x268, self.lpf_ino);
566 put_u32(buf, 0x26C, self.prj_quota_inum);
567 put_u32(buf, 0x270, self.checksum_seed);
568 put_u8(buf, 0x274, self.wtime_hi);
569 put_u8(buf, 0x275, self.mtime_hi);
570 put_u8(buf, 0x276, self.mkfs_time_hi);
571 put_u8(buf, 0x277, self.lastcheck_hi);
572 put_u8(buf, 0x278, self.first_error_time_hi);
573 put_u8(buf, 0x279, self.last_error_time_hi);
574 put_bytes(buf, 0x27A, &self.pad);
575 for i in 0..96 {
576 put_u32(buf, 0x27C + i * 4, self.reserved[i]);
577 }
578 put_u32(buf, 0x3FC, self.checksum);
579 }
580}
581
582#[derive(Debug, Clone, Default)]
592pub struct GroupDescriptor {
593 pub block_bitmap_lo: u32,
594 pub inode_bitmap_lo: u32,
595 pub inode_table_lo: u32,
596 pub free_blocks_count_lo: u16,
597 pub free_inodes_count_lo: u16,
598 pub used_dirs_count_lo: u16,
599 pub flags: u16,
600 pub exclude_bitmap_lo: u32,
601 pub block_bitmap_csum_lo: u16,
602 pub inode_bitmap_csum_lo: u16,
603 pub itable_unused_lo: u16,
604 pub checksum: u16,
605}
606
607impl GroupDescriptor {
608 pub const SIZE: usize = 32;
609
610 pub fn read_from(buf: &[u8]) -> Self {
611 debug_assert!(buf.len() >= Self::SIZE);
612 Self {
613 block_bitmap_lo: get_u32(buf, 0x00),
614 inode_bitmap_lo: get_u32(buf, 0x04),
615 inode_table_lo: get_u32(buf, 0x08),
616 free_blocks_count_lo: get_u16(buf, 0x0C),
617 free_inodes_count_lo: get_u16(buf, 0x0E),
618 used_dirs_count_lo: get_u16(buf, 0x10),
619 flags: get_u16(buf, 0x12),
620 exclude_bitmap_lo: get_u32(buf, 0x14),
621 block_bitmap_csum_lo: get_u16(buf, 0x18),
622 inode_bitmap_csum_lo: get_u16(buf, 0x1A),
623 itable_unused_lo: get_u16(buf, 0x1C),
624 checksum: get_u16(buf, 0x1E),
625 }
626 }
627
628 pub fn write_to(&self, buf: &mut [u8]) {
629 debug_assert!(buf.len() >= Self::SIZE);
630 buf[..Self::SIZE].fill(0);
631
632 put_u32(buf, 0x00, self.block_bitmap_lo);
633 put_u32(buf, 0x04, self.inode_bitmap_lo);
634 put_u32(buf, 0x08, self.inode_table_lo);
635 put_u16(buf, 0x0C, self.free_blocks_count_lo);
636 put_u16(buf, 0x0E, self.free_inodes_count_lo);
637 put_u16(buf, 0x10, self.used_dirs_count_lo);
638 put_u16(buf, 0x12, self.flags);
639 put_u32(buf, 0x14, self.exclude_bitmap_lo);
640 put_u16(buf, 0x18, self.block_bitmap_csum_lo);
641 put_u16(buf, 0x1A, self.inode_bitmap_csum_lo);
642 put_u16(buf, 0x1C, self.itable_unused_lo);
643 put_u16(buf, 0x1E, self.checksum);
644 }
645}
646
647#[derive(Debug, Clone)]
652pub struct Inode {
653 pub mode: u16,
655 pub uid: u16,
656 pub size_lo: u32,
657 pub atime: u32,
658 pub ctime: u32,
659 pub mtime: u32,
660 pub dtime: u32,
661 pub gid: u16,
662 pub links_count: u16,
663 pub blocks_lo: u32,
664 pub flags: u32,
665 pub version: u32,
666
667 pub block: [u8; INODE_BLOCK_SIZE],
669
670 pub generation: u32,
671 pub xattr_block_lo: u32,
672 pub size_hi: u32,
673 pub obsolete_fragment_addr: u32,
674
675 pub blocks_hi: u16,
677 pub xattr_block_hi: u16,
678 pub uid_hi: u16,
679 pub gid_hi: u16,
680 pub checksum_lo: u16,
681 pub reserved: u16,
682
683 pub extra_isize: u16,
685 pub checksum_hi: u16,
686 pub ctime_extra: u32,
687 pub mtime_extra: u32,
688 pub atime_extra: u32,
689 pub crtime: u32,
690 pub crtime_extra: u32,
691 pub version_hi: u32,
692 pub projid: u32,
693
694 pub inline_xattrs: [u8; 96],
696}
697
698impl Default for Inode {
699 fn default() -> Self {
700 Self {
701 mode: 0,
702 uid: 0,
703 size_lo: 0,
704 atime: 0,
705 ctime: 0,
706 mtime: 0,
707 dtime: 0,
708 gid: 0,
709 links_count: 0,
710 blocks_lo: 0,
711 flags: 0,
712 version: 0,
713 block: [0; INODE_BLOCK_SIZE],
714 generation: 0,
715 xattr_block_lo: 0,
716 size_hi: 0,
717 obsolete_fragment_addr: 0,
718 blocks_hi: 0,
719 xattr_block_hi: 0,
720 uid_hi: 0,
721 gid_hi: 0,
722 checksum_lo: 0,
723 reserved: 0,
724 extra_isize: 0,
725 checksum_hi: 0,
726 ctime_extra: 0,
727 mtime_extra: 0,
728 atime_extra: 0,
729 crtime: 0,
730 crtime_extra: 0,
731 version_hi: 0,
732 projid: 0,
733 inline_xattrs: [0; 96],
734 }
735 }
736}
737
738impl Inode {
739 pub const SIZE: usize = INODE_SIZE as usize;
741
742 const OFF_MODE: usize = 0x00;
745 const OFF_UID: usize = 0x02;
746 const OFF_SIZE_LO: usize = 0x04;
747 const OFF_ATIME: usize = 0x08;
748 const OFF_CTIME: usize = 0x0C;
749 const OFF_MTIME: usize = 0x10;
750 const OFF_DTIME: usize = 0x14;
751 const OFF_GID: usize = 0x18;
752 const OFF_LINKS: usize = 0x1A;
753 const OFF_BLOCKS_LO: usize = 0x1C;
754 const OFF_FLAGS: usize = 0x20;
755 const OFF_VERSION: usize = 0x24;
756 const OFF_BLOCK: usize = 0x28; const OFF_GENERATION: usize = 0x64;
758 const OFF_XATTR_LO: usize = 0x68;
759 const OFF_SIZE_HI: usize = 0x6C;
760 const OFF_FRAG_ADDR: usize = 0x70;
761 const OFF_BLOCKS_HI: usize = 0x74;
763 const OFF_XATTR_HI: usize = 0x76;
764 const OFF_UID_HI: usize = 0x78;
765 const OFF_GID_HI: usize = 0x7A;
766 const OFF_CSUM_LO: usize = 0x7C;
767 const OFF_RESERVED: usize = 0x7E;
768 const OFF_EXTRA_ISIZE: usize = 0x80;
770 const OFF_CSUM_HI: usize = 0x82;
771 const OFF_CTIME_EXTRA: usize = 0x84;
772 const OFF_MTIME_EXTRA: usize = 0x88;
773 const OFF_ATIME_EXTRA: usize = 0x8C;
774 const OFF_CRTIME: usize = 0x90;
775 const OFF_CRTIME_EXTRA: usize = 0x94;
776 const OFF_VERSION_HI: usize = 0x98;
777 const OFF_PROJID: usize = 0x9C;
778 const OFF_INLINE_XATTRS: usize = INODE_ACTUAL_SIZE as usize;
780
781 pub fn read_from(buf: &[u8]) -> Self {
784 debug_assert!(buf.len() >= Self::SIZE);
785 Self {
786 mode: get_u16(buf, Self::OFF_MODE),
787 uid: get_u16(buf, Self::OFF_UID),
788 size_lo: get_u32(buf, Self::OFF_SIZE_LO),
789 atime: get_u32(buf, Self::OFF_ATIME),
790 ctime: get_u32(buf, Self::OFF_CTIME),
791 mtime: get_u32(buf, Self::OFF_MTIME),
792 dtime: get_u32(buf, Self::OFF_DTIME),
793 gid: get_u16(buf, Self::OFF_GID),
794 links_count: get_u16(buf, Self::OFF_LINKS),
795 blocks_lo: get_u32(buf, Self::OFF_BLOCKS_LO),
796 flags: get_u32(buf, Self::OFF_FLAGS),
797 version: get_u32(buf, Self::OFF_VERSION),
798 block: get_bytes(buf, Self::OFF_BLOCK),
799 generation: get_u32(buf, Self::OFF_GENERATION),
800 xattr_block_lo: get_u32(buf, Self::OFF_XATTR_LO),
801 size_hi: get_u32(buf, Self::OFF_SIZE_HI),
802 obsolete_fragment_addr: get_u32(buf, Self::OFF_FRAG_ADDR),
803 blocks_hi: get_u16(buf, Self::OFF_BLOCKS_HI),
804 xattr_block_hi: get_u16(buf, Self::OFF_XATTR_HI),
805 uid_hi: get_u16(buf, Self::OFF_UID_HI),
806 gid_hi: get_u16(buf, Self::OFF_GID_HI),
807 checksum_lo: get_u16(buf, Self::OFF_CSUM_LO),
808 reserved: get_u16(buf, Self::OFF_RESERVED),
809 extra_isize: get_u16(buf, Self::OFF_EXTRA_ISIZE),
810 checksum_hi: get_u16(buf, Self::OFF_CSUM_HI),
811 ctime_extra: get_u32(buf, Self::OFF_CTIME_EXTRA),
812 mtime_extra: get_u32(buf, Self::OFF_MTIME_EXTRA),
813 atime_extra: get_u32(buf, Self::OFF_ATIME_EXTRA),
814 crtime: get_u32(buf, Self::OFF_CRTIME),
815 crtime_extra: get_u32(buf, Self::OFF_CRTIME_EXTRA),
816 version_hi: get_u32(buf, Self::OFF_VERSION_HI),
817 projid: get_u32(buf, Self::OFF_PROJID),
818 inline_xattrs: get_bytes(buf, Self::OFF_INLINE_XATTRS),
819 }
820 }
821
822 pub fn write_to(&self, buf: &mut [u8]) {
823 debug_assert!(buf.len() >= Self::SIZE);
824 buf[..Self::SIZE].fill(0);
825
826 put_u16(buf, Self::OFF_MODE, self.mode);
827 put_u16(buf, Self::OFF_UID, self.uid);
828 put_u32(buf, Self::OFF_SIZE_LO, self.size_lo);
829 put_u32(buf, Self::OFF_ATIME, self.atime);
830 put_u32(buf, Self::OFF_CTIME, self.ctime);
831 put_u32(buf, Self::OFF_MTIME, self.mtime);
832 put_u32(buf, Self::OFF_DTIME, self.dtime);
833 put_u16(buf, Self::OFF_GID, self.gid);
834 put_u16(buf, Self::OFF_LINKS, self.links_count);
835 put_u32(buf, Self::OFF_BLOCKS_LO, self.blocks_lo);
836 put_u32(buf, Self::OFF_FLAGS, self.flags);
837 put_u32(buf, Self::OFF_VERSION, self.version);
838 put_bytes(buf, Self::OFF_BLOCK, &self.block);
839 put_u32(buf, Self::OFF_GENERATION, self.generation);
840 put_u32(buf, Self::OFF_XATTR_LO, self.xattr_block_lo);
841 put_u32(buf, Self::OFF_SIZE_HI, self.size_hi);
842 put_u32(buf, Self::OFF_FRAG_ADDR, self.obsolete_fragment_addr);
843 put_u16(buf, Self::OFF_BLOCKS_HI, self.blocks_hi);
844 put_u16(buf, Self::OFF_XATTR_HI, self.xattr_block_hi);
845 put_u16(buf, Self::OFF_UID_HI, self.uid_hi);
846 put_u16(buf, Self::OFF_GID_HI, self.gid_hi);
847 put_u16(buf, Self::OFF_CSUM_LO, self.checksum_lo);
848 put_u16(buf, Self::OFF_RESERVED, self.reserved);
849 put_u16(buf, Self::OFF_EXTRA_ISIZE, self.extra_isize);
850 put_u16(buf, Self::OFF_CSUM_HI, self.checksum_hi);
851 put_u32(buf, Self::OFF_CTIME_EXTRA, self.ctime_extra);
852 put_u32(buf, Self::OFF_MTIME_EXTRA, self.mtime_extra);
853 put_u32(buf, Self::OFF_ATIME_EXTRA, self.atime_extra);
854 put_u32(buf, Self::OFF_CRTIME, self.crtime);
855 put_u32(buf, Self::OFF_CRTIME_EXTRA, self.crtime_extra);
856 put_u32(buf, Self::OFF_VERSION_HI, self.version_hi);
857 put_u32(buf, Self::OFF_PROJID, self.projid);
858 put_bytes(buf, Self::OFF_INLINE_XATTRS, &self.inline_xattrs);
859 }
860
861 pub fn root_inode() -> Self {
868 let (time_lo, time_extra) = timestamp_now();
869 Self {
870 mode: file_mode::S_IFDIR | 0o755,
871 links_count: 2,
872 flags: inode_flags::HUGE_FILE,
873 extra_isize: EXTRA_ISIZE,
874 atime: time_lo,
875 ctime: time_lo,
876 mtime: time_lo,
877 crtime: time_lo,
878 atime_extra: time_extra,
879 ctime_extra: time_extra,
880 mtime_extra: time_extra,
881 crtime_extra: time_extra,
882 ..Self::default()
883 }
884 }
885
886 #[inline]
890 pub fn is_dir(&self) -> bool {
891 is_dir(self.mode)
892 }
893
894 #[inline]
896 pub fn is_reg(&self) -> bool {
897 is_reg(self.mode)
898 }
899
900 #[inline]
902 pub fn is_link(&self) -> bool {
903 is_link(self.mode)
904 }
905
906 #[inline]
910 pub fn file_size(&self) -> u64 {
911 (self.size_lo as u64) | ((self.size_hi as u64) << 32)
912 }
913
914 #[inline]
916 pub fn set_file_size(&mut self, size: u64) {
917 self.size_lo = size as u32;
918 self.size_hi = (size >> 32) as u32;
919 }
920
921 #[inline]
925 pub fn uid_full(&self) -> u32 {
926 (self.uid as u32) | ((self.uid_hi as u32) << 16)
927 }
928
929 #[inline]
931 pub fn gid_full(&self) -> u32 {
932 (self.gid as u32) | ((self.gid_hi as u32) << 16)
933 }
934
935 #[inline]
937 pub fn set_uid(&mut self, uid: u32) {
938 self.uid = uid as u16;
939 self.uid_hi = (uid >> 16) as u16;
940 }
941
942 #[inline]
944 pub fn set_gid(&mut self, gid: u32) {
945 self.gid = gid as u16;
946 self.gid_hi = (gid >> 16) as u16;
947 }
948}
949
950#[derive(Debug, Clone, Copy, Default)]
956pub struct ExtentHeader {
957 pub magic: u16,
959 pub entries: u16,
961 pub max: u16,
963 pub depth: u16,
965 pub generation: u32,
967}
968
969impl ExtentHeader {
970 pub const SIZE: usize = 12;
971
972 pub fn read_from(buf: &[u8]) -> Self {
973 debug_assert!(buf.len() >= Self::SIZE);
974 Self {
975 magic: get_u16(buf, 0),
976 entries: get_u16(buf, 2),
977 max: get_u16(buf, 4),
978 depth: get_u16(buf, 6),
979 generation: get_u32(buf, 8),
980 }
981 }
982
983 pub fn write_to(&self, buf: &mut [u8]) {
984 debug_assert!(buf.len() >= Self::SIZE);
985 put_u16(buf, 0, self.magic);
986 put_u16(buf, 2, self.entries);
987 put_u16(buf, 4, self.max);
988 put_u16(buf, 6, self.depth);
989 put_u32(buf, 8, self.generation);
990 }
991}
992
993#[derive(Debug, Clone, Copy, Default)]
999pub struct ExtentLeaf {
1000 pub block: u32,
1002 pub len: u16,
1005 pub start_hi: u16,
1007 pub start_lo: u32,
1009}
1010
1011impl ExtentLeaf {
1012 pub const SIZE: usize = 12;
1013
1014 pub fn read_from(buf: &[u8]) -> Self {
1015 debug_assert!(buf.len() >= Self::SIZE);
1016 Self {
1017 block: get_u32(buf, 0),
1018 len: get_u16(buf, 4),
1019 start_hi: get_u16(buf, 6),
1020 start_lo: get_u32(buf, 8),
1021 }
1022 }
1023
1024 pub fn write_to(&self, buf: &mut [u8]) {
1025 debug_assert!(buf.len() >= Self::SIZE);
1026 put_u32(buf, 0, self.block);
1027 put_u16(buf, 4, self.len);
1028 put_u16(buf, 6, self.start_hi);
1029 put_u32(buf, 8, self.start_lo);
1030 }
1031
1032 #[inline]
1034 pub fn start(&self) -> u64 {
1035 (self.start_lo as u64) | ((self.start_hi as u64) << 32)
1036 }
1037}
1038
1039#[derive(Debug, Clone, Copy, Default)]
1045pub struct ExtentIndex {
1046 pub block: u32,
1048 pub leaf_lo: u32,
1050 pub leaf_hi: u16,
1052 pub unused: u16,
1053}
1054
1055impl ExtentIndex {
1056 pub const SIZE: usize = 12;
1057
1058 pub fn read_from(buf: &[u8]) -> Self {
1059 debug_assert!(buf.len() >= Self::SIZE);
1060 Self {
1061 block: get_u32(buf, 0),
1062 leaf_lo: get_u32(buf, 4),
1063 leaf_hi: get_u16(buf, 8),
1064 unused: get_u16(buf, 10),
1065 }
1066 }
1067
1068 pub fn write_to(&self, buf: &mut [u8]) {
1069 debug_assert!(buf.len() >= Self::SIZE);
1070 put_u32(buf, 0, self.block);
1071 put_u32(buf, 4, self.leaf_lo);
1072 put_u16(buf, 8, self.leaf_hi);
1073 put_u16(buf, 10, self.unused);
1074 }
1075
1076 #[inline]
1078 pub fn leaf(&self) -> u64 {
1079 (self.leaf_lo as u64) | ((self.leaf_hi as u64) << 32)
1080 }
1081}
1082
1083#[derive(Debug, Clone, Copy, Default)]
1089pub struct ExtentTail {
1090 pub checksum: u32,
1091}
1092
1093impl ExtentTail {
1094 pub const SIZE: usize = 4;
1095
1096 pub fn read_from(buf: &[u8]) -> Self {
1097 debug_assert!(buf.len() >= Self::SIZE);
1098 Self {
1099 checksum: get_u32(buf, 0),
1100 }
1101 }
1102
1103 pub fn write_to(&self, buf: &mut [u8]) {
1104 debug_assert!(buf.len() >= Self::SIZE);
1105 put_u32(buf, 0, self.checksum);
1106 }
1107}
1108
1109#[derive(Debug, Clone, Default)]
1115pub struct DirectoryEntry {
1116 pub inode: u32,
1118 pub rec_len: u16,
1120 pub name_len: u8,
1122 pub file_type: u8,
1126}
1127
1128impl DirectoryEntry {
1129 pub const SIZE: usize = 8;
1131
1132 pub fn read_from(buf: &[u8]) -> Self {
1133 debug_assert!(buf.len() >= Self::SIZE);
1134 Self {
1135 inode: get_u32(buf, 0),
1136 rec_len: get_u16(buf, 4),
1137 name_len: get_u8(buf, 6),
1138 file_type: get_u8(buf, 7),
1139 }
1140 }
1141
1142 pub fn write_to(&self, buf: &mut [u8]) {
1143 debug_assert!(buf.len() >= Self::SIZE);
1144 put_u32(buf, 0, self.inode);
1145 put_u16(buf, 4, self.rec_len);
1146 put_u8(buf, 6, self.name_len);
1147 put_u8(buf, 7, self.file_type);
1148 }
1149}
1150
1151#[derive(Debug, Clone, Default)]
1158pub struct XAttrEntry {
1159 pub name_len: u8,
1161 pub name_index: u8,
1163 pub value_offset: u16,
1165 pub value_inum: u32,
1167 pub value_size: u32,
1169 pub hash: u32,
1171}
1172
1173impl XAttrEntry {
1174 pub const SIZE: usize = 16;
1176
1177 pub fn read_from(buf: &[u8]) -> Self {
1178 debug_assert!(buf.len() >= Self::SIZE);
1179 Self {
1180 name_len: get_u8(buf, 0),
1181 name_index: get_u8(buf, 1),
1182 value_offset: get_u16(buf, 2),
1183 value_inum: get_u32(buf, 4),
1184 value_size: get_u32(buf, 8),
1185 hash: get_u32(buf, 12),
1186 }
1187 }
1188
1189 pub fn write_to(&self, buf: &mut [u8]) {
1190 debug_assert!(buf.len() >= Self::SIZE);
1191 put_u8(buf, 0, self.name_len);
1192 put_u8(buf, 1, self.name_index);
1193 put_u16(buf, 2, self.value_offset);
1194 put_u32(buf, 4, self.value_inum);
1195 put_u32(buf, 8, self.value_size);
1196 put_u32(buf, 12, self.hash);
1197 }
1198}
1199
1200#[cfg(test)]
1205mod tests {
1206 use super::*;
1207
1208 #[test]
1209 fn superblock_roundtrip() {
1210 let mut sb = SuperBlock::default();
1211 sb.magic = SUPERBLOCK_MAGIC;
1212 sb.inodes_count = 1024;
1213 sb.blocks_count_lo = 4096;
1214 sb.log_block_size = 2; sb.uuid[0] = 0xDE;
1216 sb.uuid[15] = 0xAD;
1217 sb.checksum = 0xCAFE_BABE;
1218
1219 let mut buf = [0u8; SUPERBLOCK_SIZE];
1220 sb.write_to(&mut buf);
1221
1222 let sb2 = SuperBlock::read_from(&buf);
1223 assert_eq!(sb2.magic, SUPERBLOCK_MAGIC);
1224 assert_eq!(sb2.inodes_count, 1024);
1225 assert_eq!(sb2.blocks_count_lo, 4096);
1226 assert_eq!(sb2.log_block_size, 2);
1227 assert_eq!(sb2.uuid[0], 0xDE);
1228 assert_eq!(sb2.uuid[15], 0xAD);
1229 assert_eq!(sb2.checksum, 0xCAFE_BABE);
1230 }
1231
1232 #[test]
1233 fn group_descriptor_roundtrip() {
1234 let gd = GroupDescriptor {
1235 block_bitmap_lo: 100,
1236 inode_bitmap_lo: 101,
1237 inode_table_lo: 102,
1238 free_blocks_count_lo: 500,
1239 free_inodes_count_lo: 200,
1240 used_dirs_count_lo: 3,
1241 flags: bg_flags::INODE_ZEROED,
1242 exclude_bitmap_lo: 0,
1243 block_bitmap_csum_lo: 0x1234,
1244 inode_bitmap_csum_lo: 0x5678,
1245 itable_unused_lo: 190,
1246 checksum: 0xABCD,
1247 };
1248
1249 let mut buf = [0u8; GroupDescriptor::SIZE];
1250 gd.write_to(&mut buf);
1251
1252 let gd2 = GroupDescriptor::read_from(&buf);
1253 assert_eq!(gd2.block_bitmap_lo, 100);
1254 assert_eq!(gd2.free_blocks_count_lo, 500);
1255 assert_eq!(gd2.flags, bg_flags::INODE_ZEROED);
1256 assert_eq!(gd2.checksum, 0xABCD);
1257 }
1258
1259 #[test]
1260 fn inode_roundtrip() {
1261 let mut inode = Inode::default();
1262 inode.mode = file_mode::S_IFREG | 0o644;
1263 inode.set_file_size(0x1_DEAD_BEEF);
1264 inode.set_uid(100_000);
1265 inode.set_gid(200_000);
1266 inode.links_count = 1;
1267 inode.extra_isize = EXTRA_ISIZE;
1268 inode.block[0] = 0xFF;
1269
1270 let mut buf = [0u8; Inode::SIZE];
1271 inode.write_to(&mut buf);
1272
1273 let i2 = Inode::read_from(&buf);
1274 assert!(i2.is_reg());
1275 assert!(!i2.is_dir());
1276 assert!(!i2.is_link());
1277 assert_eq!(i2.file_size(), 0x1_DEAD_BEEF);
1278 assert_eq!(i2.uid_full(), 100_000);
1279 assert_eq!(i2.gid_full(), 200_000);
1280 assert_eq!(i2.links_count, 1);
1281 assert_eq!(i2.block[0], 0xFF);
1282 }
1283
1284 #[test]
1285 fn root_inode_has_correct_fields() {
1286 let root = Inode::root_inode();
1287 assert!(root.is_dir());
1288 assert_eq!(root.links_count, 2);
1289 assert_eq!(root.flags, inode_flags::HUGE_FILE);
1290 assert_eq!(root.extra_isize, EXTRA_ISIZE);
1291 assert_ne!(root.atime, 0);
1293 assert_ne!(root.ctime, 0);
1294 assert_ne!(root.mtime, 0);
1295 assert_ne!(root.crtime, 0);
1296 }
1297
1298 #[test]
1299 fn extent_header_roundtrip() {
1300 let hdr = ExtentHeader {
1301 magic: EXTENT_HEADER_MAGIC,
1302 entries: 3,
1303 max: 4,
1304 depth: 0,
1305 generation: 42,
1306 };
1307
1308 let mut buf = [0u8; ExtentHeader::SIZE];
1309 hdr.write_to(&mut buf);
1310
1311 let hdr2 = ExtentHeader::read_from(&buf);
1312 assert_eq!(hdr2.magic, EXTENT_HEADER_MAGIC);
1313 assert_eq!(hdr2.entries, 3);
1314 assert_eq!(hdr2.max, 4);
1315 assert_eq!(hdr2.depth, 0);
1316 assert_eq!(hdr2.generation, 42);
1317 }
1318
1319 #[test]
1320 fn extent_leaf_roundtrip() {
1321 let ext = ExtentLeaf {
1322 block: 0,
1323 len: 10,
1324 start_hi: 0x00AB,
1325 start_lo: 0xCDEF_0123,
1326 };
1327
1328 let mut buf = [0u8; ExtentLeaf::SIZE];
1329 ext.write_to(&mut buf);
1330
1331 let ext2 = ExtentLeaf::read_from(&buf);
1332 assert_eq!(ext2.block, 0);
1333 assert_eq!(ext2.len, 10);
1334 assert_eq!(ext2.start(), 0x00AB_CDEF_0123);
1335 }
1336
1337 #[test]
1338 fn extent_index_roundtrip() {
1339 let idx = ExtentIndex {
1340 block: 1000,
1341 leaf_lo: 0x1234_5678,
1342 leaf_hi: 0x00FF,
1343 unused: 0,
1344 };
1345
1346 let mut buf = [0u8; ExtentIndex::SIZE];
1347 idx.write_to(&mut buf);
1348
1349 let idx2 = ExtentIndex::read_from(&buf);
1350 assert_eq!(idx2.block, 1000);
1351 assert_eq!(idx2.leaf(), 0x00FF_1234_5678);
1352 }
1353
1354 #[test]
1355 fn extent_tail_roundtrip() {
1356 let tail = ExtentTail {
1357 checksum: 0xDEAD_BEEF,
1358 };
1359
1360 let mut buf = [0u8; ExtentTail::SIZE];
1361 tail.write_to(&mut buf);
1362
1363 let tail2 = ExtentTail::read_from(&buf);
1364 assert_eq!(tail2.checksum, 0xDEAD_BEEF);
1365 }
1366
1367 #[test]
1368 fn directory_entry_roundtrip() {
1369 let de = DirectoryEntry {
1370 inode: 42,
1371 rec_len: 20,
1372 name_len: 5,
1373 file_type: FileType::Regular as u8,
1374 };
1375
1376 let mut buf = [0u8; DirectoryEntry::SIZE];
1377 de.write_to(&mut buf);
1378
1379 let de2 = DirectoryEntry::read_from(&buf);
1380 assert_eq!(de2.inode, 42);
1381 assert_eq!(de2.rec_len, 20);
1382 assert_eq!(de2.name_len, 5);
1383 assert_eq!(de2.file_type, FileType::Regular as u8);
1384 }
1385
1386 #[test]
1387 fn xattr_entry_roundtrip() {
1388 let xa = XAttrEntry {
1389 name_len: 4,
1390 name_index: 1,
1391 value_offset: 0x100,
1392 value_inum: 0,
1393 value_size: 16,
1394 hash: 0xABCD_EF01,
1395 };
1396
1397 let mut buf = [0u8; XAttrEntry::SIZE];
1398 xa.write_to(&mut buf);
1399
1400 let xa2 = XAttrEntry::read_from(&buf);
1401 assert_eq!(xa2.name_len, 4);
1402 assert_eq!(xa2.name_index, 1);
1403 assert_eq!(xa2.value_offset, 0x100);
1404 assert_eq!(xa2.value_size, 16);
1405 assert_eq!(xa2.hash, 0xABCD_EF01);
1406 }
1407
1408 #[test]
1409 fn timestamp_now_produces_sane_values() {
1410 let (lo, extra) = timestamp_now();
1411 assert!(lo > 1_000_000_000);
1413 let nanos = extra >> 2;
1415 assert!(nanos < 1_000_000_000);
1416 }
1417
1418 #[test]
1419 fn inode_size_consistency() {
1420 assert_eq!(Inode::OFF_INLINE_XATTRS, 160);
1423 assert_eq!(Inode::SIZE, 256);
1425 }
1426
1427 #[test]
1428 fn superblock_default_is_all_zeros() {
1429 let sb = SuperBlock::default();
1430 let mut buf = [0xFFu8; SUPERBLOCK_SIZE];
1431 sb.write_to(&mut buf);
1432 assert!(buf.iter().all(|&b| b == 0));
1434 }
1435}