diff --git a/lib/src/mount.rs b/lib/src/mount.rs index f830df32..2c856c52 100644 --- a/lib/src/mount.rs +++ b/lib/src/mount.rs @@ -1,11 +1,17 @@ //! Helpers for interacting with mountpoints -use std::process::Command; +use std::{ + fs::File, + os::fd::{AsFd, OwnedFd}, + path::Path, + process::Command, +}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use bootc_utils::CommandRunExt; use camino::Utf8Path; use fn_error_context::context; +use rustix::mount::{MoveMountFlags, OpenTreeFlags}; use serde::Deserialize; use crate::task::Task; @@ -88,3 +94,30 @@ pub(crate) fn is_same_as_host(path: &Utf8Path) -> Result { ); Ok(devstat.f_fsid == hostdevstat.f_fsid) } + +/// Open the target mount point in the mount namespace of pid 1 +pub(crate) fn open_tree_pid1_mountns(p: impl AsRef) -> Result { + let p = p.as_ref(); + let proc1_ns = "/proc/1/ns/mnt"; + let pid1_mountns_fd: OwnedFd = File::open(proc1_ns) + .with_context(|| format!("Opening {proc1_ns}"))? + .into(); + std::thread::scope(|s| { + let fd = s.spawn(move || -> Result<_> { + let allowed_types = Some(rustix::thread::LinkNameSpaceType::Mount); + rustix::thread::move_into_link_name_space(pid1_mountns_fd.as_fd(), allowed_types) + .context("setns")?; + let oflags = OpenTreeFlags::OPEN_TREE_CLOEXEC | OpenTreeFlags::OPEN_TREE_CLONE; + rustix::mount::open_tree(rustix::fs::CWD, p, oflags).map_err(Into::into) + }); + fd.join().unwrap() + }) +} + +pub(crate) fn mount_from_pid1(src: impl AsRef, dest: impl AsRef) -> Result<()> { + let dest = dest.as_ref(); + let src = open_tree_pid1_mountns(src)?; + let flags = MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH; + rustix::mount::move_mount(src, "", , dest, flags)?; + Ok(()) +}