diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-05-16 11:44:57 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-05-19 16:20:27 +0200 |
| commit | e8ffdb8325e80de5b756deac8f9a307a1d4dd2b4 (patch) | |
| tree | 5d80e5fd8b103293e3ce75a1d8ff680d03efabd5 | |
| parent | 14c90ebff746d4be8e9aad3ad566f5dee34a777f (diff) | |
| download | mullvadvpn-e8ffdb8325e80de5b756deac8f9a307a1d4dd2b4.tar.xz mullvadvpn-e8ffdb8325e80de5b756deac8f9a307a1d4dd2b4.zip | |
Move unix-specific code to own module in mullvad-paths
| -rw-r--r-- | mullvad-paths/src/cache.rs | 6 | ||||
| -rw-r--r-- | mullvad-paths/src/lib.rs | 108 | ||||
| -rw-r--r-- | mullvad-paths/src/logs.rs | 4 | ||||
| -rw-r--r-- | mullvad-paths/src/settings.rs | 2 | ||||
| -rw-r--r-- | mullvad-paths/src/unix.rs | 73 | ||||
| -rw-r--r-- | mullvad-paths/src/windows.rs | 14 |
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 { |
