libbtrfs 0.0.20

Rust library for working with the btrfs filesystem
Documentation
use super::*;

/// Information about a btrfs subvolume root
///
/// This structure is returned by the [`get_info`] or [`get_info_by_id`] functions and represents
/// information about a btrfs subvolume root
pub struct SubvolInfo(pub(super) btrfs_ioctl_get_subvol_info_args);

impl SubvolInfo {
    /// Id of this subvolume
    pub const fn treeid(&self) -> u64 {
        self.0.treeid
    }

    /// Name of this subvolume as a string slice
    pub fn name_as_str(&self) -> Result<&str, std::str::Utf8Error> {
        unsafe { CStr::from_ptr(self.0.name.as_ptr().cast()).to_str() }
    }

    /// Name of this subvolume as a byte slice
    pub fn name_as_bytes(&self) -> &[u8] {
        unsafe { CStr::from_ptr(self.0.name.as_ptr()).to_bytes() }
    }

    /// Id of the subvolume which contains this subvolume
    pub fn parent_id(&self) -> u64 {
        self.0.parent_id
    }

    /// Inode number of the directory which contains this subvolume
    pub fn dirid(&self) -> u64 {
        self.0.dirid
    }

    /// Latest transaction id of this subvolume
    pub fn generation(&self) -> u64 {
        self.0.generation
    }

    /// Flags for this subvolume
    pub fn flags(&self) -> u64 {
        self.0.flags
    }

    /// Readonly status for this subvolume
    pub fn readonly(&self) -> bool {
        self.0.flags & BTRFS_SUBVOL_RDONLY != 0
    }

    /// UUID of this subvolume
    pub fn uuid(&self) -> Uuid {
        Uuid::from_bytes(self.0.uuid)
    }

    /// UUID of the subvolume of which this subvolume is a snapshot. This will be nil for
    /// non-snapshot subvolumes
    ///
    /// See [`Uuid::is_nil`]
    pub fn parent_uuid(&self) -> Uuid {
        Uuid::from_bytes(self.0.parent_uuid)
    }

    /// UUID of the subvolume from which this subvolume was received. This will be nil for
    /// non-received subvolumes
    ///
    /// See [`Uuid::is_nil`]
    pub fn received_uuid(&self) -> Uuid {
        Uuid::from_bytes(self.0.received_uuid)
    }

    /// transaction ID indicating when change happened
    pub fn ctransid(&self) -> u64 {
        self.0.ctransid
    }

    /// transaction ID indicating when create happened
    pub fn otransid(&self) -> u64 {
        self.0.otransid
    }

    /// transaction ID indicating when send happened
    pub fn stransid(&self) -> u64 {
        self.0.stransid
    }

    /// transaction ID indicating when receive happened
    pub fn rtransid(&self) -> u64 {
        self.0.rtransid
    }

    /// Time corresponding to ctransid
    pub fn ctime(&self) -> &Timespec {
        &self.0.ctime
    }

    /// Time corresponding to otransid
    pub fn otime(&self) -> &Timespec {
        &self.0.otime
    }

    /// Time corresponding to stransid
    pub fn stime(&self) -> &Timespec {
        &self.0.stime
    }

    /// Time corresponding to rtransid
    pub fn rtime(&self) -> &Timespec {
        &self.0.rtime
    }
}

/// Query information about a btrfs subvolume
///
/// This function returns information about the subvolume that contains `pathname`, which can be a
/// subvolume, directory, or regular file in a btrfs filesystem
///
/// # Errors
///
/// [`io::Errors::NotFound`]
///
/// No file exists at `pathname`
///
pub fn get_info<P: AsRef<Path>>(pathname: P) -> io::Result<SubvolInfo> {
    let fd = File::open(pathname)?;

    fd::get_info(fd.as_raw_fd())
}

/// Query information about a btrfs subvolume by its subvolume id
///
/// # Errors
///
/// [`io::Errors::NotFound`]
///
/// `treeid` is not a valid subvolume id
///
/// # Notes
///
/// **Requires CAP_SYS_ADMIN capabilities**
///
pub fn get_info_by_id<P: AsRef<Path>>(treeid: u64, fs: P) -> io::Result<SubvolInfo> {
    let fd = File::open(fs)?;

    fd::get_info_by_id(treeid, fd.as_raw_fd())
}

pub mod fd {
    use super::*;
    use crate::{
        bindings::{
            btrfs_root_ref, BTRFS_FS_TREE_OBJECTID, BTRFS_ROOT_BACKREF_KEY, BTRFS_ROOT_ITEM_KEY,
        },
        util::root_item_to_subvol_info_args,
    };
    use std::{mem::MaybeUninit as Uninit, os::unix::io::RawFd, ptr::addr_of_mut};

    /// See [super::get_info()]
    pub fn get_info(fd: RawFd) -> io::Result<SubvolInfo> {
        let mut args = btrfs_ioctl_get_subvol_info_args::default();

        btrfs_ioctl(fd, BTRFS_IOC_GET_SUBVOL_INFO, &mut args)?;

        Ok(SubvolInfo(args))
    }

    /// See [super::get_info_by_id()]
    pub fn get_info_by_id(treeid: u64, fd: RawFd) -> io::Result<SubvolInfo> {
        let mut info_args = Uninit::<btrfs_ioctl_get_subvol_info_args>::uninit();
        let mut got_root_ref = treeid == BTRFS_FS_TREE_OBJECTID;
        let mut got_root_item = false;
        let mut ts = TreeSearch::new(fd, |key| {
            key.tree_id = BTRFS_ROOT_TREE_OBJECTID;
            key.min_objectid = treeid;
            key.max_objectid = treeid;
            key.min_type = BTRFS_ROOT_ITEM_KEY;
            key.max_type = if got_root_ref {
                BTRFS_ROOT_ITEM_KEY
            } else {
                BTRFS_ROOT_BACKREF_KEY
            };
        });
        let p = info_args.as_mut_ptr();

        if let Some(items) = ts.search()? {
            for item in items {
                match item.key() {
                    BTRFS_ROOT_ITEM_KEY => {
                        root_item_to_subvol_info_args(p, item.get());
                        got_root_item = true;
                    }
                    BTRFS_ROOT_BACKREF_KEY => {
                        let rr = item.get::<&btrfs_root_ref>();
                        let name = item.name_as_ibytes(rr);
                        unsafe {
                            let pname = (*p).name.as_mut_ptr();
                            name.as_ptr().copy_to_nonoverlapping(pname, name.len());
                            pname.add(name.len()).write(0);

                            addr_of_mut!((*p).dirid).write(u64::from_le(rr.dirid));
                            addr_of_mut!((*p).parent_id).write(item.offset());
                        }
                        got_root_ref = true;
                    }
                    _ => {}
                }
                if got_root_ref && got_root_item {
                    unsafe {
                        addr_of_mut!((*p).reserved).write([0; 8]);
                        addr_of_mut!((*p).treeid).write(treeid);

                        return Ok(SubvolInfo(info_args.assume_init()));
                    }
                }
            }
        }
        error!(NotFound)
    }
}