libbtrfs 0.0.20

Rust library for working with the btrfs filesystem
Documentation
use super::*;
use std::ffi::CStr;
use uuid::Uuid;

/// Information about a btrfs device
///
/// Returned by the [`info()`] function or calls to [`InfoIter::next()`]
pub struct DevInfo(btrfs_ioctl_dev_info_args);

impl DevInfo {
    /// Id for this device
    pub fn device_id(&self) -> u64 {
        self.0.devid
    }

    /// Uuid for this device
    pub fn device_uuid(&self) -> Uuid {
        Uuid::from_bytes(self.0.uuid)
    }

    /// Used bytes for this device
    pub fn bytes_used(&self) -> u64 {
        self.0.bytes_used
    }

    /// Total bytes for this device
    pub fn bytes_total(&self) -> u64 {
        self.0.total_bytes
    }

    /// Path for this device
    pub fn path_as_str(&self) -> Result<&str, std::str::Utf8Error> {
        CStr::from_bytes_until_nul(&self.0.path).unwrap().to_str()
    }

    /// Path for this device
    pub fn path_as_bytes(&self) -> &[u8] {
        CStr::from_bytes_until_nul(&self.0.path).unwrap().to_bytes()
    }

    #[cfg(VERSION_6_3)]
    /// Filesystem uuid
    ///
    /// # Notes
    ///
    /// This function requires kernel >= v6.3
    ///
    pub fn fsid(&self) -> Uuid {
        Uuid::from_bytes(self.0.fsid)
    }
}

/// Iterator over devices in a btrfs filesystem
///
/// This `struct` is created by the [`info_iter()`] function.
///
/// Call to next yeild instances of <code>[io::Result]<[DevInfo]></code>
///
/// # Errors
///
/// * [`io::ErrorKind::InvalidData`]
///
/// The iterator encounted an unexpeced number of devices
///
pub struct InfoIter {
    fd: OptionFd,
    num_devices: u64,
    max_id: u64,
    device_count: u64,
    last_id: u64,
}

impl InfoIter {
    fn new_internal(fd: OptionFd) -> io::Result<Self> {
        let fs_info = crate::fs::fd::info(fd.as_raw_fd(), crate::Opt::empty())?;
        Ok(InfoIter {
            fd,
            num_devices: fs_info.num_devices(),
            max_id: fs_info.max_id(),
            device_count: 0,
            last_id: 0,
        })
    }
}

impl Iterator for InfoIter {
    type Item = io::Result<super::DevInfo>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.last_id >= self.max_id {
            return if self.device_count != self.num_devices {
                Some(Err(io::ErrorKind::InvalidData.into()))
            } else {
                None
            };
        }
        let mut args = btrfs_ioctl_dev_info_args::default();

        for id in 1 + self.last_id.. {
            args.devid = id;

            if let Err(e) = btrfs_ioctl(self.fd.as_raw_fd(), BTRFS_IOC_DEV_INFO, &mut args) {
                if let Some(libc::ENODEV) = e.raw_os_error() {
                    continue;
                }
                return Some(Err(e));
            }
            self.last_id = id;
            self.device_count += 1;

            if self.device_count > self.num_devices {
                return Some(Err(io::ErrorKind::InvalidData.into()));
            }
            break;
        }

        Some(Ok(DevInfo(args)))
    }
}

/// Information about a device in a btrfs filesystem
///
/// Returns information about a device in a btrfs filesystem with the device id of `devid`
///
/// For an iterator over all devices in a btrfs filesystem see [`info_iter()`].
pub fn info<P: AsRef<Path>>(devid: u64, fs: P) -> io::Result<DevInfo> {
    let fd = File::open(fs)?;

    fd::info(devid, fd.as_raw_fd())
}

/// Returns an iterator over devices in a btrfs filesystem
///
/// # Examples
/// ```no_run
///
/// for dev in libbtrfs::dev::info_iter("/")? {
///     let dev = dev?;
///     println!("device id: {}", dev.device_id());
///     println!("bytes-used: {:.2}GiB", dev.bytes_used() as f32 / 2f32.powi(30));
///     println!("bytes-total: {:.2}GiB", dev.bytes_total() as f32 / 2f32.powi(30));
/// }
///
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn info_iter<P: AsRef<Path>>(fs: P) -> io::Result<InfoIter> {
    let fd = File::open(fs)?;

    InfoIter::new_internal(OptionFd::File(fd))
}

pub(super) mod fd {
    use super::*;
    use std::os::unix::io::RawFd;

    /// See [super::info()]
    pub fn info(devid: u64, fd: RawFd) -> io::Result<super::DevInfo> {
        let mut args = btrfs_ioctl_dev_info_args {
            devid,
            ..Default::default()
        };
        btrfs_ioctl(fd, BTRFS_IOC_DEV_INFO, &mut args)?;

        Ok(super::DevInfo(args))
    }

    /// See [super::info_iter()]
    pub fn info_iter(fd: RawFd) -> io::Result<InfoIter> {
        InfoIter::new_internal(OptionFd::Raw(fd))
    }
}