libbtrfs 0.0.20

Rust library for working with the btrfs filesystem
Documentation
//! Btrfs device operations.
//!
//! This module contains methods for basic management of devices in a btrfs filesystem such as
//! adding and removing devices as well as structures which provide information about devices
//! in a btrfs filesystem.
//!
use crate::{
    bindings::{
        btrfs_ioctl_dev_info_args, btrfs_ioctl_get_dev_stats, btrfs_ioctl_vol_args,
        btrfs_ioctl_vol_args_v2, vol_args_v2_volume, BTRFS_DEVICE_SPEC_BY_ID,
        BTRFS_DEV_STAT_CORRUPTION_ERRS, BTRFS_DEV_STAT_FLUSH_ERRS, BTRFS_DEV_STAT_GENERATION_ERRS,
        BTRFS_DEV_STAT_READ_ERRS, BTRFS_DEV_STAT_VALUES_MAX, BTRFS_DEV_STAT_WRITE_ERRS,
        BTRFS_IOC_ADD_DEV, BTRFS_IOC_DEV_INFO, BTRFS_IOC_GET_DEV_STATS, BTRFS_IOC_RM_DEV,
        BTRFS_IOC_RM_DEV_V2,
    },
    util::{btrfs_ioctl, vol_args_name_from_bytes, vol_args_v2_name_from_bytes, OptionFd},
};
use std::{fs::File, io, os::unix::io::AsRawFd, path::Path};

mod dev_info;
pub use dev_info::{info, info_iter, DevInfo, InfoIter};

mod dev_item;
pub use dev_item::{item_iter, DevItem, ItemIter};

/// Stats for a btrfs device
///
/// Returned by the [`get_stats()`] function
pub struct DevStats(btrfs_ioctl_get_dev_stats);

impl DevStats {
    /// Number of write errors for this device
    pub fn write_errors(&self) -> u64 {
        self.0.values[BTRFS_DEV_STAT_WRITE_ERRS as usize]
    }

    /// Number of read errors for this device
    pub fn read_errors(&self) -> u64 {
        self.0.values[BTRFS_DEV_STAT_READ_ERRS as usize]
    }

    /// Number of flush errors for this device
    pub fn flush_errors(&self) -> u64 {
        self.0.values[BTRFS_DEV_STAT_FLUSH_ERRS as usize]
    }

    /// Number of corruptions errors for this device
    pub fn corruptions_errors(&self) -> u64 {
        self.0.values[BTRFS_DEV_STAT_CORRUPTION_ERRS as usize]
    }

    /// Number of generation errors for this device
    pub fn generation_errors(&self) -> u64 {
        self.0.values[BTRFS_DEV_STAT_GENERATION_ERRS as usize]
    }

    /// Returns a boolean indicating if this device has any errors
    pub fn has_errors(&self) -> bool {
        self.0.values.iter().any(|&f| f != 0)
    }
}

/// Gets error statistics for a btrfs device
pub fn get_stats<P: AsRef<Path>>(devid: u64, fs: P) -> io::Result<DevStats> {
    let fd = File::open(fs)?;

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

/// Adds a device to a btrfs filesystem
///
/// # Notes
///
/// **Requires CAP_SYS_ADMIN capabilities**
///
pub fn add<P: AsRef<Path>>(device: &str, fs: P) -> io::Result<()> {
    let fd = File::open(fs)?;

    fd::add(device, fd.as_raw_fd())
}

/// Removes a device from a btrfs filesystem by device name
///
/// # Notes
///
/// **Requires CAP_SYS_ADMIN capabilities**
///
pub fn rm<P: AsRef<Path>>(device: &str, fs: P) -> io::Result<()> {
    let fd = File::open(fs)?;

    fd::rm(device, fd.as_raw_fd())
}

/// Remove a btrfs device from the filesystem by device id
///
/// # Notes
///
/// **Requires CAP_SYS_ADMIN capabilities**
///
pub fn rm_by_id<P: AsRef<Path>>(devid: u64, fs: P) -> io::Result<()> {
    let fd = File::open(fs)?;

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

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

    pub use dev_info::fd::{info, info_iter};

    pub use dev_item::fd::item_iter;

    /// See [super::get_stats()]
    pub fn get_stats(devid: u64, fd: RawFd) -> io::Result<DevStats> {
        let mut stats = btrfs_ioctl_get_dev_stats {
            devid,
            nr_items: BTRFS_DEV_STAT_VALUES_MAX as u64,
            ..Default::default()
        };
        btrfs_ioctl(fd, BTRFS_IOC_GET_DEV_STATS, &mut stats)?;

        Ok(DevStats(stats))
    }

    /// See [super::add()]
    pub fn add(device: &str, fd: RawFd) -> io::Result<()> {
        let mut args = btrfs_ioctl_vol_args {
            name: vol_args_name_from_bytes(device.as_bytes()),
            ..Default::default()
        };
        btrfs_ioctl(fd, BTRFS_IOC_ADD_DEV, &mut args)
    }

    /// See [super::rm()]
    pub fn rm(device: &str, fd: RawFd) -> io::Result<()> {
        fn rm_dev_v1(fd: i32, device: &str) -> io::Result<()> {
            let mut vol_args = btrfs_ioctl_vol_args {
                name: vol_args_name_from_bytes(device.as_bytes()),
                ..Default::default()
            };
            btrfs_ioctl(fd, BTRFS_IOC_RM_DEV, &mut vol_args)
        }
        let mut vol_args = btrfs_ioctl_vol_args_v2 {
            inner2: vol_args_v2_volume {
                name: vol_args_v2_name_from_bytes(device.as_bytes()),
            },
            ..Default::default()
        };
        match btrfs_ioctl(fd, BTRFS_IOC_RM_DEV_V2, &mut vol_args) {
            Err(e) => match e.raw_os_error() {
                Some(libc::ENOTTY) | Some(libc::ENOTSUP) => rm_dev_v1(fd, device),

                _ => Err(e),
            },
            _ => Ok(()),
        }
    }

    /// See [super::rm_by_id()]
    pub fn rm_by_id(devid: u64, fd: RawFd) -> io::Result<()> {
        let mut args = btrfs_ioctl_vol_args_v2 {
            flags: BTRFS_DEVICE_SPEC_BY_ID,
            inner2: vol_args_v2_volume { devid },
            ..Default::default()
        };
        btrfs_ioctl(fd, BTRFS_IOC_RM_DEV_V2, &mut args)
    }
}