From 77164e51e59878a6c4db6e19cc2343887020f526 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 2 Oct 2023 14:17:08 -0400 Subject: [PATCH] lib: Add mountutil module I copy/pasted the mountpoint check into bootc, but it'd be better to centralize. --- lib/src/commit.rs | 38 +------------------------------ lib/src/lib.rs | 1 + lib/src/mountutil.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 37 deletions(-) create mode 100644 lib/src/mountutil.rs diff --git a/lib/src/commit.rs b/lib/src/commit.rs index d504d6fd..8277e7a8 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -3,13 +3,13 @@ //! use crate::container_utils::require_ostree_container; +use crate::mountutil::is_mountpoint; use anyhow::Context; use anyhow::Result; use camino::Utf8Path; use cap_std::fs::Dir; use cap_std_ext::cap_std; use cap_std_ext::dirext::CapStdExtDirExt; -use io_lifetimes::AsFd; use rustix::fs::MetadataExt; use std::borrow::Cow; use std::convert::TryInto; @@ -102,28 +102,6 @@ fn remove_all_on_mount_recurse(root: &Dir, rootdev: u64, path: &Path) -> Result< Ok(skipped) } -/// Try to (heuristically) determine if the provided path is a mount root. -fn is_mountpoint(root: &Dir, path: &Path) -> Result> { - // https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176 - use rustix::fs::{AtFlags, StatxFlags}; - - // SAFETY(unwrap): We can infallibly convert an i32 into a u64. - let mountroot_flag: u64 = libc::STATX_ATTR_MOUNT_ROOT.try_into().unwrap(); - match rustix::fs::statx( - root.as_fd(), - path, - AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW, - StatxFlags::empty(), - ) { - Ok(r) => { - let present = (r.stx_attributes_mask & mountroot_flag) > 0; - Ok(present.then(|| r.stx_attributes & mountroot_flag > 0)) - } - Err(e) if e == rustix::io::Errno::NOSYS => Ok(None), - Err(e) => Err(e.into()), - } -} - fn clean_subdir(root: &Dir, rootdev: u64) -> Result<()> { for entry in root.entries()? { let entry = entry?; @@ -207,20 +185,6 @@ mod tests { use super::*; use cap_std_ext::cap_tempfile; - #[test] - fn test_is_mountpoint() -> Result<()> { - let root = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?; - let supported = is_mountpoint(&root, Path::new("/")).unwrap(); - match supported { - Some(r) => assert!(r), - // If the host doesn't support statx, ignore this for now - None => return Ok(()), - } - let tmpdir = cap_tempfile::TempDir::new(cap_std::ambient_authority())?; - assert!(!is_mountpoint(&tmpdir, Path::new(".")).unwrap().unwrap()); - Ok(()) - } - #[test] fn commit() -> Result<()> { let td = &cap_tempfile::tempdir(cap_std::ambient_authority())?; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 71956400..7d6a47ca 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -38,6 +38,7 @@ pub mod diff; pub mod ima; pub mod keyfileext; pub(crate) mod logging; +pub mod mountutil; pub mod refescape; #[doc(hidden)] pub mod repair; diff --git a/lib/src/mountutil.rs b/lib/src/mountutil.rs new file mode 100644 index 00000000..55815116 --- /dev/null +++ b/lib/src/mountutil.rs @@ -0,0 +1,54 @@ +//! Helpers for interacting with mounts. + +use std::os::fd::AsFd; +use std::path::Path; + +use anyhow::Result; +use cap_std::fs::Dir; +use cap_std_ext::cap_std; + +fn is_mountpoint_impl_statx(root: &Dir, path: &Path) -> Result> { + // https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176 + use rustix::fs::{AtFlags, StatxFlags}; + + // SAFETY(unwrap): We can infallibly convert an i32 into a u64. + let mountroot_flag: u64 = libc::STATX_ATTR_MOUNT_ROOT.try_into().unwrap(); + match rustix::fs::statx( + root.as_fd(), + path, + AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW, + StatxFlags::empty(), + ) { + Ok(r) => { + let present = (r.stx_attributes_mask & mountroot_flag) > 0; + Ok(present.then(|| r.stx_attributes & mountroot_flag > 0)) + } + Err(e) if e == rustix::io::Errno::NOSYS => Ok(None), + Err(e) => Err(e.into()), + } +} + +/// Try to (heuristically) determine if the provided path is a mount root. +pub fn is_mountpoint(root: &Dir, path: impl AsRef) -> Result> { + is_mountpoint_impl_statx(root, path.as_ref()) +} + +#[cfg(test)] +mod tests { + use super::*; + use cap_std_ext::cap_tempfile; + + #[test] + fn test_is_mountpoint() -> Result<()> { + let root = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?; + let supported = is_mountpoint(&root, Path::new("/")).unwrap(); + match supported { + Some(r) => assert!(r), + // If the host doesn't support statx, ignore this for now + None => return Ok(()), + } + let tmpdir = cap_tempfile::TempDir::new(cap_std::ambient_authority())?; + assert!(!is_mountpoint(&tmpdir, Path::new(".")).unwrap().unwrap()); + Ok(()) + } +}