1use crate::util::get_uuid;
10use crate::{
11 raw,
12 tree::{DiskKey, ObjectId},
13 util::raw_crc32c,
14};
15use bytes::{Buf, BufMut};
16use std::{fmt, mem};
17use uuid::Uuid;
18
19bitflags::bitflags! {
20 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26 pub struct BlockGroupFlags: u64 {
27 const DATA = raw::BTRFS_BLOCK_GROUP_DATA as u64;
28 const SYSTEM = raw::BTRFS_BLOCK_GROUP_SYSTEM as u64;
29 const METADATA = raw::BTRFS_BLOCK_GROUP_METADATA as u64;
30 const RAID0 = raw::BTRFS_BLOCK_GROUP_RAID0 as u64;
31 const RAID1 = raw::BTRFS_BLOCK_GROUP_RAID1 as u64;
32 const DUP = raw::BTRFS_BLOCK_GROUP_DUP as u64;
33 const RAID10 = raw::BTRFS_BLOCK_GROUP_RAID10 as u64;
34 const RAID5 = raw::BTRFS_BLOCK_GROUP_RAID5 as u64;
35 const RAID6 = raw::BTRFS_BLOCK_GROUP_RAID6 as u64;
36 const RAID1C3 = raw::BTRFS_BLOCK_GROUP_RAID1C3 as u64;
37 const RAID1C4 = raw::BTRFS_BLOCK_GROUP_RAID1C4 as u64;
38
39 const SINGLE = raw::BTRFS_AVAIL_ALLOC_BIT_SINGLE;
42
43 const GLOBAL_RSV = raw::BTRFS_SPACE_INFO_GLOBAL_RSV;
45 }
46}
47
48impl BlockGroupFlags {
49 #[must_use]
51 pub fn type_name(self) -> &'static str {
52 if self.contains(Self::GLOBAL_RSV) {
53 return "GlobalReserve";
54 }
55 let ty = self & (Self::DATA | Self::SYSTEM | Self::METADATA);
56 match ty {
57 t if t == Self::DATA => "Data",
58 t if t == Self::SYSTEM => "System",
59 t if t == Self::METADATA => "Metadata",
60 t if t == Self::DATA | Self::METADATA => "Data+Metadata",
61 _ => "unknown",
62 }
63 }
64
65 #[must_use]
67 pub fn profile_name(self) -> &'static str {
68 let profile = self
69 & (Self::RAID0
70 | Self::RAID1
71 | Self::DUP
72 | Self::RAID10
73 | Self::RAID5
74 | Self::RAID6
75 | Self::RAID1C3
76 | Self::RAID1C4
77 | Self::SINGLE);
78 match profile {
79 p if p == Self::RAID0 => "RAID0",
80 p if p == Self::RAID1 => "RAID1",
81 p if p == Self::DUP => "DUP",
82 p if p == Self::RAID10 => "RAID10",
83 p if p == Self::RAID5 => "RAID5",
84 p if p == Self::RAID6 => "RAID6",
85 p if p == Self::RAID1C3 => "RAID1C3",
86 p if p == Self::RAID1C4 => "RAID1C4",
87 _ => "single",
89 }
90 }
91}
92
93bitflags::bitflags! {
94 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
96 pub struct InodeFlags: u64 {
97 const NODATASUM = raw::BTRFS_INODE_NODATASUM as u64;
98 const NODATACOW = raw::BTRFS_INODE_NODATACOW as u64;
99 const READONLY = raw::BTRFS_INODE_READONLY as u64;
100 const NOCOMPRESS = raw::BTRFS_INODE_NOCOMPRESS as u64;
101 const PREALLOC = raw::BTRFS_INODE_PREALLOC as u64;
102 const SYNC = raw::BTRFS_INODE_SYNC as u64;
103 const IMMUTABLE = raw::BTRFS_INODE_IMMUTABLE as u64;
104 const APPEND = raw::BTRFS_INODE_APPEND as u64;
105 const NODUMP = raw::BTRFS_INODE_NODUMP as u64;
106 const NOATIME = raw::BTRFS_INODE_NOATIME as u64;
107 const DIRSYNC = raw::BTRFS_INODE_DIRSYNC as u64;
108 const COMPRESS = raw::BTRFS_INODE_COMPRESS as u64;
109 const ROOT_ITEM_INIT = raw::BTRFS_INODE_ROOT_ITEM_INIT as u64;
110 const _ = !0;
112 }
113}
114
115impl fmt::Display for InodeFlags {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 const NAMES: &[(InodeFlags, &str)] = &[
118 (InodeFlags::NODATASUM, "NODATASUM"),
119 (InodeFlags::NODATACOW, "NODATACOW"),
120 (InodeFlags::READONLY, "READONLY"),
121 (InodeFlags::NOCOMPRESS, "NOCOMPRESS"),
122 (InodeFlags::PREALLOC, "PREALLOC"),
123 (InodeFlags::SYNC, "SYNC"),
124 (InodeFlags::IMMUTABLE, "IMMUTABLE"),
125 (InodeFlags::APPEND, "APPEND"),
126 (InodeFlags::NODUMP, "NODUMP"),
127 (InodeFlags::NOATIME, "NOATIME"),
128 (InodeFlags::DIRSYNC, "DIRSYNC"),
129 (InodeFlags::COMPRESS, "COMPRESS"),
130 (InodeFlags::ROOT_ITEM_INIT, "ROOT_ITEM_INIT"),
131 ];
132 let known: InodeFlags = NAMES
133 .iter()
134 .fold(InodeFlags::empty(), |a, &(flag, _)| a | flag);
135 let mut parts: Vec<String> = NAMES
136 .iter()
137 .filter(|&&(flag, _)| self.contains(flag))
138 .map(|&(_, name)| name.to_string())
139 .collect();
140 let unknown = *self & !known;
141 if !unknown.is_empty() {
142 parts.push(format!("UNKNOWN: 0x{:x}", unknown.bits()));
143 }
144 if parts.is_empty() {
145 write!(f, "none")
146 } else {
147 write!(f, "{}", parts.join("|"))
148 }
149 }
150}
151#[derive(Debug, Clone, Copy)]
153pub struct Timespec {
154 pub sec: u64,
156 pub nsec: u32,
158}
159
160impl Timespec {
161 fn parse(buf: &mut &[u8]) -> Self {
162 Self {
163 sec: buf.get_u64_le(),
164 nsec: buf.get_u32_le(),
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum CompressionType {
174 None,
176 Zlib,
178 Lzo,
180 Zstd,
182 Unknown(u8),
184}
185
186impl CompressionType {
187 #[must_use]
189 pub fn from_raw(v: u8) -> Self {
190 match v {
191 0 => Self::None,
192 1 => Self::Zlib,
193 2 => Self::Lzo,
194 3 => Self::Zstd,
195 _ => Self::Unknown(v),
196 }
197 }
198
199 #[must_use]
201 pub fn name(&self) -> &'static str {
202 match self {
203 Self::None => "none",
204 Self::Zlib => "zlib",
205 Self::Lzo => "lzo",
206 Self::Zstd => "zstd",
207 Self::Unknown(_) => "unknown",
208 }
209 }
210
211 #[must_use]
213 pub fn to_raw(self) -> u8 {
214 match self {
215 Self::None => 0,
216 Self::Zlib => 1,
217 Self::Lzo => 2,
218 Self::Zstd => 3,
219 Self::Unknown(v) => v,
220 }
221 }
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub enum FileExtentType {
227 Inline,
229 Regular,
231 Prealloc,
233 Unknown(u8),
235}
236
237impl FileExtentType {
238 #[must_use]
240 pub fn from_raw(v: u8) -> Self {
241 match u32::from(v) {
242 raw::BTRFS_FILE_EXTENT_INLINE => Self::Inline,
243 raw::BTRFS_FILE_EXTENT_REG => Self::Regular,
244 raw::BTRFS_FILE_EXTENT_PREALLOC => Self::Prealloc,
245 _ => Self::Unknown(v),
246 }
247 }
248
249 #[must_use]
251 pub fn name(&self) -> &'static str {
252 match self {
253 Self::Inline => "inline",
254 Self::Regular => "regular",
255 Self::Prealloc => "prealloc",
256 Self::Unknown(_) => "unknown",
257 }
258 }
259
260 #[must_use]
262 #[allow(clippy::cast_possible_truncation)]
263 pub fn to_raw(self) -> u8 {
264 match self {
265 Self::Inline => raw::BTRFS_FILE_EXTENT_INLINE as u8,
266 Self::Regular => raw::BTRFS_FILE_EXTENT_REG as u8,
267 Self::Prealloc => raw::BTRFS_FILE_EXTENT_PREALLOC as u8,
268 Self::Unknown(v) => v,
269 }
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum FileType {
276 Unknown,
278 RegFile,
280 Dir,
282 Chrdev,
284 Blkdev,
286 Fifo,
288 Sock,
290 Symlink,
292 Xattr,
294 Other(u8),
296}
297
298impl FileType {
299 #[must_use]
301 pub fn from_raw(v: u8) -> Self {
302 match u32::from(v) {
303 raw::BTRFS_FT_UNKNOWN => Self::Unknown,
304 raw::BTRFS_FT_REG_FILE => Self::RegFile,
305 raw::BTRFS_FT_DIR => Self::Dir,
306 raw::BTRFS_FT_CHRDEV => Self::Chrdev,
307 raw::BTRFS_FT_BLKDEV => Self::Blkdev,
308 raw::BTRFS_FT_FIFO => Self::Fifo,
309 raw::BTRFS_FT_SOCK => Self::Sock,
310 raw::BTRFS_FT_SYMLINK => Self::Symlink,
311 raw::BTRFS_FT_XATTR => Self::Xattr,
312 _ => Self::Other(v),
313 }
314 }
315
316 #[must_use]
318 pub fn name(&self) -> &'static str {
319 match self {
320 Self::Unknown | Self::Other(_) => "UNKNOWN",
321 Self::RegFile => "FILE",
322 Self::Dir => "DIR",
323 Self::Chrdev => "CHRDEV",
324 Self::Blkdev => "BLKDEV",
325 Self::Fifo => "FIFO",
326 Self::Sock => "SOCK",
327 Self::Symlink => "SYMLINK",
328 Self::Xattr => "XATTR",
329 }
330 }
331}
332
333#[derive(Debug, Clone)]
338pub struct InodeItem {
339 pub generation: u64,
341 pub transid: u64,
343 pub size: u64,
345 pub nbytes: u64,
347 pub block_group: u64,
349 pub nlink: u32,
351 pub uid: u32,
353 pub gid: u32,
355 pub mode: u32,
357 pub rdev: u64,
359 pub flags: InodeFlags,
361 pub sequence: u64,
363 pub atime: Timespec,
365 pub ctime: Timespec,
367 pub mtime: Timespec,
369 pub otime: Timespec,
371}
372
373impl InodeItem {
374 #[must_use]
377 pub fn parse(data: &[u8]) -> Option<Self> {
378 if data.len() < mem::size_of::<raw::btrfs_inode_item>() {
379 return None;
380 }
381 let mut buf = data;
382 Some(Self {
383 generation: buf.get_u64_le(),
384 transid: buf.get_u64_le(),
385 size: buf.get_u64_le(),
386 nbytes: buf.get_u64_le(),
387 block_group: buf.get_u64_le(),
388 nlink: buf.get_u32_le(),
389 uid: buf.get_u32_le(),
390 gid: buf.get_u32_le(),
391 mode: buf.get_u32_le(),
392 rdev: buf.get_u64_le(),
393 flags: InodeFlags::from_bits_truncate(buf.get_u64_le()),
394 sequence: buf.get_u64_le(),
395 atime: {
397 buf.advance(32);
398 Timespec::parse(&mut buf)
399 },
400 ctime: Timespec::parse(&mut buf),
401 mtime: Timespec::parse(&mut buf),
402 otime: Timespec::parse(&mut buf),
403 })
404 }
405}
406
407#[derive(Debug, Clone)]
413pub struct InodeRef {
414 pub index: u64,
416 pub name: Vec<u8>,
418}
419
420impl InodeRef {
421 #[must_use]
423 pub fn parse_all(data: &[u8]) -> Vec<Self> {
424 let mut result = Vec::new();
425 let mut buf = data;
426 while buf.remaining() >= 10 {
427 let index = buf.get_u64_le();
428 let name_len = buf.get_u16_le() as usize;
429 if buf.remaining() < name_len {
430 break;
431 }
432 let name = buf[..name_len].to_vec();
433 buf.advance(name_len);
434 result.push(Self { index, name });
435 }
436 result
437 }
438}
439
440#[derive(Debug, Clone)]
446pub struct InodeExtref {
447 pub parent: u64,
449 pub index: u64,
451 pub name: Vec<u8>,
453}
454
455impl InodeExtref {
456 #[must_use]
458 pub fn parse_all(data: &[u8]) -> Vec<Self> {
459 let mut result = Vec::new();
460 let mut buf = data;
461 while buf.remaining() >= 18 {
462 let parent = buf.get_u64_le();
463 let index = buf.get_u64_le();
464 let name_len = buf.get_u16_le() as usize;
465 if buf.remaining() < name_len {
466 break;
467 }
468 let name = buf[..name_len].to_vec();
469 buf.advance(name_len);
470 result.push(Self {
471 parent,
472 index,
473 name,
474 });
475 }
476 result
477 }
478}
479
480#[derive(Debug, Clone)]
486pub struct DirItem {
487 pub location: DiskKey,
489 pub transid: u64,
491 pub file_type: FileType,
493 pub name: Vec<u8>,
495 pub data: Vec<u8>,
497}
498
499impl DirItem {
500 #[must_use]
502 pub fn parse_all(data: &[u8]) -> Vec<Self> {
503 let mut result = Vec::new();
504 let dir_item_size = mem::size_of::<raw::btrfs_dir_item>();
505 let mut buf = data;
506
507 while buf.remaining() >= dir_item_size {
508 let location = DiskKey::parse(buf, 0);
509 buf.advance(17); let transid = buf.get_u64_le();
511 let data_len = buf.get_u16_le() as usize;
512 let name_len = buf.get_u16_le() as usize;
513 let file_type = FileType::from_raw(buf.get_u8());
514
515 if buf.remaining() < name_len + data_len {
516 break;
517 }
518 let name = buf[..name_len].to_vec();
519 buf.advance(name_len);
520 let item_data = buf[..data_len].to_vec();
521 buf.advance(data_len);
522 result.push(Self {
523 location,
524 transid,
525 file_type,
526 name,
527 data: item_data,
528 });
529 }
530 result
531 }
532}
533
534bitflags::bitflags! {
535 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
537 pub struct RootItemFlags: u64 {
538 const RDONLY = raw::BTRFS_ROOT_SUBVOL_RDONLY as u64;
539 const DEAD = raw::BTRFS_ROOT_SUBVOL_DEAD;
540 const _ = !0;
542 }
543}
544
545impl fmt::Display for RootItemFlags {
546 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547 if self.contains(Self::RDONLY) {
548 write!(f, "RDONLY")
549 } else {
550 write!(f, "none")
551 }
552 }
553}
554
555#[derive(Debug, Clone)]
561pub struct RootItem {
562 pub generation: u64,
564 pub root_dirid: u64,
566 pub bytenr: u64,
568 pub byte_limit: u64,
570 pub bytes_used: u64,
572 pub last_snapshot: u64,
574 pub flags: RootItemFlags,
576 pub refs: u32,
578 pub drop_progress: DiskKey,
580 pub drop_level: u8,
582 pub level: u8,
584 pub generation_v2: u64,
586 pub uuid: Uuid,
588 pub parent_uuid: Uuid,
590 pub received_uuid: Uuid,
592 pub ctransid: u64,
594 pub otransid: u64,
596 pub stransid: u64,
598 pub rtransid: u64,
600 pub ctime: Timespec,
602 pub otime: Timespec,
604 pub stime: Timespec,
606 pub rtime: Timespec,
608}
609
610impl RootItem {
611 #[must_use]
615 #[allow(clippy::too_many_lines)]
616 pub fn parse(data: &[u8]) -> Option<Self> {
617 let inode_size = mem::size_of::<raw::btrfs_inode_item>();
618 if data.len() < inode_size + 8 {
619 return None;
620 }
621
622 let mut buf = &data[inode_size..];
623 let generation = buf.get_u64_le();
624 let root_dirid = buf.get_u64_le();
625 let bytenr = buf.get_u64_le();
626 let byte_limit = buf.get_u64_le();
627 let bytes_used = buf.get_u64_le();
628 let last_snapshot = buf.get_u64_le();
629 let flags = RootItemFlags::from_bits_truncate(buf.get_u64_le());
630 let refs = buf.get_u32_le();
631
632 let dp_off = inode_size + 60;
633 let drop_progress = if dp_off + 17 <= data.len() {
634 DiskKey::parse(data, dp_off)
635 } else {
636 DiskKey::parse(&[0; 17], 0)
637 };
638 let drop_level = if dp_off + 17 < data.len() {
639 data[dp_off + 17]
640 } else {
641 0
642 };
643
644 let level_off = mem::offset_of!(raw::btrfs_root_item, level);
645 let level = if level_off < data.len() {
646 data[level_off]
647 } else {
648 0
649 };
650 let generation_v2 = if level_off + 1 + 8 <= data.len() {
651 let mut b = &data[level_off + 1..];
652 b.get_u64_le()
653 } else {
654 0
655 };
656
657 let uuid_off = mem::offset_of!(raw::btrfs_root_item, uuid);
658 let uuid = if uuid_off + 16 <= data.len() {
659 let mut b = &data[uuid_off..];
660 get_uuid(&mut b)
661 } else {
662 Uuid::nil()
663 };
664 let parent_uuid = if uuid_off + 32 <= data.len() {
665 let mut b = &data[uuid_off + 16..];
666 get_uuid(&mut b)
667 } else {
668 Uuid::nil()
669 };
670 let received_uuid = if uuid_off + 48 <= data.len() {
671 let mut b = &data[uuid_off + 32..];
672 get_uuid(&mut b)
673 } else {
674 Uuid::nil()
675 };
676
677 let ct_off = mem::offset_of!(raw::btrfs_root_item, ctransid);
678 let ctransid = if ct_off + 8 <= data.len() {
679 let mut b = &data[ct_off..];
680 b.get_u64_le()
681 } else {
682 0
683 };
684 let otransid = if ct_off + 16 <= data.len() {
685 let mut b = &data[ct_off + 8..];
686 b.get_u64_le()
687 } else {
688 0
689 };
690 let stransid = if ct_off + 24 <= data.len() {
691 let mut b = &data[ct_off + 16..];
692 b.get_u64_le()
693 } else {
694 0
695 };
696 let rtransid = if ct_off + 32 <= data.len() {
697 let mut b = &data[ct_off + 24..];
698 b.get_u64_le()
699 } else {
700 0
701 };
702
703 let ctime_off = mem::offset_of!(raw::btrfs_root_item, ctime);
704 let ts_size = mem::size_of::<raw::btrfs_timespec>();
705 let ctime = if ctime_off + ts_size <= data.len() {
706 let mut b = &data[ctime_off..];
707 Timespec::parse(&mut b)
708 } else {
709 Timespec { sec: 0, nsec: 0 }
710 };
711 let otime = if ctime_off + 2 * ts_size <= data.len() {
712 let mut b = &data[ctime_off + ts_size..];
713 Timespec::parse(&mut b)
714 } else {
715 Timespec { sec: 0, nsec: 0 }
716 };
717 let stime = if ctime_off + 3 * ts_size <= data.len() {
718 let mut b = &data[ctime_off + 2 * ts_size..];
719 Timespec::parse(&mut b)
720 } else {
721 Timespec { sec: 0, nsec: 0 }
722 };
723 let rtime = if ctime_off + 4 * ts_size <= data.len() {
724 let mut b = &data[ctime_off + 3 * ts_size..];
725 Timespec::parse(&mut b)
726 } else {
727 Timespec { sec: 0, nsec: 0 }
728 };
729
730 Some(Self {
731 generation,
732 root_dirid,
733 bytenr,
734 byte_limit,
735 bytes_used,
736 last_snapshot,
737 flags,
738 refs,
739 drop_progress,
740 drop_level,
741 level,
742 generation_v2,
743 uuid,
744 parent_uuid,
745 received_uuid,
746 ctransid,
747 otransid,
748 stransid,
749 rtransid,
750 ctime,
751 otime,
752 stime,
753 rtime,
754 })
755 }
756}
757
758#[derive(Debug, Clone)]
763pub struct RootRef {
764 pub dirid: u64,
766 pub sequence: u64,
768 pub name: Vec<u8>,
770}
771
772impl RootRef {
773 #[must_use]
775 pub fn parse(data: &[u8]) -> Option<Self> {
776 if data.len() < mem::size_of::<raw::btrfs_root_ref>() {
777 return None;
778 }
779 let mut buf = data;
780 let dirid = buf.get_u64_le();
781 let sequence = buf.get_u64_le();
782 let name_len = buf.get_u16_le() as usize;
783 let name_start = mem::size_of::<raw::btrfs_root_ref>();
784 let name = if name_start + name_len <= data.len() {
785 data[name_start..name_start + name_len].to_vec()
786 } else {
787 Vec::new()
788 };
789 Some(Self {
790 dirid,
791 sequence,
792 name,
793 })
794 }
795}
796
797#[derive(Debug, Clone)]
804pub struct FileExtentItem {
805 pub generation: u64,
807 pub ram_bytes: u64,
809 pub compression: CompressionType,
811 pub extent_type: FileExtentType,
813 pub body: FileExtentBody,
815}
816
817#[derive(Debug, Clone)]
819pub enum FileExtentBody {
820 Inline {
822 inline_size: usize,
824 },
825 Regular {
827 disk_bytenr: u64,
829 disk_num_bytes: u64,
831 offset: u64,
833 num_bytes: u64,
835 },
836}
837
838impl FileExtentItem {
839 #[must_use]
841 pub fn parse(data: &[u8]) -> Option<Self> {
842 if data.len() < 21 {
843 return None;
844 }
845 let mut buf = data;
846 let generation = buf.get_u64_le();
847 let ram_bytes = buf.get_u64_le();
848 let compression = CompressionType::from_raw(buf.get_u8());
849 buf.advance(3); let extent_type = FileExtentType::from_raw(buf.get_u8());
851
852 let body = if extent_type == FileExtentType::Inline {
853 FileExtentBody::Inline {
854 inline_size: buf.remaining(),
855 }
856 } else if buf.remaining() >= 32 {
857 FileExtentBody::Regular {
858 disk_bytenr: buf.get_u64_le(),
859 disk_num_bytes: buf.get_u64_le(),
860 offset: buf.get_u64_le(),
861 num_bytes: buf.get_u64_le(),
862 }
863 } else {
864 return None;
865 };
866
867 Some(Self {
868 generation,
869 ram_bytes,
870 compression,
871 extent_type,
872 body,
873 })
874 }
875}
876
877fn extent_data_ref_hash(root: u64, objectid: u64, offset: u64) -> u64 {
881 let high_crc = raw_crc32c(!0u32, &root.to_le_bytes());
882 let low_crc = raw_crc32c(!0u32, &objectid.to_le_bytes());
883 let low_crc = raw_crc32c(low_crc, &offset.to_le_bytes());
884 (u64::from(high_crc) << 31) ^ u64::from(low_crc)
885}
886
887#[derive(Debug, Clone)]
889pub enum InlineRef {
890 TreeBlockBackref {
893 ref_offset: u64,
895 root: u64,
897 },
898 SharedBlockBackref {
901 ref_offset: u64,
903 parent: u64,
905 },
906 ExtentDataBackref {
909 ref_offset: u64,
911 root: u64,
913 objectid: u64,
915 offset: u64,
917 count: u32,
919 },
920 SharedDataBackref {
923 ref_offset: u64,
925 parent: u64,
927 count: u32,
929 },
930 ExtentOwnerRef {
933 ref_offset: u64,
935 root: u64,
937 },
938}
939
940impl InlineRef {
941 #[must_use]
943 #[allow(clippy::cast_possible_truncation)]
944 pub fn raw_type(&self) -> u8 {
945 match self {
946 Self::TreeBlockBackref { .. } => {
947 raw::BTRFS_TREE_BLOCK_REF_KEY as u8
948 }
949 Self::SharedBlockBackref { .. } => {
950 raw::BTRFS_SHARED_BLOCK_REF_KEY as u8
951 }
952 Self::ExtentDataBackref { .. } => {
953 raw::BTRFS_EXTENT_DATA_REF_KEY as u8
954 }
955 Self::SharedDataBackref { .. } => {
956 raw::BTRFS_SHARED_DATA_REF_KEY as u8
957 }
958 Self::ExtentOwnerRef { .. } => {
959 raw::BTRFS_EXTENT_OWNER_REF_KEY as u8
960 }
961 }
962 }
963
964 #[must_use]
966 pub fn raw_offset(&self) -> u64 {
967 match self {
968 Self::TreeBlockBackref { ref_offset, .. }
969 | Self::SharedBlockBackref { ref_offset, .. }
970 | Self::ExtentDataBackref { ref_offset, .. }
971 | Self::SharedDataBackref { ref_offset, .. }
972 | Self::ExtentOwnerRef { ref_offset, .. } => *ref_offset,
973 }
974 }
975}
976
977bitflags::bitflags! {
978 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
980 pub struct ExtentFlags: u64 {
981 const DATA = raw::BTRFS_EXTENT_FLAG_DATA as u64;
982 const TREE_BLOCK = raw::BTRFS_EXTENT_FLAG_TREE_BLOCK as u64;
983 const FULL_BACKREF = raw::BTRFS_BLOCK_FLAG_FULL_BACKREF as u64;
984 const _ = !0;
986 }
987}
988
989impl fmt::Display for ExtentFlags {
990 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991 let mut parts = Vec::new();
992 if self.contains(Self::DATA) {
993 parts.push("DATA");
994 }
995 if self.contains(Self::TREE_BLOCK) {
996 parts.push("TREE_BLOCK");
997 }
998 if self.contains(Self::FULL_BACKREF) {
999 parts.push("FULL_BACKREF");
1000 }
1001 write!(f, "{}", parts.join("|"))
1002 }
1003}
1004
1005#[derive(Debug, Clone)]
1011pub struct ExtentItem {
1012 pub refs: u64,
1014 pub generation: u64,
1016 pub flags: ExtentFlags,
1018 pub tree_block_key: Option<DiskKey>,
1020 pub tree_block_level: Option<u8>,
1022 pub skinny_level: Option<u64>,
1024 pub inline_refs: Vec<InlineRef>,
1026}
1027
1028impl ExtentItem {
1029 #[must_use]
1031 pub fn is_data(&self) -> bool {
1032 self.flags.contains(ExtentFlags::DATA)
1033 }
1034
1035 #[must_use]
1037 pub fn is_tree_block(&self) -> bool {
1038 self.flags.contains(ExtentFlags::TREE_BLOCK)
1039 }
1040
1041 #[must_use]
1044 pub fn parse(data: &[u8], key: &DiskKey) -> Option<Self> {
1045 use crate::tree::KeyType;
1046
1047 if data.len() < mem::size_of::<raw::btrfs_extent_item>() {
1048 return None;
1049 }
1050 let mut buf = data;
1051 let refs = buf.get_u64_le();
1052 let generation = buf.get_u64_le();
1053 let flags = ExtentFlags::from_bits_truncate(buf.get_u64_le());
1054
1055 let is_tree_block = flags.contains(ExtentFlags::TREE_BLOCK);
1056
1057 let mut tree_block_key = None;
1058 let mut tree_block_level = None;
1059 if is_tree_block
1060 && key.key_type == KeyType::ExtentItem
1061 && buf.remaining() > 17
1062 {
1063 tree_block_key = Some(DiskKey::parse(buf, 0));
1064 buf.advance(17); tree_block_level = Some(buf.get_u8());
1066 }
1067
1068 let skinny_level =
1069 if key.key_type == KeyType::MetadataItem && is_tree_block {
1070 Some(key.offset)
1071 } else {
1072 None
1073 };
1074
1075 let mut inline_refs = Vec::new();
1076 while buf.remaining() > 0 {
1077 let ref_type = buf.get_u8();
1078 let ref_offset = if buf.remaining() >= 8 {
1079 buf.get_u64_le()
1080 } else {
1081 0
1082 };
1083
1084 match u32::from(ref_type) {
1085 raw::BTRFS_TREE_BLOCK_REF_KEY => {
1086 inline_refs.push(InlineRef::TreeBlockBackref {
1087 ref_offset,
1088 root: ref_offset,
1089 });
1090 }
1091 raw::BTRFS_SHARED_BLOCK_REF_KEY => {
1092 inline_refs.push(InlineRef::SharedBlockBackref {
1093 ref_offset,
1094 parent: ref_offset,
1095 });
1096 }
1097 raw::BTRFS_EXTENT_DATA_REF_KEY => {
1098 let root = ref_offset; if buf.remaining() >= 20 {
1104 let oid = buf.get_u64_le();
1105 let off = buf.get_u64_le();
1106 let count = buf.get_u32_le();
1107 let hash = extent_data_ref_hash(root, oid, off);
1110 inline_refs.push(InlineRef::ExtentDataBackref {
1111 ref_offset: hash,
1112 root,
1113 objectid: oid,
1114 offset: off,
1115 count,
1116 });
1117 } else {
1118 break;
1119 }
1120 }
1121 raw::BTRFS_SHARED_DATA_REF_KEY => {
1122 if buf.remaining() >= 4 {
1123 let count = buf.get_u32_le();
1124 inline_refs.push(InlineRef::SharedDataBackref {
1125 ref_offset,
1126 parent: ref_offset,
1127 count,
1128 });
1129 } else {
1130 break;
1131 }
1132 }
1133 raw::BTRFS_EXTENT_OWNER_REF_KEY => {
1134 inline_refs.push(InlineRef::ExtentOwnerRef {
1135 ref_offset,
1136 root: ref_offset,
1137 });
1138 }
1139 _ => break,
1140 }
1141 }
1142
1143 Some(Self {
1144 refs,
1145 generation,
1146 flags,
1147 tree_block_key,
1148 tree_block_level,
1149 skinny_level,
1150 inline_refs,
1151 })
1152 }
1153}
1154
1155#[derive(Debug, Clone)]
1160pub struct ExtentDataRef {
1161 pub root: u64,
1163 pub objectid: u64,
1165 pub offset: u64,
1167 pub count: u32,
1169}
1170
1171impl ExtentDataRef {
1172 #[must_use]
1174 pub fn parse(data: &[u8]) -> Option<Self> {
1175 if data.len() < mem::size_of::<raw::btrfs_extent_data_ref>() {
1176 return None;
1177 }
1178 let mut buf = data;
1179 Some(Self {
1180 root: buf.get_u64_le(),
1181 objectid: buf.get_u64_le(),
1182 offset: buf.get_u64_le(),
1183 count: buf.get_u32_le(),
1184 })
1185 }
1186}
1187
1188#[derive(Debug, Clone)]
1192pub struct SharedDataRef {
1193 pub count: u32,
1195}
1196
1197impl SharedDataRef {
1198 #[must_use]
1200 pub fn parse(data: &[u8]) -> Option<Self> {
1201 if data.len() < 4 {
1202 return None;
1203 }
1204 let mut buf = data;
1205 Some(Self {
1206 count: buf.get_u32_le(),
1207 })
1208 }
1209}
1210
1211#[derive(Debug, Clone)]
1215pub struct BlockGroupItem {
1216 pub used: u64,
1218 pub chunk_objectid: u64,
1220 pub flags: BlockGroupFlags,
1222}
1223
1224impl BlockGroupItem {
1225 #[must_use]
1227 pub fn parse(data: &[u8]) -> Option<Self> {
1228 if data.len() < mem::size_of::<raw::btrfs_block_group_item>() {
1229 return None;
1230 }
1231 let mut buf = data;
1232 Some(Self {
1233 used: buf.get_u64_le(),
1234 chunk_objectid: buf.get_u64_le(),
1235 flags: BlockGroupFlags::from_bits_truncate(buf.get_u64_le()),
1236 })
1237 }
1238}
1239
1240#[derive(Debug, Clone)]
1245pub struct ChunkItem {
1246 pub length: u64,
1248 pub owner: u64,
1250 pub stripe_len: u64,
1252 pub chunk_type: BlockGroupFlags,
1254 pub io_align: u32,
1256 pub io_width: u32,
1258 pub sector_size: u32,
1260 pub num_stripes: u16,
1262 pub sub_stripes: u16,
1264 pub stripes: Vec<ChunkStripe>,
1266}
1267
1268#[derive(Debug, Clone)]
1270pub struct ChunkStripe {
1271 pub devid: u64,
1273 pub offset: u64,
1275 pub dev_uuid: Uuid,
1277}
1278
1279impl ChunkItem {
1280 #[must_use]
1282 pub fn parse(data: &[u8]) -> Option<Self> {
1283 let chunk_base_size = mem::offset_of!(raw::btrfs_chunk, stripe);
1284 if data.len() < chunk_base_size {
1285 return None;
1286 }
1287 let mut buf = data;
1288 let length = buf.get_u64_le();
1289 let owner = buf.get_u64_le();
1290 let stripe_len = buf.get_u64_le();
1291 let chunk_type = BlockGroupFlags::from_bits_truncate(buf.get_u64_le());
1292 let io_align = buf.get_u32_le();
1293 let io_width = buf.get_u32_le();
1294 let sector_size = buf.get_u32_le();
1295 let num_stripes = buf.get_u16_le();
1296 let sub_stripes = buf.get_u16_le();
1297 let stripe_size = mem::size_of::<raw::btrfs_stripe>();
1298 let mut stripes = Vec::with_capacity(num_stripes as usize);
1299 let mut sbuf = &data[chunk_base_size..];
1300 for i in 0..num_stripes as usize {
1301 let s_off = chunk_base_size + i * stripe_size;
1302 if s_off + stripe_size > data.len() {
1303 break;
1304 }
1305 let devid = sbuf.get_u64_le();
1306 let offset = sbuf.get_u64_le();
1307 let dev_uuid = get_uuid(&mut sbuf);
1308 stripes.push(ChunkStripe {
1309 devid,
1310 offset,
1311 dev_uuid,
1312 });
1313 }
1314 Some(Self {
1315 length,
1316 owner,
1317 stripe_len,
1318 chunk_type,
1319 io_align,
1320 io_width,
1321 sector_size,
1322 num_stripes,
1323 sub_stripes,
1324 stripes,
1325 })
1326 }
1327}
1328
1329#[derive(Debug, Clone)]
1334pub struct DeviceItem {
1335 pub devid: u64,
1337 pub total_bytes: u64,
1339 pub bytes_used: u64,
1341 pub io_align: u32,
1343 pub io_width: u32,
1345 pub sector_size: u32,
1347 pub dev_type: u64,
1349 pub generation: u64,
1351 pub start_offset: u64,
1353 pub dev_group: u32,
1355 pub seek_speed: u8,
1357 pub bandwidth: u8,
1359 pub uuid: Uuid,
1361 pub fsid: Uuid,
1363}
1364
1365impl DeviceItem {
1366 pub fn write_bytes(&self, buf: &mut impl BufMut) {
1368 buf.put_u64_le(self.devid);
1369 buf.put_u64_le(self.total_bytes);
1370 buf.put_u64_le(self.bytes_used);
1371 buf.put_u32_le(self.io_align);
1372 buf.put_u32_le(self.io_width);
1373 buf.put_u32_le(self.sector_size);
1374 buf.put_u64_le(self.dev_type);
1375 buf.put_u64_le(self.generation);
1376 buf.put_u64_le(self.start_offset);
1377 buf.put_u32_le(self.dev_group);
1378 buf.put_u8(self.seek_speed);
1379 buf.put_u8(self.bandwidth);
1380 buf.put_slice(self.uuid.as_bytes());
1381 buf.put_slice(self.fsid.as_bytes());
1382 }
1383
1384 #[must_use]
1386 pub fn parse(data: &[u8]) -> Option<Self> {
1387 if data.len() < mem::size_of::<raw::btrfs_dev_item>() {
1388 return None;
1389 }
1390 let mut buf = data;
1391 let devid = buf.get_u64_le();
1392 let total_bytes = buf.get_u64_le();
1393 let bytes_used = buf.get_u64_le();
1394 let io_align = buf.get_u32_le();
1395 let io_width = buf.get_u32_le();
1396 let sector_size = buf.get_u32_le();
1397 let dev_type = buf.get_u64_le();
1398 let generation = buf.get_u64_le();
1399 let start_offset = buf.get_u64_le();
1400 let dev_group = buf.get_u32_le();
1401 let seek_speed = buf.get_u8();
1402 let bandwidth = buf.get_u8();
1403 let uuid = get_uuid(&mut buf);
1404 let fsid = get_uuid(&mut buf);
1405 Some(Self {
1406 devid,
1407 total_bytes,
1408 bytes_used,
1409 io_align,
1410 io_width,
1411 sector_size,
1412 dev_type,
1413 generation,
1414 start_offset,
1415 dev_group,
1416 seek_speed,
1417 bandwidth,
1418 uuid,
1419 fsid,
1420 })
1421 }
1422}
1423
1424#[derive(Debug, Clone)]
1429pub struct DeviceExtent {
1430 pub chunk_tree: u64,
1432 pub chunk_objectid: u64,
1434 pub chunk_offset: u64,
1436 pub length: u64,
1438 pub chunk_tree_uuid: Uuid,
1440}
1441
1442impl DeviceExtent {
1443 #[must_use]
1445 pub fn parse(data: &[u8]) -> Option<Self> {
1446 if data.len() < mem::size_of::<raw::btrfs_dev_extent>() {
1447 return None;
1448 }
1449 let mut buf = data;
1450 let chunk_tree = buf.get_u64_le();
1451 let chunk_objectid = buf.get_u64_le();
1452 let chunk_offset = buf.get_u64_le();
1453 let length = buf.get_u64_le();
1454 let chunk_tree_uuid = get_uuid(&mut buf);
1455 Some(Self {
1456 chunk_tree,
1457 chunk_objectid,
1458 chunk_offset,
1459 length,
1460 chunk_tree_uuid,
1461 })
1462 }
1463}
1464
1465bitflags::bitflags! {
1466 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1468 pub struct FreeSpaceInfoFlags: u32 {
1469 const USING_BITMAPS = raw::BTRFS_FREE_SPACE_USING_BITMAPS;
1470 const _ = !0;
1472 }
1473}
1474
1475impl fmt::Display for FreeSpaceInfoFlags {
1476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1478 write!(f, "{}", self.bits())
1479 }
1480}
1481
1482#[derive(Debug, Clone)]
1486pub struct FreeSpaceInfo {
1487 pub extent_count: u32,
1489 pub flags: FreeSpaceInfoFlags,
1491}
1492
1493impl FreeSpaceInfo {
1494 #[must_use]
1496 pub fn parse(data: &[u8]) -> Option<Self> {
1497 if data.len() < 8 {
1498 return None;
1499 }
1500 let mut buf = data;
1501 Some(Self {
1502 extent_count: buf.get_u32_le(),
1503 flags: FreeSpaceInfoFlags::from_bits_truncate(buf.get_u32_le()),
1504 })
1505 }
1506}
1507
1508#[derive(Debug, Clone)]
1512pub struct QgroupStatus {
1513 pub version: u64,
1515 pub generation: u64,
1517 pub flags: u64,
1519 pub scan: u64,
1521 pub enable_gen: Option<u64>,
1523}
1524
1525impl QgroupStatus {
1526 #[must_use]
1528 pub fn parse(data: &[u8]) -> Option<Self> {
1529 if data.len() < 32 {
1530 return None;
1531 }
1532 let mut buf = data;
1533 let version = buf.get_u64_le();
1534 let generation = buf.get_u64_le();
1535 let flags = buf.get_u64_le();
1536 let scan = buf.get_u64_le();
1537 let enable_gen = if buf.remaining() >= 8 {
1538 Some(buf.get_u64_le())
1539 } else {
1540 None
1541 };
1542 Some(Self {
1543 version,
1544 generation,
1545 flags,
1546 scan,
1547 enable_gen,
1548 })
1549 }
1550}
1551
1552#[derive(Debug, Clone)]
1557pub struct QgroupInfo {
1558 pub generation: u64,
1560 pub referenced: u64,
1562 pub referenced_compressed: u64,
1564 pub exclusive: u64,
1566 pub exclusive_compressed: u64,
1568}
1569
1570impl QgroupInfo {
1571 #[must_use]
1573 pub fn parse(data: &[u8]) -> Option<Self> {
1574 if data.len() < mem::size_of::<raw::btrfs_qgroup_info_item>() {
1575 return None;
1576 }
1577 let mut buf = data;
1578 Some(Self {
1579 generation: buf.get_u64_le(),
1580 referenced: buf.get_u64_le(),
1581 referenced_compressed: buf.get_u64_le(),
1582 exclusive: buf.get_u64_le(),
1583 exclusive_compressed: buf.get_u64_le(),
1584 })
1585 }
1586}
1587
1588#[derive(Debug, Clone)]
1593pub struct QgroupLimit {
1594 pub flags: u64,
1596 pub max_referenced: u64,
1598 pub max_exclusive: u64,
1600 pub rsv_referenced: u64,
1602 pub rsv_exclusive: u64,
1604}
1605
1606impl QgroupLimit {
1607 #[must_use]
1609 pub fn parse(data: &[u8]) -> Option<Self> {
1610 if data.len() < mem::size_of::<raw::btrfs_qgroup_limit_item>() {
1611 return None;
1612 }
1613 let mut buf = data;
1614 Some(Self {
1615 flags: buf.get_u64_le(),
1616 max_referenced: buf.get_u64_le(),
1617 max_exclusive: buf.get_u64_le(),
1618 rsv_referenced: buf.get_u64_le(),
1619 rsv_exclusive: buf.get_u64_le(),
1620 })
1621 }
1622}
1623
1624#[derive(Debug, Clone)]
1630pub struct DeviceStats {
1631 pub values: Vec<(String, u64)>,
1633}
1634
1635impl DeviceStats {
1636 #[must_use]
1639 pub fn parse(data: &[u8]) -> Self {
1640 let stat_names = [
1641 "write_errs",
1642 "read_errs",
1643 "flush_errs",
1644 "corruption_errs",
1645 "generation",
1646 ];
1647 let mut buf = data;
1648 let mut values = Vec::new();
1649 for name in &stat_names {
1650 if buf.remaining() >= 8 {
1651 values.push((name.to_string(), buf.get_u64_le()));
1652 }
1653 }
1654 DeviceStats { values }
1655 }
1656}
1657
1658#[derive(Debug, Clone)]
1662pub struct UuidItem {
1663 pub subvol_ids: Vec<u64>,
1665}
1666
1667impl UuidItem {
1668 #[must_use]
1670 pub fn parse(data: &[u8]) -> Self {
1671 let mut buf = data;
1672 let mut subvol_ids = Vec::new();
1673 while buf.remaining() >= 8 {
1674 subvol_ids.push(buf.get_u64_le());
1675 }
1676 Self { subvol_ids }
1677 }
1678}
1679
1680pub enum ItemPayload {
1686 InodeItem(InodeItem),
1688 InodeRef(Vec<InodeRef>),
1690 InodeExtref(Vec<InodeExtref>),
1692 DirItem(Vec<DirItem>),
1694 DirLogItem {
1696 end: u64,
1698 },
1699 OrphanItem,
1701 RootItem(RootItem),
1703 RootRef(RootRef),
1705 FileExtentItem(FileExtentItem),
1707 ExtentCsum {
1709 data: Vec<u8>,
1711 },
1712 ExtentItem(ExtentItem),
1714 TreeBlockRef,
1716 SharedBlockRef,
1718 ExtentDataRef(ExtentDataRef),
1720 SharedDataRef(SharedDataRef),
1722 ExtentOwnerRef {
1724 root: u64,
1726 },
1727 BlockGroupItem(BlockGroupItem),
1729 FreeSpaceInfo(FreeSpaceInfo),
1731 FreeSpaceExtent,
1733 FreeSpaceBitmap,
1735 ChunkItem(ChunkItem),
1737 DeviceItem(DeviceItem),
1739 DeviceExtent(DeviceExtent),
1741 QgroupStatus(QgroupStatus),
1743 QgroupInfo(QgroupInfo),
1745 QgroupLimit(QgroupLimit),
1747 QgroupRelation,
1749 DeviceStats(DeviceStats),
1751 BalanceItem {
1753 flags: u64,
1755 },
1756 DeviceReplace(DeviceReplaceItem),
1758 UuidItem(UuidItem),
1760 StringItem(Vec<u8>),
1762 RaidStripe(RaidStripeItem),
1764 Unknown(Vec<u8>),
1766}
1767
1768#[derive(Debug, Clone)]
1772pub struct DeviceReplaceItem {
1773 pub src_devid: u64,
1775 pub cursor_left: u64,
1777 pub cursor_right: u64,
1779 pub replace_mode: u64,
1781 pub replace_state: u64,
1783 pub time_started: u64,
1785 pub time_stopped: u64,
1787 pub num_write_errors: u64,
1789 pub num_uncorrectable_read_errors: u64,
1791}
1792
1793impl DeviceReplaceItem {
1794 #[must_use]
1796 pub fn parse(data: &[u8]) -> Option<Self> {
1797 if data.len() < 80 {
1798 return None;
1799 }
1800 let mut buf = data;
1801 Some(Self {
1802 src_devid: buf.get_u64_le(),
1803 cursor_left: buf.get_u64_le(),
1804 cursor_right: buf.get_u64_le(),
1805 replace_mode: buf.get_u64_le(),
1806 replace_state: buf.get_u64_le(),
1807 time_started: buf.get_u64_le(),
1808 time_stopped: buf.get_u64_le(),
1809 num_write_errors: buf.get_u64_le(),
1810 num_uncorrectable_read_errors: buf.get_u64_le(),
1811 })
1812 }
1813}
1814
1815#[derive(Debug, Clone)]
1819pub struct RaidStripeItem {
1820 pub encoding: u64,
1822 pub stripes: Vec<RaidStripeEntry>,
1824}
1825
1826#[derive(Debug, Clone)]
1828pub struct RaidStripeEntry {
1829 pub devid: u64,
1831 pub physical: u64,
1833}
1834
1835impl RaidStripeItem {
1836 #[must_use]
1838 pub fn parse(data: &[u8]) -> Option<Self> {
1839 if data.len() < 8 {
1840 return None;
1841 }
1842 let mut buf = data;
1843 let encoding = buf.get_u64_le();
1844 let mut stripes = Vec::new();
1845 while buf.remaining() >= 16 {
1846 stripes.push(RaidStripeEntry {
1847 devid: buf.get_u64_le(),
1848 physical: buf.get_u64_le(),
1849 });
1850 }
1851 Some(Self { encoding, stripes })
1852 }
1853}
1854
1855#[must_use]
1857#[allow(clippy::too_many_lines)]
1858pub fn parse_item_payload(key: &DiskKey, data: &[u8]) -> ItemPayload {
1859 use crate::tree::KeyType;
1860
1861 match key.key_type {
1862 KeyType::InodeItem => match InodeItem::parse(data) {
1863 Some(v) => ItemPayload::InodeItem(v),
1864 None => ItemPayload::Unknown(data.to_vec()),
1865 },
1866 KeyType::InodeRef => ItemPayload::InodeRef(InodeRef::parse_all(data)),
1867 KeyType::InodeExtref => {
1868 ItemPayload::InodeExtref(InodeExtref::parse_all(data))
1869 }
1870 KeyType::DirItem | KeyType::DirIndex | KeyType::XattrItem => {
1871 ItemPayload::DirItem(DirItem::parse_all(data))
1872 }
1873 KeyType::DirLogItem | KeyType::DirLogIndex => {
1874 let end = if data.len() >= 8 {
1875 let mut buf = data;
1876 buf.get_u64_le()
1877 } else {
1878 0
1879 };
1880 ItemPayload::DirLogItem { end }
1881 }
1882 KeyType::OrphanItem => ItemPayload::OrphanItem,
1883 KeyType::RootItem => match RootItem::parse(data) {
1884 Some(v) => ItemPayload::RootItem(v),
1885 None => ItemPayload::Unknown(data.to_vec()),
1886 },
1887 KeyType::RootRef | KeyType::RootBackref => match RootRef::parse(data) {
1888 Some(v) => ItemPayload::RootRef(v),
1889 None => ItemPayload::Unknown(data.to_vec()),
1890 },
1891 KeyType::ExtentData => match FileExtentItem::parse(data) {
1892 Some(v) => ItemPayload::FileExtentItem(v),
1893 None => ItemPayload::Unknown(data.to_vec()),
1894 },
1895 KeyType::ExtentCsum => ItemPayload::ExtentCsum {
1896 data: data.to_vec(),
1897 },
1898 KeyType::ExtentItem | KeyType::MetadataItem => {
1899 match ExtentItem::parse(data, key) {
1900 Some(v) => ItemPayload::ExtentItem(v),
1901 None => ItemPayload::Unknown(data.to_vec()),
1902 }
1903 }
1904 KeyType::TreeBlockRef => ItemPayload::TreeBlockRef,
1905 KeyType::SharedBlockRef => ItemPayload::SharedBlockRef,
1906 KeyType::ExtentDataRef => match ExtentDataRef::parse(data) {
1907 Some(v) => ItemPayload::ExtentDataRef(v),
1908 None => ItemPayload::Unknown(data.to_vec()),
1909 },
1910 KeyType::SharedDataRef => match SharedDataRef::parse(data) {
1911 Some(v) => ItemPayload::SharedDataRef(v),
1912 None => ItemPayload::Unknown(data.to_vec()),
1913 },
1914 KeyType::ExtentOwnerRef => {
1915 if data.len() >= 8 {
1916 let mut buf = data;
1917 ItemPayload::ExtentOwnerRef {
1918 root: buf.get_u64_le(),
1919 }
1920 } else {
1921 ItemPayload::Unknown(data.to_vec())
1922 }
1923 }
1924 KeyType::BlockGroupItem => match BlockGroupItem::parse(data) {
1925 Some(v) => ItemPayload::BlockGroupItem(v),
1926 None => ItemPayload::Unknown(data.to_vec()),
1927 },
1928 KeyType::FreeSpaceInfo => match FreeSpaceInfo::parse(data) {
1929 Some(v) => ItemPayload::FreeSpaceInfo(v),
1930 None => ItemPayload::Unknown(data.to_vec()),
1931 },
1932 KeyType::FreeSpaceExtent => ItemPayload::FreeSpaceExtent,
1933 KeyType::FreeSpaceBitmap => ItemPayload::FreeSpaceBitmap,
1934 KeyType::ChunkItem => match ChunkItem::parse(data) {
1935 Some(v) => ItemPayload::ChunkItem(v),
1936 None => ItemPayload::Unknown(data.to_vec()),
1937 },
1938 KeyType::DeviceItem => match DeviceItem::parse(data) {
1939 Some(v) => ItemPayload::DeviceItem(v),
1940 None => ItemPayload::Unknown(data.to_vec()),
1941 },
1942 KeyType::DeviceExtent => match DeviceExtent::parse(data) {
1943 Some(v) => ItemPayload::DeviceExtent(v),
1944 None => ItemPayload::Unknown(data.to_vec()),
1945 },
1946 KeyType::QgroupStatus => match QgroupStatus::parse(data) {
1947 Some(v) => ItemPayload::QgroupStatus(v),
1948 None => ItemPayload::Unknown(data.to_vec()),
1949 },
1950 KeyType::QgroupInfo => match QgroupInfo::parse(data) {
1951 Some(v) => ItemPayload::QgroupInfo(v),
1952 None => ItemPayload::Unknown(data.to_vec()),
1953 },
1954 KeyType::QgroupLimit => match QgroupLimit::parse(data) {
1955 Some(v) => ItemPayload::QgroupLimit(v),
1956 None => ItemPayload::Unknown(data.to_vec()),
1957 },
1958 KeyType::QgroupRelation => ItemPayload::QgroupRelation,
1959 KeyType::PersistentItem => {
1960 if key.objectid == u64::from(raw::BTRFS_DEV_STATS_OBJECTID) {
1961 ItemPayload::DeviceStats(DeviceStats::parse(data))
1962 } else {
1963 ItemPayload::Unknown(data.to_vec())
1964 }
1965 }
1966 KeyType::TemporaryItem => {
1967 if ObjectId::from_raw(key.objectid) == ObjectId::Balance
1968 && data.len() >= 8
1969 {
1970 ItemPayload::BalanceItem {
1971 flags: {
1972 let mut buf = data;
1973 buf.get_u64_le()
1974 },
1975 }
1976 } else {
1977 ItemPayload::Unknown(data.to_vec())
1978 }
1979 }
1980 KeyType::DeviceReplace => match DeviceReplaceItem::parse(data) {
1981 Some(v) => ItemPayload::DeviceReplace(v),
1982 None => ItemPayload::Unknown(data.to_vec()),
1983 },
1984 KeyType::UuidKeySubvol | KeyType::UuidKeyReceivedSubvol => {
1985 ItemPayload::UuidItem(UuidItem::parse(data))
1986 }
1987 KeyType::StringItem => ItemPayload::StringItem(data.to_vec()),
1988 KeyType::RaidStripe => match RaidStripeItem::parse(data) {
1989 Some(v) => ItemPayload::RaidStripe(v),
1990 None => ItemPayload::Unknown(data.to_vec()),
1991 },
1992 _ => ItemPayload::Unknown(data.to_vec()),
1993 }
1994}
1995
1996#[cfg(test)]
1997mod tests {
1998 use super::*;
1999
2000 #[test]
2003 fn compression_type_round_trip() {
2004 for v in 0..=3 {
2005 let ct = CompressionType::from_raw(v);
2006 assert_eq!(ct.to_raw(), v);
2007 }
2008 assert_eq!(CompressionType::from_raw(0), CompressionType::None);
2009 assert_eq!(CompressionType::from_raw(1), CompressionType::Zlib);
2010 assert_eq!(CompressionType::from_raw(2), CompressionType::Lzo);
2011 assert_eq!(CompressionType::from_raw(3), CompressionType::Zstd);
2012 assert_eq!(CompressionType::from_raw(99), CompressionType::Unknown(99));
2013 assert_eq!(CompressionType::Unknown(99).to_raw(), 99);
2014 }
2015
2016 #[test]
2017 fn compression_type_names() {
2018 assert_eq!(CompressionType::None.name(), "none");
2019 assert_eq!(CompressionType::Zlib.name(), "zlib");
2020 assert_eq!(CompressionType::Lzo.name(), "lzo");
2021 assert_eq!(CompressionType::Zstd.name(), "zstd");
2022 assert_eq!(CompressionType::Unknown(42).name(), "unknown");
2023 }
2024
2025 #[test]
2026 fn file_extent_type_round_trip() {
2027 assert_eq!(FileExtentType::from_raw(0), FileExtentType::Inline);
2028 assert_eq!(FileExtentType::from_raw(1), FileExtentType::Regular);
2029 assert_eq!(FileExtentType::from_raw(2), FileExtentType::Prealloc);
2030 assert_eq!(FileExtentType::from_raw(77), FileExtentType::Unknown(77));
2031 for v in 0..=2 {
2032 let ft = FileExtentType::from_raw(v);
2033 assert_eq!(ft.to_raw(), v);
2034 }
2035 assert_eq!(FileExtentType::Unknown(77).to_raw(), 77);
2036 }
2037
2038 #[test]
2039 fn file_extent_type_names() {
2040 assert_eq!(FileExtentType::Inline.name(), "inline");
2041 assert_eq!(FileExtentType::Regular.name(), "regular");
2042 assert_eq!(FileExtentType::Prealloc.name(), "prealloc");
2043 assert_eq!(FileExtentType::Unknown(5).name(), "unknown");
2044 }
2045
2046 #[test]
2047 fn file_type_from_raw_all_variants() {
2048 assert_eq!(FileType::from_raw(0), FileType::Unknown);
2049 assert_eq!(FileType::from_raw(1), FileType::RegFile);
2050 assert_eq!(FileType::from_raw(2), FileType::Dir);
2051 assert_eq!(FileType::from_raw(3), FileType::Chrdev);
2052 assert_eq!(FileType::from_raw(4), FileType::Blkdev);
2053 assert_eq!(FileType::from_raw(5), FileType::Fifo);
2054 assert_eq!(FileType::from_raw(6), FileType::Sock);
2055 assert_eq!(FileType::from_raw(7), FileType::Symlink);
2056 assert_eq!(FileType::from_raw(8), FileType::Xattr);
2057 assert_eq!(FileType::from_raw(99), FileType::Other(99));
2058 }
2059
2060 #[test]
2061 fn file_type_names() {
2062 assert_eq!(FileType::Unknown.name(), "UNKNOWN");
2063 assert_eq!(FileType::RegFile.name(), "FILE");
2064 assert_eq!(FileType::Dir.name(), "DIR");
2065 assert_eq!(FileType::Chrdev.name(), "CHRDEV");
2066 assert_eq!(FileType::Blkdev.name(), "BLKDEV");
2067 assert_eq!(FileType::Fifo.name(), "FIFO");
2068 assert_eq!(FileType::Sock.name(), "SOCK");
2069 assert_eq!(FileType::Symlink.name(), "SYMLINK");
2070 assert_eq!(FileType::Xattr.name(), "XATTR");
2071 assert_eq!(FileType::Other(200).name(), "UNKNOWN");
2072 }
2073
2074 #[test]
2077 fn block_group_item_parse() {
2078 let mut buf = Vec::new();
2079 buf.extend_from_slice(&1000u64.to_le_bytes()); buf.extend_from_slice(&256u64.to_le_bytes()); buf.extend_from_slice(
2082 &(raw::BTRFS_BLOCK_GROUP_DATA as u64).to_le_bytes(),
2083 );
2084 let item = BlockGroupItem::parse(&buf).unwrap();
2085 assert_eq!(item.used, 1000);
2086 assert_eq!(item.chunk_objectid, 256);
2087 assert_eq!(item.flags, BlockGroupFlags::DATA);
2088 }
2089
2090 #[test]
2091 fn block_group_item_too_short() {
2092 assert!(BlockGroupItem::parse(&[0; 23]).is_none());
2093 }
2094
2095 #[test]
2096 fn free_space_info_parse() {
2097 let mut buf = Vec::new();
2098 buf.extend_from_slice(&42u32.to_le_bytes());
2099 buf.extend_from_slice(&7u32.to_le_bytes());
2100 let info = FreeSpaceInfo::parse(&buf).unwrap();
2101 assert_eq!(info.extent_count, 42);
2102 assert_eq!(info.flags, FreeSpaceInfoFlags::from_bits_truncate(7));
2103 }
2104
2105 #[test]
2106 fn free_space_info_too_short() {
2107 assert!(FreeSpaceInfo::parse(&[0; 7]).is_none());
2108 }
2109
2110 #[test]
2111 fn dev_extent_parse() {
2112 let size = mem::size_of::<raw::btrfs_dev_extent>();
2113 let mut buf = vec![0u8; size];
2114 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]);
2120 let de = DeviceExtent::parse(&buf).unwrap();
2121 assert_eq!(de.chunk_tree, 3);
2122 assert_eq!(de.chunk_objectid, 256);
2123 assert_eq!(de.chunk_offset, 0x10000);
2124 assert_eq!(de.length, 0x40000);
2125 assert_eq!(de.chunk_tree_uuid.as_bytes(), &[0xAB; 16]);
2126 }
2127
2128 #[test]
2129 fn dev_extent_too_short() {
2130 let size = mem::size_of::<raw::btrfs_dev_extent>();
2131 assert!(DeviceExtent::parse(&vec![0u8; size - 1]).is_none());
2132 }
2133
2134 #[test]
2135 fn extent_data_ref_parse() {
2136 let size = mem::size_of::<raw::btrfs_extent_data_ref>();
2137 let mut buf = vec![0u8; size];
2138 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();
2143 assert_eq!(edr.root, 5);
2144 assert_eq!(edr.objectid, 256);
2145 assert_eq!(edr.offset, 0);
2146 assert_eq!(edr.count, 1);
2147 }
2148
2149 #[test]
2150 fn extent_data_ref_too_short() {
2151 assert!(ExtentDataRef::parse(&[0; 27]).is_none());
2152 }
2153
2154 #[test]
2155 fn shared_data_ref_parse() {
2156 let buf = 17u32.to_le_bytes();
2157 let sdr = SharedDataRef::parse(&buf).unwrap();
2158 assert_eq!(sdr.count, 17);
2159 }
2160
2161 #[test]
2162 fn shared_data_ref_too_short() {
2163 assert!(SharedDataRef::parse(&[0; 3]).is_none());
2164 }
2165
2166 #[test]
2167 fn qgroup_info_parse() {
2168 let mut buf = Vec::new();
2169 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();
2175 assert_eq!(qi.generation, 100);
2176 assert_eq!(qi.referenced, 4096);
2177 assert_eq!(qi.referenced_compressed, 4096);
2178 assert_eq!(qi.exclusive, 2048);
2179 assert_eq!(qi.exclusive_compressed, 2048);
2180 }
2181
2182 #[test]
2183 fn qgroup_info_too_short() {
2184 assert!(QgroupInfo::parse(&[0; 39]).is_none());
2185 }
2186
2187 #[test]
2188 fn qgroup_limit_parse() {
2189 let mut buf = Vec::new();
2190 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();
2196 assert_eq!(ql.flags, 3);
2197 assert_eq!(ql.max_referenced, 1_000_000);
2198 assert_eq!(ql.max_exclusive, 500_000);
2199 assert_eq!(ql.rsv_referenced, 0);
2200 assert_eq!(ql.rsv_exclusive, 0);
2201 }
2202
2203 #[test]
2204 fn qgroup_limit_too_short() {
2205 assert!(QgroupLimit::parse(&[0; 39]).is_none());
2206 }
2207
2208 #[test]
2209 fn qgroup_status_parse_minimal() {
2210 let mut buf = Vec::new();
2211 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();
2216 assert_eq!(qs.version, 1);
2217 assert_eq!(qs.generation, 50);
2218 assert_eq!(qs.flags, 2);
2219 assert_eq!(qs.scan, 0);
2220 assert!(qs.enable_gen.is_none());
2221 }
2222
2223 #[test]
2224 fn qgroup_status_parse_with_enable_gen() {
2225 let mut buf = Vec::new();
2226 buf.extend_from_slice(&1u64.to_le_bytes());
2227 buf.extend_from_slice(&50u64.to_le_bytes());
2228 buf.extend_from_slice(&2u64.to_le_bytes());
2229 buf.extend_from_slice(&0u64.to_le_bytes());
2230 buf.extend_from_slice(&99u64.to_le_bytes()); let qs = QgroupStatus::parse(&buf).unwrap();
2232 assert_eq!(qs.enable_gen, Some(99));
2233 }
2234
2235 #[test]
2236 fn qgroup_status_too_short() {
2237 assert!(QgroupStatus::parse(&[0; 31]).is_none());
2238 }
2239
2240 #[test]
2241 fn dev_replace_item_parse() {
2242 let mut buf = vec![0u8; 80];
2243 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();
2253 assert_eq!(dri.src_devid, 1);
2254 assert_eq!(dri.cursor_left, 0x1000);
2255 assert_eq!(dri.cursor_right, 0x2000);
2256 assert_eq!(dri.replace_state, 2);
2257 assert_eq!(dri.time_started, 1700000000);
2258 assert_eq!(dri.time_stopped, 1700000100);
2259 assert_eq!(dri.num_write_errors, 3);
2260 assert_eq!(dri.num_uncorrectable_read_errors, 5);
2261 }
2262
2263 #[test]
2264 fn dev_replace_item_too_short() {
2265 assert!(DeviceReplaceItem::parse(&[0; 79]).is_none());
2266 }
2267
2268 #[test]
2269 fn raid_stripe_item_parse() {
2270 let mut buf = Vec::new();
2271 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());
2277 buf.extend_from_slice(&0x20000u64.to_le_bytes());
2278 let rsi = RaidStripeItem::parse(&buf).unwrap();
2279 assert_eq!(rsi.encoding, 1);
2280 assert_eq!(rsi.stripes.len(), 2);
2281 assert_eq!(rsi.stripes[0].devid, 1);
2282 assert_eq!(rsi.stripes[0].physical, 0x10000);
2283 assert_eq!(rsi.stripes[1].devid, 2);
2284 assert_eq!(rsi.stripes[1].physical, 0x20000);
2285 }
2286
2287 #[test]
2288 fn raid_stripe_item_no_stripes() {
2289 let buf = 42u64.to_le_bytes();
2290 let rsi = RaidStripeItem::parse(&buf).unwrap();
2291 assert_eq!(rsi.encoding, 42);
2292 assert!(rsi.stripes.is_empty());
2293 }
2294
2295 #[test]
2296 fn raid_stripe_item_too_short() {
2297 assert!(RaidStripeItem::parse(&[0; 7]).is_none());
2298 }
2299
2300 #[test]
2303 fn inode_ref_parse_single() {
2304 let mut buf = Vec::new();
2305 buf.extend_from_slice(&42u64.to_le_bytes()); buf.extend_from_slice(&4u16.to_le_bytes()); buf.extend_from_slice(b"test");
2308 let refs = InodeRef::parse_all(&buf);
2309 assert_eq!(refs.len(), 1);
2310 assert_eq!(refs[0].index, 42);
2311 assert_eq!(refs[0].name, b"test");
2312 }
2313
2314 #[test]
2315 fn inode_ref_parse_multiple() {
2316 let mut buf = Vec::new();
2317 buf.extend_from_slice(&1u64.to_le_bytes());
2319 buf.extend_from_slice(&3u16.to_le_bytes());
2320 buf.extend_from_slice(b"abc");
2321 buf.extend_from_slice(&2u64.to_le_bytes());
2323 buf.extend_from_slice(&2u16.to_le_bytes());
2324 buf.extend_from_slice(b"xy");
2325 let refs = InodeRef::parse_all(&buf);
2326 assert_eq!(refs.len(), 2);
2327 assert_eq!(refs[0].index, 1);
2328 assert_eq!(refs[0].name, b"abc");
2329 assert_eq!(refs[1].index, 2);
2330 assert_eq!(refs[1].name, b"xy");
2331 }
2332
2333 #[test]
2334 fn inode_ref_parse_truncated() {
2335 let mut buf = Vec::new();
2337 buf.extend_from_slice(&1u64.to_le_bytes());
2338 buf.extend_from_slice(&10u16.to_le_bytes()); buf.extend_from_slice(b"abc"); let refs = InodeRef::parse_all(&buf);
2341 assert!(refs.is_empty());
2342 }
2343
2344 #[test]
2345 fn inode_extref_parse_single() {
2346 let mut buf = Vec::new();
2347 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");
2351 let refs = InodeExtref::parse_all(&buf);
2352 assert_eq!(refs.len(), 1);
2353 assert_eq!(refs[0].parent, 256);
2354 assert_eq!(refs[0].index, 3);
2355 assert_eq!(refs[0].name, b"hello");
2356 }
2357
2358 #[test]
2359 fn dir_item_parse_single() {
2360 let dir_item_size = mem::size_of::<raw::btrfs_dir_item>();
2361 let mut buf = vec![0u8; dir_item_size];
2362 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());
2368 buf[25..27].copy_from_slice(&0u16.to_le_bytes());
2370 buf[27..29].copy_from_slice(&4u16.to_le_bytes());
2372 buf[29] = 1; buf.extend_from_slice(b"file");
2376 let items = DirItem::parse_all(&buf);
2377 assert_eq!(items.len(), 1);
2378 assert_eq!(items[0].transid, 100);
2379 assert_eq!(items[0].file_type, FileType::RegFile);
2380 assert_eq!(items[0].name, b"file");
2381 assert!(items[0].data.is_empty());
2382 }
2383
2384 #[test]
2385 fn root_ref_parse() {
2386 let hdr_size = mem::size_of::<raw::btrfs_root_ref>();
2387 let mut buf = vec![0u8; hdr_size];
2388 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");
2392 let rr = RootRef::parse(&buf).unwrap();
2393 assert_eq!(rr.dirid, 256);
2394 assert_eq!(rr.sequence, 7);
2395 assert_eq!(rr.name, b"subvol");
2396 }
2397
2398 #[test]
2399 fn root_ref_too_short() {
2400 let hdr_size = mem::size_of::<raw::btrfs_root_ref>();
2401 assert!(RootRef::parse(&vec![0u8; hdr_size - 1]).is_none());
2402 }
2403
2404 #[test]
2405 fn uuid_item_parse() {
2406 let mut buf = Vec::new();
2407 buf.extend_from_slice(&256u64.to_le_bytes());
2408 buf.extend_from_slice(&257u64.to_le_bytes());
2409 buf.extend_from_slice(&258u64.to_le_bytes());
2410 let ui = UuidItem::parse(&buf);
2411 assert_eq!(ui.subvol_ids, vec![256, 257, 258]);
2412 }
2413
2414 #[test]
2415 fn uuid_item_empty() {
2416 let ui = UuidItem::parse(&[]);
2417 assert!(ui.subvol_ids.is_empty());
2418 }
2419
2420 #[test]
2421 fn dev_stats_parse() {
2422 let mut buf = Vec::new();
2423 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);
2429 assert_eq!(ds.values.len(), 5);
2430 assert_eq!(ds.values[0], ("write_errs".to_string(), 1));
2431 assert_eq!(ds.values[1], ("read_errs".to_string(), 2));
2432 assert_eq!(ds.values[2], ("flush_errs".to_string(), 3));
2433 assert_eq!(ds.values[3], ("corruption_errs".to_string(), 4));
2434 assert_eq!(ds.values[4], ("generation".to_string(), 5));
2435 }
2436
2437 #[test]
2438 fn dev_stats_partial() {
2439 let mut buf = Vec::new();
2441 buf.extend_from_slice(&10u64.to_le_bytes());
2442 buf.extend_from_slice(&20u64.to_le_bytes());
2443 let ds = DeviceStats::parse(&buf);
2444 assert_eq!(ds.values.len(), 2);
2445 assert_eq!(ds.values[0].1, 10);
2446 assert_eq!(ds.values[1].1, 20);
2447 }
2448
2449 #[test]
2452 fn file_extent_item_inline() {
2453 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();
2461 assert_eq!(fei.generation, 7);
2462 assert_eq!(fei.ram_bytes, 10);
2463 assert_eq!(fei.compression, CompressionType::None);
2464 assert_eq!(fei.extent_type, FileExtentType::Inline);
2465 match fei.body {
2466 FileExtentBody::Inline { inline_size } => {
2467 assert_eq!(inline_size, 10)
2468 }
2469 _ => panic!("expected inline body"),
2470 }
2471 }
2472
2473 #[test]
2474 fn file_extent_item_regular() {
2475 let mut buf = vec![0u8; 53];
2476 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();
2485 assert_eq!(fei.generation, 100);
2486 assert_eq!(fei.compression, CompressionType::Zlib);
2487 assert_eq!(fei.extent_type, FileExtentType::Regular);
2488 match fei.body {
2489 FileExtentBody::Regular {
2490 disk_bytenr,
2491 disk_num_bytes,
2492 offset,
2493 num_bytes,
2494 } => {
2495 assert_eq!(disk_bytenr, 0x100000);
2496 assert_eq!(disk_num_bytes, 4096);
2497 assert_eq!(offset, 0);
2498 assert_eq!(num_bytes, 4096);
2499 }
2500 _ => panic!("expected regular body"),
2501 }
2502 }
2503
2504 #[test]
2505 fn file_extent_item_too_short() {
2506 assert!(FileExtentItem::parse(&[0; 20]).is_none());
2507 }
2508
2509 #[test]
2510 fn file_extent_item_regular_too_short() {
2511 let mut buf = vec![0u8; 21];
2513 buf[20] = 1; assert!(FileExtentItem::parse(&buf).is_none());
2515 }
2516
2517 #[test]
2520 fn raw_crc32c_known_value() {
2521 assert_eq!(raw_crc32c(0, &[]), 0);
2523 let raw = raw_crc32c(0, b"123456789");
2526 let standard = crc32c::crc32c(b"123456789");
2527 assert_eq!(standard, 0xE3069283);
2528 assert_ne!(raw, standard);
2529 assert_eq!(raw, raw_crc32c(0, b"123456789"));
2531 let chained = raw_crc32c(raw, b"more");
2533 assert_ne!(chained, raw);
2534 }
2535
2536 #[test]
2537 fn extent_data_ref_hash_deterministic() {
2538 let h1 = extent_data_ref_hash(5, 256, 0);
2539 let h2 = extent_data_ref_hash(5, 256, 0);
2540 assert_eq!(h1, h2);
2541 let h3 = extent_data_ref_hash(5, 256, 4096);
2543 assert_ne!(h1, h3);
2544 }
2545
2546 #[test]
2547 fn block_group_flags_type_name() {
2548 assert_eq!(BlockGroupFlags::DATA.type_name(), "Data");
2549 assert_eq!(BlockGroupFlags::METADATA.type_name(), "Metadata");
2550 assert_eq!(BlockGroupFlags::SYSTEM.type_name(), "System");
2551 assert_eq!(
2552 (BlockGroupFlags::DATA | BlockGroupFlags::METADATA).type_name(),
2553 "Data+Metadata"
2554 );
2555 assert_eq!(BlockGroupFlags::GLOBAL_RSV.type_name(), "GlobalReserve");
2556 }
2557
2558 #[test]
2559 fn block_group_flags_profile_name() {
2560 assert_eq!(BlockGroupFlags::DATA.profile_name(), "single");
2561 assert_eq!(
2562 (BlockGroupFlags::DATA | BlockGroupFlags::DUP).profile_name(),
2563 "DUP"
2564 );
2565 assert_eq!(
2566 (BlockGroupFlags::DATA | BlockGroupFlags::RAID0).profile_name(),
2567 "RAID0"
2568 );
2569 assert_eq!(
2570 (BlockGroupFlags::DATA | BlockGroupFlags::RAID1).profile_name(),
2571 "RAID1"
2572 );
2573 assert_eq!(
2574 (BlockGroupFlags::DATA | BlockGroupFlags::RAID10).profile_name(),
2575 "RAID10"
2576 );
2577 assert_eq!(
2578 (BlockGroupFlags::DATA | BlockGroupFlags::RAID5).profile_name(),
2579 "RAID5"
2580 );
2581 assert_eq!(
2582 (BlockGroupFlags::DATA | BlockGroupFlags::RAID6).profile_name(),
2583 "RAID6"
2584 );
2585 assert_eq!(
2586 (BlockGroupFlags::DATA | BlockGroupFlags::RAID1C3).profile_name(),
2587 "RAID1C3"
2588 );
2589 assert_eq!(
2590 (BlockGroupFlags::DATA | BlockGroupFlags::RAID1C4).profile_name(),
2591 "RAID1C4"
2592 );
2593 }
2594}