use crate::error::{MyError, MyResult};
use crate::finder::count_components;
use crate::fs::entry::{Entry, EntryResult};
use crate::fs::file::Signature;
use crate::fs::flags::FileFlags;
use crate::util::file;
#[cfg(windows)]
use crate::util::version;
use crate::zip::parent::ZipParent;
use crc32fast::Hasher;
use flate2::read::GzDecoder;
use path_clean::PathClean;
use std::cell::RefCell;
use std::cmp;
use std::ffi::OsStr;
use std::fs::File;
use std::io::Read;
use std::ops::DerefMut;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use tar::{Archive, EntryType};
#[cfg(unix)]
use uzers::{gid_t, uid_t};
struct TarEntry<'a, R: Read> {
tar_entry: RefCell<tar::Entry<'a, R>>,
tar_buffer: RefCell<Vec<u8>>,
file_path: PathBuf,
file_depth: usize,
inner_path: PathBuf,
inner_depth: usize,
}
impl<'a, R: Read> TarEntry<'a, R> {
fn new(
tar_entry: tar::Entry<'a, R>,
zip_parent: &ZipParent,
) -> Self {
let inner_path = tar_entry.path().unwrap_or_default().clean();
let tar_entry = RefCell::new(tar_entry);
let tar_buffer = RefCell::new(Vec::new());
let inner_depth = count_components(&inner_path);
let file_depth = zip_parent.depth() + inner_depth;
let file_path = zip_parent.path().join(&inner_path);
Self {
tar_entry,
tar_buffer,
file_path,
file_depth,
inner_path,
inner_depth,
}
}
}
impl<'a, R: Read> Entry for TarEntry<'a, R> {
fn file_path(&self) -> &Path {
&self.file_path
}
fn file_name(&self) -> &OsStr {
self.file_path.file_name().unwrap_or_default()
}
fn file_depth(&self) -> usize {
self.file_depth
}
fn inner_path(&self) -> Option<&Path> {
Some(&self.inner_path)
}
fn inner_depth(&self) -> Option<usize> {
Some(self.inner_depth)
}
fn file_flags(&self) -> FileFlags {
let entry = self.tar_entry.borrow();
match entry.header().entry_type() {
EntryType::Regular => FileFlags::File,
EntryType::Directory => FileFlags::Dir,
EntryType::Link | EntryType::Symlink => FileFlags::Link,
_ => FileFlags::Other,
}
}
fn read_crc(&self) -> u32 {
let mut entry = self.tar_entry.borrow_mut();
let mut buffer = self.tar_buffer.borrow_mut();
if entry.read_to_end(&mut buffer).is_ok() {
let mut hasher = Hasher::new();
hasher.update(&buffer);
hasher.finalize()
} else {
0
}
}
fn read_sig(&self) -> Option<Signature> {
let mut entry = self.tar_entry.borrow_mut();
let mut buffer = self.tar_buffer.borrow_mut();
if let Ok(count) = file::read_until(entry.deref_mut(), buffer.deref_mut(), 4) {
if count > 0 {
let mut data = [0; 4];
let count = cmp::min(count, 4);
data.copy_from_slice(&buffer[..count]);
return Some(data);
}
}
None
}
#[cfg(windows)]
fn read_version(&self) -> Option<String> {
if version::inner::test_extension(&self.file_path) {
let mut entry = self.tar_entry.borrow_mut();
let mut buffer = self.tar_buffer.borrow_mut();
if entry.read_to_end(&mut buffer).is_ok() {
return version::inner::query_buffer(&mut buffer);
}
}
None
}
fn read_link(&self) -> MyResult<Option<PathBuf>> {
let entry = self.tar_entry.borrow();
let link = entry.header().link_name()?;
let link = link.map(|link| link.to_path_buf());
Ok(link)
}
fn copy_metadata(&self, _other: &dyn Entry) {
}
fn reset_metadata(&self) {
}
fn file_mode(&self) -> u32 {
let entry = self.tar_entry.borrow();
entry.header().mode().unwrap_or_default()
}
#[cfg(unix)]
fn owner_uid(&self) -> uid_t {
let entry = self.tar_entry.borrow();
entry.header().uid().unwrap_or_default() as uid_t
}
#[cfg(unix)]
fn owner_gid(&self) -> gid_t {
let entry = self.tar_entry.borrow();
entry.header().gid().unwrap_or_default() as gid_t
}
fn file_size(&self) -> u64 {
let entry = self.tar_entry.borrow();
entry.size()
}
fn file_time(&self) -> SystemTime {
let entry = self.tar_entry.borrow();
let time = entry.header().mtime().unwrap_or_default();
SystemTime::UNIX_EPOCH + Duration::from_secs(time)
}
}
pub fn walk_entries<F: Fn(EntryResult)>(
zip_parent: &ZipParent,
want_gzip: bool,
function: &F,
) -> MyResult<()> {
let tar_path = zip_parent.path();
match File::open(tar_path) {
Ok(tar_file) => {
if want_gzip {
let tar_file = GzDecoder::new(tar_file);
let mut tar_archive = Archive::new(tar_file);
walk_archive(zip_parent, &mut tar_archive, function)
} else {
let mut tar_archive = Archive::new(tar_file);
walk_archive(zip_parent, &mut tar_archive, function)
}
}
Err(error) => {
function(Err(MyError::from((error, tar_path))));
Ok(())
}
}
}
fn walk_archive<F: Fn(EntryResult), R: Read>(
zip_parent: &ZipParent,
tar_archive: &mut Archive<R>,
function: &F,
) -> MyResult<()> {
match tar_archive.entries() {
Ok(mut entries) => {
while let Some(entry) = entries.next() {
match entry {
Ok(entry) => {
if valid_entry(&entry) {
let entry = TarEntry::new(entry, zip_parent);
function(Ok(&entry));
}
}
Err(error) => {
let path = zip_parent.path();
function(Err(MyError::from((error, path))));
}
}
}
}
Err(error) => {
let path = zip_parent.path();
function(Err(MyError::from((error, path))));
}
}
Ok(())
}
fn valid_entry<R: Read>(entry: &tar::Entry<R>) -> bool {
if let Ok(path) = entry.path() {
path.file_name().is_some()
} else {
false
}
}