@@ -15,9 +15,11 @@ use async_std::{
1515use pin_project:: pin_project;
1616
1717use 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 ) ]
269280pub 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