From 1a451279df20e1c92f5185eca62acdfa2febc186 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Thu, 9 May 2024 21:13:39 +0300 Subject: [PATCH] fix clippy errors and warings --- .idea/inspectionProfiles/Project_Default.xml | 10 + README.md | 10 +- clippy.toml | 1 + examples/crypto.rs | 10 +- src/arc_hashmap.rs | 41 +- src/crypto.rs | 97 ++-- src/crypto/buf_mut.rs | 35 +- src/crypto/reader.rs | 45 +- src/crypto/writer.rs | 64 +-- src/encryptedfs.rs | 506 ++++++++++--------- src/encryptedfs_fuse3.rs | 143 +++--- src/expire_value.rs | 32 +- src/keyring.rs | 6 +- src/lib.rs | 15 +- src/main.rs | 72 ++- src/stream_util.rs | 21 +- 16 files changed, 615 insertions(+), 493 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 clippy.toml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..5da4a46d --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index e9834f51..f437a0c7 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ You can specify the log level adding the `--log-level` argument to the command l values: `TRACE`, `DEBUG`, `INFO` (default), `WARN`, `ERROR`. ```bash ---log-level LEVEL +rencfs --log-level LEVEL ... ``` ## Start it in docker @@ -229,6 +229,14 @@ cargo build --release cargo run -- --mount-point MOUNT_POINT --data-dir DATA_DIR ``` +## Developing inside a Container + +See here how to configure for [VsCode](https://code.visualstudio.com/docs/devcontainers/containers)\ +And here for [RustRover](https://www.jetbrains.com/help/rust/connect-to-devcontainer.html) + +You can use the `.devcontainer` directory from the project to start a container with all the necessary tools to build +and run the app. + # Future - Plan is to implement it also on macOS and Windows diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..c758cbc0 --- /dev/null +++ b/clippy.toml @@ -0,0 +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", "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 diff --git a/examples/crypto.rs b/examples/crypto.rs index 6bcf4f13..fe34c962 100644 --- a/examples/crypto.rs +++ b/examples/crypto.rs @@ -7,14 +7,14 @@ use std::sync::Arc; use secrecy::SecretString; use rencfs::crypto; -use rencfs::crypto::writer::{CryptoWriter, FileCryptoWriterCallback}; +use rencfs::crypto::writer::FileCryptoWriterCallback; use rencfs::crypto::Cipher; fn main() -> anyhow::Result<()> { let password = SecretString::new("password".to_string()); let salt = crypto::hash_secret_string(&password); let cipher = Cipher::ChaCha20; - let key = Arc::new(crypto::derive_key(&password, &cipher, salt).unwrap()); + let key = Arc::new(crypto::derive_key(&password, cipher, salt).unwrap()); let mut args = args(); let path_in = args.next().expect("path_in is missing"); @@ -39,8 +39,8 @@ fn main() -> anyhow::Result<()> { } let mut file = File::open(path_in.clone()).unwrap(); let mut writer = crypto::create_file_writer( - Path::new(&path_out).to_path_buf(), - Path::new(&"/tmp").to_path_buf(), + &Path::new(&path_out).to_path_buf(), + &Path::new(&"/tmp").to_path_buf(), cipher, key.clone(), 42_u64, @@ -51,7 +51,7 @@ fn main() -> anyhow::Result<()> { writer.finish().unwrap(); let mut reader = crypto::create_file_reader( - Path::new(&path_out).to_path_buf(), + &Path::new(&path_out).to_path_buf(), cipher, key.clone(), 42_u64, diff --git a/src/arc_hashmap.rs b/src/arc_hashmap.rs index e405efb1..a1fe55cc 100644 --- a/src/arc_hashmap.rs +++ b/src/arc_hashmap.rs @@ -1,14 +1,15 @@ use std::collections::HashMap; use std::hash::Hash; use std::ops::Deref; -use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; +type Value = (Arc, Arc); pub struct ArcHashMap where K: Eq + Hash, { - map: Mutex, Arc)>>, + map: Mutex>>, } pub struct Guard { @@ -27,28 +28,31 @@ impl Deref for Guard { type Target = V; fn deref(&self) -> &Self::Target { - &*self.val + &self.val } } -impl ArcHashMap { - pub fn new() -> Self { - ArcHashMap { +impl Default for ArcHashMap { + fn default() -> Self { + Self { map: Mutex::new(HashMap::new()), } } +} +impl ArcHashMap { pub fn insert(&self, key: K, value: V) -> Guard { self.purge(); self.get_or_insert_with(key, || value) } - pub fn get<'a>(&self, key: &K) -> Option> { + #[allow(clippy::missing_panics_doc)] + pub fn get(&self, key: &K) -> Option> { self.purge(); - self.get_internal(self.map.lock().unwrap().get(key)) + Self::get_internal(self.map.lock().expect("cannot obtain lock").get(key)) } - pub fn get_internal<'a>(&self, v: Option<&(Arc, Arc)>) -> Option> { + fn get_internal(v: Option<&Value>) -> Option> { if let Some((v, rc)) = v { rc.fetch_add(1, Ordering::SeqCst); return Some(Guard { @@ -59,16 +63,18 @@ impl ArcHashMap { None } + #[allow(clippy::missing_panics_doc)] pub fn get_or_insert_with(&self, key: K, f: F) -> Guard where F: FnOnce() -> V, { self.purge(); - let mut map = self.map.lock().unwrap(); - let v = map - .entry(key) - .or_insert_with(|| (Arc::new(f()), Arc::new(AtomicUsize::new(0)))); - self.get_internal(Some(v)).unwrap() + let mut map = self.map.lock().expect("cannot obtain lock"); + Self::get_internal(Some( + map.entry(key) + .or_insert_with(|| (Arc::new(f()), Arc::new(AtomicUsize::new(0)))), + )) + .unwrap() } fn purge(&self) { @@ -76,8 +82,13 @@ impl ArcHashMap { map.retain(|_, v| v.1.load(Ordering::SeqCst) > 0); } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[allow(clippy::missing_panics_doc)] pub fn len(&self) -> usize { self.purge(); - self.map.lock().unwrap().len() + self.map.lock().expect("cannot obtain lock").len() } } diff --git a/src/crypto.rs b/src/crypto.rs index a7c89cdc..f768266f 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -2,7 +2,7 @@ use std::fs::{File, OpenOptions}; use std::io; use std::io::{Read, Seek, Write}; use std::num::ParseIntError; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use argon2::Argon2; @@ -35,7 +35,9 @@ pub mod writer; pub static BASE64: GeneralPurpose = GeneralPurpose::new(&STANDARD, NO_PAD); -#[derive(Debug, Clone, Copy, EnumIter, EnumString, Display, Serialize, Deserialize, PartialEq)] +#[derive( + Debug, Clone, Copy, EnumIter, EnumString, Display, Serialize, Deserialize, PartialEq, Eq, +)] pub enum Cipher { ChaCha20, Aes256Gcm, @@ -85,16 +87,16 @@ pub type Result = std::result::Result; pub fn create_writer( writer: W, - cipher: &Cipher, - key: Arc>, + cipher: Cipher, + key: &Arc>, nonce_seed: u64, ) -> impl CryptoWriter { create_ring_writer(writer, cipher, key, nonce_seed) } - +#[allow(clippy::missing_errors_doc)] pub fn create_file_writer( - file: PathBuf, - tmp_dir: PathBuf, + file: &Path, + tmp_dir: &Path, cipher: Cipher, key: Arc>, nonce_seed: u64, @@ -107,8 +109,8 @@ pub fn create_file_writer( fn create_ring_writer( writer: W, - cipher: &Cipher, - key: Arc>, + cipher: Cipher, + key: &Arc>, nonce_seed: u64, ) -> RingCryptoWriter { let algorithm = match cipher { @@ -120,7 +122,7 @@ fn create_ring_writer( fn create_ring_reader( reader: R, - cipher: &Cipher, + cipher: Cipher, key: Arc>, nonce_seed: u64, ) -> RingCryptoReader { @@ -150,15 +152,16 @@ fn create_ring_reader( pub fn create_reader( reader: R, - cipher: &Cipher, + cipher: Cipher, key: Arc>, nonce_seed: u64, ) -> impl CryptoReader { create_ring_reader(reader, cipher, key, nonce_seed) } +#[allow(clippy::missing_errors_doc)] pub fn create_file_reader( - file: PathBuf, + file: &Path, cipher: Cipher, key: Arc>, nonce_seed: u64, @@ -192,10 +195,11 @@ pub fn create_file_reader( // } /// `nonce_seed`: If we should include the nonce seed in the result so that it can be used when decrypting. +#[allow(clippy::missing_errors_doc)] pub fn encrypt_string_with_nonce_seed( s: &SecretString, - cipher: &Cipher, - key: Arc>, + cipher: Cipher, + key: &Arc>, nonce_seed: u64, include_nonce_seed: bool, ) -> Result { @@ -213,10 +217,11 @@ pub fn encrypt_string_with_nonce_seed( } /// Encrypt a string with a random nonce seed. It will include the nonce seed in the result so that it can be used when decrypting. +#[allow(clippy::missing_errors_doc)] pub fn encrypt_string( s: &SecretString, - cipher: &Cipher, - key: Arc>, + cipher: Cipher, + key: &Arc>, ) -> Result { let mut cursor = io::Cursor::new(vec![]); let nonce_seed = create_rng().next_u64(); @@ -229,12 +234,18 @@ pub fn encrypt_string( } /// Decrypt a string that was encrypted with including the nonce seed. -pub fn decrypt_string(s: &str, cipher: &Cipher, key: Arc>) -> Result { +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] +pub fn decrypt_string(s: &str, cipher: Cipher, key: Arc>) -> Result { // extract nonce seed - if !s.contains(".") { + if !s.contains('.') { return Err(Error::Generic("nonce seed is missing")); } - let nonce_seed = s.split('.').last().unwrap().parse::()?; + let nonce_seed = s + .split('.') + .last() + .expect("missing nonce seed") + .parse::()?; let s = s.split('.').next().unwrap(); let vec = BASE64.decode(s)?; @@ -247,9 +258,10 @@ pub fn decrypt_string(s: &str, cipher: &Cipher, key: Arc>) -> Resu } /// Decrypt a string that was encrypted with a specific nonce seed. +#[allow(clippy::missing_errors_doc)] pub fn decrypt_string_with_nonce_seed( s: &str, - cipher: &Cipher, + cipher: Cipher, key: Arc>, nonce_seed: u64, ) -> Result { @@ -262,25 +274,26 @@ pub fn decrypt_string_with_nonce_seed( Ok(SecretString::new(decrypted)) } +#[allow(clippy::missing_errors_doc)] pub fn decrypt_file_name( name: &str, - cipher: &Cipher, + cipher: Cipher, key: Arc>, ) -> Result { - let name = String::from(name).replace("|", "/"); + let name = String::from(name).replace('|', "/"); decrypt_string(&name, cipher, key) } #[instrument(skip(password, salt))] +#[allow(clippy::missing_errors_doc)] pub fn derive_key( password: &SecretString, - cipher: &Cipher, + cipher: Cipher, salt: [u8; 32], ) -> Result> { let mut dk = vec![]; let key_len = match cipher { - Cipher::ChaCha20 => 32, - Cipher::Aes256Gcm => 32, + Cipher::ChaCha20 | Cipher::Aes256Gcm => 32, }; dk.resize(key_len, 0); Argon2::default() @@ -290,21 +303,21 @@ pub fn derive_key( } /// Encrypt a file name with provided nonce seed. It will **INCLUDE** the nonce seed in the result so that it can be used when decrypting. +#[allow(clippy::missing_errors_doc)] pub fn encrypt_file_name( name: &SecretString, - cipher: &Cipher, - key: Arc>, + cipher: Cipher, + key: &Arc>, nonce_seed: u64, ) -> FsResult { // in order not to add too much to filename length we keep just 3 digits from nonce seed let nonce_seed = nonce_seed % 1000; if name.expose_secret() != "$." && name.expose_secret() != "$.." { - let normalized_name = - SecretString::new(name.expose_secret().replace("/", " ").replace("\\", " ")); + let normalized_name = SecretString::new(name.expose_secret().replace(['/', '\\'], " ")); let mut encrypted = encrypt_string_with_nonce_seed(&normalized_name, cipher, key, nonce_seed, true)?; - encrypted = encrypted.replace("/", "|"); + encrypted = encrypted.replace('/', "|"); Ok(encrypted) } else { // add nonce seed @@ -314,34 +327,39 @@ pub fn encrypt_file_name( } } +#[must_use] pub fn hash(data: &[u8]) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(data); hasher.finalize().into() } +#[allow(clippy::missing_panics_doc)] pub fn hash_reader(r: R) -> [u8; 32] { let mut hasher = Sha256::new(); let mut reader = io::BufReader::new(r); - io::copy(&mut reader, &mut hasher).unwrap(); + io::copy(&mut reader, &mut hasher).expect("cannot copy"); hasher.finalize().into() } +#[must_use] pub fn hash_secret_string(data: &SecretString) -> [u8; 32] { hash(data.expose_secret().as_bytes()) } +#[must_use] pub fn hash_secret_vec(data: &SecretVec) -> [u8; 32] { hash(data.expose_secret()) } /// Copy from `pos` position in file `len` bytes #[instrument(skip(w, key), fields(pos = pos.to_formatted_string(& Locale::en), len = len.to_formatted_string(& Locale::en)))] +#[allow(clippy::missing_errors_doc)] pub fn copy_from_file_exact( file: PathBuf, pos: u64, len: u64, - cipher: &Cipher, + cipher: Cipher, key: Arc>, nonce_seed: u64, w: &mut impl Write, @@ -350,11 +368,13 @@ pub fn copy_from_file_exact( copy_from_file(file, pos, len, cipher, key, nonce_seed, w, false)?; Ok(()) } + +#[allow(clippy::missing_errors_doc)] pub fn copy_from_file( file: PathBuf, pos: u64, len: u64, - cipher: &Cipher, + cipher: Cipher, key: Arc>, nonce_seed: u64, w: &mut impl Write, @@ -390,14 +410,21 @@ pub fn copy_from_file( Ok(len) } +#[allow(clippy::missing_panics_doc)] +#[allow(clippy::missing_errors_doc)] pub fn extract_nonce_from_encrypted_string(name: &str) -> Result { - if !name.contains(".") { + if !name.contains('.') { return Err(Error::Generic("nonce seed is missing")); } - let nonce_seed = name.split('.').last().unwrap().parse::()?; + let nonce_seed = name + .split('.') + .last() + .expect("nonce seed missing") + .parse::()?; Ok(nonce_seed) } +#[must_use] pub fn create_rng() -> impl RngCore + CryptoRng { ChaCha20Rng::from_entropy() } diff --git a/src/crypto/buf_mut.rs b/src/crypto/buf_mut.rs index e4a5ec70..e2187c98 100644 --- a/src/crypto/buf_mut.rs +++ b/src/crypto/buf_mut.rs @@ -11,6 +11,7 @@ pub struct BufMut { } impl BufMut { + #[must_use] pub fn new(from: Vec) -> Self { Self { buf: from, @@ -19,41 +20,40 @@ impl BufMut { } } + #[must_use] pub fn remaining(&self) -> usize { self.buf.len() - self.pos } - pub fn as_mut(&mut self) -> &mut [u8] { - &mut self.buf[..self.pos] - } - pub fn as_mut_remaining(&mut self) -> &mut [u8] { &mut self.buf[self.pos..] } - pub fn as_ref(&self) -> &[u8] { - &self.buf[..self.pos] - } - pub fn clear(&mut self) { self.pos = 0; self.pos_read = 0; } - pub fn pos(&self) -> usize { + #[must_use] + pub const fn available(&self) -> usize { self.pos } - pub fn pos_read(&self) -> usize { - self.pos_read + #[must_use] + pub const fn available_read(&self) -> usize { + self.available() - self.pos_read } +} - pub fn available(&self) -> usize { - self.pos() +impl AsMut<[u8]> for BufMut { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.buf[..self.pos] } +} - pub fn available_read(&self) -> usize { - self.available() - self.pos_read +impl AsRef<[u8]> for BufMut { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.pos] } } @@ -71,6 +71,9 @@ impl Write for BufMut { } impl Seek for BufMut { + #[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, @@ -102,6 +105,6 @@ impl Read for BufMut { impl Drop for BufMut { fn drop(&mut self) { - self.buf.zeroize() + self.buf.zeroize(); } } diff --git a/src/crypto/reader.rs b/src/crypto/reader.rs index b843615f..1f0e392b 100644 --- a/src/crypto/reader.rs +++ b/src/crypto/reader.rs @@ -1,7 +1,7 @@ use std::fs::File; use std::io; use std::io::{Read, Seek, SeekFrom}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use num_format::{Locale, ToFormattedString}; @@ -14,7 +14,9 @@ use crate::crypto::writer::{RandomNonceSequence, BUF_SIZE}; use crate::crypto::Cipher; use crate::{crypto, stream_util}; +#[allow(clippy::module_name_repetitions)] pub trait CryptoReader: Read + Seek + Send + Sync { + #[allow(clippy::missing_errors_doc)] fn finish(&mut self) -> io::Result; } @@ -52,6 +54,7 @@ pub trait CryptoReader: Read + Seek + Send + Sync { /// ring +#[allow(clippy::module_name_repetitions)] pub struct RingCryptoReader { input: Option, opening_key: OpeningKey, @@ -69,7 +72,7 @@ impl RingCryptoReader { key: Arc>, nonce_seed: u64, ) -> Self { - let opening_key = Self::create_opening_key(algorithm, key.clone(), nonce_seed); + let opening_key = Self::create_opening_key(algorithm, &key, nonce_seed); let buf = BufMut::new(vec![0; BUF_SIZE + algorithm.tag_len()]); Self { input: Some(r), @@ -84,7 +87,7 @@ impl RingCryptoReader { fn create_opening_key( algorithm: &'static Algorithm, - key: Arc>, + key: &Arc>, nonce_seed: u64, ) -> OpeningKey { let unbound_key = UnboundKey::new(algorithm, key.expose_secret()).unwrap(); @@ -100,7 +103,7 @@ impl RingCryptoReader { // to read from the beginning until the desired offset debug!("seeking back, recreating decryptor"); self.opening_key = - Self::create_opening_key(self.algorithm, self.key.clone(), self.nonce_seed); + Self::create_opening_key(self.algorithm, &self.key, self.nonce_seed); self.buf.clear(); self.pos = 0; self.input.as_mut().unwrap().seek(SeekFrom::Start(0))?; @@ -151,10 +154,10 @@ impl Read for RingCryptoReader { if len == 0 { return Ok(0); } - let mut data = &mut buffer[..len]; + let data = &mut buffer[..len]; let plaintext = self .opening_key - .open_within(Aad::empty(), &mut data, 0..) + .open_within(Aad::empty(), data, 0..) .map_err(|err| { error!("error opening within: {}", err); io::Error::new(io::ErrorKind::Other, "error opening within") @@ -169,15 +172,15 @@ impl Read for RingCryptoReader { } impl Seek for RingCryptoReader { + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_sign_loss)] fn seek(&mut self, pos: SeekFrom) -> io::Result { match pos { SeekFrom::Start(pos) => self.seek_from_start(pos), - SeekFrom::End(_) => { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "can't seek from end", - )) - } + SeekFrom::End(_) => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "can't seek from end", + )), SeekFrom::Current(pos) => { let new_pos = self.pos as i64 + pos; if new_pos < 0 { @@ -194,7 +197,7 @@ impl Seek for RingCryptoReader { impl CryptoReader for RingCryptoReader { fn finish(&mut self) -> io::Result { - if let None = self.input { + if self.input.is_none() { return Err(io::Error::new( io::ErrorKind::Other, "RingCryptoReader already finished", @@ -212,6 +215,7 @@ impl Drop for RingCryptoReader { /// file reader +#[allow(clippy::module_name_repetitions)] pub struct FileCryptoReader { file: PathBuf, reader: Box>, @@ -221,17 +225,18 @@ pub struct FileCryptoReader { } impl FileCryptoReader { + #[allow(clippy::missing_errors_doc)] pub fn new( - file: PathBuf, + file: &Path, cipher: Cipher, key: Arc>, nonce_seed: u64, ) -> io::Result { Ok(Self { - file: file.clone(), + file: file.to_owned(), reader: Box::new(crypto::create_reader( - File::open(&file)?, - &cipher, + File::open(file)?, + cipher, key.clone(), nonce_seed, )), @@ -250,6 +255,8 @@ impl Read for FileCryptoReader { } impl Seek for FileCryptoReader { + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_sign_loss)] fn seek(&mut self, pos: SeekFrom) -> io::Result { let pos = match pos { SeekFrom::Start(pos) => pos, @@ -275,14 +282,14 @@ impl Seek for FileCryptoReader { self.reader.finish()?; self.reader = Box::new(crypto::create_reader( File::open(&self.file).unwrap(), - &self.cipher, + self.cipher, self.key.clone(), self.nonce_seed, )); } self.reader.seek(SeekFrom::Start(pos))?; } - Ok(self.reader.stream_position()?) + self.reader.stream_position() } } diff --git a/src/crypto/writer.rs b/src/crypto/writer.rs index 1bda4b53..d45628ce 100644 --- a/src/crypto/writer.rs +++ b/src/crypto/writer.rs @@ -1,6 +1,6 @@ use std::fs::File; use std::io::{BufWriter, Seek, SeekFrom, Write}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{fs, io}; @@ -24,7 +24,9 @@ pub(crate) const BUF_SIZE: usize = 256 * 1024; #[cfg(not(test))] pub(crate) const BUF_SIZE: usize = 1024 * 1024; // 1 MB buffer +#[allow(clippy::module_name_repetitions)] pub trait CryptoWriter: Write + Send + Sync { + #[allow(clippy::missing_errors_doc)] fn finish(&mut self) -> io::Result; } @@ -60,6 +62,7 @@ pub trait CryptoWriter: Write + Send + Sync { /// ring +#[allow(clippy::module_name_repetitions)] pub struct RingCryptoWriter { out: Option>, sealing_key: SealingKey, @@ -67,13 +70,14 @@ pub struct RingCryptoWriter { } impl RingCryptoWriter { + #[allow(clippy::missing_panics_doc)] pub fn new( w: W, algorithm: &'static Algorithm, - key: Arc>, + key: &Arc>, nonce_seed: u64, ) -> Self { - let unbound_key = UnboundKey::new(&algorithm, key.expose_secret()).unwrap(); + let unbound_key = UnboundKey::new(algorithm, key.expose_secret()).expect("unbound key"); let nonce_sequence = RandomNonceSequence::new(nonce_seed); let sealing_key = SealingKey::new(unbound_key, nonce_sequence); let buf = BufMut::new(vec![0; BUF_SIZE]); @@ -109,16 +113,16 @@ impl Write for RingCryptoWriter { impl RingCryptoWriter { fn encrypt_and_write(&mut self) -> io::Result<()> { - let mut data = self.buf.as_mut(); + let data = self.buf.as_mut(); let tag = self .sealing_key - .seal_in_place_separate_tag(Aad::empty(), &mut data) + .seal_in_place_separate_tag(Aad::empty(), data) .map_err(|err| { error!("error sealing in place: {}", err); io::Error::from(io::ErrorKind::Other) })?; let out = self.out.as_mut().unwrap(); - out.write_all(&data)?; + out.write_all(data)?; self.buf.clear(); out.write_all(tag.as_ref())?; out.flush()?; @@ -186,11 +190,14 @@ pub trait CryptoWriterSeek: CryptoWriter + Seek {} /// file writer +#[allow(clippy::module_name_repetitions)] pub trait FileCryptoWriterCallback: Send + Sync { + #[allow(clippy::missing_errors_doc)] fn on_file_content_changed(&self, changed_from_pos: u64, last_write_pos: u64) -> io::Result<()>; } +#[allow(clippy::module_name_repetitions)] pub struct FileCryptoWriter { file: PathBuf, tmp_dir: PathBuf, @@ -204,31 +211,27 @@ pub struct FileCryptoWriter { } impl FileCryptoWriter { + #[allow(clippy::missing_errors_doc)] pub fn new( - file_path: PathBuf, - tmp_dir: PathBuf, + file_path: &Path, + tmp_dir: &Path, cipher: Cipher, key: Arc>, nonce_seed: u64, callback: Callback, ) -> io::Result { if !file_path.exists() { - File::create(&file_path)?; + File::create(file_path)?; } // start writer in tmp file - let tmp_path = NamedTempFile::new_in(tmp_dir.clone())? + let tmp_path = NamedTempFile::new_in(tmp_dir)? .into_temp_path() .to_path_buf(); let tmp_file = File::create(tmp_path.clone())?; Ok(Self { - file: file_path, - tmp_dir, - writer: Box::new(crypto::create_writer( - tmp_file, - &cipher, - key.clone(), - nonce_seed, - )), + file: file_path.to_owned(), + tmp_dir: tmp_dir.to_owned(), + writer: Box::new(crypto::create_writer(tmp_file, cipher, &key, nonce_seed)), cipher, key, nonce_seed, @@ -238,7 +241,7 @@ impl FileCryptoWriter { }) } - pub fn seek_from_start(&mut self, pos: u64) -> io::Result { + fn seek_from_start(&mut self, pos: u64) -> io::Result { if pos == self.pos { return Ok(pos); } @@ -246,12 +249,11 @@ impl FileCryptoWriter { if self.pos < pos { // seek forward let len = pos - self.pos; - let cipher = &self.cipher.clone(); crypto::copy_from_file( self.file.clone(), self.pos, len, - cipher, + self.cipher, self.key.clone(), self.nonce_seed, self, @@ -274,7 +276,7 @@ impl FileCryptoWriter { self.file.clone(), self.pos, u64::MAX, - &cipher, + cipher, self.key.clone(), self.nonce_seed, self, @@ -301,8 +303,8 @@ impl FileCryptoWriter { let tmp_file = File::create(tmp_path.clone())?; self.writer = Box::new(crypto::create_writer( tmp_file, - &cipher, - self.key.clone(), + cipher, + &self.key.clone(), self.nonce_seed, )); self.tmp_file_path = tmp_path; @@ -314,7 +316,7 @@ impl FileCryptoWriter { self.file.clone(), self.pos, len, - &cipher, + cipher, self.key.clone(), self.nonce_seed, self, @@ -356,15 +358,15 @@ impl CryptoWriter for FileCryptoWriter } impl Seek for FileCryptoWriter { + #[allow(clippy::cast_possible_wrap)] + #[allow(clippy::cast_sign_loss)] fn seek(&mut self, pos: SeekFrom) -> io::Result { match pos { SeekFrom::Start(pos) => self.seek_from_start(pos), - SeekFrom::End(_) => { - return Err(io::Error::new( - io::ErrorKind::Other, - "seek from end not supported", - )) - } + SeekFrom::End(_) => Err(io::Error::new( + io::ErrorKind::Other, + "seek from end not supported", + )), SeekFrom::Current(pos) => { let new_pos = self.pos as i64 + pos; if new_pos < 0 { diff --git a/src/encryptedfs.rs b/src/encryptedfs.rs index 4c275fde..13b48b14 100644 --- a/src/encryptedfs.rs +++ b/src/encryptedfs.rs @@ -5,7 +5,7 @@ use std::fs::{File, OpenOptions, ReadDir}; use std::io::ErrorKind::Other; use std::io::{Read, Seek, SeekFrom, Write}; use std::num::ParseIntError; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::atomic::AtomicU64; use std::sync::mpsc::{Receiver, Sender}; @@ -108,13 +108,12 @@ async fn recreate_handles( fh: u64, ) -> FsResult<()> { fs.reset_handles(ino, changed_from_pos, Some(fh)).await?; - let mut attr = fs.get_inode_from_storage(ino, fs.key.get().await?).await?; + let mut attr = fs.get_inode_from_storage(ino, fs.key.get().await?)?; let _guard = fs.serialize_update_inode_locks.lock().await; // if we wrote pass the filesize we need to update the filesize if last_write_pos > attr.size { attr.size = last_write_pos; - fs.write_inode_to_storage(&attr, fs.key.get().await?) - .await?; + fs.write_inode_to_storage(&attr, &fs.key.get().await?)?; } Ok(()) @@ -139,7 +138,7 @@ pub struct FileAttr { pub ctime: SystemTime, /// Time of creation (macOS only) pub crtime: SystemTime, - /// Kind of file (directory, file, pipe, etc) + /// Kind of file (directory, file, pipe, etc.) pub kind: FileType, /// Permissions pub perm: u16, @@ -166,9 +165,9 @@ pub enum FileType { // CharDevice, // /// Block device (S_IFBLK) // BlockDevice, - /// Directory (S_IFDIR) + /// Directory (`S_IFDIR`) Directory, - /// Regular file (S_IFREG) + /// Regular file (`S_IFREG`) RegularFile, // /// Symbolic link (S_IFLNK) // Symlink, @@ -176,7 +175,7 @@ pub enum FileType { // Socket, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Copy, Default)] pub struct SetFileAttr { /// Size in bytes pub size: Option, @@ -201,52 +200,62 @@ pub struct SetFileAttr { } impl SetFileAttr { - pub fn with_size(mut self, size: u64) -> Self { + #[must_use] + pub const fn with_size(mut self, size: u64) -> Self { self.size = Some(size); self } - pub fn with_atime(mut self, atime: SystemTime) -> Self { + #[must_use] + pub const fn with_atime(mut self, atime: SystemTime) -> Self { self.atime = Some(atime); self } - pub fn with_mtime(mut self, mtime: SystemTime) -> Self { + #[must_use] + pub const fn with_mtime(mut self, mtime: SystemTime) -> Self { self.mtime = Some(mtime); self } - pub fn with_ctime(mut self, ctime: SystemTime) -> Self { + #[must_use] + pub const fn with_ctime(mut self, ctime: SystemTime) -> Self { self.ctime = Some(ctime); self } - pub fn with_crtime(mut self, crtime: SystemTime) -> Self { + #[must_use] + pub const fn with_crtime(mut self, crtime: SystemTime) -> Self { self.crtime = Some(crtime); self } - pub fn with_perm(mut self, perm: u16) -> Self { + #[must_use] + pub const fn with_perm(mut self, perm: u16) -> Self { self.perm = Some(perm); self } - pub fn with_uid(mut self, uid: u32) -> Self { + #[must_use] + pub const fn with_uid(mut self, uid: u32) -> Self { self.uid = Some(uid); self } - pub fn with_gid(mut self, gid: u32) -> Self { + #[must_use] + pub const fn with_gid(mut self, gid: u32) -> Self { self.gid = Some(gid); self } - pub fn with_rdev(mut self, rdev: u32) -> Self { + #[must_use] + pub const fn with_rdev(mut self, rdev: u32) -> Self { self.rdev = Some(rdev); self } - pub fn with_flags(mut self, flags: u32) -> Self { + #[must_use] + pub const fn with_flags(mut self, flags: u32) -> Self { self.rdev = Some(flags); self } @@ -254,7 +263,7 @@ impl SetFileAttr { #[derive(Debug, Clone)] pub struct CreateFileAttr { - /// Kind of file (directory, file, pipe, etc) + /// Kind of file (directory, file, pipe, etc.) pub kind: FileType, /// Permissions pub perm: u16, @@ -361,7 +370,7 @@ struct TimeAndSizeFileAttr { impl TimeAndSizeFileAttr { #[allow(dead_code)] - fn new( + const fn new( atime: SystemTime, mtime: SystemTime, ctime: SystemTime, @@ -392,7 +401,7 @@ impl From for TimeAndSizeFileAttr { impl From for SetFileAttr { fn from(value: TimeAndSizeFileAttr) -> Self { - SetFileAttr::default() + Self::default() .with_atime(value.atime) .with_mtime(value.mtime) .with_ctime(value.ctime) @@ -416,7 +425,7 @@ impl PartialEq for DirectoryEntry { } } -/// Like [`DirectoryEntry`] but with ['FileAttr']. +/// Like [`DirectoryEntry`] but with [`FileAttr`]. #[derive(Debug)] pub struct DirectoryEntryPlus { pub ino: u64, @@ -453,10 +462,13 @@ impl Iterator for DirectoryEntryIterator { } let entry = entry.unwrap(); - let map = self.3.read().unwrap(); - let lock = map.get_or_insert_with(entry.path().to_str().unwrap().to_string(), || { - std::sync::RwLock::new(false) - }); + let lock = self + .3 + .read() + .unwrap() + .get_or_insert_with(entry.path().to_str().unwrap().to_string(), || { + std::sync::RwLock::new(false) + }); let _guard = lock.read().unwrap(); let file = File::open(entry.path()); @@ -489,7 +501,7 @@ impl Iterator for DirectoryEntryIterator { "nonce seed is missing from filename", ))); } - let res = name.split(".").last().unwrap().parse::(); + let res = name.split('.').last().unwrap().parse::(); if let Err(err) = res { error!(err = %err, "parsing nonce seed"); return Some(Err(err.into())); @@ -505,7 +517,7 @@ impl Iterator for DirectoryEntryIterator { } nonce_seed = res.unwrap(); let name = - crypto::decrypt_file_name(&name, &self.1, self.2.clone()).map_err(|err| { + crypto::decrypt_file_name(&name, self.1, self.2.clone()).map_err(|err| { error!(err = %err, "decrypting file name"); err }); @@ -517,7 +529,7 @@ impl Iterator for DirectoryEntryIterator { }; let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from( - crypto::create_reader(file, &self.1, self.2.clone(), nonce_seed), + crypto::create_reader(file, self.1, self.2.clone(), nonce_seed), ); if let Err(e) = res { return Some(Err(e.into())); @@ -548,10 +560,13 @@ impl Iterator for DirectoryEntryPlusIterator { } let entry = entry.unwrap(); - let map = self.4.read().unwrap(); - let lock = map.get_or_insert_with(entry.path().to_str().unwrap().to_string(), || { - std::sync::RwLock::new(false) - }); + let lock = self + .4 + .read() + .unwrap() + .get_or_insert_with(entry.path().to_str().unwrap().to_string(), || { + std::sync::RwLock::new(false) + }); let _guard = lock.read().unwrap(); let file = File::open(entry.path()); @@ -585,7 +600,7 @@ impl Iterator for DirectoryEntryPlusIterator { "nonce seed is missing from filename", ))); } - let res = name.split(".").last().unwrap().parse::(); + let res = name.split('.').last().unwrap().parse::(); if let Err(err) = res { error!(err = %err, "parsing nonce seed"); return Some(Err(err.into())); @@ -601,7 +616,7 @@ impl Iterator for DirectoryEntryPlusIterator { } nonce_seed = res.unwrap(); let name = - crypto::decrypt_file_name(&name, &self.2, self.3.clone()).map_err(|err| { + crypto::decrypt_file_name(&name, self.2, self.3.clone()).map_err(|err| { error!(err = %err, "decrypting file name"); err }); @@ -614,7 +629,7 @@ impl Iterator for DirectoryEntryPlusIterator { debug!(name = %name.expose_secret(), "decrypted file name"); let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from( - crypto::create_reader(file, &self.2, self.3.clone(), nonce_seed), + crypto::create_reader(file, self.2, self.3.clone(), nonce_seed), ); if let Err(e) = res { error!(err = %e, "deserializing directory entry"); @@ -622,18 +637,21 @@ impl Iterator for DirectoryEntryPlusIterator { } let (ino, kind): (u64, FileType) = res.unwrap(); - let map_guard_ino = self.5.read().unwrap(); - let lock_ino = map_guard_ino.get_or_insert_with(ino, || std::sync::RwLock::new(false)); + let lock_ino = self + .5 + .read() + .unwrap() + .get_or_insert_with(ino, || std::sync::RwLock::new(false)); let _guard_ino = lock_ino.read().unwrap(); - let file = File::open(&self.1.join(ino.to_string())); + let file = File::open(self.1.join(ino.to_string())); if let Err(e) = file { error!(err = %e, "opening file"); return Some(Err(e.into())); } let file = file.unwrap(); let attr = - bincode::deserialize_from(crypto::create_reader(file, &self.2, self.3.clone(), ino)); + bincode::deserialize_from(crypto::create_reader(file, self.2, self.3.clone(), ino)); if let Err(e) = attr { error!(err = %e, "deserializing file attr"); return Some(Err(e.into())); @@ -690,9 +708,9 @@ enum ReadHandleContextOperation { } impl ReadHandleContextOperation { - fn get_ino(&self) -> u64 { + const fn get_ino(&self) -> u64 { match *self { - ReadHandleContextOperation::Create { ino, .. } => ino, + Self::Create { ino, .. } => ino, } } } @@ -702,9 +720,9 @@ enum WriteHandleContextOperation { } impl WriteHandleContextOperation { - fn get_ino(&self) -> u64 { + const fn get_ino(&self) -> u64 { match *self { - WriteHandleContextOperation::Create { ino, .. } => ino, + Self::Create { ino, .. } => ino, } } } @@ -728,7 +746,7 @@ impl Provider, FsError> for KeyProvider { .password_provider .get_password() .ok_or(FsError::InvalidPassword)?; - read_or_create_key(&self.path, &password, &self.cipher) + read_or_create_key(&self.path, &password, self.cipher) } } @@ -765,6 +783,8 @@ pub struct EncryptedFs { } impl EncryptedFs { + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn new( data_dir: PathBuf, tmp_dir: PathBuf, @@ -776,12 +796,12 @@ impl EncryptedFs { let key_provider = KeyProvider { path: data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME), password_provider, - cipher: cipher.clone(), + cipher, }; ensure_structure_created(&data_dir.clone(), &tmp_dir.clone(), &key_provider).await?; - let fs = EncryptedFs { + let fs = Self { data_dir, tmp_dir, write_handles: RwLock::new(HashMap::new()), @@ -790,19 +810,22 @@ impl EncryptedFs { cipher, opened_files_for_read: RwLock::new(HashMap::new()), opened_files_for_write: RwLock::new(HashMap::new()), - read_write_inode_locks: Mutex::new(ArcHashMap::new()), - serialize_inode_locks: Arc::new(std::sync::RwLock::new(ArcHashMap::new())), - serialize_update_inode_locks: Mutex::new(ArcHashMap::new()), - serialize_dir_entries_locks: Arc::new(std::sync::RwLock::new(ArcHashMap::new())), + read_write_inode_locks: Mutex::new(ArcHashMap::default()), + serialize_inode_locks: Arc::new(std::sync::RwLock::new(ArcHashMap::default())), + serialize_update_inode_locks: Mutex::new(ArcHashMap::default()), + serialize_dir_entries_locks: Arc::new(std::sync::RwLock::new(ArcHashMap::default())), // todo: take duration from param - key: ExpireValue::new(key_provider, Duration::from_secs(10 * 60)).await, + key: ExpireValue::new(key_provider, Duration::from_secs(10 * 60)), self_weak: std::sync::Mutex::new(None), }; fs.ensure_root_exists().await?; let arc = Arc::new(fs); - arc.self_weak.lock().unwrap().replace(Arc::downgrade(&arc)); + arc.self_weak + .lock() + .expect("cannot obtain lock") + .replace(Arc::downgrade(&arc)); Ok(arc) } @@ -822,6 +845,8 @@ impl EncryptedFs { } /// Create a new node in the filesystem + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn create_nod( &self, parent: u64, @@ -844,8 +869,7 @@ impl EncryptedFs { attr.ino = self.generate_next_inode(); // write inode - self.write_inode_to_storage(&attr, self.key.get().await?) - .await?; + self.write_inode_to_storage(&attr, &self.key.get().await?)?; // create in contents directory match attr.kind { @@ -856,7 +880,7 @@ impl EncryptedFs { .write(true) .create(true) .truncate(true) - .open(&path)?; + .open(path)?; } FileType::Directory => { // create the directory @@ -866,38 +890,35 @@ impl EncryptedFs { // add "." and ".." entries self.insert_directory_entry( attr.ino, - DirectoryEntry { + &DirectoryEntry { ino: attr.ino, - name: SecretString::from_str("$.").unwrap(), + name: SecretString::from_str("$.").expect("cannot parse"), kind: FileType::Directory, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; self.insert_directory_entry( attr.ino, - DirectoryEntry { + &DirectoryEntry { ino: parent, - name: SecretString::from_str("$..").unwrap(), + name: SecretString::from_str("$..").expect("cannot parse"), kind: FileType::Directory, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; } } // edd entry in parent directory, used for listing self.insert_directory_entry( parent, - DirectoryEntry { + &DirectoryEntry { ino: attr.ino, name: SecretString::new(name.expose_secret().to_owned()), kind: attr.kind, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; self.update_inode( parent, SetFileAttr::default() @@ -921,6 +942,8 @@ impl EncryptedFs { Ok((handle, attr)) } + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn find_by_name( &self, parent: u64, @@ -937,9 +960,9 @@ impl EncryptedFs { } let name = { if name.expose_secret() == "." { - SecretString::from_str("$.").unwrap() + SecretString::from_str("$.").expect("cannot parse") } else if name.expose_secret() == ".." { - SecretString::from_str("$..").unwrap() + SecretString::from_str("$..").expect("cannot parse") } else { SecretString::new(name.expose_secret().to_owned()) } @@ -947,7 +970,7 @@ impl EncryptedFs { // in order not to add too much to filename length we keep just 3 digits from nonce seed let nonce_seed = parent % 1000; let name = - crypto::encrypt_file_name(&name, &self.cipher, self.key.get().await?, nonce_seed)?; + crypto::encrypt_file_name(&name, self.cipher, &self.key.get().await?, nonce_seed)?; let file = File::open( self.data_dir .join(CONTENTS_DIR) @@ -961,11 +984,14 @@ impl EncryptedFs { } /// Count children of a directory. This includes also `.` and `..`. + #[allow(clippy::missing_errors_doc)] pub async fn children_count(&self, ino: u64) -> FsResult { let iter = self.read_dir(ino).await?; Ok(iter.into_iter().count()) } + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn remove_dir(&self, parent: u64, name: &SecretString) -> FsResult<()> { if !self.is_dir(parent) { return Err(FsError::InvalidInodeType); @@ -993,8 +1019,11 @@ impl EncryptedFs { // remove inode file { - let map = self.serialize_inode_locks.write().unwrap(); - let lock = map.get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); + let lock = self + .serialize_inode_locks + .write() + .expect("cannot obtain lock") + .get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); let _guard = lock.write().unwrap(); fs::remove_file(self.data_dir.join(INODES_DIR).join(&ino_str))?; } @@ -1015,6 +1044,8 @@ impl EncryptedFs { Ok(()) } + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn remove_file(&self, parent: u64, name: &SecretString) -> FsResult<()> { if !self.is_dir(parent) { return Err(FsError::InvalidInodeType); @@ -1034,8 +1065,11 @@ impl EncryptedFs { // remove inode file { - let map = self.serialize_inode_locks.write().unwrap(); - let lock = map.get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); + let lock = self + .serialize_inode_locks + .write() + .expect("cannot obtain lock") + .get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); let _guard = lock.write().unwrap(); fs::remove_file(self.data_dir.join(INODES_DIR).join(&ino_str))?; } @@ -1057,17 +1091,19 @@ impl EncryptedFs { Ok(()) } + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::missing_errors_doc)] pub async fn exists_by_name(&self, parent: u64, name: &SecretString) -> FsResult { let name = { if name.expose_secret() == "." { - SecretString::from_str("$.").unwrap() + SecretString::from_str("$.").expect("cannot parse") } else if name.expose_secret() == ".." { - SecretString::from_str("$..").unwrap() + SecretString::from_str("$..").expect("cannot parse") } else { SecretString::new(name.expose_secret().to_owned()) } }; - let name = crypto::encrypt_file_name(&name, &self.cipher, self.key.get().await?, parent)?; + let name = crypto::encrypt_file_name(&name, self.cipher, &self.key.get().await?, parent)?; Ok(self .data_dir .join(CONTENTS_DIR) @@ -1076,6 +1112,7 @@ impl EncryptedFs { .exists()) } + #[allow(clippy::missing_errors_doc)] pub async fn read_dir(&self, ino: u64) -> FsResult { let contents_dir = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); if !contents_dir.is_dir() { @@ -1085,13 +1122,13 @@ impl EncryptedFs { let iter = fs::read_dir(contents_dir)?; Ok(DirectoryEntryIterator( iter.into_iter(), - self.cipher.clone(), + self.cipher, self.key.get().await?, self.serialize_dir_entries_locks.clone(), )) } - /// Like [read_dir](EncryptedFs::read_dir) but with [FileAttr] so we don't need to query again for those. + /// Like [`read_dir`](EncryptedFs::read_dir) but with [`FileAttr`] so we don't need to query again for those. pub async fn read_dir_plus(&self, ino: u64) -> FsResult { let contents_dir = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); if !contents_dir.is_dir() { @@ -1102,20 +1139,20 @@ impl EncryptedFs { Ok(DirectoryEntryPlusIterator( iter.into_iter(), self.data_dir.join(INODES_DIR), - self.cipher.clone(), + self.cipher, self.key.get().await?, self.serialize_dir_entries_locks.clone(), self.serialize_inode_locks.clone(), )) } - async fn get_inode_from_storage( - &self, - ino: u64, - key: Arc>, - ) -> FsResult { - let map_guard = self.serialize_inode_locks.read().unwrap(); - let lock = map_guard.get_or_insert_with(ino, || std::sync::RwLock::new(false)); + #[allow(clippy::missing_errors_doc)] + fn get_inode_from_storage(&self, ino: u64, key: Arc>) -> FsResult { + let lock = self + .serialize_inode_locks + .read() + .unwrap() + .get_or_insert_with(ino, || std::sync::RwLock::new(false)); let _guard = lock.read().unwrap(); let path = self.data_dir.join(INODES_DIR).join(ino.to_string()); @@ -1129,35 +1166,28 @@ impl EncryptedFs { FileAttr, >(Box::new(crypto::create_reader( file, - &self.cipher, + self.cipher, key, ino, )))?) } + #[allow(clippy::missing_errors_doc)] pub async fn get_inode(&self, ino: u64) -> FsResult { debug!("get inode"); - let mut attr = self - .get_inode_from_storage(ino, self.key.get().await?) - .await?; + let mut attr = self.get_inode_from_storage(ino, self.key.get().await?)?; // merge time info and size with any open read handles let open_reads = { self.opened_files_for_read.read().await.contains_key(&ino) }; if open_reads { - let fhs = self - .opened_files_for_read - .read() - .await - .get(&ino) - .map(|v| v.clone()); + let fhs = self.opened_files_for_read.read().await.get(&ino).cloned(); if let Some(fhs) = fhs { for fh in fhs { if let Some(ctx) = self.read_handles.read().await.get(&fh) { - let ctx = ctx.lock().await; - let mut attr1: SetFileAttr = ctx.attr.clone().into(); - // we don't want to set size because readers don't change the size and we might have an older version + let mut attr1: SetFileAttr = ctx.lock().await.attr.clone().into(); + // we don't want to set size because readers don't change the size, and we might have an older version attr1.size.take(); - merge_attr(&mut attr, attr1); + merge_attr(&mut attr, &attr1); } } } @@ -1166,16 +1196,11 @@ impl EncryptedFs { // merge time info and size with any open write handles let open_writes = { self.opened_files_for_write.read().await.contains_key(&ino) }; if open_writes { - let fh = self - .opened_files_for_write - .read() - .await - .get(&ino) - .map(|v| *v); + let fh = self.opened_files_for_write.read().await.get(&ino).copied(); if let Some(fh) = fh { if let Some(ctx) = self.write_handles.read().await.get(&fh) { let ctx = ctx.lock().await; - merge_attr(&mut attr, ctx.attr.clone().into()); + merge_attr(&mut attr, &ctx.attr.clone().into()); } } } @@ -1184,31 +1209,35 @@ impl EncryptedFs { } pub async fn update_inode(&self, ino: u64, set_attr: SetFileAttr) -> FsResult<()> { - let map_serialize_update = self.serialize_update_inode_locks.lock().await; - let lock_serialize_update = - map_serialize_update.get_or_insert_with(ino, || Mutex::new(false)); + let lock_serialize_update = self + .serialize_update_inode_locks + .lock() + .await + .get_or_insert_with(ino, || Mutex::new(false)); let _guard_serialize_update = lock_serialize_update.lock().await; let mut attr = self.get_inode(ino).await?; - merge_attr(&mut attr, set_attr); + merge_attr(&mut attr, &set_attr); - self.write_inode_to_storage(&attr, self.key.get().await?) - .await?; + self.write_inode_to_storage(&attr, &self.key.get().await?)?; Ok(()) } - async fn write_inode_to_storage( + fn write_inode_to_storage( &self, attr: &FileAttr, - key: Arc>, + key: &Arc>, ) -> Result<(), FsError> { - let map = self.serialize_inode_locks.write().unwrap(); - let lock = map.get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); + let lock = self + .serialize_inode_locks + .write() + .unwrap() + .get_or_insert_with(attr.ino, || std::sync::RwLock::new(false)); let _guard = lock.write().unwrap(); let path = self.data_dir.join(INODES_DIR).join(attr.ino.to_string()); let tmp = NamedTempFile::new_in(self.tmp_dir.clone())?; - let mut writer = crypto::create_writer(tmp, &self.cipher, key, attr.ino); + let mut writer = crypto::create_writer(tmp, self.cipher, key, attr.ino); bincode::serialize_into(&mut writer, &attr)?; writer.flush()?; let tmp = writer.finish()?; @@ -1223,6 +1252,8 @@ impl EncryptedFs { /// The most speed is obtained when we read sequentially from the beginning of the file. /// If the file is not opened for read, it will return an error of type ['FsError::InvalidFileHandle']. #[instrument(skip(self, buf))] + #[allow(clippy::missing_errors_doc)] + #[allow(clippy::cast_possible_truncation)] pub async fn read( &self, ino: u64, @@ -1257,7 +1288,7 @@ impl EncryptedFs { if self.is_dir(ino) { return Err(FsError::InvalidInodeType); } - if buf.len() == 0 { + if buf.is_empty() { // no-op return Ok(0); } @@ -1285,10 +1316,12 @@ impl EncryptedFs { }; ctx.attr.atime = SystemTime::now(); + drop(ctx); Ok(len) } + #[allow(clippy::missing_panics_doc)] pub async fn release(&self, handle: u64) -> FsResult<()> { debug!("release"); if handle == 0 { @@ -1304,11 +1337,15 @@ impl EncryptedFs { { let mut opened_files_for_read = self.opened_files_for_read.write().await; - opened_files_for_read.get_mut(&ctx.ino).and_then(|set| { - set.remove(&handle); - Some(()) - }); - if opened_files_for_read.get(&ctx.ino).unwrap().is_empty() { + opened_files_for_read + .get_mut(&ctx.ino) + .expect("handle is missing") + .remove(&handle); + if opened_files_for_read + .get(&ctx.ino) + .expect("handle is missing") + .is_empty() + { opened_files_for_read.remove(&ctx.ino); } } @@ -1316,12 +1353,12 @@ impl EncryptedFs { // write attr only here to avoid serializing it multiple times while reading // it will merge time fields with existing data because it might got change while we kept the handle let mut attr: SetFileAttr = ctx.attr.clone().into(); - // we don't want to set size because readers don't change the size and we might have an older version + // we don't want to set size because readers don't change the size, and we might have an older version attr.size.take(); self.update_inode(ctx.ino, attr).await?; ctx.reader.take().unwrap().finish()?; - + drop(ctx); valid_fh = true; } @@ -1338,6 +1375,7 @@ impl EncryptedFs { // write attr only here to avoid serializing it multiple times while writing // it will merge time fields with existing data because it might got change while we kept the handle self.update_inode(ctx.ino, ctx.attr.clone().into()).await?; + drop(ctx); valid_fh = true; } @@ -1397,7 +1435,7 @@ impl EncryptedFs { return Err(FsError::InvalidFileHandle); } } - if buf.len() == 0 { + if buf.is_empty() { // no-op return Ok(0); } @@ -1427,22 +1465,26 @@ impl EncryptedFs { ); ctx.attr.mtime = SystemTime::now(); ctx.attr.ctime = SystemTime::now(); + drop(ctx); Ok(len) } /// Flush the data to the underlying storage. + #[allow(clippy::missing_panics_doc)] pub async fn flush(&self, handle: u64) -> FsResult<()> { if handle == 0 { // in case of directory or if the file was crated without being opened we don't use handle return Ok(()); } - let mut valid_fh = false; - if let Some(_) = self.read_handles.read().await.get(&handle) { - valid_fh = true; - } + let mut valid_fh = self.read_handles.read().await.get(&handle).is_some(); if let Some(ctx) = self.write_handles.read().await.get(&handle) { - ctx.lock().await.writer.as_mut().unwrap().flush()?; + ctx.lock() + .await + .writer + .as_mut() + .expect("writer is missing") + .flush()?; valid_fh = true; } @@ -1488,6 +1530,7 @@ impl EncryptedFs { } /// Open a file. We can open multiple times for read but only one for write at a time. + #[allow(clippy::missing_panics_doc)] pub async fn open(&self, ino: u64, read: bool, write: bool) -> FsResult { if !read && !write { return Err(FsError::InvalidInput( @@ -1511,12 +1554,12 @@ impl EncryptedFs { } if write { let lock = map_guard.get_or_insert_with(ino, || RwLock::new(false)); - if let None = handle { + if handle.is_none() { handle = Some(self.allocate_next_handle()); } let res = self .do_with_write_handle( - *handle.as_ref().unwrap(), + *handle.as_ref().expect("handle is missing"), WriteHandleContextOperation::Create { ino, lock }, ) .await; @@ -1526,7 +1569,7 @@ impl EncryptedFs { self.read_handles .write() .await - .remove(&handle.as_ref().unwrap()); + .remove(handle.as_ref().unwrap()); return Err(FsError::AlreadyOpenForWrite); } res?; @@ -1534,6 +1577,8 @@ impl EncryptedFs { Ok(handle.unwrap()) } + #[allow(clippy::missing_panics_doc)] + #[allow(clippy::too_many_lines)] pub async fn truncate(&self, ino: u64, size: u64) -> FsResult<()> { let lock = { let map_guard = self.read_write_inode_locks.lock().await; @@ -1549,9 +1594,28 @@ impl EncryptedFs { if size == attr.size { // no-op return Ok(()); - } else if size == 0 { + } + + #[allow(clippy::items_after_statements)] + struct Callback {} + #[allow(clippy::items_after_statements)] + impl FileCryptoWriterCallback for Callback { + fn on_file_content_changed( + &self, + _changed_from_pos: u64, + _last_write_pos: u64, + ) -> io::Result<()> { + Ok(()) + } + } + + // flush writers + self.flush_and_reset_writers(ino).await?; + + if size == 0 { debug!("truncate to zero"); // truncate to zero + OpenOptions::new() .write(true) .create(true) @@ -1564,24 +1628,14 @@ impl EncryptedFs { ); // decrease size, copy from beginning until size as offset - // if we have opened writers, flush those - self.flush_writers(ino).await?; - let in_path = self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()); - let in_file = OpenOptions::new() - .read(true) - .write(true) - .open(in_path.clone())?; - let mut reader = self.create_crypto_reader(in_file, ino).await?; + let mut reader = self.create_crypto_file_reader(&in_path, ino).await?; - let tmp_path_str = format!("{}.truncate.tmp", attr.ino.to_string()); - let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); - let tmp_file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(tmp_path.clone())?; - let mut writer = self.create_crypto_writer(tmp_file, ino).await?; + let tmp = NamedTempFile::new_in(self.tmp_dir.clone())?; + let tmp_path = tmp.into_temp_path().to_path_buf(); + let mut writer = self + .create_crypto_file_writer(&tmp_path, ino, Callback {}) + .await?; // copy existing data until new size debug!("copying data until new size"); @@ -1601,32 +1655,22 @@ impl EncryptedFs { ); // increase size, write zeros from actual size to new size - // if we have opened writers, flush those - self.flush_writers(ino).await?; - let in_path = self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()); - let in_file = OpenOptions::new() - .read(true) - .write(true) - .open(in_path.clone())?; - let mut reader = self.create_crypto_reader(in_file, ino).await?; + let mut reader = self.create_crypto_file_reader(&in_path, ino).await?; - let tmp_path_str = format!("{}.truncate.tmp", attr.ino.to_string()); - let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); - let tmp_file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(tmp_path.clone())?; - let mut writer = self.create_crypto_writer(tmp_file, ino).await?; + let tmp = NamedTempFile::new_in(self.tmp_dir.clone())?; + let tmp_path = tmp.into_temp_path().to_path_buf(); + let mut writer = self + .create_crypto_file_writer(&tmp_path, ino, Callback {}) + .await?; // copy existing data debug!("copying existing data"); stream_util::copy_exact(&mut reader, &mut writer, attr.size)?; - // now fill up with zeros until new size + // seek to new size, this will fill up with zeros debug!("filling up with zeros until new size"); - stream_util::fill_zeros(&mut writer, size - attr.size)?; + writer.seek(SeekFrom::Start(size))?; writer.finish()?; debug!("rename from tmp file"); @@ -1657,10 +1701,10 @@ impl EncryptedFs { Ok(()) } - /// This will write any dirty data to the file. + /// This will write any dirty data to the file and seek to start /// > ⚠️ **Warning** /// > Need to be called in a context with write lock on `self.read_write_inode_locks.lock().await.get(ino)`. - async fn flush_writers(&self, ino: u64) -> FsResult<()> { + async fn flush_and_reset_writers(&self, ino: u64) -> FsResult<()> { debug!("flush_writers"); let map = self.opened_files_for_write.read().await; let handle = map.get(&ino); @@ -1677,6 +1721,7 @@ impl EncryptedFs { Ok(()) } + #[allow(clippy::missing_panics_doc)] pub async fn rename( &self, parent: u64, @@ -1713,20 +1758,22 @@ impl EncryptedFs { } } - let mut attr = self.find_by_name(parent, name).await?.unwrap(); + let mut attr = self + .find_by_name(parent, name) + .await? + .ok_or(FsError::NotFound("name not found"))?; // remove from parent contents self.remove_directory_entry(parent, name).await?; // add to new parent contents self.insert_directory_entry( new_parent, - DirectoryEntry { + &DirectoryEntry { ino: attr.ino, name: SecretString::new(new_name.expose_secret().to_owned()), kind: attr.kind, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; let mut parent_attr = self.get_inode(parent).await?; parent_attr.mtime = SystemTime::now(); @@ -1742,14 +1789,13 @@ impl EncryptedFs { // add parent link to new directory self.insert_directory_entry( attr.ino, - DirectoryEntry { + &DirectoryEntry { ino: new_parent, - name: SecretString::from_str("$..").unwrap(), + name: SecretString::from_str("$..").expect("cannot parse"), kind: FileType::Directory, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; } Ok(()) @@ -1763,8 +1809,8 @@ impl EncryptedFs { ) -> FsResult> { Ok(crypto::create_writer( file, - &self.cipher, - self.key.get().await?, + self.cipher, + &self.key.get().await?, nonce_seed, )) } @@ -1772,13 +1818,13 @@ impl EncryptedFs { /// Create a crypto writer to file using internal encryption info. pub async fn create_crypto_file_writer( &self, - file: PathBuf, + file: &Path, nonce_seed: u64, callback: Callback, ) -> FsResult>> { Ok(crypto::create_file_writer( file, - self.tmp_dir.clone(), + &self.tmp_dir, self.cipher, self.key.get().await?, nonce_seed, @@ -1789,7 +1835,7 @@ impl EncryptedFs { /// Create a crypto reader from file using internal encryption info. pub async fn create_crypto_file_reader( &self, - file: PathBuf, + file: &Path, nonce_seed: u64, ) -> FsResult>> { Ok(crypto::create_file_reader( @@ -1808,7 +1854,7 @@ impl EncryptedFs { ) -> FsResult> { Ok(crypto::create_reader( file, - &self.cipher, + self.cipher, self.key.get().await?, nonce_seed, )) @@ -1818,7 +1864,7 @@ impl EncryptedFs { pub async fn decrypt_string(&self, s: &str) -> FsResult { Ok(crypto::decrypt_string( s, - &self.cipher, + self.cipher, self.key.get().await?, )?) } @@ -1834,11 +1880,11 @@ impl EncryptedFs { // decrypt key let salt = crypto::hash_secret_string(&old_password); - let initial_key = crypto::derive_key(&old_password, &cipher, salt)?; + let initial_key = crypto::derive_key(&old_password, cipher, salt)?; let enc_file = data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME); let reader = crypto::create_reader( File::open(enc_file.clone())?, - &cipher, + cipher, Arc::new(initial_key), 42_u64, ); @@ -1851,9 +1897,9 @@ impl EncryptedFs { // encrypt it with new key derived from new password let salt = crypto::hash_secret_string(&new_password); - let new_key = crypto::derive_key(&new_password, &cipher, salt)?; + let new_key = crypto::derive_key(&new_password, cipher, salt)?; let tmp = NamedTempFile::new_in(data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME))?; - let mut writer = crypto::create_writer(tmp, &cipher, Arc::new(new_key), 42_u64); + let mut writer = crypto::create_writer(tmp, cipher, &Arc::new(new_key), 42_u64); bincode::serialize_into(&mut writer, &key_store)?; writer.flush()?; let tmp = writer.finish()?; @@ -1876,10 +1922,7 @@ impl EncryptedFs { // read if let Some(set) = self.opened_files_for_read.read().await.get(&ino) { - for handle in set.iter().filter(|h| match skip_fh { - None => true, - Some(fh) => **h != fh, - }) { + for handle in set.iter().filter(|h| skip_fh.map_or(true, |fh| **h != fh)) { let map = self.read_handles.read().await; let mut ctx = map.get(handle).unwrap().lock().await; let reader = ctx.reader.as_mut().unwrap(); @@ -1901,9 +1944,7 @@ impl EncryptedFs { let writer = ctx.writer.as_mut().unwrap(); writer.flush()?; writer.seek(SeekFrom::Start(0))?; - let attr = self - .get_inode_from_storage(ino, self.key.get().await?) - .await?; + let attr = self.get_inode_from_storage(ino, self.key.get().await?)?; ctx.attr.size = attr.size; } } @@ -1918,10 +1959,8 @@ impl EncryptedFs { ) -> FsResult<()> { let ino = op.get_ino(); let path = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); - let reader = self.create_crypto_file_reader(path, op.get_ino()).await?; - let attr = self - .get_inode_from_storage(ino, self.key.get().await?) - .await?; + let reader = self.create_crypto_file_reader(&path, op.get_ino()).await?; + let attr = self.get_inode_from_storage(ino, self.key.get().await?)?; match op { ReadHandleContextOperation::Create { ino, lock } => { let attr: TimeAndSizeFileAttr = attr.into(); @@ -1958,9 +1997,7 @@ impl EncryptedFs { ino, handle, ); - let writer = self - .create_crypto_file_writer(path.clone(), ino, callback) - .await?; + let writer = self.create_crypto_file_writer(&path, ino, callback).await?; match op { WriteHandleContextOperation::Create { ino, lock } => { debug!("creating write handle"); @@ -2003,8 +2040,7 @@ impl EncryptedFs { attr.gid = libc::getgid(); } - self.write_inode_to_storage(&attr, self.key.get().await?) - .await?; + self.write_inode_to_storage(&attr, &self.key.get().await?)?; // create the directory tokio::fs::create_dir(self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string())) @@ -2013,24 +2049,23 @@ impl EncryptedFs { // add "." entry self.insert_directory_entry( attr.ino, - DirectoryEntry { + &DirectoryEntry { ino: attr.ino, name: SecretString::from_str("$.").unwrap(), kind: FileType::Directory, }, - self.key.get().await?, - ) - .await?; + &self.key.get().await?, + )?; } Ok(()) } - async fn insert_directory_entry( + fn insert_directory_entry( &self, ino_contents_dir: u64, - entry: DirectoryEntry, - key: Arc>, + entry: &DirectoryEntry, + key: &Arc>, ) -> FsResult<()> { // in order not to add too much to filename length we keep just 3 digits from nonce seed let nonce_seed = ino_contents_dir % 1000; @@ -2039,7 +2074,7 @@ impl EncryptedFs { .data_dir .join(CONTENTS_DIR) .join(ino_contents_dir.to_string()); - let name = crypto::encrypt_file_name(&entry.name, &self.cipher, key.clone(), nonce_seed)?; + let name = crypto::encrypt_file_name(&entry.name, self.cipher, key, nonce_seed)?; let file_path = parent_path.join(name); let map = self.serialize_dir_entries_locks.write().unwrap(); @@ -2051,7 +2086,7 @@ impl EncryptedFs { // write inode and file type let entry = (entry.ino, entry.kind); let tmp = NamedTempFile::new_in(self.tmp_dir.clone())?; - let mut writer = crypto::create_writer(tmp, &self.cipher, key, nonce_seed); + let mut writer = crypto::create_writer(tmp, self.cipher, key, nonce_seed); bincode::serialize_into(&mut writer, &entry)?; writer.flush()?; let tmp = writer.finish()?; @@ -2061,7 +2096,7 @@ impl EncryptedFs { async fn remove_directory_entry(&self, parent: u64, name: &SecretString) -> FsResult<()> { let parent_path = self.data_dir.join(CONTENTS_DIR).join(parent.to_string()); - let name = crypto::encrypt_file_name(name, &self.cipher, self.key.get().await?, parent)?; + let name = crypto::encrypt_file_name(name, self.cipher, &self.key.get().await?, parent)?; let file_path = parent_path.join(name); let map = self.serialize_dir_entries_locks.write().unwrap(); @@ -2093,11 +2128,11 @@ impl EncryptedFs { fn read_or_create_key( path: &PathBuf, password: &SecretString, - cipher: &Cipher, + cipher: Cipher, ) -> FsResult> { // derive key from password - let salt = crypto::hash_secret_string(&password); - let derived_key = crypto::derive_key(&password, cipher, salt)?; + let salt = crypto::hash_secret_string(password); + let derived_key = crypto::derive_key(password, cipher, salt)?; if path.exists() { // read key @@ -2127,9 +2162,10 @@ fn read_or_create_key( .read(true) .write(true) .create(true) + .truncate(true) .open(path)?, cipher, - Arc::new(derived_key), + &Arc::new(derived_key), 42_u64, ); bincode::serialize_into(&mut writer, &key_store)?; @@ -2176,16 +2212,16 @@ async fn check_structure(data_dir: &PathBuf, ignore_empty: bool) -> FsResult<()> .iter() .map(|dir| dir.file_name().to_string_lossy().to_string()) .collect::>(); - if vec.len() == 0 && ignore_empty { + if vec.is_empty() && ignore_empty { return Ok(()); } if vec.len() != 3 { return Err(FsError::InvalidDataDirStructure); } // make sure existing structure is ok - vec.sort(); + vec.sort_unstable(); let mut vec2 = vec![INODES_DIR, CONTENTS_DIR, SECURITY_DIR]; - vec2.sort(); + vec2.sort_unstable(); if vec != vec2 || !data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME).is_file() { return Err(FsError::InvalidDataDirStructure); } @@ -2193,7 +2229,7 @@ async fn check_structure(data_dir: &PathBuf, ignore_empty: bool) -> FsResult<()> Ok(()) } -fn merge_attr(attr: &mut FileAttr, set_attr: SetFileAttr) { +fn merge_attr(attr: &mut FileAttr, set_attr: &SetFileAttr) { if let Some(size) = set_attr.size { attr.size = size; } @@ -2233,7 +2269,6 @@ impl FileCryptoWriterCallback for LocalFileCryptoWriterCallback { ) -> io::Result<()> { if let Some(fs) = self.0.upgrade() { let (tx, rx) = std::sync::mpsc::channel::>(); - let fs = fs.clone(); let ino = self.1; let fh = self.2; TX.lock() @@ -2265,20 +2300,21 @@ pub async fn write_all_string_to_fs( offset: u64, s: &str, fh: u64, -) -> anyhow::Result<()> { +) -> FsResult<()> { write_all_bytes_to_fs(fs, ino, offset, s.as_bytes(), fh).await } +#[allow(clippy::missing_panics_doc)] pub async fn write_all_bytes_to_fs( fs: &EncryptedFs, ino: u64, offset: u64, buf: &[u8], fh: u64, -) -> anyhow::Result<()> { +) -> FsResult<()> { let mut pos = 0_usize; loop { - let len = fs.write(ino, offset, &buf[pos..], fh).await.unwrap(); + let len = fs.write(ino, offset, &buf[pos..], fh).await?; pos += len; if pos == buf.len() { break; diff --git a/src/encryptedfs_fuse3.rs b/src/encryptedfs_fuse3.rs index d93284cc..7665d5fe 100644 --- a/src/encryptedfs_fuse3.rs +++ b/src/encryptedfs_fuse3.rs @@ -67,6 +67,7 @@ impl Iterator for DirectoryEntryIterator { inode: entry.ino, kind, name: OsString::from(entry.name.expose_secret()), + #[allow(clippy::cast_possible_wrap)] offset: self.1 as i64, })) } @@ -102,6 +103,7 @@ impl Iterator for DirectoryEntryPlusIterator { generation: 0, kind, name: OsString::from(entry.name.expose_secret()), + #[allow(clippy::cast_possible_wrap)] offset: self.1 as i64, attr: entry.attr.into(), entry_ttl: TTL, @@ -158,7 +160,8 @@ impl EncryptedFsFuse3 { self.fs.clone() } - fn creation_mode(&self, mode: u32) -> u16 { + #[allow(clippy::cast_possible_truncation)] + const fn creation_mode(&self, mode: u32) -> u16 { if self.suid_support { mode as u16 } else { @@ -237,7 +240,8 @@ impl EncryptedFsFuse3 { } } -fn creation_gid(parent: &FileAttr, gid: u32) -> u32 { +#[allow(clippy::cast_possible_truncation)] +const fn creation_gid(parent: &FileAttr, gid: u32) -> u32 { if parent.perm & libc::S_ISGID as u16 != 0 { return parent.gid; } @@ -247,7 +251,7 @@ fn creation_gid(parent: &FileAttr, gid: u32) -> u32 { impl From for fuse3::raw::prelude::FileAttr { fn from(from: FileAttr) -> Self { - fuse3::raw::prelude::FileAttr { + Self { ino: from.ino, size: from.size, blocks: from.blocks, @@ -271,7 +275,7 @@ impl From for fuse3::raw::prelude::FileAttr { impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self), err(level = Level::INFO), ret(level = Level::DEBUG))] - async fn init(&self, _req: Request) -> Result { + async fn init(&self, req: Request) -> Result { trace!(""); Ok(ReplyInit { @@ -280,7 +284,7 @@ impl Filesystem for EncryptedFsFuse3 { } #[instrument(skip(self))] - async fn destroy(&self, _req: Request) { + async fn destroy(&self, req: Request) { trace!(""); } @@ -352,10 +356,10 @@ impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self), err(level = Level::INFO), ret(level = Level::DEBUG))] async fn getattr( &self, - _req: Request, + req: Request, inode: u64, - _fh: Option, - _flags: u32, + fh: Option, + flags: u32, ) -> Result { trace!(""); @@ -379,11 +383,12 @@ impl Filesystem for EncryptedFsFuse3 { } #[instrument(skip(self), err(level = Level::INFO), ret(level = Level::DEBUG))] + #[allow(clippy::cast_possible_truncation)] async fn setattr( &self, req: Request, inode: Inode, - _fh: Option, + fh: Option, set_attr: SetAttr, ) -> Result { trace!(""); @@ -394,18 +399,18 @@ impl Filesystem for EncryptedFsFuse3 { Errno::from(ENOENT) })?; - let mut set_attr2: SetFileAttr = Default::default(); + let mut set_attr2 = SetFileAttr::default(); if let Some(mode) = set_attr.mode { debug!("chmod mode={mode:o}"); - let mut set_attr2: SetFileAttr = Default::default(); + let mut set_attr2 = SetFileAttr::default(); if req.uid != 0 && req.uid != attr.uid { return Err(EPERM.into()); } if req.uid != 0 && req.gid != attr.gid && !get_groups(req.pid).contains(&attr.gid) { // If SGID is set and the file belongs to a group that the caller is not part of // then the SGID bit is suppose to be cleared during chmod - set_attr2 = set_attr2.with_perm((mode & !libc::S_ISGID as u32) as u16); + set_attr2 = set_attr2.with_perm((mode & !libc::S_ISGID) as u16); } else { set_attr2 = set_attr2.with_perm(mode as u16); } @@ -430,7 +435,7 @@ impl Filesystem for EncryptedFsFuse3 { if set_attr.uid.is_some() || set_attr.gid.is_some() { debug!(?set_attr.uid, ?set_attr.gid, "chown"); - let mut set_attr2: SetFileAttr = Default::default(); + let mut set_attr2 = SetFileAttr::default(); if let Some(gid) = set_attr2.gid { // Non-root users can only change gid to a group they're in if req.uid != 0 && !get_groups(req.pid).contains(&gid) { @@ -459,14 +464,14 @@ impl Filesystem for EncryptedFsFuse3 { if let Some(uid) = set_attr2.uid { set_attr2 = set_attr2.with_uid(uid); // Clear SETUID on owner change - let perm = set_attr2.perm.as_ref().unwrap().clone(); + let perm = *set_attr2.perm.as_ref().unwrap(); set_attr2 = set_attr2.with_perm(perm & !(libc::S_ISUID as u16)); } if let Some(gid) = set_attr2.gid { set_attr2 = set_attr2.with_gid(gid); // Clear SETGID unless user is root if req.uid != 0 { - let perm = set_attr2.perm.as_ref().unwrap().clone(); + let perm = *set_attr2.perm.as_ref().unwrap(); set_attr2 = set_attr2.with_perm(perm & !(libc::S_ISGID as u16)); } } @@ -554,16 +559,16 @@ impl Filesystem for EncryptedFsFuse3 { parent: Inode, name: &OsStr, mode: u32, - _rdev: u32, + rdev: u32, ) -> Result { trace!(""); debug!("mode={mode:o}"); - let file_type = mode & libc::S_IFMT as u32; + let file_type = mode & libc::S_IFMT; - if file_type != libc::S_IFREG as u32 + if file_type != libc::S_IFREG // && file_type != libc::S_IFLNK as u32 - && file_type != libc::S_IFDIR as u32 + && file_type != libc::S_IFDIR { // TODO warn!("implementation is incomplete. Only supports regular files and directories. Got mode={mode:o}"); @@ -626,8 +631,9 @@ impl Filesystem for EncryptedFsFuse3 { if req.uid != 0 { mode &= !(libc::S_ISUID | libc::S_ISGID); } + #[allow(clippy::cast_possible_truncation)] if parent_attr.perm & libc::S_ISGID as u16 != 0 { - mode |= libc::S_ISGID as u32; + mode |= libc::S_ISGID; } attr.perm = self.creation_mode(mode); @@ -696,6 +702,7 @@ impl Filesystem for EncryptedFsFuse3 { let uid = req.uid; // "Sticky bit" handling + #[allow(clippy::cast_possible_truncation)] if parent_attr.perm & libc::S_ISVTX as u16 != 0 && uid != 0 && uid != parent_attr.uid @@ -723,9 +730,7 @@ impl Filesystem for EncryptedFsFuse3 { async fn rmdir(&self, req: Request, parent: Inode, name: &OsStr) -> Result<()> { trace!(""); - let parent_attr = if let Ok(attr) = self.get_fs().get_inode(parent).await { - attr - } else { + let Ok(parent_attr) = self.get_fs().get_inode(parent).await else { error!(parent, "not found"); return Err(ENOENT.into()); }; @@ -741,19 +746,16 @@ impl Filesystem for EncryptedFsFuse3 { return Err(EACCES.into()); } - let attr = match self + let Ok(Some(attr)) = self .get_fs() .find_by_name( parent, &SecretString::from_str(name.to_str().unwrap()).unwrap(), ) .await - { - Ok(Some(attr)) => attr, - _ => { - error!(parent, name = name.to_str().unwrap()); - return Err(ENOENT.into()); - } + else { + error!(parent, name = name.to_str().unwrap()); + return Err(ENOENT.into()); }; if attr.kind != FileType::Directory { @@ -762,6 +764,7 @@ impl Filesystem for EncryptedFsFuse3 { let uid = req.uid; // "Sticky bit" handling + #[allow(clippy::cast_possible_truncation)] if parent_attr.perm & libc::S_ISVTX as u16 != 0 && uid != 0 && uid != parent_attr.uid @@ -799,16 +802,14 @@ impl Filesystem for EncryptedFsFuse3 { ) -> Result<()> { trace!(""); - let attr = if let Ok(Some(attr)) = self + let Ok(Some(attr)) = self .get_fs() .find_by_name( parent, &SecretString::from_str(name.to_str().unwrap()).unwrap(), ) .await - { - attr - } else { + else { error!( parent, name = name.to_str().unwrap(), @@ -817,9 +818,7 @@ impl Filesystem for EncryptedFsFuse3 { return Err(ENOENT.into()); }; - let parent_attr = if let Ok(attr) = self.get_fs().get_inode(parent).await { - attr - } else { + let Ok(parent_attr) = self.get_fs().get_inode(parent).await else { error!(parent, "parent not found"); return Err(ENOENT.into()); }; @@ -836,6 +835,7 @@ impl Filesystem for EncryptedFsFuse3 { } // "Sticky bit" handling + #[allow(clippy::cast_possible_truncation)] if parent_attr.perm & libc::S_ISVTX as u16 != 0 && req.uid != 0 && req.uid != parent_attr.uid @@ -844,9 +844,7 @@ impl Filesystem for EncryptedFsFuse3 { return Err(EACCES.into()); } - let new_parent_attr = if let Ok(attr) = self.get_fs().get_inode(new_parent).await { - attr - } else { + let Ok(new_parent_attr) = self.get_fs().get_inode(new_parent).await else { error!(new_parent, "not found"); return Err(ENOENT.into()); }; @@ -863,6 +861,7 @@ impl Filesystem for EncryptedFsFuse3 { } // "Sticky bit" handling in new_parent + #[allow(clippy::cast_possible_truncation)] if new_parent_attr.perm & libc::S_ISVTX as u16 != 0 { if let Ok(Some(new_attrs)) = self .get_fs() @@ -897,7 +896,7 @@ impl Filesystem for EncryptedFsFuse3 { ) .await { - Ok(_) => Ok(()), + Ok(()) => Ok(()), Err(FsError::NotEmpty) => Err(ENOTEMPTY.into()), _ => Err(ENOENT.into()), } @@ -907,6 +906,7 @@ impl Filesystem for EncryptedFsFuse3 { async fn open(&self, req: Request, inode: Inode, flags: u32) -> Result { trace!(""); + #[allow(clippy::cast_possible_wrap)] let (access_mask, read, write) = match flags as i32 & libc::O_ACCMODE { libc::O_RDONLY => { // Behavior is undefined, but most filesystems return EACCES @@ -928,9 +928,9 @@ impl Filesystem for EncryptedFsFuse3 { } }; - let _create = flags & libc::O_CREAT as u32 != 0; + // let _create = flags & libc::O_CREAT as u32 != 0; let truncate = flags & libc::O_TRUNC as u32 != 0; - let _append = flags & libc::O_APPEND as u32 != 0; + // let _append = flags & libc::O_APPEND as u32 != 0; let attr = self.get_fs().get_inode(inode).await.map_err(|err| { error!(err = %err); @@ -966,7 +966,7 @@ impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self), err(level = Level::INFO))] async fn read( &self, - _req: Request, + req: Request, inode: u64, fh: u64, offset: u64, @@ -989,13 +989,13 @@ impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self, data), err(level = Level::INFO), ret(level = Level::DEBUG))] async fn write( &self, - _req: Request, + req: Request, inode: Inode, fh: u64, offset: u64, data: &[u8], - _write_flags: u32, - _flags: u32, + write_flags: u32, + flags: u32, ) -> Result { trace!(""); debug!(size = data.len()); @@ -1005,18 +1005,18 @@ impl Filesystem for EncryptedFsFuse3 { .write(inode, offset, data, fh) .await .map_err(|err| { - error!("{err:#?}"); error!(err = %err); EIO })?; Ok(ReplyWrite { + #[allow(clippy::cast_possible_truncation)] written: len as u32, }) } #[instrument(skip(self), err(level = Level::INFO), ret(level = Level::DEBUG))] - async fn statfs(&self, _req: Request, inode: u64) -> Result { + async fn statfs(&self, req: Request, inode: u64) -> Result { trace!(""); warn!("implementation is a stub"); Ok(STATFS) @@ -1055,7 +1055,7 @@ impl Filesystem for EncryptedFsFuse3 { error!(err = %err); Errno::from(ENOENT) })?; - let mut set_attr: SetFileAttr = Default::default(); + let mut set_attr = SetFileAttr::default(); // XXX: In theory we should only need to do this when WRITE_KILL_PRIV is set for 7.31+ // However, xfstests fail in that case @@ -1082,6 +1082,7 @@ impl Filesystem for EncryptedFsFuse3 { } #[instrument(skip(self), err(level = Level::INFO), ret(level = Level::DEBUG))] + #[allow(clippy::cast_possible_wrap)] async fn opendir(&self, req: Request, inode: Inode, flags: u32) -> Result { trace!(""); @@ -1125,7 +1126,7 @@ impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self), err(level = Level::INFO))] async fn readdir( &self, - _req: Request, + req: Request, inode: u64, fh: u64, offset: i64, @@ -1142,6 +1143,8 @@ impl Filesystem for EncryptedFsFuse3 { let iter = DirectoryEntryIterator(iter, 0); Ok(ReplyDirectory { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] entries: stream::iter(iter.skip(offset as usize)), }) } @@ -1157,16 +1160,17 @@ impl Filesystem for EncryptedFsFuse3 { async fn access(&self, req: Request, inode: u64, mask: u32) -> Result<()> { trace!(""); - match self.get_fs().get_inode(inode).await { - Ok(attr) => { + self.get_fs().get_inode(inode).await.map_or_else( + |_| Err(ENOENT.into()), + |attr| { + #[allow(clippy::cast_possible_wrap)] if check_access(attr.uid, attr.gid, attr.perm, req.uid, req.gid, mask as i32) { Ok(()) } else { Err(EACCES.into()) } - } - _ => Err(ENOENT.into()), - } + }, + ) } #[instrument(skip(self, name), fields(name = name.to_str().unwrap()), err(level = Level::INFO), ret(level = Level::DEBUG))] @@ -1180,6 +1184,7 @@ impl Filesystem for EncryptedFsFuse3 { ) -> Result { trace!(""); + #[allow(clippy::cast_possible_wrap)] let (read, write) = match flags as i32 & libc::O_ACCMODE { libc::O_RDONLY => (true, false), libc::O_WRONLY => (false, true), @@ -1212,11 +1217,11 @@ impl Filesystem for EncryptedFsFuse3 { #[instrument(skip(self), err(level = Level::INFO))] async fn readdirplus( &self, - _req: Request, + req: Request, parent: u64, - _fh: u64, + fh: u64, offset: u64, - _lock_owner: u64, + lock_owner: u64, ) -> Result>> { trace!(""); @@ -1230,6 +1235,7 @@ impl Filesystem for EncryptedFsFuse3 { let iter = DirectoryEntryPlusIterator(iter, 0); Ok(ReplyDirectoryPlus { + #[allow(clippy::cast_possible_truncation)] entries: stream::iter(iter.skip(offset as usize)), }) } @@ -1249,6 +1255,7 @@ impl Filesystem for EncryptedFsFuse3 { ) -> Result { trace!(""); + #[allow(clippy::cast_possible_truncation)] match self .get_fs() .copy_file_range( @@ -1291,7 +1298,8 @@ fn get_groups(pid: u32) -> Vec { vec![] } -fn clear_suid_sgid(mut perm: u16) -> u16 { +#[allow(clippy::cast_possible_truncation)] +const fn clear_suid_sgid(mut perm: u16) -> u16 { perm &= !libc::S_ISUID as u16; // SGID is only suppose to be cleared if XGRP is set if perm & libc::S_IXGRP as u16 != 0 { @@ -1301,20 +1309,20 @@ fn clear_suid_sgid(mut perm: u16) -> u16 { } fn as_file_kind(mut mode: u32) -> FileType { - mode &= libc::S_IFMT as u32; + mode &= libc::S_IFMT; - if mode == libc::S_IFREG as u32 { + if mode == libc::S_IFREG { FileType::RegularFile // } else if mode == libc::S_IFLNK as u32 { // return FileType::Symlink; - } else if mode == libc::S_IFDIR as u32 { + } else if mode == libc::S_IFDIR { FileType::Directory } else { unimplemented!("{mode}"); } } -fn dir_attr() -> CreateFileAttr { +const fn dir_attr() -> CreateFileAttr { CreateFileAttr { kind: FileType::Directory, perm: 0o777, @@ -1325,7 +1333,7 @@ fn dir_attr() -> CreateFileAttr { } } -fn file_attr() -> CreateFileAttr { +const fn file_attr() -> CreateFileAttr { CreateFileAttr { kind: FileType::RegularFile, perm: 0o644, @@ -1337,8 +1345,8 @@ fn file_attr() -> CreateFileAttr { } fn check_access( - file_uid: u32, - file_gid: u32, + #[allow(clippy::similar_names)] file_uid: u32, + #[allow(clippy::similar_names)] file_gid: u32, file_mode: u16, uid: u32, gid: u32, @@ -1371,6 +1379,7 @@ fn check_access( access_mask == 0 } +#[allow(clippy::cast_sign_loss)] fn system_time_from_timestamp(t: Timestamp) -> SystemTime { UNIX_EPOCH + Duration::new(t.sec as u64, t.nsec) } diff --git a/src/expire_value.rs b/src/expire_value.rs index e574b366..8dc5b0b4 100644 --- a/src/expire_value.rs +++ b/src/expire_value.rs @@ -1,4 +1,5 @@ use std::error::Error; +use std::marker::PhantomData; use std::string::ToString; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -9,11 +10,15 @@ use tokio::task::JoinHandle; const KEY: &str = "key"; -pub trait Provider: Send + Sync + 'static { +pub trait Provider: Send + Sync + 'static { fn provide(&self) -> Result; } -pub struct ExpireValue> { +pub struct ExpireValue< + T: Send + Sync + 'static, + E: Error + Send + Sync + 'static, + P: Provider, +> { cache: Arc>>, weak: RwLock>>, monitor: Option>, @@ -22,19 +27,21 @@ pub struct ExpireValue> { _marker: std::marker::PhantomData, } -impl> ExpireValue { - pub async fn new(provider: P, duration: Duration) -> Self { +impl> + ExpireValue +{ + pub fn new(provider: P, duration: Duration) -> Self { let mut s = Self { cache: Arc::new(Cache::new()), weak: RwLock::new(None), monitor: None, provider, duration, - _marker: Default::default(), + _marker: PhantomData {}, }; let clone = s.cache.clone(); s.monitor = Some(tokio::spawn(async move { - clone.monitor(4, 0.25, duration).await + clone.monitor(4, 0.25, duration).await; })); s @@ -61,11 +68,10 @@ impl> ExpireValu // try to take it from weak ref if let Some(ref v) = weak.upgrade() { return Some(v.clone()); - } else { - // try to take it from cache - if let Some(v) = self.cache.get(&KEY.to_string()).await { - return Some(Arc::clone(&v)); - } + } + // try to take it from cache + if let Some(v) = self.cache.get(&KEY.to_string()).await { + return Some(Arc::clone(&v)); } } None @@ -76,7 +82,9 @@ impl> ExpireValu } } -impl> Drop for ExpireValue { +impl> Drop + for ExpireValue +{ fn drop(&mut self) { if let Some(ref monitor) = self.monitor { monitor.abort(); diff --git a/src/keyring.rs b/src/keyring.rs index 9d946037..e5a884e9 100644 --- a/src/keyring.rs +++ b/src/keyring.rs @@ -1,10 +1,10 @@ use keyring::Entry; use secrecy::{ExposeSecret, SecretString}; -const KEYRING_SERVICE: &'static str = "rencfs"; -const KEYRING_USER: &'static str = "encrypted_fs"; +const KEYRING_SERVICE: &str = "rencfs"; +const KEYRING_USER: &str = "encrypted_fs"; -pub(crate) fn save(password: SecretString, suffix: &str) -> Result<(), keyring::Error> { +pub(crate) fn save(password: &SecretString, suffix: &str) -> Result<(), keyring::Error> { let entry = Entry::new(KEYRING_SERVICE, &format!("{KEYRING_USER}.{suffix}"))?; entry.set_password(password.expose_secret()) } diff --git a/src/lib.rs b/src/lib.rs index 94275b5b..4dbaff87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,14 @@ // #![feature(error_generic_member_access)] +#![deny(clippy::all)] +#![deny(clippy::pedantic)] +#![deny(clippy::nursery)] +#![deny(clippy::cargo)] +// #![deny(missing_docs)] +#![allow(clippy::similar_names)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::significant_drop_tightening)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::missing_errors_doc)] //! # Encrypted File System //! //! An encrypted file system that mounts with FUSE on Linux. It can be used to create encrypted directories. @@ -12,7 +22,7 @@ //! //! In the following example, we will see how we can use the library. //! -//! ## Calling (run_fuse)[run_fuse] +//! ## Calling (`run_fuse`)[`run_fuse`] //! //! ### Example //! @@ -226,7 +236,8 @@ pub mod expire_value; pub mod stream_util; #[allow(unreachable_code)] -pub fn is_debug() -> bool { +#[must_use] +pub const fn is_debug() -> bool { #[cfg(debug_assertions)] { return true; diff --git a/src/main.rs b/src/main.rs index c9e140a0..0bfa9844 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,16 +35,8 @@ async fn main() -> Result<()> { let log_level = if is_debug() { Level::DEBUG } else { - let str = match matches.subcommand() { - Some(("mount", matches)) => { - Some(matches.get_one::("log-level").unwrap().as_str()) - } - Some(("change-password", matches)) => { - Some(matches.get_one::("log-level").unwrap().as_str()) - } - _ => None, - }; - let log_level = Level::from_str(str.unwrap()); + let str = matches.get_one::("log-level").unwrap().as_str(); + let log_level = Level::from_str(str); if log_level.is_err() { error!("Invalid log level"); return Err(ExitStatusError::Failure(1).into()); @@ -69,7 +61,7 @@ async fn main() -> Result<()> { }) .await; match res { - Ok(Ok(Ok(_))) => Ok(()), + Ok(Ok(Ok(()))) => Ok(()), Ok(Ok(Err(err))) => { let err2 = err.downcast_ref::(); if let Some(ExitStatusError::Failure(code)) = err2 { @@ -101,12 +93,21 @@ async fn main() -> Result<()> { } } +#[allow(clippy::too_many_lines)] fn get_cli_args() -> ArgMatches { - let matches = Command::new("RencFs") + Command::new("RencFs") .version(crate_version!()) .author("Radu Marias") - .subcommand_required(true) .arg_required_else_help(true) + .arg( + Arg::new("log-level") + .long("log-level") + .short('l') + .value_name("log-level") + .default_value("INFO") + .help("Log level, possible values: TRACE, DEBUG, INFO, WARN, ERROR"), + ) + .subcommand_required(true) .subcommand( Command::new("mount") .about("Mount the filesystem exposing decrypted content from data dir") @@ -186,19 +187,11 @@ fn get_cli_args() -> ArgMatches { .default_value("ChaCha20") .help(format!("Cipher used for encryption, possible values: {}", Cipher::iter().fold(String::new(), |mut acc, x| { - acc.push_str(format!("{acc}{}{x}", if acc.len() != 0 { ", " } else { "" }).as_str()); + acc.push_str(format!("{acc}{}{x}", if acc.is_empty() { "" } else { ", " }).as_str()); acc }).as_str()), ) ) - .arg( - Arg::new("log-level") - .long("log-level") - .short('l') - .value_name("log-level") - .default_value("INFO") - .help("Log level, possible values: TRACE, DEBUG, INFO, WARN, ERROR"), - ) ).subcommand( Command::new("change-password") .about("Change password for the encrypted data") @@ -218,30 +211,21 @@ fn get_cli_args() -> ArgMatches { .default_value("ChaCha20") .help(format!("Cipher used for encryption, possible values: {}", Cipher::iter().fold(String::new(), |mut acc, x| { - acc.push_str(format!("{acc}{}{x}", if acc.len() != 0 { ", " } else { "" }).as_str()); + acc.push_str(format!("{acc}{}{x}", if acc.is_empty() { "" } else { ", " }).as_str()); acc }).as_str()), ) ) - .arg( - Arg::new("log-level") - .long("log-level") - .short('l') - .value_name("log-level") - .default_value("INFO") - .help("Log level, possible values: TRACE, DEBUG, INFO, WARN, ERROR"), - ) ) - .get_matches(); - matches + .get_matches() } async fn async_main() -> Result<()> { let matches = get_cli_args(); match matches.subcommand() { - Some(("change-password", matches)) => run_change_password(&matches).await?, - Some(("mount", matches)) => run_mount(&matches).await?, + Some(("change-password", matches)) => run_change_password(matches).await?, + Some(("mount", matches)) => run_mount(matches).await?, None => { error!("No subcommand provided"); return Err(ExitStatusError::Failure(1).into()); @@ -327,7 +311,7 @@ async fn run_mount(matches: &ArgMatches) -> Result<()> { // when running from IDE we can't read from stdin with rpassword, get it from env var let mut password = - SecretString::new(env::var("RENCFS_PASSWORD").unwrap_or_else(|_| "".to_string())); + SecretString::new(env::var("RENCFS_PASSWORD").unwrap_or_else(|_| String::new())); if password.expose_secret().is_empty() { // read password from stdin print!("Enter password: "); @@ -354,9 +338,9 @@ async fn run_mount(matches: &ArgMatches) -> Result<()> { } } // save password in keyring - keyring::save(password.clone(), "password").map_err(|err| { + keyring::save(&password, "password").map_err(|err| { error!(err = %err); - ExitStatusError::from(ExitStatusError::Failure(1)) + ExitStatusError::Failure(1) })?; if matches.get_flag("umount-on-start") { @@ -394,8 +378,9 @@ async fn run_mount(matches: &ArgMatches) -> Result<()> { }) .unwrap(); + #[allow(clippy::items_after_statements)] struct PasswordProviderImpl {} - + #[allow(clippy::items_after_statements)] impl PasswordProvider for PasswordProviderImpl { fn get_password(&self) -> Option { keyring::get("password") @@ -431,8 +416,11 @@ fn umount(mountpoint: &str) -> Result<()> { Ok(()) } +#[allow(clippy::missing_panics_doc)] pub fn log_init(level: Level) -> WorkerGuard { - let directive = format!("rencfs={}", level.as_str()).parse().unwrap(); + let directive = format!("rencfs={}", level.as_str()) + .parse() + .expect("cannot parse log directive"); let filter = EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env() @@ -445,9 +433,9 @@ pub fn log_init(level: Level) -> WorkerGuard { .with_env_filter(filter); // .with_max_level(level); if is_debug() { - builder.pretty().init() + builder.pretty().init(); } else { - builder.pretty().init() + builder.init(); } guard diff --git a/src/stream_util.rs b/src/stream_util.rs index 49b1859d..b2662e79 100644 --- a/src/stream_util.rs +++ b/src/stream_util.rs @@ -28,6 +28,7 @@ pub fn seek_forward(r: &mut impl Read, len: u64, stop_on_eof: bool) -> io::Resul let mut buffer = vec![0; BUF_SIZE]; let mut pos = 0_u64; loop { + #[allow(clippy::cast_possible_truncation)] let read_len = if pos + buffer.len() as u64 > len { (len - pos) as usize } else { @@ -51,12 +52,11 @@ pub fn seek_forward(r: &mut impl Read, len: u64, stop_on_eof: bool) -> io::Resul } else if read == 0 { if stop_on_eof { break; - } else { - return Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "unexpected eof", - ))?; } + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "unexpected eof", + )); } } @@ -79,6 +79,7 @@ pub fn copy(r: &mut impl Read, w: &mut impl Write, len: u64, stop_on_eof: bool) let mut buffer = vec![0; BUF_SIZE]; let mut read_pos = 0_u64; loop { + #[allow(clippy::cast_possible_truncation)] let buf_len = min(buffer.len(), (len - read_pos) as usize); debug!( "reading from file pos {} buf_len {}", @@ -100,12 +101,11 @@ pub fn copy(r: &mut impl Read, w: &mut impl Write, len: u64, stop_on_eof: bool) } else if read == 0 { if stop_on_eof { break; - } else { - return Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "unexpected eof", - ))?; } + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "unexpected eof", + )); } } Ok(read_pos) @@ -120,6 +120,7 @@ pub fn fill_zeros(w: &mut impl Write, len: u64) -> io::Result<()> { let buffer = vec![0; BUF_SIZE]; let mut written = 0_u64; loop { + #[allow(clippy::cast_possible_truncation)] let buf_len = min(buffer.len(), (len - written) as usize); w.write_all(&buffer[..buf_len])?; written += buf_len as u64;