fix(container): ownership probe uses systemd-run with output capture

The first drift-gate attempt called plain sudo stat, which the daemon's
privilege path doesn't answer — the probe silently failed and the chown
loop continued. host_sudo_output mirrors host_sudo (systemd-run --pipe)
but returns the process output, so the ownership check gets a real answer.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-07 20:39:33 -04:00
co-authored by Claude
parent db8937f9e9
commit b886930708
2 changed files with 20 additions and 5 deletions
@@ -362,11 +362,7 @@ fn ownership_already_correct(path: &str, host_uid_gid: &str) -> bool {
}
async fn ownership_already_correct_from_host(path: &str, host_uid_gid: &str) -> bool {
let Ok(out) = tokio::process::Command::new("sudo")
.args(["stat", "-c", "%u:%g", path])
.output()
.await
else {
let Ok(out) = crate::update::host_sudo_output(&["stat", "-c", "%u:%g", path]).await else {
return false;
};
out.status.success() && String::from_utf8_lossy(&out.stdout).trim() == host_uid_gid
+19
View File
@@ -1496,6 +1496,25 @@ pub(crate) async fn host_sudo(args: &[&str]) -> Result<std::process::ExitStatus>
.context("sudo systemd-run spawn failed")
}
/// Same mechanism as `host_sudo` but captures stdout — for read-only probes
/// (e.g. `stat`) where the answer is in the output, not the exit status.
pub(crate) async fn host_sudo_output(args: &[&str]) -> Result<std::process::Output> {
let mut full: Vec<&str> = vec![
"systemd-run",
"--wait",
"--quiet",
"--collect",
"--pipe",
"--",
];
full.extend_from_slice(args);
tokio::process::Command::new("sudo")
.args(&full)
.output()
.await
.context("sudo systemd-run output spawn failed")
}
/// Apply a downloaded update. Backs up current binaries, replaces with staged versions.
pub async fn apply_update(data_dir: &Path) -> Result<()> {
let _op = UPDATE_OP_LOCK.try_lock().map_err(|_| {