diff options
| -rw-r--r-- | ozone-cli/src/main.rs | 2 | ||||
| -rw-r--r-- | src/launcher.rs | 44 | ||||
| -rw-r--r-- | src/launcher/extract.rs | 109 | ||||
| -rw-r--r-- | src/version.rs | 3 |
4 files changed, 100 insertions, 58 deletions
diff --git a/ozone-cli/src/main.rs b/ozone-cli/src/main.rs index dd59f9d..965d27f 100644 --- a/ozone-cli/src/main.rs +++ b/ozone-cli/src/main.rs @@ -15,7 +15,7 @@ async fn main() -> Result<(), Box<dyn Error>> { let launcher = o3launcher::launcher::Launcher::new(PathBuf::from("./work").as_path(), true).await?; let profile = Profile { - version_id: "1.8.9".into(), + version_id: "1.21.4".into(), java_runtime: None, instance: "".into() }; diff --git a/src/launcher.rs b/src/launcher.rs index eac79bb..0b1ea90 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -268,18 +268,6 @@ impl Launcher { lib.natives.as_ref().map_or(None, |n| n.get(&self.system_info.os)).map(|s| s.as_str()) } - fn create_extract_job(&self, lib: &Library) -> Option<LibraryExtractJob> { - if lib.natives.is_none() { - debug!("Not extracting natives for {}, no \"natives\".", lib.name); - return None; // do not extract this one - } - - Some(LibraryExtractJob { - source: self.libraries.get_full_artifact_path(lib.name.as_str(), self.choose_lib_classifier(lib))?, - rule: lib.extract.clone() - }) - } - async fn ensure_file(&self, path: &Path, dlinfo: &DownloadInfo) -> Result<(), LaunchError> { // verify the file match util::verify_file(path, dlinfo.size, dlinfo.sha1).await { @@ -406,7 +394,7 @@ impl Launcher { * - (done) of the libraries we need, check which have correct size and sha1 * - (done) redownload necessary libraries * - (done) (if offline mode and there are libraries to download, then explode violently) - * - extract natives + * - (done) extract natives * - (done) logging * - (done) download the config if present and necessary * - (done) (explode if offline mode and we need to download stuff) @@ -439,6 +427,7 @@ impl Launcher { info!("Resolved launch version {}!", ver.id); let mut libs = Vec::new(); + let mut extract_jobs = Vec::new(); let mut downloads = Vec::new(); for lib in ver.libraries.values() { @@ -449,6 +438,14 @@ impl Launcher { libs.push(lib); if let Some(dl) = self.libraries.create_download(lib, self.choose_lib_classifier(lib)) { dl.make_dirs().await.map_err(|e| LaunchError::LibraryDirError(dl.get_path().to_path_buf(), e))?; + + if lib.natives.is_some() { + extract_jobs.push(LibraryExtractJob { + source: dl.get_path().to_owned(), + rule: lib.extract.clone() + }); + } + downloads.push(dl); } } @@ -512,9 +509,7 @@ impl Launcher { // extract natives (execute this function unconditionally because we still need the natives dir to exist) info!("Extracting natives."); - let natives_dir = self.libraries.extract_natives(libs.iter().filter_map(|lib| { - self.create_extract_job(*lib) - }).collect::<Vec<_>>()).await?; + let natives_dir = self.libraries.extract_natives(extract_jobs).await?; //todo!() Ok(()) @@ -545,6 +540,7 @@ impl Error for LibraryError { } } +#[derive(Debug)] struct LibraryExtractJob { source: PathBuf, rule: Option<LibraryExtractRule> @@ -571,14 +567,14 @@ impl LibraryRepository { if let Some(classifier) = classifier { match n.len() { - 3 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}.jar", n[1], n[2], classifier), Self::lib_replace))), - 4 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}-{}.jar", n[1], n[2], classifier, n[3]), Self::lib_replace))), + 2 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}.jar", n[0], n[1], classifier), Self::lib_replace))), + 3 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}-{}.jar", n[0], n[1], classifier, n[2]), Self::lib_replace))), _ => None } } else { match n.len() { - 3 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}.jar", n[1], n[2]), Self::lib_replace))), - 4 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}.jar", n[1], n[2], n[3]), Self::lib_replace))), + 2 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}.jar", n[0], n[1]), Self::lib_replace))), + 3 => Some(PathBuf::from(strsub::replace_thru(format!("{}-{}-{}.jar", n[0], n[1], n[2]), Self::lib_replace))), _ => None } } @@ -592,10 +588,6 @@ impl LibraryRepository { p.push(Self::get_artifact_filename(name, classifier)?); Some(p) } - - fn get_full_artifact_path(&self, name: &str, classifier: Option<&str>) -> Option<PathBuf> { - Some(self.home.join(Self::get_artifact_path(name, classifier)?)) - } fn create_download(&self, lib: &Library, classifier: Option<&str>) -> Option<VerifiedDownload> { if lib.url.is_some() || lib.downloads.is_none() { @@ -660,6 +652,7 @@ impl LibraryRepository { } async fn extract_natives<'lib>(&self, libs: Vec<LibraryExtractJob>) -> Result<PathBuf, LaunchError> { + let libs = libs; fs::create_dir_all(&self.natives).await.map_err(|e| LaunchError::IO { what: "creating natives directory", error: e @@ -678,10 +671,11 @@ impl LibraryRepository { let mut tally = 0usize; for job in libs { + debug!("Extracting natives for {}", job.source.display()); tally += extract::extract_zip(&job.source, &natives_dir, |name| job.rule.as_ref().is_some_and(|rules| rules.exclude.iter().filter(|ex| - name.starts_with(ex.as_str())).next().is_some()))?; + name.starts_with(ex.as_str())).next().is_none()))?; } Ok((natives_dir, tally)) 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) diff --git a/src/version.rs b/src/version.rs index 545a487..1424b18 100644 --- a/src/version.rs +++ b/src/version.rs @@ -403,7 +403,8 @@ where let mut map = HashMap::new(); while let Some(lib) = seq.next_element::<Library>()? { - map.insert(canonicalize_library_name(lib.name.as_str()), lib); + //map.insert(canonicalize_library_name(lib.name.as_str()), lib); + map.insert(lib.name.as_str().to_owned(), lib); } Ok(map) |
