use crate::entry::EntryMut;
use crate::error::{Error, Result};
use std::ffi::CString;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReadDiskFlags(i32);
impl ReadDiskFlags {
pub const NONE: ReadDiskFlags = ReadDiskFlags(0);
pub const RESTORE_ATIME: ReadDiskFlags = ReadDiskFlags(0x0001);
pub const HONOR_NODUMP: ReadDiskFlags = ReadDiskFlags(0x0002);
pub const MAC_COPYFILE: ReadDiskFlags = ReadDiskFlags(0x0004);
pub const NO_TRAVERSE_MOUNTS: ReadDiskFlags = ReadDiskFlags(0x0008);
pub const NO_XATTR: ReadDiskFlags = ReadDiskFlags(0x0010);
pub const NO_ACL: ReadDiskFlags = ReadDiskFlags(0x0020);
pub const NO_FFLAGS: ReadDiskFlags = ReadDiskFlags(0x0040);
pub const NO_SPARSE: ReadDiskFlags = ReadDiskFlags(0x0080);
pub fn bits(&self) -> i32 {
self.0
}
}
impl std::ops::BitOr for ReadDiskFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
ReadDiskFlags(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for ReadDiskFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymlinkMode {
Logical,
Physical,
Hybrid,
}
pub struct ReadDisk {
archive: *mut libarchive2_sys::archive,
}
unsafe impl Send for ReadDisk {}
impl ReadDisk {
pub fn new() -> Result<Self> {
unsafe {
let archive = libarchive2_sys::archive_read_disk_new();
if archive.is_null() {
return Err(Error::NullPointer);
}
Ok(ReadDisk { archive })
}
}
pub fn set_symlink_mode(&mut self, mode: SymlinkMode) -> Result<()> {
unsafe {
let ret = match mode {
SymlinkMode::Logical => {
libarchive2_sys::archive_read_disk_set_symlink_logical(self.archive)
}
SymlinkMode::Physical => {
libarchive2_sys::archive_read_disk_set_symlink_physical(self.archive)
}
SymlinkMode::Hybrid => {
libarchive2_sys::archive_read_disk_set_symlink_hybrid(self.archive)
}
};
Error::from_return_code(ret, self.archive)?;
}
Ok(())
}
pub fn set_behavior(&mut self, flags: ReadDiskFlags) -> Result<()> {
unsafe {
Error::from_return_code(
libarchive2_sys::archive_read_disk_set_behavior(self.archive, flags.bits()),
self.archive,
)?;
}
Ok(())
}
pub fn set_standard_lookup(&mut self) -> Result<()> {
unsafe {
Error::from_return_code(
libarchive2_sys::archive_read_disk_set_standard_lookup(self.archive),
self.archive,
)?;
}
Ok(())
}
pub fn open<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_str = path
.as_ref()
.to_str()
.ok_or_else(|| Error::InvalidArgument("Path contains invalid UTF-8".to_string()))?;
let c_path = CString::new(path_str)
.map_err(|_| Error::InvalidArgument("Path contains null byte".to_string()))?;
unsafe {
Error::from_return_code(
libarchive2_sys::archive_read_disk_open(self.archive, c_path.as_ptr()),
self.archive,
)?;
}
Ok(())
}
pub fn next_entry(&mut self) -> Result<Option<EntryMut>> {
unsafe {
let entry_ptr = libarchive2_sys::archive_entry_new();
if entry_ptr.is_null() {
return Err(Error::NullPointer);
}
let ret = libarchive2_sys::archive_read_next_header2(self.archive, entry_ptr);
if ret == libarchive2_sys::ARCHIVE_EOF as i32 {
libarchive2_sys::archive_entry_free(entry_ptr);
return Ok(None);
}
if let Err(e) = Error::from_return_code(ret, self.archive) {
libarchive2_sys::archive_entry_free(entry_ptr);
return Err(e);
}
Ok(Some(EntryMut {
entry: entry_ptr,
owned: true,
}))
}
}
pub fn descend(&mut self) -> Result<()> {
unsafe {
Error::from_return_code(
libarchive2_sys::archive_read_disk_descend(self.archive),
self.archive,
)?;
}
Ok(())
}
pub fn can_descend(&self) -> bool {
unsafe { libarchive2_sys::archive_read_disk_can_descend(self.archive) != 0 }
}
pub fn close(mut self) -> Result<()> {
unsafe {
if !self.archive.is_null() {
Error::from_return_code(
libarchive2_sys::archive_read_close(self.archive),
self.archive,
)?;
libarchive2_sys::archive_read_free(self.archive);
self.archive = std::ptr::null_mut();
}
}
Ok(())
}
}
impl Drop for ReadDisk {
fn drop(&mut self) {
unsafe {
if !self.archive.is_null() {
libarchive2_sys::archive_read_close(self.archive);
libarchive2_sys::archive_read_free(self.archive);
}
}
}
}