Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check executable path is suitable #38

Merged
merged 1 commit into from
Mar 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion axoupdater/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! axoupdater crate

use std::{
env::{self, args, current_dir},
env::{self, args, current_dir, current_exe},
path::PathBuf,
process::Stdio,
};
Expand Down Expand Up @@ -46,6 +46,8 @@ pub struct AxoUpdater {
latest_release: Option<Release>,
/// The current version number
current_version: Option<String>,
/// Information about the install prefix of the previous version
install_prefix: Option<Utf8PathBuf>,
/// Whether to display the underlying installer's stdout
print_installer_stdout: bool,
/// Whether to display the underlying installer's stderr
Expand All @@ -68,6 +70,7 @@ impl AxoUpdater {
source: None,
latest_release: None,
current_version: None,
install_prefix: None,
print_installer_stdout: true,
print_installer_stderr: true,
}
Expand All @@ -80,6 +83,7 @@ impl AxoUpdater {
source: None,
latest_release: None,
current_version: None,
install_prefix: None,
print_installer_stdout: true,
print_installer_stderr: true,
}
Expand All @@ -103,6 +107,7 @@ impl AxoUpdater {
source: None,
latest_release: None,
current_version: None,
install_prefix: None,
print_installer_stdout: true,
print_installer_stderr: true,
})
Expand All @@ -122,6 +127,7 @@ impl AxoUpdater {

self.source = Some(receipt.source.clone());
self.current_version = Some(receipt.version.to_owned());
self.install_prefix = Some(receipt.install_prefix.to_owned());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh, NOW it gets used (sorry again for messing it up, nice robust code to work around it... hope no one's foolish enough to make bin/bin/)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good! Since we have to do it for the bin path anyway, it's not a big deal to do it in both places


Ok(self)
}
Expand Down Expand Up @@ -177,13 +183,65 @@ impl AxoUpdater {
self
}

/// Checks to see if the loaded install receipt is for this executable.
/// Used to guard against cases where the running EXE is from a package
/// manager, but a receipt from a shell installed-copy is present on the
/// system.
/// Returns an error if the receipt hasn't been loaded yet.
pub fn check_receipt_is_for_this_executable(&self) -> AxoupdateResult<bool> {
let Some(install_prefix) = &self.install_prefix else {
return Err(AxoupdateError::NotConfigured {
missing_field: "install_prefix".to_owned(),
});
};

let current_exe_path = Utf8PathBuf::from_path_buf(current_exe()?.canonicalize()?)
.map_err(|path| AxoupdateError::CaminoConversionFailed { path })?;
// First determine the parent dir
let mut current_exe_root = if let Some(parent) = current_exe_path.parent() {
parent.to_path_buf()
} else {
current_exe_path
};
// If the parent dir is a "bin" dir, strip it to get the true root
if current_exe_root.file_name() == Some("bin") {
if let Some(parent) = current_exe_root.parent() {
current_exe_root = parent.to_path_buf();
}
}

let mut install_root = install_prefix.to_owned();
if install_root.file_name() == Some("bin") {
if let Some(parent) = install_root.parent() {
install_root = parent.to_path_buf();
}
}

// Looks like this EXE comes from a different source than the install
// receipt
if current_exe_root != install_root {
return Ok(false);
}

Ok(true)
}

/// Determines if an update is needed by querying the newest version from
/// the location specified in `source`.
/// This includes a blocking network call, so it may be slow.
/// This can only be performed if the `current_version` field has been
/// set, either by loading the install receipt or by specifying it using
/// `set_current_version`.
/// Note that this also checks to see if the current executable is
/// *eligible* for updates, by checking to see if it's the executable
/// that the install receipt is for. In the case that the executable comes
/// from a different source, it will return before the network call for a
/// new version.
pub async fn is_update_needed(&mut self) -> AxoupdateResult<bool> {
if !self.check_receipt_is_for_this_executable()? {
return Ok(false);
}

let Some(current_version) = self.current_version.to_owned() else {
return Err(AxoupdateError::NotConfigured {
missing_field: "current_version".to_owned(),
Expand Down Expand Up @@ -379,6 +437,14 @@ pub enum AxoupdateError {
#[error(transparent)]
Gazenot(#[from] GazenotError),

/// Failure when converting a PathBuf to a Utf8PathBuf
#[error("An internal error occurred when decoding path `{:?}' to utf8", path)]
#[diagnostic(help("This probably isn't your fault; please open an issue!"))]
CaminoConversionFailed {
/// The path which Camino failed to convert
path: PathBuf,
},

/// Indicates that the only updates available are located at a source
/// this crate isn't configured to support. This is returned if the
/// appropriate source is disabled via features.
Expand Down
Loading