@@ -11,9 +11,60 @@ use std::iter::repeat;
1111use std:: path:: { Path , PathBuf } ;
1212
1313use filetime:: FileTime ;
14+ use rand:: rngs:: SmallRng ;
15+ use rand:: { Rng , SeedableRng } ;
1416use tar:: { Archive , Builder , Entries , Entry , EntryType , Header , HeaderMode } ;
1517use tempfile:: { Builder as TempBuilder , TempDir } ;
1618
19+ /// A reader wrapper that returns partial results from `read()` to exercise
20+ /// parsers that might assume `read()` fills the entire buffer.
21+ ///
22+ /// Each call returns between 1 and buf.len() bytes, biased toward small
23+ /// reads by taking the minimum of two uniform samples. This gives roughly
24+ /// quadratic density toward 1, so small reads (1-10 bytes) occur frequently
25+ /// while large reads still happen. Uses a deterministic seeded RNG so
26+ /// tests remain reproducible.
27+ struct RandomReader < R > {
28+ inner : R ,
29+ rng : SmallRng ,
30+ }
31+
32+ impl < R > RandomReader < R > {
33+ fn new ( inner : R ) -> Self {
34+ RandomReader {
35+ inner,
36+ rng : SmallRng :: seed_from_u64 ( 0 ) ,
37+ }
38+ }
39+ }
40+
41+ impl < R : Read > Read for RandomReader < R > {
42+ fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
43+ if buf. is_empty ( ) {
44+ return self . inner . read ( buf) ;
45+ }
46+ // Take the min of two uniform samples to bias toward small reads.
47+ let a = self . rng . gen_range ( 1 ..=buf. len ( ) ) ;
48+ let b = self . rng . gen_range ( 1 ..=buf. len ( ) ) ;
49+ self . inner . read ( & mut buf[ ..a. min ( b) ] )
50+ }
51+ }
52+
53+ /// Convenience: wrap a byte slice in a RandomReader<Cursor<_>>.
54+ ///
55+ /// The RNG is seeded from a hash of the data, so different archives
56+ /// exercise different read-size sequences while remaining deterministic.
57+ fn random_cursor_reader < D : AsRef < [ u8 ] > > ( data : D ) -> RandomReader < Cursor < D > > {
58+ use std:: hash:: { Hash , Hasher } ;
59+ let mut hasher = std:: collections:: hash_map:: DefaultHasher :: new ( ) ;
60+ data. as_ref ( ) . hash ( & mut hasher) ;
61+ let seed = hasher. finish ( ) ;
62+ RandomReader {
63+ inner : Cursor :: new ( data) ,
64+ rng : SmallRng :: seed_from_u64 ( seed) ,
65+ }
66+ }
67+
1768macro_rules! tar {
1869 ( $e: expr) => {
1970 & include_bytes!( concat!( "archives/" , $e) ) [ ..]
@@ -30,14 +81,15 @@ fn simple_concat() {
3081 let mut archive_bytes = Vec :: new ( ) ;
3182 archive_bytes. extend ( bytes) ;
3283
33- let original_names: Vec < String > = decode_names ( & mut Archive :: new ( Cursor :: new ( & archive_bytes) ) ) ;
84+ let original_names: Vec < String > =
85+ decode_names ( & mut Archive :: new ( random_cursor_reader ( & archive_bytes) ) ) ;
3486 let expected: Vec < & str > = original_names. iter ( ) . map ( |n| n. as_str ( ) ) . collect ( ) ;
3587
3688 // concat two archives (with null in-between);
3789 archive_bytes. extend ( bytes) ;
3890
3991 // test now that when we read the archive, it stops processing at the first zero header.
40- let actual = decode_names ( & mut Archive :: new ( Cursor :: new ( & archive_bytes) ) ) ;
92+ let actual = decode_names ( & mut Archive :: new ( random_cursor_reader ( & archive_bytes) ) ) ;
4193 assert_eq ! ( expected, actual) ;
4294
4395 // extend expected by itself.
@@ -48,7 +100,7 @@ fn simple_concat() {
48100 o
49101 } ;
50102
51- let mut ar = Archive :: new ( Cursor :: new ( & archive_bytes) ) ;
103+ let mut ar = Archive :: new ( random_cursor_reader ( & archive_bytes) ) ;
52104 ar. set_ignore_zeros ( true ) ;
53105
54106 let actual = decode_names ( & mut ar) ;
@@ -71,7 +123,7 @@ fn simple_concat() {
71123
72124#[ test]
73125fn header_impls ( ) {
74- let mut ar = Archive :: new ( Cursor :: new ( tar ! ( "simple.tar" ) ) ) ;
126+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "simple.tar" ) ) ) ;
75127 let hn = Header :: new_old ( ) ;
76128 let hnb = hn. as_bytes ( ) ;
77129 for file in ar. entries ( ) . unwrap ( ) {
@@ -86,7 +138,7 @@ fn header_impls() {
86138
87139#[ test]
88140fn header_impls_missing_last_header ( ) {
89- let mut ar = Archive :: new ( Cursor :: new ( tar ! ( "simple_missing_last_header.tar" ) ) ) ;
141+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "simple_missing_last_header.tar" ) ) ) ;
90142 let hn = Header :: new_old ( ) ;
91143 let hnb = hn. as_bytes ( ) ;
92144 for file in ar. entries ( ) . unwrap ( ) {
@@ -101,7 +153,7 @@ fn header_impls_missing_last_header() {
101153
102154#[ test]
103155fn reading_files ( ) {
104- let rdr = Cursor :: new ( tar ! ( "reading_files.tar" ) ) ;
156+ let rdr = random_cursor_reader ( tar ! ( "reading_files.tar" ) ) ;
105157 let mut ar = Archive :: new ( rdr) ;
106158 let mut entries = ar. entries ( ) . unwrap ( ) ;
107159
@@ -247,7 +299,7 @@ fn reading_entries_common<R: Read>(mut entries: Entries<R>) {
247299
248300#[ test]
249301fn reading_entries ( ) {
250- let rdr = Cursor :: new ( tar ! ( "reading_files.tar" ) ) ;
302+ let rdr = random_cursor_reader ( tar ! ( "reading_files.tar" ) ) ;
251303 let mut ar = Archive :: new ( rdr) ;
252304 reading_entries_common ( ar. entries ( ) . unwrap ( ) ) ;
253305}
@@ -321,7 +373,7 @@ fn check_dirtree(td: &TempDir) {
321373#[ test]
322374fn extracting_directories ( ) {
323375 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
324- let rdr = Cursor :: new ( tar ! ( "directory.tar" ) ) ;
376+ let rdr = random_cursor_reader ( tar ! ( "directory.tar" ) ) ;
325377 let mut ar = Archive :: new ( rdr) ;
326378 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
327379 check_dirtree ( & td) ;
@@ -333,7 +385,7 @@ fn extracting_duplicate_file_fail() {
333385 let path_present = td. path ( ) . join ( "a" ) ;
334386 File :: create ( path_present) . unwrap ( ) ;
335387
336- let rdr = Cursor :: new ( tar ! ( "reading_files.tar" ) ) ;
388+ let rdr = random_cursor_reader ( tar ! ( "reading_files.tar" ) ) ;
337389 let mut ar = Archive :: new ( rdr) ;
338390 ar. set_overwrite ( false ) ;
339391 if let Err ( err) = ar. unpack ( td. path ( ) ) {
@@ -355,7 +407,7 @@ fn extracting_duplicate_file_succeed() {
355407 let path_present = td. path ( ) . join ( "a" ) ;
356408 File :: create ( path_present) . unwrap ( ) ;
357409
358- let rdr = Cursor :: new ( tar ! ( "reading_files.tar" ) ) ;
410+ let rdr = random_cursor_reader ( tar ! ( "reading_files.tar" ) ) ;
359411 let mut ar = Archive :: new ( rdr) ;
360412 ar. set_overwrite ( true ) ;
361413 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
@@ -368,7 +420,7 @@ fn extracting_duplicate_link_fail() {
368420 let path_present = td. path ( ) . join ( "lnk" ) ;
369421 std:: os:: unix:: fs:: symlink ( "file" , path_present) . unwrap ( ) ;
370422
371- let rdr = Cursor :: new ( tar ! ( "link.tar" ) ) ;
423+ let rdr = random_cursor_reader ( tar ! ( "link.tar" ) ) ;
372424 let mut ar = Archive :: new ( rdr) ;
373425 ar. set_overwrite ( false ) ;
374426 if let Err ( err) = ar. unpack ( td. path ( ) ) {
@@ -391,7 +443,7 @@ fn extracting_duplicate_link_succeed() {
391443 let path_present = td. path ( ) . join ( "lnk" ) ;
392444 std:: os:: unix:: fs:: symlink ( "file" , path_present) . unwrap ( ) ;
393445
394- let rdr = Cursor :: new ( tar ! ( "link.tar" ) ) ;
446+ let rdr = random_cursor_reader ( tar ! ( "link.tar" ) ) ;
395447 let mut ar = Archive :: new ( rdr) ;
396448 ar. set_overwrite ( true ) ;
397449 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
@@ -406,7 +458,7 @@ fn xattrs() {
406458 . prefix ( "tar-rs" )
407459 . tempdir_in ( "/var/tmp" )
408460 . unwrap ( ) ;
409- let rdr = Cursor :: new ( tar ! ( "xattrs.tar" ) ) ;
461+ let rdr = random_cursor_reader ( tar ! ( "xattrs.tar" ) ) ;
410462 let mut ar = Archive :: new ( rdr) ;
411463 ar. set_unpack_xattrs ( true ) ;
412464 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
@@ -424,7 +476,7 @@ fn no_xattrs() {
424476 . prefix ( "tar-rs" )
425477 . tempdir_in ( "/var/tmp" )
426478 . unwrap ( ) ;
427- let rdr = Cursor :: new ( tar ! ( "xattrs.tar" ) ) ;
479+ let rdr = random_cursor_reader ( tar ! ( "xattrs.tar" ) ) ;
428480 let mut ar = Archive :: new ( rdr) ;
429481 ar. set_unpack_xattrs ( false ) ;
430482 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
@@ -580,7 +632,7 @@ fn append_dir_all_does_not_work_on_non_directory() {
580632#[ test]
581633fn extracting_duplicate_dirs ( ) {
582634 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
583- let rdr = Cursor :: new ( tar ! ( "duplicate_dirs.tar" ) ) ;
635+ let rdr = random_cursor_reader ( tar ! ( "duplicate_dirs.tar" ) ) ;
584636 let mut ar = Archive :: new ( rdr) ;
585637 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
586638
@@ -738,7 +790,7 @@ fn extracting_malicious_tarball() {
738790
739791#[ test]
740792fn octal_spaces ( ) {
741- let rdr = Cursor :: new ( tar ! ( "spaces.tar" ) ) ;
793+ let rdr = random_cursor_reader ( tar ! ( "spaces.tar" ) ) ;
742794 let mut ar = Archive :: new ( rdr) ;
743795
744796 let entry = ar. entries ( ) . unwrap ( ) . next ( ) . unwrap ( ) . unwrap ( ) ;
@@ -778,15 +830,15 @@ fn extracting_malformed_tar_null_blocks() {
778830#[ test]
779831fn empty_filename ( ) {
780832 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
781- let rdr = Cursor :: new ( tar ! ( "empty_filename.tar" ) ) ;
833+ let rdr = random_cursor_reader ( tar ! ( "empty_filename.tar" ) ) ;
782834 let mut ar = Archive :: new ( rdr) ;
783835 assert ! ( ar. unpack( td. path( ) ) . is_ok( ) ) ;
784836}
785837
786838#[ test]
787839fn file_times ( ) {
788840 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
789- let rdr = Cursor :: new ( tar ! ( "file_times.tar" ) ) ;
841+ let rdr = random_cursor_reader ( tar ! ( "file_times.tar" ) ) ;
790842 let mut ar = Archive :: new ( rdr) ;
791843 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
792844
@@ -857,7 +909,7 @@ fn backslash_treated_well() {
857909#[ test]
858910#[ cfg( unix) ]
859911fn set_mask ( ) {
860- use :: std:: os:: unix:: fs:: PermissionsExt ;
912+ use std:: os:: unix:: fs:: PermissionsExt ;
861913 let mut ar = tar:: Builder :: new ( Vec :: new ( ) ) ;
862914
863915 let mut header = tar:: Header :: new_gnu ( ) ;
@@ -903,7 +955,7 @@ fn nul_bytes_in_path() {
903955
904956#[ test]
905957fn links ( ) {
906- let mut ar = Archive :: new ( Cursor :: new ( tar ! ( "link.tar" ) ) ) ;
958+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "link.tar" ) ) ) ;
907959 let mut entries = ar. entries ( ) . unwrap ( ) ;
908960 let link = entries. next ( ) . unwrap ( ) . unwrap ( ) ;
909961 assert_eq ! (
@@ -918,7 +970,7 @@ fn links() {
918970#[ cfg( unix) ] // making symlinks on windows is hard
919971fn unpack_links ( ) {
920972 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
921- let mut ar = Archive :: new ( Cursor :: new ( tar ! ( "link.tar" ) ) ) ;
973+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "link.tar" ) ) ) ;
922974 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
923975
924976 let md = fs:: symlink_metadata ( td. path ( ) . join ( "lnk" ) ) . unwrap ( ) ;
@@ -936,7 +988,7 @@ fn unpack_links() {
936988
937989#[ test]
938990fn pax_size ( ) {
939- let mut ar = Archive :: new ( tar ! ( "pax_size.tar" ) ) ;
991+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "pax_size.tar" ) ) ) ;
940992 let mut entries = ar. entries ( ) . unwrap ( ) ;
941993 let mut entry = entries. next ( ) . unwrap ( ) . unwrap ( ) ;
942994 let mut attributes = entry. pax_extensions ( ) . unwrap ( ) . unwrap ( ) ;
@@ -956,7 +1008,7 @@ fn pax_size() {
9561008
9571009#[ test]
9581010fn pax_simple ( ) {
959- let mut ar = Archive :: new ( tar ! ( "pax.tar" ) ) ;
1011+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "pax.tar" ) ) ) ;
9601012 let mut entries = ar. entries ( ) . unwrap ( ) ;
9611013
9621014 let mut first = entries. next ( ) . unwrap ( ) . unwrap ( ) ;
@@ -1011,7 +1063,7 @@ fn pax_simple_write() {
10111063
10121064#[ test]
10131065fn pax_path ( ) {
1014- let mut ar = Archive :: new ( tar ! ( "pax2.tar" ) ) ;
1066+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "pax2.tar" ) ) ) ;
10151067 let mut entries = ar. entries ( ) . unwrap ( ) ;
10161068
10171069 let first = entries. next ( ) . unwrap ( ) . unwrap ( ) ;
@@ -1020,7 +1072,7 @@ fn pax_path() {
10201072
10211073#[ test]
10221074fn pax_linkpath ( ) {
1023- let mut ar = Archive :: new ( tar ! ( "pax2.tar" ) ) ;
1075+ let mut ar = Archive :: new ( random_cursor_reader ( tar ! ( "pax2.tar" ) ) ) ;
10241076 let mut links = ar. entries ( ) . unwrap ( ) . skip ( 3 ) . take ( 2 ) ;
10251077
10261078 let long_symlink = links. next ( ) . unwrap ( ) . unwrap ( ) ;
@@ -1192,7 +1244,7 @@ fn encoded_long_name_has_trailing_nul() {
11921244
11931245#[ test]
11941246fn reading_sparse ( ) {
1195- let rdr = Cursor :: new ( tar ! ( "sparse.tar" ) ) ;
1247+ let rdr = random_cursor_reader ( tar ! ( "sparse.tar" ) ) ;
11961248 let mut ar = Archive :: new ( rdr) ;
11971249 let mut entries = ar. entries ( ) . unwrap ( ) ;
11981250
@@ -1242,7 +1294,7 @@ fn reading_sparse() {
12421294
12431295#[ test]
12441296fn extract_sparse ( ) {
1245- let rdr = Cursor :: new ( tar ! ( "sparse.tar" ) ) ;
1297+ let rdr = random_cursor_reader ( tar ! ( "sparse.tar" ) ) ;
12461298 let mut ar = Archive :: new ( rdr) ;
12471299 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
12481300 ar. unpack ( td. path ( ) ) . unwrap ( ) ;
@@ -1295,7 +1347,7 @@ fn extract_sparse() {
12951347
12961348#[ test]
12971349fn large_sparse ( ) {
1298- let rdr = Cursor :: new ( tar ! ( "sparse-large.tar" ) ) ;
1350+ let rdr = random_cursor_reader ( tar ! ( "sparse-large.tar" ) ) ;
12991351 let mut ar = Archive :: new ( rdr) ;
13001352 let mut entries = ar. entries ( ) . unwrap ( ) ;
13011353 // Only check the header info without extracting, as the file is very large,
@@ -1307,7 +1359,7 @@ fn large_sparse() {
13071359
13081360#[ test]
13091361fn sparse_with_trailing ( ) {
1310- let rdr = Cursor :: new ( tar ! ( "sparse-1.tar" ) ) ;
1362+ let rdr = random_cursor_reader ( tar ! ( "sparse-1.tar" ) ) ;
13111363 let mut ar = Archive :: new ( rdr) ;
13121364 let mut entries = ar. entries ( ) . unwrap ( ) ;
13131365 let mut a = entries. next ( ) . unwrap ( ) . unwrap ( ) ;
@@ -1556,7 +1608,7 @@ fn tar_directory_containing_symlink_to_directory() {
15561608#[ test]
15571609fn long_path ( ) {
15581610 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
1559- let rdr = Cursor :: new ( tar ! ( "7z_long_path.tar" ) ) ;
1611+ let rdr = random_cursor_reader ( tar ! ( "7z_long_path.tar" ) ) ;
15601612 let mut ar = Archive :: new ( rdr) ;
15611613 assert ! ( ar. unpack( td. path( ) ) . is_ok( ) ) ;
15621614}
@@ -1571,7 +1623,7 @@ fn unpack_path_larger_than_windows_max_path() {
15711623 . tempdir ( )
15721624 . unwrap ( ) ;
15731625 // directory in 7z_long_path.tar is over 100 chars
1574- let rdr = Cursor :: new ( tar ! ( "7z_long_path.tar" ) ) ;
1626+ let rdr = random_cursor_reader ( tar ! ( "7z_long_path.tar" ) ) ;
15751627 let mut ar = Archive :: new ( rdr) ;
15761628 // should unpack path greater than windows MAX_PATH length of 260 characters
15771629 assert ! ( ar. unpack( td. path( ) ) . is_ok( ) ) ;
@@ -1768,7 +1820,7 @@ fn pax_and_gnu_uid_gid() {
17681820
17691821 for file in & tarlist {
17701822 let td = TempBuilder :: new ( ) . prefix ( "tar-rs" ) . tempdir ( ) . unwrap ( ) ;
1771- let rdr = Cursor :: new ( file) ;
1823+ let rdr = random_cursor_reader ( file) ;
17721824 let mut ar = Archive :: new ( rdr) ;
17731825 ar. set_preserve_ownerships ( true ) ;
17741826
0 commit comments