diff --git a/src/deb/mod.rs b/src/deb/mod.rs index 1aab157..3fc4166 100644 --- a/src/deb/mod.rs +++ b/src/deb/mod.rs @@ -413,11 +413,13 @@ pub(crate) fn find_package_directory( let entries = ctx.list_files(package_parent)?; let mut found_dirs = Vec::new(); for entry in entries { - if entry.is_dir() { - if let Some(file_name) = entry.file_name() { - found_dirs.push(file_name.to_string_lossy().into_owned()); - } - log::debug!(" - {}", entry.display()); + // list_files yields context-relative paths (e.g. rooted inside + // the chroot for an unshare context): classify through the + // context, a host-side stat would miss every entry. + let is_dir = ctx.is_dir(&entry)?; + log::debug!(" - {}", entry.display()); + if is_dir && let Some(file_name) = entry.file_name() { + found_dirs.push(file_name.to_string_lossy().into_owned()); } } @@ -501,6 +503,51 @@ mod tests { } } + /// An unshare context mapped over `chroot_root`, parented on a local + /// context like the ephemeral build contexts are: exists/list_files/ + /// is_dir answer through the path mapping, no namespace privileges + /// needed. + fn unshare_test_context(chroot_root: &Path) -> Context { + let base = Context::new(crate::context::ContextConfig::Local).unwrap(); + Context::with_parent( + crate::context::ContextConfig::Unshare { + path: chroot_root.to_string_lossy().to_string(), + parent: None, + }, + Arc::new(base), + ) + } + + /// The staging-area listing must classify entries through the context: + /// an unshare context returns build-root-relative paths that a host-side + /// stat never sees (they live under the chroot root on the host), which + /// used to silently empty the 'Found directories' list of the search + /// failure message — and with it every hint about the actual layout. + #[test] + fn find_package_directory_lists_staged_directories_through_the_context() { + let chroot = tempfile::tempdir().unwrap(); + // Staged parent holding a single tree whose name matches none of + // the search patterns (the embedded-caller layout: /tree) + let staged_parent = chroot.path().join("tmp/pkh-build-1/j-42"); + std::fs::create_dir_all(staged_parent.join("tree/debian")).unwrap(); + + let ctx = unshare_test_context(chroot.path()); + let err = find_package_directory( + Path::new("/tmp/pkh-build-1/j-42"), + "bc", + "1.07.1-1ubuntu1", + "questing", + &ctx, + ) + .expect_err("no candidate matches a tree named 'tree'"); + + let message = err.to_string(); + assert!( + message.contains("Found directories: tree"), + "error should list the staged directories through the context: {message}" + ); + } + async fn test_build_end_to_end( package: &str, series: &str,