diff --git a/Cargo.lock b/Cargo.lock index 09e146559..bbf8a8e33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,6 +210,8 @@ name = "bootc-utils" version = "0.0.0" dependencies = [ "anyhow", + "cap-std-ext", + "libc", "rustix", "serde", "serde_json", diff --git a/lib/src/install.rs b/lib/src/install.rs index 5929e4cdd..fc156c1d7 100644 --- a/lib/src/install.rs +++ b/lib/src/install.rs @@ -1711,7 +1711,7 @@ pub(crate) async fn install_to_filesystem( tracing::debug!("Root filesystem: {root_path}"); - if let Some(false) = ostree_ext::mountutil::is_mountpoint(&rootfs_fd, ".")? { + if let Some(false) = bootc_utils::mount::is_mountpoint_compat(rootfs_fd.as_fd(), ".")? { anyhow::bail!("Not a mountpoint: {root_path}"); } rootfs_fd diff --git a/ostree-ext/src/commit.rs b/ostree-ext/src/commit.rs index babe9017d..4e63c9647 100644 --- a/ostree-ext/src/commit.rs +++ b/ostree-ext/src/commit.rs @@ -2,18 +2,20 @@ //! procedures as part of building an ostree container image. //! -use crate::container_utils::require_ostree_container; -use crate::mountutil::is_mountpoint; use anyhow::Context; use anyhow::Result; use cap_std::fs::Dir; use cap_std::fs::MetadataExt; use cap_std_ext::cap_std; use cap_std_ext::dirext::CapStdExtDirExt; +use std::os::fd::AsFd; use std::path::Path; use std::path::PathBuf; use tokio::task; +use crate::container_utils::require_ostree_container; +use bootc_utils::mount::is_mountpoint_compat; + /// Directories for which we will always remove all content. const FORCE_CLEAN_PATHS: &[&str] = &["run", "tmp", "var/tmp", "var/cache"]; @@ -60,7 +62,7 @@ fn clean_subdir(root: &Dir, rootdev: u64) -> Result<()> { } // Also ignore bind mounts, if we have a new enough kernel with statx() // that will tell us. - if is_mountpoint(root, &path)?.unwrap_or_default() { + if is_mountpoint_compat(root.as_fd(), &path)?.unwrap_or_default() { tracing::trace!("Skipping mount point {path:?}"); continue; } diff --git a/ostree-ext/src/lib.rs b/ostree-ext/src/lib.rs index b962c8d6c..97ec80de9 100644 --- a/ostree-ext/src/lib.rs +++ b/ostree-ext/src/lib.rs @@ -39,7 +39,6 @@ pub mod diff; pub mod ima; pub mod keyfileext; pub(crate) mod logging; -pub mod mountutil; pub mod ostree_prepareroot; pub mod refescape; #[doc(hidden)] diff --git a/utils/Cargo.toml b/utils/Cargo.toml index 6aeda593d..552d3dcfa 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -8,12 +8,14 @@ repository = "https://github.com/containers/bootc" [dependencies] anyhow = { workspace = true } +cap-std-ext = { workspace = true, features = ["fs_utf8"] } rustix = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tempfile = { workspace = true } tracing = { workspace = true } tokio = { workspace = true, features = ["process"] } +libc = { workspace = true } [dev-dependencies] similar-asserts = { workspace = true } diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 6976ce24d..38590ec0c 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -4,3 +4,4 @@ //! mod command; pub use command::*; +pub mod mount; diff --git a/ostree-ext/src/mountutil.rs b/utils/src/mount.rs similarity index 62% rename from ostree-ext/src/mountutil.rs rename to utils/src/mount.rs index f73cbba26..6ea1803f1 100644 --- a/ostree-ext/src/mountutil.rs +++ b/utils/src/mount.rs @@ -1,11 +1,9 @@ //! Helpers for interacting with mounts. -use std::os::fd::AsFd; +use std::os::fd::{AsFd, BorrowedFd}; use std::path::Path; use anyhow::Result; -use cap_std::fs::Dir; -use cap_std_ext::cap_std; // Fix musl support #[cfg(target_env = "gnu")] @@ -13,7 +11,7 @@ use libc::STATX_ATTR_MOUNT_ROOT; #[cfg(target_env = "musl")] const STATX_ATTR_MOUNT_ROOT: libc::c_int = 0x2000; -fn is_mountpoint_impl_statx(root: &Dir, path: &Path) -> Result> { +fn is_mountpoint_impl_statx(root: BorrowedFd, path: &Path) -> Result> { // https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176 use rustix::fs::{AtFlags, StatxFlags}; @@ -34,27 +32,39 @@ fn is_mountpoint_impl_statx(root: &Dir, path: &Path) -> Result> { } } -/// Try to (heuristically) determine if the provided path is a mount root. -pub fn is_mountpoint(root: &Dir, path: impl AsRef) -> Result> { +/// Check if the target path is a mount point. On older systems without +/// `statx` support or that is missing support for the `STATX_ATTR_MOUNT_ROOT`, +/// this will return `Ok(None)`. +pub fn is_mountpoint_compat(root: BorrowedFd, path: impl AsRef) -> Result> { is_mountpoint_impl_statx(root, path.as_ref()) } +/// Check if the target path is a mount point. +pub fn is_mountpoint(root: BorrowedFd, path: impl AsRef) -> Result { + match is_mountpoint_compat(root, path)? { + Some(r) => Ok(r), + None => anyhow::bail!("statx missing mountpoint support"), + } +} + #[cfg(test)] mod tests { use super::*; - use cap_std_ext::cap_tempfile; + use cap_std_ext::{cap_std, 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(); + let supported = is_mountpoint_compat(root.as_fd(), 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()); + assert!(!is_mountpoint_compat(tmpdir.as_fd(), Path::new(".")) + .unwrap() + .unwrap()); Ok(()) } }