Skip to main content

ferrosys/btrfs/
btree.rs

1//! The B-tree engine: one descent, one iteration, and the bounds that make both terminate on
2//! an image that was crafted rather than formatted.
3//!
4//! Every tree in a btrfs is the same shape — internal nodes of `(key, child address)` pairs
5//! over leaves of `(key, data)` items, sorted by the key tuple — so there is one engine and
6//! every tree is read through it. What an item *means* is decided by whoever asked for it;
7//! this module knows only that items have keys and that keys are ordered.
8//!
9//! # What bounds a walk
10//!
11//! An untrusted image can describe a tree that is not one, and each way of doing so has its
12//! own guard:
13//!
14//! - **A count larger than the block holds.** Checked against the room the block has, in the
15//!   units the block's own level says it holds — 25 bytes per item, 33 per child pointer.
16//! - **An item whose data escapes its leaf.** Checked in two directions, because a leaf fills
17//!   from both ends: the data must begin past the array describing it and end within the
18//!   block. The arithmetic is 64-bit whatever the target's pointer width is, so a crafted
19//!   offset and length behave the same on a 32-bit machine as on the one this is developed
20//!   on.
21//! - **A leaf whose data is not packed.** Every item's data abuts its neighbour's, so a leaf
22//!   whose data has been moved with its offsets moved to match — every item still inside the
23//!   block, every item pointing at bytes that are not its own — is refused. Nothing about a
24//!   bound sees that one, and what a reader would otherwise hand back is one record's bytes
25//!   under another record's key.
26//! - **A child pointer that leads back up.** Every address a descent visits is remembered, and
27//!   meeting one twice is a refusal rather than a loop.
28//! - **A child at the wrong height.** A child must be exactly one level below its parent, so a
29//!   descent has a decreasing measure independent of the visited set and terminates whatever
30//!   the addresses say.
31//! - **Keys out of order.** A tree that is not sorted is not a tree, and a search over one
32//!   silently misses items rather than failing. Every key a walk visits is held against the
33//!   one before it.
34//! - **A tree larger than the caller will hold.** [`Limits::max_walk_entries`](crate::Limits::max_walk_entries) caps the items
35//!   one walk visits.
36//!
37//! This module does I/O, through the volume it borrows.
38
39use std::collections::BTreeSet;
40use std::io::{Read, Seek};
41
42use super::ondisk::DiskKey;
43use super::volume::{ReadError, TreeBlock, TreeRoot, Volume};
44
45/// One item, taken out of the tree that held it.
46#[derive(Clone, PartialEq, Eq, Debug)]
47pub struct Located {
48    /// The key it was found under.
49    pub key: DiskKey,
50    /// Its bytes, which are as long as the item said and no longer.
51    pub data: Vec<u8>,
52}
53
54/// How many blocks stand above `leaves`, level by level, in a tree of this fan-out.
55///
56/// The first entry is the level directly above the leaves and the last is always one — the
57/// root. A tree whose leaves are one block has nothing above them, and the answer is empty:
58/// that leaf *is* the root.
59///
60/// The arithmetic is the same whether it is being asked in advance as a bound
61/// ([`geometry`](super::geometry)) or exactly of records already packed
62/// ([`materialize`](super::materialize)), so it is asked in one place. `fan_out` is floored at
63/// two, because a level whose blocks hold one child each never narrows and the stack would not
64/// end — a floor rather than an error, since the only caller that could reach it derives the
65/// fan-out from a node size the format has already accepted.
66pub(super) fn levels_above(leaves: u64, fan_out: u64) -> Vec<u64> {
67    let fan_out = fan_out.max(2);
68    let mut levels = Vec::new();
69    let mut width = leaves;
70    while width > 1 {
71        width = width.div_ceil(fan_out);
72        levels.push(width);
73    }
74    levels
75}
76
77/// A handle on one tree of a volume, for searching or iterating it.
78///
79/// Borrowed from the [`Volume`] rather than owning anything, so a caller moves between trees
80/// without reopening the filesystem — and so that every block either reads goes through the
81/// one chunk map and the one checksum check.
82pub struct Tree<'a, R> {
83    volume: &'a mut Volume<R>,
84    root: TreeRoot,
85}
86
87impl<'a, R: Read + Seek> Tree<'a, R> {
88    /// A handle on the tree `root` names.
89    pub(super) fn new(volume: &'a mut Volume<R>, root: TreeRoot) -> Self {
90        Self { volume, root }
91    }
92
93    /// Which tree this is, and where it begins.
94    #[must_use]
95    pub fn root(&self) -> TreeRoot {
96        self.root
97    }
98
99    /// Visit every item, in key order.
100    ///
101    /// The closure answers whether to keep going, so a caller looking for one thing stops
102    /// where it finds it rather than reading the rest of the tree.
103    ///
104    /// # Errors
105    ///
106    /// Whatever reading a block does, and the tree-shape refusals this module's documentation
107    /// lists.
108    pub fn for_each_item<F>(&mut self, mut visit: F) -> Result<(), ReadError>
109    where
110        F: FnMut(&DiskKey, &[u8]) -> bool,
111    {
112        self.drive(DiskKey::MIN, &mut |_| true, &mut visit)
113    }
114
115    /// Visit every item at or after `from`, in key order.
116    ///
117    /// The descent goes straight to the first item at or after the key rather than walking the
118    /// tree from its start, which is what makes "the entries of this directory" one descent
119    /// instead of a scan.
120    ///
121    /// # Errors
122    ///
123    /// As [`for_each_item`](Self::for_each_item).
124    pub fn for_each_item_from<F>(&mut self, from: DiskKey, mut visit: F) -> Result<(), ReadError>
125    where
126        F: FnMut(&DiskKey, &[u8]) -> bool,
127    {
128        self.drive(from, &mut |_| true, &mut visit)
129    }
130
131    /// Visit every block of the tree, in the order a depth-first descent meets them.
132    ///
133    /// Every block is fetched through the chunk map and its checksum verified before the
134    /// closure sees it, so a walk that completes is a statement that every block of the tree
135    /// verified. The items are bounds-checked on the way past whether or not the closure looks
136    /// at them, which is what makes this a verification pass rather than a header read.
137    ///
138    /// # Errors
139    ///
140    /// As [`for_each_item`](Self::for_each_item).
141    pub fn for_each_block<F>(&mut self, mut visit: F) -> Result<(), ReadError>
142    where
143        F: FnMut(&TreeBlock) -> bool,
144    {
145        self.drive(DiskKey::MIN, &mut visit, &mut |_, _| true)
146    }
147
148    /// The first item at or after `key`, or [`None`] where the tree holds none.
149    ///
150    /// # Errors
151    ///
152    /// As [`for_each_item`](Self::for_each_item).
153    pub fn find_first(&mut self, key: DiskKey) -> Result<Option<Located>, ReadError> {
154        let mut found = None;
155        self.for_each_item_from(key, |key, data| {
156            found = Some(Located {
157                key: *key,
158                data: data.to_vec(),
159            });
160            false
161        })?;
162        Ok(found)
163    }
164
165    /// The item stored under exactly `key`, or [`None`] where there is none.
166    ///
167    /// # Errors
168    ///
169    /// As [`for_each_item`](Self::for_each_item).
170    pub fn find_exact(&mut self, key: DiskKey) -> Result<Option<Located>, ReadError> {
171        Ok(self.find_first(key)?.filter(|found| found.key == key))
172    }
173
174    /// The last item at or before `key`, or [`None`] where every item in the tree is above it.
175    ///
176    /// The search a *range* needs, where [`find_first`](Self::find_first) is the one a *point*
177    /// needs. A record keyed by where a run begins covers everything up to the next one, so the
178    /// record covering a position is the last one at or before it — and asking for the first at
179    /// or after would find the record covering the *next* position and skip the one wanted.
180    /// That is how a file's extents are keyed, and reading from an offset in the middle of one
181    /// is the case that makes the difference visible.
182    ///
183    /// One descent, so it costs the height of the tree rather than a scan.
184    ///
185    /// # Errors
186    ///
187    /// As [`for_each_item`](Self::for_each_item).
188    pub fn find_at_or_before(&mut self, key: DiskKey) -> Result<Option<Located>, ReadError> {
189        let mut visited = BTreeSet::new();
190        visited.insert(self.root.bytenr);
191        let mut block = self.read_root()?;
192        block.check_leaf_packing()?;
193        loop {
194            if block.header().is_leaf() {
195                let at = partition(&block, &key, true)?;
196                let Some(index) = at.checked_sub(1) else {
197                    return Ok(None);
198                };
199                return Ok(Some(Located {
200                    key: block.item(index)?.key,
201                    data: block.item_data(index)?.to_vec(),
202                }));
203            }
204            let index = start_index(&block, &key)?;
205            block = self.child_of(&block, index, &mut visited)?;
206        }
207    }
208
209    /// The child at `index` of `parent`, with every guard a descent applies to one.
210    ///
211    /// Three of them, and each catches something the others cannot: an address already visited
212    /// is a tree that is not one, a child that is not exactly one level below its parent is a
213    /// descent with no decreasing measure, and a leaf whose items are not packed hands back one
214    /// record's bytes under another record's key. Every descent in this module goes through
215    /// here so that none of the three can be forgotten in one of them.
216    fn child_of(
217        &mut self,
218        parent: &TreeBlock,
219        index: usize,
220        visited: &mut BTreeSet<u64>,
221    ) -> Result<TreeBlock, ReadError> {
222        let level = parent.header().level;
223        let child_at = parent.key_ptr(index)?.blockptr;
224        if !visited.insert(child_at) {
225            return Err(ReadError::TreeCycle { logical: child_at });
226        }
227        let child = self.volume.read_block(child_at)?;
228        // A child exactly one level below its parent is what makes a descent terminate
229        // whatever the addresses say, and it is a separate guard from the visited set rather
230        // than a cheaper version of it: a crafted tree can point at a fresh block at every step
231        // and still never reach a leaf.
232        if u16::from(child.header().level) + 1 != u16::from(level) {
233            return Err(ReadError::BadTreeLevel {
234                logical: child_at,
235                level: child.header().level,
236                parent: level,
237            });
238        }
239        child.check_leaf_packing()?;
240        Ok(child)
241    }
242
243    /// How many items the tree holds, having read and verified every block on the way.
244    ///
245    /// # Errors
246    ///
247    /// As [`for_each_item`](Self::for_each_item).
248    pub fn count_items(&mut self) -> Result<u64, ReadError> {
249        let mut items = 0u64;
250        self.for_each_item(|_, _| {
251            items += 1;
252            true
253        })?;
254        Ok(items)
255    }
256
257    /// The tree's top block, checked against what the root item said it would be.
258    ///
259    /// The level is recorded in two places — the root item and the block's own header — and
260    /// holding them against each other is what catches a root item pointing at a block that is
261    /// not the one it describes.
262    fn read_root(&mut self) -> Result<TreeBlock, ReadError> {
263        let block = self.volume.read_block(self.root.bytenr)?;
264        if block.header().level != self.root.level {
265            return Err(ReadError::BadTreeLevel {
266                logical: self.root.bytenr,
267                level: block.header().level,
268                parent: self.root.level,
269            });
270        }
271        Ok(block)
272    }
273
274    /// The one descent, which every public form above is a wrapper over.
275    ///
276    /// A depth-first walk with an explicit stack: each frame is a block and how far through it
277    /// the walk has got. A block enters the stack at the entry `from` selects, so a walk from
278    /// a key descends straight to it and a walk from [`DiskKey::MIN`] starts every block at
279    /// zero.
280    fn drive(
281        &mut self,
282        from: DiskKey,
283        on_block: &mut dyn FnMut(&TreeBlock) -> bool,
284        on_item: &mut dyn FnMut(&DiskKey, &[u8]) -> bool,
285    ) -> Result<(), ReadError> {
286        let limit = self.volume.walk_limit();
287        let mut visited = BTreeSet::new();
288        visited.insert(self.root.bytenr);
289
290        let root = self.read_root()?;
291        root.check_leaf_packing()?;
292        if !on_block(&root) {
293            return Ok(());
294        }
295        let start = start_index(&root, &from)?;
296        let mut stack = vec![(root, start)];
297        let mut visits = 0usize;
298        let mut previous: Option<DiskKey> = None;
299
300        while let Some(top) = stack.len().checked_sub(1) {
301            let count = stack[top].0.count()?;
302            let index = stack[top].1;
303            if index >= count {
304                stack.pop();
305                continue;
306            }
307            stack[top].1 += 1;
308
309            if stack[top].0.header().is_leaf() {
310                visits += 1;
311                if visits > limit {
312                    return Err(ReadError::TooManyEntries {
313                        objectid: self.root.objectid,
314                        limit,
315                    });
316                }
317                let key = stack[top].0.item(index)?.key;
318                if previous.is_some_and(|last| key <= last) {
319                    return Err(ReadError::BadTreeBlock {
320                        logical: stack[top].0.header().bytenr,
321                        fault: "an item's key is not above the one before it",
322                    });
323                }
324                previous = Some(key);
325                let data = stack[top].0.item_data(index)?;
326                if !on_item(&key, data) {
327                    return Ok(());
328                }
329                continue;
330            }
331
332            let child = {
333                let (parent, _) = &stack[top];
334                self.child_of(parent, index, &mut visited)?
335            };
336            if !on_block(&child) {
337                return Ok(());
338            }
339            let start = start_index(&child, &from)?;
340            stack.push((child, start));
341        }
342        Ok(())
343    }
344}
345
346/// Where in `block` a walk from `from` begins.
347///
348/// For a **leaf** it is the first item at or after the key, so items below it are skipped. For
349/// a **node** it is the last child whose key is at or below it, since that child's subtree is
350/// where the key would be — and a node whose every key is above `from` starts at its first
351/// child.
352///
353/// Computed for every block rather than only for the leftmost path, which is the same answer
354/// and one fewer piece of state: a block entirely above `from` has no key at or below it, so
355/// the binary search lands on zero on its own.
356///
357/// The search is bounded whatever the block holds, so a block whose keys are not sorted
358/// answers with a wrong index and never with one outside the block. That a tree is sorted is
359/// checked where a walk passes each key, which is where the check is free.
360fn start_index(block: &TreeBlock, from: &DiskKey) -> Result<usize, ReadError> {
361    if *from == DiskKey::MIN {
362        return Ok(0);
363    }
364    // A leaf counts the entries strictly below the key, since that is the first one to visit.
365    // A node counts those at or below it and steps back one, to the child whose subtree is
366    // where the key would be — and a node whose every key is above `from` steps back from
367    // zero, which is its first child.
368    let leaf = block.header().is_leaf();
369    let at = partition(block, from, !leaf)?;
370    Ok(if leaf { at } else { at.saturating_sub(1) })
371}
372
373/// How many of `block`'s entries sort below `from`, counting one equal to it where `inclusive`.
374///
375/// The one binary search over a block, in the two readings a descent needs of it. It is bounded
376/// whatever the block holds, so a block whose keys are not sorted answers with a wrong index
377/// and never with one outside the block. That a tree is sorted is checked where a walk passes
378/// each key, which is where the check is free.
379fn partition(block: &TreeBlock, from: &DiskKey, inclusive: bool) -> Result<usize, ReadError> {
380    let leaf = block.header().is_leaf();
381    let (mut lo, mut hi) = (0usize, block.count()?);
382    while lo < hi {
383        let mid = lo + (hi - lo) / 2;
384        let key = if leaf {
385            block.item(mid)?.key
386        } else {
387            block.key_ptr(mid)?.key
388        };
389        let below = if inclusive { key <= *from } else { key < *from };
390        if below { lo = mid + 1 } else { hi = mid }
391    }
392    Ok(lo)
393}
394
395#[cfg(test)]
396mod tests {
397    use super::*;
398    use crate::btrfs::forge::{
399        CHUNK_LENGTH, CHUNK_LOGICAL, FIRST_FREE_AT, Forge, NODE_SIZE, ROOT_TREE_AT, leaf, node,
400        seal,
401    };
402    use crate::btrfs::ondisk::{Header, Item, ItemType, objectid};
403    use crate::{Limits, OpenOptions};
404
405    /// A key in the root tree's own namespace, so a forged tree sorts the way a real one does.
406    fn key(n: u64) -> DiskKey {
407        DiskKey::new(n, ItemType::ROOT_ITEM, 0)
408    }
409
410    /// A leaf of `n` items, each carrying its own number as a byte.
411    fn items(range: std::ops::Range<u64>) -> Vec<(DiskKey, Vec<u8>)> {
412        range.map(|n| (key(n), vec![n as u8; 8])).collect()
413    }
414
415    /// Every key a walk of `forge`'s root tree visits.
416    fn walk(forge: &Forge) -> Result<Vec<DiskKey>, ReadError> {
417        let mut volume = Volume::open(forge.source())?;
418        let root = volume.root_tree();
419        let mut keys = Vec::new();
420        volume.tree(root).for_each_item(|k, _| {
421            keys.push(*k);
422            true
423        })?;
424        Ok(keys)
425    }
426
427    #[test]
428    fn a_walk_visits_every_item_of_a_tree_in_key_order() {
429        let mut forge = Forge::new();
430        forge.root_leaf(&items(1..40));
431        assert_eq!(
432            walk(&forge).expect("a well-formed tree"),
433            items(1..40).iter().map(|(k, _)| *k).collect::<Vec<_>>()
434        );
435    }
436
437    #[test]
438    fn a_walk_descends_through_a_node_and_reaches_every_leaf_below_it() {
439        // Two leaves under one node, which is the smallest tree that exercises a descent at
440        // all — a one-leaf tree never reads a child pointer.
441        let mut forge = Forge::new();
442        let (left, right) = (FIRST_FREE_AT, FIRST_FREE_AT + NODE_SIZE as u64);
443        forge
444            .block(left, &leaf(left, objectid::ROOT_TREE, &items(1..10)))
445            .block(right, &leaf(right, objectid::ROOT_TREE, &items(10..20)))
446            .root_node(1, &[(key(1), left), (key(10), right)]);
447        assert_eq!(walk(&forge).expect("a well-formed tree").len(), 19);
448    }
449
450    #[test]
451    fn a_seek_lands_where_a_walk_of_the_whole_tree_would_have_reached() {
452        // The property the descent's start index exists for, over a tree deep enough that the
453        // choice of child matters. Every key present is probed, and so is one below and one
454        // above each — the three places an off-by-one in the binary search would show.
455        let mut forge = Forge::new();
456        let (left, right) = (FIRST_FREE_AT, FIRST_FREE_AT + NODE_SIZE as u64);
457        forge
458            .block(left, &leaf(left, objectid::ROOT_TREE, &items(10..20)))
459            .block(right, &leaf(right, objectid::ROOT_TREE, &items(20..30)))
460            .root_node(1, &[(key(10), left), (key(20), right)]);
461
462        let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
463        let root = volume.root_tree();
464        let all: Vec<DiskKey> = (10..30).map(key).collect();
465        for probe in (5..35).map(key) {
466            let expected: Vec<DiskKey> = all.iter().copied().filter(|k| *k >= probe).collect();
467            let mut got = Vec::new();
468            volume
469                .tree(root)
470                .for_each_item_from(probe, |k, _| {
471                    got.push(*k);
472                    true
473                })
474                .expect("a well-formed tree");
475            assert_eq!(got, expected, "seeking to {probe:?}");
476            assert_eq!(
477                volume
478                    .tree(root)
479                    .find_first(probe)
480                    .expect("search")
481                    .map(|f| f.key),
482                expected.first().copied()
483            );
484        }
485        // And the exact form answers only where the key is genuinely there.
486        assert!(
487            volume
488                .tree(root)
489                .find_exact(key(15))
490                .expect("search")
491                .is_some()
492        );
493        assert!(
494            volume
495                .tree(root)
496                .find_exact(key(35))
497                .expect("search")
498                .is_none()
499        );
500    }
501
502    #[test]
503    fn a_block_reached_twice_is_a_tree_that_is_not_one() {
504        // Two child pointers naming one leaf. Not a cycle in the strict sense and the same
505        // defect: a walk would visit its items twice, and the key-order check would then fire
506        // for a reason that says nothing about what is wrong.
507        let mut forge = Forge::new();
508        let leaf_at = FIRST_FREE_AT;
509        forge
510            .block(leaf_at, &leaf(leaf_at, objectid::ROOT_TREE, &items(1..10)))
511            .root_node(1, &[(key(1), leaf_at), (key(10), leaf_at)]);
512        assert!(matches!(
513            walk(&forge),
514            Err(ReadError::TreeCycle { logical }) if logical == leaf_at
515        ));
516
517        // And a node naming itself, which the same guard catches before the block is read at
518        // all — the root's own address is in the visited set from the start.
519        let mut forge = Forge::new();
520        forge.root_node(1, &[(key(1), ROOT_TREE_AT)]);
521        assert!(matches!(
522            walk(&forge),
523            Err(ReadError::TreeCycle { logical }) if logical == ROOT_TREE_AT
524        ));
525    }
526
527    #[test]
528    fn a_child_that_is_not_one_level_below_its_parent_is_refused() {
529        // The guard that makes a descent terminate whatever the addresses say: a crafted tree
530        // can name a fresh block at every step, so the visited set alone is not a bound.
531        for child_level in [0u8, 2, 3] {
532            if child_level == 1 {
533                continue;
534            }
535            let mut forge = Forge::new();
536            let child = FIRST_FREE_AT;
537            let block = if child_level == 0 {
538                leaf(child, objectid::ROOT_TREE, &items(1..4))
539            } else {
540                node(
541                    child,
542                    objectid::ROOT_TREE,
543                    child_level,
544                    &[(key(1), FIRST_FREE_AT + NODE_SIZE as u64)],
545                )
546            };
547            forge.block(child, &block).root_node(2, &[(key(1), child)]);
548            match walk(&forge) {
549                Err(ReadError::BadTreeLevel { level, parent, .. }) => {
550                    assert_eq!((level, parent), (child_level, 2));
551                }
552                other => panic!("a level-{child_level} child under a level-2 node: {other:?}"),
553            }
554        }
555    }
556
557    #[test]
558    fn a_root_block_that_is_not_the_height_its_root_item_claimed_is_refused() {
559        // The level is recorded twice — in the root item and in the block's own header — and
560        // holding them against each other is what catches a root item pointing at a block
561        // that is not the one it describes.
562        let mut forge = Forge::new();
563        forge.root_leaf(&items(1..4));
564        forge.amend_superblock(0, |sb| sb.root_level = 1);
565        assert!(matches!(
566            walk(&forge),
567            Err(ReadError::BadTreeLevel {
568                level: 0,
569                parent: 1,
570                ..
571            })
572        ));
573    }
574
575    #[test]
576    fn a_header_that_does_not_describe_the_block_it_sits_in_is_refused() {
577        // Three claims a header makes about its own block, each checked against something
578        // outside the header. None is a fault a checksum can catch: every one of these fields
579        // is inside what the checksum covers, so a block written correctly somewhere else, or
580        // for another filesystem, verifies perfectly.
581        //
582        // They are one gate because the damage differs and nothing else does — the crafted
583        // block, the walk, and the shape of the refusal are the same three lines in each.
584        /// What a row damages about a header, and the word its refusal must carry.
585        type Fault = (&'static str, Box<dyn Fn(&mut Header)>);
586
587        let faults: [Fault; 3] = [
588            (
589                "room",
590                Box::new(|header: &mut Header| {
591                    header.nritems = (NODE_SIZE / Item::SIZE + 1) as u32;
592                }),
593            ),
594            (
595                "logical address",
596                Box::new(|header: &mut Header| header.bytenr = CHUNK_LOGICAL),
597            ),
598            (
599                "another filesystem",
600                Box::new(|header: &mut Header| header.fsid = [0x99; 16]),
601            ),
602        ];
603        for (expected, damage) in faults {
604            let mut forge = Forge::new();
605            forge.root_leaf(&items(1..4));
606            forge.amend(ROOT_TREE_AT, |block| {
607                let mut header = Header::read_from(block).expect("a header");
608                damage(&mut header);
609                header.write_to(block);
610            });
611            match walk(&forge) {
612                Err(ReadError::BadTreeBlock { fault, .. }) => {
613                    assert!(fault.contains(expected), "{fault}");
614                }
615                other => panic!("a header claiming the wrong {expected}: {other:?}"),
616            }
617        }
618    }
619
620    #[test]
621    fn an_item_whose_data_escapes_its_leaf_is_refused_in_both_directions() {
622        // Asked of the block directly rather than through a walk, and deliberately: a walk
623        // checks the leaf's packing before it reads an item, and packing already implies an
624        // item cannot end past the block. What it does not imply is that the data stays out
625        // of the array describing it — and a caller holding a block from `read_block` has had
626        // neither check made for it, which is the path these two guards are on.
627        /// What a row damages about an item, and the word its refusal must carry.
628        type Escape = (&'static str, Box<dyn Fn(&mut Item)>);
629
630        let cases: [Escape; 3] = [
631            ("array", Box::new(|item: &mut Item| item.offset = 0)),
632            (
633                "block",
634                Box::new(|item: &mut Item| item.size = NODE_SIZE as u32),
635            ),
636            (
637                "block",
638                Box::new(|item: &mut Item| {
639                    item.offset = u32::MAX;
640                    item.size = u32::MAX;
641                }),
642            ),
643        ];
644        for (expected, damage) in cases {
645            let mut forge = Forge::new();
646            forge.root_leaf(&items(1..4));
647            forge.amend(ROOT_TREE_AT, |block| {
648                let mut item = Item::read_from(&block[Header::SIZE..]).expect("an item");
649                damage(&mut item);
650                item.write_to(&mut block[Header::SIZE..]);
651            });
652            let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
653            let block = volume.read_block(ROOT_TREE_AT).expect("the block verifies");
654            match block.item_data(0) {
655                Err(ReadError::BadItem {
656                    fault, index: 0, ..
657                }) => {
658                    assert!(fault.contains(expected), "{fault}");
659                }
660                other => panic!("an item escaping its leaf toward the {expected}: {other:?}"),
661            }
662        }
663    }
664
665    #[test]
666    fn a_leaf_whose_data_has_been_moved_with_its_offsets_is_refused() {
667        // Every item stays inside the block and every item points at bytes that are not its
668        // own, so no bound notices — what does is the format's own packing rule, that one
669        // item's data ends where the next one's begins. The baseline's corruptor has a switch
670        // for exactly this shape, and it is what found the check missing.
671        let mut forge = Forge::new();
672        forge.root_leaf(&items(1..6));
673        forge.amend(ROOT_TREE_AT, |block| {
674            let at = Header::SIZE + 2 * Item::SIZE;
675            let mut third = Item::read_from(&block[at..]).expect("an item");
676            third.offset -= 16;
677            third.write_to(&mut block[at..]);
678        });
679        assert!(matches!(
680            walk(&forge),
681            Err(ReadError::BadItem { fault, index: 2, .. })
682                if fault.contains("does not end where the item before it begins")
683        ));
684
685        // And the first item is bounded by the end of the block rather than by a neighbour,
686        // which is the case a rule written only about neighbours would miss.
687        let mut forge = Forge::new();
688        forge.root_leaf(&items(1..6));
689        forge.amend(ROOT_TREE_AT, |block| {
690            let mut first = Item::read_from(&block[Header::SIZE..]).expect("an item");
691            first.size -= 1;
692            first.write_to(&mut block[Header::SIZE..]);
693        });
694        assert!(matches!(
695            walk(&forge),
696            Err(ReadError::BadItem { index: 0, .. })
697        ));
698    }
699
700    #[test]
701    fn a_tree_whose_keys_are_not_in_order_is_refused_rather_than_searched_wrongly() {
702        // A binary search over unsorted keys silently misses items instead of failing, so a
703        // tree that is not sorted is refused where a walk passes each key — which is the one
704        // place the check is free.
705        let mut forge = Forge::new();
706        forge.root_leaf(&items(1..6));
707        forge.amend(ROOT_TREE_AT, |block| {
708            let mut second = Item::read_from(&block[Header::SIZE + Item::SIZE..]).expect("an item");
709            second.key = key(0);
710            second.write_to(&mut block[Header::SIZE + Item::SIZE..]);
711        });
712        assert!(matches!(
713            walk(&forge),
714            Err(ReadError::BadTreeBlock { fault, .. }) if fault.contains("above the one before")
715        ));
716
717        // Two items under one key is the same defect at its boundary: a tree's keys are
718        // unique, and a search that found one of them would never see the other.
719        let mut forge = Forge::new();
720        forge.root_leaf(&[(key(1), vec![1; 8]), (key(1), vec![2; 8])]);
721        assert!(matches!(walk(&forge), Err(ReadError::BadTreeBlock { .. })));
722    }
723
724    #[test]
725    fn a_block_whose_checksum_no_longer_covers_it_is_refused_wherever_it_sits() {
726        for at in [ROOT_TREE_AT, CHUNK_LOGICAL] {
727            let mut forge = Forge::new();
728            forge.root_leaf(&items(1..4));
729            forge.break_checksum(at);
730            let opened = Volume::open(forge.source()).and_then(|mut v| {
731                let root = v.root_tree();
732                v.tree(root).count_items()
733            });
734            assert!(
735                matches!(
736                    &opened,
737                    Err(ReadError::BadChecksum {
738                        object: "tree block",
739                        ..
740                    })
741                ),
742                "a damaged block at {at}: {opened:?}"
743            );
744        }
745    }
746
747    #[test]
748    fn a_block_at_an_address_no_chunk_maps_is_refused_rather_than_read_from_nowhere() {
749        let mut forge = Forge::new();
750        forge.root_leaf(&items(1..4));
751        // Past the end of the one chunk, which is the address space this filesystem has.
752        forge.amend_superblock(0, |sb| sb.root = CHUNK_LOGICAL + CHUNK_LENGTH);
753        assert!(matches!(
754            walk(&forge),
755            Err(ReadError::UnmappedLogical { .. })
756        ));
757    }
758
759    #[test]
760    fn a_walk_stops_at_the_cap_the_caller_set_rather_than_gathering_past_it() {
761        let mut forge = Forge::new();
762        forge.root_leaf(&items(1..40));
763        let options = OpenOptions::new().limits(Limits::new().max_walk_entries(10));
764        let mut volume =
765            Volume::open_with(forge.source(), options).expect("a well-formed filesystem");
766        let root = volume.root_tree();
767        assert!(matches!(
768            volume.tree(root).count_items(),
769            Err(ReadError::TooManyEntries { limit: 10, .. })
770        ));
771        // Exactly at the cap is inside it: a bound that refused the last entry it allowed
772        // would be off by one in the direction that refuses healthy filesystems.
773        let options = OpenOptions::new().limits(Limits::new().max_walk_entries(39));
774        let mut volume =
775            Volume::open_with(forge.source(), options).expect("a well-formed filesystem");
776        let root = volume.root_tree();
777        assert_eq!(volume.tree(root).count_items().expect("within the cap"), 39);
778    }
779
780    #[test]
781    fn a_visit_that_says_to_stop_stops_where_it_said() {
782        let mut forge = Forge::new();
783        forge.root_leaf(&items(1..40));
784        let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
785        let root = volume.root_tree();
786        let mut seen = 0;
787        volume
788            .tree(root)
789            .for_each_item(|_, _| {
790                seen += 1;
791                seen < 3
792            })
793            .expect("a well-formed tree");
794        assert_eq!(seen, 3);
795    }
796
797    #[test]
798    fn every_block_of_a_tree_is_offered_to_a_block_walk_including_its_nodes() {
799        let mut forge = Forge::new();
800        let (left, right) = (FIRST_FREE_AT, FIRST_FREE_AT + NODE_SIZE as u64);
801        forge
802            .block(left, &leaf(left, objectid::ROOT_TREE, &items(1..10)))
803            .block(right, &leaf(right, objectid::ROOT_TREE, &items(10..20)))
804            .root_node(1, &[(key(1), left), (key(10), right)]);
805        let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
806        let root = volume.root_tree();
807        let mut blocks = Vec::new();
808        volume
809            .tree(root)
810            .for_each_block(|block| {
811                blocks.push((block.header().bytenr, block.header().level));
812                true
813            })
814            .expect("a well-formed tree");
815        assert_eq!(blocks, vec![(ROOT_TREE_AT, 1), (left, 0), (right, 0)]);
816    }
817
818    #[test]
819    fn a_forged_filesystem_is_one_the_reader_accepts_before_it_is_damaged() {
820        // What makes every gate above a negative control rather than a test of a broken
821        // fixture: the same filesystem, undamaged, opens and reads.
822        let mut forge = Forge::new();
823        forge.root_leaf(&items(1..40));
824        let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
825        assert_eq!(volume.superblock().nodesize as usize, NODE_SIZE);
826        assert_eq!(volume.chunk_map().len(), 1);
827        let root = volume.root_tree();
828        assert_eq!(
829            volume.tree(root).count_items().expect("a well-formed tree"),
830            39
831        );
832        // And the seal the forge applies is the format's own recipe, not a second spelling of
833        // it: a block resealed by hand verifies exactly as one the forge wrote does.
834        let mut block = leaf(FIRST_FREE_AT, objectid::ROOT_TREE, &items(1..4));
835        block[Header::SIZE] ^= 0xff;
836        seal(&mut block);
837        forge.block(FIRST_FREE_AT, &block);
838        let mut volume = Volume::open(forge.source()).expect("a well-formed filesystem");
839        assert!(volume.read_block(FIRST_FREE_AT).is_ok());
840    }
841}