[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:
jake 2023-11-15 19:59:50 +00:00 committed by GitHub
parent 3c264205e1
commit 367a2bc71a
10 changed files with 342 additions and 149 deletions

325
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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
View 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
View 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;
}
}

View File

@ -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;

View File

@ -1,4 +1,3 @@
pub use crate::consts::HEIF_EXTENSIONS;
pub use crate::error::{Error, Result};
use crate::ImageHandler;
use image::DynamicImage;

View File

@ -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()
}
}

View File

@ -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()
}
}