Add target command resolution and fallback directory support with tests

This commit is contained in:
2026-06-11 13:13:00 -05:00
parent eabe2a3000
commit 9526f9d870
+112 -5
View File
@@ -22,6 +22,14 @@ const DEFAULT_JAVA_CACERTS_LINK: &str = "/etc/ssl/certs/java/cacerts";
const DEFAULT_PKI_TLS_CA_BUNDLE_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.crt";
const DEFAULT_PKI_TLS_CA_BUNDLE_TRUST_CRT_LINK: &str = "/etc/pki/tls/certs/ca-bundle.trust.crt";
const DEFAULT_PKI_TLS_JAVA_CACERTS_LINK: &str = "/etc/pki/tls/java/cacerts";
const TARGET_COMMAND_FALLBACK_DIRS: &[&str] = &[
"/usr/local/sbin",
"/usr/local/bin",
"/usr/sbin",
"/usr/bin",
"/sbin",
"/bin",
];
const DEFAULT_CERTDATA_URL: &str =
"https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt";
const DEFAULT_CERTDATA_OUTPUT: &str = "certdata.txt";
@@ -2073,19 +2081,22 @@ fn run_target_command(
dry_run: bool,
envs: &[(&str, &str)],
) -> Result<()> {
let resolved_program = resolve_target_program(root, program);
let resolved_program_display = resolved_program.display().to_string();
if dry_run {
print_command_preview(root, program, args, envs);
print_command_preview(root, &resolved_program_display, args, envs);
return Ok(());
}
// Execute directly on the host or via chroot for image builds.
let mut cmd = if is_root_fs(root) {
let mut cmd = Command::new(program);
let mut cmd = Command::new(&resolved_program);
cmd.args(args);
cmd
} else {
let mut cmd = Command::new("chroot");
cmd.arg(root).arg(program).args(args);
cmd.arg(root).arg(&resolved_program).args(args);
cmd
};
@@ -2096,9 +2107,13 @@ fn run_target_command(
let output = cmd.output().with_context(|| {
if is_root_fs(root) {
format!("failed to execute `{}`", program)
format!("failed to execute `{}`", resolved_program_display)
} else {
format!("failed to execute `chroot {} {}`", root.display(), program)
format!(
"failed to execute `chroot {} {}`",
root.display(),
resolved_program_display
)
}
})?;
@@ -2138,6 +2153,48 @@ fn run_target_command(
);
}
fn resolve_target_program(root: &Path, program: &str) -> PathBuf {
let path_env = std::env::var_os("PATH");
resolve_target_program_with_path(root, program, path_env.as_deref())
}
fn resolve_target_program_with_path(
root: &Path,
program: &str,
path_env: Option<&OsStr>,
) -> PathBuf {
if program.contains('/') {
return PathBuf::from(program);
}
for candidate in target_command_search_candidates(program, path_env) {
if path_in_root(root, &candidate).exists() {
return candidate;
}
}
PathBuf::from(program)
}
fn target_command_search_candidates(program: &str, path_env: Option<&OsStr>) -> Vec<PathBuf> {
let mut candidates = Vec::new();
if let Some(path_env) = path_env {
for dir in std::env::split_paths(path_env).filter(|dir| dir.is_absolute()) {
candidates.push(dir.join(program));
}
}
for dir in TARGET_COMMAND_FALLBACK_DIRS {
let candidate = Path::new(dir).join(program);
if !candidates.iter().any(|existing| existing == &candidate) {
candidates.push(candidate);
}
}
candidates
}
fn print_command_preview(root: &Path, program: &str, args: &[String], envs: &[(&str, &str)]) {
// Keep dry-run output close to the real shell command for easier debugging.
let env_prefix = envs
@@ -2278,6 +2335,56 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
assert!(!is_root_fs(Path::new("/mnt/rootfs")));
}
#[test]
fn target_command_candidates_use_absolute_path_entries_and_fallbacks() {
let candidates = target_command_search_candidates(
"trust",
Some(std::ffi::OsStr::new("/custom/bin:relative:/usr/bin")),
);
assert_eq!(candidates[0], PathBuf::from("/custom/bin/trust"));
assert_eq!(candidates[1], PathBuf::from("/usr/bin/trust"));
assert!(candidates.contains(&PathBuf::from("/bin/trust")));
assert!(!candidates.contains(&PathBuf::from("relative/trust")));
}
#[test]
fn resolve_target_program_finds_binary_inside_selected_root() {
let root = unique_test_dir("target-command-root");
let trust_host_path = root.join("custom/bin/trust");
fs::create_dir_all(trust_host_path.parent().unwrap()).unwrap();
fs::write(&trust_host_path, b"").unwrap();
let resolved = resolve_target_program_with_path(
&root,
"trust",
Some(std::ffi::OsStr::new("/custom/bin")),
);
fs::remove_dir_all(&root).unwrap();
assert_eq!(resolved, PathBuf::from("/custom/bin/trust"));
}
#[test]
fn resolve_target_program_leaves_missing_program_unqualified() {
let root = unique_test_dir("target-command-missing");
fs::create_dir_all(&root).unwrap();
let resolved =
resolve_target_program_with_path(&root, "missing-tool", Some(std::ffi::OsStr::new("")));
fs::remove_dir_all(&root).unwrap();
assert_eq!(resolved, PathBuf::from("missing-tool"));
}
fn unique_test_dir(name: &str) -> PathBuf {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
std::env::temp_dir().join(format!("ca-certs-{name}-{}-{nonce}", std::process::id()))
}
#[test]
fn certdata_parser_extracts_revision_and_objects() {
let doc = parse_certdata(SAMPLE_CERTDATA).unwrap();