feat: start forking libarchive, no biggy
parent
51cda94c1a
commit
5743f4a3a5
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
'server'
|
||||
'server',
|
||||
'libarchive'
|
||||
]
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "libarchive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = ">= 0.2.0"
|
||||
libarchive3-sys = "0.1"
|
|
@ -0,0 +1,400 @@
|
|||
use std::ffi::{CStr, CString};
|
||||
use std::str;
|
||||
use std::{default::Default, path::Path};
|
||||
|
||||
use crate::error::ErrCode;
|
||||
use libarchive3_sys::ffi;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ReadCompression {
|
||||
All,
|
||||
Bzip2,
|
||||
Compress,
|
||||
Gzip,
|
||||
Lzip,
|
||||
Lzma,
|
||||
None,
|
||||
Program(String),
|
||||
Rpm,
|
||||
Uu,
|
||||
Xz,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ReadFormat {
|
||||
SevenZip,
|
||||
All,
|
||||
Ar,
|
||||
Cab,
|
||||
Cpio,
|
||||
Empty,
|
||||
Gnutar,
|
||||
Iso9660,
|
||||
Lha,
|
||||
Mtree,
|
||||
Rar,
|
||||
Raw,
|
||||
Tar,
|
||||
Xar,
|
||||
Zip,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ReadFilter {
|
||||
All,
|
||||
Bzip2,
|
||||
Compress,
|
||||
Gzip,
|
||||
Grzip,
|
||||
Lrzip,
|
||||
Lzip,
|
||||
Lzma,
|
||||
Lzop,
|
||||
None,
|
||||
Program(String),
|
||||
ProgramSignature(String, Option<extern "C" fn() -> ()>, usize),
|
||||
Rpm,
|
||||
Uu,
|
||||
Xz,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum WriteFormat {
|
||||
SevenZip,
|
||||
ArBsd,
|
||||
ArSvr4,
|
||||
Cpio,
|
||||
CpioNewc,
|
||||
Gnutar,
|
||||
Iso9660,
|
||||
Mtree,
|
||||
MtreeClassic,
|
||||
Pax,
|
||||
PaxRestricted,
|
||||
Shar,
|
||||
SharDump,
|
||||
Ustar,
|
||||
V7tar,
|
||||
Xar,
|
||||
Zip,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum WriteFilter {
|
||||
B64Encode,
|
||||
Bzip2,
|
||||
Compress,
|
||||
Grzip,
|
||||
Gzip,
|
||||
Lrzip,
|
||||
Lzip,
|
||||
Lzma,
|
||||
Lzop,
|
||||
None,
|
||||
Program(String),
|
||||
UuEncode,
|
||||
Xz,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum FileType {
|
||||
BlockDevice,
|
||||
SymbolicLink,
|
||||
Socket,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
NamedPipe,
|
||||
Mount,
|
||||
RegularFile,
|
||||
}
|
||||
|
||||
/// The trait representing a handle to a libarchive archive.
|
||||
///
|
||||
/// # Safety
|
||||
/// Implementors of this trait **must** call the function to free the handle before they go out of
|
||||
/// scope. I'd recommend using the `Drop` trait for this.
|
||||
pub trait Handle {
|
||||
/// Returns a *const to the interal `archive` c struct
|
||||
///
|
||||
/// # Safety
|
||||
/// This pointer dangles once the `archive` has been deallocated.
|
||||
unsafe fn handle(&self) -> *const ffi::Struct_archive;
|
||||
|
||||
/// Returns a *mut to the interal `archive` c struct
|
||||
///
|
||||
/// # Safety
|
||||
/// This pointer dangles once the `archive` has been deallocated.
|
||||
unsafe fn handle_mut(&mut self) -> *mut ffi::Struct_archive;
|
||||
|
||||
/// Get the error code from the most recent libarchive function call
|
||||
fn err_code(&self) -> ErrCode {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
let code = unsafe { ffi::archive_errno(self.handle() as *mut _) };
|
||||
ErrCode(code)
|
||||
}
|
||||
|
||||
/// Get a str containing the error message from the most recent libarchive function call
|
||||
fn err_msg(&self) -> &str {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
let c_str = unsafe { CStr::from_ptr(ffi::archive_error_string(self.handle() as *mut _)) };
|
||||
c_str.to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait representing an `archive_entry`
|
||||
///
|
||||
/// # Safety
|
||||
/// See [`Handle`](trait.Handle.html)
|
||||
pub trait Entry {
|
||||
/// Gives a *const to the internal `archive_entry` c struct
|
||||
///
|
||||
/// # Safety
|
||||
/// The pointer returned here points into the `archive` struct. As lifetimes are not capable of
|
||||
/// expressing self-referential structs (yet?) this must all be unsafe. This pointer dangles
|
||||
/// if the `archive` struct held by the implementor of `Handle` is deallocated.
|
||||
///
|
||||
/// Most (all?) of the functions in libarchive take `T*`, not `const T*`, so this `*const` will
|
||||
/// probably have to be cast to a `*mut` to use it. Do not pass that `*mut` to a function that may
|
||||
/// modify it, as that is UB.
|
||||
unsafe fn entry(&self) -> *const ffi::Struct_archive_entry;
|
||||
|
||||
/// Gives a *mut to the internal `archive_entry` c struct.
|
||||
///
|
||||
/// # Safety
|
||||
/// The pointer returned here points into the archive struct. As lifetimes are not capable of
|
||||
/// expressing self-referential structs (yet?) this must all be unsafe. This pointer dangles
|
||||
/// if the `archive` struct held by the implementor of `Handle` is deallocated.
|
||||
unsafe fn entry_mut(&mut self) -> *mut ffi::Struct_archive_entry;
|
||||
|
||||
/// Get the filetype of the entry.
|
||||
fn filetype(&self) -> FileType {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
match unsafe { ffi::archive_entry_filetype(self.entry() as *mut _) } 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),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the location of a hardlink, if it exists
|
||||
fn hardlink(&self) -> Option<&str> {
|
||||
let c_str: &CStr = unsafe {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
let ptr = ffi::archive_entry_hardlink(self.entry() as *mut _);
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(ptr)
|
||||
};
|
||||
c_str.to_str().ok()
|
||||
//let buf: &[u8] = c_str.to_bytes();
|
||||
//Some(str::from_utf8(buf).unwrap())
|
||||
}
|
||||
|
||||
/// Get the pathname of the file the entry refers to
|
||||
fn pathname(&self) -> &str {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
let c_str: &CStr =
|
||||
unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry() as *mut _)) };
|
||||
let buf: &[u8] = c_str.to_bytes();
|
||||
str::from_utf8(buf).unwrap()
|
||||
}
|
||||
|
||||
/// Get the mode of the file
|
||||
fn mode(&self) -> u32 {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { ffi::archive_entry_mode(self.entry() as *mut _) }
|
||||
}
|
||||
|
||||
/// Get the id of the group that owns the file
|
||||
fn gid(&self) -> i64 {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { ffi::archive_entry_gid(self.entry() as *mut _) }
|
||||
}
|
||||
|
||||
/// Get the id of the user that owns the file
|
||||
fn uid(&self) -> i64 {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { ffi::archive_entry_uid(self.entry() as *mut _) }
|
||||
}
|
||||
|
||||
/// Get the `mtime` of the file (The time it was last modified) as a unix timestamp
|
||||
fn time(&self) -> i64 {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { ffi::archive_entry_mtime(self.entry() as *mut _) }
|
||||
}
|
||||
|
||||
/// Get the size of the file, in bytes
|
||||
fn size(&self) -> i64 {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { ffi::archive_entry_size(self.entry() as *mut _) }
|
||||
}
|
||||
|
||||
/// Get the destination of a symlink, if one exists
|
||||
fn symlink(&self) -> Option<&str> {
|
||||
match self.filetype() {
|
||||
FileType::SymbolicLink => {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
unsafe { CStr::from_ptr(ffi::archive_entry_symlink(self.entry() as *mut _)) }
|
||||
.to_str()
|
||||
.ok()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the filetype of the file.
|
||||
fn set_filetype(&mut self, file_type: FileType) {
|
||||
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,
|
||||
};
|
||||
unsafe {
|
||||
ffi::archive_entry_set_filetype(self.entry_mut(), file_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the destination of a link
|
||||
fn set_link<P: AsRef<Path>>(&mut self, path: P) {
|
||||
unsafe {
|
||||
let c_str = CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
||||
ffi::archive_entry_set_link(self.entry_mut(), c_str.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the pathname of the file
|
||||
fn set_pathname<P: AsRef<Path>>(&mut self, path: P) {
|
||||
unsafe {
|
||||
let c_str = CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
||||
ffi::archive_entry_set_pathname(self.entry_mut(), c_str.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ExtractOption {
|
||||
// The user and group IDs should be set on the restored file. By default, the user and group
|
||||
// IDs are not restored.
|
||||
Owner,
|
||||
// Full permissions (including SGID, SUID, and sticky bits) should be restored exactly as
|
||||
// specified, without obeying the current umask. Note that SUID and SGID bits can only be
|
||||
// restored if the user and group ID of the object on disk are correct. If
|
||||
// `ExtractOption::Owner` is not specified, then SUID and SGID bits will only be restored if
|
||||
// the default user and group IDs of newly-created objects on disk happen to match those
|
||||
// specified in the archive entry. By default, only basic permissions are restored, and umask
|
||||
// is obeyed.
|
||||
Permissions,
|
||||
// The timestamps (mtime, ctime, and atime) should be restored. By default, they are ignored.
|
||||
// Note that restoring of atime is not currently supported.
|
||||
Time,
|
||||
// Existing files on disk will not be overwritten. By default, existing regular files are
|
||||
// truncated and overwritten; existing directories will have their permissions updated; other
|
||||
// pre-existing objects are unlinked and recreated from scratch.
|
||||
NoOverwrite,
|
||||
// Existing files on disk will be unlinked before any attempt to create them. In some cases,
|
||||
// this can prove to be a significant performance improvement. By default, existing files are
|
||||
// truncated and rewritten, but the file is not recreated. In particular, the default behavior
|
||||
// does not break existing hard links.
|
||||
Unlink,
|
||||
// Attempt to restore ACLs. By default, extended ACLs are ignored.
|
||||
ACL,
|
||||
// Attempt to restore extended file flags. By default, file flags are ignored.
|
||||
FFlags,
|
||||
// Attempt to restore POSIX.1e extended attributes. By default, they are ignored.
|
||||
XAttr,
|
||||
// Refuse to extract any object whose final location would be altered by a symlink on disk.
|
||||
// This is intended to help guard against a variety of mischief caused by archives that
|
||||
// (deliberately or otherwise) extract files outside of the current directory. The default is
|
||||
// not to perform this check. If ARCHIVE_EXTRACT_UNLINK is specified together with this option,
|
||||
// the library will remove any intermediate symlinks it finds and return an error only if such
|
||||
// symlink could not be removed.
|
||||
SecureSymlinks,
|
||||
// Refuse to extract a path that contains a `..` element anywhere within it. The default is to
|
||||
// not refuse such paths. Note that paths ending in `..` always cause an error, regardless of
|
||||
// this flag.
|
||||
SecureNoDotDot,
|
||||
// Default: Create parent directories as needed
|
||||
NoAutoDir,
|
||||
// Default: Overwrite files, even if one on disk is newer
|
||||
NoOverwriteNewer,
|
||||
// Scan data for blocks of NUL bytes and try to recreate them with holes. This results in
|
||||
// sparse files, independent of whether the archive format supports or uses them.
|
||||
Sparse,
|
||||
// Default: Do not restore Mac extended metadata
|
||||
// This has no effect except on Mac OS
|
||||
MacMetadata,
|
||||
// Default: Use HFS+ compression if it was compressed
|
||||
// This has no effect except on Mac OS v10.6 or later
|
||||
NoHFSCompression,
|
||||
// Default: Do not use HFS+ compression if it was not compressed
|
||||
// This has no effect except on Mac OS v10.6 or later
|
||||
HFSCompressionForced,
|
||||
// Default: Do not reject entries with absolute paths
|
||||
SecureNoAbsolutePaths,
|
||||
// Default: Do not clear no-change flags when unlinking object
|
||||
ClearNoChangeFFlags,
|
||||
}
|
||||
|
||||
pub struct ExtractOptions {
|
||||
pub flags: i32,
|
||||
}
|
||||
|
||||
impl ExtractOptions {
|
||||
pub fn new() -> Self {
|
||||
ExtractOptions::default()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, opt: ExtractOption) -> &mut Self {
|
||||
let flag = match opt {
|
||||
ExtractOption::Owner => ffi::ARCHIVE_EXTRACT_OWNER,
|
||||
ExtractOption::Permissions => ffi::ARCHIVE_EXTRACT_PERM,
|
||||
ExtractOption::Time => ffi::ARCHIVE_EXTRACT_TIME,
|
||||
ExtractOption::NoOverwrite => ffi::ARCHIVE_EXTRACT_NO_OVERWRITE,
|
||||
ExtractOption::Unlink => ffi::ARCHIVE_EXTRACT_UNLINK,
|
||||
ExtractOption::ACL => ffi::ARCHIVE_EXTRACT_ACL,
|
||||
ExtractOption::FFlags => ffi::ARCHIVE_EXTRACT_FFLAGS,
|
||||
ExtractOption::XAttr => ffi::ARCHIVE_EXTRACT_XATTR,
|
||||
ExtractOption::SecureSymlinks => ffi::ARCHIVE_EXTRACT_SECURE_SYMLINKS,
|
||||
ExtractOption::SecureNoDotDot => ffi::ARCHIVE_EXTRACT_SECURE_NODOTDOT,
|
||||
ExtractOption::NoAutoDir => ffi::ARCHIVE_EXTRACT_NO_AUTODIR,
|
||||
ExtractOption::NoOverwriteNewer => ffi::ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER,
|
||||
ExtractOption::Sparse => ffi::ARCHIVE_EXTRACT_SPARSE,
|
||||
ExtractOption::MacMetadata => ffi::ARCHIVE_EXTRACT_MAC_METADATA,
|
||||
ExtractOption::NoHFSCompression => ffi::ARCHIVE_EXTRACT_NO_HFS_COMPRESSION,
|
||||
ExtractOption::HFSCompressionForced => ffi::ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED,
|
||||
ExtractOption::SecureNoAbsolutePaths => ffi::ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS,
|
||||
ExtractOption::ClearNoChangeFFlags => ffi::ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS,
|
||||
};
|
||||
self.flags |= flag;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExtractOptions {
|
||||
fn default() -> ExtractOptions {
|
||||
ExtractOptions { flags: 0 }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
use crate::archive;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ArchiveError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrCode(pub i32);
|
||||
|
||||
impl fmt::Display for ErrCode {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ArchiveError {
|
||||
Consumed,
|
||||
HeaderPosition,
|
||||
Sys(ErrCode, String),
|
||||
}
|
||||
|
||||
impl error::Error for ArchiveError {}
|
||||
|
||||
impl fmt::Display for ArchiveError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ArchiveError::Consumed => write!(fmt, "Builder already consumed"),
|
||||
ArchiveError::HeaderPosition => write!(fmt, "Header position expected to be 0"),
|
||||
ArchiveError::Sys(ref code, ref msg) => {
|
||||
write!(fmt, "{} (libarchive err_code={})", msg, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a dyn archive::Handle> for ArchiveError {
|
||||
fn from(handle: &'a dyn archive::Handle) -> ArchiveError {
|
||||
ArchiveError::Sys(handle.err_code(), handle.err_msg().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a dyn archive::Handle> for Result<()> {
|
||||
fn from(handle: &'a dyn archive::Handle) -> Result<()> {
|
||||
match handle.err_code() {
|
||||
ErrCode(0) => Ok(()),
|
||||
_ => Err(ArchiveError::from(handle)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
pub mod archive;
|
||||
pub mod error;
|
||||
pub mod read;
|
||||
|
||||
pub use error::Result;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
use crate::archive::{Handle, ReadCompression, ReadFilter, ReadFormat};
|
||||
use libarchive3_sys::ffi;
|
||||
use crate::error::{ArchiveError, Result};
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use super::file::ArchiveFile;
|
||||
use crate::read::Archive;
|
||||
|
||||
/// A struct that allows configuration of the reader before it is created.
|
||||
pub struct Builder {
|
||||
handle: *mut ffi::Struct_archive,
|
||||
consumed: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Self {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
/// Enable support for a given compression method.
|
||||
pub fn support_compression(&mut self, compression: ReadCompression) -> Result<()> {
|
||||
#[rustfmt::skip]
|
||||
let result = match compression {
|
||||
ReadCompression::All => unsafe { ffi::archive_read_support_compression_all(self.handle_mut()) },
|
||||
ReadCompression::Bzip2 => unsafe { ffi::archive_read_support_compression_bzip2(self.handle_mut()) },
|
||||
ReadCompression::Compress => unsafe { ffi::archive_read_support_compression_compress(self.handle_mut()) },
|
||||
ReadCompression::Gzip => unsafe { ffi::archive_read_support_compression_gzip(self.handle_mut()) },
|
||||
ReadCompression::Lzip => unsafe { ffi::archive_read_support_compression_lzip(self.handle_mut()) },
|
||||
ReadCompression::Lzma => unsafe { ffi::archive_read_support_compression_lzma(self.handle_mut()) },
|
||||
ReadCompression::None => unsafe { ffi::archive_read_support_compression_none(self.handle_mut()) },
|
||||
ReadCompression::Rpm => unsafe { ffi::archive_read_support_compression_rpm(self.handle_mut()) },
|
||||
ReadCompression::Uu => unsafe { ffi::archive_read_support_compression_uu(self.handle_mut()) },
|
||||
ReadCompression::Xz => unsafe { ffi::archive_read_support_compression_xz(self.handle_mut()) },
|
||||
ReadCompression::Program(prog) => {
|
||||
let c_prog = CString::new(prog).unwrap();
|
||||
unsafe {
|
||||
ffi::archive_read_support_compression_program(
|
||||
self.handle_mut(),
|
||||
c_prog.as_ptr(),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
match result {
|
||||
ffi::ARCHIVE_OK => Ok(()),
|
||||
_ => Result::from(self as &dyn Handle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable support for a given filter
|
||||
pub fn support_filter(&mut self, filter: ReadFilter) -> Result<()> {
|
||||
#[rustfmt::skip]
|
||||
let result = match filter {
|
||||
ReadFilter::All => unsafe { ffi::archive_read_support_filter_all(self.handle_mut()) },
|
||||
ReadFilter::Bzip2 => unsafe { ffi::archive_read_support_filter_bzip2(self.handle_mut()) },
|
||||
ReadFilter::Compress => unsafe { ffi::archive_read_support_filter_compress(self.handle_mut()) },
|
||||
ReadFilter::Grzip => unsafe { ffi::archive_read_support_filter_grzip(self.handle_mut()) },
|
||||
ReadFilter::Gzip => unsafe { ffi::archive_read_support_filter_gzip(self.handle_mut()) },
|
||||
ReadFilter::Lrzip => unsafe { ffi::archive_read_support_filter_lrzip(self.handle_mut()) },
|
||||
ReadFilter::Lzip => unsafe { ffi::archive_read_support_filter_lzip(self.handle_mut()) },
|
||||
ReadFilter::Lzma => unsafe { ffi::archive_read_support_filter_lzma(self.handle_mut()) },
|
||||
ReadFilter::Lzop => unsafe { ffi::archive_read_support_filter_lzop(self.handle_mut()) },
|
||||
ReadFilter::None => unsafe { ffi::archive_read_support_filter_none(self.handle_mut()) },
|
||||
ReadFilter::Rpm => unsafe { ffi::archive_read_support_filter_rpm(self.handle_mut()) },
|
||||
ReadFilter::Uu => unsafe { ffi::archive_read_support_filter_uu(self.handle_mut()) },
|
||||
ReadFilter::Xz => unsafe { ffi::archive_read_support_filter_xz(self.handle_mut()) },
|
||||
ReadFilter::Program(prog) => {
|
||||
let c_prog = CString::new(prog).unwrap();
|
||||
unsafe {
|
||||
ffi::archive_read_support_filter_program(self.handle_mut(), 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_mut(),
|
||||
c_prog.as_ptr(),
|
||||
mem::transmute(cb),
|
||||
size,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
match result {
|
||||
ffi::ARCHIVE_OK => Ok(()),
|
||||
_ => Result::from(self as &dyn Handle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable support for a given format.
|
||||
pub fn support_format(&mut self, format: ReadFormat) -> Result<()> {
|
||||
// SAFETY: Casting to *mut because these c functions take T* not const T*. They do not
|
||||
// modify the pointer, so this is sound.
|
||||
#[rustfmt::skip]
|
||||
let result = match format {
|
||||
ReadFormat::SevenZip => unsafe { ffi::archive_read_support_format_7zip(self.handle_mut()) },
|
||||
ReadFormat::All => unsafe { ffi::archive_read_support_format_all(self.handle_mut()) },
|
||||
ReadFormat::Ar => unsafe { ffi::archive_read_support_format_ar(self.handle_mut()) },
|
||||
ReadFormat::Cab => unsafe { ffi::archive_read_support_format_cab(self.handle_mut()) },
|
||||
ReadFormat::Cpio => unsafe { ffi::archive_read_support_format_cpio(self.handle_mut()) },
|
||||
ReadFormat::Empty => unsafe { ffi::archive_read_support_format_empty(self.handle_mut()) },
|
||||
ReadFormat::Gnutar => unsafe { ffi::archive_read_support_format_gnutar(self.handle_mut()) },
|
||||
ReadFormat::Iso9660 => unsafe { ffi::archive_read_support_format_iso9660(self.handle_mut()) },
|
||||
ReadFormat::Lha => unsafe { ffi::archive_read_support_format_lha(self.handle_mut()) },
|
||||
ReadFormat::Mtree => unsafe { ffi::archive_read_support_format_mtree(self.handle_mut()) },
|
||||
ReadFormat::Rar => unsafe { ffi::archive_read_support_format_rar(self.handle_mut()) },
|
||||
ReadFormat::Raw => unsafe { ffi::archive_read_support_format_raw(self.handle_mut()) },
|
||||
ReadFormat::Tar => unsafe { ffi::archive_read_support_format_tar(self.handle_mut()) },
|
||||
ReadFormat::Xar => unsafe { ffi::archive_read_support_format_xar(self.handle_mut()) },
|
||||
ReadFormat::Zip => unsafe { ffi::archive_read_support_format_zip(self.handle_mut()) },
|
||||
};
|
||||
match result {
|
||||
ffi::ARCHIVE_OK => Ok(()),
|
||||
_ => Result::from(self as &dyn Handle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a file with this builder, consuming it and returning a `FileReader`
|
||||
pub fn open_file<T: AsRef<Path>>(self, file: T) -> Result<ArchiveFile> {
|
||||
self.check_consumed()?;
|
||||
ArchiveFile::open(self, file)
|
||||
}
|
||||
|
||||
/// Open a stream with this builder, consuming it and returning a `StreamReader`
|
||||
// pub fn open_stream<T: Any + Read>(self, src: T) -> Result<StreamReader> {
|
||||
// self.check_consumed()?;
|
||||
// StreamReader::open(self, src)
|
||||
// }
|
||||
|
||||
pub fn check_consumed(&self) -> Result<()> {
|
||||
if self.consumed {
|
||||
Err(ArchiveError::Consumed)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume(&mut self) {
|
||||
self.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle for Builder {
|
||||
unsafe fn handle(&self) -> *const ffi::Struct_archive {
|
||||
self.handle as *const _
|
||||
}
|
||||
|
||||
unsafe fn handle_mut(&mut 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,
|
||||
consumed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
use libarchive3_sys::ffi;
|
||||
use super::builder::Builder;
|
||||
use crate::read::{Handle, Archive};
|
||||
use std::ffi::CString;
|
||||
use std::path::Path;
|
||||
use crate::error::ArchiveError;
|
||||
|
||||
const BLOCK_SIZE: usize = 10240;
|
||||
|
||||
pub struct ArchiveFile {
|
||||
handle: *mut ffi::Struct_archive,
|
||||
// entry: ReaderEntry,
|
||||
}
|
||||
|
||||
impl Handle for ArchiveFile {
|
||||
unsafe fn handle(&self) -> *const ffi::Struct_archive {
|
||||
self.handle as *const _
|
||||
}
|
||||
|
||||
unsafe fn handle_mut(&mut self) -> *mut ffi::Struct_archive {
|
||||
self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ArchiveFile {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::archive_read_free(self.handle_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Archive for ArchiveFile {
|
||||
fn new(handle: *mut ffi::Struct_archive) -> Self {
|
||||
Self {
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
fn open<P: AsRef<Path>>(mut builder: Builder, path: P) -> crate::Result<Self> {
|
||||
builder.check_consumed()?;
|
||||
let c_file = CString::new(path.as_ref().to_string_lossy().as_bytes()).unwrap();
|
||||
unsafe {
|
||||
match ffi::archive_read_open_filename(builder.handle_mut(), c_file.as_ptr(), BLOCK_SIZE)
|
||||
{
|
||||
ffi::ARCHIVE_OK => {
|
||||
builder.consume();
|
||||
Ok(Self::new(builder.handle_mut()))
|
||||
}
|
||||
_ => Err(ArchiveError::from(&builder as &dyn Handle)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
mod builder;
|
||||
mod file;
|
||||
|
||||
use crate::archive::Handle;
|
||||
use builder::Builder;
|
||||
use std::path::Path;
|
||||
use libarchive3_sys::ffi;
|
||||
|
||||
// Represents a read view of an archive
|
||||
pub trait Archive: Handle + Sized {
|
||||
fn new(handle: *mut ffi::Struct_archive) -> Self;
|
||||
|
||||
fn open<P: AsRef<Path>>(builder: Builder, path: P) -> crate::Result<Self>;
|
||||
// entries
|
||||
}
|
|
@ -8,6 +8,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
axum = "0.6.18"
|
||||
futures = "0.3.28"
|
||||
libarchive = { path = "../libarchive" }
|
||||
tokio = { version = "1.29.1", features = ["full"] }
|
||||
tokio-util = { version = "0.7.8", features = ["io"] }
|
||||
tower = { version = "0.4.13", features = ["make"] }
|
||||
|
|
Loading…
Reference in New Issue