summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-05-16 11:44:57 +0200
committerDavid Lönnhager <david.l@mullvad.net>2025-05-19 16:20:27 +0200
commite8ffdb8325e80de5b756deac8f9a307a1d4dd2b4 (patch)
tree5d80e5fd8b103293e3ce75a1d8ff680d03efabd5
parent14c90ebff746d4be8e9aad3ad566f5dee34a777f (diff)
downloadmullvadvpn-e8ffdb8325e80de5b756deac8f9a307a1d4dd2b4.tar.xz
mullvadvpn-e8ffdb8325e80de5b756deac8f9a307a1d4dd2b4.zip
Move unix-specific code to own module in mullvad-paths
-rw-r--r--mullvad-paths/src/cache.rs6
-rw-r--r--mullvad-paths/src/lib.rs108
-rw-r--r--mullvad-paths/src/logs.rs4
-rw-r--r--mullvad-paths/src/settings.rs2
-rw-r--r--mullvad-paths/src/unix.rs73
-rw-r--r--mullvad-paths/src/windows.rs14
6 files changed, 106 insertions, 101 deletions
diff --git a/mullvad-paths/src/cache.rs b/mullvad-paths/src/cache.rs
index 801b1bfd3d..cd1763841c 100644
--- a/mullvad-paths/src/cache.rs
+++ b/mullvad-paths/src/cache.rs
@@ -5,9 +5,9 @@ use std::{env, path::PathBuf};
/// one if that variable is unset.
pub fn cache_dir() -> Result<PathBuf> {
#[cfg(target_os = "linux")]
- let permissions = crate::Permissions::Any;
+ let permissions = crate::unix::Permissions::Any;
#[cfg(target_os = "macos")]
- let permissions = crate::Permissions::ReadExecOnly;
+ let permissions = crate::unix::Permissions::ReadExecOnly;
#[cfg(target_os = "windows")]
let permissions = true;
crate::create_and_return(get_cache_dir()?, permissions)
@@ -28,7 +28,7 @@ pub fn get_default_cache_dir() -> Result<PathBuf> {
#[cfg(windows)]
pub fn get_default_cache_dir() -> Result<PathBuf> {
- let dir = crate::get_allusersprofile_dir()?
+ let dir = crate::windows::get_allusersprofile_dir()?
.join(crate::PRODUCT_NAME)
.join("cache");
Ok(dir)
diff --git a/mullvad-paths/src/lib.rs b/mullvad-paths/src/lib.rs
index 40f9f5ae49..4e7ab1dae2 100644
--- a/mullvad-paths/src/lib.rs
+++ b/mullvad-paths/src/lib.rs
@@ -1,13 +1,18 @@
#![cfg(not(target_os = "android"))]
-#[cfg(any(target_os = "linux", target_os = "macos"))]
-use std::fs;
-#[cfg(any(target_os = "linux", target_os = "macos"))]
-use std::path::Path;
-use std::{io, path::PathBuf};
+use std::io;
#[cfg(windows)]
-use crate::windows::create_dir_recursive;
+pub mod windows;
+
+#[cfg(windows)]
+pub use windows::PRODUCT_NAME;
+
+#[cfg(unix)]
+mod unix;
+
+#[cfg(unix)]
+pub use unix::PRODUCT_NAME;
pub type Result<T> = std::result::Result<T, Error>;
@@ -41,95 +46,11 @@ pub enum Error {
NoDataDir,
}
-#[cfg(any(target_os = "linux", target_os = "macos"))]
-const PRODUCT_NAME: &str = "mullvad-vpn";
-
-#[cfg(windows)]
-pub const PRODUCT_NAME: &str = "Mullvad VPN";
-
-#[cfg(unix)]
-#[derive(Clone, Copy, PartialEq)]
-enum Permissions {
- /// Do not set any particular permissions. They will be inherited instead.
- Any,
- /// Only root should have write access. Other users will have
- /// read and execute permissions (0o755).
- ReadExecOnly,
-}
-
-#[cfg(unix)]
-impl Permissions {
- fn fs_permissions(self) -> Option<fs::Permissions> {
- match self {
- Permissions::Any => None,
- Permissions::ReadExecOnly => Some(std::os::unix::fs::PermissionsExt::from_mode(0o755)),
- }
- }
-}
-
-#[cfg(windows)]
-fn get_allusersprofile_dir() -> Result<PathBuf> {
- match std::env::var_os("ALLUSERSPROFILE") {
- Some(dir) => Ok(PathBuf::from(&dir)),
- None => Err(Error::NoProgramDataDir),
- }
-}
-
-#[cfg(unix)]
-fn create_and_return(dir: PathBuf, permissions: Permissions) -> Result<PathBuf> {
- use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
-
- let mut dir_builder = fs::DirBuilder::new();
- let fs_perms = permissions.fs_permissions();
- if let Some(fs_perms) = fs_perms.as_ref() {
- dir_builder.mode(fs_perms.mode());
- }
- match dir_builder.create(&dir) {
- Ok(()) => Ok(dir),
- // The directory already exists
- Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {
- // If the permissions are wrong, delete the directory
- if !dir_is_root_owned(&dir, fs_perms.as_ref())? {
- fs::remove_dir_all(&dir)
- .or_else(|err| {
- // ENOTDIR: If the path is not a directory, try to remove the file
- if err.raw_os_error() == Some(20) {
- fs::remove_file(&dir)
- } else {
- Err(err)
- }
- })
- .map_err(|e| Error::RemoveDir(dir.display().to_string(), e))?;
- // Try to create it again
- return create_and_return(dir, permissions);
- }
- // Correct permissions, so we're done
- Ok(dir)
- }
- // Fail on any other error
- Err(error) => Err(Error::CreateDirFailed(dir.display().to_string(), error)),
- }
-}
-
#[cfg(unix)]
-fn dir_is_root_owned(dir: &Path, perms: Option<&fs::Permissions>) -> Result<bool> {
- use std::os::unix::fs::{MetadataExt, PermissionsExt};
-
- const RELEVANT_BITS: u32 = 0o777;
-
- let meta = fs::symlink_metadata(&dir)
- .map_err(|e| Error::GetDirPermissionFailed(dir.display().to_string(), e))?;
- let matching_perms = perms
- .map(|perms| (perms.mode() & RELEVANT_BITS) == (meta.permissions().mode() & RELEVANT_BITS))
- .unwrap_or(true);
- Ok(matching_perms && meta.uid() == 0)
-}
+use unix::create_and_return;
#[cfg(windows)]
-fn create_and_return(dir: PathBuf, set_security_permissions: bool) -> Result<PathBuf> {
- create_dir_recursive(&dir, set_security_permissions)?;
- Ok(dir)
-}
+use windows::create_and_return;
mod cache;
pub use crate::cache::{cache_dir, get_cache_dir, get_default_cache_dir};
@@ -145,6 +66,3 @@ pub use crate::rpc_socket::{get_default_rpc_socket_path, get_rpc_socket_path};
mod settings;
pub use crate::settings::{get_default_settings_dir, settings_dir};
-
-#[cfg(windows)]
-pub mod windows;
diff --git a/mullvad-paths/src/logs.rs b/mullvad-paths/src/logs.rs
index eaed478794..e7940a51d5 100644
--- a/mullvad-paths/src/logs.rs
+++ b/mullvad-paths/src/logs.rs
@@ -6,7 +6,7 @@ use std::{env, path::PathBuf};
pub fn log_dir() -> Result<PathBuf> {
#[cfg(unix)]
{
- crate::create_and_return(get_log_dir()?, crate::Permissions::ReadExecOnly)
+ crate::create_and_return(get_log_dir()?, crate::unix::Permissions::ReadExecOnly)
}
#[cfg(target_os = "windows")]
{
@@ -30,6 +30,6 @@ pub fn get_default_log_dir() -> Result<PathBuf> {
#[cfg(windows)]
pub fn get_default_log_dir() -> Result<PathBuf> {
- let dir = crate::get_allusersprofile_dir()?.join(crate::PRODUCT_NAME);
+ let dir = crate::windows::get_allusersprofile_dir()?.join(crate::PRODUCT_NAME);
Ok(dir)
}
diff --git a/mullvad-paths/src/settings.rs b/mullvad-paths/src/settings.rs
index d4f154495e..6ff0a66a4d 100644
--- a/mullvad-paths/src/settings.rs
+++ b/mullvad-paths/src/settings.rs
@@ -6,7 +6,7 @@ use std::{env, path::PathBuf};
pub fn settings_dir() -> Result<PathBuf> {
#[cfg(not(target_os = "windows"))]
{
- crate::create_and_return(get_settings_dir()?, crate::Permissions::Any)
+ crate::create_and_return(get_settings_dir()?, crate::unix::Permissions::Any)
}
#[cfg(target_os = "windows")]
diff --git a/mullvad-paths/src/unix.rs b/mullvad-paths/src/unix.rs
new file mode 100644
index 0000000000..7dd7112259
--- /dev/null
+++ b/mullvad-paths/src/unix.rs
@@ -0,0 +1,73 @@
+use std::{
+ fs, io,
+ os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt},
+ path::{Path, PathBuf},
+};
+
+use crate::{Error, Result};
+
+pub const PRODUCT_NAME: &str = "mullvad-vpn";
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum Permissions {
+ /// Do not set any particular permissions. They will be inherited instead.
+ Any,
+ /// Only root should have write access. Other users will have
+ /// read and execute permissions (0o755).
+ ReadExecOnly,
+}
+
+impl Permissions {
+ fn fs_permissions(self) -> Option<fs::Permissions> {
+ match self {
+ Permissions::Any => None,
+ Permissions::ReadExecOnly => Some(std::os::unix::fs::PermissionsExt::from_mode(0o755)),
+ }
+ }
+}
+
+pub fn create_and_return(dir: PathBuf, permissions: Permissions) -> Result<PathBuf> {
+ let mut dir_builder = fs::DirBuilder::new();
+ let fs_perms = permissions.fs_permissions();
+ if let Some(fs_perms) = fs_perms.as_ref() {
+ dir_builder.mode(fs_perms.mode());
+ }
+ match dir_builder.create(&dir) {
+ Ok(()) => Ok(dir),
+ // The directory already exists
+ Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {
+ // If the permissions are wrong, delete the directory and recreate it
+ if !dir_is_root_owned(&dir, fs_perms.as_ref())? {
+ fs::remove_dir_all(&dir)
+ .or_else(|err| {
+ // ENOTDIR: If the path is not a directory, try to remove the file
+ if err.raw_os_error() == Some(20) {
+ fs::remove_file(&dir)
+ } else {
+ Err(err)
+ }
+ })
+ .map_err(|e| Error::RemoveDir(dir.display().to_string(), e))?;
+
+ // Try to create it again
+ return create_and_return(dir, permissions);
+ }
+ // Correct permissions, so we're done
+ Ok(dir)
+ }
+ // Fail on any other error
+ Err(error) => Err(Error::CreateDirFailed(dir.display().to_string(), error)),
+ }
+}
+
+/// Return whether the directofy is owned by root and, optionally, has the given permissions set
+fn dir_is_root_owned(dir: &Path, perms: Option<&fs::Permissions>) -> Result<bool> {
+ const RELEVANT_BITS: u32 = 0o777;
+
+ let meta = fs::symlink_metadata(&dir)
+ .map_err(|e| Error::GetDirPermissionFailed(dir.display().to_string(), e))?;
+ let matching_perms = perms
+ .map(|perms| (perms.mode() & RELEVANT_BITS) == (meta.permissions().mode() & RELEVANT_BITS))
+ .unwrap_or(true);
+ Ok(matching_perms && meta.uid() == 0)
+}
diff --git a/mullvad-paths/src/windows.rs b/mullvad-paths/src/windows.rs
index fa652f31f7..06db6a7752 100644
--- a/mullvad-paths/src/windows.rs
+++ b/mullvad-paths/src/windows.rs
@@ -45,6 +45,20 @@ use windows_sys::{
},
};
+pub const PRODUCT_NAME: &str = "Mullvad VPN";
+
+pub fn get_allusersprofile_dir() -> Result<PathBuf> {
+ match std::env::var_os("ALLUSERSPROFILE") {
+ Some(dir) => Ok(PathBuf::from(&dir)),
+ None => Err(Error::NoProgramDataDir),
+ }
+}
+
+pub fn create_and_return(dir: PathBuf, set_security_permissions: bool) -> Result<PathBuf> {
+ create_dir_recursive(&dir, set_security_permissions)?;
+ Ok(dir)
+}
+
struct Handle(HANDLE);
impl Drop for Handle {