1 extern crate filetime;
2 extern crate tar;
3 extern crate tempdir;
4 #[cfg(all(unix, feature = "xattr"))]
5 extern crate xattr;
6 
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};
12 
13 use self::tempdir::TempDir;
14 use filetime::FileTime;
15 use tar::{Archive, Builder, EntryType, Header};
16 
17 macro_rules! t {
18     ($e:expr) => {
19         match $e {
20             Ok(v) => v,
21             Err(e) => panic!("{} returned {}", stringify!($e), e),
22         }
23     };
24 }
25 
26 macro_rules! tar {
27     ($e:expr) => {
28         &include_bytes!(concat!("archives/", $e))[..]
29     };
30 }
31 
32 mod header;
33 
34 /// test that we can concatenate the simple.tar archive and extract the same entries twice when we
35 /// use the ignore_zeros option.
36 #[test]
simple_concat()37 fn simple_concat() {
38     let bytes = tar!("simple.tar");
39     let mut archive_bytes = Vec::new();
40     archive_bytes.extend(bytes);
41 
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();
44 
45     // concat two archives (with null in-between);
46     archive_bytes.extend(bytes);
47 
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);
51 
52     // extend expected by itself.
53     let expected: Vec<&str> = {
54         let mut o = Vec::new();
55         o.extend(&expected);
56         o.extend(&expected);
57         o
58     };
59 
60     let mut ar = Archive::new(Cursor::new(&archive_bytes));
61     ar.set_ignore_zeros(true);
62 
63     let actual = decode_names(&mut ar);
64     assert_eq!(expected, actual);
65 
66     fn decode_names<R>(ar: &mut Archive<R>) -> Vec<String>
67     where
68         R: Read,
69     {
70         let mut names = Vec::new();
71 
72         for entry in t!(ar.entries()) {
73             let e = t!(entry);
74             names.push(t!(::std::str::from_utf8(&e.path_bytes())).to_string());
75         }
76 
77         names
78     }
79 }
80 
81 #[test]
header_impls()82 fn header_impls() {
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()) {
87         let file = t!(file);
88         let h1 = file.header();
89         let h1b = h1.as_bytes();
90         let h2 = h1.clone();
91         let h2b = h2.as_bytes();
92         assert!(h1b[..] == h2b[..] && h2b[..] != hnb[..])
93     }
94 }
95 
96 #[test]
header_impls_missing_last_header()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()) {
102         let file = t!(file);
103         let h1 = file.header();
104         let h1b = h1.as_bytes();
105         let h2 = h1.clone();
106         let h2b = h2.as_bytes();
107         assert!(h1b[..] == h2b[..] && h2b[..] != hnb[..])
108     }
109 }
110 
111 #[test]
reading_files()112 fn reading_files() {
113     let rdr = Cursor::new(tar!("reading_files.tar"));
114     let mut ar = Archive::new(rdr);
115     let mut entries = t!(ar.entries());
116 
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");
122 
123     let mut b = t!(entries.next().unwrap());
124     assert_eq!(&*b.header().path_bytes(), b"b");
125     s.truncate(0);
126     t!(b.read_to_string(&mut s));
127     assert_eq!(s, "b\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb\n");
128 
129     assert!(entries.next().is_none());
130 }
131 
132 #[test]
writing_files()133 fn writing_files() {
134     let mut ar = Builder::new(Vec::new());
135     let td = t!(TempDir::new("tar-rs"));
136 
137     let path = td.path().join("test");
138     t!(t!(File::create(&path)).write_all(b"test"));
139 
140     t!(ar.append_file("test2", &mut t!(File::open(&path))));
141 
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());
146 
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");
152 
153     assert!(entries.next().is_none());
154 }
155 
156 #[test]
large_filename()157 fn large_filename() {
158     let mut ar = Builder::new(Vec::new());
159     let td = t!(TempDir::new("tar-rs"));
160 
161     let path = td.path().join("test");
162     t!(t!(File::create(&path)).write_all(b"test"));
163 
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)));
168     header.set_cksum();
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"[..]));
173 
174     let rd = Cursor::new(t!(ar.into_inner()));
175     let mut ar = Archive::new(rd);
176     let mut entries = t!(ar.entries());
177 
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");
185 
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");
193 
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");
202 
203     assert!(entries.next().is_none());
204 }
205 
206 #[test]
reading_entries()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");
216     s.truncate(0);
217     t!(a.read_to_string(&mut s));
218     assert_eq!(s, "");
219     let mut b = t!(entries.next().unwrap());
220 
221     assert_eq!(&*b.header().path_bytes(), b"b");
222     s.truncate(0);
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());
226 }
227 
check_dirtree(td: &TempDir)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));
235 }
236 
237 #[test]
extracting_directories()238 fn extracting_directories() {
239     let td = t!(TempDir::new("tar-rs"));
240     let rdr = Cursor::new(tar!("directory.tar"));
241     let mut ar = Archive::new(rdr);
242     t!(ar.unpack(td.path()));
243     check_dirtree(&td);
244 }
245 
246 #[test]
247 #[cfg(all(unix, feature = "xattr"))]
xattrs()248 fn xattrs() {
249     // If /tmp is a tmpfs, xattr will fail
250     // The xattr crate's unit tests also use /var/tmp for this reason
251     let td = t!(TempDir::new_in("/var/tmp", "tar-rs"));
252     let rdr = Cursor::new(tar!("xattrs.tar"));
253     let mut ar = Archive::new(rdr);
254     ar.set_unpack_xattrs(true);
255     t!(ar.unpack(td.path()));
256 
257     let val = xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap();
258     assert_eq!(val.unwrap(), "epm".as_bytes());
259 }
260 
261 #[test]
262 #[cfg(all(unix, feature = "xattr"))]
no_xattrs()263 fn no_xattrs() {
264     // If /tmp is a tmpfs, xattr will fail
265     // The xattr crate's unit tests also use /var/tmp for this reason
266     let td = t!(TempDir::new_in("/var/tmp", "tar-rs"));
267     let rdr = Cursor::new(tar!("xattrs.tar"));
268     let mut ar = Archive::new(rdr);
269     ar.set_unpack_xattrs(false);
270     t!(ar.unpack(td.path()));
271 
272     assert_eq!(
273         xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap(),
274         None
275     );
276 }
277 
278 #[test]
writing_and_extracting_directories()279 fn writing_and_extracting_directories() {
280     let td = t!(TempDir::new("tar-rs"));
281 
282     let mut ar = Builder::new(Vec::new());
283     let tmppath = td.path().join("tmpfile");
284     t!(t!(File::create(&tmppath)).write_all(b"c"));
285     t!(ar.append_dir("a", "."));
286     t!(ar.append_dir("a/b", "."));
287     t!(ar.append_file("a/c", &mut t!(File::open(&tmppath))));
288     t!(ar.finish());
289 
290     let rdr = Cursor::new(t!(ar.into_inner()));
291     let mut ar = Archive::new(rdr);
292     t!(ar.unpack(td.path()));
293     check_dirtree(&td);
294 }
295 
296 #[test]
writing_directories_recursively()297 fn writing_directories_recursively() {
298     let td = t!(TempDir::new("tar-rs"));
299 
300     let base_dir = td.path().join("base");
301     t!(fs::create_dir(&base_dir));
302     t!(t!(File::create(base_dir.join("file1"))).write_all(b"file1"));
303     let sub_dir = base_dir.join("sub");
304     t!(fs::create_dir(&sub_dir));
305     t!(t!(File::create(sub_dir.join("file2"))).write_all(b"file2"));
306 
307     let mut ar = Builder::new(Vec::new());
308     t!(ar.append_dir_all("foobar", base_dir));
309     let data = t!(ar.into_inner());
310 
311     let mut ar = Archive::new(Cursor::new(data));
312     t!(ar.unpack(td.path()));
313     let base_dir = td.path().join("foobar");
314     assert!(fs::metadata(&base_dir).map(|m| m.is_dir()).unwrap_or(false));
315     let file1_path = base_dir.join("file1");
316     assert!(fs::metadata(&file1_path)
317         .map(|m| m.is_file())
318         .unwrap_or(false));
319     let sub_dir = base_dir.join("sub");
320     assert!(fs::metadata(&sub_dir).map(|m| m.is_dir()).unwrap_or(false));
321     let file2_path = sub_dir.join("file2");
322     assert!(fs::metadata(&file2_path)
323         .map(|m| m.is_file())
324         .unwrap_or(false));
325 }
326 
327 #[test]
append_dir_all_blank_dest()328 fn append_dir_all_blank_dest() {
329     let td = t!(TempDir::new("tar-rs"));
330 
331     let base_dir = td.path().join("base");
332     t!(fs::create_dir(&base_dir));
333     t!(t!(File::create(base_dir.join("file1"))).write_all(b"file1"));
334     let sub_dir = base_dir.join("sub");
335     t!(fs::create_dir(&sub_dir));
336     t!(t!(File::create(sub_dir.join("file2"))).write_all(b"file2"));
337 
338     let mut ar = Builder::new(Vec::new());
339     t!(ar.append_dir_all("", base_dir));
340     let data = t!(ar.into_inner());
341 
342     let mut ar = Archive::new(Cursor::new(data));
343     t!(ar.unpack(td.path()));
344     let base_dir = td.path();
345     assert!(fs::metadata(&base_dir).map(|m| m.is_dir()).unwrap_or(false));
346     let file1_path = base_dir.join("file1");
347     assert!(fs::metadata(&file1_path)
348         .map(|m| m.is_file())
349         .unwrap_or(false));
350     let sub_dir = base_dir.join("sub");
351     assert!(fs::metadata(&sub_dir).map(|m| m.is_dir()).unwrap_or(false));
352     let file2_path = sub_dir.join("file2");
353     assert!(fs::metadata(&file2_path)
354         .map(|m| m.is_file())
355         .unwrap_or(false));
356 }
357 
358 #[test]
append_dir_all_does_not_work_on_non_directory()359 fn append_dir_all_does_not_work_on_non_directory() {
360     let td = t!(TempDir::new("tar-rs"));
361     let path = td.path().join("test");
362     t!(t!(File::create(&path)).write_all(b"test"));
363 
364     let mut ar = Builder::new(Vec::new());
365     let result = ar.append_dir_all("test", path);
366     assert!(result.is_err());
367 }
368 
369 #[test]
extracting_duplicate_dirs()370 fn extracting_duplicate_dirs() {
371     let td = t!(TempDir::new("tar-rs"));
372     let rdr = Cursor::new(tar!("duplicate_dirs.tar"));
373     let mut ar = Archive::new(rdr);
374     t!(ar.unpack(td.path()));
375 
376     let some_dir = td.path().join("some_dir");
377     assert!(fs::metadata(&some_dir).map(|m| m.is_dir()).unwrap_or(false));
378 }
379 
380 #[test]
unpack_old_style_bsd_dir()381 fn unpack_old_style_bsd_dir() {
382     let td = t!(TempDir::new("tar-rs"));
383 
384     let mut ar = Builder::new(Vec::new());
385 
386     let mut header = Header::new_old();
387     header.set_entry_type(EntryType::Regular);
388     t!(header.set_path("testdir/"));
389     header.set_size(0);
390     header.set_cksum();
391     t!(ar.append(&header, &mut io::empty()));
392 
393     // Extracting
394     let rdr = Cursor::new(t!(ar.into_inner()));
395     let mut ar = Archive::new(rdr);
396     t!(ar.unpack(td.path()));
397 
398     // Iterating
399     let rdr = Cursor::new(ar.into_inner().into_inner());
400     let mut ar = Archive::new(rdr);
401     assert!(t!(ar.entries()).all(|fr| fr.is_ok()));
402 
403     assert!(td.path().join("testdir").is_dir());
404 }
405 
406 #[test]
handling_incorrect_file_size()407 fn handling_incorrect_file_size() {
408     let td = t!(TempDir::new("tar-rs"));
409 
410     let mut ar = Builder::new(Vec::new());
411 
412     let path = td.path().join("tmpfile");
413     t!(File::create(&path));
414     let mut file = t!(File::open(&path));
415     let mut header = Header::new_old();
416     t!(header.set_path("somepath"));
417     header.set_metadata(&t!(file.metadata()));
418     header.set_size(2048); // past the end of file null blocks
419     header.set_cksum();
420     t!(ar.append(&header, &mut file));
421 
422     // Extracting
423     let rdr = Cursor::new(t!(ar.into_inner()));
424     let mut ar = Archive::new(rdr);
425     assert!(ar.unpack(td.path()).is_err());
426 
427     // Iterating
428     let rdr = Cursor::new(ar.into_inner().into_inner());
429     let mut ar = Archive::new(rdr);
430     assert!(t!(ar.entries()).any(|fr| fr.is_err()));
431 }
432 
433 #[test]
extracting_malicious_tarball()434 fn extracting_malicious_tarball() {
435     let td = t!(TempDir::new("tar-rs"));
436 
437     let mut evil_tar = Vec::new();
438 
439     {
440         let mut a = Builder::new(&mut evil_tar);
441         let mut append = |path: &str| {
442             let mut header = Header::new_gnu();
443             assert!(header.set_path(path).is_err(), "was ok: {:?}", path);
444             {
445                 let h = header.as_gnu_mut().unwrap();
446                 for (a, b) in h.name.iter_mut().zip(path.as_bytes()) {
447                     *a = *b;
448                 }
449             }
450             header.set_size(1);
451             header.set_cksum();
452             t!(a.append(&header, io::repeat(1).take(1)));
453         };
454         append("/tmp/abs_evil.txt");
455         append("//tmp/abs_evil2.txt");
456         append("///tmp/abs_evil3.txt");
457         append("/./tmp/abs_evil4.txt");
458         append("//./tmp/abs_evil5.txt");
459         append("///./tmp/abs_evil6.txt");
460         append("/../tmp/rel_evil.txt");
461         append("../rel_evil2.txt");
462         append("./../rel_evil3.txt");
463         append("some/../../rel_evil4.txt");
464         append("");
465         append("././//./..");
466         append("..");
467         append("/////////..");
468         append("/////////");
469     }
470 
471     let mut ar = Archive::new(&evil_tar[..]);
472     t!(ar.unpack(td.path()));
473 
474     assert!(fs::metadata("/tmp/abs_evil.txt").is_err());
475     assert!(fs::metadata("/tmp/abs_evil.txt2").is_err());
476     assert!(fs::metadata("/tmp/abs_evil.txt3").is_err());
477     assert!(fs::metadata("/tmp/abs_evil.txt4").is_err());
478     assert!(fs::metadata("/tmp/abs_evil.txt5").is_err());
479     assert!(fs::metadata("/tmp/abs_evil.txt6").is_err());
480     assert!(fs::metadata("/tmp/rel_evil.txt").is_err());
481     assert!(fs::metadata("/tmp/rel_evil.txt").is_err());
482     assert!(fs::metadata(td.path().join("../tmp/rel_evil.txt")).is_err());
483     assert!(fs::metadata(td.path().join("../rel_evil2.txt")).is_err());
484     assert!(fs::metadata(td.path().join("../rel_evil3.txt")).is_err());
485     assert!(fs::metadata(td.path().join("../rel_evil4.txt")).is_err());
486 
487     // The `some` subdirectory should not be created because the only
488     // filename that references this has '..'.
489     assert!(fs::metadata(td.path().join("some")).is_err());
490 
491     // The `tmp` subdirectory should be created and within this
492     // subdirectory, there should be files named `abs_evil.txt` through
493     // `abs_evil6.txt`.
494     assert!(fs::metadata(td.path().join("tmp"))
495         .map(|m| m.is_dir())
496         .unwrap_or(false));
497     assert!(fs::metadata(td.path().join("tmp/abs_evil.txt"))
498         .map(|m| m.is_file())
499         .unwrap_or(false));
500     assert!(fs::metadata(td.path().join("tmp/abs_evil2.txt"))
501         .map(|m| m.is_file())
502         .unwrap_or(false));
503     assert!(fs::metadata(td.path().join("tmp/abs_evil3.txt"))
504         .map(|m| m.is_file())
505         .unwrap_or(false));
506     assert!(fs::metadata(td.path().join("tmp/abs_evil4.txt"))
507         .map(|m| m.is_file())
508         .unwrap_or(false));
509     assert!(fs::metadata(td.path().join("tmp/abs_evil5.txt"))
510         .map(|m| m.is_file())
511         .unwrap_or(false));
512     assert!(fs::metadata(td.path().join("tmp/abs_evil6.txt"))
513         .map(|m| m.is_file())
514         .unwrap_or(false));
515 }
516 
517 #[test]
octal_spaces()518 fn octal_spaces() {
519     let rdr = Cursor::new(tar!("spaces.tar"));
520     let mut ar = Archive::new(rdr);
521 
522     let entry = ar.entries().unwrap().next().unwrap().unwrap();
523     assert_eq!(entry.header().mode().unwrap() & 0o777, 0o777);
524     assert_eq!(entry.header().uid().unwrap(), 0);
525     assert_eq!(entry.header().gid().unwrap(), 0);
526     assert_eq!(entry.header().size().unwrap(), 2);
527     assert_eq!(entry.header().mtime().unwrap(), 0o12440016664);
528     assert_eq!(entry.header().cksum().unwrap(), 0o4253);
529 }
530 
531 #[test]
extracting_malformed_tar_null_blocks()532 fn extracting_malformed_tar_null_blocks() {
533     let td = t!(TempDir::new("tar-rs"));
534 
535     let mut ar = Builder::new(Vec::new());
536 
537     let path1 = td.path().join("tmpfile1");
538     let path2 = td.path().join("tmpfile2");
539     t!(File::create(&path1));
540     t!(File::create(&path2));
541     t!(ar.append_file("tmpfile1", &mut t!(File::open(&path1))));
542     let mut data = t!(ar.into_inner());
543     let amt = data.len();
544     data.truncate(amt - 512);
545     let mut ar = Builder::new(data);
546     t!(ar.append_file("tmpfile2", &mut t!(File::open(&path2))));
547     t!(ar.finish());
548 
549     let data = t!(ar.into_inner());
550     let mut ar = Archive::new(&data[..]);
551     assert!(ar.unpack(td.path()).is_ok());
552 }
553 
554 #[test]
empty_filename()555 fn empty_filename() {
556     let td = t!(TempDir::new("tar-rs"));
557     let rdr = Cursor::new(tar!("empty_filename.tar"));
558     let mut ar = Archive::new(rdr);
559     assert!(ar.unpack(td.path()).is_ok());
560 }
561 
562 #[test]
file_times()563 fn file_times() {
564     let td = t!(TempDir::new("tar-rs"));
565     let rdr = Cursor::new(tar!("file_times.tar"));
566     let mut ar = Archive::new(rdr);
567     t!(ar.unpack(td.path()));
568 
569     let meta = fs::metadata(td.path().join("a")).unwrap();
570     let mtime = FileTime::from_last_modification_time(&meta);
571     let atime = FileTime::from_last_access_time(&meta);
572     assert_eq!(mtime.unix_seconds(), 1000000000);
573     assert_eq!(mtime.nanoseconds(), 0);
574     assert_eq!(atime.unix_seconds(), 1000000000);
575     assert_eq!(atime.nanoseconds(), 0);
576 }
577 
578 #[test]
backslash_treated_well()579 fn backslash_treated_well() {
580     // Insert a file into an archive with a backslash
581     let td = t!(TempDir::new("tar-rs"));
582     let mut ar = Builder::new(Vec::<u8>::new());
583     t!(ar.append_dir("foo\\bar", td.path()));
584     let mut ar = Archive::new(Cursor::new(t!(ar.into_inner())));
585     let f = t!(t!(ar.entries()).next().unwrap());
586     if cfg!(unix) {
587         assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar"));
588     } else {
589         assert_eq!(t!(f.header().path()).to_str(), Some("foo/bar"));
590     }
591 
592     // Unpack an archive with a backslash in the name
593     let mut ar = Builder::new(Vec::<u8>::new());
594     let mut header = Header::new_gnu();
595     header.set_metadata(&t!(fs::metadata(td.path())));
596     header.set_size(0);
597     for (a, b) in header.as_old_mut().name.iter_mut().zip(b"foo\\bar\x00") {
598         *a = *b;
599     }
600     header.set_cksum();
601     t!(ar.append(&header, &mut io::empty()));
602     let data = t!(ar.into_inner());
603     let mut ar = Archive::new(&data[..]);
604     let f = t!(t!(ar.entries()).next().unwrap());
605     assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar"));
606 
607     let mut ar = Archive::new(&data[..]);
608     t!(ar.unpack(td.path()));
609     assert!(fs::metadata(td.path().join("foo\\bar")).is_ok());
610 }
611 
612 #[cfg(unix)]
613 #[test]
nul_bytes_in_path()614 fn nul_bytes_in_path() {
615     use std::ffi::OsStr;
616     use std::os::unix::prelude::*;
617 
618     let nul_path = OsStr::from_bytes(b"foo\0");
619     let td = t!(TempDir::new("tar-rs"));
620     let mut ar = Builder::new(Vec::<u8>::new());
621     let err = ar.append_dir(nul_path, td.path()).unwrap_err();
622     assert!(err.to_string().contains("contains a nul byte"));
623 }
624 
625 #[test]
links()626 fn links() {
627     let mut ar = Archive::new(Cursor::new(tar!("link.tar")));
628     let mut entries = t!(ar.entries());
629     let link = t!(entries.next().unwrap());
630     assert_eq!(
631         t!(link.header().link_name()).as_ref().map(|p| &**p),
632         Some(Path::new("file"))
633     );
634     let other = t!(entries.next().unwrap());
635     assert!(t!(other.header().link_name()).is_none());
636 }
637 
638 #[test]
639 #[cfg(unix)] // making symlinks on windows is hard
unpack_links()640 fn unpack_links() {
641     let td = t!(TempDir::new("tar-rs"));
642     let mut ar = Archive::new(Cursor::new(tar!("link.tar")));
643     t!(ar.unpack(td.path()));
644 
645     let md = t!(fs::symlink_metadata(td.path().join("lnk")));
646     assert!(md.file_type().is_symlink());
647     assert_eq!(
648         &*t!(fs::read_link(td.path().join("lnk"))),
649         Path::new("file")
650     );
651     t!(File::open(td.path().join("lnk")));
652 }
653 
654 #[test]
pax_simple()655 fn pax_simple() {
656     let mut ar = Archive::new(tar!("pax.tar"));
657     let mut entries = t!(ar.entries());
658 
659     let mut first = t!(entries.next().unwrap());
660     let mut attributes = t!(first.pax_extensions()).unwrap();
661     let first = t!(attributes.next().unwrap());
662     let second = t!(attributes.next().unwrap());
663     let third = t!(attributes.next().unwrap());
664     assert!(attributes.next().is_none());
665 
666     assert_eq!(first.key(), Ok("mtime"));
667     assert_eq!(first.value(), Ok("1453146164.953123768"));
668     assert_eq!(second.key(), Ok("atime"));
669     assert_eq!(second.value(), Ok("1453251915.24892486"));
670     assert_eq!(third.key(), Ok("ctime"));
671     assert_eq!(third.value(), Ok("1453146164.953123768"));
672 }
673 
674 #[test]
pax_path()675 fn pax_path() {
676     let mut ar = Archive::new(tar!("pax2.tar"));
677     let mut entries = t!(ar.entries());
678 
679     let first = t!(entries.next().unwrap());
680     assert!(first.path().unwrap().ends_with("aaaaaaaaaaaaaaa"));
681 }
682 
683 #[test]
long_name_trailing_nul()684 fn long_name_trailing_nul() {
685     let mut b = Builder::new(Vec::<u8>::new());
686 
687     let mut h = Header::new_gnu();
688     t!(h.set_path("././@LongLink"));
689     h.set_size(4);
690     h.set_entry_type(EntryType::new(b'L'));
691     h.set_cksum();
692     t!(b.append(&h, "foo\0".as_bytes()));
693     let mut h = Header::new_gnu();
694 
695     t!(h.set_path("bar"));
696     h.set_size(6);
697     h.set_entry_type(EntryType::file());
698     h.set_cksum();
699     t!(b.append(&h, "foobar".as_bytes()));
700 
701     let contents = t!(b.into_inner());
702     let mut a = Archive::new(&contents[..]);
703 
704     let e = t!(t!(a.entries()).next().unwrap());
705     assert_eq!(&*e.path_bytes(), b"foo");
706 }
707 
708 #[test]
long_linkname_trailing_nul()709 fn long_linkname_trailing_nul() {
710     let mut b = Builder::new(Vec::<u8>::new());
711 
712     let mut h = Header::new_gnu();
713     t!(h.set_path("././@LongLink"));
714     h.set_size(4);
715     h.set_entry_type(EntryType::new(b'K'));
716     h.set_cksum();
717     t!(b.append(&h, "foo\0".as_bytes()));
718     let mut h = Header::new_gnu();
719 
720     t!(h.set_path("bar"));
721     h.set_size(6);
722     h.set_entry_type(EntryType::file());
723     h.set_cksum();
724     t!(b.append(&h, "foobar".as_bytes()));
725 
726     let contents = t!(b.into_inner());
727     let mut a = Archive::new(&contents[..]);
728 
729     let e = t!(t!(a.entries()).next().unwrap());
730     assert_eq!(&*e.link_name_bytes().unwrap(), b"foo");
731 }
732 
733 #[test]
encoded_long_name_has_trailing_nul()734 fn encoded_long_name_has_trailing_nul() {
735     let td = t!(TempDir::new("tar-rs"));
736     let path = td.path().join("foo");
737     t!(t!(File::create(&path)).write_all(b"test"));
738 
739     let mut b = Builder::new(Vec::<u8>::new());
740     let long = repeat("abcd").take(200).collect::<String>();
741 
742     t!(b.append_file(&long, &mut t!(File::open(&path))));
743 
744     let contents = t!(b.into_inner());
745     let mut a = Archive::new(&contents[..]);
746 
747     let mut e = t!(t!(a.entries()).raw(true).next().unwrap());
748     let mut name = Vec::new();
749     t!(e.read_to_end(&mut name));
750     assert_eq!(name[name.len() - 1], 0);
751 
752     let header_name = &e.header().as_gnu().unwrap().name;
753     assert!(header_name.starts_with(b"././@LongLink\x00"));
754 }
755 
756 #[test]
reading_sparse()757 fn reading_sparse() {
758     let rdr = Cursor::new(tar!("sparse.tar"));
759     let mut ar = Archive::new(rdr);
760     let mut entries = t!(ar.entries());
761 
762     let mut a = t!(entries.next().unwrap());
763     let mut s = String::new();
764     assert_eq!(&*a.header().path_bytes(), b"sparse_begin.txt");
765     t!(a.read_to_string(&mut s));
766     assert_eq!(&s[..5], "test\n");
767     assert!(s[5..].chars().all(|x| x == '\u{0}'));
768 
769     let mut a = t!(entries.next().unwrap());
770     let mut s = String::new();
771     assert_eq!(&*a.header().path_bytes(), b"sparse_end.txt");
772     t!(a.read_to_string(&mut s));
773     assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}'));
774     assert_eq!(&s[s.len() - 9..], "test_end\n");
775 
776     let mut a = t!(entries.next().unwrap());
777     let mut s = String::new();
778     assert_eq!(&*a.header().path_bytes(), b"sparse_ext.txt");
779     t!(a.read_to_string(&mut s));
780     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
781     assert_eq!(&s[0x1000..0x1000 + 5], "text\n");
782     assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}'));
783     assert_eq!(&s[0x3000..0x3000 + 5], "text\n");
784     assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}'));
785     assert_eq!(&s[0x5000..0x5000 + 5], "text\n");
786     assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}'));
787     assert_eq!(&s[0x7000..0x7000 + 5], "text\n");
788     assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}'));
789     assert_eq!(&s[0x9000..0x9000 + 5], "text\n");
790     assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}'));
791     assert_eq!(&s[0xb000..0xb000 + 5], "text\n");
792 
793     let mut a = t!(entries.next().unwrap());
794     let mut s = String::new();
795     assert_eq!(&*a.header().path_bytes(), b"sparse.txt");
796     t!(a.read_to_string(&mut s));
797     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
798     assert_eq!(&s[0x1000..0x1000 + 6], "hello\n");
799     assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}'));
800     assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n");
801     assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}'));
802 
803     assert!(entries.next().is_none());
804 }
805 
806 #[test]
extract_sparse()807 fn extract_sparse() {
808     let rdr = Cursor::new(tar!("sparse.tar"));
809     let mut ar = Archive::new(rdr);
810     let td = t!(TempDir::new("tar-rs"));
811     t!(ar.unpack(td.path()));
812 
813     let mut s = String::new();
814     t!(t!(File::open(td.path().join("sparse_begin.txt"))).read_to_string(&mut s));
815     assert_eq!(&s[..5], "test\n");
816     assert!(s[5..].chars().all(|x| x == '\u{0}'));
817 
818     s.truncate(0);
819     t!(t!(File::open(td.path().join("sparse_end.txt"))).read_to_string(&mut s));
820     assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}'));
821     assert_eq!(&s[s.len() - 9..], "test_end\n");
822 
823     s.truncate(0);
824     t!(t!(File::open(td.path().join("sparse_ext.txt"))).read_to_string(&mut s));
825     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
826     assert_eq!(&s[0x1000..0x1000 + 5], "text\n");
827     assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}'));
828     assert_eq!(&s[0x3000..0x3000 + 5], "text\n");
829     assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}'));
830     assert_eq!(&s[0x5000..0x5000 + 5], "text\n");
831     assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}'));
832     assert_eq!(&s[0x7000..0x7000 + 5], "text\n");
833     assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}'));
834     assert_eq!(&s[0x9000..0x9000 + 5], "text\n");
835     assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}'));
836     assert_eq!(&s[0xb000..0xb000 + 5], "text\n");
837 
838     s.truncate(0);
839     t!(t!(File::open(td.path().join("sparse.txt"))).read_to_string(&mut s));
840     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
841     assert_eq!(&s[0x1000..0x1000 + 6], "hello\n");
842     assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}'));
843     assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n");
844     assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}'));
845 }
846 
847 #[test]
path_separators()848 fn path_separators() {
849     let mut ar = Builder::new(Vec::new());
850     let td = t!(TempDir::new("tar-rs"));
851 
852     let path = td.path().join("test");
853     t!(t!(File::create(&path)).write_all(b"test"));
854 
855     let short_path: PathBuf = repeat("abcd").take(2).collect();
856     let long_path: PathBuf = repeat("abcd").take(50).collect();
857 
858     // Make sure UStar headers normalize to Unix path separators
859     let mut header = Header::new_ustar();
860 
861     t!(header.set_path(&short_path));
862     assert_eq!(t!(header.path()), short_path);
863     assert!(!header.path_bytes().contains(&b'\\'));
864 
865     t!(header.set_path(&long_path));
866     assert_eq!(t!(header.path()), long_path);
867     assert!(!header.path_bytes().contains(&b'\\'));
868 
869     // Make sure GNU headers normalize to Unix path separators,
870     // including the `@LongLink` fallback used by `append_file`.
871     t!(ar.append_file(&short_path, &mut t!(File::open(&path))));
872     t!(ar.append_file(&long_path, &mut t!(File::open(&path))));
873 
874     let rd = Cursor::new(t!(ar.into_inner()));
875     let mut ar = Archive::new(rd);
876     let mut entries = t!(ar.entries());
877 
878     let entry = t!(entries.next().unwrap());
879     assert_eq!(t!(entry.path()), short_path);
880     assert!(!entry.path_bytes().contains(&b'\\'));
881 
882     let entry = t!(entries.next().unwrap());
883     assert_eq!(t!(entry.path()), long_path);
884     assert!(!entry.path_bytes().contains(&b'\\'));
885 
886     assert!(entries.next().is_none());
887 }
888 
889 #[test]
890 #[cfg(unix)]
append_path_symlink()891 fn append_path_symlink() {
892     use std::borrow::Cow;
893     use std::env;
894     use std::os::unix::fs::symlink;
895 
896     let mut ar = Builder::new(Vec::new());
897     ar.follow_symlinks(false);
898     let td = t!(TempDir::new("tar-rs"));
899 
900     let long_linkname = repeat("abcd").take(30).collect::<String>();
901     let long_pathname = repeat("dcba").take(30).collect::<String>();
902     t!(env::set_current_dir(td.path()));
903     // "short" path name / short link name
904     t!(symlink("testdest", "test"));
905     t!(ar.append_path("test"));
906     // short path name / long link name
907     t!(symlink(&long_linkname, "test2"));
908     t!(ar.append_path("test2"));
909     // long path name / long link name
910     t!(symlink(&long_linkname, &long_pathname));
911     t!(ar.append_path(&long_pathname));
912 
913     let rd = Cursor::new(t!(ar.into_inner()));
914     let mut ar = Archive::new(rd);
915     let mut entries = t!(ar.entries());
916 
917     let entry = t!(entries.next().unwrap());
918     assert_eq!(t!(entry.path()), Path::new("test"));
919     assert_eq!(
920         t!(entry.link_name()),
921         Some(Cow::from(Path::new("testdest")))
922     );
923     assert_eq!(t!(entry.header().size()), 0);
924 
925     let entry = t!(entries.next().unwrap());
926     assert_eq!(t!(entry.path()), Path::new("test2"));
927     assert_eq!(
928         t!(entry.link_name()),
929         Some(Cow::from(Path::new(&long_linkname)))
930     );
931     assert_eq!(t!(entry.header().size()), 0);
932 
933     let entry = t!(entries.next().unwrap());
934     assert_eq!(t!(entry.path()), Path::new(&long_pathname));
935     assert_eq!(
936         t!(entry.link_name()),
937         Some(Cow::from(Path::new(&long_linkname)))
938     );
939     assert_eq!(t!(entry.header().size()), 0);
940 
941     assert!(entries.next().is_none());
942 }
943 
944 #[test]
name_with_slash_doesnt_fool_long_link_and_bsd_compat()945 fn name_with_slash_doesnt_fool_long_link_and_bsd_compat() {
946     let td = t!(TempDir::new("tar-rs"));
947 
948     let mut ar = Builder::new(Vec::new());
949 
950     let mut h = Header::new_gnu();
951     t!(h.set_path("././@LongLink"));
952     h.set_size(4);
953     h.set_entry_type(EntryType::new(b'L'));
954     h.set_cksum();
955     t!(ar.append(&h, "foo\0".as_bytes()));
956 
957     let mut header = Header::new_gnu();
958     header.set_entry_type(EntryType::Regular);
959     t!(header.set_path("testdir/"));
960     header.set_size(0);
961     header.set_cksum();
962     t!(ar.append(&header, &mut io::empty()));
963 
964     // Extracting
965     let rdr = Cursor::new(t!(ar.into_inner()));
966     let mut ar = Archive::new(rdr);
967     t!(ar.unpack(td.path()));
968 
969     // Iterating
970     let rdr = Cursor::new(ar.into_inner().into_inner());
971     let mut ar = Archive::new(rdr);
972     assert!(t!(ar.entries()).all(|fr| fr.is_ok()));
973 
974     assert!(td.path().join("foo").is_file());
975 }
976 
977 #[test]
insert_local_file_different_name()978 fn insert_local_file_different_name() {
979     let mut ar = Builder::new(Vec::new());
980     let td = t!(TempDir::new("tar-rs"));
981     let path = td.path().join("directory");
982     t!(fs::create_dir(&path));
983     ar.append_path_with_name(&path, "archive/dir").unwrap();
984     let path = td.path().join("file");
985     t!(t!(File::create(&path)).write_all(b"test"));
986     ar.append_path_with_name(&path, "archive/dir/f").unwrap();
987 
988     let rd = Cursor::new(t!(ar.into_inner()));
989     let mut ar = Archive::new(rd);
990     let mut entries = t!(ar.entries());
991     let entry = t!(entries.next().unwrap());
992     assert_eq!(t!(entry.path()), Path::new("archive/dir"));
993     let entry = t!(entries.next().unwrap());
994     assert_eq!(t!(entry.path()), Path::new("archive/dir/f"));
995     assert!(entries.next().is_none());
996 }
997 
998 #[test]
999 #[cfg(unix)]
tar_directory_containing_symlink_to_directory()1000 fn tar_directory_containing_symlink_to_directory() {
1001     use std::os::unix::fs::symlink;
1002 
1003     let td = t!(TempDir::new("tar-rs"));
1004     let dummy_src = t!(TempDir::new("dummy_src"));
1005     let dummy_dst = td.path().join("dummy_dst");
1006     let mut ar = Builder::new(Vec::new());
1007     t!(symlink(dummy_src.path().display().to_string(), &dummy_dst));
1008 
1009     assert!(dummy_dst.read_link().is_ok());
1010     assert!(dummy_dst.read_link().unwrap().is_dir());
1011     ar.append_dir_all("symlinks", td.path()).unwrap();
1012     ar.finish().unwrap();
1013 }
1014