use enum_iterator::all;
use std::collections::HashSet;
use std::ffi::CString;
use std::mem::MaybeUninit;
use std::path::{Path, PathBuf};
use std::ptr::NonNull;
use std::str::FromStr;
use crate::core::cache::Cache;
use crate::core::device::Tag;
use crate::core::entries::{FsTabEntry, MountInfoEntry};
use crate::core::flags::MountFlag;
use crate::core::flags::UserspaceMountFlag;
use crate::core::fs::{FileLock, FileSystem};
use crate::tables::{FsTab, GcItem, MountInfo};
use crate::{owning_mut_from_ptr, owning_ref_from_ptr};
use crate::ffi_utils;
use crate::mount::ExitCode;
use crate::mount::ExitStatus;
use crate::mount::MntBuilder;
use crate::mount::MountBuilder;
use crate::mount::MountError;
use crate::mount::MountIter;
use crate::mount::MountNamespace;
use crate::mount::MountOptionsMode;
use crate::mount::MountSource;
use crate::mount::ProcessExitStatus;
use crate::mount::ReMountIter;
#[derive(Debug)]
pub struct Mount {
pub(crate) inner: *mut libmount::libmnt_context,
pub(crate) gc: Vec<GcItem>,
}
impl Mount {
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn from_ptr(ptr: *mut libmount::libmnt_context) -> Mount {
Self {
inner: ptr,
gc: vec![],
}
}
#[doc(hidden)]
pub(crate) fn new() -> Result<Mount, MountError> {
log::debug!("Mount::new creating a new `Mount` instance");
let mut inner = MaybeUninit::<*mut libmount::libmnt_context>::zeroed();
unsafe {
inner.write(libmount::mnt_new_context());
}
match unsafe { inner.assume_init() } {
inner if inner.is_null() => {
let err_msg = "failed to create a new `Mount` instance".to_owned();
log::debug!(
"Mount::new {}. libmount::mnt_new_contex returned a NULL pointer",
err_msg
);
Err(MountError::Creation(err_msg))
}
inner => {
log::debug!("Mount::new created a new `Mount` instance");
let mount = Self::from_ptr(inner);
Ok(mount)
}
}
}
#[doc(hidden)]
fn return_code_to_exit_status(&self, return_code: i32) -> Result<ExitStatus, MountError> {
log::debug!(
"Mount::return_code_to_exit_status converting to exit status the return code: {:?}",
return_code
);
const BUFFER_LENGTH: usize = 4097; let mut buffer: Vec<libc::c_char> = vec![0; BUFFER_LENGTH];
let rc = unsafe {
libmount::mnt_context_get_excode(
self.inner,
return_code,
buffer.as_mut_ptr(),
BUFFER_LENGTH,
)
};
let exit_code = ExitCode::try_from(rc)?;
let error_message = ffi_utils::c_char_array_to_string(buffer.as_ptr());
let exit_status = ExitStatus::new(exit_code, error_message);
log::debug!(
"Mount::return_code_to_exit_status converted return code: {:?} to exit status {:?}",
return_code,
exit_status
);
Ok(exit_status)
}
#[doc(hidden)]
fn disable_canonicalize(
mount: *mut libmount::libmnt_context,
disable: bool,
) -> Result<(), MountError> {
let op = if disable { 1 } else { 0 };
let op_str = if disable {
"disable".to_owned()
} else {
"enable".to_owned()
};
let result = unsafe { libmount::mnt_context_disable_canonicalize(mount, op) };
match result {
0 => {
log::debug!(
"Mount::disable_canonicalize {}d path canonicalization",
op_str
);
Ok(())
}
code => {
let err_msg = format!("failed to {} path canonicalization", op_str);
log::debug!("Mount::disable_canonicalize {}. libmount::mnt_context_disable_canonicalize returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn disable_path_canonicalization(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_path_canonicalization disabling path canonicalization");
Self::disable_canonicalize(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn enable_path_canonicalization(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_path_canonicalization enabling path canonicalization");
Self::disable_canonicalize(self.inner, false)
}
#[doc(hidden)]
fn disable_mnt_helpers(
mount: *mut libmount::libmnt_context,
disable: bool,
) -> Result<(), MountError> {
let op = if disable { 1 } else { 0 };
let op_str = if disable {
"disable".to_owned()
} else {
"enable".to_owned()
};
let result = unsafe { libmount::mnt_context_disable_helpers(mount, op) };
match result {
0 => {
log::debug!("Mount::disable_mnt_helpers {}d mount helpers", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} mount helpers", op_str);
log::debug!("Mount::disable_mnt_helpers {}. libmount::mnt_context_disable_helpers returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn disable_helpers(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_helpers disabling mount helpers");
Self::disable_mnt_helpers(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn enable_helpers(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_helpers enabling mount helpers");
Self::disable_mnt_helpers(self.inner, false)
}
#[doc(hidden)]
fn disable_swap_match(
mount: *mut libmount::libmnt_context,
disable: bool,
) -> Result<(), MountError> {
let op = if disable { 1 } else { 0 };
let op_str = if disable {
"disable".to_owned()
} else {
"enable".to_owned()
};
let result = unsafe { libmount::mnt_context_disable_swapmatch(mount, op) };
match result {
0 => {
log::debug!("Mount::disable_swap_match {}d mount point lookup", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} mount point lookup", op_str);
log::debug!("Mount::disable_swap_match {}. libmount::mnt_context_disable_swapmatch returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn disable_mount_point_lookup(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_mount_point_lookup disabling mount point lookup");
Self::disable_swap_match(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn enable_mount_point_lookup(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_mount_point_lookup enabling mount point lookup");
Self::disable_swap_match(self.inner, false)
}
#[doc(hidden)]
fn disable_mtab(mount: *mut libmount::libmnt_context, disable: bool) -> Result<(), MountError> {
let op = if disable { 1 } else { 0 };
let op_str = if disable {
"disable".to_owned()
} else {
"enable".to_owned()
};
let result = unsafe { libmount::mnt_context_disable_mtab(mount, op) };
match result {
0 => {
log::debug!(
"Mount::disable_mtab {}d userspace mount table updates",
op_str
);
Ok(())
}
code => {
let err_msg = format!("failed to {} userspace mount table updates", op_str);
log::debug!("Mount::disable_mtab {}. libmount::mnt_context_disable_mtab returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn do_not_update_utab(&mut self) -> Result<(), MountError> {
log::debug!("Mount::do_not_update_utab disabling userspace mount table updates");
Self::disable_mtab(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn update_utab(&mut self) -> Result<(), MountError> {
log::debug!("Mount::update_utab enabling userspace mount table updates");
Self::disable_mtab(self.inner, false)
}
#[doc(hidden)]
fn enable_fake(mount: *mut libmount::libmnt_context, enable: bool) -> Result<(), MountError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_fake(mount, op) };
match result {
0 => {
log::debug!("Mount::enable_fake {}d dry run", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} dry run", op_str);
log::debug!("Mount::enable_fake {}. libmount::mnt_context_enable_fake returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn enable_dry_run(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_dry_run enabling dry run");
Self::enable_fake(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn disable_dry_run(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_dry_run disabling dry run");
Self::enable_fake(self.inner, false)
}
#[doc(hidden)]
fn enable_force_mount_device_read_write(
mount: *mut libmount::libmnt_context,
enable: bool,
) -> Result<(), MountError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_rwonly_mount(mount, op) };
match result {
0 => {
log::debug!(
"Mount::enable_force_mount_device_read_write {}d force mount device read-write",
op_str
);
Ok(())
}
code => {
let err_msg = format!("failed to {} force mount device read-write", op_str);
log::debug!("Mount::enable_force_mount_device_read_write {}. libmount::mnt_context_enable_rwonly_mount returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn enable_force_mount_read_write(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::enable_force_mount_read_write enabling force mount device in read-write mode"
);
Self::enable_force_mount_device_read_write(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn disable_force_mount_read_write(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::disable_force_mount_read_write disabling force mount device in read-write mode"
);
Self::enable_force_mount_device_read_write(self.inner, false)
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
fn ignore_autofs(mount: *mut libmount::libmnt_context, ignore: bool) -> Result<(), MountError> {
let op = if ignore { 1 } else { 0 };
let op_str = if ignore {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_noautofs(mount, op) };
match result {
0 => {
log::debug!(
"Mount::ignore_autofs {}d ignore `autofs` mount table entries",
op_str
);
Ok(())
}
code => {
let err_msg = format!("failed to {} ignore `autofs` mount table entries", op_str);
log::debug!("Mount::ignore_autofs {}. libmount::mnt_context_enable_noautofs returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
pub(crate) fn enable_ignore_autofs(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::enable_ignore_autofs enabling `Mount` functionality to ignore `autofs` mount table entries"
);
Self::ignore_autofs(self.inner, true)
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
pub(crate) fn disable_ignore_autofs(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::enable_ignore_autofs disabling `Mount` functionality to ignore `autofs` mount table entries"
);
Self::ignore_autofs(self.inner, false)
}
#[doc(hidden)]
fn ignore_unsupported_mount_options(
mount: *mut libmount::libmnt_context,
ignore: bool,
) -> Result<(), MountError> {
let op = if ignore { 1 } else { 0 };
let op_str = if ignore {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_sloppy(mount, op) };
match result {
0 => {
log::debug!(
"Mount::ignore_unsupported_mount_options {}d ignore unsupported mount options",
op_str
);
Ok(())
}
code => {
let err_msg = format!("failed to {} ignore unsupported mount options", op_str);
log::debug!("Mount::ignore_unsupported_mount_options {}. libmount::mnt_context_enable_sloppy returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn enable_ignore_unsupported_mount_options(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::enable_ignore_unsupported_mount_options enabling `Mount` functionality to ignore mount options not supported by a file system"
);
Self::ignore_unsupported_mount_options(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn disable_ignore_unsupported_mount_options(&mut self) -> Result<(), MountError> {
log::debug!(
"Mount::disable_ignore_unsupported_mount_options disabling `Mount` functionality to ignore mount options not supported by a file system"
);
Self::ignore_unsupported_mount_options(self.inner, false)
}
#[doc(hidden)]
pub(crate) fn force_user_mount(&mut self) -> Result<(), MountError> {
log::debug!("Mount::force_user_mount removing all safety checks");
let result = unsafe { libmount::mnt_context_force_unrestricted(self.inner) };
match result {
0 => {
log::debug!("Mount::force_user_mount removed all safety checks");
Ok(())
}
code => {
let err_msg = "failed to remove all safety checks".to_owned();
log::debug!("Mount::force_user_mount {}. libmount::mnt_context_force_unrestricted returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
fn enable_verbose(
mount: *mut libmount::libmnt_context,
enable: bool,
) -> Result<(), MountError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_verbose(mount, op) };
match result {
0 => {
log::debug!("Mount::enable_verbose {}d verbose output", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} verbose output", op_str);
log::debug!("Mount::enable_verbose {}. libmount::mnt_context_enable_verbose returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn enable_verbose_output(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_verbose_output enabling verbose output");
Self::enable_verbose(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn disable_verbose_output(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_verbose_output disabling verbose output");
Self::enable_verbose(self.inner, false)
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
fn enable_only_once(
mount: *mut libmount::libmnt_context,
enable: bool,
) -> Result<(), MountError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_onlyonce(mount, op) };
match result {
0 => {
log::debug!("Mount::enable_only_once {}d mount only once", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} mount only once", op_str);
log::debug!("Mount::enable_only_once {}. libmount::mnt_context_enable_onlyonce returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
pub(crate) fn enable_mount_only_once(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_mount_only_once enabling check if device not already mounted");
Self::enable_only_once(self.inner, true)
}
#[cfg(mount = "v2_39")]
#[doc(hidden)]
pub(crate) fn disable_mount_only_once(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_mount_only_once disabling check if device not already mounted");
Self::enable_only_once(self.inner, false)
}
#[doc(hidden)]
fn enable_fork(mount: *mut libmount::libmnt_context, enable: bool) -> Result<(), MountError> {
let op = if enable { 1 } else { 0 };
let op_str = if enable {
"enable".to_owned()
} else {
"disable".to_owned()
};
let result = unsafe { libmount::mnt_context_enable_fork(mount, op) };
match result {
0 => {
log::debug!("Mount::enable_fork {}d parallel mounts", op_str);
Ok(())
}
code => {
let err_msg = format!("failed to {} parallel mounts", op_str);
log::debug!("Mount::enable_fork {}. libmount::mnt_context_enable_fork returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn enable_parallel_mount(&mut self) -> Result<(), MountError> {
log::debug!("Mount::enable_parallel_mount enabling parallel mounts");
Self::enable_fork(self.inner, true)
}
#[doc(hidden)]
pub(crate) fn disable_parallel_mount(&mut self) -> Result<(), MountError> {
log::debug!("Mount::disable_parallel_mount disabling parallel mounts");
Self::enable_fork(self.inner, false)
}
#[doc(hidden)]
pub(crate) fn set_cache(&mut self, cache: Cache) -> Result<(), MountError> {
log::debug!("Mount::set_cache overriding internal cache with custom instance");
let result = unsafe { libmount::mnt_context_set_cache(self.inner, cache.inner) };
match result {
0 => {
log::debug!("Mount::set_cache overrode internal cache with custom table");
Ok(())
}
code => {
let err_msg = "failed to override internal cache with custom instance".to_owned();
log::debug!("Mount::set_cache {}. libmount::mnt_context_set_cache returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_fstab(&mut self, table: FsTab) -> Result<(), MountError> {
log::debug!("Mount::set_fstab overriding internal fstab with custom table");
let result = unsafe { libmount::mnt_context_set_fstab(self.inner, table.inner) };
match result {
0 => {
log::debug!("Mount::set_fstab overrode internal fstab with custom table");
Ok(())
}
code => {
let err_msg = "failed to override internal fstab with custom table".to_owned();
log::debug!("Mount::set_fstab {}. libmount::mnt_context_set_fstab returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_file_systems_filter<T>(&mut self, fs_list: T) -> Result<(), MountError>
where
T: AsRef<str>,
{
let fs_list = fs_list.as_ref();
let fs_list_cstr = ffi_utils::as_ref_str_to_c_string(fs_list)?;
log::debug!(
"Mount::set_file_systems_filter setting the list of file systems: {:?}",
fs_list
);
let result =
unsafe { libmount::mnt_context_set_fstype_pattern(self.inner, fs_list_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::set_file_systems_filter set the list of file systems: {:?}",
fs_list
);
Ok(())
}
code => {
let err_msg = format!("failed to set file systems list: {:?}", fs_list);
log::debug!("Mount::set_file_systems_filter {}. libmount::mnt_context_set_fstype_pattern returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_data(&mut self, data: NonNull<libc::c_void>) -> Result<(), MountError> {
log::debug!("Mount::set_mount_data overriding data argument of mount syscall");
let result = unsafe { libmount::mnt_context_set_mountdata(self.inner, data.as_ptr()) };
match result {
0 => {
log::debug!("Mount::set_mount_data overrode data argument of mount syscall");
Ok(())
}
code => {
let err_msg = "failed to override data argument of mount syscall".to_owned();
log::debug!("Mount::set_mount_data {}. libmount::mnt_context_set_mountdata returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_options<T>(&mut self, options_list: T) -> Result<(), MountError>
where
T: AsRef<str>,
{
let options_list = options_list.as_ref();
let options_list_cstr = ffi_utils::as_ref_str_to_c_string(options_list)?;
log::debug!(
"Mount::set_mount_options setting mount options: {:?}",
options_list
);
let result =
unsafe { libmount::mnt_context_set_options(self.inner, options_list_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::set_mount_options set mount options: {:?}",
options_list
);
Ok(())
}
code => {
let err_msg = format!("failed to set mount options: {:?}", options_list);
log::debug!("Mount::set_mount_options {}. libmount::mnt_context_set_options returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_options_mode(
&mut self,
mode: Vec<MountOptionsMode>,
) -> Result<(), MountError> {
log::debug!(
"Mount::set_mount_options_mode setting mount options mode: {:?}",
mode
);
let options_mode = mode.iter().fold(0, |acc, &m| acc | (m as i32));
let result = unsafe { libmount::mnt_context_set_optsmode(self.inner, options_mode) };
match result {
0 => {
log::debug!(
"Mount::set_mount_options_mode set mount options mode: {:?}",
mode
);
Ok(())
}
code => {
let err_msg = format!("failed to set mount options mode: {:?}", mode);
log::debug!("Mount::set_mount_options_mode {}. libmount::mnt_context_set_optsmode returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_options_filter<T>(&mut self, options_list: T) -> Result<(), MountError>
where
T: AsRef<str>,
{
let options_list = options_list.as_ref();
let options_list_cstr = ffi_utils::as_ref_str_to_c_string(options_list)?;
log::debug!(
"Mount::set_mount_options_filter setting mount options filter: {:?}",
options_list
);
let result = unsafe {
libmount::mnt_context_set_options_pattern(self.inner, options_list_cstr.as_ptr())
};
match result {
0 => {
log::debug!(
"Mount::set_mount_options_filter set mount options filter: {:?}",
options_list
);
Ok(())
}
code => {
let err_msg = format!("failed to set mount options filter: {:?}", options_list);
log::debug!("Mount::set_mount_options_filter {}. libmount::mnt_context_set_options_pattern returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
fn set_source(mount: *mut libmount::libmnt_context, source: CString) -> Result<(), MountError> {
let result = unsafe { libmount::mnt_context_set_source(mount, source.as_ptr()) };
match result {
0 => {
log::debug!("Mount::set_source mount source set");
Ok(())
}
code => {
let err_msg = format!("failed to set mount source: {:?}", source);
log::debug!("Mount::set_source {}. libmount::mnt_context_set_source returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_source(&mut self, source: MountSource) -> Result<(), MountError> {
let source = source.to_string();
let source_cstr = ffi_utils::as_ref_path_to_c_string(&source)?;
log::debug!("Mount::set_mount_source setting mount source: {:?}", source);
Self::set_source(self.inner, source_cstr)
}
#[doc(hidden)]
pub(crate) fn set_mount_target<T>(&mut self, target: T) -> Result<(), MountError>
where
T: AsRef<Path>,
{
let target = target.as_ref();
let target_cstr = ffi_utils::as_ref_path_to_c_string(target)?;
log::debug!(
"Mount::set_mount_target setting mount target to: {:?}",
target
);
let result = unsafe { libmount::mnt_context_set_target(self.inner, target_cstr.as_ptr()) };
match result {
0 => {
log::debug!("Mount::set_mount_target set mount target to: {:?}", target);
Ok(())
}
code => {
let err_msg = format!("failed to set mount target to: {:?}", target);
log::debug!("Mount::set_mount_target {}. libmount::mnt_context_set_target returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_target_namespace<T>(&mut self, path: T) -> Result<(), MountError>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let path_cstr = ffi_utils::as_ref_path_to_c_string(path)?;
log::debug!(
"Mount::set_mount_target_namespace setting mount target namespace: {:?}",
path
);
let result = unsafe { libmount::mnt_context_set_target_ns(self.inner, path_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::set_mount_target_namespace set mount target namespace: {:?}",
path
);
Ok(())
}
code if code == -libc::ENOSYS => {
let err_msg = "`libmount` was compiled without namespace support".to_owned();
log::debug!("Mount::set_mount_target_namespace {}. libmount::mnt_context_set_target returned error code: {:?}", err_msg, code);
Err(MountError::NoNamespaceSupport(err_msg))
}
code => {
let err_msg = format!("failed to set mount target namespace: {:?}", path);
log::debug!("Mount::set_mount_target_namespace {}. libmount::mnt_context_set_target_ns returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_mount_target_prefix<T>(&mut self, path: T) -> Result<(), MountError>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let path_cstr = ffi_utils::as_ref_path_to_c_string(path)?;
log::debug!(
"Mount::set_mount_target_prefix setting mount target prefix: {:?}",
path
);
let result =
unsafe { libmount::mnt_context_set_target_prefix(self.inner, path_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::set_mount_target_prefix set mount target prefix: {:?}",
path
);
Ok(())
}
code => {
let err_msg = format!("failed to set mount target prefix: {:?}", path);
log::debug!("Mount::set_mount_target_prefix {}. libmount::mnt_context_set_target_prefix returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
pub(crate) fn set_userspace_mount_flags(
&mut self,
flags: Vec<UserspaceMountFlag>,
) -> Result<(), MountError> {
log::debug!(
"Mount::set_userspace_mount_flags setting userspace mount flags: {:?}",
flags
);
let bits = flags.iter().fold(0, |acc, &flag| acc | (flag as u64));
let result = unsafe { libmount::mnt_context_set_user_mflags(self.inner, bits) };
match result {
0 => {
log::debug!(
"Mount::set_userspace_mount_flags set userspace mount flags: {:?}",
flags
);
Ok(())
}
code => {
let err_msg = format!("failed to set userspace mount flags: {:?}", flags);
log::debug!("Mount::set_userspace_mount_flags {}. libmount::mnt_context_set_user_mflags returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn builder() -> MountBuilder {
log::debug!("Mount::builder creating new `MountBuilder` instance");
MntBuilder::builder()
}
pub fn set_mount_flags<T>(&mut self, flags: T) -> Result<(), MountError>
where
T: AsRef<[MountFlag]>,
{
let flags = flags.as_ref();
log::debug!("Mount::set_mount_flags setting mount flags: {:?}", flags);
let bits = flags.iter().fold(0, |acc, &flag| acc | (flag as u64));
let result = unsafe { libmount::mnt_context_set_mflags(self.inner, bits) };
match result {
0 => {
log::debug!("Mount::set_mount_flags set mount flags: {:?}", flags);
Ok(())
}
code => {
let err_msg = format!("failed to set mount flags: {:?}", flags);
log::debug!("Mount::set_mount_flags {}. libmount::mnt_context_set_mflags returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn set_file_system_type(&mut self, fs_type: FileSystem) -> Result<(), MountError> {
log::debug!(
"Mount::set_file_system_type setting file system type: {:?}",
fs_type
);
let fs_type_cstr = ffi_utils::as_ref_str_to_c_string(&fs_type)?;
let result = unsafe { libmount::mnt_context_set_fstype(self.inner, fs_type_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::set_file_system_type set file system type: {:?}",
fs_type
);
Ok(())
}
code => {
let err_msg = format!("failed to set file system type: {:?}", fs_type);
log::debug!("Mount::set_file_system_type {}. libmount::mnt_context_set_fstype returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn append_mount_options<T>(&mut self, option_list: T) -> Result<(), MountError>
where
T: AsRef<str>,
{
let option_list = option_list.as_ref();
let opts_cstr = ffi_utils::as_ref_str_to_c_string(option_list)?;
log::debug!(
"Mount::append_mount_options appending mount options: {:?}",
option_list
);
let result =
unsafe { libmount::mnt_context_append_options(self.inner, opts_cstr.as_ptr()) };
match result {
0 => {
log::debug!(
"Mount::append_mount_options appended mount options: {:?}",
option_list
);
Ok(())
}
code => {
let err_msg = format!("failed to append mount options: {:?}", option_list);
log::debug!("Mount::append_mount_options {}. libmount::mnt_context_append_options returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn mount_device(&mut self) -> Result<ExitStatus, MountError> {
log::debug!("Mount::mount_device mounting device");
let return_code = unsafe { libmount::mnt_context_mount(self.inner) };
self.return_code_to_exit_status(return_code)
}
pub fn prepare_mount(&mut self) -> Result<(), MountError> {
log::debug!("Mount::prepare_mount preparing for mount");
let result = unsafe { libmount::mnt_context_prepare_mount(self.inner) };
match result {
0 => {
log::debug!("Mount::prepare_mount preparation successful");
Ok(())
}
code => {
let err_msg = "failed to prepare for device mount".to_owned();
log::debug!("Mount::prepare_mount {}. libmount::mnt_context_prepare_mount returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn call_mount_syscall(&mut self) -> Result<ExitStatus, MountError> {
log::debug!("Mount::call_mount_syscall mounting device");
let return_code = unsafe { libmount::mnt_context_do_mount(self.inner) };
self.return_code_to_exit_status(return_code)
}
pub fn finalize_mount(&mut self) -> Result<(), MountError> {
log::debug!("Mount::finalize_mount finalizing mount");
let result = unsafe { libmount::mnt_context_finalize_mount(self.inner) };
match result {
0 => {
log::debug!("Mount::finalize_mount finalized mount");
Ok(())
}
code => {
let err_msg = "failed to finalize device mount".to_owned();
log::debug!("Mount::finalize_mount {}. libmount::mnt_context_finalize_mount returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
#[doc(hidden)]
fn find_mounted_entry<'a>(
mount: &mut Self,
target: CString,
) -> Result<&'a MountInfoEntry, MountError> {
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
let result = unsafe {
libmount::mnt_context_find_umount_fs(mount.inner, target.as_ptr(), ptr.as_mut_ptr())
};
match result {
0 => {
log::debug!(
"Mount::find_mounted_entry found mount table entry matching {:?}",
target
);
let ptr = unsafe { ptr.assume_init() };
let entry = owning_ref_from_ptr!(mount, MountInfoEntry, ptr);
Ok(entry)
}
code => {
let err_msg = format!("failed to find mount table entry matching {:?}", target);
log::debug!("Mount::find_mounted_entry {}. libmount::mnt_context_find_umount_fs returned error code: {:?}", err_msg, code);
Err(MountError::Action(err_msg))
}
}
}
pub fn find_entry_matching_source<T>(
&mut self,
source: T,
) -> Result<&MountInfoEntry, MountError>
where
T: AsRef<Path>,
{
let source = source.as_ref();
let source_cstr = ffi_utils::as_ref_path_to_c_string(source)?;
log::debug!(
"Mount::find_entry_matching_source finding mounted table entry matching source: {:?}",
source
);
Self::find_mounted_entry(self, source_cstr)
}
pub fn find_entry_matching_target<T>(
&mut self,
target: T,
) -> Result<&MountInfoEntry, MountError>
where
T: AsRef<Path>,
{
let target = target.as_ref();
let target_cstr = ffi_utils::as_ref_path_to_c_string(target)?;
log::debug!(
"Mount::find_entry_matching_target finding mounted table entry matching target: {:?}",
target
);
Self::find_mounted_entry(self, target_cstr)
}
pub fn find_entry_matching_tag(&mut self, tag: &Tag) -> Result<&MountInfoEntry, MountError> {
let tag_cstr = ffi_utils::as_ref_path_to_c_string(tag.to_string())?;
log::debug!(
"Mount::find_entry_matching_tag finding mounted table entry matching tag: {:?}",
tag
);
Self::find_mounted_entry(self, tag_cstr)
}
pub fn set_syscall_exit_status(&mut self, exit_status: i32) -> Result<(), MountError> {
log::debug!(
"Mount::set_syscall_exit_status setting mount syscall exit status to {:?}",
exit_status
);
let result = unsafe { libmount::mnt_context_set_syscall_status(self.inner, exit_status) };
match result {
0 => {
log::debug!(
"Mount::set_syscall_exit_status set mount syscall exit status to {:?}",
exit_status
);
Ok(())
}
code => {
let err_msg = format!(
"ailed to set mount syscall exit status to {:?}",
exit_status
);
log::debug!("Mount::set_syscall_exit_status {}. libmount::mnt_context_set_syscall_status returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn reset_syscall_exit_status(&mut self) -> Result<(), MountError> {
log::debug!("Mount::reset_syscall_exit_status resetting syscall exit status");
let result = unsafe { libmount::mnt_context_reset_status(self.inner) };
match result {
0 => {
log::debug!("Mount::reset_syscall_exit_status reset syscall exit status");
Ok(())
}
code => {
let err_msg = "failed to reset syscall exit status".to_owned();
log::debug!("Mount::reset_syscall_exit_status {}. libmount::mnt_context_reset_status returned error code: {:?}", err_msg, code);
Err(MountError::Config(err_msg))
}
}
}
pub fn switch_to_namespace(&mut self, namespace: MountNamespace) -> Option<MountNamespace<'_>> {
log::debug!("Mount::switch_to_namespace switching namespace");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_ns>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_switch_ns(self.inner, namespace.ptr));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no prior namespace";
log::debug!("Mount::switch_to_namespace {}. libmount::mnt_context_switch_ns returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::switch_to_namespace switched namespace");
let namespace = MountNamespace::from_raw_parts(ptr, self);
Some(namespace)
}
}
}
pub fn switch_to_original_namespace(&mut self) -> Option<MountNamespace<'_>> {
log::debug!("Mount::switch_to_original_namespace switching to original namespace");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_ns>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_switch_origin_ns(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no prior namespace";
log::debug!("Mount::switch_to_original_namespace {}. libmount::mnt_context_switch_origin_ns returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::switch_to_original_namespace switched to original namespace");
let namespace = MountNamespace::from_raw_parts(ptr, self);
Some(namespace)
}
}
}
pub fn switch_to_target_namespace(&mut self) -> Option<MountNamespace<'_>> {
log::debug!("Mount::switch_to_target_namespace switching to target namespace");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_ns>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_switch_target_ns(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no prior namespace";
log::debug!("Mount::switch_to_target_namespace {}. libmount::mnt_context_switch_target_ns returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::switch_to_target_namespace switched to target namespace");
let namespace = MountNamespace::from_raw_parts(ptr, self);
Some(namespace)
}
}
}
pub fn wait_on_children(&mut self) -> ProcessExitStatus {
log::debug!("Mount::wait_on_children waiting on child processes");
let mut children = 0i32;
let mut errors = 0i32;
unsafe {
libmount::mnt_context_wait_for_children(
self.inner,
&mut children as *mut _,
&mut errors as *mut _,
);
}
ProcessExitStatus::new(children as usize, errors as usize)
}
pub fn source(&self) -> Option<String> {
log::debug!("Mount::source getting identifier of device to mount");
let mut identifier = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
identifier.write(libmount::mnt_context_get_source(self.inner));
}
match unsafe { identifier.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get identifier of device to mount";
log::debug!(
"Mount::source {}. libmount::mnt_context_get_source returned a NULL pointer",
err_msg
);
None
}
ptr => {
log::debug!("Mount::source got identifier of device to mount");
let source = ffi_utils::c_char_array_to_string(ptr);
Some(source)
}
}
}
pub fn target(&self) -> Option<PathBuf> {
log::debug!("Mount::target getting mount point");
let mut mount_point = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
mount_point.write(libmount::mnt_context_get_target(self.inner));
}
match unsafe { mount_point.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get mount point";
log::debug!(
"Mount::target {}. libmount::mnt_context_get_target returned a NULL pointer",
err_msg
);
None
}
ptr => {
log::debug!("Mount::target got mount point");
let target = ffi_utils::const_c_char_array_to_path_buf(ptr);
Some(target)
}
}
}
pub fn target_namespace(&self) -> Option<MountNamespace<'_>> {
log::debug!("Mount::target_namespace getting mount point's namespace");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_ns>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_target_ns(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no mount point namespace";
log::debug!("Mount::target_namespace {}. libmount::mnt_context_get_target_ns returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::target_namespace got mount point's namespace");
let ns = MountNamespace::from_raw_parts(ptr, self);
Some(ns)
}
}
}
pub fn target_prefix(&self) -> Option<PathBuf> {
log::debug!("Mount::target_prefix getting mount point prefix");
let mut mount_point = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
mount_point.write(libmount::mnt_context_get_target_prefix(self.inner));
}
match unsafe { mount_point.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get mount point prefix ";
log::debug!("Mount::target_prefix {}. libmount::mnt_context_get_target_prefix returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::target_prefix got mount point prefix ");
let target_prefix = ffi_utils::const_c_char_array_to_path_buf(ptr);
Some(target_prefix)
}
}
}
pub fn internal_table_entry(&self) -> Option<&FsTabEntry> {
log::debug!("Mount::internal_table_entry getting reference to internal mount table entry");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_fs(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get reference to internal mount table entry";
log::debug!(
"Mount::internal_table_entry {}. libmount::mnt_context_get_fs returned a NULL pointer",
err_msg
);
None
}
ptr => {
log::debug!(
"Mount::internal_table_entry got reference to internal mount table entry"
);
let entry = owning_ref_from_ptr!(self, FsTabEntry, ptr);
Some(entry)
}
}
}
pub fn internal_table_entry_user_data(&self) -> Option<NonNull<libc::c_void>> {
log::debug!("Mount::internal_table_entry_user_data getting private user data associated with internal table entry");
let mut ptr = MaybeUninit::<*mut libc::c_void>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_fs_userdata(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg =
"failed to get private user data associated with internal table entry";
log::debug!("Mount::internal_table_entry_user_data {}. libmount::mnt_context_get_fs_userdata returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::internal_table_entry_user_data got private user data associated with internal table entry");
NonNull::new(ptr)
}
}
}
pub fn utab_file_lock(&mut self) -> Option<&mut FileLock> {
log::debug!("Mount::utab_file_lock getting `utab` file lock");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_lock>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_lock(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no `utab` file lock";
log::debug!("Mount::utab_file_lock {}. libmount::mnt_context_get_lock returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::utab_file_lock got `utab` file lock");
let lock = owning_mut_from_ptr!(self, FileLock, ptr);
Some(lock)
}
}
}
pub fn mount_options(&self) -> Option<String> {
log::debug!("Mount::mount_options getting mount options");
let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_options(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get mount options";
log::debug!("Mount::mount_options {}. libmount::mnt_context_get_options returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::mount_options got mount options");
let options = ffi_utils::c_char_array_to_string(ptr);
Some(options)
}
}
}
pub fn mount_options_mode(&self) -> Option<MountOptionsMode> {
log::debug!("Mount::mount_options_mode getting mount options mode");
let result = unsafe { libmount::mnt_context_get_optsmode(self.inner) };
match result {
0 => {
let err_msg = "no mount options mode set";
log::debug!("Mount::mount_options_mode {}. libmount::mnt_context_get_optsmode returned error code: 0", err_msg);
None
}
mode => {
log::debug!("Mount::mount_options_mode got mount options mode");
MountOptionsMode::try_from(mode).ok()
}
}
}
pub fn mount_flags(&self) -> Option<HashSet<MountFlag>> {
log::debug!("Mount::mount_flags getting mount flags");
let mut bits = MaybeUninit::<libc::c_ulong>::zeroed();
let result = unsafe { libmount::mnt_context_get_mflags(self.inner, bits.as_mut_ptr()) };
match result {
0 => {
log::debug!("Mount::mount_flags got mount flags");
let bits = unsafe { bits.assume_init() };
let flags: HashSet<_> = all::<MountFlag>()
.filter(|&flag| (flag as u64) & bits != 0)
.collect();
Some(flags)
}
code => {
let err_msg = "failed to get mount flags";
log::debug!("Mount::mount_flags {}. libmount::mnt_context_get_mflags returned error code: {:?}", err_msg, code);
None
}
}
}
pub fn mount_helper_exit_status(&self) -> i32 {
let status = unsafe { libmount::mnt_context_get_helper_status(self.inner) };
log::debug!("Mount::mount_helper_exit_status value: {:?}", status);
status
}
pub fn mount_syscall_errno(&self) -> Option<i32> {
log::debug!("Mount::mount_syscall_errno getting mount(2) syscall error number");
let result = unsafe { libmount::mnt_context_get_syscall_errno(self.inner) };
match result {
0 => {
let err_msg = "mount(2) syscall was never invoked, or ran successfully";
log::debug!("Mount::mount_syscall_errno {}. libmount::mnt_context_get_syscall_errno returned error code: 0", err_msg);
None
}
errno => {
log::debug!("Mount::mount_syscall_errno got mount(2) syscall error number");
Some(errno)
}
}
}
pub fn userspace_mount_flags(&self) -> Option<HashSet<UserspaceMountFlag>> {
log::debug!("Mount::userspace_mount_flags getting user space mount flags");
let mut bits = MaybeUninit::<libc::c_ulong>::zeroed();
let result =
unsafe { libmount::mnt_context_get_user_mflags(self.inner, bits.as_mut_ptr()) };
match result {
0 => {
log::debug!("Mount::userspace_mount_flags got user space mount flags");
let bits = unsafe { bits.assume_init() };
let flags: HashSet<_> = all::<UserspaceMountFlag>()
.filter(|&flag| (flag as u64) & bits != 0)
.collect();
Some(flags)
}
code => {
let err_msg = "failed to get user space mount flags";
log::debug!("Mount::userspace_mount_flags {}. libmount::mnt_context_get_user_mflags returned error code: {:?}", err_msg, code);
None
}
}
}
pub fn cache(&self) -> Option<&Cache> {
log::debug!("Mount::cache getting associated cache");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_cache>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_cache(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get associated cache";
log::debug!(
"Mount::cache {}. libmount::mnt_context_get_cache returned a NULL pointer",
err_msg
);
None
}
ptr => {
log::debug!("Mount::cache got associated cache");
let cache = owning_ref_from_ptr!(self, Cache, ptr);
Some(cache)
}
}
}
pub fn fstab(&self) -> Option<FsTab> {
log::debug!("Mount::fstab getting file system description file `fstab`");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_table>::zeroed();
let result = unsafe { libmount::mnt_context_get_fstab(self.inner, ptr.as_mut_ptr()) };
match result {
0 => {
log::debug!("Mount::fstab got file system description file `fstab`");
let ptr = unsafe { ptr.assume_init() };
let table = FsTab::from_ptr(ptr);
Some(table)
}
code => {
let err_msg = "failed to get file system description file `fstab`";
log::debug!(
"Mount::fstab {}. libmount::mnt_context_get_fstab returned error code: {:?}",
err_msg,
code
);
None
}
}
}
pub fn fstab_user_data(&self) -> Option<NonNull<libc::c_void>> {
log::debug!("Mount::fstab_user_data getting `fstab` associated user data");
let mut ptr = MaybeUninit::<*mut libc::c_void>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_fstab_userdata(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no associated user data";
log::debug!("Mount::fstab_user_data {}. libmount::mnt_context_get_fstab_userdata returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::fstab_user_data got `fstab` associated user data");
NonNull::new(ptr)
}
}
}
pub fn file_system_type(&self) -> Option<FileSystem> {
log::debug!("Mount::file_system_type getting file system type");
let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_fstype(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to get file system type";
log::debug!("Mount::file_system_type {}. libmount::mnt_context_get_fstype returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::file_system_type got file system type");
let fs_type_cstr = ffi_utils::c_char_array_to_string(ptr);
match FileSystem::from_str(fs_type_cstr.as_ref()) {
Ok(fs_type) => Some(fs_type),
Err(e) => {
log::debug!("Mount::file_system_type {:?}", e);
None
}
}
}
}
}
pub fn mountinfo(&self) -> Option<MountInfo> {
log::debug!("Mount::mountinfo getting `mountinfo` table");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_table>::zeroed();
let result = unsafe { libmount::mnt_context_get_mtab(self.inner, ptr.as_mut_ptr()) };
match result {
0 => {
log::debug!("Mount::mountinfo got `mountinfo` table");
let ptr = unsafe { ptr.assume_init() };
let table = MountInfo::from_ptr(ptr);
Some(table)
}
code => {
let err_msg = "failed to get `mountinfo` table";
log::debug!(
"Mount::mountinfo {}. libmount::mnt_context_get_mtab returned error code: {:?}",
err_msg,
code
);
None
}
}
}
pub fn mountinfo_user_data(&self) -> Option<NonNull<libc::c_void>> {
log::debug!("Mount::mountinfo_user_data getting `mountinfo` associated user data");
let mut ptr = MaybeUninit::<*mut libc::c_void>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_mtab_userdata(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no associated user data";
log::debug!("Mount::mountinfo_user_data {}. libmount::mnt_context_get_mtab_userdata returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::mountinfo_user_data got `mountinfo` associated user data");
NonNull::new(ptr)
}
}
}
pub fn original_namespace(&self) -> Option<MountNamespace<'_>> {
log::debug!("Mount::original_namespace getting mount namespace");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_ns>::zeroed();
unsafe {
ptr.write(libmount::mnt_context_get_origin_ns(self.inner));
}
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "found no original namespace";
log::debug!("Mount::original_namespace {}. libmount::mnt_context_get_origin_ns returned a NULL pointer", err_msg);
None
}
ptr => {
log::debug!("Mount::original_namespace got mount namespace");
let ns = MountNamespace::from_raw_parts(ptr, self);
Some(ns)
}
}
}
pub fn seq_mount(&mut self) -> MountIter<'_> {
MountIter::new(self).unwrap()
}
pub fn seq_remount(&mut self) -> ReMountIter<'_> {
ReMountIter::new(self).unwrap()
}
pub fn is_entry_mounted(&self, entry: &FsTabEntry) -> bool {
log::debug!("Mount::is_entry_mounted checking if mount table entry was mounted");
let mut status = MaybeUninit::<libc::c_int>::zeroed();
let result = unsafe {
libmount::mnt_context_is_fs_mounted(self.inner, entry.inner, status.as_mut_ptr())
};
match result {
0 => {
log::debug!("Mount::is_entry_mounted checked if mount table entry was mounted");
let status = unsafe { status.assume_init() };
status == 1
}
code => {
let err_msg = "failed to check if mount table entry was mounted";
log::debug!("Mount::is_entry_mounted {}. libmount::mnt_context_is_fs_mounted returned error code: {:?}", err_msg, code);
false
}
}
}
pub fn is_parent_process(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_parent(self.inner) == 1 };
log::debug!("Mount::is_parent_process value: {:?}", state);
state
}
pub fn is_child_process(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_child(self.inner) == 1 };
log::debug!("Mount::is_child_process value: {:?}", state);
state
}
pub fn is_dry_run(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_fake(self.inner) == 1 };
log::debug!("Mount::is_dry_run value: {:?}", state);
state
}
pub fn is_verbose(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_verbose(self.inner) == 1 };
log::debug!("Mount::is_verbose value: {:?}", state);
state
}
pub fn is_user_mount(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_restricted(self.inner) == 1 };
log::debug!("Mount::is_user_mount value: {:?}", state);
state
}
pub fn disabled_path_canonicalization(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_nocanonicalize(self.inner) == 1 };
log::debug!("Mount::disabled_path_canonicalization value: {:?}", state);
state
}
pub fn disabled_mount_point_lookup(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_swapmatch(self.inner) == 1 };
log::debug!("Mount::disabled_mount_point_lookup value: {:?}", state);
state
}
pub fn has_called_mount_syscall(&self) -> bool {
let state = unsafe { libmount::mnt_context_syscall_called(self.inner) == 1 };
log::debug!("Mount::has_called_mount_syscall value: {:?}", state);
state
}
pub fn has_disabled_helpers(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_nohelpers(self.inner) == 1 };
log::debug!("Mount::has_disabled_helpers value: {:?}", state);
state
}
pub fn has_run_mount_helper(&self) -> bool {
let state = unsafe { libmount::mnt_context_helper_executed(self.inner) == 1 };
log::debug!("Mount::has_run_mount_helper value: {:?}", state);
state
}
pub fn is_mount_successful(&self) -> bool {
let state = unsafe { libmount::mnt_context_get_status(self.inner) == 1 };
log::debug!("Mount::is_mount_successful value: {:?}", state);
state
}
pub fn is_mounted_read_only(&self) -> bool {
let state = unsafe { libmount::mnt_context_forced_rdonly(self.inner) == 1 };
log::debug!("Mount::is_mounted_read_only value: {:?}", state);
state
}
pub fn does_not_update_utab(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_nohelpers(self.inner) == 1 };
log::debug!("Mount::does_not_update_utab value: {:?}", state);
state
}
pub fn does_parallel_mount(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_fork(self.inner) == 1 };
log::debug!("Mount::does_parallel_mount value: {:?}", state);
state
}
pub fn forces_mount_read_write(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_rwonly_mount(self.inner) == 1 };
log::debug!("Mount::forces_mount_read_write value: {:?}", state);
state
}
pub fn ignores_unsupported_mount_options(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_sloppy(self.inner) == 1 };
log::debug!(
"Mount::ignores_unsupported_mount_options value: {:?}",
state
);
state
}
#[cfg(mount = "v2_39")]
pub fn mounts_only_once(&self) -> bool {
let state = unsafe { libmount::mnt_context_is_onlyonce(self.inner) == 1 };
log::debug!("Mount::mounts_only_once value: {:?}", state);
state
}
}
impl AsRef<Mount> for Mount {
fn as_ref(&self) -> &Mount {
self
}
}
impl Drop for Mount {
fn drop(&mut self) {
log::debug!("Mount::drop deallocating `Mount` instance");
unsafe {
libmount::mnt_free_context(self.inner);
}
while let Some(gc_item) = self.gc.pop() {
gc_item.destroy();
}
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use pretty_assertions::{assert_eq, assert_ne};
use tempfile::Builder;
use tempfile::NamedTempFile;
use std::fs::File;
use std::io::Write;
use super::*;
use crate::core::device::BlockDevice;
use crate::mount::ExitCode;
static BASE_DIR_TEST_IMG_FILES: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/third-party/vendor/util-linux/blkid/images"
);
fn decode_into<P, W>(xz_file_path: P, writer: &mut W) -> std::io::Result<u64>
where
P: AsRef<Path>,
W: Write + ?Sized,
{
let xz_file_path = xz_file_path.as_ref();
let compressed_image_file = std::fs::File::open(xz_file_path)?;
let mut decompressed = xz2::read::XzDecoder::new(compressed_image_file);
std::io::copy(&mut decompressed, writer)
}
fn disk_image(fs_type: &str) -> NamedTempFile {
let img_path = format!("{BASE_DIR_TEST_IMG_FILES}/filesystems/{fs_type}.img.xz");
let mut named_file = NamedTempFile::new().expect("failed to get new NamedTempFile");
decode_into(img_path, named_file.as_file_mut()).expect("failed to create named disk image");
named_file
}
#[test]
fn mount_can_mount_an_image_file() -> crate::Result<()> {
if inside_vm::inside_vm() {
let image_file = disk_image("ext3");
let source = BlockDevice::from(image_file.path());
let tmp_dir = Builder::new().prefix("rsmount-test-").tempdir().unwrap();
let mut mount = Mount::builder()
.source(source)
.target(tmp_dir.path())
.build()?;
let status = mount.mount_device()?;
let actual = mount.is_mount_successful();
let expected = true;
assert_eq!(actual, expected);
let actual = status.exit_code();
let expected = ExitCode::Success;
assert_eq!(actual, &expected);
}
Ok(())
}
}