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,
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!(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]
large_filename()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]
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!(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]
extracting_duplicate_file_fail()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]
extracting_duplicate_file_succeed()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)]
extracting_duplicate_link_fail()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)]
extracting_duplicate_link_succeed()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"))]
xattrs()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"))]
no_xattrs()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]
writing_and_extracting_directories()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]
writing_directories_recursively()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]
append_dir_all_blank_dest()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]
append_dir_all_does_not_work_on_non_directory()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]
extracting_duplicate_dirs()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]
unpack_old_style_bsd_dir()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]
handling_incorrect_file_size()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]
extracting_malicious_tarball()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]
octal_spaces()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]
extracting_malformed_tar_null_blocks()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]
empty_filename()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]
file_times()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]
zero_file_times()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]
backslash_treated_well()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]
nul_bytes_in_path()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]
links()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
unpack_links()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]
pax_size()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]
pax_simple()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]
pax_path()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]
pax_linkpath()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]
long_name_trailing_nul()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]
long_linkname_trailing_nul()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]
encoded_long_name_has_trailing_nul()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]
reading_sparse()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]
extract_sparse()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]
sparse_with_trailing()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]
path_separators()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)]
append_path_symlink()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]
name_with_slash_doesnt_fool_long_link_and_bsd_compat()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]
insert_local_file_different_name()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)]
tar_directory_containing_symlink_to_directory()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]
long_path()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]
unpack_path_larger_than_windows_max_path()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]
append_long_multibyte()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]
read_only_directory_containing_files()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")]
tar_directory_containing_special_files()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