From b8d81cc328dc88dd419485f5f80e1170ada79bd7 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Fri, 17 Jan 2025 00:42:01 -0600 Subject: fix bugs --- src/launcher/extract.rs | 109 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 31 deletions(-) (limited to 'src/launcher/extract.rs') diff --git a/src/launcher/extract.rs b/src/launcher/extract.rs index c9e0dc9..206d34f 100644 --- a/src/launcher/extract.rs +++ b/src/launcher/extract.rs @@ -1,10 +1,10 @@ use std::error::Error; use std::fmt::{Display, Formatter}; -use std::{fs, io}; +use std::{fs, io, os}; use std::fs::File; -use std::io::{BufReader, Error as IOError}; +use std::io::{BufReader, Error as IOError, Read}; use std::path::{Component, Path, PathBuf}; -use log::debug; +use log::{debug, trace}; use zip::result::ZipError; use zip::ZipArchive; @@ -47,58 +47,105 @@ impl Error for ZipExtractError { } } +fn check_entry_path(name: &str) -> Result<&Path, ZipExtractError> { + let entry_path: &Path = Path::new(name); + + let mut depth = 0usize; + for component in entry_path.components() { + depth = match component { + Component::Prefix(_) | Component::RootDir => + return Err(ZipExtractError::InvalidEntry { + why: "root path component in entry", + name: name.to_owned() + }), + Component::ParentDir => depth.checked_sub(1) + .map_or_else(|| Err(ZipExtractError::InvalidEntry { + why: "entry path escapes extraction root", + name: name.to_owned() + }), |s| Ok(s))?, + Component::Normal(_) => depth + 1, + _ => depth + } + } + + Ok(entry_path) +} + +#[cfg(unix)] +fn extract_symlink(path: impl AsRef, target: &str) -> io::Result<()> { + os::unix::fs::symlink(target, path) +} + +#[cfg(windows)] +fn extract_symlink(path: impl AsRef, target: &str) -> io::Result<()> { + os::windows::fs::symlink_file(target, path) +} + +#[cfg(not(any(unix, windows)))] +fn extract_symlink(path: impl AsRef, _target: &str) -> io::Result<()> { + warn!("Refusing to extract symbolic link to {}. I don't know how to do it on this platform!", path.as_ref().display()); + Ok(()) +} + pub fn extract_zip(zip_path: impl AsRef, extract_root: impl AsRef, condition: F) -> Result where F: Fn(&str) -> bool { debug!("Extracting zip file {} into {}", zip_path.as_ref().display(), extract_root.as_ref().display()); - + fs::create_dir_all(&extract_root).map_err(|e| ZipExtractError::from(("create extract root", e)))?; let mut extracted = 0usize; - let file = File::open(zip_path).map_err(|e| ZipExtractError::from(("extract zip file (open)", e)))?; + let file = File::open(&zip_path).map_err(|e| ZipExtractError::from(("extract zip file (open)", e)))?; let read = BufReader::new(file); let mut archive = ZipArchive::new(read).map_err(|e| ZipExtractError::from(("read zip archive", e)))?; + // create directories for n in 0..archive.len() { - let mut entry = archive.by_index(n).map_err(|e| ZipExtractError::from(("access zip entry", e)))?; - let name = entry.name(); + let entry = archive.by_index(n).map_err(|e| ZipExtractError::from(("read zip entry (1)", e)))?; + if !entry.is_dir() { continue; } + let name = entry.name(); if !condition(name) { continue; } - let entry_path: &Path = Path::new(name); - let mut depth = 0usize; - for component in entry_path.components() { - depth = match component { - Component::Prefix(_) | Component::RootDir => - return Err(ZipExtractError::InvalidEntry { - why: "root path component in entry", - name: name.to_owned() - }), - Component::ParentDir => depth.checked_sub(1) - .map_or_else(|| Err(ZipExtractError::InvalidEntry { - why: "entry path escapes extraction root", - name: name.to_owned() - }), |s| Ok(s))?, - Component::Normal(_) => depth + 1, - _ => depth - } + let entry_path = check_entry_path(name)?; + let entry_path: PathBuf = [extract_root.as_ref(), entry_path].iter().collect(); + + trace!("Extracting directory {} from {}", entry.name(), zip_path.as_ref().display()); + fs::create_dir_all(entry_path).map_err(|e| ZipExtractError::from(("extract directory", e)))?; + } + + // extract the files + for n in 0..archive.len() { + let mut entry = archive.by_index(n).map_err(|e| ZipExtractError::from(("read zip entry (2)", e)))?; + let name = entry.name(); + + if entry.is_dir() { continue; } + + if !condition(name) { + continue; } + let entry_path = check_entry_path(name)?; let entry_path: PathBuf = [extract_root.as_ref(), entry_path].iter().collect(); - // hmm some redundant directory creations will be happening here on linux :( - if let Some(parent) = entry_path.parent() { - fs::create_dir_all(parent).map_err(|e| ZipExtractError::from(("create entry directory", e)))?; - } + if entry.is_symlink() { + let mut target = String::new(); + entry.read_to_string(&mut target).map_err(|e| ZipExtractError::from(("read to symlink target", e)))?; + + trace!("Extracting symbolic link {} -> {} from {}", entry.name(), target, zip_path.as_ref().display()); + extract_symlink(entry_path.as_path(), target.as_str()).map_err(|e| ZipExtractError::from(("extract symlink", e)))?; + } else if entry.is_file() { + let mut outfile = File::create(&entry_path).map_err(|e| ZipExtractError::from(("extract zip entry (open)", e)))?; - let mut outfile = File::create(&entry_path).map_err(|e| ZipExtractError::from(("extract zip entry (open)", e)))?; - io::copy(&mut entry, &mut outfile).map_err(|e| ZipExtractError::from(("extract zip entry (write)", e)))?; - extracted += 1; + trace!("Extracting file {} from {}", entry.name(), zip_path.as_ref().display()); + io::copy(&mut entry, &mut outfile).map_err(|e| ZipExtractError::from(("extract zip entry (write)", e)))?; + extracted += 1; + } } Ok(extracted) -- cgit v1.2.3-70-g09d2