Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1899,12 +1899,11 @@ pub fn verify_member(name: &str) -> Result<()> {
#[cfg(all(test, unix))]
mod quipu_alias_layout_tests {
use super::verify_quipu_alias;
use std::os::unix::fs::{symlink, PermissionsExt};
use std::os::unix::fs::symlink;
use std::{fs, path::Path};

fn script(path: &Path, version: &str) {
fs::write(path, format!("#!/bin/sh\necho '{version}'\n")).unwrap();
fs::set_permissions(path, fs::Permissions::from_mode(0o755)).unwrap();
crate::test_exec::write_executable(path, &format!("#!/bin/sh\necho '{version}'\n"));
}

#[test]
Expand Down
7 changes: 4 additions & 3 deletions src/adapter/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,6 @@ mod release_verification_tests {
#[cfg(all(test, unix))]
mod seeds_identity_tests {
use super::*;
use std::os::unix::fs::PermissionsExt;

#[test]
fn seeds_identity_accepts_both_editions_and_refuses_foreign_sd() {
Expand All @@ -877,8 +876,10 @@ mod seeds_identity_tests {
("sd is an intuitive find and replace CLI", false),
("seeds is a different program", false),
] {
std::fs::write(&executable, format!("#!/bin/sh\nprintf '%s\\n' '{text}'\n")).unwrap();
std::fs::set_permissions(&executable, std::fs::Permissions::from_mode(0o755)).unwrap();
crate::test_exec::write_executable(
&executable,
&format!("#!/bin/sh\nprintf '%s\\n' '{text}'\n"),
);
assert_eq!(identify(&executable, program).is_ok(), accepted, "{text}");
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/apt_owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,11 @@ fn refuse_if_owned(tool: &str, path: &Path, owner: Option<String>) -> anyhow::Re
#[cfg(all(test, unix))]
mod tests {
use super::*;
use std::os::unix::fs::{symlink, PermissionsExt};
use std::os::unix::fs::symlink;

fn fake_dpkg_query(dir: &Path, body: &str) -> std::path::PathBuf {
let path = dir.join("dpkg-query");
fs::write(&path, format!("#!/bin/sh\n{body}\n")).unwrap();
fs::set_permissions(&path, fs::Permissions::from_mode(0o755)).unwrap();
crate::test_exec::write_executable(&path, &format!("#!/bin/sh\n{body}\n"));
path
}

Expand Down
7 changes: 2 additions & 5 deletions src/dp_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,8 @@ mod tests {
/// first: delete that read and `keeps_a_different_local_target` fails.
#[cfg(unix)]
fn fake_dp(dir: &Path) -> PathBuf {
use std::os::unix::fs::PermissionsExt;
let script = dir.join("dp");
std::fs::write(
crate::test_exec::write_executable(
&script,
r#"#!/bin/sh
store="$(dirname "$0")/bd.rule"
Expand All @@ -229,9 +228,7 @@ case "$1" in
*) exit 2 ;;
esac
"#,
)
.unwrap();
std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755)).unwrap();
);
script
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ mod quipu_auth;
mod quipu_credential;
#[cfg(unix)]
pub mod quipu_mcp;
#[cfg(all(test, unix))]
mod test_exec;
pub mod vocabulary;
10 changes: 3 additions & 7 deletions src/release_update/rollback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ fn rollback_at(
#[cfg(test)]
mod tests {
use super::*;
use std::os::unix::fs::PermissionsExt;
fn fixture() -> (tempfile::TempDir, Plan, PathBuf, PathBuf, String, String) {
let dir = tempfile::tempdir().unwrap();
let state_path = dir.path().join("state.json");
Expand Down Expand Up @@ -213,10 +212,8 @@ mod tests {
};
let destination = dir.path().join("sd");
let prior = dir.path().join("prior");
fs::write(&destination, script("0.2.2")).unwrap();
fs::write(&prior, script("0.2.0")).unwrap();
fs::set_permissions(&destination, fs::Permissions::from_mode(0o755)).unwrap();
fs::set_permissions(&prior, fs::Permissions::from_mode(0o755)).unwrap();
crate::test_exec::write_executable(&destination, &script("0.2.2"));
crate::test_exec::write_executable(&prior, &script("0.2.0"));
let current_hash = hash(&destination).unwrap();
let prior_hash = hash(&prior).unwrap();
let backup = dir.path().join("release-backups/sd").join(&prior_hash);
Expand Down Expand Up @@ -260,8 +257,7 @@ mod tests {
fn failed_functional_proof_restores_current_bytes_and_state() {
let (dir, plan, state, destination, current, _prior) = fixture();
let prior = dir.path().join("broken");
fs::write(&prior, "#!/bin/sh\ncase \"$1\" in --help) echo 'seeds is the beads-compatible tracker';; --version) echo 'sd 0.2.0 (seeds)';; *) exit 7;; esac\n").unwrap();
fs::set_permissions(&prior, fs::Permissions::from_mode(0o755)).unwrap();
crate::test_exec::write_executable(&prior, "#!/bin/sh\ncase \"$1\" in --help) echo 'seeds is the beads-compatible tracker';; --version) echo 'sd 0.2.0 (seeds)';; *) exit 7;; esac\n");
let prior_hash = hash(&prior).unwrap();
let backup = dir.path().join("release-backups/sd").join(&prior_hash);
atomic_copy(&prior, &backup).unwrap();
Expand Down
33 changes: 33 additions & 0 deletions src/test_exec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Test-only: write a fake executable that a parallel test cannot make busy.
//!
//! Parallel tests write scripts and then exec them. If any sibling thread forks
//! while this process holds the script open for writing, the forked child
//! inherits that descriptor until it calls exec (`O_CLOEXEC` does not close the
//! window between fork and exec). An exec of the script in that window fails
//! with ETXTBSY, "Text file busy" (aegis-zke).
//!
//! Writing through a short-lived `sh` child means the write descriptor only
//! ever exists in that child. This process never holds it, so no sibling fork
//! can inherit it, and once the child has exited the file has no writer.

use std::io::Write;
use std::path::Path;
use std::process::{Command, Stdio};

/// Write `contents` to `path` with mode 0755, from a child process.
pub(crate) fn write_executable(path: &Path, contents: &str) {
let mut child = Command::new("/bin/sh")
.args(["-c", "cat > \"$1\" && chmod 0755 \"$1\"", "sh"])
.arg(path)
.stdin(Stdio::piped())
.spawn()
.expect("spawn sh to write a fake executable");
child
.stdin
.take()
.expect("piped stdin")
.write_all(contents.as_bytes())
.expect("write fake executable contents");
let status = child.wait().expect("wait for the fake executable writer");
assert!(status.success(), "writing {} failed", path.display());
}
Loading