Skip to content

Commit 0139bff

Browse files
committed
fix: auto-rewind source reader on seekable entry points
Callers previously had to manually `seek(SeekFrom::Start(0))` between successive uses of the same reader (e.g. list_archive_files followed by uncompress_archive_file), because libarchive only recognizes the archive format from byte 0. Forgetting the rewind surfaced as an opaque "unrecognized format" error. Since the public entry points already require `Read + Seek`, rewind the reader at the top of `run_with_archive` and `ArchiveIterator::new` so reader reuse works out of the box. Closes #117
1 parent b868768 commit 0139bff

4 files changed

Lines changed: 36 additions & 0 deletions

File tree

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
## [Unreleased] - ReleaseDate
66

7+
* Rewind the source reader at the start of every seekable entry point
8+
(`list_archive_files`, `list_archive_entries`, `uncompress_archive`,
9+
`uncompress_archive_file`, `ArchiveIterator`, and their `_with_encoding`
10+
variants), so callers can reuse the same reader across successive calls
11+
without a manual `seek(SeekFrom::Start(0))` between them [#117]
712
* Fix RAR extraction failing with "Can't decompress an entry marked as a
813
directory" whenever the archive contains directory entries. Data reads are
914
now skipped for directory entries in `uncompress_archive`,

src/iterator.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ impl<R: Read + Seek> ArchiveIterator<R> {
180180
R: Read + Seek,
181181
{
182182
let utf8_guard = ffi::UTF8LocaleGuard::new();
183+
// libarchive only sniffs the format from offset 0.
184+
source.seek(SeekFrom::Start(0))?;
183185
crate::zip_preflight::reject_unsupported_zip_methods(&mut source)?;
184186
let reader = source;
185187
let buffer = [0; READER_BUFFER_SIZE];

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ where
512512
R: Read + Seek,
513513
{
514514
let _utf8_guard = ffi::UTF8LocaleGuard::new();
515+
// libarchive only sniffs the format from offset 0.
516+
reader.seek(SeekFrom::Start(0))?;
515517
zip_preflight::reject_unsupported_zip_methods(&mut reader)?;
516518
unsafe {
517519
let archive_entry: *mut ffi::archive_entry = std::ptr::null_mut();

tests/integration_test.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,33 @@ fn successfully_list_archive_files() {
153153
);
154154
}
155155

156+
#[test]
157+
fn reader_is_rewound_between_calls() {
158+
let mut source = std::fs::File::open("tests/fixtures/tree.tar").unwrap();
159+
160+
let listed = list_archive_files(&mut source).unwrap();
161+
assert!(listed.contains(&"tree/branch2/leaf".to_string()));
162+
163+
let mut target = Vec::new();
164+
let written = uncompress_archive_file(&mut source, &mut target, "tree/branch2/leaf")
165+
.expect("extract should succeed even though the reader was left past the archive");
166+
assert_eq!(written, 14);
167+
assert_eq!(String::from_utf8_lossy(&target), "Goodbye World\n");
168+
169+
let listed_again = list_archive_files(&mut source).unwrap();
170+
assert_eq!(listed, listed_again);
171+
172+
let names: Vec<String> = ArchiveIteratorBuilder::new(&mut source)
173+
.build()
174+
.unwrap()
175+
.filter_map(|c| match c {
176+
ArchiveContents::StartOfEntry(name, _) => Some(name),
177+
_ => None,
178+
})
179+
.collect();
180+
assert_eq!(names, listed);
181+
}
182+
156183
#[test]
157184
fn successfully_list_archive_entries() {
158185
let source = std::fs::File::open("tests/fixtures/tree.tar").unwrap();

0 commit comments

Comments
 (0)