build: stop turning read failures into silently wrong metadata
Two read_file(...).unwrap_or_default() calls masqueraded IO errors as empty data: an unreadable debian/files became 'binary build with no binary artifacts found; cannot distribute', and an unreadable dpkg status file produced an empty Installed-Build-Depends. Tolerate a missing debian/files (first build in a fresh tree) but propagate real read errors, and hard-error on an unreadable status file like the source-build path does. installed_build_depends_from_content also returned a bare newline for zero entries, defeating render_buildinfo's empty-guard and emitting a malformed 'Installed-Build-Depends:' field with a blank continuation; it now returns an empty string so the field is omitted.
This commit is contained in:
+78
-5
@@ -75,9 +75,17 @@ pub fn generate_binary_metadata(
|
||||
let control_content = ctx.read_file(&package_dir.join("debian/control"))?;
|
||||
let control = ControlInfo::parse_content(&control_content)?;
|
||||
|
||||
let files_content = ctx
|
||||
.read_file(&package_dir.join("debian/files"))
|
||||
.unwrap_or_default();
|
||||
// A missing `debian/files` is tolerated (first binary build in a fresh
|
||||
// tree has nothing registered yet; that surfaces below as the "no binary
|
||||
// artifacts" error), like `FilesList::load`. Any other read failure must
|
||||
// not be silently mistaken for an empty registry.
|
||||
let files_path = package_dir.join("debian/files");
|
||||
let files_content = if ctx.exists(&files_path)? {
|
||||
ctx.read_file(&files_path)
|
||||
.map_err(|e| format!("cannot read '{}': {}", files_path.display(), e))?
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let mut files_list = FilesList::parse(&files_content)?;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
@@ -208,9 +216,13 @@ pub fn generate_binary_metadata(
|
||||
// ------------------------------------------------------------------
|
||||
// Installed-Build-Depends closure over the context status database
|
||||
// ------------------------------------------------------------------
|
||||
// Like the source-build path, a status database that cannot be read is
|
||||
// a hard error: silently treating it as empty would drop (or gut) the
|
||||
// `Installed-Build-Depends` field of the produced metadata.
|
||||
let status_path = Path::new("/var/lib/dpkg/status");
|
||||
let status_content = ctx
|
||||
.read_file(Path::new("/var/lib/dpkg/status"))
|
||||
.unwrap_or_default();
|
||||
.read_file(status_path)
|
||||
.map_err(|e| format!("cannot read status file '{}': {}", status_path.display(), e))?;
|
||||
let bd_fields = [
|
||||
control.source.get("Build-Depends").unwrap_or(""),
|
||||
control.source.get("Build-Depends-Arch").unwrap_or(""),
|
||||
@@ -650,4 +662,65 @@ Description: test package
|
||||
assert!(err.contains("previous version"), "{err}");
|
||||
assert!(err.contains("unbalanced parenthesis"), "{err}");
|
||||
}
|
||||
|
||||
/// An unreadable `debian/files` (e.g. permissions) must fail the
|
||||
/// metadata generation with an error naming the read failure, instead of
|
||||
/// being silently treated as an empty registry and reported as "no
|
||||
/// binary artifacts found". A *missing* file stays tolerated (first
|
||||
/// build in a fresh tree); the distinction matters.
|
||||
#[test]
|
||||
fn unreadable_debian_files_errors_instead_of_empty_registry() {
|
||||
if crate::utils::root::is_root().unwrap_or(false) {
|
||||
// Root can read files regardless of permissions.
|
||||
return;
|
||||
}
|
||||
let changelog = "\
|
||||
hello (1.0-1) unstable; urgency=medium
|
||||
|
||||
* Regular build.
|
||||
|
||||
-- A B <a@b.c> Mon, 01 Jan 2024 00:00:00 +0000
|
||||
";
|
||||
let control = "\
|
||||
Source: hello
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Maintainer: A B <a@b.c>
|
||||
|
||||
Package: hello
|
||||
Architecture: all
|
||||
Description: test package
|
||||
";
|
||||
let base = tempfile::tempdir().expect("tempdir");
|
||||
let tree = base.path().join("hello-1.0");
|
||||
std::fs::create_dir_all(tree.join("debian")).expect("mkdir tree");
|
||||
std::fs::write(tree.join("debian/changelog"), changelog).expect("write changelog");
|
||||
std::fs::write(tree.join("debian/control"), control).expect("write control");
|
||||
let files_path = tree.join("debian/files");
|
||||
std::fs::write(&files_path, "hello_1.0-1_all.deb devel optional\n").expect("write files");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
std::fs::set_permissions(&files_path, std::fs::Permissions::from_mode(0o000))
|
||||
.expect("chmod files");
|
||||
}
|
||||
|
||||
let ctx = Arc::new(
|
||||
crate::context::Context::new(crate::context::ContextConfig::Local).expect("context"),
|
||||
);
|
||||
let opts = BinaryMetadataOptions {
|
||||
profiles: Vec::new(),
|
||||
vendor: "debian".to_string(),
|
||||
exported_env: BTreeMap::new(),
|
||||
build_arch: "amd64".to_string(),
|
||||
host_arch: "amd64".to_string(),
|
||||
};
|
||||
let err = generate_binary_metadata(&ctx, &tree, base.path(), &opts)
|
||||
.expect_err("unreadable debian/files must fail with a read error");
|
||||
let err = err.to_string();
|
||||
assert!(err.contains("cannot read"), "{err}");
|
||||
assert!(err.contains("debian/files"), "{err}");
|
||||
#[cfg(unix)]
|
||||
assert!(err.contains("Permission denied"), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user