diff options
| author | 2025-01-16 23:39:40 -0600 | |
|---|---|---|
| committer | 2025-01-16 23:39:40 -0600 | |
| commit | 5c74cae3ca4f97b49fe8858c6fa84d224fc02ccf (patch) | |
| tree | 7e525dbc98a48c4adcf89f56ae492e3ee64881d2 /src/launcher/extract.rs | |
| parent | start on extracting libraries (diff) | |
extracting natives
Diffstat (limited to 'src/launcher/extract.rs')
| -rw-r--r-- | src/launcher/extract.rs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/launcher/extract.rs b/src/launcher/extract.rs new file mode 100644 index 0000000..c9e0dc9 --- /dev/null +++ b/src/launcher/extract.rs @@ -0,0 +1,105 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; +use std::{fs, io}; +use std::fs::File; +use std::io::{BufReader, Error as IOError}; +use std::path::{Component, Path, PathBuf}; +use log::debug; +use zip::result::ZipError; +use zip::ZipArchive; + +#[derive(Debug)] +pub enum ZipExtractError { + IO { what: &'static str, error: IOError }, + Zip { what: &'static str, error: ZipError }, + InvalidEntry { why: &'static str, name: String } +} + +impl From<(&'static str, IOError)> for ZipExtractError { + fn from((what, error): (&'static str, IOError)) -> Self { + ZipExtractError::IO { what, error } + } +} + +impl From<(&'static str, ZipError)> for ZipExtractError { + fn from((what, error): (&'static str, ZipError)) -> Self { + ZipExtractError::Zip { what, error } + } +} + +impl Display for ZipExtractError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ZipExtractError::IO { what, error } => write!(f, "i/o error ({what}): {error}"), + ZipExtractError::Zip { what, error } => write!(f, "zip error ({what}): {error}"), + ZipExtractError::InvalidEntry { why, name } => write!(f, "invalid entry in zip file ({why}): {name}") + } + } +} + +impl Error for ZipExtractError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + ZipExtractError::IO { error, .. } => Some(error), + ZipExtractError::Zip { error, .. } => Some(error), + _ => None + } + } +} + +pub fn extract_zip<F>(zip_path: impl AsRef<Path>, extract_root: impl AsRef<Path>, condition: F) -> Result<usize, ZipExtractError> +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 read = BufReader::new(file); + + let mut archive = ZipArchive::new(read).map_err(|e| ZipExtractError::from(("read zip archive", e)))?; + + 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(); + + 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: 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)))?; + } + + 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; + } + + Ok(extracted) +} |
