1use crate::raw::{
8 BTRFS_AVAIL_ALLOC_BIT_SINGLE, BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_DUP,
9 BTRFS_BLOCK_GROUP_METADATA, BTRFS_BLOCK_GROUP_RAID0, BTRFS_BLOCK_GROUP_RAID1,
10 BTRFS_BLOCK_GROUP_RAID1C3, BTRFS_BLOCK_GROUP_RAID1C4, BTRFS_BLOCK_GROUP_RAID5,
11 BTRFS_BLOCK_GROUP_RAID6, BTRFS_BLOCK_GROUP_RAID10, BTRFS_BLOCK_GROUP_SYSTEM,
12 BTRFS_SPACE_INFO_GLOBAL_RSV, btrfs_ioc_space_info, btrfs_ioctl_space_args,
13 btrfs_ioctl_space_info,
14};
15use bitflags::bitflags;
16use std::{
17 fmt, mem,
18 os::{fd::AsRawFd, unix::io::BorrowedFd},
19};
20
21bitflags! {
22 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29 pub struct BlockGroupFlags: u64 {
30 const DATA = BTRFS_BLOCK_GROUP_DATA as u64;
32 const SYSTEM = BTRFS_BLOCK_GROUP_SYSTEM as u64;
33 const METADATA = BTRFS_BLOCK_GROUP_METADATA as u64;
34
35 const RAID0 = BTRFS_BLOCK_GROUP_RAID0 as u64;
37 const RAID1 = BTRFS_BLOCK_GROUP_RAID1 as u64;
38 const DUP = BTRFS_BLOCK_GROUP_DUP as u64;
39 const RAID10 = BTRFS_BLOCK_GROUP_RAID10 as u64;
40 const RAID5 = BTRFS_BLOCK_GROUP_RAID5 as u64;
41 const RAID6 = BTRFS_BLOCK_GROUP_RAID6 as u64;
42 const RAID1C3 = BTRFS_BLOCK_GROUP_RAID1C3 as u64;
43 const RAID1C4 = BTRFS_BLOCK_GROUP_RAID1C4 as u64;
44
45 const SINGLE = BTRFS_AVAIL_ALLOC_BIT_SINGLE;
48
49 const GLOBAL_RSV = BTRFS_SPACE_INFO_GLOBAL_RSV;
51 }
52}
53
54impl BlockGroupFlags {
55 pub fn type_name(self) -> &'static str {
57 if self.contains(Self::GLOBAL_RSV) {
58 return "GlobalReserve";
59 }
60 let ty = self & (Self::DATA | Self::SYSTEM | Self::METADATA);
61 match ty {
62 t if t == Self::DATA => "Data",
63 t if t == Self::SYSTEM => "System",
64 t if t == Self::METADATA => "Metadata",
65 t if t == Self::DATA | Self::METADATA => "Data+Metadata",
66 _ => "unknown",
67 }
68 }
69
70 pub fn profile_name(self) -> &'static str {
72 let profile = self
73 & (Self::RAID0
74 | Self::RAID1
75 | Self::DUP
76 | Self::RAID10
77 | Self::RAID5
78 | Self::RAID6
79 | Self::RAID1C3
80 | Self::RAID1C4
81 | Self::SINGLE);
82 match profile {
83 p if p == Self::RAID0 => "RAID0",
84 p if p == Self::RAID1 => "RAID1",
85 p if p == Self::DUP => "DUP",
86 p if p == Self::RAID10 => "RAID10",
87 p if p == Self::RAID5 => "RAID5",
88 p if p == Self::RAID6 => "RAID6",
89 p if p == Self::RAID1C3 => "RAID1C3",
90 p if p == Self::RAID1C4 => "RAID1C4",
91 _ => "single",
93 }
94 }
95}
96
97impl fmt::Display for BlockGroupFlags {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write!(f, "{}, {}", self.type_name(), self.profile_name())
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct SpaceInfo {
110 pub flags: BlockGroupFlags,
111 pub total_bytes: u64,
112 pub used_bytes: u64,
113}
114
115impl From<btrfs_ioctl_space_info> for SpaceInfo {
116 fn from(raw: btrfs_ioctl_space_info) -> Self {
117 Self {
118 flags: BlockGroupFlags::from_bits_truncate(raw.flags),
119 total_bytes: raw.total_bytes,
120 used_bytes: raw.used_bytes,
121 }
122 }
123}
124
125pub fn space_info(fd: BorrowedFd) -> nix::Result<Vec<SpaceInfo>> {
133 let mut args: btrfs_ioctl_space_args = unsafe { mem::zeroed() };
135 unsafe { btrfs_ioc_space_info(fd.as_raw_fd(), &mut args) }?;
136 let count = args.total_spaces as usize;
137
138 if count == 0 {
139 return Ok(Vec::new());
140 }
141
142 let base_size = mem::size_of::<btrfs_ioctl_space_args>();
148 let info_size = mem::size_of::<btrfs_ioctl_space_info>();
149 let total_bytes = base_size + count * info_size;
150 let num_u64s = total_bytes.div_ceil(mem::size_of::<u64>());
151 let mut buf = vec![0u64; num_u64s];
152
153 unsafe {
157 let args_ptr = buf.as_mut_ptr() as *mut btrfs_ioctl_space_args;
158 (*args_ptr).space_slots = count as u64;
159 btrfs_ioc_space_info(fd.as_raw_fd(), &mut *args_ptr)?;
160 Ok((*args_ptr)
161 .spaces
162 .as_slice(count)
163 .iter()
164 .copied()
165 .map(SpaceInfo::from)
166 .collect())
167 }
168}