13da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
23da5c369Sopenharmony_ciuse crate::Error;
33da5c369Sopenharmony_ciuse crate::{Errno, NixPath, Result};
43da5c369Sopenharmony_ciuse libc::c_int;
53da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
63da5c369Sopenharmony_ciuse libc::{c_char, c_uint, c_void};
73da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
83da5c369Sopenharmony_ciuse std::{
93da5c369Sopenharmony_ci    borrow::Cow,
103da5c369Sopenharmony_ci    ffi::{CStr, CString},
113da5c369Sopenharmony_ci    fmt, io,
123da5c369Sopenharmony_ci    marker::PhantomData,
133da5c369Sopenharmony_ci};
143da5c369Sopenharmony_ci
153da5c369Sopenharmony_cilibc_bitflags!(
163da5c369Sopenharmony_ci    /// Used with [`Nmount::nmount`].
173da5c369Sopenharmony_ci    pub struct MntFlags: c_int {
183da5c369Sopenharmony_ci        /// ACL support enabled.
193da5c369Sopenharmony_ci        #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
203da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
213da5c369Sopenharmony_ci        MNT_ACLS;
223da5c369Sopenharmony_ci        /// All I/O to the file system should be done asynchronously.
233da5c369Sopenharmony_ci        MNT_ASYNC;
243da5c369Sopenharmony_ci        /// dir should instead be a file system ID encoded as “FSID:val0:val1”.
253da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
263da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
273da5c369Sopenharmony_ci        MNT_BYFSID;
283da5c369Sopenharmony_ci        /// Force a read-write mount even if the file system appears to be
293da5c369Sopenharmony_ci        /// unclean.
303da5c369Sopenharmony_ci        MNT_FORCE;
313da5c369Sopenharmony_ci        /// GEOM journal support enabled.
323da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
333da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
343da5c369Sopenharmony_ci        MNT_GJOURNAL;
353da5c369Sopenharmony_ci        /// MAC support for objects.
363da5c369Sopenharmony_ci        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
373da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
383da5c369Sopenharmony_ci        MNT_MULTILABEL;
393da5c369Sopenharmony_ci        /// Disable read clustering.
403da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
413da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
423da5c369Sopenharmony_ci        MNT_NOCLUSTERR;
433da5c369Sopenharmony_ci        /// Disable write clustering.
443da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
453da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
463da5c369Sopenharmony_ci        MNT_NOCLUSTERW;
473da5c369Sopenharmony_ci        /// Enable NFS version 4 ACLs.
483da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
493da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
503da5c369Sopenharmony_ci        MNT_NFS4ACLS;
513da5c369Sopenharmony_ci        /// Do not update access times.
523da5c369Sopenharmony_ci        MNT_NOATIME;
533da5c369Sopenharmony_ci        /// Disallow program execution.
543da5c369Sopenharmony_ci        MNT_NOEXEC;
553da5c369Sopenharmony_ci        /// Do not honor setuid or setgid bits on files when executing them.
563da5c369Sopenharmony_ci        MNT_NOSUID;
573da5c369Sopenharmony_ci        /// Do not follow symlinks.
583da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
593da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
603da5c369Sopenharmony_ci        MNT_NOSYMFOLLOW;
613da5c369Sopenharmony_ci        /// Mount read-only.
623da5c369Sopenharmony_ci        MNT_RDONLY;
633da5c369Sopenharmony_ci        /// Causes the vfs subsystem to update its data structures pertaining to
643da5c369Sopenharmony_ci        /// the specified already mounted file system.
653da5c369Sopenharmony_ci        MNT_RELOAD;
663da5c369Sopenharmony_ci        /// Create a snapshot of the file system.
673da5c369Sopenharmony_ci        ///
683da5c369Sopenharmony_ci        /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
693da5c369Sopenharmony_ci        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
703da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
713da5c369Sopenharmony_ci        MNT_SNAPSHOT;
723da5c369Sopenharmony_ci        /// Using soft updates.
733da5c369Sopenharmony_ci        #[cfg(any(
743da5c369Sopenharmony_ci                target_os = "dragonfly",
753da5c369Sopenharmony_ci                target_os = "freebsd",
763da5c369Sopenharmony_ci                target_os = "netbsd",
773da5c369Sopenharmony_ci                target_os = "openbsd"
783da5c369Sopenharmony_ci        ))]
793da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
803da5c369Sopenharmony_ci        MNT_SOFTDEP;
813da5c369Sopenharmony_ci        /// Directories with the SUID bit set chown new files to their own
823da5c369Sopenharmony_ci        /// owner.
833da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
843da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
853da5c369Sopenharmony_ci        MNT_SUIDDIR;
863da5c369Sopenharmony_ci        /// All I/O to the file system should be done synchronously.
873da5c369Sopenharmony_ci        MNT_SYNCHRONOUS;
883da5c369Sopenharmony_ci        /// Union with underlying fs.
893da5c369Sopenharmony_ci        #[cfg(any(
903da5c369Sopenharmony_ci                target_os = "macos",
913da5c369Sopenharmony_ci                target_os = "freebsd",
923da5c369Sopenharmony_ci                target_os = "netbsd"
933da5c369Sopenharmony_ci        ))]
943da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
953da5c369Sopenharmony_ci        MNT_UNION;
963da5c369Sopenharmony_ci        /// Indicates that the mount command is being applied to an already
973da5c369Sopenharmony_ci        /// mounted file system.
983da5c369Sopenharmony_ci        MNT_UPDATE;
993da5c369Sopenharmony_ci        /// Check vnode use counts.
1003da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
1013da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1023da5c369Sopenharmony_ci        MNT_NONBUSY;
1033da5c369Sopenharmony_ci    }
1043da5c369Sopenharmony_ci);
1053da5c369Sopenharmony_ci
1063da5c369Sopenharmony_ci/// The Error type of [`Nmount::nmount`].
1073da5c369Sopenharmony_ci///
1083da5c369Sopenharmony_ci/// It wraps an [`Errno`], but also may contain an additional message returned
1093da5c369Sopenharmony_ci/// by `nmount(2)`.
1103da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1113da5c369Sopenharmony_ci#[derive(Debug)]
1123da5c369Sopenharmony_cipub struct NmountError {
1133da5c369Sopenharmony_ci    errno: Error,
1143da5c369Sopenharmony_ci    errmsg: Option<String>,
1153da5c369Sopenharmony_ci}
1163da5c369Sopenharmony_ci
1173da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1183da5c369Sopenharmony_ciimpl NmountError {
1193da5c369Sopenharmony_ci    /// Returns the additional error string sometimes generated by `nmount(2)`.
1203da5c369Sopenharmony_ci    pub fn errmsg(&self) -> Option<&str> {
1213da5c369Sopenharmony_ci        self.errmsg.as_deref()
1223da5c369Sopenharmony_ci    }
1233da5c369Sopenharmony_ci
1243da5c369Sopenharmony_ci    /// Returns the inner [`Error`]
1253da5c369Sopenharmony_ci    pub const fn error(&self) -> Error {
1263da5c369Sopenharmony_ci        self.errno
1273da5c369Sopenharmony_ci    }
1283da5c369Sopenharmony_ci
1293da5c369Sopenharmony_ci    fn new(error: Error, errmsg: Option<&CStr>) -> Self {
1303da5c369Sopenharmony_ci        Self {
1313da5c369Sopenharmony_ci            errno: error,
1323da5c369Sopenharmony_ci            errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
1333da5c369Sopenharmony_ci        }
1343da5c369Sopenharmony_ci    }
1353da5c369Sopenharmony_ci}
1363da5c369Sopenharmony_ci
1373da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1383da5c369Sopenharmony_ciimpl std::error::Error for NmountError {}
1393da5c369Sopenharmony_ci
1403da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1413da5c369Sopenharmony_ciimpl fmt::Display for NmountError {
1423da5c369Sopenharmony_ci    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1433da5c369Sopenharmony_ci        if let Some(errmsg) = &self.errmsg {
1443da5c369Sopenharmony_ci            write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
1453da5c369Sopenharmony_ci        } else {
1463da5c369Sopenharmony_ci            write!(f, "{:?}: {}", self.errno, self.errno.desc())
1473da5c369Sopenharmony_ci        }
1483da5c369Sopenharmony_ci    }
1493da5c369Sopenharmony_ci}
1503da5c369Sopenharmony_ci
1513da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1523da5c369Sopenharmony_ciimpl From<NmountError> for io::Error {
1533da5c369Sopenharmony_ci    fn from(err: NmountError) -> Self {
1543da5c369Sopenharmony_ci        err.errno.into()
1553da5c369Sopenharmony_ci    }
1563da5c369Sopenharmony_ci}
1573da5c369Sopenharmony_ci
1583da5c369Sopenharmony_ci/// Result type of [`Nmount::nmount`].
1593da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
1603da5c369Sopenharmony_cipub type NmountResult = std::result::Result<(), NmountError>;
1613da5c369Sopenharmony_ci
1623da5c369Sopenharmony_ci/// Mount a FreeBSD file system.
1633da5c369Sopenharmony_ci///
1643da5c369Sopenharmony_ci/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
1653da5c369Sopenharmony_ci/// takes its options as a series of name-value pairs.  Most of the values are
1663da5c369Sopenharmony_ci/// strings, as are all of the names.  The `Nmount` structure builds up an
1673da5c369Sopenharmony_ci/// argument list and then executes the syscall.
1683da5c369Sopenharmony_ci///
1693da5c369Sopenharmony_ci/// # Examples
1703da5c369Sopenharmony_ci///
1713da5c369Sopenharmony_ci/// To mount `target` onto `mountpoint` with `nullfs`:
1723da5c369Sopenharmony_ci/// ```
1733da5c369Sopenharmony_ci/// # use nix::unistd::Uid;
1743da5c369Sopenharmony_ci/// # use ::sysctl::{CtlValue, Sysctl};
1753da5c369Sopenharmony_ci/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
1763da5c369Sopenharmony_ci/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
1773da5c369Sopenharmony_ci/// #     return;
1783da5c369Sopenharmony_ci/// # };
1793da5c369Sopenharmony_ci/// use nix::mount::{MntFlags, Nmount, unmount};
1803da5c369Sopenharmony_ci/// use std::ffi::CString;
1813da5c369Sopenharmony_ci/// use tempfile::tempdir;
1823da5c369Sopenharmony_ci///
1833da5c369Sopenharmony_ci/// let mountpoint = tempdir().unwrap();
1843da5c369Sopenharmony_ci/// let target = tempdir().unwrap();
1853da5c369Sopenharmony_ci///
1863da5c369Sopenharmony_ci/// let fstype = CString::new("fstype").unwrap();
1873da5c369Sopenharmony_ci/// let nullfs = CString::new("nullfs").unwrap();
1883da5c369Sopenharmony_ci/// Nmount::new()
1893da5c369Sopenharmony_ci///     .str_opt(&fstype, &nullfs)
1903da5c369Sopenharmony_ci///     .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
1913da5c369Sopenharmony_ci///     .str_opt_owned("target", target.path().to_str().unwrap())
1923da5c369Sopenharmony_ci///     .nmount(MntFlags::empty()).unwrap();
1933da5c369Sopenharmony_ci///
1943da5c369Sopenharmony_ci/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
1953da5c369Sopenharmony_ci/// ```
1963da5c369Sopenharmony_ci///
1973da5c369Sopenharmony_ci/// # See Also
1983da5c369Sopenharmony_ci/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
1993da5c369Sopenharmony_ci/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
2003da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
2013da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
2023da5c369Sopenharmony_ci#[derive(Debug, Default)]
2033da5c369Sopenharmony_cipub struct Nmount<'a> {
2043da5c369Sopenharmony_ci    // n.b. notgull: In reality, this is a list that contains
2053da5c369Sopenharmony_ci    //               both mutable and immutable pointers.
2063da5c369Sopenharmony_ci    //               Be careful using this.
2073da5c369Sopenharmony_ci    iov: Vec<libc::iovec>,
2083da5c369Sopenharmony_ci    is_owned: Vec<bool>,
2093da5c369Sopenharmony_ci    marker: PhantomData<&'a ()>,
2103da5c369Sopenharmony_ci}
2113da5c369Sopenharmony_ci
2123da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
2133da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
2143da5c369Sopenharmony_ciimpl<'a> Nmount<'a> {
2153da5c369Sopenharmony_ci    /// Helper function to push a slice onto the `iov` array.
2163da5c369Sopenharmony_ci    fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
2173da5c369Sopenharmony_ci        self.iov.push(libc::iovec {
2183da5c369Sopenharmony_ci            iov_base: val.as_ptr() as *mut _,
2193da5c369Sopenharmony_ci            iov_len: val.len(),
2203da5c369Sopenharmony_ci        });
2213da5c369Sopenharmony_ci        self.is_owned.push(is_owned);
2223da5c369Sopenharmony_ci    }
2233da5c369Sopenharmony_ci
2243da5c369Sopenharmony_ci    /// Helper function to push a pointer and its length onto the `iov` array.
2253da5c369Sopenharmony_ci    fn push_pointer_and_length(
2263da5c369Sopenharmony_ci        &mut self,
2273da5c369Sopenharmony_ci        val: *const u8,
2283da5c369Sopenharmony_ci        len: usize,
2293da5c369Sopenharmony_ci        is_owned: bool,
2303da5c369Sopenharmony_ci    ) {
2313da5c369Sopenharmony_ci        self.iov.push(libc::iovec {
2323da5c369Sopenharmony_ci            iov_base: val as *mut _,
2333da5c369Sopenharmony_ci            iov_len: len,
2343da5c369Sopenharmony_ci        });
2353da5c369Sopenharmony_ci        self.is_owned.push(is_owned);
2363da5c369Sopenharmony_ci    }
2373da5c369Sopenharmony_ci
2383da5c369Sopenharmony_ci    /// Helper function to push a `nix` path as owned.
2393da5c369Sopenharmony_ci    fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
2403da5c369Sopenharmony_ci        val.with_nix_path(|s| {
2413da5c369Sopenharmony_ci            let len = s.to_bytes_with_nul().len();
2423da5c369Sopenharmony_ci            let ptr = s.to_owned().into_raw() as *const u8;
2433da5c369Sopenharmony_ci
2443da5c369Sopenharmony_ci            self.push_pointer_and_length(ptr, len, true);
2453da5c369Sopenharmony_ci        })
2463da5c369Sopenharmony_ci        .unwrap();
2473da5c369Sopenharmony_ci    }
2483da5c369Sopenharmony_ci
2493da5c369Sopenharmony_ci    /// Add an opaque mount option.
2503da5c369Sopenharmony_ci    ///
2513da5c369Sopenharmony_ci    /// Some file systems take binary-valued mount options.  They can be set
2523da5c369Sopenharmony_ci    /// with this method.
2533da5c369Sopenharmony_ci    ///
2543da5c369Sopenharmony_ci    /// # Safety
2553da5c369Sopenharmony_ci    ///
2563da5c369Sopenharmony_ci    /// Unsafe because it will cause `Nmount::nmount` to dereference a raw
2573da5c369Sopenharmony_ci    /// pointer.  The user is responsible for ensuring that `val` is valid and
2583da5c369Sopenharmony_ci    /// its lifetime outlives `self`!  An easy way to do that is to give the
2593da5c369Sopenharmony_ci    /// value a larger scope than `name`
2603da5c369Sopenharmony_ci    ///
2613da5c369Sopenharmony_ci    /// # Examples
2623da5c369Sopenharmony_ci    /// ```
2633da5c369Sopenharmony_ci    /// use libc::c_void;
2643da5c369Sopenharmony_ci    /// use nix::mount::Nmount;
2653da5c369Sopenharmony_ci    /// use std::ffi::CString;
2663da5c369Sopenharmony_ci    /// use std::mem;
2673da5c369Sopenharmony_ci    ///
2683da5c369Sopenharmony_ci    /// // Note that flags outlives name
2693da5c369Sopenharmony_ci    /// let mut flags: u32 = 0xdeadbeef;
2703da5c369Sopenharmony_ci    /// let name = CString::new("flags").unwrap();
2713da5c369Sopenharmony_ci    /// let p = &mut flags as *mut u32 as *mut c_void;
2723da5c369Sopenharmony_ci    /// let len = mem::size_of_val(&flags);
2733da5c369Sopenharmony_ci    /// let mut nmount = Nmount::new();
2743da5c369Sopenharmony_ci    /// unsafe { nmount.mut_ptr_opt(&name, p, len) };
2753da5c369Sopenharmony_ci    /// ```
2763da5c369Sopenharmony_ci    pub unsafe fn mut_ptr_opt(
2773da5c369Sopenharmony_ci        &mut self,
2783da5c369Sopenharmony_ci        name: &'a CStr,
2793da5c369Sopenharmony_ci        val: *mut c_void,
2803da5c369Sopenharmony_ci        len: usize,
2813da5c369Sopenharmony_ci    ) -> &mut Self {
2823da5c369Sopenharmony_ci        self.push_slice(name.to_bytes_with_nul(), false);
2833da5c369Sopenharmony_ci        self.push_pointer_and_length(val.cast(), len, false);
2843da5c369Sopenharmony_ci        self
2853da5c369Sopenharmony_ci    }
2863da5c369Sopenharmony_ci
2873da5c369Sopenharmony_ci    /// Add a mount option that does not take a value.
2883da5c369Sopenharmony_ci    ///
2893da5c369Sopenharmony_ci    /// # Examples
2903da5c369Sopenharmony_ci    /// ```
2913da5c369Sopenharmony_ci    /// use nix::mount::Nmount;
2923da5c369Sopenharmony_ci    /// use std::ffi::CString;
2933da5c369Sopenharmony_ci    ///
2943da5c369Sopenharmony_ci    /// let read_only = CString::new("ro").unwrap();
2953da5c369Sopenharmony_ci    /// Nmount::new()
2963da5c369Sopenharmony_ci    ///     .null_opt(&read_only);
2973da5c369Sopenharmony_ci    /// ```
2983da5c369Sopenharmony_ci    pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
2993da5c369Sopenharmony_ci        self.push_slice(name.to_bytes_with_nul(), false);
3003da5c369Sopenharmony_ci        self.push_slice(&[], false);
3013da5c369Sopenharmony_ci        self
3023da5c369Sopenharmony_ci    }
3033da5c369Sopenharmony_ci
3043da5c369Sopenharmony_ci    /// Add a mount option that does not take a value, but whose name must be
3053da5c369Sopenharmony_ci    /// owned.
3063da5c369Sopenharmony_ci    ///
3073da5c369Sopenharmony_ci    ///
3083da5c369Sopenharmony_ci    /// This has higher runtime cost than [`Nmount::null_opt`], but is useful
3093da5c369Sopenharmony_ci    /// when the name's lifetime doesn't outlive the `Nmount`, or it's a
3103da5c369Sopenharmony_ci    /// different string type than `CStr`.
3113da5c369Sopenharmony_ci    ///
3123da5c369Sopenharmony_ci    /// # Examples
3133da5c369Sopenharmony_ci    /// ```
3143da5c369Sopenharmony_ci    /// use nix::mount::Nmount;
3153da5c369Sopenharmony_ci    ///
3163da5c369Sopenharmony_ci    /// let read_only = "ro";
3173da5c369Sopenharmony_ci    /// let mut nmount: Nmount<'static> = Nmount::new();
3183da5c369Sopenharmony_ci    /// nmount.null_opt_owned(read_only);
3193da5c369Sopenharmony_ci    /// ```
3203da5c369Sopenharmony_ci    pub fn null_opt_owned<P: ?Sized + NixPath>(
3213da5c369Sopenharmony_ci        &mut self,
3223da5c369Sopenharmony_ci        name: &P,
3233da5c369Sopenharmony_ci    ) -> &mut Self {
3243da5c369Sopenharmony_ci        self.push_nix_path(name);
3253da5c369Sopenharmony_ci        self.push_slice(&[], false);
3263da5c369Sopenharmony_ci        self
3273da5c369Sopenharmony_ci    }
3283da5c369Sopenharmony_ci
3293da5c369Sopenharmony_ci    /// Add a mount option as a [`CStr`].
3303da5c369Sopenharmony_ci    ///
3313da5c369Sopenharmony_ci    /// # Examples
3323da5c369Sopenharmony_ci    /// ```
3333da5c369Sopenharmony_ci    /// use nix::mount::Nmount;
3343da5c369Sopenharmony_ci    /// use std::ffi::CString;
3353da5c369Sopenharmony_ci    ///
3363da5c369Sopenharmony_ci    /// let fstype = CString::new("fstype").unwrap();
3373da5c369Sopenharmony_ci    /// let nullfs = CString::new("nullfs").unwrap();
3383da5c369Sopenharmony_ci    /// Nmount::new()
3393da5c369Sopenharmony_ci    ///     .str_opt(&fstype, &nullfs);
3403da5c369Sopenharmony_ci    /// ```
3413da5c369Sopenharmony_ci    pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
3423da5c369Sopenharmony_ci        self.push_slice(name.to_bytes_with_nul(), false);
3433da5c369Sopenharmony_ci        self.push_slice(val.to_bytes_with_nul(), false);
3443da5c369Sopenharmony_ci        self
3453da5c369Sopenharmony_ci    }
3463da5c369Sopenharmony_ci
3473da5c369Sopenharmony_ci    /// Add a mount option as an owned string.
3483da5c369Sopenharmony_ci    ///
3493da5c369Sopenharmony_ci    /// This has higher runtime cost than [`Nmount::str_opt`], but is useful
3503da5c369Sopenharmony_ci    /// when the value's lifetime doesn't outlive the `Nmount`, or it's a
3513da5c369Sopenharmony_ci    /// different string type than `CStr`.
3523da5c369Sopenharmony_ci    ///
3533da5c369Sopenharmony_ci    /// # Examples
3543da5c369Sopenharmony_ci    /// ```
3553da5c369Sopenharmony_ci    /// use nix::mount::Nmount;
3563da5c369Sopenharmony_ci    /// use std::path::Path;
3573da5c369Sopenharmony_ci    ///
3583da5c369Sopenharmony_ci    /// let mountpoint = Path::new("/mnt");
3593da5c369Sopenharmony_ci    /// Nmount::new()
3603da5c369Sopenharmony_ci    ///     .str_opt_owned("fspath", mountpoint.to_str().unwrap());
3613da5c369Sopenharmony_ci    /// ```
3623da5c369Sopenharmony_ci    pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
3633da5c369Sopenharmony_ci    where
3643da5c369Sopenharmony_ci        P1: ?Sized + NixPath,
3653da5c369Sopenharmony_ci        P2: ?Sized + NixPath,
3663da5c369Sopenharmony_ci    {
3673da5c369Sopenharmony_ci        self.push_nix_path(name);
3683da5c369Sopenharmony_ci        self.push_nix_path(val);
3693da5c369Sopenharmony_ci        self
3703da5c369Sopenharmony_ci    }
3713da5c369Sopenharmony_ci
3723da5c369Sopenharmony_ci    /// Create a new `Nmount` struct with no options
3733da5c369Sopenharmony_ci    pub fn new() -> Self {
3743da5c369Sopenharmony_ci        Self::default()
3753da5c369Sopenharmony_ci    }
3763da5c369Sopenharmony_ci
3773da5c369Sopenharmony_ci    /// Actually mount the file system.
3783da5c369Sopenharmony_ci    pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
3793da5c369Sopenharmony_ci        const ERRMSG_NAME: &[u8] = b"errmsg\0";
3803da5c369Sopenharmony_ci        let mut errmsg = vec![0u8; 255];
3813da5c369Sopenharmony_ci
3823da5c369Sopenharmony_ci        // nmount can return extra error information via a "errmsg" return
3833da5c369Sopenharmony_ci        // argument.
3843da5c369Sopenharmony_ci        self.push_slice(ERRMSG_NAME, false);
3853da5c369Sopenharmony_ci
3863da5c369Sopenharmony_ci        // SAFETY: we are pushing a mutable iovec here, so we can't use
3873da5c369Sopenharmony_ci        //         the above method
3883da5c369Sopenharmony_ci        self.iov.push(libc::iovec {
3893da5c369Sopenharmony_ci            iov_base: errmsg.as_mut_ptr() as *mut c_void,
3903da5c369Sopenharmony_ci            iov_len: errmsg.len(),
3913da5c369Sopenharmony_ci        });
3923da5c369Sopenharmony_ci
3933da5c369Sopenharmony_ci        let niov = self.iov.len() as c_uint;
3943da5c369Sopenharmony_ci        let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
3953da5c369Sopenharmony_ci        let res = unsafe { libc::nmount(iovp, niov, flags.bits) };
3963da5c369Sopenharmony_ci        match Errno::result(res) {
3973da5c369Sopenharmony_ci            Ok(_) => Ok(()),
3983da5c369Sopenharmony_ci            Err(error) => {
3993da5c369Sopenharmony_ci                let errmsg = match errmsg.iter().position(|&x| x == 0) {
4003da5c369Sopenharmony_ci                    None => None,
4013da5c369Sopenharmony_ci                    Some(0) => None,
4023da5c369Sopenharmony_ci                    Some(n) => {
4033da5c369Sopenharmony_ci                        let sl = &errmsg[0..n + 1];
4043da5c369Sopenharmony_ci                        Some(CStr::from_bytes_with_nul(sl).unwrap())
4053da5c369Sopenharmony_ci                    }
4063da5c369Sopenharmony_ci                };
4073da5c369Sopenharmony_ci                Err(NmountError::new(error, errmsg))
4083da5c369Sopenharmony_ci            }
4093da5c369Sopenharmony_ci        }
4103da5c369Sopenharmony_ci    }
4113da5c369Sopenharmony_ci}
4123da5c369Sopenharmony_ci
4133da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
4143da5c369Sopenharmony_ciimpl<'a> Drop for Nmount<'a> {
4153da5c369Sopenharmony_ci    fn drop(&mut self) {
4163da5c369Sopenharmony_ci        for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
4173da5c369Sopenharmony_ci            if *is_owned {
4183da5c369Sopenharmony_ci                // Free the owned string.  Safe because we recorded ownership,
4193da5c369Sopenharmony_ci                // and Nmount does not implement Clone.
4203da5c369Sopenharmony_ci                unsafe {
4213da5c369Sopenharmony_ci                    drop(CString::from_raw(iov.iov_base as *mut c_char));
4223da5c369Sopenharmony_ci                }
4233da5c369Sopenharmony_ci            }
4243da5c369Sopenharmony_ci        }
4253da5c369Sopenharmony_ci    }
4263da5c369Sopenharmony_ci}
4273da5c369Sopenharmony_ci
4283da5c369Sopenharmony_ci/// Unmount the file system mounted at `mountpoint`.
4293da5c369Sopenharmony_ci///
4303da5c369Sopenharmony_ci/// Useful flags include
4313da5c369Sopenharmony_ci/// * `MNT_FORCE` -     Unmount even if still in use.
4323da5c369Sopenharmony_ci#[cfg_attr(
4333da5c369Sopenharmony_ci    target_os = "freebsd",
4343da5c369Sopenharmony_ci    doc = "
4353da5c369Sopenharmony_ci* `MNT_BYFSID` -    `mountpoint` is not a path, but a file system ID
4363da5c369Sopenharmony_ci                    encoded as `FSID:val0:val1`, where `val0` and `val1`
4373da5c369Sopenharmony_ci                    are the contents of the `fsid_t val[]` array in decimal.
4383da5c369Sopenharmony_ci                    The file system that has the specified file system ID
4393da5c369Sopenharmony_ci                    will be unmounted.  See
4403da5c369Sopenharmony_ci                    [`statfs`](crate::sys::statfs::statfs) to determine the
4413da5c369Sopenharmony_ci                    `fsid`.
4423da5c369Sopenharmony_ci"
4433da5c369Sopenharmony_ci)]
4443da5c369Sopenharmony_cipub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
4453da5c369Sopenharmony_ciwhere
4463da5c369Sopenharmony_ci    P: ?Sized + NixPath,
4473da5c369Sopenharmony_ci{
4483da5c369Sopenharmony_ci    let res = mountpoint.with_nix_path(|cstr| unsafe {
4493da5c369Sopenharmony_ci        libc::unmount(cstr.as_ptr(), flags.bits)
4503da5c369Sopenharmony_ci    })?;
4513da5c369Sopenharmony_ci
4523da5c369Sopenharmony_ci    Errno::result(res).map(drop)
4533da5c369Sopenharmony_ci}
454