libbtrfs 0.0.20

Rust library for working with the btrfs filesystem
Documentation
use super::*;
use crate::{
    bindings::{
        btrfs_dev_item, btrfs_ioctl_search_header, BTRFS_CHUNK_TREE_OBJECTID,
        BTRFS_DEV_ITEMS_OBJECTID, BTRFS_DEV_ITEM_KEY, BTRFS_SEARCH_ARGS_BUFSIZE,
    },
    tree_search::fd::TreeSearch,
    util::OptionFd,
};
use std::mem::size_of;
use uuid::Uuid;

/// Information about a btrfs device
///
/// Returned by the [`ItemIter`] iterator
pub struct DevItem<'a>(&'a btrfs_dev_item);

impl DevItem<'_> {
    pub fn device_id(&self) -> u64 {
        u64::from_le(self.0.devid)
    }

    pub fn total_bytes(&self) -> u64 {
        u64::from_le(self.0.total_bytes)
    }

    pub fn bytes_used(&self) -> u64 {
        u64::from_le(self.0.bytes_used)
    }

    pub fn io_align(&self) -> u32 {
        u32::from_le(self.0.io_align)
    }

    pub fn io_width(&self) -> u32 {
        u32::from_le(self.0.io_width)
    }

    pub fn sector_size(&self) -> u32 {
        u32::from_le(self.0.sector_size)
    }

    pub fn device_type(&self) -> u64 {
        u64::from_le(self.0.type_)
    }

    pub fn generation(&self) -> u64 {
        u64::from_le(self.0.generation)
    }

    pub fn start_offset(&self) -> u64 {
        u64::from_le(self.0.start_offset)
    }

    pub fn dev_group(&self) -> u32 {
        u32::from_le(self.0.dev_group)
    }

    pub fn seek_speed(&self) -> u8 {
        self.0.seek_speed
    }

    pub fn bandwidth(&self) -> u8 {
        self.0.bandwidth
    }

    pub fn uuid(&self) -> Uuid {
        Uuid::from_bytes(self.0.uuid)
    }

    pub fn fsid(&self) -> Uuid {
        Uuid::from_bytes(self.0.fsid)
    }
}

/// Iterator over devices in a btrfs filesystem.
///
/// This `struct` is created by the [`item_iter()`] function.
///
/// Call to next yeild instances of <code>[io::Result]<[DevInfo]></code>
pub struct ItemIter<'a>(Vec<DevItem<'a>>);

impl ItemIter<'_> {
    fn new_internal(fd: OptionFd) -> io::Result<Self> {
        let mut max_items = BTRFS_SEARCH_ARGS_BUFSIZE
            / (size_of::<btrfs_ioctl_search_header>() + size_of::<btrfs_dev_item>());

        let mut ts = TreeSearch::new(fd.as_raw_fd(), |key| {
            key.tree_id = BTRFS_CHUNK_TREE_OBJECTID;
            key.min_objectid = BTRFS_DEV_ITEMS_OBJECTID;
            key.max_objectid = BTRFS_DEV_ITEMS_OBJECTID;
            key.min_type = BTRFS_DEV_ITEM_KEY;
            key.max_type = BTRFS_DEV_ITEM_KEY;
            key.nr_items = max_items as u32;
            key.min_offset = 1;
        });
        let mut buf: Vec<DevItem> = Vec::new();

        while let Some(items) = ts.search()? {
            buf.extend(items.map(|item| DevItem(item.get())));

            if max_items != buf.len() {
                break;
            }
            ts.min_offset(buf.last().unwrap().device_id() + 1);
            max_items <<= 1;
        }

        Ok(Self(buf))
    }
}

impl<'a> IntoIterator for ItemIter<'a> {
    type Item = DevItem<'a>;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

/// Returns an iterator over devices in a btrfs filesystem
///
/// # Notes
///
/// **Requires CAP_SYS_ADMIN capabilities**
///
pub fn item_iter<'a, P: AsRef<Path>>(fs: P) -> io::Result<ItemIter<'a>> {
    let fd = File::open(fs)?;

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

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

    /// See [super::item_iter()]
    pub fn item_iter<'a>(fd: RawFd) -> io::Result<ItemIter<'a>> {
        ItemIter::new_internal(OptionFd::Raw(fd))
    }
}