diff --git a/libraries/libarchive/.gitignore b/libraries/libarchive/.gitignore
new file mode 100644
index 00000000..866ed041
--- /dev/null
+++ b/libraries/libarchive/.gitignore
@@ -0,0 +1,4 @@
+target
+Cargo.lock
+.tags
+.tags1
diff --git a/libraries/libarchive/.travis.yml b/libraries/libarchive/.travis.yml
new file mode 100644
index 00000000..68e89ccb
--- /dev/null
+++ b/libraries/libarchive/.travis.yml
@@ -0,0 +1,21 @@
+language: rust
+rust: stable
+sudo: false
+addons:
+ apt:
+ packages:
+ - libarchive13
+script:
+- cargo build
+- cargo test
+- cargo doc
+after_success: |
+ [ $TRAVIS_BRANCH = master ] &&
+ [ $TRAVIS_PULL_REQUEST = false ] &&
+ echo '' > target/doc/index.html &&
+ pip install --user ghp-import &&
+ $HOME/.local/bin/ghp-import -n target/doc &&
+ git push -qf https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
+env:
+ global:
+ secure: KKPYXC3dn8CR8YRPuqkBSEu9VUjt7qSTLENnPYz4B2+lFT0iI265MqZGaHFhBv3YHjV/ONjvOj6Tn1l1JFIzLyBbiJ9EeY/5u/kh+tN2fjR8AniaqPG+SZuRERZwe92T/8xr+fGdzlG9hKr7XgAXQAdB7hAlQoU+cuiwuB0+FDMBciDELnLokIRCoKzW0LSY5EvQQ2BkPHuWv3n04isd7WF11IsidqN0HMDtSaVSC6FbfpW/MdlRqVzey/VxG/BpY0FWvCkRayB7NVLEWXtr/rWyn3jY6pKqev+I0w6eIL/hvKrQUrmSP3y0RJcCgq/7asdNQDeIFXbT1emFXw3dKmA9ZdSzBnm5Z2NO18DNAb3yUQjCJztSWaCfyEbKFbrV/V6cl8I61gHzGzS8kWG8kAbC6dSQxaAtm3wrcrPI1q4bHj0JAXJ27ICKAHoxMUWX50+tMZDchv8exQjBPu35gPi+5tJGCuGeSnZ33w+XHPTdJtb1Hqt87CSEs8umJyZbjT/NpIWzgKJz/2dTX1dpvEnJ0THRMTd2W2TQra9Y9e6GxUZlV6MVI6pDSBKdee6fGTxAtfmVzgL6Bfey5vP+70RanHmLTN/FJXw/thEfZaSsZnK298XsmJ6WXksCEBo40E0wINDe6R7R4egzTzoFqUzsv/uPNu/0QXXrM8DWTo4=
diff --git a/libraries/libarchive/Cargo.toml b/libraries/libarchive/Cargo.toml
new file mode 100644
index 00000000..11e0573a
--- /dev/null
+++ b/libraries/libarchive/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "libarchive-drop"
+version = "0.1.1"
+authors = ["Jamie Winsor ", "Drop OSS"]
+license = "Apache-2.0"
+repository = "https://github.com/Drop-OSS/libarchive-rust"
+description = "A safe Rust API for authoring and extracting archives with libarchive"
+keywords = ["libarchive", "archive", "tar", "zip"]
+
+[dependencies]
+libc = ">= 0.2.0"
+libarchive3-sys = "0.1"
diff --git a/libraries/libarchive/LICENSE b/libraries/libarchive/LICENSE
new file mode 100644
index 00000000..55c6a20f
--- /dev/null
+++ b/libraries/libarchive/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jamie Winsor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/libraries/libarchive/README.md b/libraries/libarchive/README.md
new file mode 100644
index 00000000..d66d0221
--- /dev/null
+++ b/libraries/libarchive/README.md
@@ -0,0 +1,37 @@
+# libarchive-rust
+
+[](https://travis-ci.org/chef/libarchive-rust)
+[](https://crates.io/crates/libarchive)
+
+A Rust crate for interacting with archives using [libarchive](http://www.libarchive.org)
+
+## Requirements
+
+Version 3 of libarchive is required to use this library.
+
+The required libraries and binaries can be installed by running:
+
+#### Debian / Ubuntu
+```shell
+$ sudo apt-get install libarchive13
+```
+
+#### Mac OS X
+```shell
+$ brew install libarchive
+```
+
+## Usage
+
+Put this in your `Cargo.toml`:
+
+```toml
+[dependencies]
+libarchive = "*"
+```
+
+And this in your crate root:
+
+```rust
+extern crate libarchive;
+```
diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs
new file mode 100644
index 00000000..71559923
--- /dev/null
+++ b/libraries/libarchive/src/archive.rs
@@ -0,0 +1,303 @@
+use std::default::Default;
+use std::ffi::{CStr, CString};
+use std::path::PathBuf;
+use std::str;
+
+use error::ErrCode;
+use libarchive3_sys::ffi;
+
+pub enum ReadCompression {
+ All,
+ Bzip2,
+ Compress,
+ Gzip,
+ Lzip,
+ Lzma,
+ None,
+ Program(String),
+ Rpm,
+ Uu,
+ Xz,
+}
+
+pub enum ReadFormat {
+ SevenZip,
+ All,
+ Ar,
+ Cab,
+ Cpio,
+ Empty,
+ Gnutar,
+ Iso9660,
+ Lha,
+ Mtree,
+ Rar,
+ Raw,
+ Tar,
+ Xar,
+ Zip,
+}
+
+pub enum ReadFilter {
+ All,
+ Bzip2,
+ Compress,
+ Gzip,
+ Grzip,
+ Lrzip,
+ Lzip,
+ Lzma,
+ Lzop,
+ None,
+ Program(String),
+ ProgramSignature(String, Option ()>, usize),
+ Rpm,
+ Uu,
+ Xz,
+}
+
+pub enum WriteFormat {
+ SevenZip,
+ ArBsd,
+ ArSvr4,
+ Cpio,
+ CpioNewc,
+ Gnutar,
+ Iso9660,
+ Mtree,
+ MtreeClassic,
+ Pax,
+ PaxRestricted,
+ Shar,
+ SharDump,
+ Ustar,
+ V7tar,
+ Xar,
+ Zip,
+}
+
+pub enum WriteFilter {
+ B64Encode,
+ Bzip2,
+ Compress,
+ Grzip,
+ Gzip,
+ Lrzip,
+ Lzip,
+ Lzma,
+ Lzop,
+ None,
+ Program(String),
+ UuEncode,
+ Xz,
+}
+
+pub enum FileType {
+ BlockDevice,
+ SymbolicLink,
+ Socket,
+ CharacterDevice,
+ Directory,
+ NamedPipe,
+ Mount,
+ RegularFile,
+}
+
+pub trait Handle {
+ unsafe fn handle(&self) -> *mut ffi::Struct_archive;
+
+ fn err_code(&self) -> ErrCode {
+ let code = unsafe { ffi::archive_errno(self.handle()) };
+ ErrCode(code)
+ }
+
+ fn err_msg(&self) -> String {
+ unsafe {
+ let c_str = CStr::from_ptr(ffi::archive_error_string(self.handle()));
+ let buf = c_str.to_bytes();
+ String::from(str::from_utf8(buf).unwrap())
+ }
+ }
+}
+
+pub trait Entry {
+ unsafe fn entry(&self) -> *mut ffi::Struct_archive_entry;
+
+ fn filetype(&self) -> FileType {
+ unsafe {
+ match ffi::archive_entry_filetype(self.entry()) as u32 {
+ ffi::AE_IFBLK => FileType::BlockDevice,
+ ffi::AE_IFCHR => FileType::CharacterDevice,
+ ffi::AE_IFLNK => FileType::SymbolicLink,
+ ffi::AE_IFDIR => FileType::Directory,
+ ffi::AE_IFIFO => FileType::NamedPipe,
+ ffi::AE_IFMT => FileType::Mount,
+ ffi::AE_IFREG => FileType::RegularFile,
+ ffi::AE_IFSOCK => FileType::Socket,
+ code => unreachable!("undefined filetype: {}", code),
+ }
+ }
+ }
+
+ fn hardlink(&self) -> Option<&str> {
+ let c_str: &CStr = unsafe {
+ let ptr = ffi::archive_entry_hardlink(self.entry());
+ if ptr.is_null() {
+ return None;
+ }
+ CStr::from_ptr(ptr)
+ };
+ let buf: &[u8] = c_str.to_bytes();
+ Some(str::from_utf8(buf).unwrap())
+ }
+
+ fn pathname(&self) -> &str {
+ let c_str: &CStr = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry())) };
+ let buf: &[u8] = c_str.to_bytes();
+ str::from_utf8(buf).unwrap()
+ }
+
+ fn size(&self) -> i64 {
+ unsafe { ffi::archive_entry_size(self.entry()) }
+ }
+
+ fn symlink(&self) -> &str {
+ let c_str: &CStr = unsafe { CStr::from_ptr(ffi::archive_entry_symlink(self.entry())) };
+ let buf: &[u8] = c_str.to_bytes();
+ str::from_utf8(buf).unwrap()
+ }
+
+ fn set_filetype(&mut self, file_type: FileType) {
+ unsafe {
+ let file_type = match file_type {
+ FileType::BlockDevice => ffi::AE_IFBLK,
+ FileType::CharacterDevice => ffi::AE_IFCHR,
+ FileType::SymbolicLink => ffi::AE_IFLNK,
+ FileType::Directory => ffi::AE_IFDIR,
+ FileType::NamedPipe => ffi::AE_IFIFO,
+ FileType::Mount => ffi::AE_IFMT,
+ FileType::RegularFile => ffi::AE_IFREG,
+ FileType::Socket => ffi::AE_IFSOCK,
+ };
+ ffi::archive_entry_set_filetype(self.entry(), file_type);
+ }
+ }
+
+ fn set_link(&mut self, path: &PathBuf) {
+ unsafe {
+ let c_str = CString::new(path.to_str().unwrap()).unwrap();
+ ffi::archive_entry_set_link(self.entry(), c_str.as_ptr());
+ }
+ }
+
+ fn set_pathname(&mut self, path: &PathBuf) {
+ unsafe {
+ let c_str = CString::new(path.to_str().unwrap()).unwrap();
+ ffi::archive_entry_set_pathname(self.entry(), c_str.as_ptr());
+ }
+ }
+}
+
+pub enum ExtractOption {
+ // The user and group IDs should be set on the restored file. By default, the user and group
+ // IDs are not restored.
+ Owner,
+ // Full permissions (including SGID, SUID, and sticky bits) should be restored exactly as
+ // specified, without obeying the current umask. Note that SUID and SGID bits can only be
+ // restored if the user and group ID of the object on disk are correct. If
+ // `ExtractOption::Owner` is not specified, then SUID and SGID bits will only be restored if
+ // the default user and group IDs of newly-created objects on disk happen to match those
+ // specified in the archive entry. By default, only basic permissions are restored, and umask
+ // is obeyed.
+ Permissions,
+ // The timestamps (mtime, ctime, and atime) should be restored. By default, they are ignored.
+ // Note that restoring of atime is not currently supported.
+ Time,
+ // Existing files on disk will not be overwritten. By default, existing regular files are
+ // truncated and overwritten; existing directories will have their permissions updated; other
+ // pre-existing objects are unlinked and recreated from scratch.
+ NoOverwrite,
+ // Existing files on disk will be unlinked before any attempt to create them. In some cases,
+ // this can prove to be a significant performance improvement. By default, existing files are
+ // truncated and rewritten, but the file is not recreated. In particular, the default behavior
+ // does not break existing hard links.
+ Unlink,
+ // Attempt to restore ACLs. By default, extended ACLs are ignored.
+ ACL,
+ // Attempt to restore extended file flags. By default, file flags are ignored.
+ FFlags,
+ // Attempt to restore POSIX.1e extended attributes. By default, they are ignored.
+ XAttr,
+ // Refuse to extract any object whose final location would be altered by a symlink on disk.
+ // This is intended to help guard against a variety of mischief caused by archives that
+ // (deliberately or otherwise) extract files outside of the current directory. The default is
+ // not to perform this check. If ARCHIVE_EXTRACT_UNLINK is specified together with this option,
+ // the library will remove any intermediate symlinks it finds and return an error only if such
+ // symlink could not be removed.
+ SecureSymlinks,
+ // Refuse to extract a path that contains a `..` element anywhere within it. The default is to
+ // not refuse such paths. Note that paths ending in `..` always cause an error, regardless of
+ // this flag.
+ SecureNoDotDot,
+ // Default: Create parent directories as needed
+ NoAutoDir,
+ // Default: Overwrite files, even if one on disk is newer
+ NoOverwriteNewer,
+ // Scan data for blocks of NUL bytes and try to recreate them with holes. This results in
+ // sparse files, independent of whether the archive format supports or uses them.
+ Sparse,
+ // Default: Do not restore Mac extended metadata
+ // This has no effect except on Mac OS
+ MacMetadata,
+ // Default: Use HFS+ compression if it was compressed
+ // This has no effect except on Mac OS v10.6 or later
+ NoHFSCompression,
+ // Default: Do not use HFS+ compression if it was not compressed
+ // This has no effect except on Mac OS v10.6 or later
+ HFSCompressionForced,
+ // Default: Do not reject entries with absolute paths */
+ SecureNoAbsolutePaths,
+ // Default: Do not clear no-change flags when unlinking object */
+ ClearNoChangeFFlags,
+}
+
+pub struct ExtractOptions {
+ pub flags: i32,
+}
+
+impl ExtractOptions {
+ pub fn new() -> Self {
+ ExtractOptions::default()
+ }
+
+ pub fn add(&mut self, opt: ExtractOption) -> &mut Self {
+ let flag = match opt {
+ ExtractOption::Owner => ffi::ARCHIVE_EXTRACT_OWNER,
+ ExtractOption::Permissions => ffi::ARCHIVE_EXTRACT_PERM,
+ ExtractOption::Time => ffi::ARCHIVE_EXTRACT_TIME,
+ ExtractOption::NoOverwrite => ffi::ARCHIVE_EXTRACT_NO_OVERWRITE,
+ ExtractOption::Unlink => ffi::ARCHIVE_EXTRACT_UNLINK,
+ ExtractOption::ACL => ffi::ARCHIVE_EXTRACT_ACL,
+ ExtractOption::FFlags => ffi::ARCHIVE_EXTRACT_FFLAGS,
+ ExtractOption::XAttr => ffi::ARCHIVE_EXTRACT_XATTR,
+ ExtractOption::SecureSymlinks => ffi::ARCHIVE_EXTRACT_SECURE_SYMLINKS,
+ ExtractOption::SecureNoDotDot => ffi::ARCHIVE_EXTRACT_SECURE_NODOTDOT,
+ ExtractOption::NoAutoDir => ffi::ARCHIVE_EXTRACT_NO_AUTODIR,
+ ExtractOption::NoOverwriteNewer => ffi::ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER,
+ ExtractOption::Sparse => ffi::ARCHIVE_EXTRACT_SPARSE,
+ ExtractOption::MacMetadata => ffi::ARCHIVE_EXTRACT_MAC_METADATA,
+ ExtractOption::NoHFSCompression => ffi::ARCHIVE_EXTRACT_NO_HFS_COMPRESSION,
+ ExtractOption::HFSCompressionForced => ffi::ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED,
+ ExtractOption::SecureNoAbsolutePaths => ffi::ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS,
+ ExtractOption::ClearNoChangeFFlags => ffi::ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS,
+ };
+ self.flags |= flag;
+ self
+ }
+}
+
+impl Default for ExtractOptions {
+ fn default() -> ExtractOptions {
+ ExtractOptions { flags: 0 }
+ }
+}
diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs
new file mode 100644
index 00000000..4f574257
--- /dev/null
+++ b/libraries/libarchive/src/error.rs
@@ -0,0 +1,58 @@
+use archive;
+use std::error;
+use std::fmt;
+
+pub type ArchiveResult = Result;
+
+#[derive(Debug)]
+pub struct ErrCode(pub i32);
+
+impl fmt::Display for ErrCode {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "{}", self.0)
+ }
+}
+
+#[derive(Debug)]
+pub enum ArchiveError {
+ Consumed,
+ HeaderPosition,
+ Sys(ErrCode, String),
+}
+
+impl error::Error for ArchiveError {
+ fn description(&self) -> &str {
+ match self {
+ &ArchiveError::Consumed => "Builder already consumed",
+ &ArchiveError::HeaderPosition => "Header position expected to be 0",
+ &ArchiveError::Sys(_, _) => "libarchive system error",
+ }
+ }
+}
+
+impl fmt::Display for ArchiveError {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &ArchiveError::Consumed => write!(fmt, "Builder already consumed"),
+ &ArchiveError::HeaderPosition => write!(fmt, "Header position expected to be 0"),
+ &ArchiveError::Sys(ref code, ref msg) => {
+ write!(fmt, "{} (libarchive err_code={})", msg, code)
+ }
+ }
+ }
+}
+
+impl<'a> From<&'a dyn archive::Handle> for ArchiveError {
+ fn from(handle: &'a dyn archive::Handle) -> ArchiveError {
+ ArchiveError::Sys(handle.err_code(), handle.err_msg())
+ }
+}
+
+impl<'a> From<&'a dyn archive::Handle> for ArchiveResult<()> {
+ fn from(handle: &'a dyn archive::Handle) -> ArchiveResult<()> {
+ match handle.err_code() {
+ ErrCode(0) => Ok(()),
+ _ => Err(ArchiveError::from(handle)),
+ }
+ }
+}
diff --git a/libraries/libarchive/src/lib.rs b/libraries/libarchive/src/lib.rs
new file mode 100644
index 00000000..8006328e
--- /dev/null
+++ b/libraries/libarchive/src/lib.rs
@@ -0,0 +1,7 @@
+extern crate libarchive3_sys;
+extern crate libc;
+
+pub mod archive;
+pub mod error;
+pub mod reader;
+pub mod writer;
diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs
new file mode 100644
index 00000000..6211d5f1
--- /dev/null
+++ b/libraries/libarchive/src/reader.rs
@@ -0,0 +1,388 @@
+use std::any::Any;
+use std::default::Default;
+use std::ffi::CString;
+use std::io::{self, Read};
+use std::mem;
+use std::path::Path;
+use std::ptr;
+use std::slice;
+
+use libarchive3_sys::ffi;
+use libc::{c_void, ssize_t};
+
+use archive::{Entry, Handle, ReadCompression, ReadFilter, ReadFormat};
+use error::{ArchiveError, ArchiveResult};
+
+const BLOCK_SIZE: usize = 10240;
+
+unsafe extern "C" fn stream_read_callback(
+ handle: *mut ffi::Struct_archive,
+ data: *mut c_void,
+ buff: *mut *const c_void,
+) -> ssize_t {
+ let pipe: &mut Pipe = &mut *(data as *mut Pipe);
+ *buff = pipe.buffer.as_mut_ptr() as *mut c_void;
+ match pipe.read_bytes() {
+ Ok(size) => size as ssize_t,
+ Err(e) => {
+ let desc = CString::new(e.to_string()).unwrap();
+ ffi::archive_set_error(handle, e.raw_os_error().unwrap_or(0), desc.as_ptr());
+ -1 as ssize_t
+ }
+ }
+}
+
+pub trait Reader: Handle {
+ fn entry(&mut self) -> &mut ReaderEntry;
+
+ fn header_position(&self) -> i64 {
+ unsafe { ffi::archive_read_header_position(self.handle()) }
+ }
+
+ fn next_header(&mut self) -> Option<&mut ReaderEntry> {
+ let res = unsafe { ffi::archive_read_next_header(self.handle(), &mut self.entry().handle) };
+ if res == 0 {
+ Some(self.entry())
+ } else {
+ None
+ }
+ }
+
+ fn read_block(&self) -> ArchiveResult