From 98168ddcf1dcbdd6e166fc8e4fc80a23ed36d3c8 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Thu, 17 Dec 2015 18:24:09 -0800 Subject: [PATCH 01/19] Initial commit --- libraries/libarchive/.gitignore | 4 + libraries/libarchive/.travis.yml | 21 + libraries/libarchive/Cargo.toml | 13 + libraries/libarchive/LICENSE | 21 + libraries/libarchive/README.md | 39 ++ libraries/libarchive/src/archive.rs | 225 ++++++++++ libraries/libarchive/src/error.rs | 36 ++ libraries/libarchive/src/lib.rs | 7 + libraries/libarchive/src/reader.rs | 407 ++++++++++++++++++ libraries/libarchive/src/writer.rs | 339 +++++++++++++++ .../libarchive/tests/fixtures/sample.tar.gz | Bin 0 -> 148 bytes libraries/libarchive/tests/lib.rs | 97 +++++ libraries/libarchive/tests/util/mod.rs | 1 + libraries/libarchive/tests/util/path.rs | 18 + 14 files changed, 1228 insertions(+) create mode 100644 libraries/libarchive/.gitignore create mode 100644 libraries/libarchive/.travis.yml create mode 100644 libraries/libarchive/Cargo.toml create mode 100644 libraries/libarchive/LICENSE create mode 100644 libraries/libarchive/README.md create mode 100644 libraries/libarchive/src/archive.rs create mode 100644 libraries/libarchive/src/error.rs create mode 100644 libraries/libarchive/src/lib.rs create mode 100644 libraries/libarchive/src/reader.rs create mode 100644 libraries/libarchive/src/writer.rs create mode 100644 libraries/libarchive/tests/fixtures/sample.tar.gz create mode 100644 libraries/libarchive/tests/lib.rs create mode 100644 libraries/libarchive/tests/util/mod.rs create mode 100644 libraries/libarchive/tests/util/path.rs 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..f0d1c70a --- /dev/null +++ b/libraries/libarchive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "libarchive" +version = "0.1.0" +authors = ["Jamie Winsor "] +license = "MIT" +repository = "https://github.com/reset/libarchive-rust" + +[dependencies] +libc = "*" + +[dependencies.libarchive3-sys] +git = "https://github.com/reset/libarchive3-sys" +branch = "master" 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..0336b5d8 --- /dev/null +++ b/libraries/libarchive/README.md @@ -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; +``` diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs new file mode 100644 index 00000000..06163780 --- /dev/null +++ b/libraries/libarchive/src/archive.rs @@ -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 ()>, 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 } + } +} diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs new file mode 100644 index 00000000..88ff4ea1 --- /dev/null +++ b/libraries/libarchive/src/error.rs @@ -0,0 +1,36 @@ +use archive; + +pub type ArchiveResult = Result; + +#[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)), + } + } +} diff --git a/libraries/libarchive/src/lib.rs b/libraries/libarchive/src/lib.rs new file mode 100644 index 00000000..c24af2f9 --- /dev/null +++ b/libraries/libarchive/src/lib.rs @@ -0,0 +1,7 @@ +extern crate libc; +extern crate libarchive3_sys; + +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..45bb1d76 --- /dev/null +++ b/libraries/libarchive/src/reader.rs @@ -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, + buffer: Vec, +} + +impl Pipe { + fn new(src: T) -> Self { + Pipe { + reader: Box::new(src), + buffer: vec![0; 8192], + } + } + + fn read_bytes(&mut self) -> io::Result { + self.reader.read(&mut self.buffer[..]) + } +} + +impl FileReader { + pub fn open>(mut builder: Builder, file: T) -> ArchiveResult { + 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(mut builder: Builder, src: T) -> ArchiveResult { + 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>(self, file: T) -> ArchiveResult { + try!(self.check_consumed()); + FileReader::open(self, file) + } + + pub fn open_stream(self, src: T) -> ArchiveResult { + 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 + } +} diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs new file mode 100644 index 00000000..0942bdee --- /dev/null +++ b/libraries/libarchive/src/writer.rs @@ -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(&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(&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>(mut self, file: T) -> ArchiveResult { + 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); + } + } + } +} diff --git a/libraries/libarchive/tests/fixtures/sample.tar.gz b/libraries/libarchive/tests/fixtures/sample.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4d9aa8e726b079aa47e52d0d9cebfb7b8e3ef4f7 GIT binary patch literal 148 zcmb2|=3ww=$qHj&etXf8>yUv!>%(=nM|X(N^h~ajTc{b!!jd4L9+x=IqetxQH2KU;}ug>^fiF#e5T$Ql=Y_i_>Js~Fx u`>MSo=J6grS3B2b+aKY_zy6C?`dNN`etG?E1{ARGE1PJJ{u>4j1_l5zM?h5o literal 0 HcmV?d00001 diff --git a/libraries/libarchive/tests/lib.rs b/libraries/libarchive/tests/lib.rs new file mode 100644 index 00000000..fcbd7acf --- /dev/null +++ b/libraries/libarchive/tests/lib.rs @@ -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) +} diff --git a/libraries/libarchive/tests/util/mod.rs b/libraries/libarchive/tests/util/mod.rs new file mode 100644 index 00000000..4da97892 --- /dev/null +++ b/libraries/libarchive/tests/util/mod.rs @@ -0,0 +1 @@ +pub mod path; diff --git a/libraries/libarchive/tests/util/path.rs b/libraries/libarchive/tests/util/path.rs new file mode 100644 index 00000000..da579b33 --- /dev/null +++ b/libraries/libarchive/tests/util/path.rs @@ -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) +} From fe0b023a2d44bb9818e7fa35d6f2c73d9cfbcf06 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Sat, 9 Jan 2016 16:14:19 -0800 Subject: [PATCH 02/19] implement display trait for archive error and err code --- libraries/libarchive/src/error.rs | 34 ++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs index 88ff4ea1..0c5c70de 100644 --- a/libraries/libarchive/src/error.rs +++ b/libraries/libarchive/src/error.rs @@ -1,3 +1,5 @@ +use std::error; +use std::fmt; use archive; pub type ArchiveResult = Result; @@ -5,13 +7,39 @@ pub type ArchiveResult = Result; #[derive(Debug)] pub struct ErrCode(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, - Sys(ErrCode, String), - ReadFailure, - WriteFailure, 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 archive::Handle> for ArchiveError { From a45b168b6ca0172ff7886164f38a7048feb2987b Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 11 Jan 2016 19:23:04 -0800 Subject: [PATCH 03/19] lock to >= 0.2.0 of libc --- libraries/libarchive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libarchive/Cargo.toml b/libraries/libarchive/Cargo.toml index f0d1c70a..4dff20a5 100644 --- a/libraries/libarchive/Cargo.toml +++ b/libraries/libarchive/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" repository = "https://github.com/reset/libarchive-rust" [dependencies] -libc = "*" +libc = ">= 0.2.0" [dependencies.libarchive3-sys] git = "https://github.com/reset/libarchive3-sys" From a069bd2a98bd88d00b21b0acc26efec2aca222e2 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 11 Jan 2016 19:38:01 -0800 Subject: [PATCH 04/19] fix segmentation fault add read_block function to reader --- libraries/libarchive/src/archive.rs | 6 ++++-- libraries/libarchive/src/error.rs | 12 +++--------- libraries/libarchive/src/reader.rs | 30 +++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs index 06163780..e680965d 100644 --- a/libraries/libarchive/src/archive.rs +++ b/libraries/libarchive/src/archive.rs @@ -3,6 +3,7 @@ use std::str; use std::ffi::CStr; use libarchive3_sys::ffi; +use error::ErrCode; pub enum ReadCompression { All, @@ -93,8 +94,9 @@ pub enum WriteFilter { pub trait Handle { unsafe fn handle(&self) -> *mut ffi::Struct_archive; - fn err_code(&self) -> i32 { - unsafe { ffi::archive_errno(self.handle()) } + fn err_code(&self) -> ErrCode { + let code = unsafe { ffi::archive_errno(self.handle()) }; + ErrCode(code) } fn err_msg(&self) -> String { diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs index 0c5c70de..7d22effb 100644 --- a/libraries/libarchive/src/error.rs +++ b/libraries/libarchive/src/error.rs @@ -5,7 +5,7 @@ use archive; pub type ArchiveResult = Result; #[derive(Debug)] -pub struct ErrCode(i32); +pub struct ErrCode(pub i32); impl fmt::Display for ErrCode { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { @@ -44,20 +44,14 @@ impl fmt::Display for ArchiveError { 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()) + ArchiveError::Sys(handle.err_code(), handle.err_msg()) } } impl<'a> From<&'a archive::Handle> for ArchiveResult<()> { fn from(handle: &'a archive::Handle) -> ArchiveResult<()> { match handle.err_code() { - 0 => Ok(()), + ErrCode(0) => Ok(()), _ => Err(ArchiveError::from(handle)), } } diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs index 45bb1d76..ee124df4 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -6,6 +6,7 @@ use std::io::{self, Read}; use std::mem; use std::path::Path; use std::ptr; +use std::slice; use libc::{c_void, ssize_t}; use libarchive3_sys::ffi; @@ -38,7 +39,7 @@ pub trait Reader : Handle { unsafe { ffi::archive_read_header_position(self.handle()) } } - fn next_header(&mut self) -> Option<&ReaderEntry> { + 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()) @@ -46,6 +47,20 @@ pub trait Reader : Handle { None } } + + fn read_block(&self) -> ArchiveResult> { + let mut buff = ptr::null(); + let mut size = 0; + let mut offset = 0; + + unsafe { + match ffi::archive_read_data_block(self.handle(), &mut buff, &mut size, &mut offset) { + ffi::ARCHIVE_EOF => Ok(None), + ffi::ARCHIVE_OK => Ok(Some(slice::from_raw_parts(buff as *const u8, size))), + _ => Err(ArchiveError::Sys(self.err_code(), self.err_msg())), + } + } + } } pub struct FileReader { @@ -56,7 +71,7 @@ pub struct FileReader { pub struct StreamReader { handle: *mut ffi::Struct_archive, entry: ReaderEntry, - _pipe: Pipe, + _pipe: Box, } pub struct Builder { @@ -133,9 +148,8 @@ impl Drop for FileReader { impl StreamReader { pub fn open(mut builder: Builder, src: T) -> ArchiveResult { unsafe { - builder.consume(); - let mut pipe = Pipe::new(src); - let pipe_ptr: *mut c_void = &mut pipe as *mut Pipe as *mut c_void; + let mut pipe = Box::new(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, @@ -147,9 +161,13 @@ impl StreamReader { entry: ReaderEntry::default(), _pipe: pipe, }; + builder.consume(); Ok(reader) } - _ => Err(ArchiveError::from(&builder as &Handle)), + _ => { + builder.consume(); + Err(ArchiveError::from(&builder as &Handle)) + } } } } From 39b37933d77f07d190b80a4e83ed63ddcff5693a Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Tue, 12 Jan 2016 00:00:30 -0800 Subject: [PATCH 05/19] add option for writing an archive's entries with a prefix --- libraries/libarchive/src/archive.rs | 13 +++++++++++-- libraries/libarchive/src/writer.rs | 23 +++++++++++++++++------ libraries/libarchive/tests/lib.rs | 17 +++++++++-------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs index e680965d..4fa3172a 100644 --- a/libraries/libarchive/src/archive.rs +++ b/libraries/libarchive/src/archive.rs @@ -1,9 +1,10 @@ use std::default::Default; +use std::ffi::{CStr, CString}; +use std::path::PathBuf; use std::str; -use std::ffi::CStr; use libarchive3_sys::ffi; -use error::ErrCode; +use error::{ArchiveResult, ErrCode}; pub enum ReadCompression { All, @@ -120,6 +121,14 @@ pub trait Entry { fn size(&self) -> i64 { unsafe { ffi::archive_entry_size(self.entry()) } } + + fn set_pathname(&mut self, path: PathBuf) -> ArchiveResult<()> { + unsafe { + let c_str = CString::new(path.to_str().unwrap()).unwrap(); + ffi::archive_entry_set_pathname(self.entry(), c_str.as_ptr()); + } + Ok(()) + } } pub enum ExtractOption { diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 0942bdee..14e41b80 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -94,14 +94,19 @@ impl Disk { } // * Failures - HeaderPosition - pub fn write(&self, reader: &mut T) -> ArchiveResult<()> { + pub fn write(&self, reader: &mut T, prefix: Option<&str>) -> ArchiveResult { if reader.header_position() != 0 { return Err(ArchiveError::HeaderPosition); } + let mut bytes: usize = 0; let mut write_pending: bool = false; loop { { if let Some(entry) = reader.next_header() { + if let Some(pfx) = prefix { + let path = Path::new(pfx).join(entry.pathname()); + try!(entry.set_pathname(path)); + } match self.write_header(entry) { Ok(()) => (), Err(e) => return Err(e), @@ -110,14 +115,20 @@ impl Disk { write_pending = true } } else { - return Ok(()); + break; } } if write_pending { - try!(self.write_data(reader)); + bytes += try!(self.write_data(reader)); write_pending = false; } } + unsafe { + match ffi::archive_write_finish_entry(self.handle()) { + ffi::ARCHIVE_OK => Ok(bytes), + _ => Err(ArchiveError::from(self as &Handle)), + } + } } pub fn close(&self) -> ArchiveResult<()> { @@ -127,19 +138,19 @@ impl Disk { ArchiveResult::from(self as &Handle) } - fn write_data(&self, reader: &T) -> ArchiveResult<()> { + fn write_data(&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_EOF => Ok(size), ffi::ARCHIVE_OK => { if ffi::archive_write_data_block(self.handle, buff, size, offset) < 0 { Err(ArchiveError::from(self as &Handle)) } else { - Ok(()) + Ok(size) } } _ => Err(ArchiveError::from(reader as &Handle)), diff --git a/libraries/libarchive/tests/lib.rs b/libraries/libarchive/tests/lib.rs index fcbd7acf..872e9472 100644 --- a/libraries/libarchive/tests/lib.rs +++ b/libraries/libarchive/tests/lib.rs @@ -35,11 +35,12 @@ fn read_archive_from_stream() { builder.support_filter(ReadFilter::All).ok(); match builder.open_stream(f) { Ok(mut reader) => { - println!("{:?}", reader.header_position()); + assert_eq!(reader.header_position(), 0); let writer = writer::Disk::new(); - writer.write(&mut reader).ok(); - println!("{:?}", reader.header_position()); - assert_eq!(4, 4) + let count = writer.write(&mut reader, Some("/opt/bldr/fucks")).ok().unwrap(); + assert_eq!(count, 14); + assert_eq!(reader.header_position(), 1024); + assert_eq!(4, 4); }, Err(e) => { println!("{:?}", e); @@ -56,7 +57,7 @@ fn extracting_from_file() { let mut reader = builder.open_file(tar).ok().unwrap(); println!("{:?}", reader.header_position()); let writer = writer::Disk::new(); - writer.write(&mut reader).ok(); + writer.write(&mut reader, None).ok(); println!("{:?}", reader.header_position()); assert_eq!(4, 4) } @@ -73,7 +74,7 @@ fn extracting_an_archive_with_options() { opts.add(archive::ExtractOption::Time); let writer = writer::Disk::new(); writer.set_options(&opts).ok(); - writer.write(&mut reader).ok(); + writer.write(&mut reader, None).ok(); println!("{:?}", reader.header_position()); assert_eq!(4, 4) } @@ -87,9 +88,9 @@ fn extracting_a_reader_twice() { let mut reader = builder.open_file(tar).ok().unwrap(); println!("{:?}", reader.header_position()); let writer = writer::Disk::new(); - writer.write(&mut reader).ok(); + writer.write(&mut reader, None).ok(); println!("{:?}", reader.header_position()); - match writer.write(&mut reader) { + match writer.write(&mut reader, None) { Ok(_) => println!("oops"), Err(_) => println!("nice") } From ad5ed84d918b4736ebf8b0d10f7101294cfb1e8c Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Thu, 17 Mar 2016 13:17:17 -0700 Subject: [PATCH 06/19] fix bug where all data blocks were not extracted to file --- libraries/libarchive/src/writer.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 14e41b80..975e8eb3 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -144,16 +144,20 @@ impl Disk { let mut offset = 0; unsafe { - match ffi::archive_read_data_block(reader.handle(), &mut buff, &mut size, &mut offset) { - ffi::ARCHIVE_EOF => Ok(size), - ffi::ARCHIVE_OK => { - if ffi::archive_write_data_block(self.handle, buff, size, offset) < 0 { - Err(ArchiveError::from(self as &Handle)) - } else { - Ok(size) + loop { + match ffi::archive_read_data_block(reader.handle(), + &mut buff, + &mut size, + &mut offset) { + ffi::ARCHIVE_EOF => return Ok(size), + ffi::ARCHIVE_OK => { + if ffi::archive_write_data_block(self.handle, buff, size, offset) != + ffi::ARCHIVE_OK as isize { + return Err(ArchiveError::from(self as &Handle)); + } } + _ => return Err(ArchiveError::from(reader as &Handle)), } - _ => Err(ArchiveError::from(reader as &Handle)), } } } From f7cc72e0711b8a533ec529e10de747ad9e54547a Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Thu, 17 Mar 2016 13:32:37 -0700 Subject: [PATCH 07/19] prepare for first release --- libraries/libarchive/Cargo.toml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/libarchive/Cargo.toml b/libraries/libarchive/Cargo.toml index 4dff20a5..93c018fc 100644 --- a/libraries/libarchive/Cargo.toml +++ b/libraries/libarchive/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "libarchive" version = "0.1.0" -authors = ["Jamie Winsor "] -license = "MIT" -repository = "https://github.com/reset/libarchive-rust" +authors = ["Jamie Winsor "] +license = "Apache-2.0" +repository = "https://github.com/chef/libarchive-rust" +description = "A safe Rust API for authoring and extracting archives with libarchive" +keywords = ["libarchive", "archive", "tar", "zip"] [dependencies] libc = ">= 0.2.0" - -[dependencies.libarchive3-sys] -git = "https://github.com/reset/libarchive3-sys" -branch = "master" +libarchive3-sys = "0.1" From a2e51c7f8b5fc53515c6a493d3ec48466f7566e8 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 21 Mar 2016 13:22:30 -0700 Subject: [PATCH 08/19] match result code before extracting an error --- libraries/libarchive/src/reader.rs | 165 ++++++++++------------------- libraries/libarchive/src/writer.rs | 162 ++++++++++++---------------- 2 files changed, 124 insertions(+), 203 deletions(-) diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs index ee124df4..b0aaf207 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -107,7 +107,7 @@ impl FileReader { 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 => { + ffi::ARCHIVE_OK => { builder.consume(); Ok(Self::new(builder.handle())) } @@ -155,7 +155,7 @@ impl StreamReader { None, Some(stream_read_callback), None) { - 0 => { + ffi::ARCHIVE_OK => { let reader = StreamReader { handle: builder.handle(), entry: ReaderEntry::default(), @@ -200,84 +200,63 @@ impl Builder { } pub fn support_compression(&mut self, compression: ReadCompression) -> ArchiveResult<()> { - match compression { + let result = match compression { ReadCompression::All => unsafe { - ffi::archive_read_support_compression_all(self.handle); + ffi::archive_read_support_compression_all(self.handle) }, ReadCompression::Bzip2 => unsafe { - ffi::archive_read_support_compression_bzip2(self.handle); + ffi::archive_read_support_compression_bzip2(self.handle) }, ReadCompression::Compress => unsafe { - ffi::archive_read_support_compression_compress(self.handle); + ffi::archive_read_support_compression_compress(self.handle) }, ReadCompression::Gzip => unsafe { - ffi::archive_read_support_compression_gzip(self.handle); + ffi::archive_read_support_compression_gzip(self.handle) }, ReadCompression::Lzip => unsafe { - ffi::archive_read_support_compression_lzip(self.handle); + ffi::archive_read_support_compression_lzip(self.handle) }, ReadCompression::Lzma => unsafe { - ffi::archive_read_support_compression_lzma(self.handle); + ffi::archive_read_support_compression_lzma(self.handle) }, ReadCompression::None => unsafe { - ffi::archive_read_support_compression_none(self.handle); + 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()); + 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); + 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) }, + }; + match result { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &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); - }, + let result = 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); + 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()); - } + 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(); @@ -285,71 +264,43 @@ impl Builder { ffi::archive_read_support_filter_program_signature(self.handle, c_prog.as_ptr(), mem::transmute(cb), - size); + 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); - }, + 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) }, + }; + match result { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &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()); - }, + let result = 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()); + 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()) }, + }; + match result { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), } - ArchiveResult::from(self as &Handle) } pub fn open_file>(self, file: T) -> ArchiveResult { diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 975e8eb3..06a5b85b 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -62,24 +62,30 @@ impl Disk { pub fn set_bytes_per_block(&mut self, count: i32) -> ArchiveResult<()> { unsafe { - ffi::archive_write_set_bytes_per_block(self.handle, count); + match ffi::archive_write_set_bytes_per_block(self.handle, count) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - 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); + match ffi::archive_write_set_bytes_in_last_block(self.handle, count) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - 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); + match ffi::archive_write_disk_set_options(self.handle, eopt.flags) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - ArchiveResult::from(self as &Handle) } // This convenience function installs a standard set of user and group lookup functions. These @@ -88,9 +94,11 @@ impl Disk { // 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); + match ffi::archive_write_disk_set_standard_lookup(self.handle) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - ArchiveResult::from(self as &Handle) } // * Failures - HeaderPosition @@ -133,9 +141,11 @@ impl Disk { pub fn close(&self) -> ArchiveResult<()> { unsafe { - ffi::archive_write_close(self.handle()); + match ffi::archive_write_close(self.handle()) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - ArchiveResult::from(self as &Handle) } fn write_data(&self, reader: &T) -> ArchiveResult { @@ -164,9 +174,11 @@ impl Disk { fn write_header(&self, entry: &ReaderEntry) -> ArchiveResult<()> { unsafe { - ffi::archive_write_header(self.handle, entry.entry()); + match ffi::archive_write_header(self.handle, entry.entry()) { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), + } } - ArchiveResult::from(self as &Handle) } } @@ -190,6 +202,7 @@ impl Default for Disk { impl Drop for Disk { fn drop(&mut self) { + // JW TODO: do not close, libarchive will handle it self.close().unwrap(); unsafe { ffi::archive_write_free(self.handle()); @@ -203,107 +216,64 @@ impl Builder { } pub fn add_filter(&mut self, filter: WriteFilter) -> ArchiveResult<()> { - match filter { + let result = 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); + 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()) }; - + 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); - }, + WriteFilter::UuEncode => unsafe { ffi::archive_write_add_filter_uuencode(self.handle) }, + WriteFilter::Xz => unsafe { ffi::archive_write_add_filter_xz(self.handle) }, + }; + match result { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &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); - }, + let result = 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); + 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); + 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); + 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); + 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) }, + }; + match result { + ffi::ARCHIVE_OK => Ok(()), + _ => ArchiveResult::from(self as &Handle), } - ArchiveResult::from(self as &Handle) } pub fn open_file>(mut self, file: T) -> ArchiveResult { @@ -313,7 +283,7 @@ impl Builder { 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 => { + ffi::ARCHIVE_OK => { self.consumed = true; Ok(Writer::new(self.handle)) } From baa9b408d655c11f46f534a85a8e9420b3f88f53 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 21 Mar 2016 13:23:47 -0700 Subject: [PATCH 09/19] don't close on free - libarchive takes care of this --- libraries/libarchive/src/reader.rs | 2 -- libraries/libarchive/src/writer.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs index b0aaf207..c913b1c8 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -139,7 +139,6 @@ impl Reader for FileReader { 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()); } } @@ -188,7 +187,6 @@ impl Reader for StreamReader { 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()); } } diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 06a5b85b..ca90e524 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -202,8 +202,6 @@ impl Default for Disk { impl Drop for Disk { fn drop(&mut self) { - // JW TODO: do not close, libarchive will handle it - self.close().unwrap(); unsafe { ffi::archive_write_free(self.handle()); } From 9e3659dcf90e2cbb77d4e99dfae3b87509b1fa1a Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 21 Mar 2016 15:58:09 -0700 Subject: [PATCH 10/19] expose ability to set/get filetype of archive entry --- libraries/libarchive/src/archive.rs | 48 +++++++++++++++++++++++++++-- libraries/libarchive/src/writer.rs | 2 +- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs index 4fa3172a..ff5a1dfb 100644 --- a/libraries/libarchive/src/archive.rs +++ b/libraries/libarchive/src/archive.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::str; use libarchive3_sys::ffi; -use error::{ArchiveResult, ErrCode}; +use error::ErrCode; pub enum ReadCompression { All, @@ -92,6 +92,17 @@ pub enum WriteFilter { Xz, } +pub enum FileType { + BlockDevice, + SymbolicLink, + Socket, + CharacterDevice, + Directory, + NamedPipe, + Mount, + RegularFile, +} + pub trait Handle { unsafe fn handle(&self) -> *mut ffi::Struct_archive; @@ -112,6 +123,22 @@ pub trait Handle { 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 pathname(&self) -> &str { let c_str: &CStr = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry())) }; let buf: &[u8] = c_str.to_bytes(); @@ -122,12 +149,27 @@ pub trait Entry { unsafe { ffi::archive_entry_size(self.entry()) } } - fn set_pathname(&mut self, path: PathBuf) -> ArchiveResult<()> { + 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_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()); } - Ok(()) } } diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index ca90e524..6b79b2fc 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -113,7 +113,7 @@ impl Disk { if let Some(entry) = reader.next_header() { if let Some(pfx) = prefix { let path = Path::new(pfx).join(entry.pathname()); - try!(entry.set_pathname(path)); + entry.set_pathname(path); } match self.write_header(entry) { Ok(()) => (), From fc8b870317b28f90f0be2a0b72ed5beeb4ceb176 Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 21 Mar 2016 17:06:57 -0700 Subject: [PATCH 11/19] write prefix for hardlinks on Disk restore --- libraries/libarchive/src/archive.rs | 27 ++++++++++++++++++++++++++- libraries/libarchive/src/writer.rs | 6 +++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs index ff5a1dfb..2a798702 100644 --- a/libraries/libarchive/src/archive.rs +++ b/libraries/libarchive/src/archive.rs @@ -139,6 +139,18 @@ pub trait Entry { } } + 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(); @@ -149,6 +161,12 @@ pub trait Entry { 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 { @@ -165,7 +183,14 @@ pub trait Entry { } } - fn set_pathname(&mut self, path: PathBuf) { + 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()); diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 6b79b2fc..9218aa2f 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -113,7 +113,11 @@ impl Disk { if let Some(entry) = reader.next_header() { if let Some(pfx) = prefix { let path = Path::new(pfx).join(entry.pathname()); - entry.set_pathname(path); + entry.set_pathname(&path); + if entry.hardlink().is_some() { + let path = Path::new(pfx).join(entry.hardlink().unwrap()); + entry.set_link(&path); + } } match self.write_header(entry) { Ok(()) => (), From 5325f1533957342e68e45e6bcecd245efa88f22e Mon Sep 17 00:00:00 2001 From: Jamie Winsor Date: Mon, 21 Mar 2016 17:25:21 -0700 Subject: [PATCH 12/19] version bump 0.1.1 --- libraries/libarchive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libarchive/Cargo.toml b/libraries/libarchive/Cargo.toml index 93c018fc..2d1462e1 100644 --- a/libraries/libarchive/Cargo.toml +++ b/libraries/libarchive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libarchive" -version = "0.1.0" +version = "0.1.1" authors = ["Jamie Winsor "] license = "Apache-2.0" repository = "https://github.com/chef/libarchive-rust" From fa34497ddeebc3df54ff510dda48ce4179b58d73 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 2 Jan 2019 15:57:21 -0800 Subject: [PATCH 13/19] Fix travis badge --- libraries/libarchive/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libarchive/README.md b/libraries/libarchive/README.md index 0336b5d8..f47a7411 100644 --- a/libraries/libarchive/README.md +++ b/libraries/libarchive/README.md @@ -1,6 +1,6 @@ # libarchive-rust -[![Build Status](https://travis-ci.org/reset/libarchive-rust.svg?branch=master)](https://travis-ci.org/reset/libarchive-rust) +[![Build Status](https://travis-ci.org/chef/libarchive-rust.svg?branch=master)](https://travis-ci.org/chef/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) From c7b823dc1052c35cd68cada6713f7f94b43cdd45 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 3 Jan 2019 18:13:37 -0800 Subject: [PATCH 14/19] Remove invalid docs link --- libraries/libarchive/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/libarchive/README.md b/libraries/libarchive/README.md index f47a7411..d66d0221 100644 --- a/libraries/libarchive/README.md +++ b/libraries/libarchive/README.md @@ -5,8 +5,6 @@ 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. From e5d818f8199a55b192dc1bf3e5da944ee7848c2a Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sat, 28 Feb 2026 14:59:54 +1100 Subject: [PATCH 15/19] fix: add send trait to filereader --- libraries/libarchive/src/reader.rs | 45 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs index c913b1c8..da6ad924 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -8,18 +8,19 @@ use std::path::Path; use std::ptr; use std::slice; -use libc::{c_void, ssize_t}; use libarchive3_sys::ffi; +use libc::{c_void, ssize_t}; -use archive::{Entry, ReadCompression, ReadFilter, ReadFormat, Handle}; -use error::{ArchiveResult, ArchiveError}; +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 { +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() { @@ -32,7 +33,7 @@ unsafe extern "C" fn stream_read_callback(handle: *mut ffi::Struct_archive, } } -pub trait Reader : Handle { +pub trait Reader: Handle { fn entry(&mut self) -> &mut ReaderEntry; fn header_position(&self) -> i64 { @@ -68,6 +69,8 @@ pub struct FileReader { entry: ReaderEntry, } +unsafe impl Send for FileReader {} + pub struct StreamReader { handle: *mut ffi::Struct_archive, entry: ReaderEntry, @@ -149,11 +152,13 @@ impl StreamReader { unsafe { let mut pipe = Box::new(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) { + match ffi::archive_read_open( + builder.handle(), + pipe_ptr, + None, + Some(stream_read_callback), + None, + ) { ffi::ARCHIVE_OK => { let reader = StreamReader { handle: builder.handle(), @@ -259,10 +264,12 @@ impl Builder { 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) + 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) }, @@ -363,7 +370,9 @@ impl ReaderEntry { impl Default for ReaderEntry { fn default() -> Self { - ReaderEntry { handle: ptr::null_mut() } + ReaderEntry { + handle: ptr::null_mut(), + } } } From e1f1cbb4daaa7a8d91241e4097bf64896e7b3a53 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 1 Mar 2026 22:18:22 +1100 Subject: [PATCH 16/19] feat: null-ptr fix --- libraries/libarchive/src/error.rs | 8 ++++---- libraries/libarchive/src/reader.rs | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs index 7d22effb..1479f21f 100644 --- a/libraries/libarchive/src/error.rs +++ b/libraries/libarchive/src/error.rs @@ -42,14 +42,14 @@ impl fmt::Display for ArchiveError { } } -impl<'a> From<&'a archive::Handle> for ArchiveError { - fn from(handle: &'a archive::Handle) -> ArchiveError { +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 archive::Handle> for ArchiveResult<()> { - fn from(handle: &'a archive::Handle) -> ArchiveResult<()> { +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/reader.rs b/libraries/libarchive/src/reader.rs index da6ad924..2d56ca13 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -57,7 +57,13 @@ pub trait Reader: Handle { unsafe { match ffi::archive_read_data_block(self.handle(), &mut buff, &mut size, &mut offset) { ffi::ARCHIVE_EOF => Ok(None), - ffi::ARCHIVE_OK => Ok(Some(slice::from_raw_parts(buff as *const u8, size))), + ffi::ARCHIVE_OK => { + if buff != ptr::null() { + Ok(Some(slice::from_raw_parts(buff as *const u8, size))) + } else { + return self.read_block(); + } + } _ => Err(ArchiveError::Sys(self.err_code(), self.err_msg())), } } From 69e5268410e84cbc98c0eb8cd6ae2126f3f6c14e Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 1 Mar 2026 22:21:13 +1100 Subject: [PATCH 17/19] fix: rename package --- libraries/libarchive/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/libarchive/Cargo.toml b/libraries/libarchive/Cargo.toml index 2d1462e1..11e0573a 100644 --- a/libraries/libarchive/Cargo.toml +++ b/libraries/libarchive/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "libarchive" +name = "libarchive-drop" version = "0.1.1" -authors = ["Jamie Winsor "] +authors = ["Jamie Winsor ", "Drop OSS"] license = "Apache-2.0" -repository = "https://github.com/chef/libarchive-rust" +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"] From b1949d952df61b0de43a80f2f746913b1f1d0642 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Mon, 2 Mar 2026 22:11:49 +1100 Subject: [PATCH 18/19] fix: formatting --- libraries/libarchive/src/archive.rs | 2 +- libraries/libarchive/src/error.rs | 2 +- libraries/libarchive/src/lib.rs | 2 +- libraries/libarchive/src/writer.rs | 19 +++++++++++-------- libraries/libarchive/tests/lib.rs | 11 +++++++---- libraries/libarchive/tests/util/path.rs | 9 ++++++++- 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/libraries/libarchive/src/archive.rs b/libraries/libarchive/src/archive.rs index 2a798702..71559923 100644 --- a/libraries/libarchive/src/archive.rs +++ b/libraries/libarchive/src/archive.rs @@ -3,8 +3,8 @@ use std::ffi::{CStr, CString}; use std::path::PathBuf; use std::str; -use libarchive3_sys::ffi; use error::ErrCode; +use libarchive3_sys::ffi; pub enum ReadCompression { All, diff --git a/libraries/libarchive/src/error.rs b/libraries/libarchive/src/error.rs index 1479f21f..4f574257 100644 --- a/libraries/libarchive/src/error.rs +++ b/libraries/libarchive/src/error.rs @@ -1,6 +1,6 @@ +use archive; use std::error; use std::fmt; -use archive; pub type ArchiveResult = Result; diff --git a/libraries/libarchive/src/lib.rs b/libraries/libarchive/src/lib.rs index c24af2f9..8006328e 100644 --- a/libraries/libarchive/src/lib.rs +++ b/libraries/libarchive/src/lib.rs @@ -1,5 +1,5 @@ -extern crate libc; extern crate libarchive3_sys; +extern crate libc; pub mod archive; pub mod error; diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 9218aa2f..5a9627e3 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -1,13 +1,13 @@ use std::default::Default; +use std::ffi::CString; use std::path::Path; use std::ptr; -use std::ffi::CString; use libarchive3_sys::ffi; use archive::{Entry, ExtractOptions, Handle, WriteFilter, WriteFormat}; +use error::{ArchiveError, ArchiveResult}; use reader::{Reader, ReaderEntry}; -use error::{ArchiveResult, ArchiveError}; pub struct Writer { handle: *mut ffi::Struct_archive, @@ -159,14 +159,17 @@ impl Disk { unsafe { loop { - match ffi::archive_read_data_block(reader.handle(), - &mut buff, - &mut size, - &mut offset) { + match ffi::archive_read_data_block( + reader.handle(), + &mut buff, + &mut size, + &mut offset, + ) { ffi::ARCHIVE_EOF => return Ok(size), ffi::ARCHIVE_OK => { - if ffi::archive_write_data_block(self.handle, buff, size, offset) != - ffi::ARCHIVE_OK as isize { + if ffi::archive_write_data_block(self.handle, buff, size, offset) + != ffi::ARCHIVE_OK as isize + { return Err(ArchiveError::from(self as &Handle)); } } diff --git a/libraries/libarchive/tests/lib.rs b/libraries/libarchive/tests/lib.rs index 872e9472..d35c5373 100644 --- a/libraries/libarchive/tests/lib.rs +++ b/libraries/libarchive/tests/lib.rs @@ -2,10 +2,10 @@ extern crate libarchive; pub mod util; -use std::fs::File; use libarchive::archive::{self, ReadFilter, ReadFormat}; use libarchive::reader::{self, Reader}; use libarchive::writer; +use std::fs::File; #[test] fn reading_from_file() { @@ -37,11 +37,14 @@ fn read_archive_from_stream() { Ok(mut reader) => { assert_eq!(reader.header_position(), 0); let writer = writer::Disk::new(); - let count = writer.write(&mut reader, Some("/opt/bldr/fucks")).ok().unwrap(); + let count = writer + .write(&mut reader, Some("/opt/bldr/fucks")) + .ok() + .unwrap(); assert_eq!(count, 14); assert_eq!(reader.header_position(), 1024); assert_eq!(4, 4); - }, + } Err(e) => { println!("{:?}", e); } @@ -92,7 +95,7 @@ fn extracting_a_reader_twice() { println!("{:?}", reader.header_position()); match writer.write(&mut reader, None) { Ok(_) => println!("oops"), - Err(_) => println!("nice") + Err(_) => println!("nice"), } assert_eq!(4, 4) } diff --git a/libraries/libarchive/tests/util/path.rs b/libraries/libarchive/tests/util/path.rs index da579b33..882a2973 100644 --- a/libraries/libarchive/tests/util/path.rs +++ b/libraries/libarchive/tests/util/path.rs @@ -6,7 +6,14 @@ pub fn exe_path() -> PathBuf { } pub fn root() -> PathBuf { - exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests") + exe_path() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("tests") } pub fn fixtures() -> PathBuf { From 52a158bc22054ebbc1b147072c9cabdf0f00568e Mon Sep 17 00:00:00 2001 From: DecDuck Date: Mon, 2 Mar 2026 22:24:47 +1100 Subject: [PATCH 19/19] fix: clippy lints --- libraries/libarchive/src/reader.rs | 21 ++++++++++----------- libraries/libarchive/src/writer.rs | 26 +++++++++++++------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/libarchive/src/reader.rs b/libraries/libarchive/src/reader.rs index 2d56ca13..6211d5f1 100644 --- a/libraries/libarchive/src/reader.rs +++ b/libraries/libarchive/src/reader.rs @@ -1,6 +1,5 @@ use std::any::Any; use std::default::Default; -use std::error::Error; use std::ffi::CString; use std::io::{self, Read}; use std::mem; @@ -26,7 +25,7 @@ unsafe extern "C" fn stream_read_callback( match pipe.read_bytes() { Ok(size) => size as ssize_t, Err(e) => { - let desc = CString::new(e.description()).unwrap(); + 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 } @@ -93,7 +92,7 @@ pub struct ReaderEntry { } struct Pipe { - reader: Box, + reader: Box, buffer: Vec, } @@ -112,7 +111,7 @@ impl Pipe { impl FileReader { pub fn open>(mut builder: Builder, file: T) -> ArchiveResult { - try!(builder.check_consumed()); + 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) { @@ -120,7 +119,7 @@ impl FileReader { builder.consume(); Ok(Self::new(builder.handle())) } - _ => Err(ArchiveError::from(&builder as &Handle)), + _ => Err(ArchiveError::from(&builder as &dyn Handle)), } } } @@ -176,7 +175,7 @@ impl StreamReader { } _ => { builder.consume(); - Err(ArchiveError::from(&builder as &Handle)) + Err(ArchiveError::from(&builder as &dyn Handle)) } } } @@ -245,7 +244,7 @@ impl Builder { }; match result { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } @@ -284,7 +283,7 @@ impl Builder { }; match result { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } @@ -310,17 +309,17 @@ impl Builder { }; match result { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } pub fn open_file>(self, file: T) -> ArchiveResult { - try!(self.check_consumed()); + self.check_consumed()?; FileReader::open(self, file) } pub fn open_stream(self, src: T) -> ArchiveResult { - try!(self.check_consumed()); + self.check_consumed()?; StreamReader::open(self, src) } diff --git a/libraries/libarchive/src/writer.rs b/libraries/libarchive/src/writer.rs index 5a9627e3..ac29d42e 100644 --- a/libraries/libarchive/src/writer.rs +++ b/libraries/libarchive/src/writer.rs @@ -64,7 +64,7 @@ impl Disk { unsafe { match ffi::archive_write_set_bytes_per_block(self.handle, count) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -73,7 +73,7 @@ impl Disk { unsafe { match ffi::archive_write_set_bytes_in_last_block(self.handle, count) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -83,7 +83,7 @@ impl Disk { unsafe { match ffi::archive_write_disk_set_options(self.handle, eopt.flags) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -96,7 +96,7 @@ impl Disk { unsafe { match ffi::archive_write_disk_set_standard_lookup(self.handle) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -131,14 +131,14 @@ impl Disk { } } if write_pending { - bytes += try!(self.write_data(reader)); + bytes += self.write_data(reader)?; write_pending = false; } } unsafe { match ffi::archive_write_finish_entry(self.handle()) { ffi::ARCHIVE_OK => Ok(bytes), - _ => Err(ArchiveError::from(self as &Handle)), + _ => Err(ArchiveError::from(self as &dyn Handle)), } } } @@ -147,7 +147,7 @@ impl Disk { unsafe { match ffi::archive_write_close(self.handle()) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -170,10 +170,10 @@ impl Disk { if ffi::archive_write_data_block(self.handle, buff, size, offset) != ffi::ARCHIVE_OK as isize { - return Err(ArchiveError::from(self as &Handle)); + return Err(ArchiveError::from(self as &dyn Handle)); } } - _ => return Err(ArchiveError::from(reader as &Handle)), + _ => return Err(ArchiveError::from(reader as &dyn Handle)), } } } @@ -183,7 +183,7 @@ impl Disk { unsafe { match ffi::archive_write_header(self.handle, entry.entry()) { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } } @@ -243,7 +243,7 @@ impl Builder { }; match result { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } @@ -277,7 +277,7 @@ impl Builder { }; match result { ffi::ARCHIVE_OK => Ok(()), - _ => ArchiveResult::from(self as &Handle), + _ => ArchiveResult::from(self as &dyn Handle), } } @@ -292,7 +292,7 @@ impl Builder { self.consumed = true; Ok(Writer::new(self.handle)) } - _ => Err(ArchiveError::from(&self as &Handle)), + _ => Err(ArchiveError::from(&self as &dyn Handle)), } } }