deb: sync upper entries through the privileged removal path
A rootless build's upperdir entries are copy-up'd by whoever wrote them during the build: the unprivileged build user, which host-side is a subuid of the invoking user. The host-side sync of a forked record removes the stale upper copy of every host-modified file, and the plain fs removals failed with EPERM on those build-user-owned entries: the kernel incremental run died with a bare 'Permission denied (os error 13)' and no context, ten minutes into the run. Route the sync removals through the privileged path (the same sudo fallback the record teardowns use) and wrap the sync error at the call site, so a failure names the operation instead of surfacing a bare errno string.
This commit is contained in:
+6
-1
@@ -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)"
|
||||
|
||||
+12
-12
@@ -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<usize> {
|
||||
let mut removed_prefixes: Vec<String> = Vec::new();
|
||||
let mut removed = 0;
|
||||
@@ -2084,13 +2090,13 @@ 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(()) => {
|
||||
if !meta.is_dir() && !meta.is_file() {
|
||||
continue;
|
||||
}
|
||||
crate::deb::ephemeral::privileged_remove(&path)?;
|
||||
removed += 1;
|
||||
if *dir {
|
||||
let mut prefix = rel.clone();
|
||||
@@ -2100,12 +2106,6 @@ pub(crate) fn apply_upper_sync(upper_root: &Path, ops: &[UpperOp]) -> io::Result
|
||||
removed_prefixes.push(prefix);
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() != io::ErrorKind::NotFound => {
|
||||
return Err(e);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
Ok(removed)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user