btrfsutil/subvolume/
iterator.rs

1use crate::common;
2use crate::error::LibError;
3use crate::subvolume::Subvolume;
4use crate::Result;
5
6use std::convert::TryFrom;
7use std::convert::TryInto;
8use std::ffi::CString;
9use std::path::Path;
10
11use btrfsutil_sys::btrfs_util_create_subvolume_iterator;
12use btrfsutil_sys::btrfs_util_destroy_subvolume_iterator;
13use btrfsutil_sys::btrfs_util_subvolume_iterator;
14use btrfsutil_sys::btrfs_util_subvolume_iterator_next;
15
16bitflags! {
17    /// Subvolume iterator options
18    pub struct SubvolumeIteratorFlags: i32 {
19        /// Post order
20        const POST_ORDER = btrfsutil_sys::BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER as i32;
21    }
22}
23
24/// A subvolume iterator.
25pub struct SubvolumeIterator(*mut btrfs_util_subvolume_iterator);
26
27impl SubvolumeIterator {
28    /// Create a new subvolume iterator.
29    pub fn new<'a, P, F>(path: P, flags: F) -> Result<Self>
30    where
31        P: Into<&'a Path>,
32        F: Into<Option<SubvolumeIteratorFlags>>,
33    {
34        Self::new_impl(path.into(), flags.into())
35    }
36
37    fn new_impl(path: &Path, flags: Option<SubvolumeIteratorFlags>) -> Result<Self> {
38        let path_cstr = common::path_to_cstr(path);
39        let flags_val = if let Some(val) = flags { val.bits() } else { 0 };
40
41        let raw_iterator_ptr: *mut btrfs_util_subvolume_iterator = {
42            let mut raw_iterator_ptr: *mut btrfs_util_subvolume_iterator = std::ptr::null_mut();
43            unsafe_wrapper!({
44                btrfs_util_create_subvolume_iterator(
45                    path_cstr.as_ptr(),
46                    0, // read below
47                    flags_val,
48                    &mut raw_iterator_ptr,
49                )
50            })?;
51            // using 0 instead of an id is intentional
52            // https://github.com/kdave/btrfs-progs/blob/11acf45eea6dd81e891564967051e2bb10bd25f7/libbtrfsutil/subvolume.c#L971
53            // if we specify an id then libbtrfsutil will use elevated privileges to search for
54            // subvolumes
55            // if we don't, then it will use elevated privileges only if the current user is root
56            raw_iterator_ptr
57        };
58
59        Ok(Self(raw_iterator_ptr))
60    }
61}
62
63impl Iterator for SubvolumeIterator {
64    type Item = Result<Subvolume>;
65
66    fn next(&mut self) -> Option<Result<Subvolume>> {
67        let mut cstr_ptr: *mut std::os::raw::c_char = std::ptr::null_mut();
68        let mut id: u64 = 0;
69
70        if let Err(e) =
71            unsafe_wrapper!({ btrfs_util_subvolume_iterator_next(self.0, &mut cstr_ptr, &mut id) })
72        {
73            if e == LibError::StopIteration {
74                None
75            } else {
76                Err(e).into()
77            }
78        } else if !cstr_ptr.is_null() {
79            let path = common::cstr_to_path(unsafe { CString::from_raw(cstr_ptr).as_ref() });
80            Subvolume::get(path.as_path()).into()
81        } else if id != 0 {
82            Subvolume::try_from(id).into()
83        } else {
84            panic!("subvolume iterator returned both a null path")
85        }
86    }
87}
88
89impl Drop for SubvolumeIterator {
90    fn drop(&mut self) {
91        unsafe {
92            btrfs_util_destroy_subvolume_iterator(self.0);
93        }
94    }
95}
96
97impl TryFrom<&Subvolume> for SubvolumeIterator {
98    type Error = LibError;
99
100    /// Same as SubvolumeIterator::new with no flags.
101    #[inline]
102    fn try_from(src: &Subvolume) -> Result<SubvolumeIterator> {
103        SubvolumeIterator::new_impl(src.path(), None)
104    }
105}
106
107impl TryInto<Vec<Subvolume>> for SubvolumeIterator {
108    type Error = LibError;
109
110    /// Same as SubvolumeIterator.`collect::<Result<Vec<Subvolume>>>`.
111    #[inline]
112    fn try_into(self) -> Result<Vec<Subvolume>> {
113        self.collect::<Result<Vec<Subvolume>>>()
114    }
115}