Skip to content

Commit f1dc7aa

Browse files
committed
archive: work from leaves up when unpacking directories
1 parent f4f439c commit f1dc7aa

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

src/archive.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,16 @@ impl Archive<dyn Read + '_> {
222222
file.unpack_in(dst)?;
223223
}
224224
}
225-
for mut dir in directories {
225+
226+
// Work from leaves up, with the assumption* that entries for
227+
// parent directories preceed their children. Otherwise when
228+
// a parent directory has no write permission, any empty
229+
// subdirectories beneath it cannot be created.
230+
//
231+
// [*] If sufficiently motivated, one can create an archive
232+
// which violates this. In practice, archives typically are
233+
// structured this way.
234+
for mut dir in directories.into_iter().rev() {
226235
dir.unpack_in(dst)?;
227236
}
228237

tests/all.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,33 @@ fn read_only_directory_containing_files() {
13171317
assert!(ar.unpack(td.path()).is_ok());
13181318
}
13191319

1320+
#[test]
1321+
fn read_only_directory_containing_empty_directory() {
1322+
let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1323+
1324+
let mut b = Builder::new(Vec::<u8>::new());
1325+
1326+
let mut h = Header::new_gnu();
1327+
t!(h.set_path("dir/"));
1328+
h.set_size(0);
1329+
h.set_entry_type(EntryType::dir());
1330+
h.set_mode(0o444);
1331+
h.set_cksum();
1332+
t!(b.append(&h, "".as_bytes()));
1333+
1334+
let mut h = Header::new_gnu();
1335+
t!(h.set_path("dir/subdir"));
1336+
h.set_size(0);
1337+
h.set_entry_type(EntryType::dir());
1338+
h.set_mode(0o755);
1339+
h.set_cksum();
1340+
t!(b.append(&h, "".as_bytes()));
1341+
1342+
let contents = t!(b.into_inner());
1343+
let mut ar = Archive::new(&contents[..]);
1344+
assert!(ar.unpack(td.path()).is_ok());
1345+
}
1346+
13201347
// This test was marked linux only due to macOS CI can't handle `set_current_dir` correctly
13211348
#[test]
13221349
#[cfg(target_os = "linux")]

0 commit comments

Comments
 (0)