...
 
Commits (91)
language: rust
rust:
- stable
- beta
- nightly
env:
global:
- RUSTFLAGS='-C link-dead-code'
sudo: false
before_script:
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
matrix:
include:
- rust: stable
- rust: beta
- rust: nightly
- name: "master doc to gh-pages"
rust: nightly
script:
- cargo doc --no-deps
deploy:
provider: script
script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd target/doc && ../../rust_out)
skip_cleanup: true
on:
branch: master
script:
- cargo build --verbose
- cargo test --verbose
- cargo test --verbose --no-default-features
- cargo clean && cargo build
- rustdoc --test README.md -L target/debug -L target/debug/deps
- cargo doc --no-deps
after_success:
- travis-cargo --only nightly doc-upload
- travis-cargo coveralls --no-sudo
- cargo test
- cargo test --no-default-features
notifications:
email:
on_success: never
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
env:
global:
secure: "BkV2h2/dkKFUBhdiN3CvUt/zVdIsLhqlVIpS5uV5mQNyiQyZQ+czCmxvAoBF0r6ER6gdbUwvtBVTZ9fT+JmdpxcRJB7/oJ535MYRVkng13GjmDjj3y4KbGZdKsXgNpWPk5EbpFYZ6VdJ6LnEOBEE2aWbAbHwXgWxDCQtlgTeoXQ="
[package]
name = "tar"
version = "0.4.13"
version = "0.4.23"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
homepage = "https://github.com/alexcrichton/tar-rs"
repository = "https://github.com/alexcrichton/tar-rs"
......@@ -9,6 +8,7 @@ documentation = "https://docs.rs/tar"
license = "MIT/Apache-2.0"
keywords = ["tar", "tarfile", "encoding"]
readme = "README.md"
edition = "2018"
description = """
A Rust implementation of a TAR file reader and writer. This library does not
......@@ -18,14 +18,14 @@ contents are never required to be entirely resident in memory all at once.
"""
[dependencies]
libc = "0.2"
filetime = "0.1.5"
filetime = "0.2"
[dev-dependencies]
tempdir = "0.3"
[target."cfg(unix)".dependencies]
xattr = { version = "0.1.7", optional = true }
xattr = { version = "0.2", optional = true }
libc = "0.2"
[features]
default = ["xattr"]
# tar-rs
[![Build Status](https://travis-ci.org/alexcrichton/tar-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/tar-rs)
[![Build Status](https://travis-ci.com/alexcrichton/tar-rs.svg?branch=master)](https://travis-ci.com/alexcrichton/tar-rs)
[![Build status](https://ci.appveyor.com/api/projects/status/0udgokm2fc6ljorj?svg=true)](https://ci.appveyor.com/project/alexcrichton/tar-rs)
[![Coverage Status](https://coveralls.io/repos/alexcrichton/tar-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/alexcrichton/tar-rs?branch=master)
......@@ -64,8 +64,17 @@ fn main() {
# License
`tar-rs` is primarily distributed under the terms of both the MIT license and
the Apache License (Version 2.0), with portions covered by various BSD-like
licenses.
This project is licensed under either of
See LICENSE-APACHE, and LICENSE-MIT for details.
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this project by you, as defined in the Apache-2.0 license,
shall be dual licensed as above, without any additional terms or conditions.
......@@ -6,8 +6,8 @@
extern crate tar;
use std::io::{stdin, stdout, copy};
use std::env::args_os;
use std::io::{copy, stdin, stdout};
use std::path::Path;
use tar::Archive;
......
......@@ -17,8 +17,8 @@ fn main() {
println!("size: {}", f.header().size().unwrap());
println!("entry size: {}", f.header().entry_size().unwrap());
println!("link name: {:?}", f.link_name().unwrap());
println!("file type: {:x}", f.header().entry_type().as_byte());
println!("mode: {:o}", f.header().mode().unwrap());
println!("file type: {:#x}", f.header().entry_type().as_byte());
println!("mode: {:#o}", f.header().mode().unwrap());
println!("uid: {}", f.header().uid().unwrap());
println!("gid: {}", f.header().gid().unwrap());
println!("mtime: {}", f.header().mtime().unwrap());
......@@ -37,11 +37,12 @@ fn main() {
println!("pax extensions:");
for e in extensions {
let e = e.unwrap();
println!("\t{:?} = {:?}",
String::from_utf8_lossy(e.key_bytes()),
String::from_utf8_lossy(e.value_bytes()));
println!(
"\t{:?} = {:?}",
String::from_utf8_lossy(e.key_bytes()),
String::from_utf8_lossy(e.value_bytes())
);
}
}
}
}
......@@ -8,5 +8,6 @@ fn main() {
let mut a = Builder::new(file);
a.append_path("README.md").unwrap();
a.append_file("lib.rs", &mut File::open("src/lib.rs").unwrap()).unwrap();
a.append_file("lib.rs", &mut File::open("src/lib.rs").unwrap())
.unwrap();
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -49,8 +49,7 @@ impl EntryType {
/// appropriate to create a file type from.
pub fn new(byte: u8) -> EntryType {
match byte {
b'\x00' |
b'0' => EntryType::Regular,
b'\x00' | b'0' => EntryType::Regular,
b'1' => EntryType::Link,
b'2' => EntryType::Symlink,
b'3' => EntryType::Char,
......@@ -63,26 +62,26 @@ impl EntryType {
b'L' => EntryType::GNULongName,
b'K' => EntryType::GNULongLink,
b'S' => EntryType::GNUSparse,
b => EntryType::__Nonexhaustive(b),
b => EntryType::__Nonexhaustive(b),
}
}
/// Returns the raw underlying byte that this entry type represents.
pub fn as_byte(&self) -> u8 {
match *self {
EntryType::Regular => b'0',
EntryType::Link => b'1',
EntryType::Symlink => b'2',
EntryType::Char => b'3',
EntryType::Block => b'4',
EntryType::Directory => b'5',
EntryType::Fifo => b'6',
EntryType::Continuous => b'7',
EntryType::XHeader => b'x',
EntryType::Regular => b'0',
EntryType::Link => b'1',
EntryType::Symlink => b'2',
EntryType::Char => b'3',
EntryType::Block => b'4',
EntryType::Directory => b'5',
EntryType::Fifo => b'6',
EntryType::Continuous => b'7',
EntryType::XHeader => b'x',
EntryType::XGlobalHeader => b'g',
EntryType::GNULongName => b'L',
EntryType::GNULongLink => b'K',
EntryType::GNUSparse => b'S',
EntryType::GNULongName => b'L',
EntryType::GNULongLink => b'K',
EntryType::GNUSparse => b'S',
EntryType::__Nonexhaustive(b) => b,
}
}
......
This diff is collapsed.
......@@ -21,21 +21,15 @@
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
extern crate libc;
extern crate filetime;
#[cfg(all(unix, feature = "xattr"))]
extern crate xattr;
use std::io::{Error, ErrorKind};
use std::ops::{Deref, DerefMut};
pub use header::{Header, HeaderMode, OldHeader, UstarHeader, GnuHeader, GnuSparseHeader};
pub use header::{GnuExtSparseHeader};
pub use entry_type::EntryType;
pub use entry::Entry;
pub use archive::{Archive, Entries};
pub use builder::Builder;
pub use pax::{PaxExtensions, PaxExtension};
pub use crate::archive::{Archive, Entries};
pub use crate::builder::Builder;
pub use crate::entry::Entry;
pub use crate::entry_type::EntryType;
pub use crate::header::GnuExtSparseHeader;
pub use crate::header::{GnuHeader, GnuSparseHeader, Header, HeaderMode, OldHeader, UstarHeader};
pub use crate::pax::{PaxExtension, PaxExtensions};
mod archive;
mod builder;
......@@ -45,24 +39,6 @@ mod error;
mod header;
mod pax;
// FIXME(rust-lang/rust#26403):
// Right now there's a bug when a DST struct's last field has more
// alignment than the rest of a structure, causing invalid pointers to be
// created when it's casted around at runtime. To work around this we force
// our DST struct to instead have a forcibly higher alignment via a
// synthesized u64 (hopefully the largest alignment we'll run into in
// practice), and this should hopefully ensure that the pointers all work
// out.
struct AlignHigher<R: ?Sized>(u64, R);
impl<R: ?Sized> Deref for AlignHigher<R> {
type Target = R;
fn deref(&self) -> &R { &self.1 }
}
impl<R: ?Sized> DerefMut for AlignHigher<R> {
fn deref_mut(&mut self) -> &mut R { &mut self.1 }
}
fn other(msg: &str) -> Error {
Error::new(ErrorKind::Other, msg)
}
use std::io;
use std::str;
use std::slice;
use std::str;
use other;
use crate::other;
/// An iterator over the pax extensions in an archive entry.
///
......@@ -19,8 +19,12 @@ pub struct PaxExtension<'entry> {
}
pub fn pax_extensions(a: &[u8]) -> PaxExtensions {
fn is_newline(a: &u8) -> bool { *a == b'\n' }
PaxExtensions { data: a.split(is_newline) }
fn is_newline(a: &u8) -> bool {
*a == b'\n'
}
PaxExtensions {
data: a.split(is_newline),
}
}
impl<'entry> Iterator for PaxExtensions<'entry> {
......@@ -33,26 +37,30 @@ impl<'entry> Iterator for PaxExtensions<'entry> {
None => return None,
};
Some(line.iter().position(|b| *b == b' ').and_then(|i| {
str::from_utf8(&line[..i]).ok().and_then(|len| {
len.parse::<usize>().ok().map(|j| (i + 1, j))
})
}).and_then(|(kvstart, reported_len)| {
if line.len() + 1 == reported_len {
line[kvstart..].iter().position(|b| *b == b'=').map(|equals| {
(kvstart, equals)
Some(
line.iter()
.position(|b| *b == b' ')
.and_then(|i| {
str::from_utf8(&line[..i])
.ok()
.and_then(|len| len.parse::<usize>().ok().map(|j| (i + 1, j)))
})
.and_then(|(kvstart, reported_len)| {
if line.len() + 1 == reported_len {
line[kvstart..]
.iter()
.position(|b| *b == b'=')
.map(|equals| (kvstart, equals))
} else {
None
}
})
.map(|(kvstart, equals)| PaxExtension {
key: &line[kvstart..kvstart + equals],
value: &line[kvstart + equals + 1..],
})
} else {
None
}
}).map(|(kvstart, equals)| {
PaxExtension {
key: &line[kvstart..kvstart + equals],
value: &line[kvstart + equals + 1..],
}
}).ok_or_else(|| {
other("malformed pax extension")
}))
.ok_or_else(|| other("malformed pax extension")),
)
}
}
......
This diff is collapsed.
File mode changed from 100755 to 100644
......@@ -2,15 +2,17 @@ extern crate tar;
extern crate tempdir;
use std::fs::File;
use std::io::Read;
use tempdir::TempDir;
use std::path::PathBuf;
macro_rules! t {
($e:expr) => (match $e {
Ok(v) => v,
Err(e) => panic!("{} returned {}", stringify!($e), e),
})
($e:expr) => {
match $e {
Ok(v) => v,
Err(e) => panic!("{} returned {}", stringify!($e), e),
}
};
}
#[test]
......@@ -32,6 +34,11 @@ fn absolute_symlink() {
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]
......@@ -147,6 +154,32 @@ fn relative_link_deref_error() {
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!(TempDir::new("tar"));
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]
fn modify_link_just_created() {
let mut ar = tar::Builder::new(Vec::new());
......@@ -216,6 +249,7 @@ fn parent_paths_error() {
#[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();
......@@ -242,3 +276,73 @@ fn good_parent_paths_ok() {
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!(TempDir::new("tar"));
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!(TempDir::new("tar"));
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);
}
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::Path;
use std::{iter, thread, time};
use std::{iter, mem, thread, time};
use tempdir::TempDir;
use tar::{Header, HeaderMode};
use tar::{GnuHeader, Header, HeaderMode};
#[test]
fn default_gnu() {
......@@ -59,6 +59,18 @@ fn link_name() {
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();
......@@ -94,8 +106,8 @@ fn dev_major_minor() {
assert_eq!(t!(h.device_major()), Some(1));
assert_eq!(t!(h.device_minor()), Some(2));
h.as_ustar_mut().unwrap().dev_minor[0] = 0xff;
h.as_ustar_mut().unwrap().dev_major[0] = 0xff;
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());
......@@ -160,14 +172,14 @@ fn set_metadata_deterministic() {
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)
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.
......@@ -187,3 +199,38 @@ fn set_metadata_deterministic() {
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);
}