use crate::{
Error,
Errno,
NixPath,
Result,
};
use libc::{c_char, c_int, c_uint, c_void};
use std::{
borrow::Cow,
ffi::{CString, CStr},
fmt,
io,
marker::PhantomData,
};
libc_bitflags!(
pub struct MntFlags: c_int {
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_ACLS;
MNT_ASYNC;
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_BYFSID;
MNT_FORCE;
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_GJOURNAL;
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_MULTILABEL;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NOCLUSTERR;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NOCLUSTERW;
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NFS4ACLS;
MNT_NOATIME;
MNT_NOEXEC;
MNT_NOSUID;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NOSYMFOLLOW;
MNT_RDONLY;
MNT_RELOAD;
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_SNAPSHOT;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_SOFTDEP;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_SUIDDIR;
MNT_SYNCHRONOUS;
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_UNION;
MNT_UPDATE;
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NONBUSY;
}
);
#[derive(Debug)]
pub struct NmountError {
errno: Error,
errmsg: Option<String>
}
impl NmountError {
pub fn errmsg(&self) -> Option<&str> {
self.errmsg.as_deref()
}
pub const fn error(&self) -> Error {
self.errno
}
fn new(error: Error, errmsg: Option<&CStr>) -> Self {
Self {
errno: error,
errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned)
}
}
}
impl std::error::Error for NmountError {}
impl fmt::Display for NmountError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(errmsg) = &self.errmsg {
write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
} else {
write!(f, "{:?}: {}", self.errno, self.errno.desc())
}
}
}
impl From<NmountError> for io::Error {
fn from(err: NmountError) -> Self {
err.errno.into()
}
}
pub type NmountResult = std::result::Result<(), NmountError>;
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[derive(Debug, Default)]
pub struct Nmount<'a>{
iov: Vec<libc::iovec>,
is_owned: Vec<bool>,
marker: PhantomData<&'a ()>,
}
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
impl<'a> Nmount<'a> {
fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
self.iov.push(libc::iovec {
iov_base: val.as_ptr() as *mut _,
iov_len: val.len(),
});
self.is_owned.push(is_owned);
}
fn push_pointer_and_length(&mut self, val: *const u8, len: usize, is_owned: bool) {
self.iov.push(libc::iovec {
iov_base: val as *mut _,
iov_len: len,
});
self.is_owned.push(is_owned);
}
fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
val.with_nix_path(|s| {
let len = s.to_bytes_with_nul().len();
let ptr = s.to_owned().into_raw() as *const u8;
self.push_pointer_and_length(ptr, len, true);
}).unwrap();
}
pub unsafe fn mut_ptr_opt(
&mut self,
name: &'a CStr,
val: *mut c_void,
len: usize
) -> &mut Self
{
self.push_slice(name.to_bytes_with_nul(), false);
self.push_pointer_and_length(val.cast(), len, false);
self
}
pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(&[], false);
self
}
pub fn null_opt_owned<P: ?Sized + NixPath>(&mut self, name: &P) -> &mut Self
{
self.push_nix_path(name);
self.push_slice(&[], false);
self
}
pub fn str_opt(
&mut self,
name: &'a CStr,
val: &'a CStr
) -> &mut Self
{
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(val.to_bytes_with_nul(), false);
self
}
pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
where P1: ?Sized + NixPath,
P2: ?Sized + NixPath
{
self.push_nix_path(name);
self.push_nix_path(val);
self
}
pub fn new() -> Self {
Self::default()
}
pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
const ERRMSG_NAME: &[u8] = b"errmsg\0";
let mut errmsg = vec![0u8; 255];
self.push_slice(ERRMSG_NAME, false);
self.iov.push(libc::iovec {
iov_base: errmsg.as_mut_ptr() as *mut c_void,
iov_len: errmsg.len(),
});
let niov = self.iov.len() as c_uint;
let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
let res = unsafe {
libc::nmount(iovp, niov, flags.bits)
};
match Errno::result(res) {
Ok(_) => Ok(()),
Err(error) => {
let errmsg = match errmsg.iter().position(|&x| x == 0) {
None => None,
Some(0) => None,
Some(n) => {
let sl = &errmsg[0..n + 1];
Some(CStr::from_bytes_with_nul(sl).unwrap())
}
};
Err(NmountError::new(error, errmsg))
}
}
}
}
#[cfg(target_os = "freebsd")]
impl<'a> Drop for Nmount<'a> {
fn drop(&mut self) {
for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
if *is_owned {
unsafe {
drop(CString::from_raw(iov.iov_base as *mut c_char));
}
}
}
}
}
pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
where P: ?Sized + NixPath
{
let res = mountpoint.with_nix_path(|cstr| {
unsafe { libc::unmount(cstr.as_ptr(), flags.bits) }
})?;
Errno::result(res).map(drop)
}