diff options
Diffstat (limited to 'vendor/tar-0.4.38/tests')
-rw-r--r-- | vendor/tar-0.4.38/tests/all.rs | 1387 | ||||
-rw-r--r-- | vendor/tar-0.4.38/tests/entry.rs | 379 | ||||
-rw-r--r-- | vendor/tar-0.4.38/tests/header/mod.rs | 246 |
3 files changed, 2012 insertions, 0 deletions
diff --git a/vendor/tar-0.4.38/tests/all.rs b/vendor/tar-0.4.38/tests/all.rs new file mode 100644 index 000000000..11103bd6b --- /dev/null +++ b/vendor/tar-0.4.38/tests/all.rs @@ -0,0 +1,1387 @@ +extern crate filetime; +extern crate tar; +extern crate tempfile; +#[cfg(all(unix, feature = "xattr"))] +extern crate xattr; + +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io::{self, Cursor}; +use std::iter::repeat; +use std::path::{Path, PathBuf}; + +use filetime::FileTime; +use tar::{Archive, Builder, Entries, EntryType, Header, HeaderMode}; +use tempfile::{Builder as TempBuilder, TempDir}; + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(v) => v, + Err(e) => panic!("{} returned {}", stringify!($e), e), + } + }; +} + +macro_rules! tar { + ($e:expr) => { + &include_bytes!(concat!("archives/", $e))[..] + }; +} + +mod header; + +/// test that we can concatenate the simple.tar archive and extract the same entries twice when we +/// use the ignore_zeros option. +#[test] +fn simple_concat() { + let bytes = tar!("simple.tar"); + let mut archive_bytes = Vec::new(); + archive_bytes.extend(bytes); + + let original_names: Vec<String> = decode_names(&mut Archive::new(Cursor::new(&archive_bytes))); + let expected: Vec<&str> = original_names.iter().map(|n| n.as_str()).collect(); + + // concat two archives (with null in-between); + archive_bytes.extend(bytes); + + // test now that when we read the archive, it stops processing at the first zero header. + let actual = decode_names(&mut Archive::new(Cursor::new(&archive_bytes))); + assert_eq!(expected, actual); + + // extend expected by itself. + let expected: Vec<&str> = { + let mut o = Vec::new(); + o.extend(&expected); + o.extend(&expected); + o + }; + + let mut ar = Archive::new(Cursor::new(&archive_bytes)); + ar.set_ignore_zeros(true); + + let actual = decode_names(&mut ar); + assert_eq!(expected, actual); + + fn decode_names<R>(ar: &mut Archive<R>) -> Vec<String> + where + R: Read, + { + let mut names = Vec::new(); + + for entry in t!(ar.entries()) { + let e = t!(entry); + names.push(t!(::std::str::from_utf8(&e.path_bytes())).to_string()); + } + + names + } +} + +#[test] +fn header_impls() { + let mut ar = Archive::new(Cursor::new(tar!("simple.tar"))); + let hn = Header::new_old(); + let hnb = hn.as_bytes(); + for file in t!(ar.entries()) { + let file = t!(file); + let h1 = file.header(); + let h1b = h1.as_bytes(); + let h2 = h1.clone(); + let h2b = h2.as_bytes(); + assert!(h1b[..] == h2b[..] && h2b[..] != hnb[..]) + } +} + +#[test] +fn header_impls_missing_last_header() { + let mut ar = Archive::new(Cursor::new(tar!("simple_missing_last_header.tar"))); + let hn = Header::new_old(); + let hnb = hn.as_bytes(); + for file in t!(ar.entries()) { + let file = t!(file); + let h1 = file.header(); + let h1b = h1.as_bytes(); + let h2 = h1.clone(); + let h2b = h2.as_bytes(); + assert!(h1b[..] == h2b[..] && h2b[..] != hnb[..]) + } +} + +#[test] +fn reading_files() { + let rdr = Cursor::new(tar!("reading_files.tar")); + let mut ar = Archive::new(rdr); + let mut entries = t!(ar.entries()); + + let mut a = t!(entries.next().unwrap()); + assert_eq!(&*a.header().path_bytes(), b"a"); + let mut s = String::new(); + t!(a.read_to_string(&mut s)); + assert_eq!(s, "a\na\na\na\na\na\na\na\na\na\na\n"); + + let mut b = t!(entries.next().unwrap()); + assert_eq!(&*b.header().path_bytes(), b"b"); + s.truncate(0); + t!(b.read_to_string(&mut s)); + assert_eq!(s, "b\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb\n"); + + assert!(entries.next().is_none()); +} + +#[test] +fn writing_files() { + let mut ar = Builder::new(Vec::new()); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let path = td.path().join("test"); + t!(t!(File::create(&path)).write_all(b"test")); + + t!(ar.append_file("test2", &mut t!(File::open(&path)))); + + let data = t!(ar.into_inner()); + let mut ar = Archive::new(Cursor::new(data)); + let mut entries = t!(ar.entries()); + let mut f = t!(entries.next().unwrap()); + + assert_eq!(&*f.header().path_bytes(), b"test2"); + assert_eq!(f.header().size().unwrap(), 4); + let mut s = String::new(); + t!(f.read_to_string(&mut s)); + assert_eq!(s, "test"); + + assert!(entries.next().is_none()); +} + +#[test] +fn large_filename() { + let mut ar = Builder::new(Vec::new()); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let path = td.path().join("test"); + t!(t!(File::create(&path)).write_all(b"test")); + + let filename = repeat("abcd/").take(50).collect::<String>(); + let mut header = Header::new_ustar(); + header.set_path(&filename).unwrap(); + header.set_metadata(&t!(fs::metadata(&path))); + header.set_cksum(); + t!(ar.append(&header, &b"test"[..])); + let too_long = repeat("abcd").take(200).collect::<String>(); + t!(ar.append_file(&too_long, &mut t!(File::open(&path)))); + t!(ar.append_data(&mut header, &too_long, &b"test"[..])); + + let rd = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rd); + let mut entries = t!(ar.entries()); + + // The short entry added with `append` + let mut f = entries.next().unwrap().unwrap(); + assert_eq!(&*f.header().path_bytes(), filename.as_bytes()); + assert_eq!(f.header().size().unwrap(), 4); + let mut s = String::new(); + t!(f.read_to_string(&mut s)); + assert_eq!(s, "test"); + + // The long entry added with `append_file` + let mut f = entries.next().unwrap().unwrap(); + assert_eq!(&*f.path_bytes(), too_long.as_bytes()); + assert_eq!(f.header().size().unwrap(), 4); + let mut s = String::new(); + t!(f.read_to_string(&mut s)); + assert_eq!(s, "test"); + + // The long entry added with `append_data` + let mut f = entries.next().unwrap().unwrap(); + assert!(f.header().path_bytes().len() < too_long.len()); + assert_eq!(&*f.path_bytes(), too_long.as_bytes()); + assert_eq!(f.header().size().unwrap(), 4); + let mut s = String::new(); + t!(f.read_to_string(&mut s)); + assert_eq!(s, "test"); + + assert!(entries.next().is_none()); +} + +fn reading_entries_common<R: Read>(mut entries: Entries<R>) { + let mut a = t!(entries.next().unwrap()); + assert_eq!(&*a.header().path_bytes(), b"a"); + let mut s = String::new(); + t!(a.read_to_string(&mut s)); + assert_eq!(s, "a\na\na\na\na\na\na\na\na\na\na\n"); + s.truncate(0); + t!(a.read_to_string(&mut s)); + assert_eq!(s, ""); + + let mut b = t!(entries.next().unwrap()); + assert_eq!(&*b.header().path_bytes(), b"b"); + s.truncate(0); + t!(b.read_to_string(&mut s)); + assert_eq!(s, "b\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb\n"); + assert!(entries.next().is_none()); +} + +#[test] +fn reading_entries() { + let rdr = Cursor::new(tar!("reading_files.tar")); + let mut ar = Archive::new(rdr); + reading_entries_common(t!(ar.entries())); +} + +#[test] +fn reading_entries_with_seek() { + let rdr = Cursor::new(tar!("reading_files.tar")); + let mut ar = Archive::new(rdr); + reading_entries_common(t!(ar.entries_with_seek())); +} + +struct LoggingReader<R> { + inner: R, + read_bytes: u64, +} + +impl<R> LoggingReader<R> { + fn new(reader: R) -> LoggingReader<R> { + LoggingReader { + inner: reader, + read_bytes: 0, + } + } +} + +impl<T: Read> Read for LoggingReader<T> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.read(buf).map(|i| { + self.read_bytes += i as u64; + i + }) + } +} + +impl<T: Seek> Seek for LoggingReader<T> { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { + self.inner.seek(pos) + } +} + +#[test] +fn skipping_entries_with_seek() { + let mut reader = LoggingReader::new(Cursor::new(tar!("reading_files.tar"))); + let mut ar_reader = Archive::new(&mut reader); + let files: Vec<_> = t!(ar_reader.entries()) + .map(|entry| entry.unwrap().path().unwrap().to_path_buf()) + .collect(); + + let mut seekable_reader = LoggingReader::new(Cursor::new(tar!("reading_files.tar"))); + let mut ar_seekable_reader = Archive::new(&mut seekable_reader); + let files_seekable: Vec<_> = t!(ar_seekable_reader.entries_with_seek()) + .map(|entry| entry.unwrap().path().unwrap().to_path_buf()) + .collect(); + + assert!(files == files_seekable); + assert!(seekable_reader.read_bytes < reader.read_bytes); +} + +fn check_dirtree(td: &TempDir) { + let dir_a = td.path().join("a"); + let dir_b = td.path().join("a/b"); + let file_c = td.path().join("a/c"); + assert!(fs::metadata(&dir_a).map(|m| m.is_dir()).unwrap_or(false)); + assert!(fs::metadata(&dir_b).map(|m| m.is_dir()).unwrap_or(false)); + assert!(fs::metadata(&file_c).map(|m| m.is_file()).unwrap_or(false)); +} + +#[test] +fn extracting_directories() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let rdr = Cursor::new(tar!("directory.tar")); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + check_dirtree(&td); +} + +#[test] +fn extracting_duplicate_file_fail() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path_present = td.path().join("a"); + t!(File::create(path_present)); + + let rdr = Cursor::new(tar!("reading_files.tar")); + let mut ar = Archive::new(rdr); + ar.set_overwrite(false); + if let Err(err) = ar.unpack(td.path()) { + if err.kind() == std::io::ErrorKind::AlreadyExists { + // as expected with overwrite false + return; + } + panic!("unexpected error: {:?}", err); + } + panic!( + "unpack() should have returned an error of kind {:?}, returned Ok", + std::io::ErrorKind::AlreadyExists + ) +} + +#[test] +fn extracting_duplicate_file_succeed() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path_present = td.path().join("a"); + t!(File::create(path_present)); + + let rdr = Cursor::new(tar!("reading_files.tar")); + let mut ar = Archive::new(rdr); + ar.set_overwrite(true); + t!(ar.unpack(td.path())); +} + +#[test] +#[cfg(unix)] +fn extracting_duplicate_link_fail() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path_present = td.path().join("lnk"); + t!(std::os::unix::fs::symlink("file", path_present)); + + let rdr = Cursor::new(tar!("link.tar")); + let mut ar = Archive::new(rdr); + ar.set_overwrite(false); + if let Err(err) = ar.unpack(td.path()) { + if err.kind() == std::io::ErrorKind::AlreadyExists { + // as expected with overwrite false + return; + } + panic!("unexpected error: {:?}", err); + } + panic!( + "unpack() should have returned an error of kind {:?}, returned Ok", + std::io::ErrorKind::AlreadyExists + ) +} + +#[test] +#[cfg(unix)] +fn extracting_duplicate_link_succeed() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path_present = td.path().join("lnk"); + t!(std::os::unix::fs::symlink("file", path_present)); + + let rdr = Cursor::new(tar!("link.tar")); + let mut ar = Archive::new(rdr); + ar.set_overwrite(true); + t!(ar.unpack(td.path())); +} + +#[test] +#[cfg(all(unix, feature = "xattr"))] +fn xattrs() { + // If /tmp is a tmpfs, xattr will fail + // The xattr crate's unit tests also use /var/tmp for this reason + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir_in("/var/tmp")); + let rdr = Cursor::new(tar!("xattrs.tar")); + let mut ar = Archive::new(rdr); + ar.set_unpack_xattrs(true); + t!(ar.unpack(td.path())); + + let val = xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap(); + assert_eq!(val.unwrap(), "epm".as_bytes()); +} + +#[test] +#[cfg(all(unix, feature = "xattr"))] +fn no_xattrs() { + // If /tmp is a tmpfs, xattr will fail + // The xattr crate's unit tests also use /var/tmp for this reason + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir_in("/var/tmp")); + let rdr = Cursor::new(tar!("xattrs.tar")); + let mut ar = Archive::new(rdr); + ar.set_unpack_xattrs(false); + t!(ar.unpack(td.path())); + + assert_eq!( + xattr::get(td.path().join("a/b"), "user.pax.flags").unwrap(), + None + ); +} + +#[test] +fn writing_and_extracting_directories() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + let tmppath = td.path().join("tmpfile"); + t!(t!(File::create(&tmppath)).write_all(b"c")); + t!(ar.append_dir("a", ".")); + t!(ar.append_dir("a/b", ".")); + t!(ar.append_file("a/c", &mut t!(File::open(&tmppath)))); + t!(ar.finish()); + + let rdr = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + check_dirtree(&td); +} + +#[test] +fn writing_directories_recursively() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let base_dir = td.path().join("base"); + t!(fs::create_dir(&base_dir)); + t!(t!(File::create(base_dir.join("file1"))).write_all(b"file1")); + let sub_dir = base_dir.join("sub"); + t!(fs::create_dir(&sub_dir)); + t!(t!(File::create(sub_dir.join("file2"))).write_all(b"file2")); + + let mut ar = Builder::new(Vec::new()); + t!(ar.append_dir_all("foobar", base_dir)); + let data = t!(ar.into_inner()); + + let mut ar = Archive::new(Cursor::new(data)); + t!(ar.unpack(td.path())); + let base_dir = td.path().join("foobar"); + assert!(fs::metadata(&base_dir).map(|m| m.is_dir()).unwrap_or(false)); + let file1_path = base_dir.join("file1"); + assert!(fs::metadata(&file1_path) + .map(|m| m.is_file()) + .unwrap_or(false)); + let sub_dir = base_dir.join("sub"); + assert!(fs::metadata(&sub_dir).map(|m| m.is_dir()).unwrap_or(false)); + let file2_path = sub_dir.join("file2"); + assert!(fs::metadata(&file2_path) + .map(|m| m.is_file()) + .unwrap_or(false)); +} + +#[test] +fn append_dir_all_blank_dest() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let base_dir = td.path().join("base"); + t!(fs::create_dir(&base_dir)); + t!(t!(File::create(base_dir.join("file1"))).write_all(b"file1")); + let sub_dir = base_dir.join("sub"); + t!(fs::create_dir(&sub_dir)); + t!(t!(File::create(sub_dir.join("file2"))).write_all(b"file2")); + + let mut ar = Builder::new(Vec::new()); + t!(ar.append_dir_all("", base_dir)); + let data = t!(ar.into_inner()); + + let mut ar = Archive::new(Cursor::new(data)); + t!(ar.unpack(td.path())); + let base_dir = td.path(); + assert!(fs::metadata(&base_dir).map(|m| m.is_dir()).unwrap_or(false)); + let file1_path = base_dir.join("file1"); + assert!(fs::metadata(&file1_path) + .map(|m| m.is_file()) + .unwrap_or(false)); + let sub_dir = base_dir.join("sub"); + assert!(fs::metadata(&sub_dir).map(|m| m.is_dir()).unwrap_or(false)); + let file2_path = sub_dir.join("file2"); + assert!(fs::metadata(&file2_path) + .map(|m| m.is_file()) + .unwrap_or(false)); +} + +#[test] +fn append_dir_all_does_not_work_on_non_directory() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path = td.path().join("test"); + t!(t!(File::create(&path)).write_all(b"test")); + + let mut ar = Builder::new(Vec::new()); + let result = ar.append_dir_all("test", path); + assert!(result.is_err()); +} + +#[test] +fn extracting_duplicate_dirs() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let rdr = Cursor::new(tar!("duplicate_dirs.tar")); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + + let some_dir = td.path().join("some_dir"); + assert!(fs::metadata(&some_dir).map(|m| m.is_dir()).unwrap_or(false)); +} + +#[test] +fn unpack_old_style_bsd_dir() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + + let mut header = Header::new_old(); + header.set_entry_type(EntryType::Regular); + t!(header.set_path("testdir/")); + header.set_size(0); + header.set_cksum(); + t!(ar.append(&header, &mut io::empty())); + + // Extracting + let rdr = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + + // Iterating + let rdr = Cursor::new(ar.into_inner().into_inner()); + let mut ar = Archive::new(rdr); + assert!(t!(ar.entries()).all(|fr| fr.is_ok())); + + assert!(td.path().join("testdir").is_dir()); +} + +#[test] +fn handling_incorrect_file_size() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + + let path = td.path().join("tmpfile"); + t!(File::create(&path)); + let mut file = t!(File::open(&path)); + let mut header = Header::new_old(); + t!(header.set_path("somepath")); + header.set_metadata(&t!(file.metadata())); + header.set_size(2048); // past the end of file null blocks + header.set_cksum(); + t!(ar.append(&header, &mut file)); + + // Extracting + let rdr = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rdr); + assert!(ar.unpack(td.path()).is_err()); + + // Iterating + let rdr = Cursor::new(ar.into_inner().into_inner()); + let mut ar = Archive::new(rdr); + assert!(t!(ar.entries()).any(|fr| fr.is_err())); +} + +#[test] +fn extracting_malicious_tarball() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut evil_tar = Vec::new(); + + { + let mut a = Builder::new(&mut evil_tar); + let mut append = |path: &str| { + let mut header = Header::new_gnu(); + assert!(header.set_path(path).is_err(), "was ok: {:?}", path); + { + let h = header.as_gnu_mut().unwrap(); + for (a, b) in h.name.iter_mut().zip(path.as_bytes()) { + *a = *b; + } + } + header.set_size(1); + header.set_cksum(); + t!(a.append(&header, io::repeat(1).take(1))); + }; + append("/tmp/abs_evil.txt"); + append("//tmp/abs_evil2.txt"); + append("///tmp/abs_evil3.txt"); + append("/./tmp/abs_evil4.txt"); + append("//./tmp/abs_evil5.txt"); + append("///./tmp/abs_evil6.txt"); + append("/../tmp/rel_evil.txt"); + append("../rel_evil2.txt"); + append("./../rel_evil3.txt"); + append("some/../../rel_evil4.txt"); + append(""); + append("././//./.."); + append(".."); + append("/////////.."); + append("/////////"); + } + + let mut ar = Archive::new(&evil_tar[..]); + t!(ar.unpack(td.path())); + + assert!(fs::metadata("/tmp/abs_evil.txt").is_err()); + assert!(fs::metadata("/tmp/abs_evil.txt2").is_err()); + assert!(fs::metadata("/tmp/abs_evil.txt3").is_err()); + assert!(fs::metadata("/tmp/abs_evil.txt4").is_err()); + assert!(fs::metadata("/tmp/abs_evil.txt5").is_err()); + assert!(fs::metadata("/tmp/abs_evil.txt6").is_err()); + assert!(fs::metadata("/tmp/rel_evil.txt").is_err()); + assert!(fs::metadata("/tmp/rel_evil.txt").is_err()); + assert!(fs::metadata(td.path().join("../tmp/rel_evil.txt")).is_err()); + assert!(fs::metadata(td.path().join("../rel_evil2.txt")).is_err()); + assert!(fs::metadata(td.path().join("../rel_evil3.txt")).is_err()); + assert!(fs::metadata(td.path().join("../rel_evil4.txt")).is_err()); + + // The `some` subdirectory should not be created because the only + // filename that references this has '..'. + assert!(fs::metadata(td.path().join("some")).is_err()); + + // The `tmp` subdirectory should be created and within this + // subdirectory, there should be files named `abs_evil.txt` through + // `abs_evil6.txt`. + assert!(fs::metadata(td.path().join("tmp")) + .map(|m| m.is_dir()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil2.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil3.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil4.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil5.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); + assert!(fs::metadata(td.path().join("tmp/abs_evil6.txt")) + .map(|m| m.is_file()) + .unwrap_or(false)); +} + +#[test] +fn octal_spaces() { + let rdr = Cursor::new(tar!("spaces.tar")); + let mut ar = Archive::new(rdr); + + let entry = ar.entries().unwrap().next().unwrap().unwrap(); + assert_eq!(entry.header().mode().unwrap() & 0o777, 0o777); + assert_eq!(entry.header().uid().unwrap(), 0); + assert_eq!(entry.header().gid().unwrap(), 0); + assert_eq!(entry.header().size().unwrap(), 2); + assert_eq!(entry.header().mtime().unwrap(), 0o12440016664); + assert_eq!(entry.header().cksum().unwrap(), 0o4253); +} + +#[test] +fn extracting_malformed_tar_null_blocks() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + + let path1 = td.path().join("tmpfile1"); + let path2 = td.path().join("tmpfile2"); + t!(File::create(&path1)); + t!(File::create(&path2)); + t!(ar.append_file("tmpfile1", &mut t!(File::open(&path1)))); + let mut data = t!(ar.into_inner()); + let amt = data.len(); + data.truncate(amt - 512); + let mut ar = Builder::new(data); + t!(ar.append_file("tmpfile2", &mut t!(File::open(&path2)))); + t!(ar.finish()); + + let data = t!(ar.into_inner()); + let mut ar = Archive::new(&data[..]); + assert!(ar.unpack(td.path()).is_ok()); +} + +#[test] +fn empty_filename() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let rdr = Cursor::new(tar!("empty_filename.tar")); + let mut ar = Archive::new(rdr); + assert!(ar.unpack(td.path()).is_ok()); +} + +#[test] +fn file_times() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let rdr = Cursor::new(tar!("file_times.tar")); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + + let meta = fs::metadata(td.path().join("a")).unwrap(); + let mtime = FileTime::from_last_modification_time(&meta); + let atime = FileTime::from_last_access_time(&meta); + assert_eq!(mtime.unix_seconds(), 1000000000); + assert_eq!(mtime.nanoseconds(), 0); + assert_eq!(atime.unix_seconds(), 1000000000); + assert_eq!(atime.nanoseconds(), 0); +} + +#[test] +fn zero_file_times() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + ar.mode(HeaderMode::Deterministic); + let path = td.path().join("tmpfile"); + t!(File::create(&path)); + t!(ar.append_path_with_name(&path, "a")); + + let data = t!(ar.into_inner()); + let mut ar = Archive::new(&data[..]); + assert!(ar.unpack(td.path()).is_ok()); + + let meta = fs::metadata(td.path().join("a")).unwrap(); + let mtime = FileTime::from_last_modification_time(&meta); + let atime = FileTime::from_last_access_time(&meta); + assert!(mtime.unix_seconds() != 0); + assert!(atime.unix_seconds() != 0); +} + +#[test] +fn backslash_treated_well() { + // Insert a file into an archive with a backslash + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let mut ar = Builder::new(Vec::<u8>::new()); + t!(ar.append_dir("foo\\bar", td.path())); + let mut ar = Archive::new(Cursor::new(t!(ar.into_inner()))); + let f = t!(t!(ar.entries()).next().unwrap()); + if cfg!(unix) { + assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar")); + } else { + assert_eq!(t!(f.header().path()).to_str(), Some("foo/bar")); + } + + // Unpack an archive with a backslash in the name + let mut ar = Builder::new(Vec::<u8>::new()); + let mut header = Header::new_gnu(); + header.set_metadata(&t!(fs::metadata(td.path()))); + header.set_size(0); + for (a, b) in header.as_old_mut().name.iter_mut().zip(b"foo\\bar\x00") { + *a = *b; + } + header.set_cksum(); + t!(ar.append(&header, &mut io::empty())); + let data = t!(ar.into_inner()); + let mut ar = Archive::new(&data[..]); + let f = t!(t!(ar.entries()).next().unwrap()); + assert_eq!(t!(f.header().path()).to_str(), Some("foo\\bar")); + + let mut ar = Archive::new(&data[..]); + t!(ar.unpack(td.path())); + assert!(fs::metadata(td.path().join("foo\\bar")).is_ok()); +} + +#[cfg(unix)] +#[test] +fn nul_bytes_in_path() { + use std::ffi::OsStr; + use std::os::unix::prelude::*; + + let nul_path = OsStr::from_bytes(b"foo\0"); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let mut ar = Builder::new(Vec::<u8>::new()); + let err = ar.append_dir(nul_path, td.path()).unwrap_err(); + assert!(err.to_string().contains("contains a nul byte")); +} + +#[test] +fn links() { + let mut ar = Archive::new(Cursor::new(tar!("link.tar"))); + let mut entries = t!(ar.entries()); + let link = t!(entries.next().unwrap()); + assert_eq!( + t!(link.header().link_name()).as_ref().map(|p| &**p), + Some(Path::new("file")) + ); + let other = t!(entries.next().unwrap()); + assert!(t!(other.header().link_name()).is_none()); +} + +#[test] +#[cfg(unix)] // making symlinks on windows is hard +fn unpack_links() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let mut ar = Archive::new(Cursor::new(tar!("link.tar"))); + t!(ar.unpack(td.path())); + + let md = t!(fs::symlink_metadata(td.path().join("lnk"))); + assert!(md.file_type().is_symlink()); + assert_eq!( + &*t!(fs::read_link(td.path().join("lnk"))), + Path::new("file") + ); + t!(File::open(td.path().join("lnk"))); +} + +#[test] +fn pax_size() { + let mut ar = Archive::new(tar!("pax_size.tar")); + let mut entries = t!(ar.entries()); + let mut entry = t!(entries.next().unwrap()); + let mut attributes = t!(entry.pax_extensions()).unwrap(); + + let _first = t!(attributes.next().unwrap()); + let _second = t!(attributes.next().unwrap()); + let _third = t!(attributes.next().unwrap()); + let fourth = t!(attributes.next().unwrap()); + assert!(attributes.next().is_none()); + + assert_eq!(fourth.key(), Ok("size")); + assert_eq!(fourth.value(), Ok("4")); + + assert_eq!(entry.header().size().unwrap(), 0); + assert_eq!(entry.size(), 4); +} + +#[test] +fn pax_simple() { + let mut ar = Archive::new(tar!("pax.tar")); + let mut entries = t!(ar.entries()); + + let mut first = t!(entries.next().unwrap()); + let mut attributes = t!(first.pax_extensions()).unwrap(); + let first = t!(attributes.next().unwrap()); + let second = t!(attributes.next().unwrap()); + let third = t!(attributes.next().unwrap()); + assert!(attributes.next().is_none()); + + assert_eq!(first.key(), Ok("mtime")); + assert_eq!(first.value(), Ok("1453146164.953123768")); + assert_eq!(second.key(), Ok("atime")); + assert_eq!(second.value(), Ok("1453251915.24892486")); + assert_eq!(third.key(), Ok("ctime")); + assert_eq!(third.value(), Ok("1453146164.953123768")); +} + +#[test] +fn pax_path() { + let mut ar = Archive::new(tar!("pax2.tar")); + let mut entries = t!(ar.entries()); + + let first = t!(entries.next().unwrap()); + assert!(first.path().unwrap().ends_with("aaaaaaaaaaaaaaa")); +} + +#[test] +fn pax_linkpath() { + let mut ar = Archive::new(tar!("pax2.tar")); + let mut links = t!(ar.entries()).skip(3).take(2); + + let long_symlink = t!(links.next().unwrap()); + let link_name = long_symlink.link_name().unwrap().unwrap(); + assert!(link_name.to_str().unwrap().len() > 99); + assert!(link_name.ends_with("bbbbbbbbbbbbbbb")); + + let long_hardlink = t!(links.next().unwrap()); + let link_name = long_hardlink.link_name().unwrap().unwrap(); + assert!(link_name.to_str().unwrap().len() > 99); + assert!(link_name.ends_with("ccccccccccccccc")); +} + +#[test] +fn long_name_trailing_nul() { + let mut b = Builder::new(Vec::<u8>::new()); + + let mut h = Header::new_gnu(); + t!(h.set_path("././@LongLink")); + h.set_size(4); + h.set_entry_type(EntryType::new(b'L')); + h.set_cksum(); + t!(b.append(&h, "foo\0".as_bytes())); + let mut h = Header::new_gnu(); + + t!(h.set_path("bar")); + h.set_size(6); + h.set_entry_type(EntryType::file()); + h.set_cksum(); + t!(b.append(&h, "foobar".as_bytes())); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let e = t!(t!(a.entries()).next().unwrap()); + assert_eq!(&*e.path_bytes(), b"foo"); +} + +#[test] +fn long_linkname_trailing_nul() { + let mut b = Builder::new(Vec::<u8>::new()); + + let mut h = Header::new_gnu(); + t!(h.set_path("././@LongLink")); + h.set_size(4); + h.set_entry_type(EntryType::new(b'K')); + h.set_cksum(); + t!(b.append(&h, "foo\0".as_bytes())); + let mut h = Header::new_gnu(); + + t!(h.set_path("bar")); + h.set_size(6); + h.set_entry_type(EntryType::file()); + h.set_cksum(); + t!(b.append(&h, "foobar".as_bytes())); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let e = t!(t!(a.entries()).next().unwrap()); + assert_eq!(&*e.link_name_bytes().unwrap(), b"foo"); +} + +#[test] +fn long_linkname_gnu() { + for t in [tar::EntryType::Symlink, tar::EntryType::Link] { + let mut b = Builder::new(Vec::<u8>::new()); + let mut h = Header::new_gnu(); + h.set_entry_type(t); + h.set_size(0); + let path = "usr/lib/.build-id/05/159ed904e45ff5100f7acd3d3b99fa7e27e34f"; + let target = "../../../../usr/lib64/qt5/plugins/wayland-graphics-integration-server/libqt-wayland-compositor-xcomposite-egl.so"; + t!(b.append_link(&mut h, path, target)); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let e = &t!(t!(a.entries()).next().unwrap()); + assert_eq!(e.header().entry_type(), t); + assert_eq!(e.path().unwrap().to_str().unwrap(), path); + assert_eq!(e.link_name().unwrap().unwrap().to_str().unwrap(), target); + } +} + +#[test] +fn linkname_literal() { + for t in [tar::EntryType::Symlink, tar::EntryType::Link] { + let mut b = Builder::new(Vec::<u8>::new()); + let mut h = Header::new_gnu(); + h.set_entry_type(t); + h.set_size(0); + let path = "usr/lib/systemd/systemd-sysv-install"; + let target = "../../..//sbin/chkconfig"; + h.set_link_name_literal(target).unwrap(); + t!(b.append_data(&mut h, path, std::io::empty())); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let e = &t!(t!(a.entries()).next().unwrap()); + assert_eq!(e.header().entry_type(), t); + assert_eq!(e.path().unwrap().to_str().unwrap(), path); + assert_eq!(e.link_name().unwrap().unwrap().to_str().unwrap(), target); + } +} + +#[test] +fn encoded_long_name_has_trailing_nul() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path = td.path().join("foo"); + t!(t!(File::create(&path)).write_all(b"test")); + + let mut b = Builder::new(Vec::<u8>::new()); + let long = repeat("abcd").take(200).collect::<String>(); + + t!(b.append_file(&long, &mut t!(File::open(&path)))); + + let contents = t!(b.into_inner()); + let mut a = Archive::new(&contents[..]); + + let mut e = t!(t!(a.entries()).raw(true).next().unwrap()); + let mut name = Vec::new(); + t!(e.read_to_end(&mut name)); + assert_eq!(name[name.len() - 1], 0); + + let header_name = &e.header().as_gnu().unwrap().name; + assert!(header_name.starts_with(b"././@LongLink\x00")); +} + +#[test] +fn reading_sparse() { + let rdr = Cursor::new(tar!("sparse.tar")); + let mut ar = Archive::new(rdr); + let mut entries = t!(ar.entries()); + + let mut a = t!(entries.next().unwrap()); + let mut s = String::new(); + assert_eq!(&*a.header().path_bytes(), b"sparse_begin.txt"); + t!(a.read_to_string(&mut s)); + assert_eq!(&s[..5], "test\n"); + assert!(s[5..].chars().all(|x| x == '\u{0}')); + + let mut a = t!(entries.next().unwrap()); + let mut s = String::new(); + assert_eq!(&*a.header().path_bytes(), b"sparse_end.txt"); + t!(a.read_to_string(&mut s)); + assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[s.len() - 9..], "test_end\n"); + + let mut a = t!(entries.next().unwrap()); + let mut s = String::new(); + assert_eq!(&*a.header().path_bytes(), b"sparse_ext.txt"); + t!(a.read_to_string(&mut s)); + assert!(s[..0x1000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x1000..0x1000 + 5], "text\n"); + assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x3000..0x3000 + 5], "text\n"); + assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x5000..0x5000 + 5], "text\n"); + assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x7000..0x7000 + 5], "text\n"); + assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x9000..0x9000 + 5], "text\n"); + assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0xb000..0xb000 + 5], "text\n"); + + let mut a = t!(entries.next().unwrap()); + let mut s = String::new(); + assert_eq!(&*a.header().path_bytes(), b"sparse.txt"); + t!(a.read_to_string(&mut s)); + assert!(s[..0x1000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x1000..0x1000 + 6], "hello\n"); + assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n"); + assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}')); + + assert!(entries.next().is_none()); +} + +#[test] +fn extract_sparse() { + let rdr = Cursor::new(tar!("sparse.tar")); + let mut ar = Archive::new(rdr); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + t!(ar.unpack(td.path())); + + let mut s = String::new(); + t!(t!(File::open(td.path().join("sparse_begin.txt"))).read_to_string(&mut s)); + assert_eq!(&s[..5], "test\n"); + assert!(s[5..].chars().all(|x| x == '\u{0}')); + + s.truncate(0); + t!(t!(File::open(td.path().join("sparse_end.txt"))).read_to_string(&mut s)); + assert!(s[..s.len() - 9].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[s.len() - 9..], "test_end\n"); + + s.truncate(0); + t!(t!(File::open(td.path().join("sparse_ext.txt"))).read_to_string(&mut s)); + assert!(s[..0x1000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x1000..0x1000 + 5], "text\n"); + assert!(s[0x1000 + 5..0x3000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x3000..0x3000 + 5], "text\n"); + assert!(s[0x3000 + 5..0x5000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x5000..0x5000 + 5], "text\n"); + assert!(s[0x5000 + 5..0x7000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x7000..0x7000 + 5], "text\n"); + assert!(s[0x7000 + 5..0x9000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x9000..0x9000 + 5], "text\n"); + assert!(s[0x9000 + 5..0xb000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0xb000..0xb000 + 5], "text\n"); + + s.truncate(0); + t!(t!(File::open(td.path().join("sparse.txt"))).read_to_string(&mut s)); + assert!(s[..0x1000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x1000..0x1000 + 6], "hello\n"); + assert!(s[0x1000 + 6..0x2fa0].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x2fa0..0x2fa0 + 6], "world\n"); + assert!(s[0x2fa0 + 6..0x4000].chars().all(|x| x == '\u{0}')); +} + +#[test] +fn sparse_with_trailing() { + let rdr = Cursor::new(tar!("sparse-1.tar")); + let mut ar = Archive::new(rdr); + let mut entries = t!(ar.entries()); + let mut a = t!(entries.next().unwrap()); + let mut s = String::new(); + t!(a.read_to_string(&mut s)); + assert_eq!(0x100_00c, s.len()); + assert_eq!(&s[..0xc], "0MB through\n"); + assert!(s[0xc..0x100_000].chars().all(|x| x == '\u{0}')); + assert_eq!(&s[0x100_000..], "1MB through\n"); +} + +#[test] +fn path_separators() { + let mut ar = Builder::new(Vec::new()); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let path = td.path().join("test"); + t!(t!(File::create(&path)).write_all(b"test")); + + let short_path: PathBuf = repeat("abcd").take(2).collect(); + let long_path: PathBuf = repeat("abcd").take(50).collect(); + + // Make sure UStar headers normalize to Unix path separators + let mut header = Header::new_ustar(); + + t!(header.set_path(&short_path)); + assert_eq!(t!(header.path()), short_path); + assert!(!header.path_bytes().contains(&b'\\')); + + t!(header.set_path(&long_path)); + assert_eq!(t!(header.path()), long_path); + assert!(!header.path_bytes().contains(&b'\\')); + + // Make sure GNU headers normalize to Unix path separators, + // including the `@LongLink` fallback used by `append_file`. + t!(ar.append_file(&short_path, &mut t!(File::open(&path)))); + t!(ar.append_file(&long_path, &mut t!(File::open(&path)))); + + let rd = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rd); + let mut entries = t!(ar.entries()); + + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), short_path); + assert!(!entry.path_bytes().contains(&b'\\')); + + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), long_path); + assert!(!entry.path_bytes().contains(&b'\\')); + + assert!(entries.next().is_none()); +} + +#[test] +#[cfg(unix)] +fn append_path_symlink() { + use std::borrow::Cow; + use std::env; + use std::os::unix::fs::symlink; + + let mut ar = Builder::new(Vec::new()); + ar.follow_symlinks(false); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let long_linkname = repeat("abcd").take(30).collect::<String>(); + let long_pathname = repeat("dcba").take(30).collect::<String>(); + t!(env::set_current_dir(td.path())); + // "short" path name / short link name + t!(symlink("testdest", "test")); + t!(ar.append_path("test")); + // short path name / long link name + t!(symlink(&long_linkname, "test2")); + t!(ar.append_path("test2")); + // long path name / long link name + t!(symlink(&long_linkname, &long_pathname)); + t!(ar.append_path(&long_pathname)); + + let rd = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rd); + let mut entries = t!(ar.entries()); + + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), Path::new("test")); + assert_eq!( + t!(entry.link_name()), + Some(Cow::from(Path::new("testdest"))) + ); + assert_eq!(t!(entry.header().size()), 0); + + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), Path::new("test2")); + assert_eq!( + t!(entry.link_name()), + Some(Cow::from(Path::new(&long_linkname))) + ); + assert_eq!(t!(entry.header().size()), 0); + + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), Path::new(&long_pathname)); + assert_eq!( + t!(entry.link_name()), + Some(Cow::from(Path::new(&long_linkname))) + ); + assert_eq!(t!(entry.header().size()), 0); + + assert!(entries.next().is_none()); +} + +#[test] +fn name_with_slash_doesnt_fool_long_link_and_bsd_compat() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut ar = Builder::new(Vec::new()); + + let mut h = Header::new_gnu(); + t!(h.set_path("././@LongLink")); + h.set_size(4); + h.set_entry_type(EntryType::new(b'L')); + h.set_cksum(); + t!(ar.append(&h, "foo\0".as_bytes())); + + let mut header = Header::new_gnu(); + header.set_entry_type(EntryType::Regular); + t!(header.set_path("testdir/")); + header.set_size(0); + header.set_cksum(); + t!(ar.append(&header, &mut io::empty())); + + // Extracting + let rdr = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rdr); + t!(ar.unpack(td.path())); + + // Iterating + let rdr = Cursor::new(ar.into_inner().into_inner()); + let mut ar = Archive::new(rdr); + assert!(t!(ar.entries()).all(|fr| fr.is_ok())); + + assert!(td.path().join("foo").is_file()); +} + +#[test] +fn insert_local_file_different_name() { + let mut ar = Builder::new(Vec::new()); + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let path = td.path().join("directory"); + t!(fs::create_dir(&path)); + ar.append_path_with_name(&path, "archive/dir").unwrap(); + let path = td.path().join("file"); + t!(t!(File::create(&path)).write_all(b"test")); + ar.append_path_with_name(&path, "archive/dir/f").unwrap(); + + let rd = Cursor::new(t!(ar.into_inner())); + let mut ar = Archive::new(rd); + let mut entries = t!(ar.entries()); + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), Path::new("archive/dir")); + let entry = t!(entries.next().unwrap()); + assert_eq!(t!(entry.path()), Path::new("archive/dir/f")); + assert!(entries.next().is_none()); +} + +#[test] +#[cfg(unix)] +fn tar_directory_containing_symlink_to_directory() { + use std::os::unix::fs::symlink; + + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let dummy_src = t!(TempBuilder::new().prefix("dummy_src").tempdir()); + let dummy_dst = td.path().join("dummy_dst"); + let mut ar = Builder::new(Vec::new()); + t!(symlink(dummy_src.path().display().to_string(), &dummy_dst)); + + assert!(dummy_dst.read_link().is_ok()); + assert!(dummy_dst.read_link().unwrap().is_dir()); + ar.append_dir_all("symlinks", td.path()).unwrap(); + ar.finish().unwrap(); +} + +#[test] +fn long_path() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let rdr = Cursor::new(tar!("7z_long_path.tar")); + let mut ar = Archive::new(rdr); + assert!(ar.unpack(td.path()).is_ok()); +} + +#[test] +fn unpack_path_larger_than_windows_max_path() { + let dir_name = "iamaprettylongnameandtobepreciseiam91characterslongwhichsomethinkisreallylongandothersdonot"; + // 183 character directory name + let really_long_path = format!("{}{}", dir_name, dir_name); + let td = t!(TempBuilder::new().prefix(&really_long_path).tempdir()); + // directory in 7z_long_path.tar is over 100 chars + let rdr = Cursor::new(tar!("7z_long_path.tar")); + let mut ar = Archive::new(rdr); + // should unpack path greater than windows MAX_PATH length of 260 characters + assert!(ar.unpack(td.path()).is_ok()); +} + +#[test] +fn append_long_multibyte() { + let mut x = tar::Builder::new(Vec::new()); + let mut name = String::new(); + let data: &[u8] = &[]; + for _ in 0..512 { + name.push('a'); + name.push('ð‘¢®'); + x.append_data(&mut Header::new_gnu(), &name, data).unwrap(); + name.pop(); + } +} + +#[test] +fn read_only_directory_containing_files() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + + let mut b = Builder::new(Vec::<u8>::new()); + + let mut h = Header::new_gnu(); + t!(h.set_path("dir/")); + h.set_size(0); + h.set_entry_type(EntryType::dir()); + h.set_mode(0o444); + h.set_cksum(); + t!(b.append(&h, "".as_bytes())); + + let mut h = Header::new_gnu(); + t!(h.set_path("dir/file")); + h.set_size(2); + h.set_entry_type(EntryType::file()); + h.set_cksum(); + t!(b.append(&h, "hi".as_bytes())); + + let contents = t!(b.into_inner()); + let mut ar = Archive::new(&contents[..]); + assert!(ar.unpack(td.path()).is_ok()); +} + +// This test was marked linux only due to macOS CI can't handle `set_current_dir` correctly +#[test] +#[cfg(target_os = "linux")] +fn tar_directory_containing_special_files() { + use std::env; + use std::ffi::CString; + + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let fifo = td.path().join("fifo"); + + unsafe { + let fifo_path = t!(CString::new(fifo.to_str().unwrap())); + let ret = libc::mknod(fifo_path.as_ptr(), libc::S_IFIFO | 0o644, 0); + if ret != 0 { + libc::perror(fifo_path.as_ptr()); + panic!("Failed to create a FIFO file"); + } + } + + t!(env::set_current_dir(td.path())); + let mut ar = Builder::new(Vec::new()); + // append_path has a different logic for processing files, so we need to test it as well + t!(ar.append_path("fifo")); + t!(ar.append_dir_all("special", td.path())); + // unfortunately, block device file cannot be created by non-root users + // as a substitute, just test the file that exists on most Unix systems + t!(env::set_current_dir("/dev/")); + t!(ar.append_path("loop0")); + // CI systems seem to have issues with creating a chr device + t!(ar.append_path("null")); + t!(ar.finish()); +} + +#[test] +fn header_size_overflow() { + // maximal file size doesn't overflow anything + let mut ar = Builder::new(Vec::new()); + let mut header = Header::new_gnu(); + header.set_size(u64::MAX); + header.set_cksum(); + ar.append(&mut header, "x".as_bytes()).unwrap(); + let result = t!(ar.into_inner()); + let mut ar = Archive::new(&result[..]); + let mut e = ar.entries().unwrap(); + let err = e.next().unwrap().err().unwrap(); + assert!( + err.to_string().contains("size overflow"), + "bad error: {}", + err + ); + + // back-to-back entries that would overflow also don't panic + let mut ar = Builder::new(Vec::new()); + let mut header = Header::new_gnu(); + header.set_size(1_000); + header.set_cksum(); + ar.append(&mut header, &[0u8; 1_000][..]).unwrap(); + let mut header = Header::new_gnu(); + header.set_size(u64::MAX - 513); + header.set_cksum(); + ar.append(&mut header, "x".as_bytes()).unwrap(); + let result = t!(ar.into_inner()); + let mut ar = Archive::new(&result[..]); + let mut e = ar.entries().unwrap(); + e.next().unwrap().unwrap(); + let err = e.next().unwrap().err().unwrap(); + assert!( + err.to_string().contains("size overflow"), + "bad error: {}", + err + ); +} diff --git a/vendor/tar-0.4.38/tests/entry.rs b/vendor/tar-0.4.38/tests/entry.rs new file mode 100644 index 000000000..fa8eeaee7 --- /dev/null +++ b/vendor/tar-0.4.38/tests/entry.rs @@ -0,0 +1,379 @@ +extern crate tar; +extern crate tempfile; + +use std::fs::{create_dir, File}; +use std::io::Read; + +use tempfile::Builder; + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(v) => v, + Err(e) => panic!("{} returned {}", stringify!($e), e), + } + }; +} + +#[test] +fn absolute_symlink() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + t!(ar.unpack(td.path())); + + t!(td.path().join("foo").symlink_metadata()); + + let mut ar = tar::Archive::new(&bytes[..]); + let mut entries = t!(ar.entries()); + let entry = t!(entries.next().unwrap()); + assert_eq!(&*entry.link_name_bytes().unwrap(), b"/bar"); +} + +#[test] +fn absolute_hardlink() { + let td = t!(Builder::new().prefix("tar").tempdir()); + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Link); + t!(header.set_path("bar")); + // This absolute path under tempdir will be created at unpack time + t!(header.set_link_name(td.path().join("foo"))); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + t!(ar.unpack(td.path())); + t!(td.path().join("foo").metadata()); + t!(td.path().join("bar").metadata()); +} + +#[test] +fn relative_hardlink() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Link); + t!(header.set_path("bar")); + t!(header.set_link_name("foo")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + t!(ar.unpack(td.path())); + t!(td.path().join("foo").metadata()); + t!(td.path().join("bar").metadata()); +} + +#[test] +fn absolute_link_deref_error() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("/")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + assert!(ar.unpack(td.path()).is_err()); + t!(td.path().join("foo").symlink_metadata()); + assert!(File::open(td.path().join("foo").join("bar")).is_err()); +} + +#[test] +fn relative_link_deref_error() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("../../../../")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + assert!(ar.unpack(td.path()).is_err()); + t!(td.path().join("foo").symlink_metadata()); + assert!(File::open(td.path().join("foo").join("bar")).is_err()); +} + +#[test] +#[cfg(unix)] +fn directory_maintains_permissions() { + use ::std::os::unix::fs::PermissionsExt; + + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Directory); + t!(header.set_path("foo")); + header.set_mode(0o777); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + t!(ar.unpack(td.path())); + let f = t!(File::open(td.path().join("foo"))); + let md = t!(f.metadata()); + assert!(md.is_dir()); + assert_eq!(md.permissions().mode(), 0o40777); +} + +#[test] +#[cfg(not(windows))] // dangling symlinks have weird permissions +fn modify_link_just_created() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("bar/foo")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + t!(ar.unpack(td.path())); + + t!(File::open(td.path().join("bar/foo"))); + t!(File::open(td.path().join("bar/bar"))); + t!(File::open(td.path().join("foo/foo"))); + t!(File::open(td.path().join("foo/bar"))); +} + +#[test] +#[cfg(not(windows))] // dangling symlinks have weird permissions +fn modify_outside_with_relative_symlink() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("symlink")); + t!(header.set_link_name("..")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("symlink/foo/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + let tar_dir = td.path().join("tar"); + create_dir(&tar_dir).unwrap(); + assert!(ar.unpack(tar_dir).is_err()); + assert!(!td.path().join("foo").exists()); +} + +#[test] +fn parent_paths_error() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("..")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo/bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + assert!(ar.unpack(td.path()).is_err()); + t!(td.path().join("foo").symlink_metadata()); + assert!(File::open(td.path().join("foo").join("bar")).is_err()); +} + +#[test] +#[cfg(unix)] +fn good_parent_paths_ok() { + use std::path::PathBuf; + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path(PathBuf::from("foo").join("bar"))); + t!(header.set_link_name(PathBuf::from("..").join("bar"))); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("bar")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + t!(ar.unpack(td.path())); + t!(td.path().join("foo").join("bar").read_link()); + let dst = t!(td.path().join("foo").join("bar").canonicalize()); + t!(File::open(dst)); +} + +#[test] +fn modify_hard_link_just_created() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Link); + t!(header.set_path("foo")); + t!(header.set_link_name("../test")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(1); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo")); + header.set_cksum(); + t!(ar.append(&header, &b"x"[..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + + let test = td.path().join("test"); + t!(File::create(&test)); + + let dir = td.path().join("dir"); + assert!(ar.unpack(&dir).is_err()); + + let mut contents = Vec::new(); + t!(t!(File::open(&test)).read_to_end(&mut contents)); + assert_eq!(contents.len(), 0); +} + +#[test] +fn modify_symlink_just_created() { + let mut ar = tar::Builder::new(Vec::new()); + + let mut header = tar::Header::new_gnu(); + header.set_size(0); + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_path("foo")); + t!(header.set_link_name("../test")); + header.set_cksum(); + t!(ar.append(&header, &[][..])); + + let mut header = tar::Header::new_gnu(); + header.set_size(1); + header.set_entry_type(tar::EntryType::Regular); + t!(header.set_path("foo")); + header.set_cksum(); + t!(ar.append(&header, &b"x"[..])); + + let bytes = t!(ar.into_inner()); + let mut ar = tar::Archive::new(&bytes[..]); + + let td = t!(Builder::new().prefix("tar").tempdir()); + + let test = td.path().join("test"); + t!(File::create(&test)); + + let dir = td.path().join("dir"); + t!(ar.unpack(&dir)); + + let mut contents = Vec::new(); + t!(t!(File::open(&test)).read_to_end(&mut contents)); + assert_eq!(contents.len(), 0); +} diff --git a/vendor/tar-0.4.38/tests/header/mod.rs b/vendor/tar-0.4.38/tests/header/mod.rs new file mode 100644 index 000000000..86692e337 --- /dev/null +++ b/vendor/tar-0.4.38/tests/header/mod.rs @@ -0,0 +1,246 @@ +use std::fs::{self, File}; +use std::io::{self, Write}; +use std::path::Path; +use std::{iter, mem, thread, time}; + +use tempfile::Builder; + +use tar::{GnuHeader, Header, HeaderMode}; + +#[test] +fn default_gnu() { + let mut h = Header::new_gnu(); + assert!(h.as_gnu().is_some()); + assert!(h.as_gnu_mut().is_some()); + assert!(h.as_ustar().is_none()); + assert!(h.as_ustar_mut().is_none()); +} + +#[test] +fn goto_old() { + let mut h = Header::new_old(); + assert!(h.as_gnu().is_none()); + assert!(h.as_gnu_mut().is_none()); + assert!(h.as_ustar().is_none()); + assert!(h.as_ustar_mut().is_none()); +} + +#[test] +fn goto_ustar() { + let mut h = Header::new_ustar(); + assert!(h.as_gnu().is_none()); + assert!(h.as_gnu_mut().is_none()); + assert!(h.as_ustar().is_some()); + assert!(h.as_ustar_mut().is_some()); +} + +#[test] +fn link_name() { + let mut h = Header::new_gnu(); + t!(h.set_link_name("foo")); + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo")); + t!(h.set_link_name("../foo")); + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("../foo")); + t!(h.set_link_name("foo/bar")); + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo/bar")); + t!(h.set_link_name("foo\\ba")); + if cfg!(windows) { + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo/ba")); + } else { + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo\\ba")); + } + + let name = "foo\\bar\0"; + for (slot, val) in h.as_old_mut().linkname.iter_mut().zip(name.as_bytes()) { + *slot = *val; + } + assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo\\bar")); + + assert!(h.set_link_name("\0").is_err()); +} + +#[test] +fn mtime() { + let h = Header::new_gnu(); + assert_eq!(t!(h.mtime()), 0); + + let h = Header::new_ustar(); + assert_eq!(t!(h.mtime()), 0); + + let h = Header::new_old(); + assert_eq!(t!(h.mtime()), 0); +} + +#[test] +fn user_and_group_name() { + let mut h = Header::new_gnu(); + t!(h.set_username("foo")); + t!(h.set_groupname("bar")); + assert_eq!(t!(h.username()), Some("foo")); + assert_eq!(t!(h.groupname()), Some("bar")); + + h = Header::new_ustar(); + t!(h.set_username("foo")); + t!(h.set_groupname("bar")); + assert_eq!(t!(h.username()), Some("foo")); + assert_eq!(t!(h.groupname()), Some("bar")); + + h = Header::new_old(); + assert_eq!(t!(h.username()), None); + assert_eq!(t!(h.groupname()), None); + assert!(h.set_username("foo").is_err()); + assert!(h.set_groupname("foo").is_err()); +} + +#[test] +fn dev_major_minor() { + let mut h = Header::new_gnu(); + t!(h.set_device_major(1)); + t!(h.set_device_minor(2)); + assert_eq!(t!(h.device_major()), Some(1)); + assert_eq!(t!(h.device_minor()), Some(2)); + + h = Header::new_ustar(); + t!(h.set_device_major(1)); + t!(h.set_device_minor(2)); + assert_eq!(t!(h.device_major()), Some(1)); + assert_eq!(t!(h.device_minor()), Some(2)); + + h.as_ustar_mut().unwrap().dev_minor[0] = 0x7f; + h.as_ustar_mut().unwrap().dev_major[0] = 0x7f; + assert!(h.device_major().is_err()); + assert!(h.device_minor().is_err()); + + h.as_ustar_mut().unwrap().dev_minor[0] = b'g'; + h.as_ustar_mut().unwrap().dev_major[0] = b'h'; + assert!(h.device_major().is_err()); + assert!(h.device_minor().is_err()); + + h = Header::new_old(); + assert_eq!(t!(h.device_major()), None); + assert_eq!(t!(h.device_minor()), None); + assert!(h.set_device_major(1).is_err()); + assert!(h.set_device_minor(1).is_err()); +} + +#[test] +fn set_path() { + let mut h = Header::new_gnu(); + t!(h.set_path("foo")); + assert_eq!(t!(h.path()).to_str(), Some("foo")); + t!(h.set_path("foo/")); + assert_eq!(t!(h.path()).to_str(), Some("foo/")); + t!(h.set_path("foo/bar")); + assert_eq!(t!(h.path()).to_str(), Some("foo/bar")); + t!(h.set_path("foo\\bar")); + if cfg!(windows) { + assert_eq!(t!(h.path()).to_str(), Some("foo/bar")); + } else { + assert_eq!(t!(h.path()).to_str(), Some("foo\\bar")); + } + + // set_path documentation explictly states it removes any ".", signfying the + // current directory, from the path. This test ensures that documented + // beavhior occurs + t!(h.set_path("./control")); + assert_eq!(t!(h.path()).to_str(), Some("control")); + + let long_name = iter::repeat("foo").take(100).collect::<String>(); + let medium1 = iter::repeat("foo").take(52).collect::<String>(); + let medium2 = iter::repeat("fo/").take(52).collect::<String>(); + + assert!(h.set_path(&long_name).is_err()); + assert!(h.set_path(&medium1).is_err()); + assert!(h.set_path(&medium2).is_err()); + assert!(h.set_path("\0").is_err()); + + assert!(h.set_path("..").is_err()); + assert!(h.set_path("foo/..").is_err()); + assert!(h.set_path("foo/../bar").is_err()); + + h = Header::new_ustar(); + t!(h.set_path("foo")); + assert_eq!(t!(h.path()).to_str(), Some("foo")); + + assert!(h.set_path(&long_name).is_err()); + assert!(h.set_path(&medium1).is_err()); + t!(h.set_path(&medium2)); + assert_eq!(t!(h.path()).to_str(), Some(&medium2[..])); +} + +#[test] +fn set_ustar_path_hard() { + let mut h = Header::new_ustar(); + let p = Path::new("a").join(&vec!["a"; 100].join("")); + t!(h.set_path(&p)); + assert_eq!(t!(h.path()), p); +} + +#[test] +fn set_metadata_deterministic() { + let td = t!(Builder::new().prefix("tar-rs").tempdir()); + let tmppath = td.path().join("tmpfile"); + + fn mk_header(path: &Path, readonly: bool) -> Result<Header, io::Error> { + let mut file = t!(File::create(path)); + t!(file.write_all(b"c")); + let mut perms = t!(file.metadata()).permissions(); + perms.set_readonly(readonly); + t!(fs::set_permissions(path, perms)); + let mut h = Header::new_ustar(); + h.set_metadata_in_mode(&t!(path.metadata()), HeaderMode::Deterministic); + Ok(h) + } + + // Create "the same" File twice in a row, one second apart, with differing readonly values. + let one = t!(mk_header(tmppath.as_path(), false)); + thread::sleep(time::Duration::from_millis(1050)); + let two = t!(mk_header(tmppath.as_path(), true)); + + // Always expected to match. + assert_eq!(t!(one.size()), t!(two.size())); + assert_eq!(t!(one.path()), t!(two.path())); + assert_eq!(t!(one.mode()), t!(two.mode())); + + // Would not match without `Deterministic`. + assert_eq!(t!(one.mtime()), t!(two.mtime())); + // TODO: No great way to validate that these would not be filled, but + // check them anyway. + assert_eq!(t!(one.uid()), t!(two.uid())); + assert_eq!(t!(one.gid()), t!(two.gid())); +} + +#[test] +fn extended_numeric_format() { + let mut h: GnuHeader = unsafe { mem::zeroed() }; + h.as_header_mut().set_size(42); + assert_eq!(h.size, [48, 48, 48, 48, 48, 48, 48, 48, 48, 53, 50, 0]); + h.as_header_mut().set_size(8589934593); + assert_eq!(h.size, [0x80, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 1]); + h.size = [0x80, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 0]; + assert_eq!(h.as_header().entry_size().unwrap(), 0x0200000000); + h.size = [48, 48, 48, 48, 48, 48, 48, 48, 48, 53, 51, 0]; + assert_eq!(h.as_header().entry_size().unwrap(), 43); + + h.as_header_mut().set_gid(42); + assert_eq!(h.gid, [48, 48, 48, 48, 48, 53, 50, 0]); + assert_eq!(h.as_header().gid().unwrap(), 42); + h.as_header_mut().set_gid(0x7fffffffffffffff); + assert_eq!(h.gid, [0xff; 8]); + assert_eq!(h.as_header().gid().unwrap(), 0x7fffffffffffffff); + h.uid = [0x80, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78]; + assert_eq!(h.as_header().uid().unwrap(), 0x12345678); + + h.mtime = [ + 0x80, 0, 0, 0, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + ]; + assert_eq!(h.as_header().mtime().unwrap(), 0x0123456789abcdef); +} + +#[test] +fn byte_slice_conversion() { + let h = Header::new_gnu(); + let b: &[u8] = h.as_bytes(); + let b_conv: &[u8] = Header::from_byte_slice(h.as_bytes()).as_bytes(); + assert_eq!(b, b_conv); +} |