Эх сурвалжийг харах

fix(updater): fallback if tmp is on different mount point, closes #4500 (#4504)

Lucas Fernandes Nogueira 3 жил өмнө
parent
commit
fd125f76d7

+ 5 - 0
.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.

+ 70 - 59
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<R: Runtime> Update<R> {
     // 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<R: Runtime> Update<R> {
 // 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<R: Read + Seek>(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<dyn FnOnce() -> Option<PathBuf>>,
+    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<R: Read + Seek>(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<R: Read + Seek>(
@@ -795,7 +807,6 @@ fn copy_files_and_run<R: Read + Seek>(
 // │      └── Contents                          # Application contents...
 // │          └── ...
 // └── ...
-#[cfg(feature = "updater")]
 #[cfg(target_os = "macos")]
 fn copy_files_and_run<R: Read + Seek>(archive_buffer: R, extract_path: &Path) -> Result {
   let mut extracted_files: Vec<PathBuf> = Vec::new();

+ 4 - 0
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<T = ()> = std::result::Result<T, Error>;