diff --git a/clippy.toml b/clippy.toml index 1aa6f67..b08c276 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -allowed-duplicate-crates = ["syn", "socket2", "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", 'windows_x86_64_gnu', "windows_x86_64_gnullvm", "windows_x86_64_msvc", "memoffset", "nix", "parking_lot_core", "polling", "redox_syscall", "regex-automata", "regex-syntax", "rustix", "sha2", "bitflags", "block-buffer", "crypto-common", "digest", "event-listener", "event-listener-strategy", "fastrand", "futures-lite", "async-io", "async-lock", "heck", "linux-raw-sys"] \ No newline at end of file +allowed-duplicate-crates = ["syn", "socket2", "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", 'windows_x86_64_gnu', "windows_x86_64_gnullvm", "windows_x86_64_msvc", "memoffset", "nix", "parking_lot_core", "polling", "redox_syscall", "regex-automata", "regex-syntax", "rustix", "sha2", "bitflags", "block-buffer", "crypto-common", "digest", "event-listener", "event-listener-strategy", "fastrand", "futures-lite", "async-io", "async-lock", "heck", "linux-raw-sys", "arrayvec", "cfg-if"] \ No newline at end of file diff --git a/src/crypto.rs b/src/crypto.rs index f7b9615..6a3be19 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -226,11 +226,7 @@ pub fn decrypt_file_name( #[instrument(skip(password, salt))] #[allow(clippy::missing_errors_doc)] -pub fn derive_key( - password: &SecretString, - cipher: Cipher, - salt: &Vec, -) -> Result> { +pub fn derive_key(password: &SecretString, cipher: Cipher, salt: &[u8]) -> Result> { let mut dk = vec![]; let key_len = cipher.key_len(); dk.resize(key_len, 0); diff --git a/src/crypto/reader.rs b/src/crypto/reader.rs index ef78340..e1ed567 100644 --- a/src/crypto/reader.rs +++ b/src/crypto/reader.rs @@ -513,17 +513,18 @@ impl CryptoReader for FileCryptoReader {} // impl CryptoReader for ChunkedFileCryptoReader {} mod bench { - use crate::crypto; - use crate::crypto::Cipher; - use rand::RngCore; - use secrecy::SecretVec; use std::io; use std::io::Seek; use std::io::Write; use std::sync::Arc; use test::{black_box, Bencher}; + use rand::RngCore; + use secrecy::SecretVec; + + use crate::crypto; use crate::crypto::writer::CryptoWriter; + use crate::crypto::Cipher; #[bench] fn bench_reader_10mb_cha_cha20poly1305_file(b: &mut Bencher) { diff --git a/src/crypto/writer.rs b/src/crypto/writer.rs index 8d913fd..21f74b6 100644 --- a/src/crypto/writer.rs +++ b/src/crypto/writer.rs @@ -836,7 +836,6 @@ impl CryptoWriterSeek for FileCryptoWriter {} #[cfg(test)] mod test { - use blake3::portable::hash1; use std::io; use std::io::Write; use std::io::{Read, Seek}; @@ -932,7 +931,8 @@ mod test { mod bench { use ::test::{black_box, Bencher}; use std::io; - use std::io::{Error, SeekFrom, Write}; + use std::io::Write; + use std::io::{Error, SeekFrom}; use std::io::{Read, Seek}; use std::sync::Arc; @@ -1064,6 +1064,9 @@ mod bench { } impl Seek for RandomReader { + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] fn seek(&mut self, pos: SeekFrom) -> io::Result { let new_pos = match pos { SeekFrom::Start(pos) => pos as i64, diff --git a/src/main.rs b/src/main.rs index 7d282ba..775e6c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ +use std::cell::RefCell; use std::ffi::OsStr; use std::io::Write; use std::path::{Path, PathBuf}; +use std::rc::Rc; use std::str::FromStr; +use std::sync::{Arc, Mutex}; use std::{env, io, panic, process}; use anyhow::Result; @@ -19,7 +22,8 @@ use tracing_subscriber::EnvFilter; use rencfs::crypto::Cipher; use rencfs::encryptedfs::{EncryptedFs, FsError, PasswordProvider}; -use rencfs::{is_debug, mount}; +use rencfs::mount::MountPoint; +use rencfs::{async_util, is_debug, mount}; mod keyring; @@ -160,14 +164,6 @@ fn get_cli_args() -> ArgMatches { .action(ArgAction::SetTrue) .help("If we should try to umount the mountpoint before starting the FUSE server. This can be useful when the previous run crashed or was forced kll and the mountpoint is still mounted."), ) - .arg( - Arg::new("auto_unmount") - .long("auto_unmount") - .short('x') - .default_value("true") - .action(ArgAction::SetTrue) - .help("Automatically unmount on process exit"), - ) .arg( Arg::new("allow-root") .long("allow-root") @@ -336,29 +332,6 @@ async fn run_mount(cipher: Cipher, matches: &ArgMatches) -> Result<()> { }); } - let auto_unmount = matches.get_flag("auto_unmount"); - let mountpoint_kill = mountpoint.clone(); - // unmount on process kill - set_handler(move || { - info!("Received signal to exit"); - let mut status: Option = None; - remove_pass(); - if auto_unmount { - info!("Unmounting {}", mountpoint_kill); - } - umount(mountpoint_kill.as_str()) - .map_err(|err| { - error!(err = %err); - status.replace(ExitStatusError::Failure(1)); - }) - .ok(); - - process::exit(status.map_or(0, |x| match x { - ExitStatusError::Failure(status) => status, - })); - }) - .unwrap(); - #[allow(clippy::items_after_statements)] struct PasswordProviderImpl {} #[allow(clippy::items_after_statements)] @@ -380,7 +353,6 @@ async fn run_mount(cipher: Cipher, matches: &ArgMatches) -> Result<()> { } } } - let mut mount_point = mount::create_mount_point( Path::new(&mountpoint).to_path_buf(), Path::new(&data_dir).to_path_buf(), @@ -391,11 +363,62 @@ async fn run_mount(cipher: Cipher, matches: &ArgMatches) -> Result<()> { matches.get_flag("direct-io"), matches.get_flag("suid"), ); - mount_point.mount().await?; - - remove_pass(); - - info!("Bye!"); + let mount_handle = mount_point.mount().await.map_err(|err| { + error!(err = %err); + ExitStatusError::Failure(1) + })?; + let mut mount_handle = Arc::new(Mutex::new(Some(Some(mount_handle)))); + let mount_handle_clone = mount_handle.clone(); + // cleanup on process kill + set_handler(move || { + // can't use tracing methods here as guard cannot be dropper to flush content before we exit + eprintln!("Received signal to exit"); + let mut status: Option = None; + remove_pass(); + eprintln!("Unmounting {}", mountpoint); + // create new tokio runtime + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let _ = rt + .block_on(async { + mount_handle_clone + .lock() + .unwrap() + .replace(None) + .unwrap() + .unwrap() + .umount() + .await?; + Ok::<(), io::Error>(()) + }) + .map_err(|err| { + eprintln!("Error: {}", err); + status.replace(ExitStatusError::Failure(1)); + err + }); + eprintln!("Bye!"); + process::exit(status.map_or(0, |x| match x { + ExitStatusError::Failure(status) => status, + })); + })?; + + // mount_handle + // .lock() + // .unwrap() + // .as_mut() + // .unwrap() + // .as_mut() + // .unwrap() + // .await?; + task::spawn_blocking(|| { + let rt = tokio::runtime::Handle::current(); + rt.block_on(async { + tokio::time::sleep(tokio::time::Duration::from_secs(u64::MAX)).await; + }) + }) + .await?; Ok(()) } diff --git a/src/mount.rs b/src/mount.rs index 40a8a4e..7c1a276 100644 --- a/src/mount.rs +++ b/src/mount.rs @@ -1,27 +1,77 @@ use crate::crypto::Cipher; use crate::encryptedfs::{FsResult, PasswordProvider}; use async_trait::async_trait; +use futures_util::FutureExt; +use std::future::Future; +use std::io; use std::path::PathBuf; +use std::pin::Pin; +use std::task::{Context, Poll}; #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "linux")] -use linux::Fuse3MountPoint as MountPointImpl; +use linux::MountHandleInnerImpl; +#[cfg(target_os = "linux")] +use linux::MountPointImpl; + #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] -use macos::MacOsFuse3MountPoint as MountPointImpl; +use macos::MountHandleInnerImpl; +#[cfg(target_os = "macos")] +use macos::MountPointImpl; + #[cfg(target_os = "windows")] mod windows; #[cfg(target_os = "windows")] -use windows::WindowsMountPoint as MountPointImpl; +use windows::MountHandleInnerImpl; +#[cfg(target_os = "windows")] +use windows::MountPointImpl; #[async_trait] +#[allow(clippy::module_name_repetitions)] +#[allow(clippy::struct_excessive_bools)] pub trait MountPoint { - async fn mount(&mut self) -> FsResult<()>; - async fn umount(&mut self) -> FsResult<()>; + fn new( + mountpoint: PathBuf, + data_dir: PathBuf, + password_provider: Box, + cipher: Cipher, + allow_root: bool, + allow_other: bool, + direct_io: bool, + suid_support: bool, + ) -> Self + where + Self: Sized; + async fn mount(mut self) -> FsResult; +} + +pub struct MountHandle { + inner: MountHandleInnerImpl, +} +impl MountHandle { + pub async fn umount(mut self) -> io::Result<()> { + self.inner.umount().await + } +} + +impl Future for MountHandle { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.inner.poll_unpin(cx) + } +} + +#[async_trait] +pub(crate) trait MountHandleInner: Future> { + async fn umount(mut self) -> io::Result<()>; } +#[must_use] +#[allow(clippy::struct_excessive_bools)] pub fn create_mount_point( mountpoint: PathBuf, data_dir: PathBuf, @@ -31,8 +81,8 @@ pub fn create_mount_point( allow_other: bool, direct_io: bool, suid_support: bool, -) -> Box { - Box::new(MountPointImpl::new( +) -> impl MountPoint { + MountPointImpl::new( mountpoint, data_dir, password_provider, @@ -41,5 +91,5 @@ pub fn create_mount_point( allow_other, direct_io, suid_support, - )) + ) } diff --git a/src/mount/linux.rs b/src/mount/linux.rs index 70f54c9..0126f6f 100644 --- a/src/mount/linux.rs +++ b/src/mount/linux.rs @@ -1,15 +1,19 @@ -use async_trait::async_trait; use std::ffi::{OsStr, OsString}; use std::fs::File; +use std::future::Future; +use std::io; use std::io::{BufRead, BufReader}; use std::iter::Skip; use std::num::NonZeroU32; use std::os::raw::c_int; use std::path::PathBuf; +use std::pin::Pin; use std::str::FromStr; use std::sync::Arc; +use std::task::{Context, Poll}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use async_trait::async_trait; use bytes::Bytes; use fuse3::raw::prelude::{ DirectoryEntry, DirectoryEntryPlus, ReplyAttr, ReplyCopyFileRange, ReplyCreated, ReplyData, @@ -17,8 +21,8 @@ use fuse3::raw::prelude::{ }; use fuse3::raw::{Filesystem, MountHandle, Request, Session}; use fuse3::{Errno, Inode, MountOptions, Result, SetAttr, Timestamp}; -use futures_util::stream; use futures_util::stream::Iter; +use futures_util::{stream, FutureExt}; use libc::{EACCES, EEXIST, EFBIG, EIO, EISDIR, ENAMETOOLONG, ENOENT, ENOTDIR, ENOTEMPTY, EPERM}; use secrecy::{ExposeSecret, SecretString}; use tracing::{debug, error, instrument, trace, warn}; @@ -29,7 +33,8 @@ use crate::encryptedfs::{ CreateFileAttr, EncryptedFs, FileAttr, FileType, FsError, FsResult, PasswordProvider, SetFileAttr, }; -use crate::mount::MountPoint; +use crate::mount; +use crate::mount::{MountHandleInner, MountPoint}; const TTL: Duration = Duration::from_secs(1); const STATFS: ReplyStatFs = ReplyStatFs { @@ -1370,7 +1375,8 @@ fn system_time_from_timestamp(t: Timestamp) -> SystemTime { UNIX_EPOCH + Duration::new(t.sec as u64, t.nsec) } -pub(super) struct Fuse3MountPoint { +#[allow(clippy::struct_excessive_bools)] +pub struct MountPointImpl { mountpoint: PathBuf, data_dir: PathBuf, password_provider: Option>, @@ -1379,10 +1385,11 @@ pub(super) struct Fuse3MountPoint { allow_other: bool, direct_io: bool, suid_support: bool, - mount_handle: Option, // todo: see how we can use it later to umount } -impl Fuse3MountPoint { - pub(super) fn new( + +#[async_trait] +impl MountPoint for MountPointImpl { + fn new( mountpoint: PathBuf, data_dir: PathBuf, password_provider: Box, @@ -1401,37 +1408,48 @@ impl Fuse3MountPoint { allow_other, direct_io, suid_support, - mount_handle: None, } } + + async fn mount(mut self) -> FsResult { + let handle = mount_fuse( + self.mountpoint.clone(), + self.data_dir.clone(), + self.password_provider.take().unwrap(), + self.cipher, + self.allow_root, + self.allow_other, + self.direct_io, + self.suid_support, + ) + .await?; + Ok(mount::MountHandle { + inner: MountHandleInnerImpl { inner: handle }, + }) + } } -#[async_trait] -impl MountPoint for Fuse3MountPoint { - async fn mount(&mut self) -> FsResult<()> { - self.mount_handle = Some( - run_fuse( - self.mountpoint.clone(), - self.data_dir.clone(), - self.password_provider.take().unwrap(), - self.cipher, - self.allow_root, - self.allow_other, - self.direct_io, - self.suid_support, - ) - .await?, - ); - Ok(()) +pub(in crate::mount) struct MountHandleInnerImpl { + inner: MountHandle, +} + +impl Future for MountHandleInnerImpl { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.inner.poll_unpin(cx) } +} - async fn umount(&mut self) -> FsResult<()> { - todo!() +#[async_trait] +impl MountHandleInner for MountHandleInnerImpl { + async fn umount(mut self) -> io::Result<()> { + self.inner.unmount().await } } #[instrument(skip(password_provider))] -pub async fn run_fuse( +async fn mount_fuse( mountpoint: PathBuf, data_dir: PathBuf, password_provider: Box, diff --git a/src/mount/macos.rs b/src/mount/macos.rs index 6c06ce2..7d8784c 100644 --- a/src/mount/macos.rs +++ b/src/mount/macos.rs @@ -1,11 +1,16 @@ -use crate::crypto::Cipher; -use crate::encryptedfs::{FsResult, PasswordProvider}; +use crate::mount::MountHandleInner; use crate::mount::MountPoint; use async_trait::async_trait; +use std::future::Future; +use std::io; use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; use tracing::{error, warn}; -pub(super) struct MacOsFuse3MountPoint { +#[allow(clippy::struct_excessive_bools)] +pub struct MountPointImpl { mountpoint: PathBuf, data_dir: PathBuf, password_provider: Option>, @@ -15,8 +20,10 @@ pub(super) struct MacOsFuse3MountPoint { direct_io: bool, suid_support: bool, } -impl MacOsFuse3MountPoint { - pub(super) fn new( + +#[async_trait] +impl MountPoint for MountPointImpl { + fn new( mountpoint: PathBuf, data_dir: PathBuf, password_provider: Box, @@ -37,16 +44,39 @@ impl MacOsFuse3MountPoint { suid_support, } } + + async fn mount(mut self) -> FsResult { + let handle = mount_fuse( + self.mountpoint.clone(), + self.data_dir.clone(), + self.password_provider.take().unwrap(), + self.cipher, + self.allow_root, + self.allow_other, + self.direct_io, + self.suid_support, + ) + .await?; + Ok(mount::MountHandle { + inner: MountHandleInnerImpl {}, + }) + } } -#[async_trait] -impl MountPoint for MacOsFuse3MountPoint { - async fn mount(&mut self) -> FsResult<()> { +pub(in crate::mount) struct MountHandleInnerImpl {} + +impl Future for MountHandleInnerImpl { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { error!("he he, not yet ready for this platform, but soon my friend, soon :)"); - Ok(()) + Poll::Ready(Ok(())) } +} - async fn umount(&mut self) -> FsResult<()> { - todo!() +#[async_trait] +impl MountHandleInner for MountHandleInnerImpl { + async fn umount(mut self) -> io::Result<()> { + Ok(()) } } diff --git a/src/mount/windows.rs b/src/mount/windows.rs index 1fdab2a..7d8784c 100644 --- a/src/mount/windows.rs +++ b/src/mount/windows.rs @@ -1,11 +1,16 @@ -use crate::crypto::Cipher; -use crate::encryptedfs::{FsResult, PasswordProvider}; +use crate::mount::MountHandleInner; use crate::mount::MountPoint; use async_trait::async_trait; +use std::future::Future; +use std::io; use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; use tracing::{error, warn}; -pub(super) struct WindowsMountPoint { +#[allow(clippy::struct_excessive_bools)] +pub struct MountPointImpl { mountpoint: PathBuf, data_dir: PathBuf, password_provider: Option>, @@ -15,8 +20,10 @@ pub(super) struct WindowsMountPoint { direct_io: bool, suid_support: bool, } -impl WindowsMountPoint { - pub(super) fn new( + +#[async_trait] +impl MountPoint for MountPointImpl { + fn new( mountpoint: PathBuf, data_dir: PathBuf, password_provider: Box, @@ -37,16 +44,39 @@ impl WindowsMountPoint { suid_support, } } + + async fn mount(mut self) -> FsResult { + let handle = mount_fuse( + self.mountpoint.clone(), + self.data_dir.clone(), + self.password_provider.take().unwrap(), + self.cipher, + self.allow_root, + self.allow_other, + self.direct_io, + self.suid_support, + ) + .await?; + Ok(mount::MountHandle { + inner: MountHandleInnerImpl {}, + }) + } } -#[async_trait] -impl MountPoint for WindowsMountPoint { - async fn mount(&mut self) -> FsResult<()> { +pub(in crate::mount) struct MountHandleInnerImpl {} + +impl Future for MountHandleInnerImpl { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { error!("he he, not yet ready for this platform, but soon my friend, soon :)"); - Ok(()) + Poll::Ready(Ok(())) } +} - async fn umount(&mut self) -> FsResult<()> { - todo!() +#[async_trait] +impl MountHandleInner for MountHandleInnerImpl { + async fn umount(mut self) -> io::Result<()> { + Ok(()) } }