From 367a2bc71a97111611b1993d67ea5d381f02df2b Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Wed, 15 Nov 2023 19:59:50 +0000 Subject: [PATCH] [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 --- Cargo.lock | Bin 239379 -> 240409 bytes apps/desktop/crates/macos/src/lib.rs | 4 +- crates/fda/Cargo.toml | 12 ++ crates/fda/README.md | 7 + crates/fda/src/error.rs | 13 ++ crates/fda/src/lib.rs | 121 ++++++++++++++++++ crates/images/src/generic.rs | 2 +- crates/images/src/heif.rs | 1 - crates/media-metadata/src/image/flash/data.rs | 2 +- .../media-metadata/src/image/flash/values.rs | 4 +- 10 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 crates/fda/Cargo.toml create mode 100644 crates/fda/README.md create mode 100644 crates/fda/src/error.rs create mode 100644 crates/fda/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 601db1aa17cd083826ac1f507c816d6600eb0338..47f6972415f90560885418d0ccaa7c34776d3eb5 100644 GIT binary patch delta 3694 zcmYLM4Uk?{d7g9cZZ`iv?6SMbW|K`!LJBcCzxSTQlvp{hMk zjMdR{FQ}zP8(qsl9rvy>GDWT=6<({l5}x{ebQ>9KUOjQ~NW3fFcIVq~z4^eM@wT{s zN^E6YX>ZfSqh)jIa}&K+*1So&Mf1vI42*p#C&z{D)|I*I&ciT>D9};t{dk(KgmTD_G=PGs>B57MO<*}~-u(1}E zQc{N?6*tbP=KWj8dQUjhN2s?f6ssG)WY-lsY2}4tGH_c(q*+m+jFZX=!?YJpvRXUu zlC)6;Dy-vy@~AlTvTp7@H`e^uun|){HsC zuB|iP#qOsApdrm@@A@N84)(XLzWm&LqI=7aesQ2V)^6;5=lJs_7HgY-eEhrkySh2^ zjel+)KRr8Ld!}QgF;vQ;PDwLwedSo-OrQ?|OR};Kk}Gb5WC_Su!3E8Em7=Kfv2A3u zp^q%cKYNaB>HY2#XGng1Gg;Q$vH8mFM(7$<4cOjk>e1s&F!UM@k*G!aw5>RGlm*8` z9h|MIDgf|coIuB)eWMs_ezt0)H~7sFQliWAj``(y^XnVNdcSsZoS@6XlLv?LW0#h* z(@I!XC0c7Lm}a$fHZae4O)HTC^(uwrgc3$a;X`dwvOo{XRK7ML>3pOp$9tbW9fz?i zdH&T7#EItn&(H6T{M*yZo0qrFPN!%vATEZwGOVVa0V}C|qSOOJd}+9i4g*tRM$o1r z))fVmno>>cF85+AAHKf4xOeD(zBLbF>>E!^Hs`kPn$||v4yhynER3Lv``THSB!H+9 zP(*{{8HH3mf&d+=0HdrJ3nsZ{-JPTP$(`juUN*lRYR`<5zuQPeKDL?g_RMy&dpz%Y zz1X!~n}C;PEk){vgi@YtWLnfR2F7A-YLOIRcfty;R0o&Tz>;CoIBj)0@-a}p( z1h0MKW-{KMxRGohBs}k+#m4;oL@sG}dU6S>(%uoshX@{fIg%If06k1r=G*p>jd|!M ztoo#2jETOcQZmQ;g;9#uRn03~IWMcq8^NP-!B*&H$WF`9EDC4=l3AVnj!;kEao!=;keLD#+c7vkFxIV zk@@+=OJrgG^MA(?@Az>sui1BGtbO1Bxn&^V^+C{}0jF#20IarNQqGx706YaQMRcy_ zwDL*w5Gxu4tGPi_P;i^`o;%I+sSl7%q)>g-2wcW ze<)YvRbM9y+i!k>^f~}h$+7nSN639eKJ_j#(O&i?vS%^wulN#~$Zd~Y)aUC>$l|dA1pf7U)oO@I!AeUMm{Ad2TyKM_M5zILay+d5lVfvO&vE@W2B)Iy!0v!<_LTD0hh} zQ6;8A9W8hA@1e&9-FSsylU)xt~0?&ZySX!8<WJ3fW;N%o{3;8R0xC3Ap`ly9yA=?P|j<& z|55RiqT6$JI-o)rs05WZ5FH6E5J|wT)Dp_AKhX|&*We__*; zHZ7nimto-Ovn z@wD{$;+mDDFMq6Ue_RZb{NmMcy9url-WJ9~*_0@(bp#Q^F(Oug%Zr{W2mfKAj~yC- zC?@5i)m8wzX2B~lqZdk`-Y8zVKwr~?o^YQ!oyQ!qG8wSsH!{42onLZD|uMASNl6e%hr;1pQ2(5~kF0~fcmOUsX6 zAi#qw%AtJ!aJeY|bfjFCm+v9V^0swQwB;k^Z|9%4UG6{j@>n_2E*>ktSoHO#yVpLu zvV2>Sr~Z|^V+Q05af552)S>PQ&4udG&M`wqaaR}%!2^MD#Z)CJ)Kz+p|7}E)uuJ*; zzjVi4T~4--O_Y0o5hVB4maChSZ*91c)DPE`*ML}_nkuLA%+|t$ha;!Z#}uVUX<#;CovbzJ-=wNRuZhWo>oeY5gMpy9fDJI<>huyFek(uJz@n{El_y|Dp*mUtjJSK$3gE zLPp!8o66@pF9PZrX%B5K^Z#E==9!bl#qIkpEeEFvYu2g_?WcdWT(*pu{L>rC>HOF^ lsOL-9m9G~A25g%jytW)`-@Kvx{l(2QlN0UXo67@N{2%MNg?Rt~ delta 3409 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"] +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" diff --git a/crates/fda/README.md b/crates/fda/README.md new file mode 100644 index 000000000..084028de4 --- /dev/null +++ b/crates/fda/README.md @@ -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. diff --git a/crates/fda/src/error.rs b/crates/fda/src/error.rs new file mode 100644 index 000000000..ca0986462 --- /dev/null +++ b/crates/fda/src/error.rs @@ -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 = std::result::Result; diff --git a/crates/fda/src/lib.rs b/crates/fda/src/lib.rs new file mode 100644 index 000000000..5149d8527 --- /dev/null +++ b/crates/fda/src/lib.rs @@ -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); + +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; + } +} diff --git a/crates/images/src/generic.rs b/crates/images/src/generic.rs index badd06568..fb7f09c68 100644 --- a/crates/images/src/generic.rs +++ b/crates/images/src/generic.rs @@ -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; diff --git a/crates/images/src/heif.rs b/crates/images/src/heif.rs index b3ba47ace..e698ec120 100644 --- a/crates/images/src/heif.rs +++ b/crates/images/src/heif.rs @@ -1,4 +1,3 @@ -pub use crate::consts::HEIF_EXTENSIONS; pub use crate::error::{Error, Result}; use crate::ImageHandler; use image::DynamicImage; diff --git a/crates/media-metadata/src/image/flash/data.rs b/crates/media-metadata/src/image/flash/data.rs index e5ca637bc..19cdae21c 100644 --- a/crates/media-metadata/src/image/flash/data.rs +++ b/crates/media-metadata/src/image/flash/data.rs @@ -26,7 +26,7 @@ impl Flash { #[must_use] pub fn from_reader(reader: &ExifReader) -> Option { let value = reader.get_tag_int(Tag::Flash)?; - FlashValue::try_from(value).ok()?.into() + FlashValue::from(value).into() } } diff --git a/crates/media-metadata/src/image/flash/values.rs b/crates/media-metadata/src/image/flash/values.rs index 8df205515..bbf3d62da 100644 --- a/crates/media-metadata/src/image/flash/values.rs +++ b/crates/media-metadata/src/image/flash/values.rs @@ -38,8 +38,8 @@ pub enum FlashValue { impl FlashValue { #[must_use] - pub fn new(value: u32) -> Option { - value.try_into().ok() + pub fn new(value: u32) -> Self { + value.into() } }