From fd125f76d768099dc3d4b2d4114349ffc31ffac9 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 28 Jun 2022 08:20:59 -0700 Subject: [PATCH] fix(updater): fallback if tmp is on different mount point, closes #4500 (#4504) --- .changes/fix-updater-linux.md | 5 ++ core/tauri/src/updater/core.rs | 129 +++++++++++++++++--------------- core/tauri/src/updater/error.rs | 4 + 3 files changed, 79 insertions(+), 59 deletions(-) create mode 100644 .changes/fix-updater-linux.md diff --git a/.changes/fix-updater-linux.md b/.changes/fix-updater-linux.md new file mode 100644 index 00000000000..35fd76c16e5 --- /dev/null +++ b/.changes/fix-updater-linux.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Adjust the updater to fallback to `$HOME/.cache` or the current working directory as temp directory if the system default is in a different mount point. diff --git a/core/tauri/src/updater/core.rs b/core/tauri/src/updater/core.rs index 96e9127d453..de6d3407d78 100644 --- a/core/tauri/src/updater/core.rs +++ b/core/tauri/src/updater/core.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: MIT use super::error::{Error, Result}; -#[cfg(feature = "updater")] use crate::api::file::{ArchiveFormat, Extract, Move}; use crate::{ api::http::{ClientBuilder, HttpRequestBuilder}, @@ -21,7 +20,6 @@ use tauri_utils::{platform::current_exe, Env}; use time::OffsetDateTime; use url::Url; -#[cfg(feature = "updater")] use std::io::Seek; use std::{ collections::HashMap, @@ -33,7 +31,6 @@ use std::{ time::Duration, }; -#[cfg(feature = "updater")] #[cfg(not(target_os = "macos"))] use std::ffi::OsStr; @@ -602,29 +599,27 @@ impl Update { // if there is no signature, bail out. verify_signature(&mut archive_buffer, &self.signature, &pub_key)?; - #[cfg(feature = "updater")] - { - // we copy the files depending of the operating system - // we run the setup, appimage re-install or overwrite the - // macos .app - #[cfg(target_os = "windows")] - copy_files_and_run( - archive_buffer, - &self.extract_path, - self.with_elevated_task, - self - .app - .config() - .tauri - .updater - .windows - .install_mode - .clone() - .msiexec_args(), - )?; - #[cfg(not(target_os = "windows"))] - copy_files_and_run(archive_buffer, &self.extract_path)?; - } + // we copy the files depending of the operating system + // we run the setup, appimage re-install or overwrite the + // macos .app + #[cfg(target_os = "windows")] + copy_files_and_run( + archive_buffer, + &self.extract_path, + self.with_elevated_task, + self + .app + .config() + .tauri + .updater + .windows + .install_mode + .clone() + .msiexec_args(), + )?; + #[cfg(not(target_os = "windows"))] + copy_files_and_run(archive_buffer, &self.extract_path)?; + // We are done! Ok(()) } @@ -640,42 +635,60 @@ impl Update { // We should have an AppImage already installed to be able to copy and install // the extract_path is the current AppImage path // tmp_dir is where our new AppImage is found -#[cfg(feature = "updater")] #[cfg(target_os = "linux")] fn copy_files_and_run(archive_buffer: R, extract_path: &Path) -> Result { - use std::os::unix::fs::PermissionsExt; - let tmp_dir = tempfile::Builder::new() - .prefix("tauri_current_app") - .tempdir()?; - let mut perms = std::fs::metadata(tmp_dir.path())?.permissions(); - perms.set_mode(0o700); - std::fs::set_permissions(tmp_dir.path(), perms)?; - - let tmp_app_image = &tmp_dir.path().join("current_app.AppImage"); - - // create a backup of our current app image - Move::from_source(extract_path).to_dest(tmp_app_image)?; - - // extract the buffer to the tmp_dir - // we extract our signed archive into our final directory without any temp file - let mut extractor = - Extract::from_cursor(archive_buffer, ArchiveFormat::Tar(Some(Compression::Gz))); - - extractor.with_files(|entry| { - let path = entry.path()?; - if path.extension() == Some(OsStr::new("AppImage")) { - // if something went wrong during the extraction, we should restore previous app - if let Err(err) = entry.extract(extract_path) { - Move::from_source(tmp_app_image).to_dest(extract_path)?; - return Err(crate::api::Error::Extract(err.to_string())); + use std::os::unix::fs::{MetadataExt, PermissionsExt}; + + let extract_path_metadata = extract_path.metadata()?; + + let tmp_dir_locations = vec![ + Box::new(|| Some(env::temp_dir())) as Box Option>, + Box::new(dirs_next::cache_dir), + Box::new(|| Some(extract_path.parent().unwrap().to_path_buf())), + ]; + + for tmp_dir_location in tmp_dir_locations { + if let Some(tmp_dir_location) = tmp_dir_location() { + let tmp_dir = tempfile::Builder::new() + .prefix("tauri_current_app") + .tempdir_in(tmp_dir_location)?; + let tmp_dir_metadata = tmp_dir.path().metadata()?; + + if extract_path_metadata.dev() == tmp_dir_metadata.dev() { + let mut perms = tmp_dir_metadata.permissions(); + perms.set_mode(0o700); + std::fs::set_permissions(tmp_dir.path(), perms)?; + + let tmp_app_image = &tmp_dir.path().join("current_app.AppImage"); + + // create a backup of our current app image + Move::from_source(extract_path).to_dest(tmp_app_image)?; + + // extract the buffer to the tmp_dir + // we extract our signed archive into our final directory without any temp file + let mut extractor = + Extract::from_cursor(archive_buffer, ArchiveFormat::Tar(Some(Compression::Gz))); + + return extractor + .with_files(|entry| { + let path = entry.path()?; + if path.extension() == Some(OsStr::new("AppImage")) { + // if something went wrong during the extraction, we should restore previous app + if let Err(err) = entry.extract(extract_path) { + Move::from_source(tmp_app_image).to_dest(extract_path)?; + return Err(crate::api::Error::Extract(err.to_string())); + } + // early finish we have everything we need here + return Ok(true); + } + Ok(false) + }) + .map_err(Into::into); } - // early finish we have everything we need here - return Ok(true); } - Ok(false) - })?; + } - Ok(()) + Err(Error::TempDirNotOnSameMountPoint) } // Windows @@ -692,7 +705,6 @@ fn copy_files_and_run(archive_buffer: R, extract_path: &Path) -> // ## EXE // Update server can provide a custom EXE (installer) who can run any task. -#[cfg(feature = "updater")] #[cfg(target_os = "windows")] #[allow(clippy::unnecessary_wraps)] fn copy_files_and_run( @@ -795,7 +807,6 @@ fn copy_files_and_run( // │ └── Contents # Application contents... // │ └── ... // └── ... -#[cfg(feature = "updater")] #[cfg(target_os = "macos")] fn copy_files_and_run(archive_buffer: R, extract_path: &Path) -> Result { let mut extracted_files: Vec = Vec::new(); diff --git a/core/tauri/src/updater/error.rs b/core/tauri/src/updater/error.rs index c4ca6a56c3d..9c2e5e30715 100644 --- a/core/tauri/src/updater/error.rs +++ b/core/tauri/src/updater/error.rs @@ -65,6 +65,10 @@ pub enum Error { /// HTTP error. #[error(transparent)] Http(#[from] http::Error), + /// Temp dir is not on same mount mount. This prevents our updater to rename the AppImage to a temp file. + #[cfg(target_os = "linux")] + #[error("temp directory is not on the same mount point as the AppImage")] + TempDirNotOnSameMountPoint, } pub type Result = std::result::Result;