diff --git a/src/apt/keyring.rs b/src/apt/keyring.rs index 0384e6e..6f306a1 100644 --- a/src/apt/keyring.rs +++ b/src/apt/keyring.rs @@ -7,7 +7,7 @@ use crate::context; use crate::distro_info; use serde::Deserialize; use std::error::Error; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; /// Launchpad API response structure for PPA information @@ -16,21 +16,36 @@ struct LaunchpadPpaResponse { signing_key_fingerprint: String, } -/// Download a keyring into apt trusted.gpg.d directory, trusting that keyring -pub async fn download_trust_keyring( +/// Download a keyring to the application cache directory and return the path +/// +/// This function downloads the keyring to a user-writable cache directory +/// instead of the system apt keyring directory, allowing non-root usage. +/// The returned path can be passed to mmdebstrap via --keyring. +/// +/// # Arguments +/// * `ctx` - Optional context to use +/// * `series` - The distribution series (e.g., "noble", "sid") +/// +/// # Returns +/// The path to the downloaded keyring file +pub async fn download_cache_keyring( ctx: Option>, series: &str, -) -> Result<(), Box> { +) -> Result> { let ctx = ctx.unwrap_or_else(context::current); // Obtain keyring URL from distro_info let keyring_url = distro_info::get_keyring_url(series).await?; log::debug!("Downloading keyring from: {}", keyring_url); - // Create trusted.gpg.d directory if it doesn't exist - let trusted_gpg_d = "/etc/apt/trusted.gpg.d"; - if !ctx.exists(Path::new(trusted_gpg_d))? { - ctx.command("mkdir").arg("-p").arg(trusted_gpg_d).status()?; + // Get the application cache directory + let proj_dirs = directories::ProjectDirs::from("com", "pkh", "pkh") + .ok_or("Could not determine project directories")?; + let cache_dir = proj_dirs.cache_dir(); + + // Create cache directory if it doesn't exist + if !ctx.exists(cache_dir)? { + ctx.command("mkdir").arg("-p").arg(cache_dir).status()?; } // Extract the original filename from the keyring URL @@ -39,9 +54,9 @@ pub async fn download_trust_keyring( .next_back() .unwrap_or("pkh-{}.gpg") .replace("{}", series); - let keyring_path = format!("{}/{}", trusted_gpg_d, filename); + let keyring_path = cache_dir.join(&filename); - // Download the keyring directly to the final location using curl + // Download the keyring using curl let mut curl_cmd = ctx.command("curl"); curl_cmd .arg("-s") @@ -57,11 +72,11 @@ pub async fn download_trust_keyring( } log::info!( - "Successfully downloaded and installed keyring for {} to {}", + "Successfully downloaded keyring for {} to {}", series, - keyring_path + keyring_path.display() ); - Ok(()) + Ok(keyring_path) } /// Download and import a PPA key using Launchpad API diff --git a/src/deb/ephemeral.rs b/src/deb/ephemeral.rs index 971013b..1bb02cd 100644 --- a/src/deb/ephemeral.rs +++ b/src/deb/ephemeral.rs @@ -143,22 +143,17 @@ impl EphemeralContextGuard { .arg(lockfile_path.to_string_lossy().to_string()) .status()?; - // Make sure we have the right apt keyrings to mmdebstrap the chroot - // Check for root privileges before downloading keyring - if crate::utils::root::is_root()? { - crate::apt::keyring::download_trust_keyring(Some(ctx.clone()), series).await?; - } else { - log::info!( - "Lacking root privileges. Please ensure that the keyrings for the target distribution are present on your system." - ); - } + // Download the keyring to the cache directory + let keyring_path = + crate::apt::keyring::download_cache_keyring(Some(ctx.clone()), series).await?; // Use mmdebstrap to download the tarball to the cache directory let mut cmd = ctx.command("mmdebstrap"); cmd.arg("--variant=buildd") .arg("--mode=unshare") .arg("--include=mount,curl,ca-certificates") - .arg("--format=tar"); + .arg("--format=tar") + .arg(format!("--keyring={}", keyring_path.display())); // Add architecture if specified if let Some(a) = arch { @@ -238,10 +233,15 @@ impl EphemeralContextGuard { .arg(dev_zero_path.to_string_lossy().to_string()) .status(); - // Create new device nodes using fakeroot and mknod - let status_null = ctx - .command("sudo") - .arg("mknod") + // Check if we're running as root + let is_root = crate::utils::root::is_root()?; + + // Create new device nodes using mknod (with sudo if not root) + let mut cmd_null = ctx.command(if is_root { "mknod" } else { "sudo" }); + if !is_root { + cmd_null.arg("mknod"); + } + let status_null = cmd_null .arg("-m") .arg("666") .arg(dev_null_path.to_string_lossy().to_string()) @@ -250,9 +250,11 @@ impl EphemeralContextGuard { .arg("3") .status()?; - let status_zero = ctx - .command("sudo") - .arg("mknod") + let mut cmd_zero = ctx.command(if is_root { "mknod" } else { "sudo" }); + if !is_root { + cmd_zero.arg("mknod"); + } + let status_zero = cmd_zero .arg("-m") .arg("666") .arg(dev_zero_path.to_string_lossy().to_string()) @@ -288,12 +290,24 @@ impl Drop for EphemeralContextGuard { "Build succeeded, removing chroot directory: {}", self.chroot_path.display() ); - let result = context::current() - .command("sudo") - .arg("rm") - .arg("-rf") - .arg(&self.chroot_path) - .status(); + + // Check if we're running as root to avoid unnecessary sudo + let is_root = crate::utils::root::is_root().unwrap_or(false); + + let result = if is_root { + context::current() + .command("rm") + .arg("-rf") + .arg(&self.chroot_path) + .status() + } else { + context::current() + .command("sudo") + .arg("rm") + .arg("-rf") + .arg(&self.chroot_path) + .status() + }; match result { Ok(status) => {