use crate::raw::{
btrfs_ioc_space_info, btrfs_ioctl_space_args, btrfs_ioctl_space_info,
};
pub use btrfs_disk::items::BlockGroupFlags;
use std::{
mem,
os::{fd::AsRawFd, unix::io::BorrowedFd},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SpaceInfo {
pub flags: BlockGroupFlags,
pub total_bytes: u64,
pub used_bytes: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_bits_preserves_known_flags() {
let raw = BlockGroupFlags::DATA.bits() | BlockGroupFlags::RAID1.bits();
let flags = BlockGroupFlags::from_bits_truncate(raw);
assert!(flags.contains(BlockGroupFlags::DATA));
assert!(flags.contains(BlockGroupFlags::RAID1));
}
}
impl From<btrfs_ioctl_space_info> for SpaceInfo {
fn from(raw: btrfs_ioctl_space_info) -> Self {
Self {
flags: BlockGroupFlags::from_bits_truncate(raw.flags),
total_bytes: raw.total_bytes,
used_bytes: raw.used_bytes,
}
}
}
#[allow(clippy::cast_possible_truncation)] pub fn space_info(fd: BorrowedFd) -> nix::Result<Vec<SpaceInfo>> {
let mut args: btrfs_ioctl_space_args = unsafe { mem::zeroed() };
unsafe { btrfs_ioc_space_info(fd.as_raw_fd(), &raw mut args) }?;
let count = args.total_spaces as usize;
if count == 0 {
return Ok(Vec::new());
}
let base_size = mem::size_of::<btrfs_ioctl_space_args>();
let info_size = mem::size_of::<btrfs_ioctl_space_info>();
let total_bytes = base_size + count * info_size;
let num_u64s = total_bytes.div_ceil(mem::size_of::<u64>());
let mut buf = vec![0u64; num_u64s];
unsafe {
let args_ptr = buf.as_mut_ptr().cast::<btrfs_ioctl_space_args>();
(*args_ptr).space_slots = count as u64;
btrfs_ioc_space_info(fd.as_raw_fd(), &raw mut *args_ptr)?;
Ok((*args_ptr)
.spaces
.as_slice(count)
.iter()
.copied()
.map(SpaceInfo::from)
.collect())
}
}