467 lines
14 KiB
Rust
467 lines
14 KiB
Rust
extern crate tar;
|
|
extern crate tempfile;
|
|
|
|
use std::fs::create_dir;
|
|
use std::fs::File;
|
|
use std::io::Read;
|
|
|
|
use tempfile::Builder;
|
|
|
|
#[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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
ar.unpack(td.path()).unwrap();
|
|
|
|
td.path().join("foo").symlink_metadata().unwrap();
|
|
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
let mut entries = ar.entries().unwrap();
|
|
let entry = entries.next().unwrap().unwrap();
|
|
assert_eq!(&*entry.link_name_bytes().unwrap(), b"/bar");
|
|
}
|
|
|
|
#[test]
|
|
fn absolute_hardlink() {
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Link);
|
|
header.set_path("bar").unwrap();
|
|
// This absolute path under tempdir will be created at unpack time
|
|
header.set_link_name(td.path().join("foo")).unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
ar.unpack(td.path()).unwrap();
|
|
td.path().join("foo").metadata().unwrap();
|
|
td.path().join("bar").metadata().unwrap();
|
|
}
|
|
|
|
#[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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Link);
|
|
header.set_path("bar").unwrap();
|
|
header.set_link_name("foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
ar.unpack(td.path()).unwrap();
|
|
td.path().join("foo").metadata().unwrap();
|
|
td.path().join("bar").metadata().unwrap();
|
|
}
|
|
|
|
#[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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("/").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
assert!(ar.unpack(td.path()).is_err());
|
|
td.path().join("foo").symlink_metadata().unwrap();
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("../../../../").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
assert!(ar.unpack(td.path()).is_err());
|
|
td.path().join("foo").symlink_metadata().unwrap();
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_mode(0o777);
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
ar.unpack(td.path()).unwrap();
|
|
let f = File::open(td.path().join("foo")).unwrap();
|
|
let md = f.metadata().unwrap();
|
|
assert!(md.is_dir());
|
|
assert_eq!(md.permissions().mode(), 0o40777);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn set_entry_mask() {
|
|
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::Regular);
|
|
header.set_path("foo").unwrap();
|
|
header.set_mode(0o777);
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
let foo_path = td.path().join("foo");
|
|
|
|
let mut entries = ar.entries().unwrap();
|
|
let mut foo = entries.next().unwrap().unwrap();
|
|
foo.set_mask(0o027);
|
|
foo.unpack(&foo_path).unwrap();
|
|
|
|
let f = File::open(foo_path).unwrap();
|
|
let md = f.metadata().unwrap();
|
|
assert!(md.is_file());
|
|
assert_eq!(md.permissions().mode(), 0o100750);
|
|
}
|
|
|
|
#[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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("bar/foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
ar.unpack(td.path()).unwrap();
|
|
|
|
File::open(td.path().join("bar/foo")).unwrap();
|
|
File::open(td.path().join("bar/bar")).unwrap();
|
|
File::open(td.path().join("foo/foo")).unwrap();
|
|
File::open(td.path().join("foo/bar")).unwrap();
|
|
}
|
|
|
|
#[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);
|
|
header.set_path("symlink").unwrap();
|
|
header.set_link_name("..").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("symlink/foo/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("..").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo/bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
assert!(ar.unpack(td.path()).is_err());
|
|
td.path().join("foo").symlink_metadata().unwrap();
|
|
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);
|
|
header.set_path(PathBuf::from("foo").join("bar")).unwrap();
|
|
header
|
|
.set_link_name(PathBuf::from("..").join("bar"))
|
|
.unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("bar").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
ar.unpack(td.path()).unwrap();
|
|
td.path().join("foo").join("bar").read_link().unwrap();
|
|
let dst = td.path().join("foo").join("bar").canonicalize().unwrap();
|
|
File::open(dst).unwrap();
|
|
}
|
|
|
|
#[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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("../test").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(1);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &b"x"[..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
|
|
let test = td.path().join("test");
|
|
File::create(&test).unwrap();
|
|
|
|
let dir = td.path().join("dir");
|
|
assert!(ar.unpack(&dir).is_err());
|
|
|
|
let mut contents = Vec::new();
|
|
File::open(&test)
|
|
.unwrap()
|
|
.read_to_end(&mut contents)
|
|
.unwrap();
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name("../test").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(1);
|
|
header.set_entry_type(tar::EntryType::Regular);
|
|
header.set_path("foo").unwrap();
|
|
header.set_cksum();
|
|
ar.append(&header, &b"x"[..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
|
|
let test = td.path().join("test");
|
|
File::create(&test).unwrap();
|
|
|
|
let dir = td.path().join("dir");
|
|
ar.unpack(&dir).unwrap();
|
|
|
|
let mut contents = Vec::new();
|
|
File::open(&test)
|
|
.unwrap()
|
|
.read_to_end(&mut contents)
|
|
.unwrap();
|
|
assert_eq!(contents.len(), 0);
|
|
}
|
|
|
|
/// Test that unpacking a tarball with a symlink followed by a directory entry
|
|
/// with the same name does not allow modifying permissions of arbitrary directories
|
|
/// outside the extraction path.
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn symlink_dir_collision_does_not_modify_external_dir_permissions() {
|
|
use ::std::fs;
|
|
use ::std::os::unix::fs::PermissionsExt;
|
|
|
|
let td = Builder::new().prefix("tar").tempdir().unwrap();
|
|
|
|
let target_dir = td.path().join("target-dir");
|
|
fs::create_dir(&target_dir).unwrap();
|
|
fs::set_permissions(&target_dir, fs::Permissions::from_mode(0o700)).unwrap();
|
|
let before_mode = fs::metadata(&target_dir).unwrap().permissions().mode() & 0o7777;
|
|
assert_eq!(before_mode, 0o700);
|
|
|
|
let extract_dir = td.path().join("extract-dir");
|
|
fs::create_dir(&extract_dir).unwrap();
|
|
|
|
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);
|
|
header.set_path("foo").unwrap();
|
|
header.set_link_name(&target_dir).unwrap();
|
|
header.set_mode(0o777);
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let mut header = tar::Header::new_gnu();
|
|
header.set_size(0);
|
|
header.set_entry_type(tar::EntryType::Directory);
|
|
header.set_path("foo").unwrap();
|
|
header.set_mode(0o777);
|
|
header.set_cksum();
|
|
ar.append(&header, &[][..]).unwrap();
|
|
|
|
let bytes = ar.into_inner().unwrap();
|
|
let mut ar = tar::Archive::new(&bytes[..]);
|
|
|
|
let result = ar.unpack(&extract_dir);
|
|
assert!(result.is_err());
|
|
|
|
let symlink_path = extract_dir.join("foo");
|
|
assert!(symlink_path
|
|
.symlink_metadata()
|
|
.unwrap()
|
|
.file_type()
|
|
.is_symlink());
|
|
|
|
let after_mode = fs::metadata(&target_dir).unwrap().permissions().mode() & 0o7777;
|
|
assert_eq!(after_mode, 0o700);
|
|
}
|