use crate::{
bindings::{
btrfs_ioctl_fs_info_args, btrfs_ioctl_space_args, btrfs_ioctl_space_info,
BTRFS_FS_INFO_FLAG_CSUM_INFO, BTRFS_FS_INFO_FLAG_GENERATION,
BTRFS_FS_INFO_FLAG_METADATA_UUID, BTRFS_IOC_FS_INFO, BTRFS_IOC_SPACE_INFO,
},
util::btrfs_ioctl,
Opt,
};
use std::{
alloc::{alloc, dealloc, Layout},
ffi::CString,
fs::File,
io,
mem::{align_of, size_of, MaybeUninit as Uninit},
os::unix::{io::AsRawFd, prelude::OsStrExt},
path::Path,
ptr,
};
use uuid::Uuid;
pub mod block_group;
pub struct FsInfo(btrfs_ioctl_fs_info_args);
impl FsInfo {
pub fn max_id(&self) -> u64 {
self.0.max_id
}
pub fn num_devices(&self) -> u64 {
self.0.num_devices
}
pub fn fsid(&self) -> Uuid {
Uuid::from_bytes(self.0.fsid)
}
pub fn nodesize(&self) -> u32 {
self.0.nodesize
}
pub fn sectorsize(&self) -> u32 {
self.0.sectorsize
}
pub fn clone_alignment(&self) -> u32 {
self.0.clone_alignment
}
pub fn csum_type(&self) -> u16 {
(self.0.flags & BTRFS_FS_INFO_FLAG_CSUM_INFO != 0)
.then_some(self.0.csum_type)
.expect("CSUM_INFO Flag not provided")
}
pub fn csum_size(&self) -> u16 {
(self.0.flags & BTRFS_FS_INFO_FLAG_CSUM_INFO != 0)
.then_some(self.0.csum_size)
.expect("CSUM_INFO Flag not provided")
}
pub fn generation(&self) -> u64 {
(self.0.flags & BTRFS_FS_INFO_FLAG_GENERATION != 0)
.then_some(self.0.generation)
.expect("GENERATION Flag not provided")
}
pub fn metadata_uuid(&self) -> Uuid {
(self.0.flags & BTRFS_FS_INFO_FLAG_METADATA_UUID != 0)
.then_some(Uuid::from_bytes(self.0.metadata_uuid))
.expect("METADATA_UUID Flag not provided")
}
}
pub struct SpaceInfo(btrfs_ioctl_space_info);
impl SpaceInfo {
pub fn block_type(&self) -> block_group::BlockType {
self.0.flags.into()
}
pub fn raid_profile(&self) -> io::Result<block_group::RaidProfile> {
self.0.flags.try_into()
}
pub fn flags(&self) -> u64 {
self.0.flags
}
pub fn used_bytes(&self) -> u64 {
self.0.used_bytes
}
pub fn total_bytes(&self) -> u64 {
self.0.total_bytes
}
}
pub struct Spaces(Vec<SpaceInfo>);
impl IntoIterator for Spaces {
type Item = SpaceInfo;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
pub fn is_btrfs<P: AsRef<Path>>(fs: P) -> io::Result<bool> {
let cstring = CString::new(fs.as_ref().as_os_str().as_bytes()).unwrap();
let mut sfs = Uninit::<libc::statfs>::uninit();
unsafe {
syscall!(statfs(cstring.as_ptr(), sfs.as_mut_ptr()))?;
Ok(sfs.assume_init().f_type == libc::BTRFS_SUPER_MAGIC)
}
}
pub fn info<P: AsRef<Path>>(fs: P, flags: Opt) -> io::Result<FsInfo> {
let fd = File::open(fs.as_ref())?;
fd::info(fd.as_raw_fd(), flags)
}
pub fn get_spaces<P: AsRef<Path>>(fs: P) -> io::Result<Spaces> {
let fd = File::open(fs.as_ref())?;
fd::get_spaces(fd.as_raw_fd())
}
pub mod fd {
use super::*;
use std::os::unix::io::RawFd;
pub fn is_btrfs(fd: RawFd) -> io::Result<bool> {
let mut sfs = Uninit::<libc::statfs>::uninit();
unsafe {
syscall!(fstatfs(fd, sfs.as_mut_ptr()))?;
Ok(sfs.assume_init().f_type == libc::BTRFS_SUPER_MAGIC)
}
}
pub fn info(fd: RawFd, flags: Opt) -> io::Result<FsInfo> {
let mut args = btrfs_ioctl_fs_info_args {
flags: flags.fs_info_flags(),
..Default::default()
};
btrfs_ioctl(fd, BTRFS_IOC_FS_INFO, &mut args)?;
Ok(FsInfo(args))
}
pub fn get_spaces(fd: RawFd) -> io::Result<Spaces> {
let mut args = btrfs_ioctl_space_args::default();
btrfs_ioctl(fd, BTRFS_IOC_SPACE_INFO, &mut args)?;
let total_spaces = args.total_spaces as usize;
let size = size_of::<btrfs_ioctl_space_args>()
+ (size_of::<btrfs_ioctl_space_info>() * total_spaces);
let layout = Layout::from_size_align(size, align_of::<btrfs_ioctl_space_args>()).unwrap();
unsafe {
let argp = alloc(layout).cast::<btrfs_ioctl_space_args>();
if argp.is_null() {
error!(OutOfMemory)
}
(*argp).space_slots = args.total_spaces;
(*argp).total_spaces = 0;
btrfs_ioctl(fd, BTRFS_IOC_SPACE_INFO, argp)?;
let ret = (*argp)
.spaces
.as_slice(total_spaces)
.iter()
.map(|info| SpaceInfo(ptr::read(info)))
.collect::<Vec<SpaceInfo>>();
dealloc(argp.cast(), layout);
Ok(Spaces(ret))
}
}
}