1use btrfs_disk::{
13 superblock::ChecksumType,
14 tree::{DiskKey, KeyType, TreeBlock},
15 util::{csum_tree_block, write_disk_key},
16};
17use bytes::{Buf, BufMut};
18use uuid::Uuid;
19
20pub const HEADER_SIZE: usize = 101;
22
23pub const ITEM_SIZE: usize = 25;
25
26pub const KEY_PTR_SIZE: usize = 33;
28
29pub const DISK_KEY_SIZE: usize = 17;
31
32pub const BTRFS_MAX_LEVEL: usize = 8;
34
35#[derive(Clone)]
41pub struct ExtentBuffer {
42 data: Vec<u8>,
43 logical: u64,
45}
46
47impl ExtentBuffer {
48 #[must_use]
54 pub fn from_raw(data: Vec<u8>, logical: u64) -> Self {
55 assert!(!data.is_empty(), "ExtentBuffer: empty data");
56 Self { data, logical }
57 }
58
59 #[must_use]
61 pub fn new_zeroed(nodesize: u32, logical: u64) -> Self {
62 Self {
63 data: vec![0u8; nodesize as usize],
64 logical,
65 }
66 }
67
68 #[must_use]
70 pub fn logical(&self) -> u64 {
71 self.logical
72 }
73
74 pub fn set_logical(&mut self, logical: u64) {
76 self.logical = logical;
77 }
78
79 #[must_use]
81 pub fn nodesize(&self) -> u32 {
82 self.data.len() as u32
83 }
84
85 #[must_use]
87 pub fn as_bytes(&self) -> &[u8] {
88 &self.data
89 }
90
91 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
93 &mut self.data
94 }
95
96 #[must_use]
98 pub fn as_tree_block(&self) -> TreeBlock {
99 TreeBlock::parse(&self.data)
100 }
101
102 #[must_use]
106 pub fn generation(&self) -> u64 {
107 let mut b = &self.data[80..88];
108 b.get_u64_le()
109 }
110
111 #[must_use]
113 pub fn owner(&self) -> u64 {
114 let mut b = &self.data[88..96];
115 b.get_u64_le()
116 }
117
118 #[must_use]
120 pub fn nritems(&self) -> u32 {
121 let mut b = &self.data[96..100];
122 b.get_u32_le()
123 }
124
125 #[must_use]
127 pub fn level(&self) -> u8 {
128 self.data[100]
129 }
130
131 #[must_use]
133 pub fn bytenr(&self) -> u64 {
134 let mut b = &self.data[48..56];
135 b.get_u64_le()
136 }
137
138 #[must_use]
140 pub fn flags(&self) -> u64 {
141 let mut b = &self.data[56..64];
142 b.get_u64_le()
143 }
144
145 #[must_use]
151 pub fn fsid(&self) -> Uuid {
152 Uuid::from_bytes(self.data[32..48].try_into().unwrap())
153 }
154
155 #[must_use]
161 pub fn chunk_tree_uuid(&self) -> Uuid {
162 Uuid::from_bytes(self.data[64..80].try_into().unwrap())
163 }
164
165 pub fn set_generation(&mut self, generation: u64) {
169 (&mut self.data[80..88]).put_u64_le(generation);
170 }
171
172 pub fn set_owner(&mut self, owner: u64) {
174 (&mut self.data[88..96]).put_u64_le(owner);
175 }
176
177 pub fn set_nritems(&mut self, nritems: u32) {
179 (&mut self.data[96..100]).put_u32_le(nritems);
180 }
181
182 pub fn set_level(&mut self, level: u8) {
184 self.data[100] = level;
185 }
186
187 pub fn set_bytenr(&mut self, bytenr: u64) {
189 (&mut self.data[48..56]).put_u64_le(bytenr);
190 }
191
192 pub fn set_flags(&mut self, flags: u64) {
194 (&mut self.data[56..64]).put_u64_le(flags);
195 }
196
197 pub fn set_fsid(&mut self, fsid: &Uuid) {
199 self.data[32..48].copy_from_slice(fsid.as_bytes());
200 }
201
202 pub fn set_chunk_tree_uuid(&mut self, uuid: &Uuid) {
204 self.data[64..80].copy_from_slice(uuid.as_bytes());
205 }
206
207 #[must_use]
215 pub fn item_key(&self, slot: usize) -> DiskKey {
216 let off = HEADER_SIZE + slot * ITEM_SIZE;
217 DiskKey::parse(&self.data, off)
218 }
219
220 #[must_use]
223 pub fn item_offset(&self, slot: usize) -> u32 {
224 let off = HEADER_SIZE + slot * ITEM_SIZE + DISK_KEY_SIZE;
225 let mut b = &self.data[off..off + 4];
226 b.get_u32_le()
227 }
228
229 #[must_use]
231 pub fn item_size(&self, slot: usize) -> u32 {
232 let off = HEADER_SIZE + slot * ITEM_SIZE + DISK_KEY_SIZE + 4;
233 let mut b = &self.data[off..off + 4];
234 b.get_u32_le()
235 }
236
237 #[must_use]
240 pub fn item_data_offset(&self, slot: usize) -> usize {
241 HEADER_SIZE + self.item_offset(slot) as usize
242 }
243
244 #[must_use]
246 pub fn item_data(&self, slot: usize) -> &[u8] {
247 let start = self.item_data_offset(slot);
248 let size = self.item_size(slot) as usize;
249 &self.data[start..start + size]
250 }
251
252 pub fn item_data_mut(&mut self, slot: usize) -> &mut [u8] {
254 let start = self.item_data_offset(slot);
255 let size = self.item_size(slot) as usize;
256 &mut self.data[start..start + size]
257 }
258
259 pub fn set_item_key(&mut self, slot: usize, key: &DiskKey) {
261 let off = HEADER_SIZE + slot * ITEM_SIZE;
262 write_disk_key(&mut self.data, off, key);
263 }
264
265 pub fn set_item_offset(&mut self, slot: usize, offset: u32) {
267 let off = HEADER_SIZE + slot * ITEM_SIZE + DISK_KEY_SIZE;
268 (&mut self.data[off..off + 4]).put_u32_le(offset);
269 }
270
271 pub fn set_item_size(&mut self, slot: usize, size: u32) {
273 let off = HEADER_SIZE + slot * ITEM_SIZE + DISK_KEY_SIZE + 4;
274 (&mut self.data[off..off + 4]).put_u32_le(size);
275 }
276
277 #[must_use]
281 pub fn key_ptr_key(&self, slot: usize) -> DiskKey {
282 let off = HEADER_SIZE + slot * KEY_PTR_SIZE;
283 DiskKey::parse(&self.data, off)
284 }
285
286 #[must_use]
288 pub fn key_ptr_blockptr(&self, slot: usize) -> u64 {
289 let off = HEADER_SIZE + slot * KEY_PTR_SIZE + DISK_KEY_SIZE;
290 let mut b = &self.data[off..off + 8];
291 b.get_u64_le()
292 }
293
294 #[must_use]
296 pub fn key_ptr_generation(&self, slot: usize) -> u64 {
297 let off = HEADER_SIZE + slot * KEY_PTR_SIZE + DISK_KEY_SIZE + 8;
298 let mut b = &self.data[off..off + 8];
299 b.get_u64_le()
300 }
301
302 pub fn set_key_ptr_key(&mut self, slot: usize, key: &DiskKey) {
304 let off = HEADER_SIZE + slot * KEY_PTR_SIZE;
305 write_disk_key(&mut self.data, off, key);
306 }
307
308 pub fn set_key_ptr_blockptr(&mut self, slot: usize, blockptr: u64) {
310 let off = HEADER_SIZE + slot * KEY_PTR_SIZE + DISK_KEY_SIZE;
311 (&mut self.data[off..off + 8]).put_u64_le(blockptr);
312 }
313
314 pub fn set_key_ptr_generation(&mut self, slot: usize, generation: u64) {
316 let off = HEADER_SIZE + slot * KEY_PTR_SIZE + DISK_KEY_SIZE + 8;
317 (&mut self.data[off..off + 8]).put_u64_le(generation);
318 }
319
320 pub fn set_key_ptr(
322 &mut self,
323 slot: usize,
324 key: &DiskKey,
325 blockptr: u64,
326 generation: u64,
327 ) {
328 self.set_key_ptr_key(slot, key);
329 self.set_key_ptr_blockptr(slot, blockptr);
330 self.set_key_ptr_generation(slot, generation);
331 }
332
333 #[must_use]
341 pub fn leaf_free_space(&self) -> u32 {
342 let nritems = self.nritems() as usize;
343 if nritems == 0 {
344 return self.nodesize() - HEADER_SIZE as u32;
346 }
347 let items_end = (HEADER_SIZE + nritems * ITEM_SIZE) as u32;
348 let data_start = self.leaf_data_end();
349 data_start.saturating_sub(items_end)
350 }
351
352 #[must_use]
358 pub fn leaf_data_end(&self) -> u32 {
359 let nritems = self.nritems();
360 if nritems == 0 {
361 return self.nodesize();
362 }
363 HEADER_SIZE as u32 + self.item_offset(nritems as usize - 1)
365 }
366
367 pub fn update_checksum(&mut self, csum_type: ChecksumType) {
371 csum_tree_block(&mut self.data, csum_type);
372 }
373
374 pub fn copy_within(&mut self, src: core::ops::Range<usize>, dest: usize) {
380 self.data.copy_within(src, dest);
381 }
382
383 pub fn zero_range(&mut self, offset: usize, len: usize) {
385 self.data[offset..offset + len].fill(0);
386 }
387
388 #[must_use]
390 pub fn is_leaf(&self) -> bool {
391 self.level() == 0
392 }
393
394 #[must_use]
396 pub fn is_node(&self) -> bool {
397 self.level() > 0
398 }
399
400 #[must_use]
402 pub fn max_key_ptrs(&self) -> u32 {
403 (self.nodesize() - HEADER_SIZE as u32) / KEY_PTR_SIZE as u32
404 }
405
406 pub fn check_leaf(&self) -> Result<(), String> {
422 if self.level() != 0 {
423 return Err(format!(
424 "check_leaf: block at {} has level {} (expected 0)",
425 self.logical,
426 self.level()
427 ));
428 }
429 if self.bytenr() != self.logical {
430 return Err(format!(
431 "check_leaf: bytenr {} != logical {}",
432 self.bytenr(),
433 self.logical
434 ));
435 }
436 let nritems = self.nritems() as usize;
437 let nodesize = self.nodesize() as usize;
438
439 let items_end = HEADER_SIZE + nritems * ITEM_SIZE;
441 if items_end > nodesize {
442 return Err(format!(
443 "check_leaf: {nritems} items need {items_end} bytes, \
444 block is only {nodesize}",
445 ));
446 }
447
448 if nritems == 0 {
449 return Ok(());
450 }
451
452 for i in 1..nritems {
454 let prev = self.item_key(i - 1);
455 let curr = self.item_key(i);
456 if key_cmp(&prev, &curr) != std::cmp::Ordering::Less {
457 return Err(format!(
458 "check_leaf: key at slot {} ({:?}) >= key at slot {i} ({curr:?})",
459 i - 1,
460 prev,
461 ));
462 }
463 }
464
465 let first_data_end = HEADER_SIZE
468 + self.item_offset(0) as usize
469 + self.item_size(0) as usize;
470 if first_data_end > nodesize {
471 return Err(format!(
472 "check_leaf: item 0 data ends at {first_data_end}, \
473 beyond block size {nodesize}",
474 ));
475 }
476
477 for i in 1..nritems {
478 let prev_off = self.item_offset(i - 1);
479 let curr_off = self.item_offset(i);
480 let curr_size = self.item_size(i);
481 if curr_off > prev_off {
483 return Err(format!(
484 "check_leaf: offset at slot {i} ({curr_off}) > \
485 offset at slot {} ({prev_off})",
486 i - 1,
487 ));
488 }
489 if curr_off + curr_size > prev_off {
494 return Err(format!(
495 "check_leaf: item {i} data [{curr_off}..{}] overlaps \
496 item {} data [{prev_off}..{}]",
497 curr_off + curr_size,
498 i - 1,
499 prev_off + self.item_size(i - 1),
500 ));
501 }
502 }
503
504 let data_start = HEADER_SIZE + self.item_offset(nritems - 1) as usize;
506 if items_end > data_start {
507 return Err(format!(
508 "check_leaf: item descriptors end at {items_end} but \
509 data starts at {data_start}",
510 ));
511 }
512
513 Ok(())
514 }
515
516 pub fn check_node(&self) -> Result<(), String> {
529 if self.level() == 0 {
530 return Err(format!(
531 "check_node: block at {} has level 0 (expected > 0)",
532 self.logical,
533 ));
534 }
535 if self.bytenr() != self.logical {
536 return Err(format!(
537 "check_node: bytenr {} != logical {}",
538 self.bytenr(),
539 self.logical
540 ));
541 }
542 let nritems = self.nritems() as usize;
543 let max_ptrs = self.max_key_ptrs() as usize;
544 if nritems > max_ptrs {
545 return Err(format!(
546 "check_node: {nritems} key pointers exceeds maximum {max_ptrs}",
547 ));
548 }
549
550 for i in 0..nritems {
551 if self.key_ptr_blockptr(i) == 0 {
552 return Err(format!("check_node: slot {i} has blockptr 0"));
553 }
554 }
555
556 for i in 1..nritems {
557 let prev = self.key_ptr_key(i - 1);
558 let curr = self.key_ptr_key(i);
559 if key_cmp(&prev, &curr) != std::cmp::Ordering::Less {
560 return Err(format!(
561 "check_node: key at slot {} ({:?}) >= key at slot {i} ({curr:?})",
562 i - 1,
563 prev,
564 ));
565 }
566 }
567
568 Ok(())
569 }
570
571 pub fn check(&self) -> Result<(), String> {
575 if self.level() == 0 {
576 self.check_leaf()
577 } else {
578 self.check_node()
579 }
580 }
581}
582
583#[inline]
589pub fn debug_assert_leaf_valid(eb: &ExtentBuffer) {
590 debug_assert!(
591 eb.check_leaf().is_ok(),
592 "leaf invariant violation at logical {}: {}",
593 eb.logical(),
594 eb.check_leaf().unwrap_err(),
595 );
596}
597
598#[inline]
604pub fn debug_assert_node_valid(eb: &ExtentBuffer) {
605 debug_assert!(
606 eb.check_node().is_ok(),
607 "node invariant violation at logical {}: {}",
608 eb.logical(),
609 eb.check_node().unwrap_err(),
610 );
611}
612
613#[inline]
618pub fn debug_assert_block_valid(eb: &ExtentBuffer) {
619 debug_assert!(
620 eb.check().is_ok(),
621 "block invariant violation at logical {}: {}",
622 eb.logical(),
623 eb.check().unwrap_err(),
624 );
625}
626
627#[allow(clippy::missing_fields_in_debug)]
628impl std::fmt::Debug for ExtentBuffer {
629 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 f.debug_struct("ExtentBuffer")
631 .field("logical", &self.logical)
632 .field("level", &self.level())
633 .field("nritems", &self.nritems())
634 .field("generation", &self.generation())
635 .field("owner", &self.owner())
636 .finish()
637 }
638}
639
640#[must_use]
644pub fn key_cmp(a: &DiskKey, b: &DiskKey) -> std::cmp::Ordering {
645 a.objectid
646 .cmp(&b.objectid)
647 .then_with(|| a.key_type.to_raw().cmp(&b.key_type.to_raw()))
648 .then_with(|| a.offset.cmp(&b.offset))
649}
650
651#[must_use]
653pub fn min_key() -> DiskKey {
654 DiskKey {
655 objectid: 0,
656 key_type: KeyType::from_raw(0),
657 offset: 0,
658 }
659}
660
661#[must_use]
663pub fn max_key() -> DiskKey {
664 DiskKey {
665 objectid: u64::MAX,
666 key_type: KeyType::from_raw(u8::MAX),
667 offset: u64::MAX,
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674
675 fn make_leaf(
676 nodesize: u32,
677 nritems: u32,
678 generation: u64,
679 owner: u64,
680 ) -> ExtentBuffer {
681 let mut eb = ExtentBuffer::new_zeroed(nodesize, 65536);
682 eb.set_generation(generation);
683 eb.set_owner(owner);
684 eb.set_nritems(nritems);
685 eb.set_level(0);
686 eb.set_bytenr(65536);
687 eb
688 }
689
690 fn make_node(
691 nodesize: u32,
692 nritems: u32,
693 level: u8,
694 generation: u64,
695 ) -> ExtentBuffer {
696 let mut eb = ExtentBuffer::new_zeroed(nodesize, 131072);
697 eb.set_generation(generation);
698 eb.set_owner(2);
699 eb.set_nritems(nritems);
700 eb.set_level(level);
701 eb.set_bytenr(131072);
702 eb
703 }
704
705 #[test]
706 fn header_round_trip() {
707 let mut eb = ExtentBuffer::new_zeroed(16384, 65536);
708 eb.set_generation(42);
709 eb.set_owner(5);
710 eb.set_nritems(10);
711 eb.set_level(0);
712 eb.set_bytenr(65536);
713 eb.set_flags(1);
714
715 assert_eq!(eb.generation(), 42);
716 assert_eq!(eb.owner(), 5);
717 assert_eq!(eb.nritems(), 10);
718 assert_eq!(eb.level(), 0);
719 assert_eq!(eb.bytenr(), 65536);
720 assert_eq!(eb.flags(), 1);
721 assert_eq!(eb.logical(), 65536);
722 assert_eq!(eb.nodesize(), 16384);
723 assert!(eb.is_leaf());
724 assert!(!eb.is_node());
725 }
726
727 #[test]
728 fn uuid_round_trip() {
729 let mut eb = ExtentBuffer::new_zeroed(16384, 0);
730 let fsid =
731 Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
732 let ctu =
733 Uuid::parse_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
734 eb.set_fsid(&fsid);
735 eb.set_chunk_tree_uuid(&ctu);
736 assert_eq!(eb.fsid(), fsid);
737 assert_eq!(eb.chunk_tree_uuid(), ctu);
738 }
739
740 #[test]
741 fn item_accessors() {
742 let mut eb = make_leaf(16384, 2, 7, 5);
743 let key0 = DiskKey {
744 objectid: 256,
745 key_type: KeyType::InodeItem,
746 offset: 0,
747 };
748 let key1 = DiskKey {
749 objectid: 256,
750 key_type: KeyType::DirItem,
751 offset: 100,
752 };
753
754 let data_off_0 = 16384 - HEADER_SIZE as u32 - 160;
756 eb.set_item_key(0, &key0);
757 eb.set_item_offset(0, data_off_0);
758 eb.set_item_size(0, 160);
759
760 let data_off_1 = data_off_0 - 50;
762 eb.set_item_key(1, &key1);
763 eb.set_item_offset(1, data_off_1);
764 eb.set_item_size(1, 50);
765
766 let k0 = eb.item_key(0);
768 assert_eq!(k0.objectid, 256);
769 assert_eq!(k0.key_type, KeyType::InodeItem);
770 assert_eq!(k0.offset, 0);
771 assert_eq!(eb.item_offset(0), data_off_0);
772 assert_eq!(eb.item_size(0), 160);
773
774 let k1 = eb.item_key(1);
775 assert_eq!(k1.objectid, 256);
776 assert_eq!(k1.key_type, KeyType::DirItem);
777 assert_eq!(k1.offset, 100);
778 assert_eq!(eb.item_offset(1), data_off_1);
779 assert_eq!(eb.item_size(1), 50);
780
781 assert_eq!(eb.item_data(0).len(), 160);
783 assert_eq!(eb.item_data(1).len(), 50);
784
785 eb.item_data_mut(0)[0] = 0xAA;
787 eb.item_data_mut(1)[0] = 0xBB;
788 assert_eq!(eb.item_data(0)[0], 0xAA);
789 assert_eq!(eb.item_data(1)[0], 0xBB);
790 }
791
792 #[test]
793 fn key_ptr_accessors() {
794 let mut eb = make_node(16384, 3, 1, 10);
795
796 for i in 0..3u64 {
797 let key = DiskKey {
798 objectid: i + 1,
799 key_type: KeyType::RootItem,
800 offset: 0,
801 };
802 eb.set_key_ptr(i as usize, &key, (i + 1) * 65536, 10 - i);
803 }
804
805 for i in 0..3u64 {
806 let k = eb.key_ptr_key(i as usize);
807 assert_eq!(k.objectid, i + 1);
808 assert_eq!(k.key_type, KeyType::RootItem);
809 assert_eq!(eb.key_ptr_blockptr(i as usize), (i + 1) * 65536);
810 assert_eq!(eb.key_ptr_generation(i as usize), 10 - i);
811 }
812 }
813
814 #[test]
815 fn leaf_free_space_empty() {
816 let eb = make_leaf(16384, 0, 1, 5);
817 assert_eq!(eb.leaf_free_space(), 16384 - HEADER_SIZE as u32);
818 }
819
820 #[test]
821 fn leaf_free_space_with_items() {
822 let mut eb = make_leaf(4096, 1, 1, 5);
823 let data_off = 4096 - HEADER_SIZE as u32 - 100;
825 eb.set_item_key(
826 0,
827 &DiskKey {
828 objectid: 1,
829 key_type: KeyType::InodeItem,
830 offset: 0,
831 },
832 );
833 eb.set_item_offset(0, data_off);
834 eb.set_item_size(0, 100);
835
836 let expected = (HEADER_SIZE as u32 + data_off)
840 - (HEADER_SIZE as u32 + ITEM_SIZE as u32);
841 assert_eq!(eb.leaf_free_space(), expected);
842 }
843
844 #[test]
845 fn key_comparison() {
846 let a = DiskKey {
847 objectid: 1,
848 key_type: KeyType::InodeItem,
849 offset: 0,
850 };
851 let b = DiskKey {
852 objectid: 2,
853 key_type: KeyType::InodeItem,
854 offset: 0,
855 };
856 assert_eq!(key_cmp(&a, &b), std::cmp::Ordering::Less);
857 assert_eq!(key_cmp(&b, &a), std::cmp::Ordering::Greater);
858 assert_eq!(key_cmp(&a, &a), std::cmp::Ordering::Equal);
859
860 let c = DiskKey {
862 objectid: 1,
863 key_type: KeyType::DirItem,
864 offset: 0,
865 };
866 assert_eq!(key_cmp(&a, &c), std::cmp::Ordering::Less);
867 }
868
869 #[test]
870 fn min_max_keys() {
871 assert_eq!(key_cmp(&min_key(), &max_key()), std::cmp::Ordering::Less);
872 assert_eq!(key_cmp(&min_key(), &min_key()), std::cmp::Ordering::Equal);
873 }
874
875 #[test]
876 fn checksum_round_trip() {
877 let mut eb = make_leaf(4096, 0, 1, 5);
878 eb.set_bytenr(65536);
879 eb.update_checksum(ChecksumType::Crc32);
880 assert_ne!(&eb.as_bytes()[0..4], &[0, 0, 0, 0]);
882 let csum1: [u8; 4] = eb.as_bytes()[0..4].try_into().unwrap();
884 eb.update_checksum(ChecksumType::Crc32);
885 let csum2: [u8; 4] = eb.as_bytes()[0..4].try_into().unwrap();
886 assert_eq!(csum1, csum2);
887 }
888
889 #[test]
890 fn checksum_dispatches_per_algorithm() {
891 for csum_type in [
895 ChecksumType::Xxhash,
896 ChecksumType::Sha256,
897 ChecksumType::Blake2,
898 ] {
899 let mut eb = make_leaf(4096, 0, 1, 5);
900 eb.set_bytenr(65536);
901 eb.update_checksum(csum_type);
902 let n = csum_type.size();
903 assert!(
904 eb.as_bytes()[..n].iter().any(|&b| b != 0),
905 "{csum_type:?}: hash bytes are all zero",
906 );
907 assert!(
908 eb.as_bytes()[n..32].iter().all(|&b| b == 0),
909 "{csum_type:?}: tail of csum field is not zero",
910 );
911 }
912 }
913
914 #[test]
915 fn clone_independence() {
916 let mut eb = make_leaf(4096, 0, 1, 5);
917 let eb2 = eb.clone();
918 eb.set_generation(999);
919 assert_eq!(eb.generation(), 999);
920 assert_eq!(eb2.generation(), 1);
921 }
922
923 #[test]
924 fn as_tree_block_parse() {
925 let mut eb = make_leaf(4096, 1, 7, 5);
926 eb.set_bytenr(65536);
927 let key = DiskKey {
928 objectid: 256,
929 key_type: KeyType::InodeItem,
930 offset: 0,
931 };
932 let data_off = 4096 - HEADER_SIZE as u32 - 160;
933 eb.set_item_key(0, &key);
934 eb.set_item_offset(0, data_off);
935 eb.set_item_size(0, 160);
936
937 let tb = eb.as_tree_block();
938 match &tb {
939 TreeBlock::Leaf { header, items, .. } => {
940 assert_eq!(header.generation, 7);
941 assert_eq!(items.len(), 1);
942 assert_eq!(items[0].key.objectid, 256);
943 }
944 TreeBlock::Node { .. } => panic!("expected leaf"),
945 }
946 }
947
948 #[test]
949 fn copy_within_and_zero() {
950 let mut eb = ExtentBuffer::new_zeroed(256, 0);
951 eb.as_bytes_mut()[10] = 0xAA;
952 eb.as_bytes_mut()[11] = 0xBB;
953 eb.copy_within(10..12, 20);
954 assert_eq!(eb.as_bytes()[20], 0xAA);
955 assert_eq!(eb.as_bytes()[21], 0xBB);
956 eb.zero_range(20, 2);
957 assert_eq!(eb.as_bytes()[20], 0);
958 assert_eq!(eb.as_bytes()[21], 0);
959 }
960
961 #[test]
962 fn check_leaf_valid() {
963 let mut eb = make_leaf(4096, 0, 1, 5);
964 eb.set_bytenr(65536);
965 assert!(eb.check_leaf().is_ok());
966
967 let mut data_end = 4096u32;
969 let key0 = DiskKey {
970 objectid: 1,
971 key_type: KeyType::InodeItem,
972 offset: 0,
973 };
974 data_end -= 100;
975 eb.set_item_key(0, &key0);
976 eb.set_item_offset(0, data_end - HEADER_SIZE as u32);
977 eb.set_item_size(0, 100);
978
979 let key1 = DiskKey {
980 objectid: 2,
981 key_type: KeyType::InodeItem,
982 offset: 0,
983 };
984 data_end -= 50;
985 eb.set_item_key(1, &key1);
986 eb.set_item_offset(1, data_end - HEADER_SIZE as u32);
987 eb.set_item_size(1, 50);
988
989 eb.set_nritems(2);
990 assert!(eb.check_leaf().is_ok());
991 }
992
993 #[test]
994 fn check_leaf_bad_key_order() {
995 let mut eb = make_leaf(4096, 0, 1, 5);
996 eb.set_bytenr(65536);
997 eb.set_nritems(2);
998
999 let key0 = DiskKey {
1001 objectid: 10,
1002 key_type: KeyType::InodeItem,
1003 offset: 0,
1004 };
1005 let key1 = DiskKey {
1006 objectid: 5,
1007 key_type: KeyType::InodeItem,
1008 offset: 0,
1009 };
1010 eb.set_item_key(0, &key0);
1011 eb.set_item_offset(0, 4096 - HEADER_SIZE as u32 - 50);
1012 eb.set_item_size(0, 50);
1013 eb.set_item_key(1, &key1);
1014 eb.set_item_offset(1, 4096 - HEADER_SIZE as u32 - 100);
1015 eb.set_item_size(1, 50);
1016
1017 let err = eb.check_leaf().unwrap_err();
1018 assert!(err.contains("key at slot 0"), "{err}");
1019 }
1020
1021 #[test]
1022 fn check_leaf_bytenr_mismatch() {
1023 let mut eb = make_leaf(4096, 0, 1, 5);
1024 eb.set_bytenr(99999); let err = eb.check_leaf().unwrap_err();
1026 assert!(err.contains("bytenr"), "{err}");
1027 }
1028
1029 #[test]
1030 fn check_node_valid() {
1031 let mut eb = make_node(4096, 2, 1, 10);
1032 let key0 = DiskKey {
1033 objectid: 1,
1034 key_type: KeyType::RootItem,
1035 offset: 0,
1036 };
1037 let key1 = DiskKey {
1038 objectid: 100,
1039 key_type: KeyType::RootItem,
1040 offset: 0,
1041 };
1042 eb.set_key_ptr(0, &key0, 65536, 10);
1043 eb.set_key_ptr(1, &key1, 131072, 10);
1044 assert!(eb.check_node().is_ok());
1045 }
1046
1047 #[test]
1048 fn check_node_bad_key_order() {
1049 let mut eb = make_node(4096, 2, 1, 10);
1050 let key0 = DiskKey {
1051 objectid: 100,
1052 key_type: KeyType::RootItem,
1053 offset: 0,
1054 };
1055 let key1 = DiskKey {
1056 objectid: 1,
1057 key_type: KeyType::RootItem,
1058 offset: 0,
1059 };
1060 eb.set_key_ptr(0, &key0, 65536, 10);
1061 eb.set_key_ptr(1, &key1, 131072, 10);
1062 let err = eb.check_node().unwrap_err();
1063 assert!(err.contains("key at slot 0"), "{err}");
1064 }
1065
1066 #[test]
1067 fn check_node_zero_blockptr() {
1068 let mut eb = make_node(4096, 1, 1, 10);
1069 let key0 = DiskKey {
1070 objectid: 1,
1071 key_type: KeyType::RootItem,
1072 offset: 0,
1073 };
1074 eb.set_key_ptr_key(0, &key0);
1075 eb.set_key_ptr_blockptr(0, 0);
1076 eb.set_key_ptr_generation(0, 10);
1077 let err = eb.check_node().unwrap_err();
1078 assert!(err.contains("blockptr 0"), "{err}");
1079 }
1080
1081 #[test]
1082 fn check_dispatches_by_level() {
1083 let mut leaf = make_leaf(4096, 0, 1, 5);
1084 leaf.set_bytenr(65536);
1085 assert!(leaf.check().is_ok());
1086
1087 let mut node = make_node(4096, 1, 1, 10);
1088 let key = DiskKey {
1089 objectid: 1,
1090 key_type: KeyType::RootItem,
1091 offset: 0,
1092 };
1093 node.set_key_ptr(0, &key, 65536, 10);
1094 assert!(node.check().is_ok());
1095 }
1096}