Skip to main content

tauri_utils/platform/
starting_binary.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use ctor::ctor;
6use std::{
7  io::{Error, ErrorKind, Result},
8  path::{Path, PathBuf},
9};
10
11/// A cached version of the current binary using [`ctor`] to cache it before even `main` runs.
12#[ctor]
13#[used]
14pub(super) static STARTING_BINARY: StartingBinary = StartingBinary::new();
15
16/// Represents a binary path that was cached when the program was loaded.
17pub(super) struct StartingBinary(std::io::Result<PathBuf>);
18
19impl StartingBinary {
20  /// Find the starting executable as safely as possible.
21  fn new() -> Self {
22    // see notes on current_exe() for security implications
23    let dangerous_path = match std::env::current_exe() {
24      Ok(dangerous_path) => dangerous_path,
25      error @ Err(_) => return Self(error),
26    };
27
28    // note: this only checks symlinks on problematic platforms, see implementation below
29    if let Some(symlink) = Self::has_symlink(&dangerous_path) {
30      return Self(Err(Error::new(
31        ErrorKind::InvalidData,
32        format!("StartingBinary found current_exe() that contains a symlink on a non-allowed platform: {}", symlink.display()),
33      )));
34    }
35
36    // we canonicalize the path to resolve any symlinks to the real exe path
37    Self(dangerous_path.canonicalize())
38  }
39
40  /// A clone of the [`PathBuf`] found to be the starting path.
41  ///
42  /// Because [`Error`] is not clone-able, it is recreated instead.
43  pub(super) fn cloned(&self) -> Result<PathBuf> {
44    // false positive
45    #[allow(clippy::useless_asref)]
46    self
47      .0
48      .as_ref()
49      .map(Clone::clone)
50      .map_err(|e| Error::new(e.kind(), e.to_string()))
51  }
52
53  /// We only care about checking this on macOS currently, as it has the least symlink protections.
54  #[cfg(any(
55    not(target_os = "macos"),
56    feature = "process-relaunch-dangerous-allow-symlink-macos"
57  ))]
58  fn has_symlink(_: &Path) -> Option<&Path> {
59    None
60  }
61
62  /// We only care about checking this on macOS currently, as it has the least symlink protections.
63  #[cfg(all(
64    target_os = "macos",
65    not(feature = "process-relaunch-dangerous-allow-symlink-macos")
66  ))]
67  fn has_symlink(path: &Path) -> Option<&Path> {
68    path.ancestors().find(|ancestor| {
69      matches!(
70        ancestor
71          .symlink_metadata()
72          .as_ref()
73          .map(std::fs::Metadata::file_type)
74          .as_ref()
75          .map(std::fs::FileType::is_symlink),
76        Ok(true)
77      )
78    })
79  }
80}