Skip to content

Commit 0c18195

Browse files
fix: patch for tarbug
1 parent f732623 commit 0c18195

File tree

3 files changed

+240
-98
lines changed

3 files changed

+240
-98
lines changed

src/archive.rs

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ use async_std::{
1515
use pin_project::pin_project;
1616

1717
use crate::{
18+
Entry, GnuExtSparseHeader, GnuSparseHeader, Header,
1819
entry::{EntryFields, EntryIo},
1920
error::TarError,
20-
other, Entry, GnuExtSparseHeader, GnuSparseHeader, Header,
21+
other,
22+
pax::pax_extensions,
2123
};
2224

2325
/// A top-level representation of an archive file.
@@ -171,7 +173,7 @@ impl<R: Read + Unpin> Archive<R> {
171173

172174
Ok(Entries {
173175
archive: self,
174-
current: (0, None, 0, None),
176+
current: State::default(),
175177
fields: None,
176178
gnu_longlink: None,
177179
gnu_longname: None,
@@ -263,12 +265,21 @@ impl<R: Read + Unpin> Archive<R> {
263265
}
264266
}
265267

268+
#[derive(Debug, Default)]
269+
struct State {
270+
next: u64,
271+
current_header: Option<Header>,
272+
current_header_pos: usize,
273+
current_ext: Option<GnuExtSparseHeader>,
274+
pax_extensions: Option<Vec<u8>>,
275+
}
276+
266277
/// Stream of `Entry`s.
267278
#[pin_project]
268279
#[derive(Debug)]
269280
pub struct Entries<R: Read + Unpin> {
270281
archive: Archive<R>,
271-
current: (u64, Option<Header>, usize, Option<GnuExtSparseHeader>),
282+
current: State,
272283
fields: Option<EntryFields<Archive<R>>>,
273284
gnu_longname: Option<Vec<u8>>,
274285
gnu_longlink: Option<Vec<u8>>,
@@ -300,7 +311,13 @@ impl<R: Read + Unpin> Stream for Entries<R> {
300311
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
301312
let mut this = self.project();
302313
loop {
303-
let (next, current_header, current_header_pos, _) = &mut this.current;
314+
let State {
315+
next,
316+
current_header,
317+
current_header_pos,
318+
pax_extensions,
319+
..
320+
} = &mut this.current;
304321

305322
let fields = if let Some(fields) = this.fields.as_mut() {
306323
fields
@@ -310,6 +327,7 @@ impl<R: Read + Unpin> Stream for Entries<R> {
310327
next,
311328
current_header,
312329
current_header_pos,
330+
pax_extensions.as_deref(),
313331
cx
314332
))));
315333
continue;
@@ -350,6 +368,7 @@ impl<R: Read + Unpin> Stream for Entries<R> {
350368
))));
351369
}
352370
*this.pax_extensions = Some(ready_err!(Pin::new(fields).poll_read_all(cx)));
371+
this.current.pax_extensions = this.pax_extensions.clone();
353372
*this.fields = None;
354373
continue;
355374
}
@@ -358,12 +377,17 @@ impl<R: Read + Unpin> Stream for Entries<R> {
358377
fields.long_linkname = this.gnu_longlink.take();
359378
fields.pax_extensions = this.pax_extensions.take();
360379

361-
let (next, _, current_pos, current_ext) = &mut this.current;
380+
let State {
381+
next,
382+
current_header_pos,
383+
current_ext,
384+
..
385+
} = &mut this.current;
362386
ready_err!(poll_parse_sparse_header(
363387
this.archive,
364388
next,
365389
current_ext,
366-
current_pos,
390+
current_header_pos,
367391
fields,
368392
cx
369393
));
@@ -385,7 +409,7 @@ impl<R: Read + Unpin> Stream for RawEntries<R> {
385409
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
386410
let archive = self.archive.clone();
387411
let (next, current_header, current_header_pos) = &mut self.current;
388-
poll_next_raw(&archive, next, current_header, current_header_pos, cx)
412+
poll_next_raw(&archive, next, current_header, current_header_pos, None, cx)
389413
}
390414
}
391415

@@ -394,6 +418,7 @@ fn poll_next_raw<R: Read + Unpin>(
394418
next: &mut u64,
395419
current_header: &mut Option<Header>,
396420
current_header_pos: &mut usize,
421+
pax_extensions_data: Option<&[u8]>,
397422
cx: &mut Context<'_>,
398423
) -> Poll<Option<io::Result<Entry<Archive<R>>>>> {
399424
let mut header_pos = *next;
@@ -456,11 +481,65 @@ fn poll_next_raw<R: Read + Unpin>(
456481
}
457482

458483
let file_pos = *next;
459-
let size = header.entry_size()?;
460484

461-
let data = EntryIo::Data(archive.clone().take(size));
485+
let mut header = current_header.take().unwrap();
486+
487+
// when pax extensions are available, the size should come from there.
488+
let mut size = header.entry_size()?;
489+
490+
// the size above will be overriden by the pax data if it has a size field.
491+
// same for uid and gid, which will be overridden in the header itself.
492+
if let Some(pax_extensions_data) = pax_extensions_data {
493+
let pax = pax_extensions(pax_extensions_data);
494+
for extension in pax {
495+
let extension = extension.map_err(|_e| other("pax extensions invalid"))?;
496+
497+
// ignore keys that aren't parsable as a string at this stage.
498+
// that isn't relevant to the size/uid/gid processing.
499+
let Some(key) = extension.key().ok() else {
500+
continue;
501+
};
462502

463-
let header = current_header.take().unwrap();
503+
match key {
504+
"size" => {
505+
let size_str = extension
506+
.value()
507+
.map_err(|_e| other("failed to parse pax size as string"))?;
508+
size = size_str
509+
.parse::<u64>()
510+
.map_err(|_e| other("failed to parse pax size"))?;
511+
}
512+
513+
"uid" => {
514+
let uid_str = extension
515+
.value()
516+
.map_err(|_e| other("failed to parse pax uid as string"))?;
517+
header.set_uid(
518+
uid_str
519+
.parse::<u64>()
520+
.map_err(|_e| other("failed to parse pax uid"))?,
521+
);
522+
}
523+
524+
"gid" => {
525+
let gid_str = extension
526+
.value()
527+
.map_err(|_e| other("failed to parse pax gid as string"))?;
528+
header.set_gid(
529+
gid_str
530+
.parse::<u64>()
531+
.map_err(|_e| other("failed to parse pax gid"))?,
532+
);
533+
}
534+
535+
_ => {
536+
continue;
537+
}
538+
}
539+
}
540+
}
541+
542+
let data = EntryIo::Data(archive.clone().take(size));
464543

465544
let ArchiveInner {
466545
unpack_xattrs,

0 commit comments

Comments
 (0)