Skip to content

Commit

Permalink
fix(updater): fallback if tmp is on different mount point, closes #4500
Browse files Browse the repository at this point in the history
… (#4504)
  • Loading branch information
lucasfernog committed Jun 28, 2022
1 parent f6edc6d commit fd125f7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .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.
129 changes: 70 additions & 59 deletions core/tauri/src/updater/core.rs
Expand Up @@ -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},
Expand All @@ -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,
Expand All @@ -33,7 +31,6 @@ use std::{
time::Duration,
};

#[cfg(feature = "updater")]
#[cfg(not(target_os = "macos"))]
use std::ffi::OsStr;

Expand Down Expand Up @@ -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(())
}
Expand All @@ -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
Expand All @@ -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>(
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions core/tauri/src/updater/error.rs
Expand Up @@ -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>;

0 comments on commit fd125f7

Please sign in to comment.