diff --git a/core/archipelago/src/container/prod_orchestrator.rs b/core/archipelago/src/container/prod_orchestrator.rs index 9d773f8f..37ae6330 100644 --- a/core/archipelago/src/container/prod_orchestrator.rs +++ b/core/archipelago/src/container/prod_orchestrator.rs @@ -291,6 +291,14 @@ async fn chown_for_rootless_container(uid_gid: &str, path: &str) -> Result<()> { if ownership_already_correct(path, &host_uid_gid) { return Ok(()); } + // Some rootless-service contexts can read the directory entry but fail + // Rust's metadata lookup through an ACL/namespace boundary. Ask the + // host's root view before falling through to an expensive `chown -R`. + // This is still cheap compared with recursively walking Postgres on every + // reconcile and makes the drift gate authoritative on deployed nodes. + if ownership_already_correct_from_host(path, &host_uid_gid).await { + return Ok(()); + } if uid > 0 && uid < 100_000 { let output = tokio::process::Command::new("podman") @@ -353,6 +361,17 @@ fn ownership_already_correct(path: &str, host_uid_gid: &str) -> bool { md.uid() == uid && md.gid() == gid } +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 { + return false; + }; + out.status.success() && String::from_utf8_lossy(&out.stdout).trim() == host_uid_gid +} + /// `(container-id, mount-dest)` pairs whose in-container chown returned a hard, /// permanent failure (e.g. "Operation not permitted" on a mount that can't be /// re-owned from inside the userns). Remembered for the life of the process so