1 extern crate filetime;
2 extern crate tar;
3 extern crate tempfile;
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 filetime::FileTime;
14 use tar::{Archive, Builder, EntryType, Header, HeaderMode};
15 use tempfile::{Builder as TempBuilder, TempDir};
16 
17 macro_rules! t {
18     ($e:expr) => {
19         match $e {
20             Ok(v) => v,
pax_extensions(a: &[u8]) -> PaxExtensions21             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     };
pax_extensions_size(a: &[u8]) -> Option<u64>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]
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);
next(&mut self) -> Option<io::Result<PaxExtension<'entry>>>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]
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     }
key(&self) -> Result<&'entry str, str::Utf8Error>94 }
95 
96 #[test]
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 
value_bytes(&self) -> &'entry [u8]111 #[test]
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]
133 fn writing_files() {
134     let mut ar = Builder::new(Vec::new());
135     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
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]
157 fn large_filename() {
158     let mut ar = Builder::new(Vec::new());
159     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
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]
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 
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]
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()));
243     check_dirtree(&td);
244 }
245 
246 #[test]
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));
251 
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
258             return;
259         }
260         panic!("unexpected error: {:?}", err);
261     }
262     panic!(
263         "unpack() should have returned an error of kind {:?}, returned Ok",
264         std::io::ErrorKind::AlreadyExists
265     )
266 }
267 
268 #[test]
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));
273 
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()));
278 }
279 
280 #[test]
281 #[cfg(unix)]
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));
286 
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
293             return;
294         }
295         panic!("unexpected error: {:?}", err);
296     }
297     panic!(
298         "unpack() should have returned an error of kind {:?}, returned Ok",
299         std::io::ErrorKind::AlreadyExists
300     )
301 }
302 
303 #[test]
304 #[cfg(unix)]
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));
309 
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()));
314 }
315 
316 #[test]
317 #[cfg(all(unix, feature = "xattr"))]
318 fn xattrs() {
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()));
326 
327     let val = xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap();
328     assert_eq!(val.unwrap(), "epm".as_bytes());
329 }
330 
331 #[test]
332 #[cfg(all(unix, feature = "xattr"))]
333 fn no_xattrs() {
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()));
341 
342     assert_eq!(
343         xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap(),
344         None
345     );
346 }
347 
348 #[test]
349 fn writing_and_extracting_directories() {
350     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
351 
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))));
358     t!(ar.finish());
359 
360     let rdr = Cursor::new(t!(ar.into_inner()));
361     let mut ar = Archive::new(rdr);
362     t!(ar.unpack(td.path()));
363     check_dirtree(&td);
364 }
365 
366 #[test]
367 fn writing_directories_recursively() {
368     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
369 
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"));
376 
377     let mut ar = Builder::new(Vec::new());
378     t!(ar.append_dir_all("foobar", base_dir));
379     let data = t!(ar.into_inner());
380 
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())
388         .unwrap_or(false));
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())
394         .unwrap_or(false));
395 }
396 
397 #[test]
398 fn append_dir_all_blank_dest() {
399     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
400 
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"));
407 
408     let mut ar = Builder::new(Vec::new());
409     t!(ar.append_dir_all("", base_dir));
410     let data = t!(ar.into_inner());
411 
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())
419         .unwrap_or(false));
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())
425         .unwrap_or(false));
426 }
427 
428 #[test]
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"));
433 
434     let mut ar = Builder::new(Vec::new());
435     let result = ar.append_dir_all("test", path);
436     assert!(result.is_err());
437 }
438 
439 #[test]
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()));
445 
446     let some_dir = td.path().join("some_dir");
447     assert!(fs::metadata(&some_dir).map(|m| m.is_dir()).unwrap_or(false));
448 }
449 
450 #[test]
451 fn unpack_old_style_bsd_dir() {
452     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
453 
454     let mut ar = Builder::new(Vec::new());
455 
456     let mut header = Header::new_old();
457     header.set_entry_type(EntryType::Regular);
458     t!(header.set_path("testdir/"));
459     header.set_size(0);
460     header.set_cksum();
461     t!(ar.append(&header, &mut io::empty()));
462 
463     // Extracting
464     let rdr = Cursor::new(t!(ar.into_inner()));
465     let mut ar = Archive::new(rdr);
466     t!(ar.unpack(td.path()));
467 
468     // Iterating
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()));
472 
473     assert!(td.path().join("testdir").is_dir());
474 }
475 
476 #[test]
477 fn handling_incorrect_file_size() {
478     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
479 
480     let mut ar = Builder::new(Vec::new());
481 
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
489     header.set_cksum();
490     t!(ar.append(&header, &mut file));
491 
492     // Extracting
493     let rdr = Cursor::new(t!(ar.into_inner()));
494     let mut ar = Archive::new(rdr);
495     assert!(ar.unpack(td.path()).is_err());
496 
497     // Iterating
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()));
501 }
502 
503 #[test]
504 fn extracting_malicious_tarball() {
505     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
506 
507     let mut evil_tar = Vec::new();
508 
509     {
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);
514             {
515                 let h = header.as_gnu_mut().unwrap();
516                 for (a, b) in h.name.iter_mut().zip(path.as_bytes()) {
517                     *a = *b;
518                 }
519             }
520             header.set_size(1);
521             header.set_cksum();
522             t!(a.append(&header, io::repeat(1).take(1)));
523         };
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");
534         append("");
535         append("././//./..");
536         append("..");
537         append("/////////..");
538         append("/////////");
539     }
540 
541     let mut ar = Archive::new(&evil_tar[..]);
542     t!(ar.unpack(td.path()));
543 
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());
556 
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());
560 
561     // The `tmp` subdirectory should be created and within this
562     // subdirectory, there should be files named `abs_evil.txt` through
563     // `abs_evil6.txt`.
564     assert!(fs::metadata(td.path().join("tmp"))
565         .map(|m| m.is_dir())
566         .unwrap_or(false));
567     assert!(fs::metadata(td.path().join("tmp/abs_evil.txt"))
568         .map(|m| m.is_file())
569         .unwrap_or(false));
570     assert!(fs::metadata(td.path().join("tmp/abs_evil2.txt"))
571         .map(|m| m.is_file())
572         .unwrap_or(false));
573     assert!(fs::metadata(td.path().join("tmp/abs_evil3.txt"))
574         .map(|m| m.is_file())
575         .unwrap_or(false));
576     assert!(fs::metadata(td.path().join("tmp/abs_evil4.txt"))
577         .map(|m| m.is_file())
578         .unwrap_or(false));
579     assert!(fs::metadata(td.path().join("tmp/abs_evil5.txt"))
580         .map(|m| m.is_file())
581         .unwrap_or(false));
582     assert!(fs::metadata(td.path().join("tmp/abs_evil6.txt"))
583         .map(|m| m.is_file())
584         .unwrap_or(false));
585 }
586 
587 #[test]
588 fn octal_spaces() {
589     let rdr = Cursor::new(tar!("spaces.tar"));
590     let mut ar = Archive::new(rdr);
591 
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);
599 }
600 
601 #[test]
602 fn extracting_malformed_tar_null_blocks() {
603     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
604 
605     let mut ar = Builder::new(Vec::new());
606 
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))));
617     t!(ar.finish());
618 
619     let data = t!(ar.into_inner());
620     let mut ar = Archive::new(&data[..]);
621     assert!(ar.unpack(td.path()).is_ok());
622 }
623 
624 #[test]
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());
630 }
631 
632 #[test]
633 fn file_times() {
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()));
638 
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);
646 }
647 
648 #[test]
649 fn zero_file_times() {
650     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
651 
652     let mut ar = Builder::new(Vec::new());
653     ar.mode(HeaderMode::Deterministic);
654     let path = td.path().join("tmpfile");
655     t!(File::create(&path));
656     t!(ar.append_path_with_name(&path, "a"));
657 
658     let data = t!(ar.into_inner());
659     let mut ar = Archive::new(&data[..]);
660     assert!(ar.unpack(td.path()).is_ok());
661 
662     let meta = fs::metadata(td.path().join("a")).unwrap();
663     let mtime = FileTime::from_last_modification_time(&meta);
664     let atime = FileTime::from_last_access_time(&meta);
665     assert!(mtime.unix_seconds() != 0);
666     assert!(atime.unix_seconds() != 0);
667 }
668 
669 #[test]
670 fn backslash_treated_well() {
671     // Insert a file into an archive with a backslash
672     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
673     let mut ar = Builder::new(Vec::<u8>::new());
674     t!(ar.append_dir("foo\\bar", td.path()));
675     let mut ar = Archive::new(Cursor::new(t!(ar.into_inner())));
676     let f = t!(t!(ar.entries()).next().unwrap());
677     if cfg!(unix) {
678         assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar"));
679     } else {
680         assert_eq!(t!(f.header().path()).to_str(), Some("foo/bar"));
681     }
682 
683     // Unpack an archive with a backslash in the name
684     let mut ar = Builder::new(Vec::<u8>::new());
685     let mut header = Header::new_gnu();
686     header.set_metadata(&t!(fs::metadata(td.path())));
687     header.set_size(0);
688     for (a, b) in header.as_old_mut().name.iter_mut().zip(b"foo\\bar\x00") {
689         *a = *b;
690     }
691     header.set_cksum();
692     t!(ar.append(&header, &mut io::empty()));
693     let data = t!(ar.into_inner());
694     let mut ar = Archive::new(&data[..]);
695     let f = t!(t!(ar.entries()).next().unwrap());
696     assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar"));
697 
698     let mut ar = Archive::new(&data[..]);
699     t!(ar.unpack(td.path()));
700     assert!(fs::metadata(td.path().join("foo\\bar")).is_ok());
701 }
702 
703 #[cfg(unix)]
704 #[test]
705 fn nul_bytes_in_path() {
706     use std::ffi::OsStr;
707     use std::os::unix::prelude::*;
708 
709     let nul_path = OsStr::from_bytes(b"foo\0");
710     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
711     let mut ar = Builder::new(Vec::<u8>::new());
712     let err = ar.append_dir(nul_path, td.path()).unwrap_err();
713     assert!(err.to_string().contains("contains a nul byte"));
714 }
715 
716 #[test]
717 fn links() {
718     let mut ar = Archive::new(Cursor::new(tar!("link.tar")));
719     let mut entries = t!(ar.entries());
720     let link = t!(entries.next().unwrap());
721     assert_eq!(
722         t!(link.header().link_name()).as_ref().map(|p| &**p),
723         Some(Path::new("file"))
724     );
725     let other = t!(entries.next().unwrap());
726     assert!(t!(other.header().link_name()).is_none());
727 }
728 
729 #[test]
730 #[cfg(unix)] // making symlinks on windows is hard
731 fn unpack_links() {
732     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
733     let mut ar = Archive::new(Cursor::new(tar!("link.tar")));
734     t!(ar.unpack(td.path()));
735 
736     let md = t!(fs::symlink_metadata(td.path().join("lnk")));
737     assert!(md.file_type().is_symlink());
738     assert_eq!(
739         &*t!(fs::read_link(td.path().join("lnk"))),
740         Path::new("file")
741     );
742     t!(File::open(td.path().join("lnk")));
743 }
744 
745 #[test]
746 fn pax_size() {
747     let mut ar = Archive::new(tar!("pax_size.tar"));
748     let mut entries = t!(ar.entries());
749     let mut entry = t!(entries.next().unwrap());
750     let mut attributes = t!(entry.pax_extensions()).unwrap();
751 
752     let _first = t!(attributes.next().unwrap());
753     let _second = t!(attributes.next().unwrap());
754     let _third = t!(attributes.next().unwrap());
755     let fourth = t!(attributes.next().unwrap());
756     assert!(attributes.next().is_none());
757 
758     assert_eq!(fourth.key(), Ok("size"));
759     assert_eq!(fourth.value(), Ok("4"));
760 
761     assert_eq!(entry.header().size().unwrap(), 0);
762     assert_eq!(entry.size(), 4);
763 }
764 
765 #[test]
766 fn pax_simple() {
767     let mut ar = Archive::new(tar!("pax.tar"));
768     let mut entries = t!(ar.entries());
769 
770     let mut first = t!(entries.next().unwrap());
771     let mut attributes = t!(first.pax_extensions()).unwrap();
772     let first = t!(attributes.next().unwrap());
773     let second = t!(attributes.next().unwrap());
774     let third = t!(attributes.next().unwrap());
775     assert!(attributes.next().is_none());
776 
777     assert_eq!(first.key(), Ok("mtime"));
778     assert_eq!(first.value(), Ok("1453146164.953123768"));
779     assert_eq!(second.key(), Ok("atime"));
780     assert_eq!(second.value(), Ok("1453251915.24892486"));
781     assert_eq!(third.key(), Ok("ctime"));
782     assert_eq!(third.value(), Ok("1453146164.953123768"));
783 }
784 
785 #[test]
786 fn pax_path() {
787     let mut ar = Archive::new(tar!("pax2.tar"));
788     let mut entries = t!(ar.entries());
789 
790     let first = t!(entries.next().unwrap());
791     assert!(first.path().unwrap().ends_with("aaaaaaaaaaaaaaa"));
792 }
793 
794 #[test]
795 fn pax_linkpath() {
796     let mut ar = Archive::new(tar!("pax2.tar"));
797     let mut links = t!(ar.entries()).skip(3).take(2);
798 
799     let long_symlink = t!(links.next().unwrap());
800     let link_name = long_symlink.link_name().unwrap().unwrap();
801     assert!(link_name.to_str().unwrap().len() > 99);
802     assert!(link_name.ends_with("bbbbbbbbbbbbbbb"));
803 
804     let long_hardlink = t!(links.next().unwrap());
805     let link_name = long_hardlink.link_name().unwrap().unwrap();
806     assert!(link_name.to_str().unwrap().len() > 99);
807     assert!(link_name.ends_with("ccccccccccccccc"));
808 }
809 
810 #[test]
811 fn long_name_trailing_nul() {
812     let mut b = Builder::new(Vec::<u8>::new());
813 
814     let mut h = Header::new_gnu();
815     t!(h.set_path("././@LongLink"));
816     h.set_size(4);
817     h.set_entry_type(EntryType::new(b'L'));
818     h.set_cksum();
819     t!(b.append(&h, "foo\0".as_bytes()));
820     let mut h = Header::new_gnu();
821 
822     t!(h.set_path("bar"));
823     h.set_size(6);
824     h.set_entry_type(EntryType::file());
825     h.set_cksum();
826     t!(b.append(&h, "foobar".as_bytes()));
827 
828     let contents = t!(b.into_inner());
829     let mut a = Archive::new(&contents[..]);
830 
831     let e = t!(t!(a.entries()).next().unwrap());
832     assert_eq!(&*e.path_bytes(), b"foo");
833 }
834 
835 #[test]
836 fn long_linkname_trailing_nul() {
837     let mut b = Builder::new(Vec::<u8>::new());
838 
839     let mut h = Header::new_gnu();
840     t!(h.set_path("././@LongLink"));
841     h.set_size(4);
842     h.set_entry_type(EntryType::new(b'K'));
843     h.set_cksum();
844     t!(b.append(&h, "foo\0".as_bytes()));
845     let mut h = Header::new_gnu();
846 
847     t!(h.set_path("bar"));
848     h.set_size(6);
849     h.set_entry_type(EntryType::file());
850     h.set_cksum();
851     t!(b.append(&h, "foobar".as_bytes()));
852 
853     let contents = t!(b.into_inner());
854     let mut a = Archive::new(&contents[..]);
855 
856     let e = t!(t!(a.entries()).next().unwrap());
857     assert_eq!(&*e.link_name_bytes().unwrap(), b"foo");
858 }
859 
860 #[test]
861 fn encoded_long_name_has_trailing_nul() {
862     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
863     let path = td.path().join("foo");
864     t!(t!(File::create(&path)).write_all(b"test"));
865 
866     let mut b = Builder::new(Vec::<u8>::new());
867     let long = repeat("abcd").take(200).collect::<String>();
868 
869     t!(b.append_file(&long, &mut t!(File::open(&path))));
870 
871     let contents = t!(b.into_inner());
872     let mut a = Archive::new(&contents[..]);
873 
874     let mut e = t!(t!(a.entries()).raw(true).next().unwrap());
875     let mut name = Vec::new();
876     t!(e.read_to_end(&mut name));
877     assert_eq!(name[name.len() - 1], 0);
878 
879     let header_name = &e.header().as_gnu().unwrap().name;
880     assert!(header_name.starts_with(b"././@LongLink\x00"));
881 }
882 
883 #[test]
884 fn reading_sparse() {
885     let rdr = Cursor::new(tar!("sparse.tar"));
886     let mut ar = Archive::new(rdr);
887     let mut entries = t!(ar.entries());
888 
889     let mut a = t!(entries.next().unwrap());
890     let mut s = String::new();
891     assert_eq!(&*a.header().path_bytes(), b"sparse_begin.txt");
892     t!(a.read_to_string(&mut s));
893     assert_eq!(&s[..5], "test\n");
894     assert!(s[5..].chars().all(|x| x == '\u{0}'));
895 
896     let mut a = t!(entries.next().unwrap());
897     let mut s = String::new();
898     assert_eq!(&*a.header().path_bytes(), b"sparse_end.txt");
899     t!(a.read_to_string(&mut s));
900     assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}'));
901     assert_eq!(&s[s.len() - 9..], "test_end\n");
902 
903     let mut a = t!(entries.next().unwrap());
904     let mut s = String::new();
905     assert_eq!(&*a.header().path_bytes(), b"sparse_ext.txt");
906     t!(a.read_to_string(&mut s));
907     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
908     assert_eq!(&s[0x1000..0x1000 + 5], "text\n");
909     assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}'));
910     assert_eq!(&s[0x3000..0x3000 + 5], "text\n");
911     assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}'));
912     assert_eq!(&s[0x5000..0x5000 + 5], "text\n");
913     assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}'));
914     assert_eq!(&s[0x7000..0x7000 + 5], "text\n");
915     assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}'));
916     assert_eq!(&s[0x9000..0x9000 + 5], "text\n");
917     assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}'));
918     assert_eq!(&s[0xb000..0xb000 + 5], "text\n");
919 
920     let mut a = t!(entries.next().unwrap());
921     let mut s = String::new();
922     assert_eq!(&*a.header().path_bytes(), b"sparse.txt");
923     t!(a.read_to_string(&mut s));
924     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
925     assert_eq!(&s[0x1000..0x1000 + 6], "hello\n");
926     assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}'));
927     assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n");
928     assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}'));
929 
930     assert!(entries.next().is_none());
931 }
932 
933 #[test]
934 fn extract_sparse() {
935     let rdr = Cursor::new(tar!("sparse.tar"));
936     let mut ar = Archive::new(rdr);
937     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
938     t!(ar.unpack(td.path()));
939 
940     let mut s = String::new();
941     t!(t!(File::open(td.path().join("sparse_begin.txt"))).read_to_string(&mut s));
942     assert_eq!(&s[..5], "test\n");
943     assert!(s[5..].chars().all(|x| x == '\u{0}'));
944 
945     s.truncate(0);
946     t!(t!(File::open(td.path().join("sparse_end.txt"))).read_to_string(&mut s));
947     assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}'));
948     assert_eq!(&s[s.len() - 9..], "test_end\n");
949 
950     s.truncate(0);
951     t!(t!(File::open(td.path().join("sparse_ext.txt"))).read_to_string(&mut s));
952     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
953     assert_eq!(&s[0x1000..0x1000 + 5], "text\n");
954     assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}'));
955     assert_eq!(&s[0x3000..0x3000 + 5], "text\n");
956     assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}'));
957     assert_eq!(&s[0x5000..0x5000 + 5], "text\n");
958     assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}'));
959     assert_eq!(&s[0x7000..0x7000 + 5], "text\n");
960     assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}'));
961     assert_eq!(&s[0x9000..0x9000 + 5], "text\n");
962     assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}'));
963     assert_eq!(&s[0xb000..0xb000 + 5], "text\n");
964 
965     s.truncate(0);
966     t!(t!(File::open(td.path().join("sparse.txt"))).read_to_string(&mut s));
967     assert!(s[..0x1000].chars().all(|x| x == '\u{0}'));
968     assert_eq!(&s[0x1000..0x1000 + 6], "hello\n");
969     assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}'));
970     assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n");
971     assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}'));
972 }
973 
974 #[test]
975 fn sparse_with_trailing() {
976     let rdr = Cursor::new(tar!("sparse-1.tar"));
977     let mut ar = Archive::new(rdr);
978     let mut entries = t!(ar.entries());
979     let mut a = t!(entries.next().unwrap());
980     let mut s = String::new();
981     t!(a.read_to_string(&mut s));
982     assert_eq!(0x100_00c, s.len());
983     assert_eq!(&s[..0xc], "0MB through\n");
984     assert!(s[0xc..0x100_000].chars().all(|x| x == '\u{0}'));
985     assert_eq!(&s[0x100_000..], "1MB through\n");
986 }
987 
988 #[test]
989 fn path_separators() {
990     let mut ar = Builder::new(Vec::new());
991     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
992 
993     let path = td.path().join("test");
994     t!(t!(File::create(&path)).write_all(b"test"));
995 
996     let short_path: PathBuf = repeat("abcd").take(2).collect();
997     let long_path: PathBuf = repeat("abcd").take(50).collect();
998 
999     // Make sure UStar headers normalize to Unix path separators
1000     let mut header = Header::new_ustar();
1001 
1002     t!(header.set_path(&short_path));
1003     assert_eq!(t!(header.path()), short_path);
1004     assert!(!header.path_bytes().contains(&b'\\'));
1005 
1006     t!(header.set_path(&long_path));
1007     assert_eq!(t!(header.path()), long_path);
1008     assert!(!header.path_bytes().contains(&b'\\'));
1009 
1010     // Make sure GNU headers normalize to Unix path separators,
1011     // including the `@LongLink` fallback used by `append_file`.
1012     t!(ar.append_file(&short_path, &mut t!(File::open(&path))));
1013     t!(ar.append_file(&long_path, &mut t!(File::open(&path))));
1014 
1015     let rd = Cursor::new(t!(ar.into_inner()));
1016     let mut ar = Archive::new(rd);
1017     let mut entries = t!(ar.entries());
1018 
1019     let entry = t!(entries.next().unwrap());
1020     assert_eq!(t!(entry.path()), short_path);
1021     assert!(!entry.path_bytes().contains(&b'\\'));
1022 
1023     let entry = t!(entries.next().unwrap());
1024     assert_eq!(t!(entry.path()), long_path);
1025     assert!(!entry.path_bytes().contains(&b'\\'));
1026 
1027     assert!(entries.next().is_none());
1028 }
1029 
1030 #[test]
1031 #[cfg(unix)]
1032 fn append_path_symlink() {
1033     use std::borrow::Cow;
1034     use std::env;
1035     use std::os::unix::fs::symlink;
1036 
1037     let mut ar = Builder::new(Vec::new());
1038     ar.follow_symlinks(false);
1039     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1040 
1041     let long_linkname = repeat("abcd").take(30).collect::<String>();
1042     let long_pathname = repeat("dcba").take(30).collect::<String>();
1043     t!(env::set_current_dir(td.path()));
1044     // "short" path name / short link name
1045     t!(symlink("testdest", "test"));
1046     t!(ar.append_path("test"));
1047     // short path name / long link name
1048     t!(symlink(&long_linkname, "test2"));
1049     t!(ar.append_path("test2"));
1050     // long path name / long link name
1051     t!(symlink(&long_linkname, &long_pathname));
1052     t!(ar.append_path(&long_pathname));
1053 
1054     let rd = Cursor::new(t!(ar.into_inner()));
1055     let mut ar = Archive::new(rd);
1056     let mut entries = t!(ar.entries());
1057 
1058     let entry = t!(entries.next().unwrap());
1059     assert_eq!(t!(entry.path()), Path::new("test"));
1060     assert_eq!(
1061         t!(entry.link_name()),
1062         Some(Cow::from(Path::new("testdest")))
1063     );
1064     assert_eq!(t!(entry.header().size()), 0);
1065 
1066     let entry = t!(entries.next().unwrap());
1067     assert_eq!(t!(entry.path()), Path::new("test2"));
1068     assert_eq!(
1069         t!(entry.link_name()),
1070         Some(Cow::from(Path::new(&long_linkname)))
1071     );
1072     assert_eq!(t!(entry.header().size()), 0);
1073 
1074     let entry = t!(entries.next().unwrap());
1075     assert_eq!(t!(entry.path()), Path::new(&long_pathname));
1076     assert_eq!(
1077         t!(entry.link_name()),
1078         Some(Cow::from(Path::new(&long_linkname)))
1079     );
1080     assert_eq!(t!(entry.header().size()), 0);
1081 
1082     assert!(entries.next().is_none());
1083 }
1084 
1085 #[test]
1086 fn name_with_slash_doesnt_fool_long_link_and_bsd_compat() {
1087     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1088 
1089     let mut ar = Builder::new(Vec::new());
1090 
1091     let mut h = Header::new_gnu();
1092     t!(h.set_path("././@LongLink"));
1093     h.set_size(4);
1094     h.set_entry_type(EntryType::new(b'L'));
1095     h.set_cksum();
1096     t!(ar.append(&h, "foo\0".as_bytes()));
1097 
1098     let mut header = Header::new_gnu();
1099     header.set_entry_type(EntryType::Regular);
1100     t!(header.set_path("testdir/"));
1101     header.set_size(0);
1102     header.set_cksum();
1103     t!(ar.append(&header, &mut io::empty()));
1104 
1105     // Extracting
1106     let rdr = Cursor::new(t!(ar.into_inner()));
1107     let mut ar = Archive::new(rdr);
1108     t!(ar.unpack(td.path()));
1109 
1110     // Iterating
1111     let rdr = Cursor::new(ar.into_inner().into_inner());
1112     let mut ar = Archive::new(rdr);
1113     assert!(t!(ar.entries()).all(|fr| fr.is_ok()));
1114 
1115     assert!(td.path().join("foo").is_file());
1116 }
1117 
1118 #[test]
1119 fn insert_local_file_different_name() {
1120     let mut ar = Builder::new(Vec::new());
1121     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1122     let path = td.path().join("directory");
1123     t!(fs::create_dir(&path));
1124     ar.append_path_with_name(&path, "archive/dir").unwrap();
1125     let path = td.path().join("file");
1126     t!(t!(File::create(&path)).write_all(b"test"));
1127     ar.append_path_with_name(&path, "archive/dir/f").unwrap();
1128 
1129     let rd = Cursor::new(t!(ar.into_inner()));
1130     let mut ar = Archive::new(rd);
1131     let mut entries = t!(ar.entries());
1132     let entry = t!(entries.next().unwrap());
1133     assert_eq!(t!(entry.path()), Path::new("archive/dir"));
1134     let entry = t!(entries.next().unwrap());
1135     assert_eq!(t!(entry.path()), Path::new("archive/dir/f"));
1136     assert!(entries.next().is_none());
1137 }
1138 
1139 #[test]
1140 #[cfg(unix)]
1141 fn tar_directory_containing_symlink_to_directory() {
1142     use std::os::unix::fs::symlink;
1143 
1144     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1145     let dummy_src = t!(TempBuilder::new().prefix("dummy_src").tempdir());
1146     let dummy_dst = td.path().join("dummy_dst");
1147     let mut ar = Builder::new(Vec::new());
1148     t!(symlink(dummy_src.path().display().to_string(), &dummy_dst));
1149 
1150     assert!(dummy_dst.read_link().is_ok());
1151     assert!(dummy_dst.read_link().unwrap().is_dir());
1152     ar.append_dir_all("symlinks", td.path()).unwrap();
1153     ar.finish().unwrap();
1154 }
1155 
1156 #[test]
1157 fn long_path() {
1158     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1159     let rdr = Cursor::new(tar!("7z_long_path.tar"));
1160     let mut ar = Archive::new(rdr);
1161     assert!(ar.unpack(td.path()).is_ok());
1162 }
1163 
1164 #[test]
1165 fn unpack_path_larger_than_windows_max_path() {
1166     let dir_name = "iamaprettylongnameandtobepreciseiam91characterslongwhichsomethinkisreallylongandothersdonot";
1167     // 183 character directory name
1168     let really_long_path = format!("{}{}", dir_name, dir_name);
1169     let td = t!(TempBuilder::new().prefix(&really_long_path).tempdir());
1170     // directory in 7z_long_path.tar is over 100 chars
1171     let rdr = Cursor::new(tar!("7z_long_path.tar"));
1172     let mut ar = Archive::new(rdr);
1173     // should unpack path greater than windows MAX_PATH length of 260 characters
1174     assert!(ar.unpack(td.path()).is_ok());
1175 }
1176 
1177 #[test]
1178 fn append_long_multibyte() {
1179     let mut x = tar::Builder::new(Vec::new());
1180     let mut name = String::new();
1181     let data: &[u8] = &[];
1182     for _ in 0..512 {
1183         name.push('a');
1184         name.push('��');
1185         x.append_data(&mut Header::new_gnu(), &name, data).unwrap();
1186         name.pop();
1187     }
1188 }
1189 
1190 #[test]
1191 fn read_only_directory_containing_files() {
1192     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1193 
1194     let mut b = Builder::new(Vec::<u8>::new());
1195 
1196     let mut h = Header::new_gnu();
1197     t!(h.set_path("dir/"));
1198     h.set_size(0);
1199     h.set_entry_type(EntryType::dir());
1200     h.set_mode(0o444);
1201     h.set_cksum();
1202     t!(b.append(&h, "".as_bytes()));
1203 
1204     let mut h = Header::new_gnu();
1205     t!(h.set_path("dir/file"));
1206     h.set_size(2);
1207     h.set_entry_type(EntryType::file());
1208     h.set_cksum();
1209     t!(b.append(&h, "hi".as_bytes()));
1210 
1211     let contents = t!(b.into_inner());
1212     let mut ar = Archive::new(&contents[..]);
1213     assert!(ar.unpack(td.path()).is_ok());
1214 }
1215 
1216 // This test was marked linux only due to macOS CI can't handle `set_current_dir` correctly
1217 #[test]
1218 #[cfg(target_os = "linux")]
1219 fn tar_directory_containing_special_files() {
1220     use std::env;
1221     use std::ffi::CString;
1222 
1223     let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
1224     let fifo = td.path().join("fifo");
1225 
1226     unsafe {
1227         let fifo_path = t!(CString::new(fifo.to_str().unwrap()));
1228         let ret = libc::mknod(fifo_path.as_ptr(), libc::S_IFIFO | 0o644, 0);
1229         if ret != 0 {
1230             libc::perror(fifo_path.as_ptr());
1231             panic!("Failed to create a FIFO file");
1232         }
1233     }
1234 
1235     t!(env::set_current_dir(td.path()));
1236     let mut ar = Builder::new(Vec::new());
1237     // append_path has a different logic for processing files, so we need to test it as well
1238     t!(ar.append_path("fifo"));
1239     t!(ar.append_dir_all("special", td.path()));
1240     // unfortunately, block device file cannot be created by non-root users
1241     // as a substitute, just test the file that exists on most Unix systems
1242     t!(env::set_current_dir("/dev/"));
1243     t!(ar.append_path("loop0"));
1244     // CI systems seem to have issues with creating a chr device
1245     t!(ar.append_path("null"));
1246     t!(ar.finish());
1247 }
1248