bwrap: Create a RoFilesMount struct

This cleans up the drop handling/ownership.
This commit is contained in:
Colin Walters 2021-03-26 00:32:09 +00:00
parent fa81456cbf
commit 8ba6348267

View File

@ -27,37 +27,7 @@ pub(crate) struct Bubblewrap {
child_argv0: Option<NonZeroUsize>, child_argv0: Option<NonZeroUsize>,
launcher: gio::SubprocessLauncher, // 🚀 launcher: gio::SubprocessLauncher, // 🚀
tempdirs: Vec<tempfile::TempDir>, rofiles_mounts: Vec<RoFilesMount>,
}
impl Drop for Bubblewrap {
fn drop(&mut self) {
for d in self.tempdirs.drain(..) {
// We need to unmount the tempdirs, before letting
// the drop handler recursively remove them.
let success = Command::new("fusermount")
.arg("-u")
.arg(d.path())
.status()
.map_err(anyhow::Error::new)
.and_then(|status| -> Result<()> {
if !status.success() {
Err(anyhow::anyhow!("{}", status))
} else {
Ok(())
}
})
.err()
.map(|e| {
systemd::journal::print(4, &format!("{}", e));
})
.is_none();
if !success {
// If fusermount fails, then we cannot remove it; just leak it.
let _ = d.into_path();
}
}
}
} }
// nspawn by default doesn't give us CAP_NET_ADMIN; see // nspawn by default doesn't give us CAP_NET_ADMIN; see
@ -72,21 +42,69 @@ fn running_in_nspawn() -> bool {
std::env::var_os("container").as_deref() == Some(std::ffi::OsStr::new("systemd-nspawn")) std::env::var_os("container").as_deref() == Some(std::ffi::OsStr::new("systemd-nspawn"))
} }
fn setup_rofiles_in(rootfs: &openat::Dir, path: &str) -> Result<tempfile::TempDir> { /// A wrapper for rofiles-fuse from ostree. This protects the underlying
let path = path.trim_start_matches('/'); /// hardlinked files from mutation. The mount point is a temporary
let tempdir = tempfile::Builder::new() /// directory.
.prefix("rpmostree-rofiles-fuse") struct RoFilesMount {
.tempdir()?; tempdir: Option<tempfile::TempDir>,
let status = std::process::Command::new("rofiles-fuse") }
.arg("--copyup")
.arg(path) impl RoFilesMount {
.arg(tempdir.path()) /// Create a new rofiles-fuse mount point
.current_dir(format!("/proc/self/fd/{}", rootfs.as_raw_fd())) fn new(rootfs: &openat::Dir, path: &str) -> Result<Self> {
.status()?; let path = path.trim_start_matches('/');
if !status.success() { let tempdir = tempfile::Builder::new()
return Err(anyhow::anyhow!("{}", status)); .prefix("rpmostree-rofiles-fuse")
.tempdir()?;
let status = std::process::Command::new("rofiles-fuse")
.arg("--copyup")
.arg(path)
.arg(tempdir.path())
.current_dir(format!("/proc/self/fd/{}", rootfs.as_raw_fd()))
.status()?;
if !status.success() {
return Err(anyhow::anyhow!("{}", status));
}
Ok(Self {
tempdir: Some(tempdir),
})
}
fn path(&self) -> &Path {
self.tempdir.as_ref().unwrap().path()
}
}
impl Drop for RoFilesMount {
fn drop(&mut self) {
let tempdir = if let Some(d) = self.tempdir.take() {
d
} else {
return;
};
// We need to unmount before letting the tempdir cleanup run.
let success = Command::new("fusermount")
.arg("-u")
.arg(tempdir.path())
.status()
.map_err(anyhow::Error::new)
.and_then(|status| -> Result<()> {
if !status.success() {
Err(anyhow::anyhow!("{}", status))
} else {
Ok(())
}
})
.err()
.map(|e| {
systemd::journal::print(4, &format!("{}", e));
})
.is_none();
if !success {
// If fusermount fails, then we cannot remove it; just leak it.
let _ = tempdir.into_path();
}
} }
Ok(tempdir)
} }
fn child_wait_check( fn child_wait_check(
@ -230,15 +248,15 @@ impl Bubblewrap {
argv, argv,
launcher, launcher,
child_argv0: None, child_argv0: None,
tempdirs: Vec::new(), rofiles_mounts: Vec::new(),
}) })
} }
fn setup_rofiles(&mut self, path: &str) -> Result<()> { fn setup_rofiles(&mut self, path: &str) -> Result<()> {
let tmpdir = setup_rofiles_in(&self.rootfs_fd, path)?; let mnt = RoFilesMount::new(&self.rootfs_fd, path)?;
let tmpdir_path = tmpdir.path().to_str().expect("tempdir str"); let tmpdir_path = mnt.path().to_str().expect("tempdir str");
self.bind_readwrite(tmpdir_path, path); self.bind_readwrite(tmpdir_path, path);
self.tempdirs.push(tmpdir); self.rofiles_mounts.push(mnt);
Ok(()) Ok(())
} }