From 08dba4588e93ce338cf01d740bf0923a4f46ade8 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Wed, 22 Jan 2025 15:31:52 -0600 Subject: more jre download stuff --- src/launcher/jre.rs | 109 ++++++++++++++++++++++++++++++++++++++++++- src/launcher/jre/manifest.rs | 17 ++++++- src/util.rs | 6 ++- 3 files changed, 129 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/launcher/jre.rs b/src/launcher/jre.rs index 40fd8c1..4be4c7b 100644 --- a/src/launcher/jre.rs +++ b/src/launcher/jre.rs @@ -1,6 +1,9 @@ +use std::collections::HashSet; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::path::{Path, PathBuf}; +use std::sync::Arc; +use futures::{stream, StreamExt, TryStreamExt}; use log::{debug, info, warn}; use tokio::{fs, io, io::ErrorKind}; use tokio::fs::File; @@ -11,7 +14,8 @@ mod manifest; use arch::JRE_ARCH; use manifest::JavaRuntimesManifest; -use crate::launcher::jre::manifest::JavaRuntimeManifest; +use manifest::JavaRuntimeManifest; +use crate::launcher::jre::manifest::JavaRuntimeFile; use crate::util; use crate::util::{EnsureFileError, FileVerifyError, IntegrityError}; use crate::version::DownloadInfo; @@ -91,6 +95,109 @@ impl JavaRuntimeRepository { self.load_runtime_manifest(component, &runtime.manifest).await } + + fn clean_up_runtime_sync(path: &Path, manifest: Arc) -> Result<(), io::Error> { + for entry in walkdir::WalkDir::new(path).contents_first(true) { + let entry = entry?; + + if !entry.path().strip_prefix(path) + .expect("walkdir escaped root (???)") + .to_str().map_or(None, |s| manifest.files.get(s)) + .is_none_or(|f| (f.is_file() != entry.file_type().is_file()) + || (f.is_directory() != entry.file_type().is_dir()) + || (f.is_link() != entry.file_type().is_symlink())) { + // path is invalid utf-8, extraneous, or of the wrong type + debug!("File {} is extraneous or of wrong type ({:?}). Deleting it.", entry.path().display(), entry.file_type()); + + if entry.file_type().is_dir() { + std::fs::remove_dir(entry.path())?; + } else { + std::fs::remove_file(entry.path())?; + } + } + } + + Ok(()) + } + + async fn clean_up_runtime(path: &Path, manifest: Arc) -> Result<(), io::Error> { + let (tx, rx) = tokio::sync::oneshot::channel(); + + let path = path.to_owned(); + let manifest = manifest.clone(); + + tokio::task::spawn_blocking(move || { + let res = Self::clean_up_runtime_sync(&path, manifest); + let _ = tx.send(res); + }).await.expect("clean_up_runtime_sync panicked"); + + rx.await.expect("clean_up_runtime_sync hung up") + } + + async fn ensure_jre_dirs(&self, path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> { + stream::iter(manifest.files.iter().filter(|(_, f)| f.is_directory())) + .map::, _>(|(name, _)| Ok(async move { + let ent_path = util::check_path(name).map_err(JavaRuntimeError::MalformedManifest)?; + let ent_path = [path, ent_path].into_iter().collect::(); + + match fs::metadata(&ent_path).await { + Ok(meta) => { + if !meta.is_dir() { + debug!("Deleting misplaced file at {}", ent_path.display()); + fs::remove_file(&ent_path).await.map_err(|e| JavaRuntimeError::IO { + what: "deleting misplaced file", + error: e + })?; + } + }, + Err(e) if e.kind() == ErrorKind::NotFound => (), + Err(e) => return Err(JavaRuntimeError::IO { what: "'stat'ing directory", error: e }) + } + + match fs::create_dir(&ent_path).await { + Ok(_) => { + debug!("Created directory at {}", ent_path.display()); + Ok(()) + }, + Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), + Err(e) => { + warn!("Could not create directory {} for JRE!", ent_path.display()); + return Err(JavaRuntimeError::IO { what: "creating directory", error: e }); + } + } + })) + .try_buffer_unordered(32) + .try_fold((), |_, _| async { Ok(()) }).await + } + + async fn ensure_jre_files(path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> { + stream::iter(manifest.files.iter().filter(|(_, f)| f.is_file())) + .map(|(name, file)| Ok(async move { + + })); + + todo!() + } + + async fn ensure_jre(&self, component: &str, manifest: JavaRuntimeManifest) -> Result<(), JavaRuntimeError> { + let runtime_path = self.get_component_dir(component); + let runtime_path = runtime_path.join("runtime"); + let manifest = Arc::new(manifest); + + fs::create_dir_all(&runtime_path).await + .map_err(|e| JavaRuntimeError::IO { what: "creating runtime directory", error: e })?; + + debug!("Cleaning up JRE directory for {component}"); + Self::clean_up_runtime(runtime_path.as_path(), manifest).await + .map_err(|e| JavaRuntimeError::IO { what: "cleaning up runtime directory", error: e })?; + + debug!("Building directory structure for {component}"); + self.ensure_jre_dirs(&runtime_path, manifest.as_ref()).await?; + + + + todo!() + } } #[derive(Debug)] diff --git a/src/launcher/jre/manifest.rs b/src/launcher/jre/manifest.rs index ca21a2b..41780d0 100644 --- a/src/launcher/jre/manifest.rs +++ b/src/launcher/jre/manifest.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use indexmap::IndexMap; use serde::Deserialize; use crate::version::DownloadInfo; @@ -44,7 +45,21 @@ pub enum JavaRuntimeFile { } } +impl JavaRuntimeFile { + pub fn is_file(&self) -> bool { + matches!(*self, JavaRuntimeFile::File { .. }) + } + + pub fn is_directory(&self) -> bool { + matches!(*self, JavaRuntimeFile::Directory) + } + + pub fn is_link(&self) -> bool { + matches!(*self, JavaRuntimeFile::Link { .. }) + } +} + #[derive(Debug, Deserialize)] pub struct JavaRuntimeManifest { - pub files: HashMap + pub files: IndexMap } diff --git a/src/util.rs b/src/util.rs index b7b7c6d..8ad3632 100644 --- a/src/util.rs +++ b/src/util.rs @@ -68,7 +68,7 @@ pub async fn verify_file(path: impl AsRef, expect_size: Option, exp let path = path.as_ref(); if expect_size.is_none() && expect_sha1.is_none() { - return match path.metadata() { + return match fs::metadata(path).await { Ok(_) => { debug!("No size or sha1 for {}, have to assume it's good.", path.display()); Ok(()) @@ -257,6 +257,9 @@ pub fn strip_verbatim(path: &Path) -> &Path { return path; }; + use std::path::Prefix; + use std::ffi::OsStr; + match p.kind() { Prefix::VerbatimDisk(_) => Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(&path.as_os_str().as_encoded_bytes()[4..]) }), @@ -283,6 +286,7 @@ impl AsJavaPath for Path { mod tests { #[allow(unused_imports)] use super::*; + use std::path::Prefix; #[test] #[cfg(windows)] -- cgit v1.2.3-70-g09d2