From 6c992b7c5fb9221376c30aae8ff004297c14315d Mon Sep 17 00:00:00 2001 From: Jonson Petard <41122242+greenhat616@users.noreply.github.com> Date: Fri, 29 Dec 2023 12:12:29 +0800 Subject: [PATCH] fix(updater): allow to use elevated permission to copy and override core --- backend/tauri/Cargo.toml | 2 +- backend/tauri/src/core/updater.rs | 92 +++++++++++++++++++++++-------- 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/backend/tauri/Cargo.toml b/backend/tauri/Cargo.toml index fedd456e17..3c8a74be81 100644 --- a/backend/tauri/Cargo.toml +++ b/backend/tauri/Cargo.toml @@ -64,9 +64,9 @@ rs-snowflake = "0.6" rocksdb = "0.21" thiserror = { workspace = true, version = "1.0" } simd-json = "0.13.4" +runas = "=1.0.0" # blocked by https://github.com/mitsuhiko/rust-runas/issues/13 [target.'cfg(windows)'.dependencies] -runas = "=1.0.0" # blocked by https://github.com/mitsuhiko/rust-runas/issues/13 deelevate = "0.2.0" winreg = { version = "0.50", features = ["transactions"] } windows-sys = { version = "0.48", features = [ diff --git a/backend/tauri/src/core/updater.rs b/backend/tauri/src/core/updater.rs index 6f549378b7..1f054b1fee 100644 --- a/backend/tauri/src/core/updater.rs +++ b/backend/tauri/src/core/updater.rs @@ -1,18 +1,18 @@ use std::{collections::HashMap, io::Cursor, path::Path, sync::OnceLock}; +use super::CoreManager; use crate::config::ClashCore; use anyhow::{anyhow, Result}; use gunzip::Decompressor; -use log::debug; -use serde::{de, Deserialize, Serialize}; +use log::{debug, warn}; +use runas::Command as RunasCommand; +use serde::{Deserialize, Serialize}; #[cfg(target_family = "unix")] use std::os::unix::fs::PermissionsExt; use tempfile::{tempdir, TempDir}; use tokio::{join, sync::RwLock, task::spawn_blocking}; use zip::ZipArchive; -use super::CoreManager; - pub struct Updater { manifest_version: ManifestVersion, mirror: String, @@ -123,14 +123,15 @@ impl Updater { self.mirror.as_str(), "MetaCubeX/mihomo/releases/download/Prerelease-Alpha/version.txt" ); - Ok(client - .get(url) - .send() - .await? - .text() - .await? - .trim() - .to_string()) + let res = client.get(url).send().await?; + let status_code = res.status(); + if !status_code.is_success() { + anyhow::bail!( + "failed to get mihomo alpha version: response status is {}, expected 200", + status_code + ); + } + Ok(res.text().await?.trim().to_string()) } pub async fn update_core(&self, core_type: &ClashCore) -> Result<()> { @@ -165,10 +166,42 @@ impl Updater { let core_dir = core_dir.parent().ok_or(anyhow!("failed to get core dir"))?; let target_core = core_dir.join(target_core); debug!("copying core to {:?}", target_core); - std::fs::copy( - tmp_dir.path().join(core_type.clone().to_string()), - target_core, - )?; + let tmp_core_path = tmp_dir.path().join(core_type.clone().to_string()); + match std::fs::copy(tmp_core_path.clone(), target_core.clone()) { + Ok(_) => {} + Err(err) => { + warn!( + "failed to copy core: {}, trying to use elevated permission to copy and override core", + err + ); + #[cfg(target_os = "windows")] + { + // 防止 UAC 弹窗堵塞主线程 + tokio::task::spawn_blocking(move || { + RunasCommand::new("cmd") + .args(&[ + "/C", + "copy", + "/Y", + tmp_core_path.to_str().unwrap(), + target_core.to_str().unwrap(), + ]) + .status() + }) + .await??; + } + #[cfg(not(target_os = "windows"))] + { + RunasCommand::new("cp") + .args(&[ + "-f", + tmp_core_path.to_str().unwrap(), + target_core.to_str().unwrap(), + ]) + .status()?; + } + } + }; // 5. if core is used before, restart it if current_core == *core_type { @@ -235,7 +268,15 @@ impl Updater { let mut dst = std::fs::File::create(&file_path)?; let client = reqwest::Client::new(); - let mut buff = Cursor::new(client.get(url).send().await?.bytes().await?); + let res = client.get(url).send().await?; + let status_code = res.status(); + if !status_code.is_success() { + anyhow::bail!( + "failed to download core: response status is {}, expected 200", + status_code + ); + } + let mut buff = Cursor::new(res.bytes().await?); std::io::copy(&mut buff, &mut dst)?; Ok(artifact) } @@ -284,7 +325,7 @@ fn decompress_and_set_permission( }; let tmp_core = tmp_path.join(core_type.clone().to_string()); debug!("writing core to {:?} ({} bytes)", tmp_core, buff.len()); - let mut core_file = std::fs::File::create(tmp_core.to_owned())?; + let mut core_file = std::fs::File::create(&tmp_core)?; std::io::copy(&mut buff.as_slice(), &mut core_file)?; #[cfg(target_family = "unix")] { @@ -300,12 +341,15 @@ pub async fn get_latest_version_manifest(mirror: &str) -> Result() - .await?) + let res = client.get(url).send().await?; + let status_code = res.status(); + if !status_code.is_success() { + anyhow::bail!( + "failed to get latest version manifest: response status is {}, expected 200", + status_code + ); + } + Ok(res.json::().await?) } enum CoreTypeMeta {