mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
[ENG-1026] Full Disk Access (#1778)
* basic FDA start * FDA checks and prompt * lockfile * misc clippy * Some small warnings --------- Co-authored-by: Ericson Fogo Soares <ericson.ds999@gmail.com>
This commit is contained in:
parent
3c264205e1
commit
367a2bc71a
325
Cargo.lock
generated
325
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,8 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use swift_rs::*;
|
||||
use swift_rs::{swift, Bool, Int, SRData, SRObjectArray, SRString};
|
||||
|
||||
pub type NSObject = *mut std::ffi::c_void;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum AppThemeType {
|
||||
Light = 0 as Int,
|
||||
Dark = 1 as Int,
|
||||
|
||||
12
crates/fda/Cargo.toml
Normal file
12
crates/fda/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "sd-fda"
|
||||
version = "0.1.0"
|
||||
authors = ["Jake Robinson <jake@spacedrive.com>"]
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
dirs = "5.0.1"
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "fs", "macros"] }
|
||||
thiserror = "1.0.50"
|
||||
7
crates/fda/README.md
Normal file
7
crates/fda/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Spacedrive FDA Handling
|
||||
|
||||
## Platforms
|
||||
|
||||
### MacOS
|
||||
|
||||
On MacOS, we are able to open the "Full disk access" settings prompt to instruct the user to allow Spacedrive full disk access, which should alleviate all permissions issues.
|
||||
13
crates/fda/src/error.rs
Normal file
13
crates/fda/src/error.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("unable to access path: {0}")]
|
||||
PermissionDenied(PathBuf),
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[error("there was an error while prompting for full disk access")]
|
||||
FDAPromptError,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
121
crates/fda/src/lib.rs
Normal file
121
crates/fda/src/lib.rs
Normal file
@ -0,0 +1,121 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
clippy::expect_used,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::as_conversions,
|
||||
clippy::dbg_macro,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(unsafe_code, deprecated_in_future)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
|
||||
|
||||
use std::{io::ErrorKind, path::PathBuf};
|
||||
|
||||
use dirs::{
|
||||
audio_dir, cache_dir, config_dir, config_local_dir, data_dir, data_local_dir, desktop_dir,
|
||||
document_dir, download_dir, executable_dir, home_dir, picture_dir, preference_dir, public_dir,
|
||||
runtime_dir, state_dir, template_dir, video_dir,
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
|
||||
use error::Result;
|
||||
|
||||
pub struct FullDiskAccess(Vec<PathBuf>);
|
||||
|
||||
impl FullDiskAccess {
|
||||
async fn can_access_path(path: PathBuf) -> bool {
|
||||
match tokio::fs::read_dir(path).await {
|
||||
Ok(_) => true,
|
||||
Err(e) => !matches!(e.kind(), ErrorKind::PermissionDenied),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn has_fda() -> bool {
|
||||
let dirs = Self::default();
|
||||
for dir in dirs.0 {
|
||||
if !Self::can_access_path(dir).await {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub fn request_fda() -> Result<()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use error::Error;
|
||||
use std::process::Command;
|
||||
|
||||
Command::new("open")
|
||||
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")
|
||||
.status()
|
||||
.map_err(|_| Error::FDAPromptError)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FullDiskAccess {
|
||||
fn default() -> Self {
|
||||
Self(
|
||||
[
|
||||
audio_dir(),
|
||||
cache_dir(),
|
||||
config_dir(),
|
||||
config_local_dir(),
|
||||
data_dir(),
|
||||
data_local_dir(),
|
||||
desktop_dir(),
|
||||
document_dir(),
|
||||
download_dir(),
|
||||
executable_dir(),
|
||||
home_dir(),
|
||||
picture_dir(),
|
||||
preference_dir(),
|
||||
public_dir(),
|
||||
runtime_dir(),
|
||||
state_dir(),
|
||||
template_dir(),
|
||||
video_dir(),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FullDiskAccess;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "Miri can't run this test")]
|
||||
#[ignore = "CI can't run this due to lack of a GUI"]
|
||||
fn macos_open_full_disk_prompt() {
|
||||
FullDiskAccess::request_fda().unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn has_fda() {
|
||||
FullDiskAccess::has_fda().await;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::error::Result;
|
||||
use crate::ImageHandler;
|
||||
use image::DynamicImage;
|
||||
use std::path::Path;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
pub use crate::consts::HEIF_EXTENSIONS;
|
||||
pub use crate::error::{Error, Result};
|
||||
use crate::ImageHandler;
|
||||
use image::DynamicImage;
|
||||
|
||||
@ -26,7 +26,7 @@ impl Flash {
|
||||
#[must_use]
|
||||
pub fn from_reader(reader: &ExifReader) -> Option<Self> {
|
||||
let value = reader.get_tag_int(Tag::Flash)?;
|
||||
FlashValue::try_from(value).ok()?.into()
|
||||
FlashValue::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,8 +38,8 @@ pub enum FlashValue {
|
||||
|
||||
impl FlashValue {
|
||||
#[must_use]
|
||||
pub fn new(value: u32) -> Option<Self> {
|
||||
value.try_into().ok()
|
||||
pub fn new(value: u32) -> Self {
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user