4 #[cfg(all(unix, feature = "xattr"))]
7 use std
::fs
::{self, File}
;
8 use std
::io
::prelude
::*;
9 use std
::io
::{self, Cursor}
;
10 use std
::iter
::repeat
;
11 use std
::path
::{Path, PathBuf}
;
13 use filetime
::FileTime
;
14 use tar
::{Archive, Builder, EntryType, Header}
;
15 use tempfile
::{Builder as TempBuilder, TempDir}
;
21 Err(e
) => panic
!("{} returned {}", stringify
!($e
), e
),
28 &include_bytes
!(concat
!("archives/", $e
))[..]
34 /// test that we can concatenate the simple.tar archive and extract the same entries twice when we
35 /// use the ignore_zeros option.
38 let bytes
= tar
!("simple.tar");
39 let mut archive_bytes
= Vec
::new();
40 archive_bytes
.extend(bytes
);
42 let original_names
: Vec
<String
> = decode_names(&mut Archive
::new(Cursor
::new(&archive_bytes
)));
43 let expected
: Vec
<&str> = original_names
.iter().map(|n
| n
.as_str()).collect();
45 // concat two archives (with null in-between);
46 archive_bytes
.extend(bytes
);
48 // test now that when we read the archive, it stops processing at the first zero header.
49 let actual
= decode_names(&mut Archive
::new(Cursor
::new(&archive_bytes
)));
50 assert_eq
!(expected
, actual
);
52 // extend expected by itself.
53 let expected
: Vec
<&str> = {
54 let mut o
= Vec
::new();
60 let mut ar
= Archive
::new(Cursor
::new(&archive_bytes
));
61 ar
.set_ignore_zeros(true);
63 let actual
= decode_names(&mut ar
);
64 assert_eq
!(expected
, actual
);
66 fn decode_names
<R
>(ar
: &mut Archive
<R
>) -> Vec
<String
>
70 let mut names
= Vec
::new();
72 for entry
in t
!(ar
.entries()) {
74 names
.push(t
!(::std
::str::from_utf8(&e
.path_bytes())).to_string());
83 let mut ar
= Archive
::new(Cursor
::new(tar
!("simple.tar")));
84 let hn
= Header
::new_old();
85 let hnb
= hn
.as_bytes();
86 for file
in t
!(ar
.entries()) {
88 let h1
= file
.header();
89 let h1b
= h1
.as_bytes();
91 let h2b
= h2
.as_bytes();
92 assert
!(h1b
[..] == h2b
[..] && h2b
[..] != hnb
[..])
97 fn header_impls_missing_last_header() {
98 let mut ar
= Archive
::new(Cursor
::new(tar
!("simple_missing_last_header.tar")));
99 let hn
= Header
::new_old();
100 let hnb
= hn
.as_bytes();
101 for file
in t
!(ar
.entries()) {
103 let h1
= file
.header();
104 let h1b
= h1
.as_bytes();
106 let h2b
= h2
.as_bytes();
107 assert
!(h1b
[..] == h2b
[..] && h2b
[..] != hnb
[..])
113 let rdr
= Cursor
::new(tar
!("reading_files.tar"));
114 let mut ar
= Archive
::new(rdr
);
115 let mut entries
= t
!(ar
.entries());
117 let mut a
= t
!(entries
.next().unwrap());
118 assert_eq
!(&*a
.header().path_bytes(), b
"a");
119 let mut s
= String
::new();
120 t
!(a
.read_to_string(&mut s
));
121 assert_eq
!(s
, "a\na\na\na\na\na\na\na\na\na\na\n");
123 let mut b
= t
!(entries
.next().unwrap());
124 assert_eq
!(&*b
.header().path_bytes(), b
"b");
126 t
!(b
.read_to_string(&mut s
));
127 assert_eq
!(s
, "b\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb\n");
129 assert
!(entries
.next().is_none());
134 let mut ar
= Builder
::new(Vec
::new());
135 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
137 let path
= td
.path().join("test");
138 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
140 t
!(ar
.append_file("test2", &mut t
!(File
::open(&path
))));
142 let data
= t
!(ar
.into_inner());
143 let mut ar
= Archive
::new(Cursor
::new(data
));
144 let mut entries
= t
!(ar
.entries());
145 let mut f
= t
!(entries
.next().unwrap());
147 assert_eq
!(&*f
.header().path_bytes(), b
"test2");
148 assert_eq
!(f
.header().size().unwrap(), 4);
149 let mut s
= String
::new();
150 t
!(f
.read_to_string(&mut s
));
151 assert_eq
!(s
, "test");
153 assert
!(entries
.next().is_none());
157 fn large_filename() {
158 let mut ar
= Builder
::new(Vec
::new());
159 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
161 let path
= td
.path().join("test");
162 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
164 let filename
= repeat("abcd/").take(50).collect
::<String
>();
165 let mut header
= Header
::new_ustar();
166 header
.set_path(&filename
).unwrap();
167 header
.set_metadata(&t
!(fs
::metadata(&path
)));
169 t
!(ar
.append(&header
, &b
"test"[..]));
170 let too_long
= repeat("abcd").take(200).collect
::<String
>();
171 t
!(ar
.append_file(&too_long
, &mut t
!(File
::open(&path
))));
172 t
!(ar
.append_data(&mut header
, &too_long
, &b
"test"[..]));
174 let rd
= Cursor
::new(t
!(ar
.into_inner()));
175 let mut ar
= Archive
::new(rd
);
176 let mut entries
= t
!(ar
.entries());
178 // The short entry added with `append`
179 let mut f
= entries
.next().unwrap().unwrap();
180 assert_eq
!(&*f
.header().path_bytes(), filename
.as_bytes());
181 assert_eq
!(f
.header().size().unwrap(), 4);
182 let mut s
= String
::new();
183 t
!(f
.read_to_string(&mut s
));
184 assert_eq
!(s
, "test");
186 // The long entry added with `append_file`
187 let mut f
= entries
.next().unwrap().unwrap();
188 assert_eq
!(&*f
.path_bytes(), too_long
.as_bytes());
189 assert_eq
!(f
.header().size().unwrap(), 4);
190 let mut s
= String
::new();
191 t
!(f
.read_to_string(&mut s
));
192 assert_eq
!(s
, "test");
194 // The long entry added with `append_data`
195 let mut f
= entries
.next().unwrap().unwrap();
196 assert
!(f
.header().path_bytes().len() < too_long
.len());
197 assert_eq
!(&*f
.path_bytes(), too_long
.as_bytes());
198 assert_eq
!(f
.header().size().unwrap(), 4);
199 let mut s
= String
::new();
200 t
!(f
.read_to_string(&mut s
));
201 assert_eq
!(s
, "test");
203 assert
!(entries
.next().is_none());
207 fn reading_entries() {
208 let rdr
= Cursor
::new(tar
!("reading_files.tar"));
209 let mut ar
= Archive
::new(rdr
);
210 let mut entries
= t
!(ar
.entries());
211 let mut a
= t
!(entries
.next().unwrap());
212 assert_eq
!(&*a
.header().path_bytes(), b
"a");
213 let mut s
= String
::new();
214 t
!(a
.read_to_string(&mut s
));
215 assert_eq
!(s
, "a\na\na\na\na\na\na\na\na\na\na\n");
217 t
!(a
.read_to_string(&mut s
));
219 let mut b
= t
!(entries
.next().unwrap());
221 assert_eq
!(&*b
.header().path_bytes(), b
"b");
223 t
!(b
.read_to_string(&mut s
));
224 assert_eq
!(s
, "b\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb\n");
225 assert
!(entries
.next().is_none());
228 fn check_dirtree(td
: &TempDir
) {
229 let dir_a
= td
.path().join("a");
230 let dir_b
= td
.path().join("a/b");
231 let file_c
= td
.path().join("a/c");
232 assert
!(fs
::metadata(&dir_a
).map(|m
| m
.is_dir()).unwrap_or(false));
233 assert
!(fs
::metadata(&dir_b
).map(|m
| m
.is_dir()).unwrap_or(false));
234 assert
!(fs
::metadata(&file_c
).map(|m
| m
.is_file()).unwrap_or(false));
238 fn extracting_directories() {
239 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
240 let rdr
= Cursor
::new(tar
!("directory.tar"));
241 let mut ar
= Archive
::new(rdr
);
242 t
!(ar
.unpack(td
.path()));
247 fn extracting_duplicate_file_fail() {
248 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
249 let path_present
= td
.path().join("a");
250 t
!(File
::create(path_present
));
252 let rdr
= Cursor
::new(tar
!("reading_files.tar"));
253 let mut ar
= Archive
::new(rdr
);
254 ar
.set_overwrite(false);
255 if let Err(err
) = ar
.unpack(td
.path()) {
256 if err
.kind() == std
::io
::ErrorKind
::AlreadyExists
{
257 // as expected with overwrite false
260 panic
!("unexpected error: {:?}", err
);
263 "unpack() should have returned an error of kind {:?}, returned Ok",
264 std
::io
::ErrorKind
::AlreadyExists
269 fn extracting_duplicate_file_succeed() {
270 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
271 let path_present
= td
.path().join("a");
272 t
!(File
::create(path_present
));
274 let rdr
= Cursor
::new(tar
!("reading_files.tar"));
275 let mut ar
= Archive
::new(rdr
);
276 ar
.set_overwrite(true);
277 t
!(ar
.unpack(td
.path()));
282 fn extracting_duplicate_link_fail() {
283 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
284 let path_present
= td
.path().join("lnk");
285 t
!(std
::os
::unix
::fs
::symlink("file", path_present
));
287 let rdr
= Cursor
::new(tar
!("link.tar"));
288 let mut ar
= Archive
::new(rdr
);
289 ar
.set_overwrite(false);
290 if let Err(err
) = ar
.unpack(td
.path()) {
291 if err
.kind() == std
::io
::ErrorKind
::AlreadyExists
{
292 // as expected with overwrite false
295 panic
!("unexpected error: {:?}", err
);
298 "unpack() should have returned an error of kind {:?}, returned Ok",
299 std
::io
::ErrorKind
::AlreadyExists
305 fn extracting_duplicate_link_succeed() {
306 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
307 let path_present
= td
.path().join("lnk");
308 t
!(std
::os
::unix
::fs
::symlink("file", path_present
));
310 let rdr
= Cursor
::new(tar
!("link.tar"));
311 let mut ar
= Archive
::new(rdr
);
312 ar
.set_overwrite(true);
313 t
!(ar
.unpack(td
.path()));
317 #[cfg(all(unix, feature = "xattr"))]
319 // If /tmp is a tmpfs, xattr will fail
320 // The xattr crate's unit tests also use /var/tmp for this reason
321 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir_in("/var/tmp"));
322 let rdr
= Cursor
::new(tar
!("xattrs.tar"));
323 let mut ar
= Archive
::new(rdr
);
324 ar
.set_unpack_xattrs(true);
325 t
!(ar
.unpack(td
.path()));
327 let val
= xattr
::get(td
.path().join("a/b"), "user.pax.flags").unwrap();
328 assert_eq
!(val
.unwrap(), "epm".as_bytes());
332 #[cfg(all(unix, feature = "xattr"))]
334 // If /tmp is a tmpfs, xattr will fail
335 // The xattr crate's unit tests also use /var/tmp for this reason
336 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir_in("/var/tmp"));
337 let rdr
= Cursor
::new(tar
!("xattrs.tar"));
338 let mut ar
= Archive
::new(rdr
);
339 ar
.set_unpack_xattrs(false);
340 t
!(ar
.unpack(td
.path()));
343 xattr
::get(td
.path().join("a/b"), "user.pax.flags").unwrap(),
349 fn writing_and_extracting_directories() {
350 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
352 let mut ar
= Builder
::new(Vec
::new());
353 let tmppath
= td
.path().join("tmpfile");
354 t
!(t
!(File
::create(&tmppath
)).write_all(b
"c"));
355 t
!(ar
.append_dir("a", "."));
356 t
!(ar
.append_dir("a/b", "."));
357 t
!(ar
.append_file("a/c", &mut t
!(File
::open(&tmppath
))));
360 let rdr
= Cursor
::new(t
!(ar
.into_inner()));
361 let mut ar
= Archive
::new(rdr
);
362 t
!(ar
.unpack(td
.path()));
367 fn writing_directories_recursively() {
368 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
370 let base_dir
= td
.path().join("base");
371 t
!(fs
::create_dir(&base_dir
));
372 t
!(t
!(File
::create(base_dir
.join("file1"))).write_all(b
"file1"));
373 let sub_dir
= base_dir
.join("sub");
374 t
!(fs
::create_dir(&sub_dir
));
375 t
!(t
!(File
::create(sub_dir
.join("file2"))).write_all(b
"file2"));
377 let mut ar
= Builder
::new(Vec
::new());
378 t
!(ar
.append_dir_all("foobar", base_dir
));
379 let data
= t
!(ar
.into_inner());
381 let mut ar
= Archive
::new(Cursor
::new(data
));
382 t
!(ar
.unpack(td
.path()));
383 let base_dir
= td
.path().join("foobar");
384 assert
!(fs
::metadata(&base_dir
).map(|m
| m
.is_dir()).unwrap_or(false));
385 let file1_path
= base_dir
.join("file1");
386 assert
!(fs
::metadata(&file1_path
)
387 .map(|m
| m
.is_file())
389 let sub_dir
= base_dir
.join("sub");
390 assert
!(fs
::metadata(&sub_dir
).map(|m
| m
.is_dir()).unwrap_or(false));
391 let file2_path
= sub_dir
.join("file2");
392 assert
!(fs
::metadata(&file2_path
)
393 .map(|m
| m
.is_file())
398 fn append_dir_all_blank_dest() {
399 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
401 let base_dir
= td
.path().join("base");
402 t
!(fs
::create_dir(&base_dir
));
403 t
!(t
!(File
::create(base_dir
.join("file1"))).write_all(b
"file1"));
404 let sub_dir
= base_dir
.join("sub");
405 t
!(fs
::create_dir(&sub_dir
));
406 t
!(t
!(File
::create(sub_dir
.join("file2"))).write_all(b
"file2"));
408 let mut ar
= Builder
::new(Vec
::new());
409 t
!(ar
.append_dir_all("", base_dir
));
410 let data
= t
!(ar
.into_inner());
412 let mut ar
= Archive
::new(Cursor
::new(data
));
413 t
!(ar
.unpack(td
.path()));
414 let base_dir
= td
.path();
415 assert
!(fs
::metadata(&base_dir
).map(|m
| m
.is_dir()).unwrap_or(false));
416 let file1_path
= base_dir
.join("file1");
417 assert
!(fs
::metadata(&file1_path
)
418 .map(|m
| m
.is_file())
420 let sub_dir
= base_dir
.join("sub");
421 assert
!(fs
::metadata(&sub_dir
).map(|m
| m
.is_dir()).unwrap_or(false));
422 let file2_path
= sub_dir
.join("file2");
423 assert
!(fs
::metadata(&file2_path
)
424 .map(|m
| m
.is_file())
429 fn append_dir_all_does_not_work_on_non_directory() {
430 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
431 let path
= td
.path().join("test");
432 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
434 let mut ar
= Builder
::new(Vec
::new());
435 let result
= ar
.append_dir_all("test", path
);
436 assert
!(result
.is_err());
440 fn extracting_duplicate_dirs() {
441 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
442 let rdr
= Cursor
::new(tar
!("duplicate_dirs.tar"));
443 let mut ar
= Archive
::new(rdr
);
444 t
!(ar
.unpack(td
.path()));
446 let some_dir
= td
.path().join("some_dir");
447 assert
!(fs
::metadata(&some_dir
).map(|m
| m
.is_dir()).unwrap_or(false));
451 fn unpack_old_style_bsd_dir() {
452 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
454 let mut ar
= Builder
::new(Vec
::new());
456 let mut header
= Header
::new_old();
457 header
.set_entry_type(EntryType
::Regular
);
458 t
!(header
.set_path("testdir/"));
461 t
!(ar
.append(&header
, &mut io
::empty()));
464 let rdr
= Cursor
::new(t
!(ar
.into_inner()));
465 let mut ar
= Archive
::new(rdr
);
466 t
!(ar
.unpack(td
.path()));
469 let rdr
= Cursor
::new(ar
.into_inner().into_inner());
470 let mut ar
= Archive
::new(rdr
);
471 assert
!(t
!(ar
.entries()).all(|fr
| fr
.is_ok()));
473 assert
!(td
.path().join("testdir").is_dir());
477 fn handling_incorrect_file_size() {
478 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
480 let mut ar
= Builder
::new(Vec
::new());
482 let path
= td
.path().join("tmpfile");
483 t
!(File
::create(&path
));
484 let mut file
= t
!(File
::open(&path
));
485 let mut header
= Header
::new_old();
486 t
!(header
.set_path("somepath"));
487 header
.set_metadata(&t
!(file
.metadata()));
488 header
.set_size(2048); // past the end of file null blocks
490 t
!(ar
.append(&header
, &mut file
));
493 let rdr
= Cursor
::new(t
!(ar
.into_inner()));
494 let mut ar
= Archive
::new(rdr
);
495 assert
!(ar
.unpack(td
.path()).is_err());
498 let rdr
= Cursor
::new(ar
.into_inner().into_inner());
499 let mut ar
= Archive
::new(rdr
);
500 assert
!(t
!(ar
.entries()).any(|fr
| fr
.is_err()));
504 fn extracting_malicious_tarball() {
505 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
507 let mut evil_tar
= Vec
::new();
510 let mut a
= Builder
::new(&mut evil_tar
);
511 let mut append
= |path
: &str| {
512 let mut header
= Header
::new_gnu();
513 assert
!(header
.set_path(path
).is_err(), "was ok: {:?}", path
);
515 let h
= header
.as_gnu_mut().unwrap();
516 for (a
, b
) in h
.name
.iter_mut().zip(path
.as_bytes()) {
522 t
!(a
.append(&header
, io
::repeat(1).take(1)));
524 append("/tmp/abs_evil.txt");
525 append("//tmp/abs_evil2.txt");
526 append("///tmp/abs_evil3.txt");
527 append("/./tmp/abs_evil4.txt");
528 append("//./tmp/abs_evil5.txt");
529 append("///./tmp/abs_evil6.txt");
530 append("/../tmp/rel_evil.txt");
531 append("../rel_evil2.txt");
532 append("./../rel_evil3.txt");
533 append("some/../../rel_evil4.txt");
535 append("././//./..");
537 append("/////////..");
541 let mut ar
= Archive
::new(&evil_tar
[..]);
542 t
!(ar
.unpack(td
.path()));
544 assert
!(fs
::metadata("/tmp/abs_evil.txt").is_err());
545 assert
!(fs
::metadata("/tmp/abs_evil.txt2").is_err());
546 assert
!(fs
::metadata("/tmp/abs_evil.txt3").is_err());
547 assert
!(fs
::metadata("/tmp/abs_evil.txt4").is_err());
548 assert
!(fs
::metadata("/tmp/abs_evil.txt5").is_err());
549 assert
!(fs
::metadata("/tmp/abs_evil.txt6").is_err());
550 assert
!(fs
::metadata("/tmp/rel_evil.txt").is_err());
551 assert
!(fs
::metadata("/tmp/rel_evil.txt").is_err());
552 assert
!(fs
::metadata(td
.path().join("../tmp/rel_evil.txt")).is_err());
553 assert
!(fs
::metadata(td
.path().join("../rel_evil2.txt")).is_err());
554 assert
!(fs
::metadata(td
.path().join("../rel_evil3.txt")).is_err());
555 assert
!(fs
::metadata(td
.path().join("../rel_evil4.txt")).is_err());
557 // The `some` subdirectory should not be created because the only
558 // filename that references this has '..'.
559 assert
!(fs
::metadata(td
.path().join("some")).is_err());
561 // The `tmp` subdirectory should be created and within this
562 // subdirectory, there should be files named `abs_evil.txt` through
564 assert
!(fs
::metadata(td
.path().join("tmp"))
567 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil.txt"))
568 .map(|m
| m
.is_file())
570 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil2.txt"))
571 .map(|m
| m
.is_file())
573 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil3.txt"))
574 .map(|m
| m
.is_file())
576 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil4.txt"))
577 .map(|m
| m
.is_file())
579 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil5.txt"))
580 .map(|m
| m
.is_file())
582 assert
!(fs
::metadata(td
.path().join("tmp/abs_evil6.txt"))
583 .map(|m
| m
.is_file())
589 let rdr
= Cursor
::new(tar
!("spaces.tar"));
590 let mut ar
= Archive
::new(rdr
);
592 let entry
= ar
.entries().unwrap().next().unwrap().unwrap();
593 assert_eq
!(entry
.header().mode().unwrap() & 0o777, 0o777);
594 assert_eq
!(entry
.header().uid().unwrap(), 0);
595 assert_eq
!(entry
.header().gid().unwrap(), 0);
596 assert_eq
!(entry
.header().size().unwrap(), 2);
597 assert_eq
!(entry
.header().mtime().unwrap(), 0o12440016664);
598 assert_eq
!(entry
.header().cksum().unwrap(), 0o4253);
602 fn extracting_malformed_tar_null_blocks() {
603 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
605 let mut ar
= Builder
::new(Vec
::new());
607 let path1
= td
.path().join("tmpfile1");
608 let path2
= td
.path().join("tmpfile2");
609 t
!(File
::create(&path1
));
610 t
!(File
::create(&path2
));
611 t
!(ar
.append_file("tmpfile1", &mut t
!(File
::open(&path1
))));
612 let mut data
= t
!(ar
.into_inner());
613 let amt
= data
.len();
614 data
.truncate(amt
- 512);
615 let mut ar
= Builder
::new(data
);
616 t
!(ar
.append_file("tmpfile2", &mut t
!(File
::open(&path2
))));
619 let data
= t
!(ar
.into_inner());
620 let mut ar
= Archive
::new(&data
[..]);
621 assert
!(ar
.unpack(td
.path()).is_ok());
625 fn empty_filename() {
626 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
627 let rdr
= Cursor
::new(tar
!("empty_filename.tar"));
628 let mut ar
= Archive
::new(rdr
);
629 assert
!(ar
.unpack(td
.path()).is_ok());
634 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
635 let rdr
= Cursor
::new(tar
!("file_times.tar"));
636 let mut ar
= Archive
::new(rdr
);
637 t
!(ar
.unpack(td
.path()));
639 let meta
= fs
::metadata(td
.path().join("a")).unwrap();
640 let mtime
= FileTime
::from_last_modification_time(&meta
);
641 let atime
= FileTime
::from_last_access_time(&meta
);
642 assert_eq
!(mtime
.unix_seconds(), 1000000000);
643 assert_eq
!(mtime
.nanoseconds(), 0);
644 assert_eq
!(atime
.unix_seconds(), 1000000000);
645 assert_eq
!(atime
.nanoseconds(), 0);
649 fn backslash_treated_well() {
650 // Insert a file into an archive with a backslash
651 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
652 let mut ar
= Builder
::new(Vec
::<u8>::new());
653 t
!(ar
.append_dir("foo\\bar", td
.path()));
654 let mut ar
= Archive
::new(Cursor
::new(t
!(ar
.into_inner())));
655 let f
= t
!(t
!(ar
.entries()).next().unwrap());
657 assert_eq
!(t
!(f
.header().path()).to_str(), Some("foo\\bar"));
659 assert_eq
!(t
!(f
.header().path()).to_str(), Some("foo/bar"));
662 // Unpack an archive with a backslash in the name
663 let mut ar
= Builder
::new(Vec
::<u8>::new());
664 let mut header
= Header
::new_gnu();
665 header
.set_metadata(&t
!(fs
::metadata(td
.path())));
667 for (a
, b
) in header
.as_old_mut().name
.iter_mut().zip(b
"foo\\bar\x00") {
671 t
!(ar
.append(&header
, &mut io
::empty()));
672 let data
= t
!(ar
.into_inner());
673 let mut ar
= Archive
::new(&data
[..]);
674 let f
= t
!(t
!(ar
.entries()).next().unwrap());
675 assert_eq
!(t
!(f
.header().path()).to_str(), Some("foo\\bar"));
677 let mut ar
= Archive
::new(&data
[..]);
678 t
!(ar
.unpack(td
.path()));
679 assert
!(fs
::metadata(td
.path().join("foo\\bar")).is_ok());
684 fn nul_bytes_in_path() {
686 use std
::os
::unix
::prelude
::*;
688 let nul_path
= OsStr
::from_bytes(b
"foo\0");
689 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
690 let mut ar
= Builder
::new(Vec
::<u8>::new());
691 let err
= ar
.append_dir(nul_path
, td
.path()).unwrap_err();
692 assert
!(err
.to_string().contains("contains a nul byte"));
697 let mut ar
= Archive
::new(Cursor
::new(tar
!("link.tar")));
698 let mut entries
= t
!(ar
.entries());
699 let link
= t
!(entries
.next().unwrap());
701 t
!(link
.header().link_name()).as_ref().map(|p
| &**p
),
702 Some(Path
::new("file"))
704 let other
= t
!(entries
.next().unwrap());
705 assert
!(t
!(other
.header().link_name()).is_none());
709 #[cfg(unix)] // making symlinks on windows is hard
711 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
712 let mut ar
= Archive
::new(Cursor
::new(tar
!("link.tar")));
713 t
!(ar
.unpack(td
.path()));
715 let md
= t
!(fs
::symlink_metadata(td
.path().join("lnk")));
716 assert
!(md
.file_type().is_symlink());
718 &*t
!(fs
::read_link(td
.path().join("lnk"))),
721 t
!(File
::open(td
.path().join("lnk")));
726 let mut ar
= Archive
::new(tar
!("pax_size.tar"));
727 let mut entries
= t
!(ar
.entries());
728 let mut entry
= t
!(entries
.next().unwrap());
729 let mut attributes
= t
!(entry
.pax_extensions()).unwrap();
731 let _first
= t
!(attributes
.next().unwrap());
732 let _second
= t
!(attributes
.next().unwrap());
733 let _third
= t
!(attributes
.next().unwrap());
734 let fourth
= t
!(attributes
.next().unwrap());
735 assert
!(attributes
.next().is_none());
737 assert_eq
!(fourth
.key(), Ok("size"));
738 assert_eq
!(fourth
.value(), Ok("4"));
740 assert_eq
!(entry
.header().size().unwrap(), 0);
741 assert_eq
!(entry
.size(), 4);
746 let mut ar
= Archive
::new(tar
!("pax.tar"));
747 let mut entries
= t
!(ar
.entries());
749 let mut first
= t
!(entries
.next().unwrap());
750 let mut attributes
= t
!(first
.pax_extensions()).unwrap();
751 let first
= t
!(attributes
.next().unwrap());
752 let second
= t
!(attributes
.next().unwrap());
753 let third
= t
!(attributes
.next().unwrap());
754 assert
!(attributes
.next().is_none());
756 assert_eq
!(first
.key(), Ok("mtime"));
757 assert_eq
!(first
.value(), Ok("1453146164.953123768"));
758 assert_eq
!(second
.key(), Ok("atime"));
759 assert_eq
!(second
.value(), Ok("1453251915.24892486"));
760 assert_eq
!(third
.key(), Ok("ctime"));
761 assert_eq
!(third
.value(), Ok("1453146164.953123768"));
766 let mut ar
= Archive
::new(tar
!("pax2.tar"));
767 let mut entries
= t
!(ar
.entries());
769 let first
= t
!(entries
.next().unwrap());
770 assert
!(first
.path().unwrap().ends_with("aaaaaaaaaaaaaaa"));
774 fn long_name_trailing_nul() {
775 let mut b
= Builder
::new(Vec
::<u8>::new());
777 let mut h
= Header
::new_gnu();
778 t
!(h
.set_path("././@LongLink"));
780 h
.set_entry_type(EntryType
::new(b'L'
));
782 t
!(b
.append(&h
, "foo\0".as_bytes()));
783 let mut h
= Header
::new_gnu();
785 t
!(h
.set_path("bar"));
787 h
.set_entry_type(EntryType
::file());
789 t
!(b
.append(&h
, "foobar".as_bytes()));
791 let contents
= t
!(b
.into_inner());
792 let mut a
= Archive
::new(&contents
[..]);
794 let e
= t
!(t
!(a
.entries()).next().unwrap());
795 assert_eq
!(&*e
.path_bytes(), b
"foo");
799 fn long_linkname_trailing_nul() {
800 let mut b
= Builder
::new(Vec
::<u8>::new());
802 let mut h
= Header
::new_gnu();
803 t
!(h
.set_path("././@LongLink"));
805 h
.set_entry_type(EntryType
::new(b'K'
));
807 t
!(b
.append(&h
, "foo\0".as_bytes()));
808 let mut h
= Header
::new_gnu();
810 t
!(h
.set_path("bar"));
812 h
.set_entry_type(EntryType
::file());
814 t
!(b
.append(&h
, "foobar".as_bytes()));
816 let contents
= t
!(b
.into_inner());
817 let mut a
= Archive
::new(&contents
[..]);
819 let e
= t
!(t
!(a
.entries()).next().unwrap());
820 assert_eq
!(&*e
.link_name_bytes().unwrap(), b
"foo");
824 fn encoded_long_name_has_trailing_nul() {
825 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
826 let path
= td
.path().join("foo");
827 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
829 let mut b
= Builder
::new(Vec
::<u8>::new());
830 let long
= repeat("abcd").take(200).collect
::<String
>();
832 t
!(b
.append_file(&long
, &mut t
!(File
::open(&path
))));
834 let contents
= t
!(b
.into_inner());
835 let mut a
= Archive
::new(&contents
[..]);
837 let mut e
= t
!(t
!(a
.entries()).raw(true).next().unwrap());
838 let mut name
= Vec
::new();
839 t
!(e
.read_to_end(&mut name
));
840 assert_eq
!(name
[name
.len() - 1], 0);
842 let header_name
= &e
.header().as_gnu().unwrap().name
;
843 assert
!(header_name
.starts_with(b
"././@LongLink\x00"));
847 fn reading_sparse() {
848 let rdr
= Cursor
::new(tar
!("sparse.tar"));
849 let mut ar
= Archive
::new(rdr
);
850 let mut entries
= t
!(ar
.entries());
852 let mut a
= t
!(entries
.next().unwrap());
853 let mut s
= String
::new();
854 assert_eq
!(&*a
.header().path_bytes(), b
"sparse_begin.txt");
855 t
!(a
.read_to_string(&mut s
));
856 assert_eq
!(&s
[..5], "test\n");
857 assert
!(s
[5..].chars().all(|x
| x
== '
\u{0}'
));
859 let mut a
= t
!(entries
.next().unwrap());
860 let mut s
= String
::new();
861 assert_eq
!(&*a
.header().path_bytes(), b
"sparse_end.txt");
862 t
!(a
.read_to_string(&mut s
));
863 assert
!(s
[..s
.len() - 9].chars().all(|x
| x
== '
\u{0}'
));
864 assert_eq
!(&s
[s
.len() - 9..], "test_end\n");
866 let mut a
= t
!(entries
.next().unwrap());
867 let mut s
= String
::new();
868 assert_eq
!(&*a
.header().path_bytes(), b
"sparse_ext.txt");
869 t
!(a
.read_to_string(&mut s
));
870 assert
!(s
[..0x1000].chars().all(|x
| x
== '
\u{0}'
));
871 assert_eq
!(&s
[0x1000..0x1000 + 5], "text\n");
872 assert
!(s
[0x1000 + 5..0x3000].chars().all(|x
| x
== '
\u{0}'
));
873 assert_eq
!(&s
[0x3000..0x3000 + 5], "text\n");
874 assert
!(s
[0x3000 + 5..0x5000].chars().all(|x
| x
== '
\u{0}'
));
875 assert_eq
!(&s
[0x5000..0x5000 + 5], "text\n");
876 assert
!(s
[0x5000 + 5..0x7000].chars().all(|x
| x
== '
\u{0}'
));
877 assert_eq
!(&s
[0x7000..0x7000 + 5], "text\n");
878 assert
!(s
[0x7000 + 5..0x9000].chars().all(|x
| x
== '
\u{0}'
));
879 assert_eq
!(&s
[0x9000..0x9000 + 5], "text\n");
880 assert
!(s
[0x9000 + 5..0xb000].chars().all(|x
| x
== '
\u{0}'
));
881 assert_eq
!(&s
[0xb000..0xb000 + 5], "text\n");
883 let mut a
= t
!(entries
.next().unwrap());
884 let mut s
= String
::new();
885 assert_eq
!(&*a
.header().path_bytes(), b
"sparse.txt");
886 t
!(a
.read_to_string(&mut s
));
887 assert
!(s
[..0x1000].chars().all(|x
| x
== '
\u{0}'
));
888 assert_eq
!(&s
[0x1000..0x1000 + 6], "hello\n");
889 assert
!(s
[0x1000 + 6..0x2fa0].chars().all(|x
| x
== '
\u{0}'
));
890 assert_eq
!(&s
[0x2fa0..0x2fa0 + 6], "world\n");
891 assert
!(s
[0x2fa0 + 6..0x4000].chars().all(|x
| x
== '
\u{0}'
));
893 assert
!(entries
.next().is_none());
897 fn extract_sparse() {
898 let rdr
= Cursor
::new(tar
!("sparse.tar"));
899 let mut ar
= Archive
::new(rdr
);
900 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
901 t
!(ar
.unpack(td
.path()));
903 let mut s
= String
::new();
904 t
!(t
!(File
::open(td
.path().join("sparse_begin.txt"))).read_to_string(&mut s
));
905 assert_eq
!(&s
[..5], "test\n");
906 assert
!(s
[5..].chars().all(|x
| x
== '
\u{0}'
));
909 t
!(t
!(File
::open(td
.path().join("sparse_end.txt"))).read_to_string(&mut s
));
910 assert
!(s
[..s
.len() - 9].chars().all(|x
| x
== '
\u{0}'
));
911 assert_eq
!(&s
[s
.len() - 9..], "test_end\n");
914 t
!(t
!(File
::open(td
.path().join("sparse_ext.txt"))).read_to_string(&mut s
));
915 assert
!(s
[..0x1000].chars().all(|x
| x
== '
\u{0}'
));
916 assert_eq
!(&s
[0x1000..0x1000 + 5], "text\n");
917 assert
!(s
[0x1000 + 5..0x3000].chars().all(|x
| x
== '
\u{0}'
));
918 assert_eq
!(&s
[0x3000..0x3000 + 5], "text\n");
919 assert
!(s
[0x3000 + 5..0x5000].chars().all(|x
| x
== '
\u{0}'
));
920 assert_eq
!(&s
[0x5000..0x5000 + 5], "text\n");
921 assert
!(s
[0x5000 + 5..0x7000].chars().all(|x
| x
== '
\u{0}'
));
922 assert_eq
!(&s
[0x7000..0x7000 + 5], "text\n");
923 assert
!(s
[0x7000 + 5..0x9000].chars().all(|x
| x
== '
\u{0}'
));
924 assert_eq
!(&s
[0x9000..0x9000 + 5], "text\n");
925 assert
!(s
[0x9000 + 5..0xb000].chars().all(|x
| x
== '
\u{0}'
));
926 assert_eq
!(&s
[0xb000..0xb000 + 5], "text\n");
929 t
!(t
!(File
::open(td
.path().join("sparse.txt"))).read_to_string(&mut s
));
930 assert
!(s
[..0x1000].chars().all(|x
| x
== '
\u{0}'
));
931 assert_eq
!(&s
[0x1000..0x1000 + 6], "hello\n");
932 assert
!(s
[0x1000 + 6..0x2fa0].chars().all(|x
| x
== '
\u{0}'
));
933 assert_eq
!(&s
[0x2fa0..0x2fa0 + 6], "world\n");
934 assert
!(s
[0x2fa0 + 6..0x4000].chars().all(|x
| x
== '
\u{0}'
));
938 fn path_separators() {
939 let mut ar
= Builder
::new(Vec
::new());
940 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
942 let path
= td
.path().join("test");
943 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
945 let short_path
: PathBuf
= repeat("abcd").take(2).collect();
946 let long_path
: PathBuf
= repeat("abcd").take(50).collect();
948 // Make sure UStar headers normalize to Unix path separators
949 let mut header
= Header
::new_ustar();
951 t
!(header
.set_path(&short_path
));
952 assert_eq
!(t
!(header
.path()), short_path
);
953 assert
!(!header
.path_bytes().contains(&b'
\\'
));
955 t
!(header
.set_path(&long_path
));
956 assert_eq
!(t
!(header
.path()), long_path
);
957 assert
!(!header
.path_bytes().contains(&b'
\\'
));
959 // Make sure GNU headers normalize to Unix path separators,
960 // including the `@LongLink` fallback used by `append_file`.
961 t
!(ar
.append_file(&short_path
, &mut t
!(File
::open(&path
))));
962 t
!(ar
.append_file(&long_path
, &mut t
!(File
::open(&path
))));
964 let rd
= Cursor
::new(t
!(ar
.into_inner()));
965 let mut ar
= Archive
::new(rd
);
966 let mut entries
= t
!(ar
.entries());
968 let entry
= t
!(entries
.next().unwrap());
969 assert_eq
!(t
!(entry
.path()), short_path
);
970 assert
!(!entry
.path_bytes().contains(&b'
\\'
));
972 let entry
= t
!(entries
.next().unwrap());
973 assert_eq
!(t
!(entry
.path()), long_path
);
974 assert
!(!entry
.path_bytes().contains(&b'
\\'
));
976 assert
!(entries
.next().is_none());
981 fn append_path_symlink() {
982 use std
::borrow
::Cow
;
984 use std
::os
::unix
::fs
::symlink
;
986 let mut ar
= Builder
::new(Vec
::new());
987 ar
.follow_symlinks(false);
988 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
990 let long_linkname
= repeat("abcd").take(30).collect
::<String
>();
991 let long_pathname
= repeat("dcba").take(30).collect
::<String
>();
992 t
!(env
::set_current_dir(td
.path()));
993 // "short" path name / short link name
994 t
!(symlink("testdest", "test"));
995 t
!(ar
.append_path("test"));
996 // short path name / long link name
997 t
!(symlink(&long_linkname
, "test2"));
998 t
!(ar
.append_path("test2"));
999 // long path name / long link name
1000 t
!(symlink(&long_linkname
, &long_pathname
));
1001 t
!(ar
.append_path(&long_pathname
));
1003 let rd
= Cursor
::new(t
!(ar
.into_inner()));
1004 let mut ar
= Archive
::new(rd
);
1005 let mut entries
= t
!(ar
.entries());
1007 let entry
= t
!(entries
.next().unwrap());
1008 assert_eq
!(t
!(entry
.path()), Path
::new("test"));
1010 t
!(entry
.link_name()),
1011 Some(Cow
::from(Path
::new("testdest")))
1013 assert_eq
!(t
!(entry
.header().size()), 0);
1015 let entry
= t
!(entries
.next().unwrap());
1016 assert_eq
!(t
!(entry
.path()), Path
::new("test2"));
1018 t
!(entry
.link_name()),
1019 Some(Cow
::from(Path
::new(&long_linkname
)))
1021 assert_eq
!(t
!(entry
.header().size()), 0);
1023 let entry
= t
!(entries
.next().unwrap());
1024 assert_eq
!(t
!(entry
.path()), Path
::new(&long_pathname
));
1026 t
!(entry
.link_name()),
1027 Some(Cow
::from(Path
::new(&long_linkname
)))
1029 assert_eq
!(t
!(entry
.header().size()), 0);
1031 assert
!(entries
.next().is_none());
1035 fn name_with_slash_doesnt_fool_long_link_and_bsd_compat() {
1036 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
1038 let mut ar
= Builder
::new(Vec
::new());
1040 let mut h
= Header
::new_gnu();
1041 t
!(h
.set_path("././@LongLink"));
1043 h
.set_entry_type(EntryType
::new(b'L'
));
1045 t
!(ar
.append(&h
, "foo\0".as_bytes()));
1047 let mut header
= Header
::new_gnu();
1048 header
.set_entry_type(EntryType
::Regular
);
1049 t
!(header
.set_path("testdir/"));
1052 t
!(ar
.append(&header
, &mut io
::empty()));
1055 let rdr
= Cursor
::new(t
!(ar
.into_inner()));
1056 let mut ar
= Archive
::new(rdr
);
1057 t
!(ar
.unpack(td
.path()));
1060 let rdr
= Cursor
::new(ar
.into_inner().into_inner());
1061 let mut ar
= Archive
::new(rdr
);
1062 assert
!(t
!(ar
.entries()).all(|fr
| fr
.is_ok()));
1064 assert
!(td
.path().join("foo").is_file());
1068 fn insert_local_file_different_name() {
1069 let mut ar
= Builder
::new(Vec
::new());
1070 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
1071 let path
= td
.path().join("directory");
1072 t
!(fs
::create_dir(&path
));
1073 ar
.append_path_with_name(&path
, "archive/dir").unwrap();
1074 let path
= td
.path().join("file");
1075 t
!(t
!(File
::create(&path
)).write_all(b
"test"));
1076 ar
.append_path_with_name(&path
, "archive/dir/f").unwrap();
1078 let rd
= Cursor
::new(t
!(ar
.into_inner()));
1079 let mut ar
= Archive
::new(rd
);
1080 let mut entries
= t
!(ar
.entries());
1081 let entry
= t
!(entries
.next().unwrap());
1082 assert_eq
!(t
!(entry
.path()), Path
::new("archive/dir"));
1083 let entry
= t
!(entries
.next().unwrap());
1084 assert_eq
!(t
!(entry
.path()), Path
::new("archive/dir/f"));
1085 assert
!(entries
.next().is_none());
1090 fn tar_directory_containing_symlink_to_directory() {
1091 use std
::os
::unix
::fs
::symlink
;
1093 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
1094 let dummy_src
= t
!(TempBuilder
::new().prefix("dummy_src").tempdir());
1095 let dummy_dst
= td
.path().join("dummy_dst");
1096 let mut ar
= Builder
::new(Vec
::new());
1097 t
!(symlink(dummy_src
.path().display().to_string(), &dummy_dst
));
1099 assert
!(dummy_dst
.read_link().is_ok());
1100 assert
!(dummy_dst
.read_link().unwrap().is_dir());
1101 ar
.append_dir_all("symlinks", td
.path()).unwrap();
1102 ar
.finish().unwrap();
1107 let td
= t
!(TempBuilder
::new().prefix("tar-rs").tempdir());
1108 let rdr
= Cursor
::new(tar
!("7z_long_path.tar"));
1109 let mut ar
= Archive
::new(rdr
);
1110 assert
!(ar
.unpack(td
.path()).is_ok());
1114 fn unpack_path_larger_than_windows_max_path() {
1115 let dir_name
= "iamaprettylongnameandtobepreciseiam91characterslongwhichsomethinkisreallylongandothersdonot";
1116 // 183 character directory name
1117 let really_long_path
= format
!("{}{}", dir_name
, dir_name
);
1118 let td
= t
!(TempBuilder
::new().prefix(&really_long_path
).tempdir());
1119 // directory in 7z_long_path.tar is over 100 chars
1120 let rdr
= Cursor
::new(tar
!("7z_long_path.tar"));
1121 let mut ar
= Archive
::new(rdr
);
1122 // should unpack path greater than windows MAX_PATH length of 260 characters
1123 assert
!(ar
.unpack(td
.path()).is_ok());