summaryrefslogtreecommitdiffstats
path: root/src/launcher/extract.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-17 00:42:01 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-17 00:42:01 -0600
commitb8d81cc328dc88dd419485f5f80e1170ada79bd7 (patch)
tree917902b398e206d474cf0f6c19dd9c8f67842b1d /src/launcher/extract.rs
parentextracting natives (diff)
fix bugs
Diffstat (limited to 'src/launcher/extract.rs')
-rw-r--r--src/launcher/extract.rs109
1 files changed, 78 insertions, 31 deletions
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<Path>, target: &str) -> io::Result<()> {
+ os::unix::fs::symlink(target, path)
+}
+
+#[cfg(windows)]
+fn extract_symlink(path: impl AsRef<Path>, target: &str) -> io::Result<()> {
+ os::windows::fs::symlink_file(target, path)
+}
+
+#[cfg(not(any(unix, windows)))]
+fn extract_symlink(path: impl AsRef<Path>, _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<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 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)