feat(list): support list and decompress 7z files with password

This commit is contained in:
ttyS3 2024-03-24 05:36:53 +00:00 committed by João Marcos
parent 512d2445b2
commit 75e16510df
5 changed files with 358 additions and 367 deletions

663
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ num_cpus = "1.16.0"
once_cell = "1.19.0"
rayon = "1.10.0"
same-file = "1.0.6"
sevenz-rust = { version = "0.6.1", features = ["compress"] }
sevenz-rust = { version = "0.6.1", features = ["compress", "aes256"] }
snap = "1.1.1"
tar = "0.4.41"
tempfile = "3.10.1"

View File

@ -5,9 +5,13 @@ use std::{
io::{self, Read, Seek, Write},
path::{Path, PathBuf},
};
use bstr::ByteSlice;
use fs_err as fs;
use same_file::Handle;
use sevenz_rust::SevenZArchiveEntry;
use unrar::Archive;
use zstd::zstd_safe::WriteBuf;
use crate::{
error::FinalError,
@ -17,6 +21,7 @@ use crate::{
Bytes, EscapedPathDisplay, FileVisibilityPolicy,
},
};
use crate::list::FileInArchive;
pub fn compress_sevenz<W>(
files: &[PathBuf],
@ -96,12 +101,13 @@ where
Ok(bytes)
}
pub fn decompress_sevenz<R>(reader: R, output_path: &Path, quiet: bool) -> crate::Result<usize>
pub fn decompress_sevenz<R>(reader: R, output_path: &Path, password: Option<impl AsRef<[u8]>>, quiet: bool) -> crate::Result<usize>
where
R: Read + Seek,
{
let mut count: usize = 0;
sevenz_rust::decompress_with_extract_fn(reader, output_path, |entry, reader, path| {
let entry_extract_fn = |entry: &SevenZArchiveEntry, reader: &mut dyn Read, path: &PathBuf| {
count += 1;
// Manually handle writing all files from 7z archive, due to library exluding empty files
use std::io::BufWriter;
@ -146,11 +152,42 @@ where
Some(ft::FileTime::from_system_time(entry.last_modified_date().into())),
Some(ft::FileTime::from_system_time(entry.creation_date().into())),
)
.unwrap_or_default();
.unwrap_or_default();
}
Ok(true)
})?;
};
let password = password.as_ref().map(|p| p.as_ref());
match password {
Some(password) => sevenz_rust::decompress_with_extract_fn_and_password(reader, output_path, sevenz_rust::Password::from(password.to_str().unwrap()), entry_extract_fn)?,
None => sevenz_rust::decompress_with_extract_fn(reader, output_path, entry_extract_fn)?,
};
Ok(count)
}
/// List contents of `archive_path`, returning a vector of archive entries
pub fn list_archive(archive_path: &Path, password: Option<impl AsRef<[u8]>>) -> impl Iterator<Item = crate::Result<FileInArchive>>
{
let reader = fs::File::open(archive_path).unwrap();
let password = password.as_ref().map(|p| p.as_ref());
let mut files = Vec::new();
let entry_extract_fn = |entry: &SevenZArchiveEntry, _: &mut dyn Read, _: &PathBuf| {
files.push(Ok(FileInArchive {
path: entry.name().into(),
is_dir: entry.is_directory(),
}));
Ok(true)
};
match password {
Some(password) => sevenz_rust::decompress_with_extract_fn_and_password(reader, ".", sevenz_rust::Password::from(password.to_str().unwrap()), entry_extract_fn).unwrap(),
None => sevenz_rust::decompress_with_extract_fn(reader, ".", entry_extract_fn).unwrap(),
};
files.into_iter()
}

View File

@ -206,7 +206,7 @@ pub fn decompress_file(
io::copy(&mut reader, &mut vec)?;
if let ControlFlow::Continue(files) = smart_unpack(
|output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, quiet),
|output_dir| crate::archive::sevenz::decompress_sevenz(io::Cursor::new(vec), output_dir, password, quiet),
output_dir,
&output_file_path,
question_policy,

View File

@ -2,6 +2,7 @@ use std::{
io::{self, BufReader, Read},
path::Path,
};
use std::io::Seek;
use fs_err as fs;
@ -12,6 +13,7 @@ use crate::{
utils::{io::lock_and_flush_output_stdio, user_wants_to_continue},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
};
use crate::archive::sevenz;
/// File at input_file_path is opened for reading, example: "archive.tar.gz"
/// formats contains each format necessary for decompression, example: [Gz, Tar] (in decompression order)
@ -108,16 +110,7 @@ pub fn list_archive_contents(
}
}
let mut files = Vec::new();
sevenz_rust::decompress_file_with_extract_fn(archive_path, ".", |entry, _, _| {
files.push(Ok(FileInArchive {
path: entry.name().into(),
is_dir: entry.is_directory(),
}));
Ok(true)
})?;
Box::new(files.into_iter())
Box::new(sevenz::list_archive(archive_path, password))
}
Gzip | Bzip | Lz4 | Lzma | Snappy | Zstd => {
panic!("Not an archive! This should never happen, if it does, something is wrong with `CompressionFormat::is_archive()`. Please report this error!");