Skip to content

Commit

Permalink
Move mountpoint checking into bootc_utils
Browse files Browse the repository at this point in the history
Also:

- Add a new API that hard requires a new enough kernel, for
  cases where we really need to know for sure if it's a mountpoint
  or not.
- Change to use BorrowedFd since we don't need cap-std specifically
  here

Signed-off-by: Colin Walters <walters@verbum.org>
  • Loading branch information
cgwalters committed Dec 19, 2024
1 parent 6759010 commit b968e5a
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions ostree-ext/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@
//! procedures as part of building an ostree container image.
//! <https://github.com/ostreedev/ostree-rs-ext/issues/159>
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"];

Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 0 additions & 1 deletion ostree-ext/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 2 additions & 0 deletions utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
1 change: 1 addition & 0 deletions utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
//!
mod command;
pub use command::*;
pub mod mount;
28 changes: 19 additions & 9 deletions ostree-ext/src/mountutil.rs → utils/src/mount.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
//! 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")]
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<Option<bool>> {
fn is_mountpoint_impl_statx(root: BorrowedFd, path: &Path) -> Result<Option<bool>> {
// https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176
use rustix::fs::{AtFlags, StatxFlags};

Expand All @@ -34,27 +32,39 @@ fn is_mountpoint_impl_statx(root: &Dir, path: &Path) -> Result<Option<bool>> {
}
}

/// Try to (heuristically) determine if the provided path is a mount root.
pub fn is_mountpoint(root: &Dir, path: impl AsRef<Path>) -> Result<Option<bool>> {
/// 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<Path>) -> Result<Option<bool>> {
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<Path>) -> Result<bool> {
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(())
}
}

0 comments on commit b968e5a

Please sign in to comment.