mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-24 00:42:51 +10:00
Initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
|
.tags
|
||||||
|
.tags1
|
||||||
@@ -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=
|
||||||
@@ -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"
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# libarchive-rust
|
||||||
|
|
||||||
|
[](https://travis-ci.org/reset/libarchive-rust)
|
||||||
|
[](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;
|
||||||
|
```
|
||||||
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
extern crate libc;
|
||||||
|
extern crate libarchive3_sys;
|
||||||
|
|
||||||
|
pub mod archive;
|
||||||
|
pub mod error;
|
||||||
|
pub mod reader;
|
||||||
|
pub mod writer;
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod path;
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user