pull: create the addon directory when unwrapping a multi-orig component

unwrap_into pointed the component's unwrap at '<root>/<addon>' but
never created that directory: unwrap_wrapper moves the wrapper's
entries into it by rename, and a rename whose target parent is
missing fails with ENOENT - every entry, aborting the whole pull.
The multi-orig unit test exercised unwrap_wrapper directly and could
not see it (dovecot).

Also exercise the component through unwrap_into in the test, and
assert the wrapper's own layout does not leak to the root - the
shape dpkg-source -b rejects as unexpected upstream changes.
This commit is contained in:
2026-10-02 01:43:54 +02:00
parent 5e4b9cb231
commit 14704db777
+23 -9
View File
@@ -173,6 +173,7 @@ fn unwrap_into(dir: &Path, wrapper: &Path, addon: Option<&str>) -> Result<(), Bo
if target.is_dir() {
std::fs::remove_dir_all(&target)?;
}
std::fs::create_dir_all(&target)?;
target
}
None => dir.to_path_buf(),
@@ -1396,19 +1397,26 @@ mod tests {
#[test]
fn test_unwrap_wrapper_of_second_multi_orig_component() {
// Regression: multi-orig packages (e.g. dovecot) extract a second
// component into a tree root the first component already filled;
// recognizing the wrapper from the shape of the root skipped the
// second component's unwrap and left its sources nested one level
// too deep
// component into a tree root the first component already filled,
// and dpkg-source expects the component's content at
// '<root>/<addon>' ('pigeonhole' for '...orig-pigeonhole.tar.gz'),
// not the wrapper's own layout at the root. The addon directory
// used to never be created: every move into it failed with ENOENT
// and the pull aborted.
let temp_dir = tempfile::tempdir().unwrap();
let tree = temp_dir.path().join("tree");
let main = temp_dir.path().join("main.orig.tar");
std::fs::write(&main, build_tar(&[("dovecot-2.4.5/README", b"main")])).unwrap();
let extracted = extract_tar_archive(&main, &tree, None, |f| f).unwrap();
unwrap_wrapper(&tree, &extracted.wrapper.unwrap()).unwrap();
unwrap_into(
&tree,
&extracted.wrapper.unwrap(),
component_addon("main.orig.tar"),
)
.unwrap();
let component = temp_dir.path().join("component.orig.tar");
let component = temp_dir.path().join("pkg_1.0.orig-pigeonhole.tar.gz");
std::fs::write(
&component,
build_tar(&[
@@ -1418,20 +1426,26 @@ mod tests {
)
.unwrap();
let extracted = extract_tar_archive(&component, &tree, None, |f| f).unwrap();
unwrap_wrapper(&tree, &extracted.wrapper.unwrap()).unwrap();
unwrap_into(
&tree,
&extracted.wrapper.unwrap(),
component_addon("pkg_1.0.orig-pigeonhole.tar.gz"),
)
.unwrap();
assert_eq!(
std::fs::read_to_string(tree.join("README")).unwrap(),
"main"
);
assert_eq!(
std::fs::read_to_string(tree.join("update-version.sh")).unwrap(),
std::fs::read_to_string(tree.join("pigeonhole/update-version.sh")).unwrap(),
"sh"
);
assert_eq!(
std::fs::read_to_string(tree.join("pigeonhole/x")).unwrap(),
std::fs::read_to_string(tree.join("pigeonhole/pigeonhole/x")).unwrap(),
"ph"
);
assert!(!tree.join("update-version.sh").exists());
assert!(!tree.join("dovecot-2.4.5").exists());
assert!(!tree.join("dovecot-pigeonhole-2.4.5").exists());
}