Initial commit

This commit is contained in:
Jamie Winsor
2015-12-17 18:24:09 -08:00
commit 98168ddcf1
14 changed files with 1228 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
target
Cargo.lock
.tags
.tags1
+21
View File
@@ -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 '<meta http-equiv=refresh content=0;url=libarchive/index.html>' > 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=
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "libarchive"
version = "0.1.0"
authors = ["Jamie Winsor <jamie@vialstudios.com>"]
license = "MIT"
repository = "https://github.com/reset/libarchive-rust"
[dependencies]
libc = "*"
[dependencies.libarchive3-sys]
git = "https://github.com/reset/libarchive3-sys"
branch = "master"
+21
View File
@@ -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.
+39
View File
@@ -0,0 +1,39 @@
# libarchive-rust
[![Build Status](https://travis-ci.org/reset/libarchive-rust.svg?branch=master)](https://travis-ci.org/reset/libarchive-rust)
[![crates.io](https://meritbadge.herokuapp.com/gpgme)](https://crates.io/crates/libarchive)
A Rust crate for interacting with archives using [libarchive](http://www.libarchive.org)
[Documentation](http://reset.github.io/libarchive-rust)
## 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;
```
+225
View File
@@ -0,0 +1,225 @@
use std::default::Default;
use std::str;
use std::ffi::CStr;
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<extern "C" fn() -> ()>, 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 trait Handle {
unsafe fn handle(&self) -> *mut ffi::Struct_archive;
fn err_code(&self) -> i32 {
unsafe { ffi::archive_errno(self.handle()) }
}
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 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()) }
}
}
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 }
}
}
+36
View File
@@ -0,0 +1,36 @@
use archive;
pub type ArchiveResult<T> = Result<T, ArchiveError>;
#[derive(Debug)]
pub struct ErrCode(i32);
#[derive(Debug)]
pub enum ArchiveError {
Consumed,
Sys(ErrCode, String),
ReadFailure,
WriteFailure,
HeaderPosition,
}
impl<'a> From<&'a archive::Handle> for ArchiveError {
fn from(handle: &'a archive::Handle) -> ArchiveError {
ArchiveError::Sys(ErrCode::from(handle), handle.err_msg())
}
}
impl<'a> From<&'a archive::Handle> for ErrCode {
fn from(handle: &'a archive::Handle) -> ErrCode {
ErrCode(handle.err_code())
}
}
impl<'a> From<&'a archive::Handle> for ArchiveResult<()> {
fn from(handle: &'a archive::Handle) -> ArchiveResult<()> {
match handle.err_code() {
0 => Ok(()),
_ => Err(ArchiveError::from(handle)),
}
}
}
+7
View File
@@ -0,0 +1,7 @@
extern crate libc;
extern crate libarchive3_sys;
pub mod archive;
pub mod error;
pub mod reader;
pub mod writer;
+407
View File
@@ -0,0 +1,407 @@
use std::any::Any;
use std::default::Default;
use std::error::Error;
use std::ffi::CString;
use std::io::{self, Read};
use std::mem;
use std::path::Path;
use std::ptr;
use libc::{c_void, ssize_t};
use libarchive3_sys::ffi;
use archive::{Entry, ReadCompression, ReadFilter, ReadFormat, Handle};
use error::{ArchiveResult, ArchiveError};
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.description()).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<&ReaderEntry> {
let res = unsafe { ffi::archive_read_next_header(self.handle(), &mut self.entry().handle) };
if res == 0 {
Some(self.entry())
} else {
None
}
}
}
pub struct FileReader {
handle: *mut ffi::Struct_archive,
entry: ReaderEntry,
}
pub struct StreamReader {
handle: *mut ffi::Struct_archive,
entry: ReaderEntry,
_pipe: Pipe,
}
pub struct Builder {
handle: *mut ffi::Struct_archive,
consumed: bool,
}
pub struct ReaderEntry {
handle: *mut ffi::Struct_archive_entry,
}
struct Pipe {
reader: Box<Read>,
buffer: Vec<u8>,
}
impl Pipe {
fn new<T: Any + Read>(src: T) -> Self {
Pipe {
reader: Box::new(src),
buffer: vec![0; 8192],
}
}
fn read_bytes(&mut self) -> io::Result<usize> {
self.reader.read(&mut self.buffer[..])
}
}
impl FileReader {
pub fn open<T: AsRef<Path>>(mut builder: Builder, file: T) -> ArchiveResult<Self> {
try!(builder.check_consumed());
let c_file = CString::new(file.as_ref().to_string_lossy().as_bytes()).unwrap();
unsafe {
match ffi::archive_read_open_filename(builder.handle(), c_file.as_ptr(), BLOCK_SIZE) {
0 => {
builder.consume();
Ok(Self::new(builder.handle()))
}
_ => Err(ArchiveError::from(&builder as &Handle)),
}
}
}
fn new(handle: *mut ffi::Struct_archive) -> Self {
FileReader {
handle: handle,
entry: ReaderEntry::default(),
}
}
}
impl Handle for FileReader {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Reader for FileReader {
fn entry(&mut self) -> &mut ReaderEntry {
&mut self.entry
}
}
impl Drop for FileReader {
fn drop(&mut self) {
unsafe {
ffi::archive_read_close(self.handle()); // jw todo: close here?
ffi::archive_read_free(self.handle());
}
}
}
impl StreamReader {
pub fn open<T: Any + Read>(mut builder: Builder, src: T) -> ArchiveResult<Self> {
unsafe {
builder.consume();
let mut pipe = Pipe::new(src);
let pipe_ptr: *mut c_void = &mut pipe as *mut Pipe as *mut c_void;
match ffi::archive_read_open(builder.handle(),
pipe_ptr,
None,
Some(stream_read_callback),
None) {
0 => {
let reader = StreamReader {
handle: builder.handle(),
entry: ReaderEntry::default(),
_pipe: pipe,
};
Ok(reader)
}
_ => Err(ArchiveError::from(&builder as &Handle)),
}
}
}
}
impl Handle for StreamReader {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Reader for StreamReader {
fn entry(&mut self) -> &mut ReaderEntry {
&mut self.entry
}
}
impl Drop for StreamReader {
fn drop(&mut self) {
unsafe {
ffi::archive_read_close(self.handle()); // jw todo: close here?
ffi::archive_read_free(self.handle());
}
}
}
impl Builder {
pub fn new() -> Self {
Builder::default()
}
pub fn support_compression(&mut self, compression: ReadCompression) -> ArchiveResult<()> {
match compression {
ReadCompression::All => unsafe {
ffi::archive_read_support_compression_all(self.handle);
},
ReadCompression::Bzip2 => unsafe {
ffi::archive_read_support_compression_bzip2(self.handle);
},
ReadCompression::Compress => unsafe {
ffi::archive_read_support_compression_compress(self.handle);
},
ReadCompression::Gzip => unsafe {
ffi::archive_read_support_compression_gzip(self.handle);
},
ReadCompression::Lzip => unsafe {
ffi::archive_read_support_compression_lzip(self.handle);
},
ReadCompression::Lzma => unsafe {
ffi::archive_read_support_compression_lzma(self.handle);
},
ReadCompression::None => unsafe {
ffi::archive_read_support_compression_none(self.handle);
},
ReadCompression::Program(prog) => {
let c_prog = CString::new(prog).unwrap();
unsafe {
ffi::archive_read_support_compression_program(self.handle, c_prog.as_ptr());
}
}
ReadCompression::Rpm => unsafe {
ffi::archive_read_support_compression_rpm(self.handle);
},
ReadCompression::Uu => unsafe {
ffi::archive_read_support_compression_uu(self.handle);
},
ReadCompression::Xz => unsafe {
ffi::archive_read_support_compression_xz(self.handle);
},
}
ArchiveResult::from(self as &Handle)
}
pub fn support_filter(&mut self, filter: ReadFilter) -> ArchiveResult<()> {
match filter {
ReadFilter::All => unsafe {
ffi::archive_read_support_filter_all(self.handle);
},
ReadFilter::Bzip2 => unsafe {
ffi::archive_read_support_filter_bzip2(self.handle);
},
ReadFilter::Compress => unsafe {
ffi::archive_read_support_filter_compress(self.handle);
},
ReadFilter::Grzip => unsafe {
ffi::archive_read_support_filter_grzip(self.handle);
},
ReadFilter::Gzip => unsafe {
ffi::archive_read_support_filter_gzip(self.handle);
},
ReadFilter::Lrzip => unsafe {
ffi::archive_read_support_filter_lrzip(self.handle);
},
ReadFilter::Lzip => unsafe {
ffi::archive_read_support_filter_lzip(self.handle);
},
ReadFilter::Lzma => unsafe {
ffi::archive_read_support_filter_lzma(self.handle);
},
ReadFilter::Lzop => unsafe {
ffi::archive_read_support_filter_lzop(self.handle);
},
ReadFilter::None => unsafe {
ffi::archive_read_support_filter_none(self.handle);
},
ReadFilter::Program(prog) => {
let c_prog = CString::new(prog).unwrap();
unsafe {
ffi::archive_read_support_filter_program(self.handle, c_prog.as_ptr());
}
}
ReadFilter::ProgramSignature(prog, cb, size) => {
let c_prog = CString::new(prog).unwrap();
unsafe {
ffi::archive_read_support_filter_program_signature(self.handle,
c_prog.as_ptr(),
mem::transmute(cb),
size);
}
}
ReadFilter::Rpm => unsafe {
ffi::archive_read_support_filter_rpm(self.handle);
},
ReadFilter::Uu => unsafe {
ffi::archive_read_support_filter_uu(self.handle);
},
ReadFilter::Xz => unsafe {
ffi::archive_read_support_filter_xz(self.handle);
},
}
ArchiveResult::from(self as &Handle)
}
pub fn support_format(&self, format: ReadFormat) -> ArchiveResult<()> {
match format {
ReadFormat::SevenZip => unsafe {
ffi::archive_read_support_format_7zip(self.handle());
},
ReadFormat::All => unsafe {
ffi::archive_read_support_format_all(self.handle());
},
ReadFormat::Ar => unsafe {
ffi::archive_read_support_format_ar(self.handle());
},
ReadFormat::Cab => unsafe {
ffi::archive_read_support_format_cab(self.handle());
},
ReadFormat::Cpio => unsafe {
ffi::archive_read_support_format_cpio(self.handle());
},
ReadFormat::Empty => unsafe {
ffi::archive_read_support_format_empty(self.handle());
},
ReadFormat::Gnutar => unsafe {
ffi::archive_read_support_format_gnutar(self.handle());
},
ReadFormat::Iso9660 => unsafe {
ffi::archive_read_support_format_iso9660(self.handle());
},
ReadFormat::Lha => unsafe {
ffi::archive_read_support_format_lha(self.handle());
},
ReadFormat::Mtree => unsafe {
ffi::archive_read_support_format_mtree(self.handle());
},
ReadFormat::Rar => unsafe {
ffi::archive_read_support_format_rar(self.handle());
},
ReadFormat::Raw => unsafe {
ffi::archive_read_support_format_raw(self.handle());
},
ReadFormat::Tar => unsafe {
ffi::archive_read_support_format_tar(self.handle());
},
ReadFormat::Xar => unsafe {
ffi::archive_read_support_format_xar(self.handle());
},
ReadFormat::Zip => unsafe {
ffi::archive_read_support_format_zip(self.handle());
},
}
ArchiveResult::from(self as &Handle)
}
pub fn open_file<T: AsRef<Path>>(self, file: T) -> ArchiveResult<FileReader> {
try!(self.check_consumed());
FileReader::open(self, file)
}
pub fn open_stream<T: Any + Read>(self, src: T) -> ArchiveResult<StreamReader> {
try!(self.check_consumed());
StreamReader::open(self, src)
}
fn check_consumed(&self) -> ArchiveResult<()> {
if self.consumed {
Err(ArchiveError::Consumed)
} else {
Ok(())
}
}
fn consume(&mut self) {
self.consumed = true;
}
}
impl Handle for Builder {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Drop for Builder {
fn drop(&mut self) {
if !self.consumed {
unsafe {
ffi::archive_read_free(self.handle);
}
}
}
}
impl Default for Builder {
fn default() -> Self {
unsafe {
let handle = ffi::archive_read_new();
if handle.is_null() {
panic!("Allocation error");
}
Builder {
handle: handle,
consumed: false,
}
}
}
}
impl ReaderEntry {
pub fn new(handle: *mut ffi::Struct_archive_entry) -> Self {
ReaderEntry { handle: handle }
}
}
impl Default for ReaderEntry {
fn default() -> Self {
ReaderEntry { handle: ptr::null_mut() }
}
}
impl Entry for ReaderEntry {
unsafe fn entry(&self) -> *mut ffi::Struct_archive_entry {
self.handle
}
}
+339
View File
@@ -0,0 +1,339 @@
use std::default::Default;
use std::path::Path;
use std::ptr;
use std::ffi::CString;
use libarchive3_sys::ffi;
use archive::{Entry, ExtractOptions, Handle, WriteFilter, WriteFormat};
use reader::{Reader, ReaderEntry};
use error::{ArchiveResult, ArchiveError};
pub struct Writer {
handle: *mut ffi::Struct_archive,
}
pub struct Disk {
handle: *mut ffi::Struct_archive,
}
pub struct Builder {
handle: *mut ffi::Struct_archive,
consumed: bool,
}
impl Writer {
pub fn new(handle: *mut ffi::Struct_archive) -> Self {
Writer { handle: handle }
}
}
impl Handle for Writer {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Drop for Writer {
fn drop(&mut self) {
unsafe {
ffi::archive_write_free(self.handle());
}
}
}
impl Disk {
pub fn new() -> Self {
Disk::default()
}
// Retrieve the currently-set value for last block size. A value of -1 here indicates that the
// library should use default values.
pub fn bytes_in_last_block(&self) -> i32 {
unsafe { ffi::archive_write_get_bytes_in_last_block(self.handle) }
}
// Retrieve the block size to be used for writing. A value of -1 here indicates that the
// library should use default values. A value of zero indicates that internal blocking is
// suppressed.
pub fn bytes_per_block(&self) -> i32 {
unsafe { ffi::archive_write_get_bytes_per_block(self.handle) }
}
pub fn set_bytes_per_block(&mut self, count: i32) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_set_bytes_per_block(self.handle, count);
}
ArchiveResult::from(self as &Handle)
}
pub fn set_bytes_in_last_block(&mut self, count: i32) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_set_bytes_in_last_block(self.handle, count);
}
ArchiveResult::from(self as &Handle)
}
// Set options for extraction built from `ExtractOptions`
pub fn set_options(&self, eopt: &ExtractOptions) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_disk_set_options(self.handle, eopt.flags);
}
ArchiveResult::from(self as &Handle)
}
// This convenience function installs a standard set of user and group lookup functions. These
// functions use getpwnam(3) and getgrnam(3) to convert names to ids, defaulting to the ids if
// the names cannot be looked up. These functions also implement a simple memory cache to
// reduce the number of calls to getpwnam(3) and getgrnam(3).
pub fn set_standard_lookup(&self) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_disk_set_standard_lookup(self.handle);
}
ArchiveResult::from(self as &Handle)
}
// * Failures - HeaderPosition
pub fn write<T: Reader>(&self, reader: &mut T) -> ArchiveResult<()> {
if reader.header_position() != 0 {
return Err(ArchiveError::HeaderPosition);
}
let mut write_pending: bool = false;
loop {
{
if let Some(entry) = reader.next_header() {
match self.write_header(entry) {
Ok(()) => (),
Err(e) => return Err(e),
}
if entry.size() > 0 {
write_pending = true
}
} else {
return Ok(());
}
}
if write_pending {
try!(self.write_data(reader));
write_pending = false;
}
}
}
pub fn close(&self) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_close(self.handle());
}
ArchiveResult::from(self as &Handle)
}
fn write_data<T: Reader>(&self, reader: &T) -> ArchiveResult<()> {
let mut buff = ptr::null();
let mut size = 0;
let mut offset = 0;
unsafe {
match ffi::archive_read_data_block(reader.handle(), &mut buff, &mut size, &mut offset) {
ffi::ARCHIVE_EOF => Ok(()),
ffi::ARCHIVE_OK => {
if ffi::archive_write_data_block(self.handle, buff, size, offset) < 0 {
Err(ArchiveError::from(self as &Handle))
} else {
Ok(())
}
}
_ => Err(ArchiveError::from(reader as &Handle)),
}
}
}
fn write_header(&self, entry: &ReaderEntry) -> ArchiveResult<()> {
unsafe {
ffi::archive_write_header(self.handle, entry.entry());
}
ArchiveResult::from(self as &Handle)
}
}
impl Handle for Disk {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Default for Disk {
fn default() -> Self {
unsafe {
let handle = ffi::archive_write_disk_new();
if handle.is_null() {
panic!("Allocation error");
}
Disk { handle: handle }
}
}
}
impl Drop for Disk {
fn drop(&mut self) {
self.close().unwrap();
unsafe {
ffi::archive_write_free(self.handle());
}
}
}
impl Builder {
pub fn new() -> Self {
Builder::default()
}
pub fn add_filter(&mut self, filter: WriteFilter) -> ArchiveResult<()> {
match filter {
WriteFilter::B64Encode => unsafe {
ffi::archive_write_add_filter_b64encode(self.handle);
},
WriteFilter::Bzip2 => unsafe {
ffi::archive_write_add_filter_bzip2(self.handle);
},
WriteFilter::Compress => unsafe {
ffi::archive_write_add_filter_compress(self.handle);
},
WriteFilter::Grzip => unsafe {
ffi::archive_write_add_filter_grzip(self.handle);
},
WriteFilter::Gzip => unsafe {
ffi::archive_write_add_filter_gzip(self.handle);
},
WriteFilter::Lrzip => unsafe {
ffi::archive_write_add_filter_lrzip(self.handle);
},
WriteFilter::Lzip => unsafe {
ffi::archive_write_add_filter_lzip(self.handle);
},
WriteFilter::Lzma => unsafe {
ffi::archive_write_add_filter_lzma(self.handle);
},
WriteFilter::Lzop => unsafe {
ffi::archive_write_add_filter_lzop(self.handle);
},
WriteFilter::None => unsafe {
ffi::archive_write_add_filter_none(self.handle);
},
WriteFilter::Program(prog) => {
let c_prog = CString::new(prog).unwrap();
unsafe { ffi::archive_write_add_filter_program(self.handle, c_prog.as_ptr()) };
}
WriteFilter::UuEncode => unsafe {
ffi::archive_write_add_filter_uuencode(self.handle);
},
WriteFilter::Xz => unsafe {
ffi::archive_write_add_filter_xz(self.handle);
},
}
ArchiveResult::from(self as &Handle)
}
pub fn set_format(&self, format: WriteFormat) -> ArchiveResult<()> {
match format {
WriteFormat::SevenZip => unsafe {
ffi::archive_write_set_format_7zip(self.handle);
},
WriteFormat::ArBsd => unsafe {
ffi::archive_write_set_format_ar_bsd(self.handle);
},
WriteFormat::ArSvr4 => unsafe {
ffi::archive_write_set_format_ar_svr4(self.handle);
},
WriteFormat::Cpio => unsafe {
ffi::archive_write_set_format_cpio(self.handle);
},
WriteFormat::CpioNewc => unsafe {
ffi::archive_write_set_format_cpio_newc(self.handle);
},
WriteFormat::Gnutar => unsafe {
ffi::archive_write_set_format_gnutar(self.handle);
},
WriteFormat::Iso9660 => unsafe {
ffi::archive_write_set_format_iso9660(self.handle);
},
WriteFormat::Mtree => unsafe {
ffi::archive_write_set_format_mtree(self.handle);
},
WriteFormat::MtreeClassic => unsafe {
ffi::archive_write_set_format_mtree_classic(self.handle);
},
WriteFormat::Pax => unsafe {
ffi::archive_write_set_format_pax(self.handle);
},
WriteFormat::PaxRestricted => unsafe {
ffi::archive_write_set_format_pax_restricted(self.handle);
},
WriteFormat::Shar => unsafe {
ffi::archive_write_set_format_shar(self.handle);
},
WriteFormat::SharDump => unsafe {
ffi::archive_write_set_format_shar_dump(self.handle);
},
WriteFormat::Ustar => unsafe {
ffi::archive_write_set_format_ustar(self.handle);
},
WriteFormat::V7tar => unsafe {
ffi::archive_write_set_format_v7tar(self.handle);
},
WriteFormat::Xar => unsafe {
ffi::archive_write_set_format_xar(self.handle);
},
WriteFormat::Zip => unsafe {
ffi::archive_write_set_format_zip(self.handle);
},
}
ArchiveResult::from(self as &Handle)
}
pub fn open_file<T: AsRef<Path>>(mut self, file: T) -> ArchiveResult<Writer> {
if self.consumed {
return Err(ArchiveError::Consumed);
}
let c_file = CString::new(file.as_ref().to_string_lossy().as_bytes()).unwrap();
let res = unsafe { ffi::archive_write_open_filename(self.handle, c_file.as_ptr()) };
match res {
0 => {
self.consumed = true;
Ok(Writer::new(self.handle))
}
_ => Err(ArchiveError::from(&self as &Handle)),
}
}
}
impl Default for Builder {
fn default() -> Self {
unsafe {
let handle = ffi::archive_write_new();
if handle.is_null() {
panic!("Allocation error");
}
Builder {
handle: handle,
consumed: false,
}
}
}
}
impl Handle for Builder {
unsafe fn handle(&self) -> *mut ffi::Struct_archive {
self.handle
}
}
impl Drop for Builder {
fn drop(&mut self) {
if !self.consumed {
unsafe {
ffi::archive_write_free(self.handle);
}
}
}
}
Binary file not shown.
+97
View File
@@ -0,0 +1,97 @@
extern crate libarchive;
pub mod util;
use std::fs::File;
use libarchive::archive::{self, ReadFilter, ReadFormat};
use libarchive::reader::{self, Reader};
use libarchive::writer;
#[test]
fn reading_from_file() {
let tar = util::path::fixture("sample.tar.gz");
let mut builder = reader::Builder::new();
builder.support_format(ReadFormat::All).ok();
builder.support_filter(ReadFilter::All).ok();
let mut reader = builder.open_file(tar).ok().unwrap();
reader.next_header();
// let entry: &archive::Entry = &reader.entry;
// println!("{:?}", entry.pathname());
// println!("{:?}", entry.size());
// for entry in reader.entries() {
// let file = entry as &archive::Entry;
// println!("{:?}", file.pathname());
// println!("{:?}", file.size());
// }
assert_eq!(4, 4);
}
#[test]
fn read_archive_from_stream() {
let tar = util::path::fixture("sample.tar.gz");
let f = File::open(tar).ok().unwrap();
let mut builder = reader::Builder::new();
builder.support_format(ReadFormat::All).ok();
builder.support_filter(ReadFilter::All).ok();
match builder.open_stream(f) {
Ok(mut reader) => {
println!("{:?}", reader.header_position());
let writer = writer::Disk::new();
writer.write(&mut reader).ok();
println!("{:?}", reader.header_position());
assert_eq!(4, 4)
},
Err(e) => {
println!("{:?}", e);
}
}
}
#[test]
fn extracting_from_file() {
let tar = util::path::fixture("sample.tar.gz");
let mut builder = reader::Builder::new();
builder.support_format(ReadFormat::All).ok();
builder.support_filter(ReadFilter::All).ok();
let mut reader = builder.open_file(tar).ok().unwrap();
println!("{:?}", reader.header_position());
let writer = writer::Disk::new();
writer.write(&mut reader).ok();
println!("{:?}", reader.header_position());
assert_eq!(4, 4)
}
#[test]
fn extracting_an_archive_with_options() {
let tar = util::path::fixture("sample.tar.gz");
let mut builder = reader::Builder::new();
builder.support_format(ReadFormat::All).ok();
builder.support_filter(ReadFilter::All).ok();
let mut reader = builder.open_file(tar).ok().unwrap();
println!("{:?}", reader.header_position());
let mut opts = archive::ExtractOptions::new();
opts.add(archive::ExtractOption::Time);
let writer = writer::Disk::new();
writer.set_options(&opts).ok();
writer.write(&mut reader).ok();
println!("{:?}", reader.header_position());
assert_eq!(4, 4)
}
#[test]
fn extracting_a_reader_twice() {
let tar = util::path::fixture("sample.tar.gz");
let mut builder = reader::Builder::new();
builder.support_format(ReadFormat::All).ok();
builder.support_filter(ReadFilter::All).ok();
let mut reader = builder.open_file(tar).ok().unwrap();
println!("{:?}", reader.header_position());
let writer = writer::Disk::new();
writer.write(&mut reader).ok();
println!("{:?}", reader.header_position());
match writer.write(&mut reader) {
Ok(_) => println!("oops"),
Err(_) => println!("nice")
}
assert_eq!(4, 4)
}
+1
View File
@@ -0,0 +1 @@
pub mod path;
+18
View File
@@ -0,0 +1,18 @@
use std::env;
use std::path::PathBuf;
pub fn exe_path() -> PathBuf {
env::current_exe().unwrap()
}
pub fn root() -> PathBuf {
exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests")
}
pub fn fixtures() -> PathBuf {
root().join("fixtures")
}
pub fn fixture(name: &str) -> PathBuf {
fixtures().join(name)
}