1use crate::{
9 raw,
10 tree::{DiskKey, ObjectId},
11 util::raw_crc32c,
12};
13use bytes::{Buf, BufMut};
14use std::{fmt, mem};
15use uuid::Uuid;
16
17fn get_uuid(buf: &mut &[u8]) -> Uuid {
19 let bytes: [u8; 16] = buf[..16].try_into().unwrap();
20 buf.advance(16);
21 Uuid::from_bytes(bytes)
22}
23
24bitflags::bitflags! {
25 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31 pub struct BlockGroupFlags: u64 {
32 const DATA = raw::BTRFS_BLOCK_GROUP_DATA as u64;
33 const SYSTEM = raw::BTRFS_BLOCK_GROUP_SYSTEM as u64;
34 const METADATA = raw::BTRFS_BLOCK_GROUP_METADATA as u64;
35 const RAID0 = raw::BTRFS_BLOCK_GROUP_RAID0 as u64;
36 const RAID1 = raw::BTRFS_BLOCK_GROUP_RAID1 as u64;
37 const DUP = raw::BTRFS_BLOCK_GROUP_DUP as u64;
38 const RAID10 = raw::BTRFS_BLOCK_GROUP_RAID10 as u64;
39 const RAID5 = raw::BTRFS_BLOCK_GROUP_RAID5 as u64;
40 const RAID6 = raw::BTRFS_BLOCK_GROUP_RAID6 as u64;
41 const RAID1C3 = raw::BTRFS_BLOCK_GROUP_RAID1C3 as u64;
42 const RAID1C4 = raw::BTRFS_BLOCK_GROUP_RAID1C4 as u64;
43 }
44}
45
46impl BlockGroupFlags {
47 pub fn profile_name(self) -> &'static str {
49 let profile = self
50 & (Self::RAID0
51 | Self::RAID1
52 | Self::DUP
53 | Self::RAID10
54 | Self::RAID5
55 | Self::RAID6
56 | Self::RAID1C3
57 | Self::RAID1C4);
58 match profile {
59 p if p == Self::RAID0 => "RAID0",
60 p if p == Self::RAID1 => "RAID1",
61 p if p == Self::DUP => "DUP",
62 p if p == Self::RAID10 => "RAID10",
63 p if p == Self::RAID5 => "RAID5",
64 p if p == Self::RAID6 => "RAID6",
65 p if p == Self::RAID1C3 => "RAID1C3",
66 p if p == Self::RAID1C4 => "RAID1C4",
67 _ => "single",
68 }
69 }
70}
71
72impl fmt::Display for BlockGroupFlags {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 let mut parts: Vec<&str> = Vec::new();
75 if self.contains(Self::DATA) {
76 parts.push("DATA");
77 }
78 if self.contains(Self::SYSTEM) {
79 parts.push("SYSTEM");
80 }
81 if self.contains(Self::METADATA) {
82 parts.push("METADATA");
83 }
84 let profile = self.profile_name();
85 if parts.is_empty() {
86 write!(f, "{profile}")
87 } else {
88 parts.push(profile);
89 write!(f, "{}", parts.join("|"))
90 }
91 }
92}
93
94bitflags::bitflags! {
95 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97 pub struct InodeFlags: u64 {
98 const NODATASUM = raw::BTRFS_INODE_NODATASUM as u64;
99 const NODATACOW = raw::BTRFS_INODE_NODATACOW as u64;
100 const READONLY = raw::BTRFS_INODE_READONLY as u64;
101 const NOCOMPRESS = raw::BTRFS_INODE_NOCOMPRESS as u64;
102 const PREALLOC = raw::BTRFS_INODE_PREALLOC as u64;
103 const SYNC = raw::BTRFS_INODE_SYNC as u64;
104 const IMMUTABLE = raw::BTRFS_INODE_IMMUTABLE as u64;
105 const APPEND = raw::BTRFS_INODE_APPEND as u64;
106 const NODUMP = raw::BTRFS_INODE_NODUMP as u64;
107 const NOATIME = raw::BTRFS_INODE_NOATIME as u64;
108 const DIRSYNC = raw::BTRFS_INODE_DIRSYNC as u64;
109 const COMPRESS = raw::BTRFS_INODE_COMPRESS as u64;
110 const ROOT_ITEM_INIT = raw::BTRFS_INODE_ROOT_ITEM_INIT as u64;
111 const _ = !0;
113 }
114}
115
116impl fmt::Display for InodeFlags {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 const NAMES: &[(InodeFlags, &str)] = &[
119 (InodeFlags::NODATASUM, "NODATASUM"),
120 (InodeFlags::NODATACOW, "NODATACOW"),
121 (InodeFlags::READONLY, "READONLY"),
122 (InodeFlags::NOCOMPRESS, "NOCOMPRESS"),
123 (InodeFlags::PREALLOC, "PREALLOC"),
124 (InodeFlags::SYNC, "SYNC"),
125 (InodeFlags::IMMUTABLE, "IMMUTABLE"),
126 (InodeFlags::APPEND, "APPEND"),
127 (InodeFlags::NODUMP, "NODUMP"),
128 (InodeFlags::NOATIME, "NOATIME"),
129 (InodeFlags::DIRSYNC, "DIRSYNC"),
130 (InodeFlags::COMPRESS, "COMPRESS"),
131 (InodeFlags::ROOT_ITEM_INIT, "ROOT_ITEM_INIT"),
132 ];
133 let known: InodeFlags = NAMES
134 .iter()
135 .fold(InodeFlags::empty(), |a, &(flag, _)| a | flag);
136 let mut parts: Vec<String> = NAMES
137 .iter()
138 .filter(|&&(flag, _)| self.contains(flag))
139 .map(|&(_, name)| name.to_string())
140 .collect();
141 let unknown = *self & !known;
142 if !unknown.is_empty() {
143 parts.push(format!("UNKNOWN: 0x{:x}", unknown.bits()));
144 }
145 if parts.is_empty() {
146 write!(f, "none")
147 } else {
148 write!(f, "{}", parts.join("|"))
149 }
150 }
151}
152#[derive(Debug, Clone, Copy)]
154pub struct Timespec {
155 pub sec: u64,
157 pub nsec: u32,
159}
160
161impl Timespec {
162 fn parse(buf: &mut &[u8]) -> Self {
163 Self {
164 sec: buf.get_u64_le(),
165 nsec: buf.get_u32_le(),
166 }
167 }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum CompressionType {
173 None,
174 Zlib,
175 Lzo,
176 Zstd,
177 Unknown(u8),
178}
179
180impl CompressionType {
181 pub fn from_raw(v: u8) -> Self {
182 match v {
183 0 => Self::None,
184 1 => Self::Zlib,
185 2 => Self::Lzo,
186 3 => Self::Zstd,
187 _ => Self::Unknown(v),
188 }
189 }
190
191 pub fn name(&self) -> &'static str {
192 match self {
193 Self::None => "none",
194 Self::Zlib => "zlib",
195 Self::Lzo => "lzo",
196 Self::Zstd => "zstd",
197 Self::Unknown(_) => "unknown",
198 }
199 }
200
201 pub fn to_raw(self) -> u8 {
202 match self {
203 Self::None => 0,
204 Self::Zlib => 1,
205 Self::Lzo => 2,
206 Self::Zstd => 3,
207 Self::Unknown(v) => v,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214pub enum FileExtentType {
215 Inline,
216 Regular,
217 Prealloc,
218 Unknown(u8),
219}
220
221impl FileExtentType {
222 pub fn from_raw(v: u8) -> Self {
223 match u32::from(v) {
224 raw::BTRFS_FILE_EXTENT_INLINE => Self::Inline,
225 raw::BTRFS_FILE_EXTENT_REG => Self::Regular,
226 raw::BTRFS_FILE_EXTENT_PREALLOC => Self::Prealloc,
227 _ => Self::Unknown(v),
228 }
229 }
230
231 pub fn name(&self) -> &'static str {
232 match self {
233 Self::Inline => "inline",
234 Self::Regular => "regular",
235 Self::Prealloc => "prealloc",
236 Self::Unknown(_) => "unknown",
237 }
238 }
239
240 #[allow(clippy::cast_possible_truncation)]
241 pub fn to_raw(self) -> u8 {
242 match self {
243 Self::Inline => raw::BTRFS_FILE_EXTENT_INLINE as u8,
244 Self::Regular => raw::BTRFS_FILE_EXTENT_REG as u8,
245 Self::Prealloc => raw::BTRFS_FILE_EXTENT_PREALLOC as u8,
246 Self::Unknown(v) => v,
247 }
248 }
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum FileType {
254 Unknown,
255 RegFile,
256 Dir,
257 Chrdev,
258 Blkdev,
259 Fifo,
260 Sock,
261 Symlink,
262 Xattr,
263 Other(u8),
264}
265
266impl FileType {
267 pub fn from_raw(v: u8) -> Self {
268 match u32::from(v) {
269 raw::BTRFS_FT_UNKNOWN => Self::Unknown,
270 raw::BTRFS_FT_REG_FILE => Self::RegFile,
271 raw::BTRFS_FT_DIR => Self::Dir,
272 raw::BTRFS_FT_CHRDEV => Self::Chrdev,
273 raw::BTRFS_FT_BLKDEV => Self::Blkdev,
274 raw::BTRFS_FT_FIFO => Self::Fifo,
275 raw::BTRFS_FT_SOCK => Self::Sock,
276 raw::BTRFS_FT_SYMLINK => Self::Symlink,
277 raw::BTRFS_FT_XATTR => Self::Xattr,
278 _ => Self::Other(v),
279 }
280 }
281
282 pub fn name(&self) -> &'static str {
283 match self {
284 Self::Unknown | Self::Other(_) => "UNKNOWN",
285 Self::RegFile => "FILE",
286 Self::Dir => "DIR",
287 Self::Chrdev => "CHRDEV",
288 Self::Blkdev => "BLKDEV",
289 Self::Fifo => "FIFO",
290 Self::Sock => "SOCK",
291 Self::Symlink => "SYMLINK",
292 Self::Xattr => "XATTR",
293 }
294 }
295}
296
297#[derive(Debug, Clone)]
302pub struct InodeItem {
303 pub generation: u64,
305 pub transid: u64,
307 pub size: u64,
309 pub nbytes: u64,
311 pub block_group: u64,
313 pub nlink: u32,
315 pub uid: u32,
317 pub gid: u32,
319 pub mode: u32,
321 pub rdev: u64,
323 pub flags: InodeFlags,
325 pub sequence: u64,
327 pub atime: Timespec,
329 pub ctime: Timespec,
331 pub mtime: Timespec,
333 pub otime: Timespec,
335}
336
337impl InodeItem {
338 pub fn parse(data: &[u8]) -> Option<Self> {
339 if data.len() < mem::size_of::<raw::btrfs_inode_item>() {
340 return None;
341 }
342 let mut buf = data;
343 Some(Self {
344 generation: buf.get_u64_le(),
345 transid: buf.get_u64_le(),
346 size: buf.get_u64_le(),
347 nbytes: buf.get_u64_le(),
348 block_group: buf.get_u64_le(),
349 nlink: buf.get_u32_le(),
350 uid: buf.get_u32_le(),
351 gid: buf.get_u32_le(),
352 mode: buf.get_u32_le(),
353 rdev: buf.get_u64_le(),
354 flags: InodeFlags::from_bits_truncate(buf.get_u64_le()),
355 sequence: buf.get_u64_le(),
356 atime: {
358 buf.advance(32);
359 Timespec::parse(&mut buf)
360 },
361 ctime: Timespec::parse(&mut buf),
362 mtime: Timespec::parse(&mut buf),
363 otime: Timespec::parse(&mut buf),
364 })
365 }
366}
367
368#[derive(Debug, Clone)]
374pub struct InodeRef {
375 pub index: u64,
377 pub name: Vec<u8>,
379}
380
381impl InodeRef {
382 pub fn parse_all(data: &[u8]) -> Vec<Self> {
383 let mut result = Vec::new();
384 let mut buf = data;
385 while buf.remaining() >= 10 {
386 let index = buf.get_u64_le();
387 let name_len = buf.get_u16_le() as usize;
388 if buf.remaining() < name_len {
389 break;
390 }
391 let name = buf[..name_len].to_vec();
392 buf.advance(name_len);
393 result.push(Self { index, name });
394 }
395 result
396 }
397}
398
399#[derive(Debug, Clone)]
405pub struct InodeExtref {
406 pub parent: u64,
408 pub index: u64,
410 pub name: Vec<u8>,
412}
413
414impl InodeExtref {
415 pub fn parse_all(data: &[u8]) -> Vec<Self> {
416 let mut result = Vec::new();
417 let mut buf = data;
418 while buf.remaining() >= 18 {
419 let parent = buf.get_u64_le();
420 let index = buf.get_u64_le();
421 let name_len = buf.get_u16_le() as usize;
422 if buf.remaining() < name_len {
423 break;
424 }
425 let name = buf[..name_len].to_vec();
426 buf.advance(name_len);
427 result.push(Self {
428 parent,
429 index,
430 name,
431 });
432 }
433 result
434 }
435}
436
437#[derive(Debug, Clone)]
443pub struct DirItem {
444 pub location: DiskKey,
446 pub transid: u64,
448 pub file_type: FileType,
450 pub name: Vec<u8>,
452 pub data: Vec<u8>,
454}
455
456impl DirItem {
457 pub fn parse_all(data: &[u8]) -> Vec<Self> {
458 let mut result = Vec::new();
459 let dir_item_size = mem::size_of::<raw::btrfs_dir_item>();
460 let mut buf = data;
461
462 while buf.remaining() >= dir_item_size {
463 let location = DiskKey::parse(buf, 0);
464 buf.advance(17); let transid = buf.get_u64_le();
466 let data_len = buf.get_u16_le() as usize;
467 let name_len = buf.get_u16_le() as usize;
468 let file_type = FileType::from_raw(buf.get_u8());
469
470 if buf.remaining() < name_len + data_len {
471 break;
472 }
473 let name = buf[..name_len].to_vec();
474 buf.advance(name_len);
475 let item_data = buf[..data_len].to_vec();
476 buf.advance(data_len);
477 result.push(Self {
478 location,
479 transid,
480 file_type,
481 name,
482 data: item_data,
483 });
484 }
485 result
486 }
487}
488
489bitflags::bitflags! {
490 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
492 pub struct RootItemFlags: u64 {
493 const RDONLY = raw::BTRFS_ROOT_SUBVOL_RDONLY as u64;
494 const DEAD = raw::BTRFS_ROOT_SUBVOL_DEAD;
495 const _ = !0;
497 }
498}
499
500impl fmt::Display for RootItemFlags {
501 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502 if self.contains(Self::RDONLY) {
503 write!(f, "RDONLY")
504 } else {
505 write!(f, "none")
506 }
507 }
508}
509
510#[derive(Debug, Clone)]
516pub struct RootItem {
517 pub generation: u64,
519 pub root_dirid: u64,
521 pub bytenr: u64,
523 pub byte_limit: u64,
525 pub bytes_used: u64,
527 pub last_snapshot: u64,
529 pub flags: RootItemFlags,
531 pub refs: u32,
533 pub drop_progress: DiskKey,
535 pub drop_level: u8,
537 pub level: u8,
539 pub generation_v2: u64,
541 pub uuid: Uuid,
543 pub parent_uuid: Uuid,
545 pub received_uuid: Uuid,
547 pub ctransid: u64,
549 pub otransid: u64,
551 pub stransid: u64,
553 pub rtransid: u64,
555 pub ctime: Timespec,
557 pub otime: Timespec,
559 pub stime: Timespec,
561 pub rtime: Timespec,
563}
564
565impl RootItem {
566 pub fn parse(data: &[u8]) -> Option<Self> {
567 let inode_size = mem::size_of::<raw::btrfs_inode_item>();
568 if data.len() < inode_size + 8 {
569 return None;
570 }
571
572 let mut buf = &data[inode_size..];
573 let generation = buf.get_u64_le();
574 let root_dirid = buf.get_u64_le();
575 let bytenr = buf.get_u64_le();
576 let byte_limit = buf.get_u64_le();
577 let bytes_used = buf.get_u64_le();
578 let last_snapshot = buf.get_u64_le();
579 let flags = RootItemFlags::from_bits_truncate(buf.get_u64_le());
580 let refs = buf.get_u32_le();
581
582 let dp_off = inode_size + 60;
583 let drop_progress = if dp_off + 17 <= data.len() {
584 DiskKey::parse(data, dp_off)
585 } else {
586 DiskKey::parse(&[0; 17], 0)
587 };
588 let drop_level = if dp_off + 17 < data.len() {
589 data[dp_off + 17]
590 } else {
591 0
592 };
593
594 let level_off = mem::offset_of!(raw::btrfs_root_item, level);
595 let level = if level_off < data.len() {
596 data[level_off]
597 } else {
598 0
599 };
600 let generation_v2 = if level_off + 1 + 8 <= data.len() {
601 let mut b = &data[level_off + 1..];
602 b.get_u64_le()
603 } else {
604 0
605 };
606
607 let uuid_off = mem::offset_of!(raw::btrfs_root_item, uuid);
608 let uuid = if uuid_off + 16 <= data.len() {
609 let mut b = &data[uuid_off..];
610 get_uuid(&mut b)
611 } else {
612 Uuid::nil()
613 };
614 let parent_uuid = if uuid_off + 32 <= data.len() {
615 let mut b = &data[uuid_off + 16..];
616 get_uuid(&mut b)
617 } else {
618 Uuid::nil()
619 };
620 let received_uuid = if uuid_off + 48 <= data.len() {
621 let mut b = &data[uuid_off + 32..];
622 get_uuid(&mut b)
623 } else {
624 Uuid::nil()
625 };
626
627 let ct_off = mem::offset_of!(raw::btrfs_root_item, ctransid);
628 let ctransid = if ct_off + 8 <= data.len() {
629 let mut b = &data[ct_off..];
630 b.get_u64_le()
631 } else {
632 0
633 };
634 let otransid = if ct_off + 16 <= data.len() {
635 let mut b = &data[ct_off + 8..];
636 b.get_u64_le()
637 } else {
638 0
639 };
640 let stransid = if ct_off + 24 <= data.len() {
641 let mut b = &data[ct_off + 16..];
642 b.get_u64_le()
643 } else {
644 0
645 };
646 let rtransid = if ct_off + 32 <= data.len() {
647 let mut b = &data[ct_off + 24..];
648 b.get_u64_le()
649 } else {
650 0
651 };
652
653 let ctime_off = mem::offset_of!(raw::btrfs_root_item, ctime);
654 let ts_size = mem::size_of::<raw::btrfs_timespec>();
655 let ctime = if ctime_off + ts_size <= data.len() {
656 let mut b = &data[ctime_off..];
657 Timespec::parse(&mut b)
658 } else {
659 Timespec { sec: 0, nsec: 0 }
660 };
661 let otime = if ctime_off + 2 * ts_size <= data.len() {
662 let mut b = &data[ctime_off + ts_size..];
663 Timespec::parse(&mut b)
664 } else {
665 Timespec { sec: 0, nsec: 0 }
666 };
667 let stime = if ctime_off + 3 * ts_size <= data.len() {
668 let mut b = &data[ctime_off + 2 * ts_size..];
669 Timespec::parse(&mut b)
670 } else {
671 Timespec { sec: 0, nsec: 0 }
672 };
673 let rtime = if ctime_off + 4 * ts_size <= data.len() {
674 let mut b = &data[ctime_off + 3 * ts_size..];
675 Timespec::parse(&mut b)
676 } else {
677 Timespec { sec: 0, nsec: 0 }
678 };
679
680 Some(Self {
681 generation,
682 root_dirid,
683 bytenr,
684 byte_limit,
685 bytes_used,
686 last_snapshot,
687 flags,
688 refs,
689 drop_progress,
690 drop_level,
691 level,
692 generation_v2,
693 uuid,
694 parent_uuid,
695 received_uuid,
696 ctransid,
697 otransid,
698 stransid,
699 rtransid,
700 ctime,
701 otime,
702 stime,
703 rtime,
704 })
705 }
706}
707
708#[derive(Debug, Clone)]
713pub struct RootRef {
714 pub dirid: u64,
716 pub sequence: u64,
718 pub name: Vec<u8>,
720}
721
722impl RootRef {
723 pub fn parse(data: &[u8]) -> Option<Self> {
724 if data.len() < mem::size_of::<raw::btrfs_root_ref>() {
725 return None;
726 }
727 let mut buf = data;
728 let dirid = buf.get_u64_le();
729 let sequence = buf.get_u64_le();
730 let name_len = buf.get_u16_le() as usize;
731 let name_start = mem::size_of::<raw::btrfs_root_ref>();
732 let name = if name_start + name_len <= data.len() {
733 data[name_start..name_start + name_len].to_vec()
734 } else {
735 Vec::new()
736 };
737 Some(Self {
738 dirid,
739 sequence,
740 name,
741 })
742 }
743}
744
745#[derive(Debug, Clone)]
752pub struct FileExtentItem {
753 pub generation: u64,
755 pub ram_bytes: u64,
757 pub compression: CompressionType,
759 pub extent_type: FileExtentType,
761 pub body: FileExtentBody,
763}
764
765#[derive(Debug, Clone)]
767pub enum FileExtentBody {
768 Inline {
770 inline_size: usize,
772 },
773 Regular {
775 disk_bytenr: u64,
777 disk_num_bytes: u64,
779 offset: u64,
781 num_bytes: u64,
783 },
784}
785
786impl FileExtentItem {
787 pub fn parse(data: &[u8]) -> Option<Self> {
788 if data.len() < 21 {
789 return None;
790 }
791 let mut buf = data;
792 let generation = buf.get_u64_le();
793 let ram_bytes = buf.get_u64_le();
794 let compression = CompressionType::from_raw(buf.get_u8());
795 buf.advance(3); let extent_type = FileExtentType::from_raw(buf.get_u8());
797
798 let body = if extent_type == FileExtentType::Inline {
799 FileExtentBody::Inline {
800 inline_size: buf.remaining(),
801 }
802 } else if buf.remaining() >= 32 {
803 FileExtentBody::Regular {
804 disk_bytenr: buf.get_u64_le(),
805 disk_num_bytes: buf.get_u64_le(),
806 offset: buf.get_u64_le(),
807 num_bytes: buf.get_u64_le(),
808 }
809 } else {
810 return None;
811 };
812
813 Some(Self {
814 generation,
815 ram_bytes,
816 compression,
817 extent_type,
818 body,
819 })
820 }
821}
822
823fn extent_data_ref_hash(root: u64, objectid: u64, offset: u64) -> u64 {
827 let high_crc = raw_crc32c(!0u32, &root.to_le_bytes());
828 let low_crc = raw_crc32c(!0u32, &objectid.to_le_bytes());
829 let low_crc = raw_crc32c(low_crc, &offset.to_le_bytes());
830 (u64::from(high_crc) << 31) ^ u64::from(low_crc)
831}
832
833#[derive(Debug, Clone)]
835pub enum InlineRef {
836 TreeBlockBackref {
837 ref_offset: u64,
838 root: u64,
839 },
840 SharedBlockBackref {
841 ref_offset: u64,
842 parent: u64,
843 },
844 ExtentDataBackref {
845 ref_offset: u64,
846 root: u64,
847 objectid: u64,
848 offset: u64,
849 count: u32,
850 },
851 SharedDataBackref {
852 ref_offset: u64,
853 parent: u64,
854 count: u32,
855 },
856 ExtentOwnerRef {
857 ref_offset: u64,
858 root: u64,
859 },
860}
861
862impl InlineRef {
863 #[allow(clippy::cast_possible_truncation)]
865 pub fn raw_type(&self) -> u8 {
866 match self {
867 Self::TreeBlockBackref { .. } => {
868 raw::BTRFS_TREE_BLOCK_REF_KEY as u8
869 }
870 Self::SharedBlockBackref { .. } => {
871 raw::BTRFS_SHARED_BLOCK_REF_KEY as u8
872 }
873 Self::ExtentDataBackref { .. } => {
874 raw::BTRFS_EXTENT_DATA_REF_KEY as u8
875 }
876 Self::SharedDataBackref { .. } => {
877 raw::BTRFS_SHARED_DATA_REF_KEY as u8
878 }
879 Self::ExtentOwnerRef { .. } => {
880 raw::BTRFS_EXTENT_OWNER_REF_KEY as u8
881 }
882 }
883 }
884
885 pub fn raw_offset(&self) -> u64 {
887 match self {
888 Self::TreeBlockBackref { ref_offset, .. }
889 | Self::SharedBlockBackref { ref_offset, .. }
890 | Self::ExtentDataBackref { ref_offset, .. }
891 | Self::SharedDataBackref { ref_offset, .. }
892 | Self::ExtentOwnerRef { ref_offset, .. } => *ref_offset,
893 }
894 }
895}
896
897bitflags::bitflags! {
898 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
900 pub struct ExtentFlags: u64 {
901 const DATA = raw::BTRFS_EXTENT_FLAG_DATA as u64;
902 const TREE_BLOCK = raw::BTRFS_EXTENT_FLAG_TREE_BLOCK as u64;
903 const FULL_BACKREF = raw::BTRFS_BLOCK_FLAG_FULL_BACKREF as u64;
904 const _ = !0;
906 }
907}
908
909impl fmt::Display for ExtentFlags {
910 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
911 let mut parts = Vec::new();
912 if self.contains(Self::DATA) {
913 parts.push("DATA");
914 }
915 if self.contains(Self::TREE_BLOCK) {
916 parts.push("TREE_BLOCK");
917 }
918 if self.contains(Self::FULL_BACKREF) {
919 parts.push("FULL_BACKREF");
920 }
921 write!(f, "{}", parts.join("|"))
922 }
923}
924
925#[derive(Debug, Clone)]
931pub struct ExtentItem {
932 pub refs: u64,
934 pub generation: u64,
936 pub flags: ExtentFlags,
938 pub tree_block_key: Option<DiskKey>,
940 pub tree_block_level: Option<u8>,
942 pub skinny_level: Option<u64>,
944 pub inline_refs: Vec<InlineRef>,
946}
947
948impl ExtentItem {
949 pub fn is_data(&self) -> bool {
950 self.flags.contains(ExtentFlags::DATA)
951 }
952
953 pub fn is_tree_block(&self) -> bool {
954 self.flags.contains(ExtentFlags::TREE_BLOCK)
955 }
956
957 pub fn parse(data: &[u8], key: &DiskKey) -> Option<Self> {
958 use crate::tree::KeyType;
959
960 if data.len() < mem::size_of::<raw::btrfs_extent_item>() {
961 return None;
962 }
963 let mut buf = data;
964 let refs = buf.get_u64_le();
965 let generation = buf.get_u64_le();
966 let flags = ExtentFlags::from_bits_truncate(buf.get_u64_le());
967
968 let is_tree_block = flags.contains(ExtentFlags::TREE_BLOCK);
969
970 let mut tree_block_key = None;
971 let mut tree_block_level = None;
972 if is_tree_block
973 && key.key_type == KeyType::ExtentItem
974 && buf.remaining() > 17
975 {
976 tree_block_key = Some(DiskKey::parse(buf, 0));
977 buf.advance(17); tree_block_level = Some(buf.get_u8());
979 }
980
981 let skinny_level =
982 if key.key_type == KeyType::MetadataItem && is_tree_block {
983 Some(key.offset)
984 } else {
985 None
986 };
987
988 let mut inline_refs = Vec::new();
989 while buf.remaining() > 0 {
990 let ref_type = buf.get_u8();
991 let ref_offset = if buf.remaining() >= 8 {
992 buf.get_u64_le()
993 } else {
994 0
995 };
996
997 match u32::from(ref_type) {
998 raw::BTRFS_TREE_BLOCK_REF_KEY => {
999 inline_refs.push(InlineRef::TreeBlockBackref {
1000 ref_offset,
1001 root: ref_offset,
1002 });
1003 }
1004 raw::BTRFS_SHARED_BLOCK_REF_KEY => {
1005 inline_refs.push(InlineRef::SharedBlockBackref {
1006 ref_offset,
1007 parent: ref_offset,
1008 });
1009 }
1010 raw::BTRFS_EXTENT_DATA_REF_KEY => {
1011 let root = ref_offset; if buf.remaining() >= 20 {
1017 let oid = buf.get_u64_le();
1018 let off = buf.get_u64_le();
1019 let count = buf.get_u32_le();
1020 let hash = extent_data_ref_hash(root, oid, off);
1023 inline_refs.push(InlineRef::ExtentDataBackref {
1024 ref_offset: hash,
1025 root,
1026 objectid: oid,
1027 offset: off,
1028 count,
1029 });
1030 } else {
1031 break;
1032 }
1033 }
1034 raw::BTRFS_SHARED_DATA_REF_KEY => {
1035 if buf.remaining() >= 4 {
1036 let count = buf.get_u32_le();
1037 inline_refs.push(InlineRef::SharedDataBackref {
1038 ref_offset,
1039 parent: ref_offset,
1040 count,
1041 });
1042 } else {
1043 break;
1044 }
1045 }
1046 raw::BTRFS_EXTENT_OWNER_REF_KEY => {
1047 inline_refs.push(InlineRef::ExtentOwnerRef {
1048 ref_offset,
1049 root: ref_offset,
1050 });
1051 }
1052 _ => break,
1053 }
1054 }
1055
1056 Some(Self {
1057 refs,
1058 generation,
1059 flags,
1060 tree_block_key,
1061 tree_block_level,
1062 skinny_level,
1063 inline_refs,
1064 })
1065 }
1066}
1067
1068#[derive(Debug, Clone)]
1073pub struct ExtentDataRef {
1074 pub root: u64,
1076 pub objectid: u64,
1078 pub offset: u64,
1080 pub count: u32,
1082}
1083
1084impl ExtentDataRef {
1085 pub fn parse(data: &[u8]) -> Option<Self> {
1086 if data.len() < mem::size_of::<raw::btrfs_extent_data_ref>() {
1087 return None;
1088 }
1089 let mut buf = data;
1090 Some(Self {
1091 root: buf.get_u64_le(),
1092 objectid: buf.get_u64_le(),
1093 offset: buf.get_u64_le(),
1094 count: buf.get_u32_le(),
1095 })
1096 }
1097}
1098
1099#[derive(Debug, Clone)]
1103pub struct SharedDataRef {
1104 pub count: u32,
1106}
1107
1108impl SharedDataRef {
1109 pub fn parse(data: &[u8]) -> Option<Self> {
1110 if data.len() < 4 {
1111 return None;
1112 }
1113 let mut buf = data;
1114 Some(Self {
1115 count: buf.get_u32_le(),
1116 })
1117 }
1118}
1119
1120#[derive(Debug, Clone)]
1124pub struct BlockGroupItem {
1125 pub used: u64,
1127 pub chunk_objectid: u64,
1129 pub flags: BlockGroupFlags,
1131}
1132
1133impl BlockGroupItem {
1134 pub fn parse(data: &[u8]) -> Option<Self> {
1135 if data.len() < mem::size_of::<raw::btrfs_block_group_item>() {
1136 return None;
1137 }
1138 let mut buf = data;
1139 Some(Self {
1140 used: buf.get_u64_le(),
1141 chunk_objectid: buf.get_u64_le(),
1142 flags: BlockGroupFlags::from_bits_truncate(buf.get_u64_le()),
1143 })
1144 }
1145}
1146
1147#[derive(Debug, Clone)]
1152pub struct ChunkItem {
1153 pub length: u64,
1155 pub owner: u64,
1157 pub stripe_len: u64,
1159 pub chunk_type: BlockGroupFlags,
1161 pub io_align: u32,
1163 pub io_width: u32,
1165 pub sector_size: u32,
1167 pub num_stripes: u16,
1169 pub sub_stripes: u16,
1171 pub stripes: Vec<ChunkStripe>,
1173}
1174
1175#[derive(Debug, Clone)]
1177pub struct ChunkStripe {
1178 pub devid: u64,
1180 pub offset: u64,
1182 pub dev_uuid: Uuid,
1184}
1185
1186impl ChunkItem {
1187 pub fn parse(data: &[u8]) -> Option<Self> {
1188 let chunk_base_size = mem::offset_of!(raw::btrfs_chunk, stripe);
1189 if data.len() < chunk_base_size {
1190 return None;
1191 }
1192 let mut buf = data;
1193 let length = buf.get_u64_le();
1194 let owner = buf.get_u64_le();
1195 let stripe_len = buf.get_u64_le();
1196 let chunk_type = BlockGroupFlags::from_bits_truncate(buf.get_u64_le());
1197 let io_align = buf.get_u32_le();
1198 let io_width = buf.get_u32_le();
1199 let sector_size = buf.get_u32_le();
1200 let num_stripes = buf.get_u16_le();
1201 let sub_stripes = buf.get_u16_le();
1202 let stripe_size = mem::size_of::<raw::btrfs_stripe>();
1203 let mut stripes = Vec::with_capacity(num_stripes as usize);
1204 let mut sbuf = &data[chunk_base_size..];
1205 for i in 0..num_stripes as usize {
1206 let s_off = chunk_base_size + i * stripe_size;
1207 if s_off + stripe_size > data.len() {
1208 break;
1209 }
1210 let devid = sbuf.get_u64_le();
1211 let offset = sbuf.get_u64_le();
1212 let dev_uuid = get_uuid(&mut sbuf);
1213 stripes.push(ChunkStripe {
1214 devid,
1215 offset,
1216 dev_uuid,
1217 });
1218 }
1219 Some(Self {
1220 length,
1221 owner,
1222 stripe_len,
1223 chunk_type,
1224 io_align,
1225 io_width,
1226 sector_size,
1227 num_stripes,
1228 sub_stripes,
1229 stripes,
1230 })
1231 }
1232}
1233
1234#[derive(Debug, Clone)]
1239pub struct DeviceItem {
1240 pub devid: u64,
1242 pub total_bytes: u64,
1244 pub bytes_used: u64,
1246 pub io_align: u32,
1248 pub io_width: u32,
1250 pub sector_size: u32,
1252 pub dev_type: u64,
1254 pub generation: u64,
1256 pub start_offset: u64,
1258 pub dev_group: u32,
1260 pub seek_speed: u8,
1262 pub bandwidth: u8,
1264 pub uuid: Uuid,
1266 pub fsid: Uuid,
1268}
1269
1270impl DeviceItem {
1271 pub fn write_bytes(&self, buf: &mut impl BufMut) {
1273 buf.put_u64_le(self.devid);
1274 buf.put_u64_le(self.total_bytes);
1275 buf.put_u64_le(self.bytes_used);
1276 buf.put_u32_le(self.io_align);
1277 buf.put_u32_le(self.io_width);
1278 buf.put_u32_le(self.sector_size);
1279 buf.put_u64_le(self.dev_type);
1280 buf.put_u64_le(self.generation);
1281 buf.put_u64_le(self.start_offset);
1282 buf.put_u32_le(self.dev_group);
1283 buf.put_u8(self.seek_speed);
1284 buf.put_u8(self.bandwidth);
1285 buf.put_slice(self.uuid.as_bytes());
1286 buf.put_slice(self.fsid.as_bytes());
1287 }
1288
1289 pub fn parse(data: &[u8]) -> Option<Self> {
1290 if data.len() < mem::size_of::<raw::btrfs_dev_item>() {
1291 return None;
1292 }
1293 let mut buf = data;
1294 let devid = buf.get_u64_le();
1295 let total_bytes = buf.get_u64_le();
1296 let bytes_used = buf.get_u64_le();
1297 let io_align = buf.get_u32_le();
1298 let io_width = buf.get_u32_le();
1299 let sector_size = buf.get_u32_le();
1300 let dev_type = buf.get_u64_le();
1301 let generation = buf.get_u64_le();
1302 let start_offset = buf.get_u64_le();
1303 let dev_group = buf.get_u32_le();
1304 let seek_speed = buf.get_u8();
1305 let bandwidth = buf.get_u8();
1306 let uuid = get_uuid(&mut buf);
1307 let fsid = get_uuid(&mut buf);
1308 Some(Self {
1309 devid,
1310 total_bytes,
1311 bytes_used,
1312 io_align,
1313 io_width,
1314 sector_size,
1315 dev_type,
1316 generation,
1317 start_offset,
1318 dev_group,
1319 seek_speed,
1320 bandwidth,
1321 uuid,
1322 fsid,
1323 })
1324 }
1325}
1326
1327#[derive(Debug, Clone)]
1332pub struct DeviceExtent {
1333 pub chunk_tree: u64,
1335 pub chunk_objectid: u64,
1337 pub chunk_offset: u64,
1339 pub length: u64,
1341 pub chunk_tree_uuid: Uuid,
1343}
1344
1345impl DeviceExtent {
1346 pub fn parse(data: &[u8]) -> Option<Self> {
1347 if data.len() < mem::size_of::<raw::btrfs_dev_extent>() {
1348 return None;
1349 }
1350 let mut buf = data;
1351 let chunk_tree = buf.get_u64_le();
1352 let chunk_objectid = buf.get_u64_le();
1353 let chunk_offset = buf.get_u64_le();
1354 let length = buf.get_u64_le();
1355 let chunk_tree_uuid = get_uuid(&mut buf);
1356 Some(Self {
1357 chunk_tree,
1358 chunk_objectid,
1359 chunk_offset,
1360 length,
1361 chunk_tree_uuid,
1362 })
1363 }
1364}
1365
1366bitflags::bitflags! {
1367 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1369 pub struct FreeSpaceInfoFlags: u32 {
1370 const USING_BITMAPS = raw::BTRFS_FREE_SPACE_USING_BITMAPS;
1371 const _ = !0;
1373 }
1374}
1375
1376impl fmt::Display for FreeSpaceInfoFlags {
1377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1379 write!(f, "{}", self.bits())
1380 }
1381}
1382
1383#[derive(Debug, Clone)]
1387pub struct FreeSpaceInfo {
1388 pub extent_count: u32,
1390 pub flags: FreeSpaceInfoFlags,
1392}
1393
1394impl FreeSpaceInfo {
1395 pub fn parse(data: &[u8]) -> Option<Self> {
1396 if data.len() < 8 {
1397 return None;
1398 }
1399 let mut buf = data;
1400 Some(Self {
1401 extent_count: buf.get_u32_le(),
1402 flags: FreeSpaceInfoFlags::from_bits_truncate(buf.get_u32_le()),
1403 })
1404 }
1405}
1406
1407#[derive(Debug, Clone)]
1411pub struct QgroupStatus {
1412 pub version: u64,
1414 pub generation: u64,
1416 pub flags: u64,
1418 pub scan: u64,
1420 pub enable_gen: Option<u64>,
1422}
1423
1424impl QgroupStatus {
1425 pub fn parse(data: &[u8]) -> Option<Self> {
1426 if data.len() < 32 {
1427 return None;
1428 }
1429 let mut buf = data;
1430 let version = buf.get_u64_le();
1431 let generation = buf.get_u64_le();
1432 let flags = buf.get_u64_le();
1433 let scan = buf.get_u64_le();
1434 let enable_gen = if buf.remaining() >= 8 {
1435 Some(buf.get_u64_le())
1436 } else {
1437 None
1438 };
1439 Some(Self {
1440 version,
1441 generation,
1442 flags,
1443 scan,
1444 enable_gen,
1445 })
1446 }
1447}
1448
1449#[derive(Debug, Clone)]
1454pub struct QgroupInfo {
1455 pub generation: u64,
1457 pub referenced: u64,
1459 pub referenced_compressed: u64,
1461 pub exclusive: u64,
1463 pub exclusive_compressed: u64,
1465}
1466
1467impl QgroupInfo {
1468 pub fn parse(data: &[u8]) -> Option<Self> {
1469 if data.len() < mem::size_of::<raw::btrfs_qgroup_info_item>() {
1470 return None;
1471 }
1472 let mut buf = data;
1473 Some(Self {
1474 generation: buf.get_u64_le(),
1475 referenced: buf.get_u64_le(),
1476 referenced_compressed: buf.get_u64_le(),
1477 exclusive: buf.get_u64_le(),
1478 exclusive_compressed: buf.get_u64_le(),
1479 })
1480 }
1481}
1482
1483#[derive(Debug, Clone)]
1488pub struct QgroupLimit {
1489 pub flags: u64,
1491 pub max_referenced: u64,
1493 pub max_exclusive: u64,
1495 pub rsv_referenced: u64,
1497 pub rsv_exclusive: u64,
1499}
1500
1501impl QgroupLimit {
1502 pub fn parse(data: &[u8]) -> Option<Self> {
1503 if data.len() < mem::size_of::<raw::btrfs_qgroup_limit_item>() {
1504 return None;
1505 }
1506 let mut buf = data;
1507 Some(Self {
1508 flags: buf.get_u64_le(),
1509 max_referenced: buf.get_u64_le(),
1510 max_exclusive: buf.get_u64_le(),
1511 rsv_referenced: buf.get_u64_le(),
1512 rsv_exclusive: buf.get_u64_le(),
1513 })
1514 }
1515}
1516
1517#[derive(Debug, Clone)]
1523pub struct DeviceStats {
1524 pub values: Vec<(String, u64)>,
1526}
1527
1528impl DeviceStats {
1529 pub fn parse(data: &[u8]) -> Self {
1530 let stat_names = [
1531 "write_errs",
1532 "read_errs",
1533 "flush_errs",
1534 "corruption_errs",
1535 "generation",
1536 ];
1537 let mut buf = data;
1538 let mut values = Vec::new();
1539 for name in &stat_names {
1540 if buf.remaining() >= 8 {
1541 values.push((name.to_string(), buf.get_u64_le()));
1542 }
1543 }
1544 DeviceStats { values }
1545 }
1546}
1547
1548#[derive(Debug, Clone)]
1552pub struct UuidItem {
1553 pub subvol_ids: Vec<u64>,
1555}
1556
1557impl UuidItem {
1558 pub fn parse(data: &[u8]) -> Self {
1559 let mut buf = data;
1560 let mut subvol_ids = Vec::new();
1561 while buf.remaining() >= 8 {
1562 subvol_ids.push(buf.get_u64_le());
1563 }
1564 Self { subvol_ids }
1565 }
1566}
1567
1568pub enum ItemPayload {
1574 InodeItem(InodeItem),
1575 InodeRef(Vec<InodeRef>),
1576 InodeExtref(Vec<InodeExtref>),
1577 DirItem(Vec<DirItem>),
1578 DirLogItem { end: u64 },
1579 OrphanItem,
1580 RootItem(RootItem),
1581 RootRef(RootRef),
1582 FileExtentItem(FileExtentItem),
1583 ExtentCsum { data: Vec<u8> },
1584 ExtentItem(ExtentItem),
1585 TreeBlockRef,
1586 SharedBlockRef,
1587 ExtentDataRef(ExtentDataRef),
1588 SharedDataRef(SharedDataRef),
1589 ExtentOwnerRef { root: u64 },
1590 BlockGroupItem(BlockGroupItem),
1591 FreeSpaceInfo(FreeSpaceInfo),
1592 FreeSpaceExtent,
1593 FreeSpaceBitmap,
1594 ChunkItem(ChunkItem),
1595 DeviceItem(DeviceItem),
1596 DeviceExtent(DeviceExtent),
1597 QgroupStatus(QgroupStatus),
1598 QgroupInfo(QgroupInfo),
1599 QgroupLimit(QgroupLimit),
1600 QgroupRelation,
1601 DeviceStats(DeviceStats),
1602 BalanceItem { flags: u64 },
1603 DeviceReplace(DeviceReplaceItem),
1604 UuidItem(UuidItem),
1605 StringItem(Vec<u8>),
1606 RaidStripe(RaidStripeItem),
1607 Unknown(Vec<u8>),
1608}
1609
1610#[derive(Debug, Clone)]
1614pub struct DeviceReplaceItem {
1615 pub src_devid: u64,
1617 pub cursor_left: u64,
1619 pub cursor_right: u64,
1621 pub replace_mode: u64,
1623 pub replace_state: u64,
1625 pub time_started: u64,
1627 pub time_stopped: u64,
1629 pub num_write_errors: u64,
1631 pub num_uncorrectable_read_errors: u64,
1633}
1634
1635impl DeviceReplaceItem {
1636 pub fn parse(data: &[u8]) -> Option<Self> {
1637 if data.len() < 80 {
1638 return None;
1639 }
1640 let mut buf = data;
1641 Some(Self {
1642 src_devid: buf.get_u64_le(),
1643 cursor_left: buf.get_u64_le(),
1644 cursor_right: buf.get_u64_le(),
1645 replace_mode: buf.get_u64_le(),
1646 replace_state: buf.get_u64_le(),
1647 time_started: buf.get_u64_le(),
1648 time_stopped: buf.get_u64_le(),
1649 num_write_errors: buf.get_u64_le(),
1650 num_uncorrectable_read_errors: buf.get_u64_le(),
1651 })
1652 }
1653}
1654
1655#[derive(Debug, Clone)]
1659pub struct RaidStripeItem {
1660 pub encoding: u64,
1662 pub stripes: Vec<RaidStripeEntry>,
1664}
1665
1666#[derive(Debug, Clone)]
1668pub struct RaidStripeEntry {
1669 pub devid: u64,
1671 pub physical: u64,
1673}
1674
1675impl RaidStripeItem {
1676 pub fn parse(data: &[u8]) -> Option<Self> {
1677 if data.len() < 8 {
1678 return None;
1679 }
1680 let mut buf = data;
1681 let encoding = buf.get_u64_le();
1682 let mut stripes = Vec::new();
1683 while buf.remaining() >= 16 {
1684 stripes.push(RaidStripeEntry {
1685 devid: buf.get_u64_le(),
1686 physical: buf.get_u64_le(),
1687 });
1688 }
1689 Some(Self { encoding, stripes })
1690 }
1691}
1692
1693#[allow(clippy::too_many_lines)]
1695pub fn parse_item_payload(key: &DiskKey, data: &[u8]) -> ItemPayload {
1696 use crate::tree::KeyType;
1697
1698 match key.key_type {
1699 KeyType::InodeItem => match InodeItem::parse(data) {
1700 Some(v) => ItemPayload::InodeItem(v),
1701 None => ItemPayload::Unknown(data.to_vec()),
1702 },
1703 KeyType::InodeRef => ItemPayload::InodeRef(InodeRef::parse_all(data)),
1704 KeyType::InodeExtref => {
1705 ItemPayload::InodeExtref(InodeExtref::parse_all(data))
1706 }
1707 KeyType::DirItem | KeyType::DirIndex | KeyType::XattrItem => {
1708 ItemPayload::DirItem(DirItem::parse_all(data))
1709 }
1710 KeyType::DirLogItem | KeyType::DirLogIndex => {
1711 let end = if data.len() >= 8 {
1712 let mut buf = data;
1713 buf.get_u64_le()
1714 } else {
1715 0
1716 };
1717 ItemPayload::DirLogItem { end }
1718 }
1719 KeyType::OrphanItem => ItemPayload::OrphanItem,
1720 KeyType::RootItem => match RootItem::parse(data) {
1721 Some(v) => ItemPayload::RootItem(v),
1722 None => ItemPayload::Unknown(data.to_vec()),
1723 },
1724 KeyType::RootRef | KeyType::RootBackref => match RootRef::parse(data) {
1725 Some(v) => ItemPayload::RootRef(v),
1726 None => ItemPayload::Unknown(data.to_vec()),
1727 },
1728 KeyType::ExtentData => match FileExtentItem::parse(data) {
1729 Some(v) => ItemPayload::FileExtentItem(v),
1730 None => ItemPayload::Unknown(data.to_vec()),
1731 },
1732 KeyType::ExtentCsum => ItemPayload::ExtentCsum {
1733 data: data.to_vec(),
1734 },
1735 KeyType::ExtentItem | KeyType::MetadataItem => {
1736 match ExtentItem::parse(data, key) {
1737 Some(v) => ItemPayload::ExtentItem(v),
1738 None => ItemPayload::Unknown(data.to_vec()),
1739 }
1740 }
1741 KeyType::TreeBlockRef => ItemPayload::TreeBlockRef,
1742 KeyType::SharedBlockRef => ItemPayload::SharedBlockRef,
1743 KeyType::ExtentDataRef => match ExtentDataRef::parse(data) {
1744 Some(v) => ItemPayload::ExtentDataRef(v),
1745 None => ItemPayload::Unknown(data.to_vec()),
1746 },
1747 KeyType::SharedDataRef => match SharedDataRef::parse(data) {
1748 Some(v) => ItemPayload::SharedDataRef(v),
1749 None => ItemPayload::Unknown(data.to_vec()),
1750 },
1751 KeyType::ExtentOwnerRef => {
1752 if data.len() >= 8 {
1753 let mut buf = data;
1754 ItemPayload::ExtentOwnerRef {
1755 root: buf.get_u64_le(),
1756 }
1757 } else {
1758 ItemPayload::Unknown(data.to_vec())
1759 }
1760 }
1761 KeyType::BlockGroupItem => match BlockGroupItem::parse(data) {
1762 Some(v) => ItemPayload::BlockGroupItem(v),
1763 None => ItemPayload::Unknown(data.to_vec()),
1764 },
1765 KeyType::FreeSpaceInfo => match FreeSpaceInfo::parse(data) {
1766 Some(v) => ItemPayload::FreeSpaceInfo(v),
1767 None => ItemPayload::Unknown(data.to_vec()),
1768 },
1769 KeyType::FreeSpaceExtent => ItemPayload::FreeSpaceExtent,
1770 KeyType::FreeSpaceBitmap => ItemPayload::FreeSpaceBitmap,
1771 KeyType::ChunkItem => match ChunkItem::parse(data) {
1772 Some(v) => ItemPayload::ChunkItem(v),
1773 None => ItemPayload::Unknown(data.to_vec()),
1774 },
1775 KeyType::DeviceItem => match DeviceItem::parse(data) {
1776 Some(v) => ItemPayload::DeviceItem(v),
1777 None => ItemPayload::Unknown(data.to_vec()),
1778 },
1779 KeyType::DeviceExtent => match DeviceExtent::parse(data) {
1780 Some(v) => ItemPayload::DeviceExtent(v),
1781 None => ItemPayload::Unknown(data.to_vec()),
1782 },
1783 KeyType::QgroupStatus => match QgroupStatus::parse(data) {
1784 Some(v) => ItemPayload::QgroupStatus(v),
1785 None => ItemPayload::Unknown(data.to_vec()),
1786 },
1787 KeyType::QgroupInfo => match QgroupInfo::parse(data) {
1788 Some(v) => ItemPayload::QgroupInfo(v),
1789 None => ItemPayload::Unknown(data.to_vec()),
1790 },
1791 KeyType::QgroupLimit => match QgroupLimit::parse(data) {
1792 Some(v) => ItemPayload::QgroupLimit(v),
1793 None => ItemPayload::Unknown(data.to_vec()),
1794 },
1795 KeyType::QgroupRelation => ItemPayload::QgroupRelation,
1796 KeyType::PersistentItem => {
1797 if key.objectid == u64::from(raw::BTRFS_DEV_STATS_OBJECTID) {
1798 ItemPayload::DeviceStats(DeviceStats::parse(data))
1799 } else {
1800 ItemPayload::Unknown(data.to_vec())
1801 }
1802 }
1803 KeyType::TemporaryItem => {
1804 if ObjectId::from_raw(key.objectid) == ObjectId::Balance
1805 && data.len() >= 8
1806 {
1807 ItemPayload::BalanceItem {
1808 flags: {
1809 let mut buf = data;
1810 buf.get_u64_le()
1811 },
1812 }
1813 } else {
1814 ItemPayload::Unknown(data.to_vec())
1815 }
1816 }
1817 KeyType::DeviceReplace => match DeviceReplaceItem::parse(data) {
1818 Some(v) => ItemPayload::DeviceReplace(v),
1819 None => ItemPayload::Unknown(data.to_vec()),
1820 },
1821 KeyType::UuidKeySubvol | KeyType::UuidKeyReceivedSubvol => {
1822 ItemPayload::UuidItem(UuidItem::parse(data))
1823 }
1824 KeyType::StringItem => ItemPayload::StringItem(data.to_vec()),
1825 KeyType::RaidStripe => match RaidStripeItem::parse(data) {
1826 Some(v) => ItemPayload::RaidStripe(v),
1827 None => ItemPayload::Unknown(data.to_vec()),
1828 },
1829 _ => ItemPayload::Unknown(data.to_vec()),
1830 }
1831}
1832
1833#[cfg(test)]
1834mod tests {
1835 use super::*;
1836
1837 #[test]
1840 fn compression_type_round_trip() {
1841 for v in 0..=3 {
1842 let ct = CompressionType::from_raw(v);
1843 assert_eq!(ct.to_raw(), v);
1844 }
1845 assert_eq!(CompressionType::from_raw(0), CompressionType::None);
1846 assert_eq!(CompressionType::from_raw(1), CompressionType::Zlib);
1847 assert_eq!(CompressionType::from_raw(2), CompressionType::Lzo);
1848 assert_eq!(CompressionType::from_raw(3), CompressionType::Zstd);
1849 assert_eq!(CompressionType::from_raw(99), CompressionType::Unknown(99));
1850 assert_eq!(CompressionType::Unknown(99).to_raw(), 99);
1851 }
1852
1853 #[test]
1854 fn compression_type_names() {
1855 assert_eq!(CompressionType::None.name(), "none");
1856 assert_eq!(CompressionType::Zlib.name(), "zlib");
1857 assert_eq!(CompressionType::Lzo.name(), "lzo");
1858 assert_eq!(CompressionType::Zstd.name(), "zstd");
1859 assert_eq!(CompressionType::Unknown(42).name(), "unknown");
1860 }
1861
1862 #[test]
1863 fn file_extent_type_round_trip() {
1864 assert_eq!(FileExtentType::from_raw(0), FileExtentType::Inline);
1865 assert_eq!(FileExtentType::from_raw(1), FileExtentType::Regular);
1866 assert_eq!(FileExtentType::from_raw(2), FileExtentType::Prealloc);
1867 assert_eq!(FileExtentType::from_raw(77), FileExtentType::Unknown(77));
1868 for v in 0..=2 {
1869 let ft = FileExtentType::from_raw(v);
1870 assert_eq!(ft.to_raw(), v);
1871 }
1872 assert_eq!(FileExtentType::Unknown(77).to_raw(), 77);
1873 }
1874
1875 #[test]
1876 fn file_extent_type_names() {
1877 assert_eq!(FileExtentType::Inline.name(), "inline");
1878 assert_eq!(FileExtentType::Regular.name(), "regular");
1879 assert_eq!(FileExtentType::Prealloc.name(), "prealloc");
1880 assert_eq!(FileExtentType::Unknown(5).name(), "unknown");
1881 }
1882
1883 #[test]
1884 fn file_type_from_raw_all_variants() {
1885 assert_eq!(FileType::from_raw(0), FileType::Unknown);
1886 assert_eq!(FileType::from_raw(1), FileType::RegFile);
1887 assert_eq!(FileType::from_raw(2), FileType::Dir);
1888 assert_eq!(FileType::from_raw(3), FileType::Chrdev);
1889 assert_eq!(FileType::from_raw(4), FileType::Blkdev);
1890 assert_eq!(FileType::from_raw(5), FileType::Fifo);
1891 assert_eq!(FileType::from_raw(6), FileType::Sock);
1892 assert_eq!(FileType::from_raw(7), FileType::Symlink);
1893 assert_eq!(FileType::from_raw(8), FileType::Xattr);
1894 assert_eq!(FileType::from_raw(99), FileType::Other(99));
1895 }
1896
1897 #[test]
1898 fn file_type_names() {
1899 assert_eq!(FileType::Unknown.name(), "UNKNOWN");
1900 assert_eq!(FileType::RegFile.name(), "FILE");
1901 assert_eq!(FileType::Dir.name(), "DIR");
1902 assert_eq!(FileType::Chrdev.name(), "CHRDEV");
1903 assert_eq!(FileType::Blkdev.name(), "BLKDEV");
1904 assert_eq!(FileType::Fifo.name(), "FIFO");
1905 assert_eq!(FileType::Sock.name(), "SOCK");
1906 assert_eq!(FileType::Symlink.name(), "SYMLINK");
1907 assert_eq!(FileType::Xattr.name(), "XATTR");
1908 assert_eq!(FileType::Other(200).name(), "UNKNOWN");
1909 }
1910
1911 #[test]
1914 fn block_group_item_parse() {
1915 let mut buf = Vec::new();
1916 buf.extend_from_slice(&1000u64.to_le_bytes()); buf.extend_from_slice(&256u64.to_le_bytes()); buf.extend_from_slice(
1919 &(raw::BTRFS_BLOCK_GROUP_DATA as u64).to_le_bytes(),
1920 );
1921 let item = BlockGroupItem::parse(&buf).unwrap();
1922 assert_eq!(item.used, 1000);
1923 assert_eq!(item.chunk_objectid, 256);
1924 assert_eq!(item.flags, BlockGroupFlags::DATA);
1925 }
1926
1927 #[test]
1928 fn block_group_item_too_short() {
1929 assert!(BlockGroupItem::parse(&[0; 23]).is_none());
1930 }
1931
1932 #[test]
1933 fn free_space_info_parse() {
1934 let mut buf = Vec::new();
1935 buf.extend_from_slice(&42u32.to_le_bytes());
1936 buf.extend_from_slice(&7u32.to_le_bytes());
1937 let info = FreeSpaceInfo::parse(&buf).unwrap();
1938 assert_eq!(info.extent_count, 42);
1939 assert_eq!(info.flags, FreeSpaceInfoFlags::from_bits_truncate(7));
1940 }
1941
1942 #[test]
1943 fn free_space_info_too_short() {
1944 assert!(FreeSpaceInfo::parse(&[0; 7]).is_none());
1945 }
1946
1947 #[test]
1948 fn dev_extent_parse() {
1949 let size = mem::size_of::<raw::btrfs_dev_extent>();
1950 let mut buf = vec![0u8; size];
1951 buf[0..8].copy_from_slice(&3u64.to_le_bytes()); buf[8..16].copy_from_slice(&256u64.to_le_bytes()); buf[16..24].copy_from_slice(&0x10000u64.to_le_bytes()); buf[24..32].copy_from_slice(&0x40000u64.to_le_bytes()); buf[32..48].copy_from_slice(&[0xAB; 16]);
1957 let de = DeviceExtent::parse(&buf).unwrap();
1958 assert_eq!(de.chunk_tree, 3);
1959 assert_eq!(de.chunk_objectid, 256);
1960 assert_eq!(de.chunk_offset, 0x10000);
1961 assert_eq!(de.length, 0x40000);
1962 assert_eq!(de.chunk_tree_uuid.as_bytes(), &[0xAB; 16]);
1963 }
1964
1965 #[test]
1966 fn dev_extent_too_short() {
1967 let size = mem::size_of::<raw::btrfs_dev_extent>();
1968 assert!(DeviceExtent::parse(&vec![0u8; size - 1]).is_none());
1969 }
1970
1971 #[test]
1972 fn extent_data_ref_parse() {
1973 let size = mem::size_of::<raw::btrfs_extent_data_ref>();
1974 let mut buf = vec![0u8; size];
1975 buf[0..8].copy_from_slice(&5u64.to_le_bytes()); buf[8..16].copy_from_slice(&256u64.to_le_bytes()); buf[16..24].copy_from_slice(&0u64.to_le_bytes()); buf[24..28].copy_from_slice(&1u32.to_le_bytes()); let edr = ExtentDataRef::parse(&buf).unwrap();
1980 assert_eq!(edr.root, 5);
1981 assert_eq!(edr.objectid, 256);
1982 assert_eq!(edr.offset, 0);
1983 assert_eq!(edr.count, 1);
1984 }
1985
1986 #[test]
1987 fn extent_data_ref_too_short() {
1988 assert!(ExtentDataRef::parse(&[0; 27]).is_none());
1989 }
1990
1991 #[test]
1992 fn shared_data_ref_parse() {
1993 let buf = 17u32.to_le_bytes();
1994 let sdr = SharedDataRef::parse(&buf).unwrap();
1995 assert_eq!(sdr.count, 17);
1996 }
1997
1998 #[test]
1999 fn shared_data_ref_too_short() {
2000 assert!(SharedDataRef::parse(&[0; 3]).is_none());
2001 }
2002
2003 #[test]
2004 fn qgroup_info_parse() {
2005 let mut buf = Vec::new();
2006 buf.extend_from_slice(&100u64.to_le_bytes()); buf.extend_from_slice(&4096u64.to_le_bytes()); buf.extend_from_slice(&4096u64.to_le_bytes()); buf.extend_from_slice(&2048u64.to_le_bytes()); buf.extend_from_slice(&2048u64.to_le_bytes()); let qi = QgroupInfo::parse(&buf).unwrap();
2012 assert_eq!(qi.generation, 100);
2013 assert_eq!(qi.referenced, 4096);
2014 assert_eq!(qi.referenced_compressed, 4096);
2015 assert_eq!(qi.exclusive, 2048);
2016 assert_eq!(qi.exclusive_compressed, 2048);
2017 }
2018
2019 #[test]
2020 fn qgroup_info_too_short() {
2021 assert!(QgroupInfo::parse(&[0; 39]).is_none());
2022 }
2023
2024 #[test]
2025 fn qgroup_limit_parse() {
2026 let mut buf = Vec::new();
2027 buf.extend_from_slice(&3u64.to_le_bytes()); buf.extend_from_slice(&1_000_000u64.to_le_bytes()); buf.extend_from_slice(&500_000u64.to_le_bytes()); buf.extend_from_slice(&0u64.to_le_bytes()); buf.extend_from_slice(&0u64.to_le_bytes()); let ql = QgroupLimit::parse(&buf).unwrap();
2033 assert_eq!(ql.flags, 3);
2034 assert_eq!(ql.max_referenced, 1_000_000);
2035 assert_eq!(ql.max_exclusive, 500_000);
2036 assert_eq!(ql.rsv_referenced, 0);
2037 assert_eq!(ql.rsv_exclusive, 0);
2038 }
2039
2040 #[test]
2041 fn qgroup_limit_too_short() {
2042 assert!(QgroupLimit::parse(&[0; 39]).is_none());
2043 }
2044
2045 #[test]
2046 fn qgroup_status_parse_minimal() {
2047 let mut buf = Vec::new();
2048 buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&50u64.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes()); buf.extend_from_slice(&0u64.to_le_bytes()); let qs = QgroupStatus::parse(&buf).unwrap();
2053 assert_eq!(qs.version, 1);
2054 assert_eq!(qs.generation, 50);
2055 assert_eq!(qs.flags, 2);
2056 assert_eq!(qs.scan, 0);
2057 assert!(qs.enable_gen.is_none());
2058 }
2059
2060 #[test]
2061 fn qgroup_status_parse_with_enable_gen() {
2062 let mut buf = Vec::new();
2063 buf.extend_from_slice(&1u64.to_le_bytes());
2064 buf.extend_from_slice(&50u64.to_le_bytes());
2065 buf.extend_from_slice(&2u64.to_le_bytes());
2066 buf.extend_from_slice(&0u64.to_le_bytes());
2067 buf.extend_from_slice(&99u64.to_le_bytes()); let qs = QgroupStatus::parse(&buf).unwrap();
2069 assert_eq!(qs.enable_gen, Some(99));
2070 }
2071
2072 #[test]
2073 fn qgroup_status_too_short() {
2074 assert!(QgroupStatus::parse(&[0; 31]).is_none());
2075 }
2076
2077 #[test]
2078 fn dev_replace_item_parse() {
2079 let mut buf = vec![0u8; 80];
2080 buf[0..8].copy_from_slice(&1u64.to_le_bytes()); buf[8..16].copy_from_slice(&0x1000u64.to_le_bytes()); buf[16..24].copy_from_slice(&0x2000u64.to_le_bytes()); buf[24..32].copy_from_slice(&0u64.to_le_bytes()); buf[32..40].copy_from_slice(&2u64.to_le_bytes()); buf[40..48].copy_from_slice(&1700000000u64.to_le_bytes()); buf[48..56].copy_from_slice(&1700000100u64.to_le_bytes()); buf[56..64].copy_from_slice(&3u64.to_le_bytes()); buf[64..72].copy_from_slice(&5u64.to_le_bytes()); let dri = DeviceReplaceItem::parse(&buf).unwrap();
2090 assert_eq!(dri.src_devid, 1);
2091 assert_eq!(dri.cursor_left, 0x1000);
2092 assert_eq!(dri.cursor_right, 0x2000);
2093 assert_eq!(dri.replace_state, 2);
2094 assert_eq!(dri.time_started, 1700000000);
2095 assert_eq!(dri.time_stopped, 1700000100);
2096 assert_eq!(dri.num_write_errors, 3);
2097 assert_eq!(dri.num_uncorrectable_read_errors, 5);
2098 }
2099
2100 #[test]
2101 fn dev_replace_item_too_short() {
2102 assert!(DeviceReplaceItem::parse(&[0; 79]).is_none());
2103 }
2104
2105 #[test]
2106 fn raid_stripe_item_parse() {
2107 let mut buf = Vec::new();
2108 buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&0x10000u64.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes());
2114 buf.extend_from_slice(&0x20000u64.to_le_bytes());
2115 let rsi = RaidStripeItem::parse(&buf).unwrap();
2116 assert_eq!(rsi.encoding, 1);
2117 assert_eq!(rsi.stripes.len(), 2);
2118 assert_eq!(rsi.stripes[0].devid, 1);
2119 assert_eq!(rsi.stripes[0].physical, 0x10000);
2120 assert_eq!(rsi.stripes[1].devid, 2);
2121 assert_eq!(rsi.stripes[1].physical, 0x20000);
2122 }
2123
2124 #[test]
2125 fn raid_stripe_item_no_stripes() {
2126 let buf = 42u64.to_le_bytes();
2127 let rsi = RaidStripeItem::parse(&buf).unwrap();
2128 assert_eq!(rsi.encoding, 42);
2129 assert!(rsi.stripes.is_empty());
2130 }
2131
2132 #[test]
2133 fn raid_stripe_item_too_short() {
2134 assert!(RaidStripeItem::parse(&[0; 7]).is_none());
2135 }
2136
2137 #[test]
2140 fn inode_ref_parse_single() {
2141 let mut buf = Vec::new();
2142 buf.extend_from_slice(&42u64.to_le_bytes()); buf.extend_from_slice(&4u16.to_le_bytes()); buf.extend_from_slice(b"test");
2145 let refs = InodeRef::parse_all(&buf);
2146 assert_eq!(refs.len(), 1);
2147 assert_eq!(refs[0].index, 42);
2148 assert_eq!(refs[0].name, b"test");
2149 }
2150
2151 #[test]
2152 fn inode_ref_parse_multiple() {
2153 let mut buf = Vec::new();
2154 buf.extend_from_slice(&1u64.to_le_bytes());
2156 buf.extend_from_slice(&3u16.to_le_bytes());
2157 buf.extend_from_slice(b"abc");
2158 buf.extend_from_slice(&2u64.to_le_bytes());
2160 buf.extend_from_slice(&2u16.to_le_bytes());
2161 buf.extend_from_slice(b"xy");
2162 let refs = InodeRef::parse_all(&buf);
2163 assert_eq!(refs.len(), 2);
2164 assert_eq!(refs[0].index, 1);
2165 assert_eq!(refs[0].name, b"abc");
2166 assert_eq!(refs[1].index, 2);
2167 assert_eq!(refs[1].name, b"xy");
2168 }
2169
2170 #[test]
2171 fn inode_ref_parse_truncated() {
2172 let mut buf = Vec::new();
2174 buf.extend_from_slice(&1u64.to_le_bytes());
2175 buf.extend_from_slice(&10u16.to_le_bytes()); buf.extend_from_slice(b"abc"); let refs = InodeRef::parse_all(&buf);
2178 assert!(refs.is_empty());
2179 }
2180
2181 #[test]
2182 fn inode_extref_parse_single() {
2183 let mut buf = Vec::new();
2184 buf.extend_from_slice(&256u64.to_le_bytes()); buf.extend_from_slice(&3u64.to_le_bytes()); buf.extend_from_slice(&5u16.to_le_bytes()); buf.extend_from_slice(b"hello");
2188 let refs = InodeExtref::parse_all(&buf);
2189 assert_eq!(refs.len(), 1);
2190 assert_eq!(refs[0].parent, 256);
2191 assert_eq!(refs[0].index, 3);
2192 assert_eq!(refs[0].name, b"hello");
2193 }
2194
2195 #[test]
2196 fn dir_item_parse_single() {
2197 let dir_item_size = mem::size_of::<raw::btrfs_dir_item>();
2198 let mut buf = vec![0u8; dir_item_size];
2199 buf[0..8].copy_from_slice(&256u64.to_le_bytes()); buf[8] = 1; buf[9..17].copy_from_slice(&0u64.to_le_bytes()); buf[17..25].copy_from_slice(&100u64.to_le_bytes());
2205 buf[25..27].copy_from_slice(&0u16.to_le_bytes());
2207 buf[27..29].copy_from_slice(&4u16.to_le_bytes());
2209 buf[29] = 1; buf.extend_from_slice(b"file");
2213 let items = DirItem::parse_all(&buf);
2214 assert_eq!(items.len(), 1);
2215 assert_eq!(items[0].transid, 100);
2216 assert_eq!(items[0].file_type, FileType::RegFile);
2217 assert_eq!(items[0].name, b"file");
2218 assert!(items[0].data.is_empty());
2219 }
2220
2221 #[test]
2222 fn root_ref_parse() {
2223 let hdr_size = mem::size_of::<raw::btrfs_root_ref>();
2224 let mut buf = vec![0u8; hdr_size];
2225 buf[0..8].copy_from_slice(&256u64.to_le_bytes()); buf[8..16].copy_from_slice(&7u64.to_le_bytes()); buf[16..18].copy_from_slice(&6u16.to_le_bytes()); buf.extend_from_slice(b"subvol");
2229 let rr = RootRef::parse(&buf).unwrap();
2230 assert_eq!(rr.dirid, 256);
2231 assert_eq!(rr.sequence, 7);
2232 assert_eq!(rr.name, b"subvol");
2233 }
2234
2235 #[test]
2236 fn root_ref_too_short() {
2237 let hdr_size = mem::size_of::<raw::btrfs_root_ref>();
2238 assert!(RootRef::parse(&vec![0u8; hdr_size - 1]).is_none());
2239 }
2240
2241 #[test]
2242 fn uuid_item_parse() {
2243 let mut buf = Vec::new();
2244 buf.extend_from_slice(&256u64.to_le_bytes());
2245 buf.extend_from_slice(&257u64.to_le_bytes());
2246 buf.extend_from_slice(&258u64.to_le_bytes());
2247 let ui = UuidItem::parse(&buf);
2248 assert_eq!(ui.subvol_ids, vec![256, 257, 258]);
2249 }
2250
2251 #[test]
2252 fn uuid_item_empty() {
2253 let ui = UuidItem::parse(&[]);
2254 assert!(ui.subvol_ids.is_empty());
2255 }
2256
2257 #[test]
2258 fn dev_stats_parse() {
2259 let mut buf = Vec::new();
2260 buf.extend_from_slice(&1u64.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes()); buf.extend_from_slice(&3u64.to_le_bytes()); buf.extend_from_slice(&4u64.to_le_bytes()); buf.extend_from_slice(&5u64.to_le_bytes()); let ds = DeviceStats::parse(&buf);
2266 assert_eq!(ds.values.len(), 5);
2267 assert_eq!(ds.values[0], ("write_errs".to_string(), 1));
2268 assert_eq!(ds.values[1], ("read_errs".to_string(), 2));
2269 assert_eq!(ds.values[2], ("flush_errs".to_string(), 3));
2270 assert_eq!(ds.values[3], ("corruption_errs".to_string(), 4));
2271 assert_eq!(ds.values[4], ("generation".to_string(), 5));
2272 }
2273
2274 #[test]
2275 fn dev_stats_partial() {
2276 let mut buf = Vec::new();
2278 buf.extend_from_slice(&10u64.to_le_bytes());
2279 buf.extend_from_slice(&20u64.to_le_bytes());
2280 let ds = DeviceStats::parse(&buf);
2281 assert_eq!(ds.values.len(), 2);
2282 assert_eq!(ds.values[0].1, 10);
2283 assert_eq!(ds.values[1].1, 20);
2284 }
2285
2286 #[test]
2289 fn file_extent_item_inline() {
2290 let mut buf = vec![0u8; 21 + 10]; buf[0..8].copy_from_slice(&7u64.to_le_bytes()); buf[8..16].copy_from_slice(&10u64.to_le_bytes()); buf[16] = 0; buf[20] = 0; buf[21..31].copy_from_slice(&[0xAA; 10]); let fei = FileExtentItem::parse(&buf).unwrap();
2298 assert_eq!(fei.generation, 7);
2299 assert_eq!(fei.ram_bytes, 10);
2300 assert_eq!(fei.compression, CompressionType::None);
2301 assert_eq!(fei.extent_type, FileExtentType::Inline);
2302 match fei.body {
2303 FileExtentBody::Inline { inline_size } => {
2304 assert_eq!(inline_size, 10)
2305 }
2306 _ => panic!("expected inline body"),
2307 }
2308 }
2309
2310 #[test]
2311 fn file_extent_item_regular() {
2312 let mut buf = vec![0u8; 53];
2313 buf[0..8].copy_from_slice(&100u64.to_le_bytes()); buf[8..16].copy_from_slice(&4096u64.to_le_bytes()); buf[16] = 1; buf[20] = 1; buf[21..29].copy_from_slice(&0x100000u64.to_le_bytes()); buf[29..37].copy_from_slice(&4096u64.to_le_bytes()); buf[37..45].copy_from_slice(&0u64.to_le_bytes()); buf[45..53].copy_from_slice(&4096u64.to_le_bytes()); let fei = FileExtentItem::parse(&buf).unwrap();
2322 assert_eq!(fei.generation, 100);
2323 assert_eq!(fei.compression, CompressionType::Zlib);
2324 assert_eq!(fei.extent_type, FileExtentType::Regular);
2325 match fei.body {
2326 FileExtentBody::Regular {
2327 disk_bytenr,
2328 disk_num_bytes,
2329 offset,
2330 num_bytes,
2331 } => {
2332 assert_eq!(disk_bytenr, 0x100000);
2333 assert_eq!(disk_num_bytes, 4096);
2334 assert_eq!(offset, 0);
2335 assert_eq!(num_bytes, 4096);
2336 }
2337 _ => panic!("expected regular body"),
2338 }
2339 }
2340
2341 #[test]
2342 fn file_extent_item_too_short() {
2343 assert!(FileExtentItem::parse(&[0; 20]).is_none());
2344 }
2345
2346 #[test]
2347 fn file_extent_item_regular_too_short() {
2348 let mut buf = vec![0u8; 21];
2350 buf[20] = 1; assert!(FileExtentItem::parse(&buf).is_none());
2352 }
2353
2354 #[test]
2357 fn raw_crc32c_known_value() {
2358 assert_eq!(raw_crc32c(0, &[]), 0);
2360 let raw = raw_crc32c(0, b"123456789");
2363 let standard = crc32c::crc32c(b"123456789");
2364 assert_eq!(standard, 0xE3069283);
2365 assert_ne!(raw, standard);
2366 assert_eq!(raw, raw_crc32c(0, b"123456789"));
2368 let chained = raw_crc32c(raw, b"more");
2370 assert_ne!(chained, raw);
2371 }
2372
2373 #[test]
2374 fn extent_data_ref_hash_deterministic() {
2375 let h1 = extent_data_ref_hash(5, 256, 0);
2376 let h2 = extent_data_ref_hash(5, 256, 0);
2377 assert_eq!(h1, h2);
2378 let h3 = extent_data_ref_hash(5, 256, 4096);
2380 assert_ne!(h1, h3);
2381 }
2382
2383 #[test]
2384 fn block_group_flags_data_single() {
2385 let flags = BlockGroupFlags::DATA;
2386 assert_eq!(format!("{flags}"), "DATA|single");
2387 }
2388
2389 #[test]
2390 fn block_group_flags_metadata_dup() {
2391 let flags = BlockGroupFlags::METADATA | BlockGroupFlags::DUP;
2392 assert_eq!(format!("{flags}"), "METADATA|DUP");
2393 }
2394
2395 #[test]
2396 fn block_group_flags_system_raid1() {
2397 let flags = BlockGroupFlags::SYSTEM | BlockGroupFlags::RAID1;
2398 assert_eq!(format!("{flags}"), "SYSTEM|RAID1");
2399 }
2400
2401 #[test]
2402 fn block_group_flags_data_metadata() {
2403 let flags = BlockGroupFlags::DATA | BlockGroupFlags::METADATA;
2404 assert_eq!(format!("{flags}"), "DATA|METADATA|single");
2405 }
2406
2407 #[test]
2408 fn block_group_flags_no_type_bits() {
2409 assert_eq!(format!("{}", BlockGroupFlags::empty()), "single");
2411 assert_eq!(format!("{}", BlockGroupFlags::RAID0), "RAID0");
2412 }
2413
2414 #[test]
2415 fn block_group_flags_all_profiles() {
2416 let data = BlockGroupFlags::DATA;
2417 assert_eq!(format!("{}", data | BlockGroupFlags::RAID5), "DATA|RAID5");
2418 assert_eq!(format!("{}", data | BlockGroupFlags::RAID6), "DATA|RAID6");
2419 assert_eq!(
2420 format!("{}", data | BlockGroupFlags::RAID10),
2421 "DATA|RAID10"
2422 );
2423 assert_eq!(
2424 format!("{}", data | BlockGroupFlags::RAID1C3),
2425 "DATA|RAID1C3"
2426 );
2427 assert_eq!(
2428 format!("{}", data | BlockGroupFlags::RAID1C4),
2429 "DATA|RAID1C4"
2430 );
2431 }
2432}