From ab7f2ca1a1ba8ebd13600941e9d52878c05b5f21 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Wed, 24 Dec 2025 19:03:24 +0100 Subject: [PATCH] unshare: full rootless --- src/context/unshare.rs | 15 ++++++-- src/deb/cross.rs | 86 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/src/context/unshare.rs b/src/context/unshare.rs index d88c478..5c6b71d 100644 --- a/src/context/unshare.rs +++ b/src/context/unshare.rs @@ -166,11 +166,18 @@ impl UnshareDriver { env: &[(String, String)], cwd: Option<&str>, ) -> ContextCommand<'_> { - let mut cmd = self.parent().command("sudo"); - cmd.args(env.iter().map(|(k, v)| format!("{k}={v}"))); + let mut cmd = self.parent().command("unshare"); - cmd.arg("unshare") - .arg("--mount-proc") + cmd.envs(env.iter().cloned()); + + cmd.arg("--mount-proc") + .arg("--pid") + .arg("--ipc") + .arg("--uts") + .arg("--map-auto") + .arg("-r") + .arg("--mount") + .arg("--fork") .arg("-R") .arg(&self.path); diff --git a/src/deb/cross.rs b/src/deb/cross.rs index 3a8434b..770667c 100644 --- a/src/deb/cross.rs +++ b/src/deb/cross.rs @@ -1,9 +1,12 @@ use crate::context; use crate::context::{Context, ContextConfig}; +use directories::ProjectDirs; use std::collections::HashMap; use std::error::Error; - -use std::path::PathBuf; +use std::fs; +use std::path::{Path, PathBuf}; +use tar::Archive; +use xz2::read::XzDecoder; pub struct EphemeralContextGuard { previous_context: String, @@ -24,19 +27,8 @@ impl EphemeralContextGuard { chroot_path.display() ); - let status = context::current() - .command("sudo") - .arg("mmdebstrap") - .arg("--variant=buildd") - .arg(series) - .arg(chroot_path.to_string_lossy().to_string()) - .status()?; - - if !status.success() { - // Clean up on failure - let _ = std::fs::remove_dir_all(&chroot_path); - return Err(format!("mmdebstrap failed for series {}", series).into()); - } + // Download and extract the chroot tarball + Self::download_and_extract_chroot(series, &chroot_path)?; // Switch to an ephemeral context to build the package in the chroot context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare { @@ -49,6 +41,70 @@ impl EphemeralContextGuard { chroot_path, }) } + + fn download_and_extract_chroot( + series: &str, + chroot_path: &PathBuf, + ) -> Result<(), Box> { + // Get project directories for caching + let proj_dirs = ProjectDirs::from("com", "pkh", "pkh") + .ok_or("Could not determine project directories")?; + let cache_dir = proj_dirs.cache_dir(); + fs::create_dir_all(cache_dir)?; + + // Create tarball filename based on series + let tarball_filename = format!("{}-buildd.tar.xz", series); + let tarball_path = cache_dir.join(&tarball_filename); + + // Download tarball if it doesn't exist + if !tarball_path.exists() { + log::debug!("Downloading chroot tarball for {}...", series); + Self::download_chroot_tarball(series, &tarball_path)?; + } else { + log::debug!("Using cached chroot tarball for {}", series); + } + + // Extract tarball to chroot directory + log::debug!("Extracting chroot tarball to {}...", chroot_path.display()); + Self::extract_tarball(&tarball_path, chroot_path)?; + + Ok(()) + } + + fn download_chroot_tarball(series: &str, tarball_path: &Path) -> Result<(), Box> { + // Use mmdebstrap to download the tarball to the cache directory + let status = context::current() + .command("mmdebstrap") + .arg("--variant=buildd") + .arg("--format=tar") + .arg(series) + .arg(tarball_path.to_string_lossy().to_string()) + .status()?; + + if !status.success() { + return Err(format!("Failed to download chroot tarball for series {}", series).into()); + } + + Ok(()) + } + + fn extract_tarball( + tarball_path: &PathBuf, + chroot_path: &PathBuf, + ) -> Result<(), Box> { + // Create the chroot directory + fs::create_dir_all(chroot_path)?; + + // Open the tarball file + let tarball_file = std::fs::File::open(tarball_path)?; + let xz_decoder = XzDecoder::new(tarball_file); + let mut archive = Archive::new(xz_decoder); + + // Extract all files to the chroot directory + archive.unpack(chroot_path)?; + + Ok(()) + } } impl Drop for EphemeralContextGuard {