1use crate::block_io::BlockDevice;
44use crate::checksum::Checksummer;
45use crate::error::{Error, Result};
46
47#[derive(Clone, Copy)]
57pub struct ExtentVerifyCtx<'a> {
58 pub ino: u32,
60 pub generation: u32,
62 pub csum: &'a Checksummer,
64}
65
66impl<'a> ExtentVerifyCtx<'a> {
67 fn active(&self) -> bool {
69 self.csum.enabled
70 }
71}
72
73pub const EXT4_EXT_MAGIC: u16 = 0xF30A;
75
76pub const EXT_INIT_MAX_LEN: u16 = 32768;
79
80pub const EXT4_EXT_NODE_SIZE: usize = 12;
82
83pub const EXT4_EXT_MAX_DEPTH: u16 = 5;
87
88#[derive(Debug, Clone, Copy)]
90pub struct ExtentHeader {
91 pub magic: u16,
92 pub entries: u16,
93 pub max: u16,
94 pub depth: u16,
95 pub generation: u32,
96}
97
98impl ExtentHeader {
99 pub fn parse(raw: &[u8]) -> Result<Self> {
100 if raw.len() < EXT4_EXT_NODE_SIZE {
101 return Err(Error::CorruptExtentTree("header buffer too small"));
102 }
103 let magic = u16::from_le_bytes(raw[0x00..0x02].try_into().unwrap());
104 let entries = u16::from_le_bytes(raw[0x02..0x04].try_into().unwrap());
105 let max = u16::from_le_bytes(raw[0x04..0x06].try_into().unwrap());
106 let depth = u16::from_le_bytes(raw[0x06..0x08].try_into().unwrap());
107 let generation = u32::from_le_bytes(raw[0x08..0x0C].try_into().unwrap());
108
109 if magic != EXT4_EXT_MAGIC {
110 return Err(Error::CorruptExtentTree("bad extent header magic"));
111 }
112 if entries > max {
113 return Err(Error::CorruptExtentTree("entries > max"));
114 }
115 Ok(Self {
116 magic,
117 entries,
118 max,
119 depth,
120 generation,
121 })
122 }
123
124 pub fn is_leaf(&self) -> bool {
125 self.depth == 0
126 }
127}
128
129#[derive(Debug, Clone, Copy)]
131pub struct Extent {
132 pub logical_block: u32,
134 pub length: u16,
136 pub physical_block: u64,
138 pub uninitialized: bool,
140}
141
142impl Extent {
143 pub fn parse(raw: &[u8]) -> Result<Self> {
144 if raw.len() < EXT4_EXT_NODE_SIZE {
145 return Err(Error::CorruptExtentTree("leaf extent buffer too small"));
146 }
147 let logical_block = u32::from_le_bytes(raw[0x00..0x04].try_into().unwrap());
148 let ee_len = u16::from_le_bytes(raw[0x04..0x06].try_into().unwrap());
149 let start_hi = u16::from_le_bytes(raw[0x06..0x08].try_into().unwrap());
150 let start_lo = u32::from_le_bytes(raw[0x08..0x0C].try_into().unwrap());
151
152 let (length, uninitialized) = if ee_len > EXT_INIT_MAX_LEN {
153 (ee_len - EXT_INIT_MAX_LEN, true)
154 } else {
155 (ee_len, false)
156 };
157 let physical_block = ((start_hi as u64) << 32) | start_lo as u64;
158
159 Ok(Self {
160 logical_block,
161 length,
162 physical_block,
163 uninitialized,
164 })
165 }
166
167 pub fn contains(&self, logical: u64) -> bool {
169 let start = self.logical_block as u64;
170 let end = start + self.length as u64;
171 logical >= start && logical < end
172 }
173
174 pub fn map(&self, logical: u64) -> u64 {
176 debug_assert!(self.contains(logical));
177 self.physical_block + (logical - self.logical_block as u64)
178 }
179}
180
181#[derive(Debug, Clone, Copy)]
183pub struct ExtentIdx {
184 pub logical_block: u32,
186 pub leaf_block: u64,
188}
189
190impl ExtentIdx {
191 pub fn parse(raw: &[u8]) -> Result<Self> {
192 if raw.len() < EXT4_EXT_NODE_SIZE {
193 return Err(Error::CorruptExtentTree("index entry buffer too small"));
194 }
195 let logical_block = u32::from_le_bytes(raw[0x00..0x04].try_into().unwrap());
196 let leaf_lo = u32::from_le_bytes(raw[0x04..0x08].try_into().unwrap());
197 let leaf_hi = u16::from_le_bytes(raw[0x08..0x0A].try_into().unwrap());
198 let leaf_block = ((leaf_hi as u64) << 32) | leaf_lo as u64;
200 Ok(Self {
201 logical_block,
202 leaf_block,
203 })
204 }
205}
206
207pub fn lookup(
213 root: &[u8],
214 dev: &dyn BlockDevice,
215 block_size: u32,
216 logical_block: u64,
217) -> Result<Option<Extent>> {
218 lookup_verified(root, dev, block_size, logical_block, None)
219}
220
221pub fn lookup_verified(
226 root: &[u8],
227 dev: &dyn BlockDevice,
228 block_size: u32,
229 logical_block: u64,
230 ctx: Option<&ExtentVerifyCtx>,
231) -> Result<Option<Extent>> {
232 let mut header = ExtentHeader::parse(root)?;
234 if header.depth > EXT4_EXT_MAX_DEPTH {
235 return Err(Error::CorruptExtentTree(
236 "extent tree depth exceeds spec maximum",
237 ));
238 }
239 let mut node: Vec<u8>;
243 let mut cursor: &[u8] = root;
244 let mut descents_remaining = header.depth as usize;
248
249 loop {
250 if header.is_leaf() {
251 let mut found: Option<Extent> = None;
255 for i in 0..header.entries {
256 let off = EXT4_EXT_NODE_SIZE * (1 + i as usize);
257 if off + EXT4_EXT_NODE_SIZE > cursor.len() {
258 return Err(Error::CorruptExtentTree("leaf entry out of range"));
259 }
260 let ext = Extent::parse(&cursor[off..off + EXT4_EXT_NODE_SIZE])?;
261 if ext.contains(logical_block) {
262 found = Some(ext);
263 break;
264 }
265 if (ext.logical_block as u64) > logical_block {
267 break;
268 }
269 }
270 return Ok(found);
271 }
272
273 let mut chosen_idx: Option<ExtentIdx> = None;
285 let mut first_idx: Option<ExtentIdx> = None;
286 for i in 0..header.entries {
287 let off = EXT4_EXT_NODE_SIZE * (1 + i as usize);
288 if off + EXT4_EXT_NODE_SIZE > cursor.len() {
289 return Err(Error::CorruptExtentTree("index entry out of range"));
290 }
291 let idx = ExtentIdx::parse(&cursor[off..off + EXT4_EXT_NODE_SIZE])?;
292 if first_idx.is_none() {
293 first_idx = Some(idx);
294 }
295 if (idx.logical_block as u64) <= logical_block {
296 chosen_idx = Some(idx);
297 } else {
298 break;
299 }
300 }
301 let idx = chosen_idx
304 .or(first_idx)
305 .ok_or(Error::CorruptExtentTree("index node holds no entries"))?;
306
307 let mut buf = vec![0u8; block_size as usize];
309 let child_offset =
310 idx.leaf_block
311 .checked_mul(block_size as u64)
312 .ok_or(Error::CorruptExtentTree(
313 "extent index: child block offset overflow",
314 ))?;
315 dev.read_at(child_offset, &mut buf)?;
316 if let Some(c) = ctx {
317 if c.active() && !c.csum.verify_extent_tail(c.ino, c.generation, &buf) {
318 return Err(Error::BadChecksum {
319 what: "extent block",
320 });
321 }
322 }
323 node = buf;
324 cursor = &node[..];
325 header = ExtentHeader::parse(cursor)?;
326 if descents_remaining == 0 {
327 return Err(Error::CorruptExtentTree(
328 "extent tree descended past claimed depth",
329 ));
330 }
331 descents_remaining -= 1;
332 }
333}
334
335pub fn collect_all(root: &[u8], dev: &dyn BlockDevice, block_size: u32) -> Result<Vec<Extent>> {
338 let header = ExtentHeader::parse(root)?;
339 if header.depth > EXT4_EXT_MAX_DEPTH {
340 return Err(Error::CorruptExtentTree(
341 "extent tree depth exceeds spec maximum",
342 ));
343 }
344 let mut out = Vec::new();
345 walk(
346 root,
347 &header,
348 dev,
349 block_size,
350 &mut out,
351 &mut Vec::new(),
352 header.depth,
353 )?;
354 Ok(out)
355}
356
357pub fn collect_all_with_nodes(
361 root: &[u8],
362 dev: &dyn BlockDevice,
363 block_size: u32,
364) -> Result<(Vec<Extent>, Vec<u64>)> {
365 let header = ExtentHeader::parse(root)?;
366 if header.depth > EXT4_EXT_MAX_DEPTH {
367 return Err(Error::CorruptExtentTree(
368 "extent tree depth exceeds spec maximum",
369 ));
370 }
371 let mut out = Vec::new();
372 let mut nodes = Vec::new();
373 walk(
374 root,
375 &header,
376 dev,
377 block_size,
378 &mut out,
379 &mut nodes,
380 header.depth,
381 )?;
382 Ok((out, nodes))
383}
384
385fn walk(
386 node: &[u8],
387 header: &ExtentHeader,
388 dev: &dyn BlockDevice,
389 block_size: u32,
390 out: &mut Vec<Extent>,
391 nodes: &mut Vec<u64>,
392 descents_remaining: u16,
393) -> Result<()> {
394 if header.is_leaf() {
395 for i in 0..header.entries {
396 let off = EXT4_EXT_NODE_SIZE * (1 + i as usize);
397 if off + EXT4_EXT_NODE_SIZE > node.len() {
398 return Err(Error::CorruptExtentTree("leaf entry out of range"));
399 }
400 out.push(Extent::parse(&node[off..off + EXT4_EXT_NODE_SIZE])?);
401 }
402 return Ok(());
403 }
404
405 if descents_remaining == 0 {
406 return Err(Error::CorruptExtentTree(
407 "extent tree descended past claimed depth",
408 ));
409 }
410
411 for i in 0..header.entries {
412 let off = EXT4_EXT_NODE_SIZE * (1 + i as usize);
413 if off + EXT4_EXT_NODE_SIZE > node.len() {
414 return Err(Error::CorruptExtentTree("index entry out of range"));
415 }
416 let idx = ExtentIdx::parse(&node[off..off + EXT4_EXT_NODE_SIZE])?;
417 let mut buf = vec![0u8; block_size as usize];
418 let child_offset =
419 idx.leaf_block
420 .checked_mul(block_size as u64)
421 .ok_or(Error::CorruptExtentTree(
422 "extent walk: child block offset overflow",
423 ))?;
424 dev.read_at(child_offset, &mut buf)?;
425 nodes.push(idx.leaf_block);
426 let child_header = ExtentHeader::parse(&buf)?;
427 walk(
428 &buf,
429 &child_header,
430 dev,
431 block_size,
432 out,
433 nodes,
434 descents_remaining - 1,
435 )?;
436 }
437 Ok(())
438}
439
440pub fn map_logical(
444 root: &[u8],
445 dev: &dyn BlockDevice,
446 block_size: u32,
447 logical_block: u64,
448) -> Result<Option<u64>> {
449 map_logical_verified(root, dev, block_size, logical_block, None)
450}
451
452pub fn map_logical_verified(
455 root: &[u8],
456 dev: &dyn BlockDevice,
457 block_size: u32,
458 logical_block: u64,
459 ctx: Option<&ExtentVerifyCtx>,
460) -> Result<Option<u64>> {
461 match lookup_verified(root, dev, block_size, logical_block, ctx)? {
462 Some(ext) if !ext.uninitialized => Ok(Some(ext.map(logical_block))),
463 Some(_) => Ok(None), None => Ok(None), }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 fn make_root(ext_count: u16) -> Vec<u8> {
474 let mut buf = vec![0u8; 60];
475 buf[0..2].copy_from_slice(&EXT4_EXT_MAGIC.to_le_bytes());
477 buf[2..4].copy_from_slice(&ext_count.to_le_bytes());
478 buf[4..6].copy_from_slice(&4u16.to_le_bytes()); buf[6..8].copy_from_slice(&0u16.to_le_bytes()); buf[8..12].copy_from_slice(&0u32.to_le_bytes());
481 buf
482 }
483
484 fn set_leaf(buf: &mut [u8], i: usize, log: u32, len: u16, phys: u64) {
485 let off = 12 * (1 + i);
486 buf[off..off + 4].copy_from_slice(&log.to_le_bytes());
487 buf[off + 4..off + 6].copy_from_slice(&len.to_le_bytes());
488 buf[off + 6..off + 8].copy_from_slice(&((phys >> 32) as u16).to_le_bytes());
489 buf[off + 8..off + 12].copy_from_slice(&((phys & 0xFFFF_FFFF) as u32).to_le_bytes());
490 }
491
492 #[test]
493 fn leaf_lookup_finds_mapping() {
494 let mut buf = make_root(2);
495 set_leaf(&mut buf, 0, 0, 4, 100); set_leaf(&mut buf, 1, 4, 2, 200); struct Dummy;
500 impl BlockDevice for Dummy {
501 fn read_at(&self, _o: u64, _b: &mut [u8]) -> Result<()> {
502 unreachable!()
503 }
504 fn size_bytes(&self) -> u64 {
505 0
506 }
507 }
508
509 let r = lookup(&buf, &Dummy, 4096, 0).unwrap().unwrap();
510 assert_eq!(r.map(0), 100);
511
512 let r = lookup(&buf, &Dummy, 4096, 3).unwrap().unwrap();
513 assert_eq!(r.map(3), 103);
514
515 let r = lookup(&buf, &Dummy, 4096, 5).unwrap().unwrap();
516 assert_eq!(r.map(5), 201);
517
518 assert!(lookup(&buf, &Dummy, 4096, 10).unwrap().is_none());
520 }
521
522 #[test]
523 fn uninitialized_extent_maps_to_none() {
524 let mut buf = make_root(1);
525 set_leaf(&mut buf, 0, 0, EXT_INIT_MAX_LEN + 4, 500);
527
528 struct Dummy;
529 impl BlockDevice for Dummy {
530 fn read_at(&self, _o: u64, _b: &mut [u8]) -> Result<()> {
531 unreachable!()
532 }
533 fn size_bytes(&self) -> u64 {
534 0
535 }
536 }
537
538 assert!(map_logical(&buf, &Dummy, 4096, 2).unwrap().is_none());
540
541 let ext = lookup(&buf, &Dummy, 4096, 2).unwrap().unwrap();
542 assert!(ext.uninitialized);
543 assert_eq!(ext.length, 4);
544 }
545
546 #[test]
547 fn rejects_bad_magic() {
548 let mut buf = make_root(0);
549 buf[0] = 0x00;
550 buf[1] = 0x00;
551 let err = ExtentHeader::parse(&buf).unwrap_err();
552 assert!(matches!(err, Error::CorruptExtentTree(_)));
553 }
554
555 #[test]
556 fn rejects_entries_gt_max() {
557 let mut buf = make_root(10); buf[4..6].copy_from_slice(&4u16.to_le_bytes());
559 let err = ExtentHeader::parse(&buf).unwrap_err();
560 assert!(matches!(err, Error::CorruptExtentTree(_)));
561 }
562
563 #[test]
564 fn rejects_impossible_depth() {
565 let mut buf = make_root(0);
567 buf[6..8].copy_from_slice(&99u16.to_le_bytes());
568
569 struct Dummy;
570 impl BlockDevice for Dummy {
571 fn read_at(&self, _o: u64, _b: &mut [u8]) -> Result<()> {
572 unreachable!()
573 }
574 fn size_bytes(&self) -> u64 {
575 0
576 }
577 }
578
579 assert!(matches!(
581 lookup(&buf, &Dummy, 4096, 0),
582 Err(Error::CorruptExtentTree(_))
583 ));
584 assert!(matches!(
585 collect_all(&buf, &Dummy, 4096),
586 Err(Error::CorruptExtentTree(_))
587 ));
588 }
589
590 fn make_extent(logical_block: u32, length: u16, physical_block: u64) -> Extent {
591 Extent {
592 logical_block,
593 length,
594 physical_block,
595 uninitialized: false,
596 }
597 }
598
599 #[test]
600 fn extent_contains_within_range() {
601 let e = make_extent(10, 5, 100); assert!(e.contains(10));
603 assert!(e.contains(12));
604 assert!(e.contains(14));
605 }
606
607 #[test]
608 fn extent_contains_rejects_before_start() {
609 let e = make_extent(10, 5, 100);
610 assert!(!e.contains(9));
611 assert!(!e.contains(0));
612 }
613
614 #[test]
615 fn extent_contains_rejects_at_end() {
616 let e = make_extent(10, 5, 100); assert!(!e.contains(15));
618 assert!(!e.contains(100));
619 }
620
621 #[test]
622 fn extent_contains_single_block_extent() {
623 let e = make_extent(7, 1, 50);
624 assert!(e.contains(7));
625 assert!(!e.contains(6));
626 assert!(!e.contains(8));
627 }
628
629 #[test]
630 fn extent_map_start_of_extent() {
631 let e = make_extent(10, 5, 100);
632 assert_eq!(e.map(10), 100);
633 }
634
635 #[test]
636 fn extent_map_interior_block() {
637 let e = make_extent(10, 5, 100);
638 assert_eq!(e.map(13), 103);
639 }
640
641 #[test]
642 fn extent_map_last_block() {
643 let e = make_extent(10, 5, 100);
644 assert_eq!(e.map(14), 104);
645 }
646
647 #[test]
648 fn extent_parse_rejects_short_buffer() {
649 let short = vec![0u8; 4];
650 assert!(Extent::parse(&short).is_err());
651 }
652
653 #[test]
654 fn extent_parse_uninitialized_flag() {
655 let mut raw = vec![0u8; 12];
656 let uninit_len: u16 = EXT_INIT_MAX_LEN + 1;
658 raw[4..6].copy_from_slice(&uninit_len.to_le_bytes());
659 let e = Extent::parse(&raw).unwrap();
660 assert!(e.uninitialized);
661 assert_eq!(e.length, 1);
662 }
663
664 #[test]
665 fn extent_parse_initialized() {
666 let mut raw = vec![0u8; 12];
667 raw[0..4].copy_from_slice(&5u32.to_le_bytes()); raw[4..6].copy_from_slice(&3u16.to_le_bytes()); raw[8..12].copy_from_slice(&200u32.to_le_bytes()); let e = Extent::parse(&raw).unwrap();
671 assert_eq!(e.logical_block, 5);
672 assert_eq!(e.length, 3);
673 assert_eq!(e.physical_block, 200);
674 assert!(!e.uninitialized);
675 }
676
677 mod needs_host {
679 use super::*;
680 use crate::block_io::FileDevice;
681 use crate::fs::Filesystem;
682 use crate::inode::Inode;
683 use std::sync::Arc;
684
685 #[test]
690 fn ext4_basic_root_inode_has_extent() {
691 let path = fs_ext4_test_support::fixture(env!("CARGO_MANIFEST_DIR"), "ext4-basic.img");
692 let file = FileDevice::open(&path).unwrap_or_else(|e| panic!("open {path}: {e}"));
693 let dev: Arc<dyn BlockDevice> = Arc::new(file);
694 let fs = Filesystem::mount(dev.clone()).expect("mount");
695 let raw = fs.read_inode_raw(2).expect("root inode"); let inode = Inode::parse(&raw).expect("parse inode");
697
698 assert!(inode.is_dir(), "root inode is not a directory");
699 assert!(inode.has_extents(), "root inode does not use extents");
700
701 let header = ExtentHeader::parse(&inode.block).expect("parse extent header");
702 assert_eq!(header.magic, EXT4_EXT_MAGIC);
703 assert_eq!(header.depth, 0, "expected a leaf root for tiny test image");
704 assert!(header.entries >= 1);
705
706 let all = collect_all(&inode.block, dev.as_ref(), fs.sb.block_size())
707 .expect("collect extents");
708 assert!(!all.is_empty());
709 assert_eq!(all[0].logical_block, 0);
711 assert!(!all[0].uninitialized);
712
713 let first_phys = all[0].map(0);
716 let mut blk = vec![0u8; fs.sb.block_size() as usize];
717 dev.read_at(first_phys * fs.sb.block_size() as u64, &mut blk)
718 .unwrap();
719
720 let entries = crate::dir::parse_block(&blk, true).expect("parse dir block");
721 assert!(!entries.is_empty());
722 assert_eq!(entries[0].name, b".");
723 }
724 }
725}