diff --git a/src/deb/mod.rs b/src/deb/mod.rs index 5e4bf18..ccde698 100644 --- a/src/deb/mod.rs +++ b/src/deb/mod.rs @@ -627,7 +627,12 @@ async fn build_binary_package_impl( // not drag it into the chroot. let mut build_resume = false; if let Some(s) = &session { - build_resume = session::sync_upper_for_resume(s, &host_tree)?; + build_resume = session::sync_upper_for_resume(s, &host_tree).map_err(|e| { + format!( + "cannot sync the carried tree of the incremental build \ + with the host tree: {e}" + ) + })?; if build_resume { log::info!( "Reusing the build artifacts of the previous attempt (incremental build)" diff --git a/src/deb/session.rs b/src/deb/session.rs index fe6e558..5c526cf 100644 --- a/src/deb/session.rs +++ b/src/deb/session.rs @@ -2072,6 +2072,12 @@ pub(crate) fn plan_upper_sync( /// Apply sync ops to the upperdir, skipping paths already covered by a /// removed ancestor. Returns how many entries were actually removed. +/// +/// Removals go through the privileged path: the upperdir's entries were +/// copy-up'd by whoever wrote them during the build — in a rootless build +/// the unprivileged build user (a subuid of the invoking user host-side) — +/// so a plain removal by the invoking user fails with EPERM exactly when a +/// host-modified file's stale upper copy must go. pub(crate) fn apply_upper_sync(upper_root: &Path, ops: &[UpperOp]) -> io::Result { let mut removed_prefixes: Vec = Vec::new(); let mut removed = 0; @@ -2084,26 +2090,20 @@ pub(crate) fn apply_upper_sync(upper_root: &Path, ops: &[UpperOp]) -> io::Result continue; } let path = upper_root.join(rel.replace('/', std::path::MAIN_SEPARATOR_STR)); - let result = if *dir { - fs::remove_dir_all(&path) - } else { - fs::remove_file(&path) + let Ok(meta) = path.symlink_metadata() else { + continue; }; - match result { - Ok(()) => { - removed += 1; - if *dir { - let mut prefix = rel.clone(); - if !prefix.ends_with('/') { - prefix.push('/'); - } - removed_prefixes.push(prefix); - } + if !meta.is_dir() && !meta.is_file() { + continue; + } + crate::deb::ephemeral::privileged_remove(&path)?; + removed += 1; + if *dir { + let mut prefix = rel.clone(); + if !prefix.ends_with('/') { + prefix.push('/'); } - Err(e) if e.kind() != io::ErrorKind::NotFound => { - return Err(e); - } - Err(_) => {} + removed_prefixes.push(prefix); } } Ok(removed)