use std::cmp::Ordering;
use std::fmt;
use std::mem::MaybeUninit;
use std::ops::Index;
use std::path::Path;
use crate::core::cache::Cache;
use crate::core::device::Source;
use crate::core::entries::FsTabEntry;
use crate::core::entries::MountInfoEntry;
use crate::core::errors::MountInfoChildIterError;
use crate::core::errors::MountInfoError;
use crate::core::errors::MountInfoIterError;
use crate::core::iter::Direction;
use crate::core::iter::GenIterator;
use crate::core::iter::MountInfoChildIter;
use crate::core::iter::MountInfoIter;
use crate::core::iter::MountInfoOvermountIter;
use crate::owning_ref_from_ptr;
use crate::tables::GcItem;
use crate::tables::MountOption;
use crate::tables::ParserFlow;
use crate::ffi_utils;
#[derive(Debug)]
pub struct MountInfo {
pub(crate) inner: *mut libmount::libmnt_table,
pub(crate) gc: Vec<GcItem>,
}
impl Drop for MountInfo {
fn drop(&mut self) {
log::debug!("MountInfo::drop deallocating `MountInfo` instance");
unsafe { libmount::mnt_unref_table(self.inner) };
self.collect_garbage();
}
}
impl AsRef<MountInfo> for MountInfo {
#[inline]
fn as_ref(&self) -> &MountInfo {
self
}
}
impl Index<usize> for MountInfo {
type Output = MountInfoEntry;
fn index(&self, index: usize) -> &Self::Output {
log::debug!("MountInfo::index getting item at index: {:?}", index);
#[cold]
#[inline(never)]
#[track_caller]
fn indexing_failed() -> ! {
panic!("Index out of bounds");
}
let mut iter = MountInfoIter::new(self).unwrap();
match iter.nth(index) {
Some(item) => item,
None => indexing_failed(),
}
}
}
impl<'a> IntoIterator for &'a MountInfo {
type Item = &'a MountInfoEntry;
type IntoIter = MountInfoIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl MountInfo {
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn incr_ref_counter(&mut self) {
unsafe { libmount::mnt_ref_table(self.inner) }
}
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn decr_ref_counter(&mut self) {
unsafe { libmount::mnt_unref_table(self.inner) }
}
#[doc(hidden)]
pub(crate) fn from_ptr(ptr: *mut libmount::libmnt_table) -> MountInfo {
Self {
inner: ptr,
gc: vec![],
}
}
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn borrow_ptr(ptr: *mut libmount::libmnt_table) -> MountInfo {
let mut table = Self::from_ptr(ptr);
table.incr_ref_counter();
table
}
pub fn new() -> Result<MountInfo, MountInfoError> {
log::debug!("MountInfo::new creating a new `MountInfo` instance");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_table>::zeroed();
unsafe { ptr.write(libmount::mnt_new_table()) };
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = "failed to create a new `MountInfo`".to_owned();
log::debug!(
"MountInfo::new {err_msg}. libmount::mnt_new_table returned a NULL pointer"
);
Err(MountInfoError::Creation(err_msg))
}
ptr => {
log::debug!("MountInfo::new created a new `MountInfo` instance");
let table = Self::from_ptr(ptr);
Ok(table)
}
}
}
pub fn cache(&self) -> Option<&Cache> {
log::debug!("MountInfo::cache getting associated path and tag cache");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_cache>::zeroed();
unsafe { ptr.write(libmount::mnt_table_get_cache(self.inner)) };
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
log::debug!("MountInfo::cache failed to get associated path and tag cache. libmount::mnt_table_get_cache returned a NULL pointer");
None
}
ptr => {
log::debug!("MountInfo::cache got associated path and tag cache");
let cache = owning_ref_from_ptr!(self, Cache, ptr);
Some(cache)
}
}
}
pub fn root(&self) -> Option<&MountInfoEntry> {
log::debug!("MountInfo::root getting entry matching file system root");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
let result = unsafe { libmount::mnt_table_get_root_fs(self.inner, ptr.as_mut_ptr()) };
match result {
0 => {
log::debug!("MountInfo::root got entry matching file system root");
let ptr = unsafe { ptr.assume_init() };
let entry = owning_ref_from_ptr!(self, MountInfoEntry, ptr);
Some(entry)
}
code => {
log::debug!("MountInfo::root failed to get entry matching file system root. libmount::mnt_table_get_root_fs returned error code: {:?}", code);
None
}
}
}
pub fn first(&self) -> Option<&MountInfoEntry> {
log::debug!("MountInfo::first getting reference to first table entry");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
let result = unsafe { libmount::mnt_table_first_fs(self.inner, ptr.as_mut_ptr()) };
match result {
0 => {
log::debug!("MountInfo::first got reference to first table entry");
let ptr = unsafe { ptr.assume_init() };
let entry = owning_ref_from_ptr!(self, MountInfoEntry, ptr);
Some(entry)
}
code => {
log::debug!( "MountInfo::first failed to get reference to first table entry. libmount::mnt_table_first_fs returned error code: {code:?}");
None
}
}
}
pub fn last(&self) -> Option<&MountInfoEntry> {
log::debug!("MountInfo::last getting reference to last table entry");
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
let result = unsafe { libmount::mnt_table_last_fs(self.inner, ptr.as_mut_ptr()) };
match result {
0 => {
log::debug!("MountInfo::last got reference to last table entry");
let ptr = unsafe { ptr.assume_init() };
let entry = owning_ref_from_ptr!(self, MountInfoEntry, ptr);
Some(entry)
}
code => {
log::debug!( "MountInfo::last failed to get reference to last table entry. libmount::mnt_table_last_fs returned error code: {code:?}");
None
}
}
}
pub fn position(&self, entry: &MountInfoEntry) -> Option<usize> {
log::debug!("MountInfo::position searching for an entry in the table");
let result = unsafe { libmount::mnt_table_find_fs(self.inner, entry.inner) };
match result {
index if index > 0 => {
log::debug!(
"MountInfo::position mount table contains entry at index: {:?}",
index
);
Some(index as usize)
}
code => {
log::debug!( "MountInfo::position no matching entry in table: libmount::mnt_table_find_fs returned error code: {code:?}");
None
}
}
}
pub fn len(&self) -> usize {
let len = unsafe { libmount::mnt_table_get_nents(self.inner) };
log::debug!("MountInfo::len value: {:?}", len);
len as usize
}
pub fn get(&self, index: usize) -> Option<&MountInfoEntry> {
log::debug!(
"MountInfo::get_mut getting reference of item at index: {:?}",
index
);
MountInfoIter::new(self)
.ok()
.and_then(|mut iter| iter.nth(index))
}
#[doc(hidden)]
fn find_first_entry<'a, P>(
table: &mut Self,
iterator: *mut libmount::libmnt_iter,
predicate: P,
) -> Option<&'a MountInfoEntry>
where
P: FnMut(&MountInfoEntry) -> bool,
{
#[doc(hidden)]
unsafe extern "C" fn callback<P>(
entry_ptr: *mut libmount::libmnt_fs,
predicate_fn_ptr: *mut libc::c_void,
) -> libc::c_int
where
P: FnMut(&MountInfoEntry) -> bool,
{
let entry = MountInfoEntry::borrow_ptr(entry_ptr);
let predicate_fn = &mut *(predicate_fn_ptr as *mut P);
match predicate_fn(&entry) {
true => 1,
false => 0,
}
}
let data = Box::into_raw(Box::new(predicate));
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
let result = unsafe {
libmount::mnt_table_find_next_fs(
table.inner,
iterator,
Some(callback::<P>),
data as *mut _,
ptr.as_mut_ptr(),
)
};
match result {
0 => {
let _predicate = unsafe { Box::from_raw(data) };
log::debug!(
"MountInfo::find_first_entry found first `MountInfoEntry` matching predicate"
);
let entry_ptr = unsafe { ptr.assume_init() };
let entry = owning_ref_from_ptr!(table, MountInfoEntry, entry_ptr);
Some(entry)
}
code => {
let _predicate = unsafe { Box::from_raw(data) };
let err_msg = "failed to find `MountInfoEntry` matching predicate".to_owned();
log::debug!( "MountInfo::find_first_entry {err_msg}. libmount::mnt_table_find_next_fs returned error code: {code:?}");
None
}
}
}
pub fn find_first<P>(&mut self, predicate: P) -> Option<&MountInfoEntry>
where
P: FnMut(&MountInfoEntry) -> bool,
{
log::debug!( "MountInfo::find_first finding first table entry matching predicate while iterating Forward");
GenIterator::new(Direction::Forward)
.ok()
.and_then(|iterator| MountInfo::find_first_entry(self, iterator.inner, predicate))
}
pub fn find_back_first<P>(&mut self, predicate: P) -> Option<&MountInfoEntry>
where
P: FnMut(&MountInfoEntry) -> bool,
{
log::debug!( "MountInfo::find_back_first finding first table entry matching predicate while iterating Backward");
GenIterator::new(Direction::Backward)
.ok()
.and_then(|iterator| MountInfo::find_first_entry(self, iterator.inner, predicate))
}
#[doc(hidden)]
fn lookup_source<'a>(
table: &mut Self,
direction: Direction,
source: &Source,
) -> Option<&'a MountInfoEntry> {
let source_cstr = ffi_utils::as_ref_str_to_c_string(source.to_string()).ok()?;
let source_ptr = if source.is_pseudo_fs() {
std::ptr::null()
} else {
source_cstr.as_ptr()
};
log::debug!(
"MountInfo::lookup_source searching {:?} for entry matching source {:?}",
direction,
source
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_source(
table.inner,
source_ptr,
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!(
"failed to find entry matching source {:?} while searching {:?}",
source, direction
);
log::debug!( "MountInfo::lookup_source {err_msg}. libmount::mnt_table_find_source returned a NULL pointer");
None
}
ptr => {
log::debug!(
"MountInfo::lookup_source found entry matching source {:?} while searching {:?}",
source,
direction
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_source(&mut self, source: &Source) -> Option<&MountInfoEntry> {
let direction = Direction::Forward;
log::debug!(
"MountInfo::find_source searching {:?} for the first entry with a source matching {:?}",
direction,
source
);
Self::lookup_source(self, direction, source)
}
pub fn find_back_source(&mut self, source: &Source) -> Option<&MountInfoEntry> {
let direction = Direction::Backward;
log::debug!(
"MountInfo::find_back_source searching {:?} for the first entry with a source matching {:?}",
direction,
source
);
Self::lookup_source(self, direction, source)
}
#[doc(hidden)]
fn lookup_source_path<'a>(
table: &mut Self,
direction: Direction,
path: &Path,
) -> Option<&'a MountInfoEntry> {
let path_cstr = ffi_utils::as_ref_path_to_c_string(path).ok()?;
let path_ptr = if path_cstr.is_empty() {
std::ptr::null()
} else {
path_cstr.as_ptr()
};
log::debug!(
"MountInfo::lookup_source_path searching {:?} for entry matching source path {:?}",
direction,
path
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_srcpath(
table.inner,
path_ptr,
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!(
"failed to find entry matching source path {:?} while searching {:?}",
path, direction
);
log::debug!( "MountInfo::lookup_source_path {err_msg}. libmount::mnt_table_find_srcpath returned a NULL pointer");
None
}
ptr => {
log::debug!(
"MountInfo::lookup_source_path found entry matching source path {:?} while searching {:?}",
path,
direction
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_source_path<T>(&mut self, path: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Forward;
log::debug!(
"MountInfo::find_source_path searching {:?} for the first entry with a source matching {:?}",
direction,
path
);
Self::lookup_source_path(self, direction, path)
}
pub fn find_back_source_path<T>(&mut self, path: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Backward;
log::debug!(
"MountInfo::find_back_source_path searching {:?} for the first entry with a source matching {:?}",
direction,
path
);
Self::lookup_source_path(self, direction, path)
}
#[doc(hidden)]
fn lookup_device<'a>(
table: &mut Self,
direction: Direction,
device_number: u64,
) -> Option<&'a MountInfoEntry> {
log::debug!(
"MountInfo::lookup_device searching {:?} for device numbered {:?}",
direction,
device_number
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_devno(
table.inner,
device_number,
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
log::debug!("MountInfo::lookup_device found no device with number {:?} while searching {:?}. libmount::mnt_table_find_devno returned a NULL pointer", device_number, direction);
None
}
ptr => {
log::debug!(
"MountInfo::lookup_device found entry for device number {:?}",
device_number
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_device(&mut self, device_number: u64) -> Option<&MountInfoEntry> {
log::debug!("MountInfo::find_device searching from top to bottom for entry matching device number {:?}", device_number);
Self::lookup_device(self, Direction::Forward, device_number)
}
pub fn find_back_device(&mut self, device_number: u64) -> Option<&MountInfoEntry> {
log::debug!("MountInfo::find_back_device searching from bottom to top for entry matching device number {:?}", device_number);
Self::lookup_device(self, Direction::Backward, device_number)
}
pub fn distinct_first_by<F>(&mut self, cmp: F) -> Result<(), MountInfoError>
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
log::debug!(
"MountInfo::distinct_first_by merging matching entries to the first occurrence"
);
Self::filter_by(
self,
libmount::MNT_UNIQ_FORWARD | libmount::MNT_UNIQ_KEEPTREE,
cmp,
)
}
pub fn distinct_last_by<F>(&mut self, cmp: F) -> Result<(), MountInfoError>
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
log::debug!("MountInfo::distinct_last_by merging matching entries to the last occurrence");
Self::filter_by(self, libmount::MNT_UNIQ_KEEPTREE, cmp)
}
#[doc(hidden)]
fn lookup_target<'a>(
table: &mut Self,
direction: Direction,
path: &Path,
) -> Option<&'a MountInfoEntry> {
let path_cstr = ffi_utils::as_ref_path_to_c_string(path).ok()?;
log::debug!(
"MountInfo::lookup_target searching {:?} for entry matching target {:?}",
direction,
path
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_target(
table.inner,
path_cstr.as_ptr(),
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!(
"failed to find entry matching target {:?} while searching {:?}",
path, direction
);
log::debug!( "MountInfo::lookup_target {err_msg}. libmount::mnt_table_find_target returned a NULL pointer");
None
}
ptr => {
log::debug!(
"MountInfo::lookup_target found entry matching target {:?} while searching {:?}",
path,
direction
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
#[doc(hidden)]
fn lookup_mount_point<'a>(
table: &mut Self,
direction: Direction,
mount_point: &Path,
) -> Option<&'a MountInfoEntry> {
let mount_point_cstr = ffi_utils::as_ref_path_to_c_string(mount_point).ok()?;
log::debug!(
"MountInfo::lookup_mount_point searching {:?} for entry with mount point {:?}",
direction,
mount_point
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_mountpoint(
table.inner,
mount_point_cstr.as_ptr(),
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
log::debug!( "MountInfo::lookup_mount_point found no entry with mount point {:?} while searching {:?}. libmount::mnt_table_find_mountpoint returned a NULL pointer", mount_point, direction);
None
}
ptr => {
log::debug!(
"MountInfo::lookup_mount_point found entry with mount point {:?}",
mount_point
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_mount_point<T>(&mut self, mount_point: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let mount_point = mount_point.as_ref();
log::debug!( "MountInfo::find_mount_point searching table from top to bottom for entry matching mount point {:?}", mount_point);
Self::lookup_mount_point(self, Direction::Forward, mount_point)
}
pub fn find_back_mount_point<T>(&mut self, mount_point: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let mount_point = mount_point.as_ref();
log::debug!( "MountInfo::find_back_mount_point searching table from bottom to top for entry matching mount point {:?}", mount_point);
Self::lookup_mount_point(self, Direction::Backward, mount_point)
}
pub fn find_target<T>(&mut self, path: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Forward;
log::debug!(
"MountInfo::find_target searching {:?} for the first entry with a target matching {:?}",
direction,
path
);
Self::lookup_target(self, direction, path)
}
pub fn find_back_target<T>(&mut self, path: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Backward;
log::debug!(
"MountInfo::find_back_target searching {:?} for the first entry with a target matching {:?}",
direction,
path
);
Self::lookup_target(self, direction, path)
}
#[doc(hidden)]
fn lookup_target_with_options<'a>(
table: &mut Self,
direction: Direction,
path: &Path,
option_name: &str,
option_value: Option<&str>,
) -> Option<&'a MountInfoEntry> {
let option_value = option_value.map_or_else(String::new, |value| value.to_owned());
let path_cstr = ffi_utils::as_ref_path_to_c_string(path).ok()?;
let opt_name_cstr = ffi_utils::as_ref_str_to_c_string(option_name).ok()?;
let opt_value_cstr = ffi_utils::as_ref_str_to_c_string(&option_value).ok()?;
let opt_value_ptr = if opt_value_cstr.is_empty() {
std::ptr::null()
} else {
opt_value_cstr.as_ptr()
};
let opt_value = if option_value.is_empty() {
option_value
} else {
format!(" with value {:?}", option_value)
};
log::debug!(
"MountInfo::lookup_target_with_options searching {:?} for entry matching the combination of path {:?} and option {:?}{}",
direction,
path,
option_name,
opt_value
);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_target_with_option(
table.inner,
path_cstr.as_ptr(),
opt_name_cstr.as_ptr(),
opt_value_ptr,
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!("found no entry matching the combination of path {:?} and option {:?}{} while searching {:?}", path, option_name, opt_value, direction );
log::debug!( "MountInfo::lookup_target_with_options {err_msg}. libmount::mnt_table_find_target_with_option returned a NULL pointer");
None
}
ptr => {
log::debug!(
"MountInfo::lookup_target_with_options found entry matching the combination of path {:?} and option {:?}{}",
path,
option_name,
opt_value
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_target_with_option<P, T>(
&mut self,
path: P,
option_name: T,
) -> Option<&MountInfoEntry>
where
P: AsRef<Path>,
T: AsRef<str>,
{
let path = path.as_ref();
let option_name = option_name.as_ref();
let direction = Direction::Forward;
log::debug!( "MountInfo::find_target_with_option searching {:?} for entry matching the combination of path {:?} and option {:?}", direction, path, option_name);
Self::lookup_target_with_options(self, direction, path, option_name, None)
}
pub fn find_back_target_with_option<P, T>(
&mut self,
path: P,
option_name: T,
) -> Option<&MountInfoEntry>
where
P: AsRef<Path>,
T: AsRef<str>,
{
let path = path.as_ref();
let option_name = option_name.as_ref();
let direction = Direction::Backward;
log::debug!( "MountInfo::find_back_target_with_option searching {:?} for entry matching the combination of path {:?} and option {:?}", direction, path, option_name);
Self::lookup_target_with_options(self, direction, path, option_name, None)
}
pub fn find_target_with_exact_option<P, T>(
&mut self,
path: P,
option: &MountOption,
) -> Option<&MountInfoEntry>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Forward;
log::debug!( "MountInfo::find_target_with_option searching {:?} for entry matching the combination of path {:?} and option {:?}", direction, path, option);
Self::lookup_target_with_options(self, direction, path, option.name(), option.value())
}
pub fn find_back_target_with_exact_option<P, T>(
&mut self,
path: P,
option: &MountOption,
) -> Option<&MountInfoEntry>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let direction = Direction::Backward;
log::debug!( "MountInfo::find_back_target_with_option searching {:?} for entry matching the combination of path {:?} and option {:?}", direction, path, option);
Self::lookup_target_with_options(self, direction, path, option.name(), option.value())
}
#[doc(hidden)]
fn lookup_pair<'a>(
table: &mut Self,
direction: Direction,
source: &Source,
target: &Path,
) -> Option<&'a MountInfoEntry> {
let source_cstr = ffi_utils::as_ref_str_to_c_string(source.to_string()).ok()?;
let target_cstr = ffi_utils::as_ref_path_to_c_string(target).ok()?;
log::debug!( "MountInfo::lookup_pair searching {:?} for entry matching source/target pair {:?} / {:?}", direction, source, target);
let mut ptr = MaybeUninit::<*mut libmount::libmnt_fs>::zeroed();
unsafe {
ptr.write(libmount::mnt_table_find_pair(
table.inner,
source_cstr.as_ptr(),
target_cstr.as_ptr(),
direction as i32,
))
};
match unsafe { ptr.assume_init() } {
ptr if ptr.is_null() => {
let err_msg = format!(
"found no entry with source/target pair {:?} / {:?} while searching {:?}",
source, target, direction,
);
log::debug!( "MountInfo::lookup_pair {err_msg}. libmount::mnt_table_find_pair returned a NULL pointer");
None
}
ptr => {
log::debug!(
"MountInfo::lookup_pair found entry matching source/target pair {:?} / {:?}",
source,
target
);
let entry = owning_ref_from_ptr!(table, MountInfoEntry, ptr);
Some(entry)
}
}
}
pub fn find_pair<T>(&mut self, source: &Source, target: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let target = target.as_ref();
log::debug!( "MountInfo::find_pair searching table from top to bottom for entry with source/target pair {:?} / {:?}", source, target);
Self::lookup_pair(self, Direction::Forward, source, target)
}
pub fn find_back_pair<T>(&mut self, source: &Source, target: T) -> Option<&MountInfoEntry>
where
T: AsRef<Path>,
{
let target = target.as_ref();
log::debug!( "MountInfo::find_back_pair searching table from bottom to top for entry with source/target pair {:?} / {:?}", source, target);
Self::lookup_pair(self, Direction::Backward, source, target)
}
pub fn iter(&self) -> MountInfoIter<'_> {
log::debug!("MountInfo::iter creating a new `MountInfoIter`");
MountInfoIter::new(self).unwrap()
}
pub fn try_iter(&self) -> Result<MountInfoIter<'_>, MountInfoIterError> {
log::debug!("MountInfo::try_iter creating a new `MountInfoIter`");
MountInfoIter::new(self)
}
pub fn iter_children<'table>(
&'table self,
parent: &'table MountInfoEntry,
) -> MountInfoChildIter<'table> {
log::debug!("MountInfo::iter_children creating a new `MountInfoChildIter`");
MountInfoChildIter::new(self, parent).unwrap()
}
pub fn try_iter_children<'table>(
&'table self,
parent: &'table MountInfoEntry,
) -> Result<MountInfoChildIter<'table>, MountInfoChildIterError> {
log::debug!("MountInfo::try_iter_children creating a new `MountInfoChildIter`");
MountInfoChildIter::new(self, parent)
}
pub fn iter_overmounts<'table>(
&'table self,
entry: &'table MountInfoEntry,
) -> MountInfoOvermountIter<'table> {
log::debug!("MountInfo::iter_overmounts creating a new `MountInfoOvermountIter`");
MountInfoOvermountIter::new(self, entry)
}
pub fn set_parser_error_handler<F>(&mut self, err_handler: F) -> Result<(), MountInfoError>
where
F: Fn(&str, usize) -> ParserFlow,
{
log::debug!("MountInfo::set_parser_error_handler setting up parser error handler");
#[doc(hidden)]
unsafe extern "C" fn parser_callback<F>(
table: *mut libmount::libmnt_table,
file_name: *const libc::c_char,
line: libc::c_int,
) -> libc::c_int
where
F: Fn(&str, usize) -> ParserFlow,
{
let file_name = ffi_utils::const_char_array_to_str_ref(file_name)
.ok()
.unwrap_or("");
let mut callback_ptr = MaybeUninit::<*mut libc::c_void>::zeroed();
unsafe {
callback_ptr.write(libmount::mnt_table_get_userdata(table));
}
let callback_ptr = unsafe { callback_ptr.assume_init() };
let handler = &mut *(callback_ptr as *mut F);
handler(file_name, line as usize) as i32
}
let user_data = Box::into_raw(Box::new(err_handler));
let result = unsafe { libmount::mnt_table_set_userdata(self.inner, user_data as *mut _) };
match result {
0 => {
let result = unsafe {
libmount::mnt_table_set_parser_errcb(self.inner, Some(parser_callback::<F>))
};
match result {
0 => {
log::debug!(
"MountInfo::set_parser_error_handler set up parser error handler"
);
Ok(())
}
code => {
let err_msg = "failed to set parser syntax error handler".to_owned();
log::debug!( "MountInfo::set_parser_error_handler {err_msg}. libmount::mnt_table_set_parser_errcb returned error code: {code:?}");
let _ = unsafe { Box::from_raw(user_data) };
Err(MountInfoError::Config(err_msg))
}
}
}
code => {
let err_msg = "failed to set error handler as userdata".to_owned();
log::debug!( "MountInfo::set_parser_error_handler {err_msg}. libmount::mnt_table_set_userdata returned error code: {code:?}");
let _ = unsafe { Box::from_raw(user_data) };
Err(MountInfoError::Config(err_msg))
}
}
}
pub fn set_cache(&mut self, cache: Cache) -> Result<(), MountInfoError> {
log::debug!("MountInfo::set_cache setting up a cache of paths and tags");
unsafe {
libmount::mnt_ref_cache(cache.inner);
}
let result = unsafe { libmount::mnt_table_set_cache(self.inner, cache.inner) };
match result {
0 => {
log::debug!("MountInfo::set_cache set up a cache of paths and tags");
Ok(())
}
code => {
let err_msg = "failed to set up a cache of paths and tags".to_owned();
log::debug!( "MountInfo::set_cache {err_msg}. libmount::mnt_table_set_cache returned error code: {code:?}");
Err(MountInfoError::Config(err_msg))
}
}
}
fn collect_garbage(&mut self) {
while let Some(gc_item) = self.gc.pop() {
gc_item.destroy();
}
}
fn filter_by<F>(table: &mut Self, flags: u32, cmp_fn: F) -> Result<(), MountInfoError>
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
#[doc(hidden)]
unsafe extern "C" fn compare<F>(
table: *mut libmount::libmnt_table,
this: *mut libmount::libmnt_fs,
other: *mut libmount::libmnt_fs,
) -> libc::c_int
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
let this = MountInfoEntry::borrow_ptr(this);
let other = MountInfoEntry::borrow_ptr(other);
let mut user_data_ptr = MaybeUninit::<*mut libc::c_void>::zeroed();
unsafe {
user_data_ptr.write(libmount::mnt_table_get_userdata(table));
}
let user_data = unsafe { user_data_ptr.assume_init() };
let fn_cmp = &mut *(user_data as *mut F);
match fn_cmp(&this, &other) {
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
}
}
let user_data = Box::into_raw(Box::new(cmp_fn));
let result = unsafe { libmount::mnt_table_set_userdata(table.inner, user_data as *mut _) };
match result {
0 => {
let result = unsafe {
libmount::mnt_table_uniq_fs(table.inner, flags as i32, Some(compare::<F>))
};
match result {
0 => {
log::debug!("MountInfo::filter_by removed duplicates");
let _ = unsafe { Box::from_raw(user_data) };
Ok(())
}
code => {
let err_msg = "failed to remove duplicates from table".to_owned();
log::debug!( "MountInfo::filter_by {err_msg}. libmount::mnt_table_uniq_fs returned error code: {code:?}");
let _ = unsafe { Box::from_raw(user_data) };
Err(MountInfoError::Deduplicate(err_msg))
}
}
}
code => {
let err_msg = "failed to set the comparison function as userdata".to_owned();
log::debug!( "MountInfo::filter_by {err_msg}. libmount::mnt_table_uniq_fs returned error code: {code:?}");
let _ = unsafe { Box::from_raw(user_data) };
Err(MountInfoError::Deduplicate(err_msg))
}
}
}
pub fn dedup_first_by<F>(&mut self, cmp: F) -> Result<(), MountInfoError>
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
log::debug!("MountInfo::dedup_first_by merging matching entries to the first occurrence");
Self::filter_by(self, libmount::MNT_UNIQ_FORWARD, cmp)
}
pub fn dedup_last_by<F>(&mut self, cmp: F) -> Result<(), MountInfoError>
where
F: FnMut(&MountInfoEntry, &MountInfoEntry) -> Ordering,
{
log::debug!("MountInfo::dedup_last_by merging matching entries to the last occurrence");
static MNT_UNIQ_BACKWARD: u32 = 0;
Self::filter_by(self, MNT_UNIQ_BACKWARD, cmp)
}
pub fn import_mountinfo(&mut self) -> Result<(), MountInfoError> {
log::debug!("MountInfo::import_mountinfo import entries from /proc/self/mountinfo and /run/mount/utab");
unsafe {
match libmount::mnt_table_parse_mtab(self.inner, std::ptr::null()) {
0 => {
log::debug!(
"MountInfo::import_mountinfo imported entries from /proc/self/mountinfo and /run/mount/utab"
);
Ok(())
}
code => {
let err_msg =
"failed to import entries from /proc/self/mountinfo and /run/mount/utab"
.to_owned();
log::debug!("MountInfo::import_mountinfo {}. libmount::mnt_table_parse_mtab returned error code: {:?}", err_msg, code);
Err(MountInfoError::Import(err_msg))
}
}
}
}
pub fn is_empty(&self) -> bool {
let state = unsafe { libmount::mnt_table_is_empty(self.inner) == 1 };
log::debug!("MountInfo::is_empty value: {:?}", state);
state
}
pub fn is_mounted(&self, entry: &FsTabEntry) -> bool {
let state = unsafe { libmount::mnt_table_is_fs_mounted(self.inner, entry.inner) == 1 };
log::debug!("FsTab::is_mounted value: {:?}", state);
state
}
}
impl fmt::Display for MountInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut output: Vec<String> = vec![];
for line in self.iter() {
output.push(line.to_string());
}
write!(f, "{}", output.join("\n"))
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
use pretty_assertions::{assert_eq, assert_ne};
#[test]
fn mount_info_can_import_mountinfo_file() -> crate::Result<()> {
let mut mount_info = MountInfo::new()?;
mount_info.import_mountinfo()?;
println!("{mount_info}");
Ok(())
}
}