cargo-binstall/crates/binstalk-downloader/src/download/zip_extraction.rs
Jiahao XU f3ddc224ba
Use rc-zip-sync for zip extraction
Fixed #1080

In this commit, binstalk-downloader is updated to
- first download the zip into a temporary file, since
  there is no correct way to extract zip from a stream.
- then use rc-zip-sync to read from the zip and extract
  it to filesystem.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
2024-10-29 01:13:45 +11:00

67 lines
2 KiB
Rust

use std::{
fs::{self, create_dir_all, File},
io::{self, Read},
path::Path,
};
use cfg_if::cfg_if;
use rc_zip_sync::{rc_zip::parse::EntryKind, ReadZip};
use super::{DownloadError, ExtractedFiles};
pub(super) fn do_extract_zip(f: File, dir: &Path) -> Result<ExtractedFiles, DownloadError> {
let mut extracted_files = ExtractedFiles::new();
for entry in f.read_zip()?.entries() {
let Some(name) = entry.sanitized_name() else {
continue;
};
let path = dir.join(name);
let do_extract_file = || {
let mut entry_writer = File::create(&path)?;
let mut entry_reader = entry.reader();
io::copy(&mut entry_reader, &mut entry_writer)?;
Ok::<_, io::Error>(())
};
let parent = path
.parent()
.expect("all full entry paths should have parent paths");
create_dir_all(parent)?;
extracted_files.add_dir(parent);
match entry.kind() {
EntryKind::Symlink => {
extracted_files.add_file(&path);
cfg_if! {
if #[cfg(windows)] {
do_extract_file()?;
} else {
match fs::symlink_metadata(&path) {
Ok(metadata) if metadata.is_file() => fs::remove_file(&path)?,
_ => (),
}
let mut src = String::new();
entry.reader().read_to_string(&mut src)?;
// validate pointing path before creating a symbolic link
if src.contains("..") {
continue;
}
std::os::unix::fs::symlink(src, &path)?;
}
}
}
EntryKind::Directory => (),
EntryKind::File => {
extracted_files.add_file(&path);
do_extract_file()?;
}
}
}
Ok(extracted_files)
}