Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Commit

Permalink
commit: Skip bind mounts too
Browse files Browse the repository at this point in the history
This fixes a failure I was seeing using `buildah` in Gitlab CI;
it seems like `/run/secrets` ends up as a bind mount here, and
because it's a bind mount it has the same device node.

With new enough Linux there's a handy attribute from the `statx`
system call that will tell us whether a given path is a mount
point root.  Let's use that to start.

(The other alternatives are much uglier; see the linked systemd
 git source code)

We may need to end up doing some of that if we find we
keep hitting this with systems that have older kernels.
  • Loading branch information
cgwalters committed Sep 18, 2023
1 parent 691b14e commit 62317fd
Showing 1 changed file with 28 additions and 1 deletion.
29 changes: 28 additions & 1 deletion lib/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ 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;
Expand Down Expand Up @@ -101,16 +102,42 @@ 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) -> bool {
// https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176
use rustix::fs::{AtFlags, StatxFlags};

let attr_mount_root: u32 = libc::STATX_ATTR_MOUNT_ROOT.try_into().unwrap();
let flags: StatxFlags = StatxFlags::from_bits(attr_mount_root).unwrap();
if let Ok(meta) = rustix::fs::statx(
root.as_fd(),
path,
AtFlags::NO_AUTOMOUNT | AtFlags::SYMLINK_NOFOLLOW,
flags,
) {
(meta.stx_attributes & attr_mount_root as u64) > 0
} else {
return false;
}
}

fn clean_subdir(root: &Dir, rootdev: u64) -> Result<()> {
for entry in root.entries()? {
let entry = entry?;
let metadata = entry.metadata()?;
let dev = metadata.dev();
let path = PathBuf::from(entry.file_name());
// Ignore other filesystem mounts, e.g. podman injects /run/.containerenv
if dev != rootdev {
tracing::trace!("Skipping entry in foreign dev {path:?}");
continue;
}
// Also ignore bind mounts, if we have a new enough kernel with statx()
// that will tell us.
if is_mountpoint(root, &path) {
tracing::trace!("Skipping mount point {path:?}");
continue;
}
let path = PathBuf::from(entry.file_name());
if metadata.is_dir() {
remove_all_on_mount_recurse(root, rootdev, &path)?;
} else {
Expand Down

0 comments on commit 62317fd

Please sign in to comment.