From d5658e82758f81bfc8bb0dac141ad0935c3de030 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Thu, 2 May 2024 00:16:01 +0300 Subject: [PATCH] add ArcHashmap to store values that are auto removed when no ref to them add homepage url add locks on dir entry operations allow publicly only change operations on inode meta with specified fields to update, it will merge time info to take the most recent ones add back change password when fs is created --- Cargo.toml | 1 + examples/arc_hashmap.rs | 14 +++ src/arc_hashmap.rs | 45 +++++---- src/encryptedfs.rs | 198 ++++++++++++++++++++++++--------------- src/encryptedfs_fuse3.rs | 51 +++++----- 5 files changed, 182 insertions(+), 127 deletions(-) create mode 100644 examples/arc_hashmap.rs diff --git a/Cargo.toml b/Cargo.toml index 15aebf65..b1a3acce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ version = "0.2.0" edition = "2021" license = "Apache-2.0" authors = ["Radu Marias "] +homepage = "https://github.com/radumarias/rencfs" repository = "https://github.com/radumarias/rencfs" readme = "README.md" keywords = ["filesystem", "fuse", "encryption", "system", "security"] diff --git a/examples/arc_hashmap.rs b/examples/arc_hashmap.rs new file mode 100644 index 00000000..77c7592f --- /dev/null +++ b/examples/arc_hashmap.rs @@ -0,0 +1,14 @@ +use rencfs::arc_hashmap::ArcHashMap; + +fn main() { + let mut m = ArcHashMap::new(); + { + let v = m.insert(1, 2); + println!("size {}", m.len()); + m.insert(2, 3); + println!("size {}", m.len()); + let v = m.get_or_insert_with(3, || 4); + println!("size {}", m.len()); + } + println!("size {}", m.len()); +} \ No newline at end of file diff --git a/src/arc_hashmap.rs b/src/arc_hashmap.rs index 6b285761..c438791b 100644 --- a/src/arc_hashmap.rs +++ b/src/arc_hashmap.rs @@ -1,14 +1,14 @@ use std::collections::HashMap; use std::hash::Hash; use std::ops::Deref; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct ArcHashMap where - K: Eq + Hash + Copy, + K: Eq + Hash, { - map: HashMap, Arc)>, + map: Mutex, Arc)>>, } pub struct Guard @@ -32,51 +32,50 @@ impl Deref for Guard { } } -impl ArcHashMap { +impl ArcHashMap { pub fn new() -> Self { ArcHashMap { - map: HashMap::new(), + map: Mutex::new(HashMap::new()), } } - pub fn insert(&mut self, key: K, value: V) -> Guard { + pub fn insert(&self, key: K, value: V) -> Guard { self.purge(); self.get_or_insert_with(key, || value) } - pub fn get<'a>(&mut self, key: &K) -> Option> { - let v = self.map.get_mut(key); + pub fn get<'a>(&self, key: &K) -> Option> { + self.purge(); + self.get_internal(self.map.lock().unwrap().get(key)) + } + + pub fn get_internal<'a>(&self, v: Option<&(Arc, Arc)>) -> Option> { if let Some((v, rc)) = v { rc.fetch_add(1, Ordering::SeqCst); return Some(Guard { val: v.clone(), rc: rc.clone() }); } - self.purge(); None } - pub fn get_or_insert_with(&mut self, key: K, f: F) -> Guard + pub fn get_or_insert_with(&self, key: K, f: F) -> Guard where F: FnOnce() -> V, { self.purge(); - let key2 = key.clone(); - self.map.entry(key).or_insert_with(|| { - (Arc::new(f()), Arc::new(AtomicUsize::new(1))) + 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(&key2).unwrap() + self.get_internal(Some(v)).unwrap() } - fn purge(&mut self) { - let keys = self.map.keys().cloned().collect::>(); - for k in keys { - if self.map.get(&k).unwrap().1.load(Ordering::SeqCst) == 0 { - self.map.remove(&k); - } - } + fn purge(&self) { + let mut map = self.map.lock().unwrap(); + map.retain(|_, v| v.1.load(Ordering::SeqCst) > 0); } - pub fn len(&mut self) -> usize { + pub fn len(&self) -> usize { self.purge(); - self.map.len() + self.map.lock().unwrap().len() } } \ No newline at end of file diff --git a/src/encryptedfs.rs b/src/encryptedfs.rs index 64dda666..a55bae3f 100644 --- a/src/encryptedfs.rs +++ b/src/encryptedfs.rs @@ -5,9 +5,9 @@ use std::fmt::Debug; use std::fs::{File, OpenOptions, ReadDir}; use std::io::{Read, Write}; use std::ops::Deref; -use std::os::unix::fs::MetadataExt; use std::path::PathBuf; use std::str::FromStr; +use std::sync::Arc; use std::sync::atomic::AtomicU64; use std::time::{Duration, SystemTime}; @@ -363,7 +363,7 @@ impl PartialEq for DirectoryEntryPlus { pub type FsResult = Result; -pub struct DirectoryEntryIterator(ReadDir, Cipher, SecretVec); +pub struct DirectoryEntryIterator(ReadDir, Cipher, Arc>, Arc>>>); impl Iterator for DirectoryEntryIterator { type Item = FsResult; @@ -374,10 +374,16 @@ impl Iterator for DirectoryEntryIterator { return Some(Err(e.into())); } 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 _guard = lock.read().unwrap(); + let file = File::open(entry.path()); if let Err(e) = file { return Some(Err(e.into())); } + let file = file.unwrap(); let name = entry.file_name().to_string_lossy().to_string(); let name = { @@ -389,6 +395,7 @@ impl Iterator for DirectoryEntryIterator { crypto_util::decrypt_and_unnormalize_end_file_name(&name, &self.1, &self.2) } }; + let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto_util::create_decryptor(file, &self.1, &self.2)); if let Err(e) = res { return Some(Err(e.into())); @@ -398,7 +405,9 @@ impl Iterator for DirectoryEntryIterator { } } -pub struct DirectoryEntryPlusIterator(ReadDir, PathBuf, Cipher, SecretVec); +pub struct DirectoryEntryPlusIterator(ReadDir, PathBuf, Cipher, Arc>, + Arc>>>, + Arc>>>); impl Iterator for DirectoryEntryPlusIterator { type Item = FsResult; @@ -411,6 +420,11 @@ impl Iterator for DirectoryEntryPlusIterator { return Some(Err(e.into())); } 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 _guard = lock.read().unwrap(); + let file = File::open(entry.path()); if let Err(e) = file { error!(err = %e, "opening file"); @@ -434,6 +448,10 @@ 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 _guard_ino = lock_ino.read().unwrap(); + let file = File::open(&self.1.join(ino.to_string())); if let Err(e) = file { error!(err = %e, "opening file"); @@ -562,9 +580,10 @@ pub struct EncryptedFs { opened_files_for_read: RwLock>>, opened_files_for_write: RwLock>, // used for rw ops of actual serialization - serialize_inode_locks: Mutex>>, + serialize_inode_locks: Arc>>>, // used for the update op serialize_update_inode_locks: Mutex>>, + serialize_dir_entries_locks: Arc>>>, read_write_inode_locks: Mutex>>, key: ExpireValue, FsError, KeyProvider>, } @@ -580,6 +599,7 @@ impl EncryptedFs { let path = PathBuf::from(&data_dir); ensure_structure_created(&path.clone()).await?; + check_password(&path, &password, &cipher)?; let key_provider = KeyProvider { path: path.join(SECURITY_DIR).join(KEY_ENC_FILENAME), @@ -597,11 +617,13 @@ impl EncryptedFs { 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: 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())), // todo: take duration from param key: ExpireValue::new(key_provider, Duration::from_secs(10 * 60)).await, }; + let _ = fs.ensure_root_exists().await; Ok(fs) @@ -638,7 +660,7 @@ impl EncryptedFs { attr.ino = self.generate_next_inode(); // write inode - self.write_inode(&attr).await?; + self.write_inode_to_storage(&attr, &*self.key.get().await?).await?; // create in contents directory match attr.kind { @@ -660,12 +682,12 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::from_str("$.").unwrap(), kind: FileType::Directory, - }).await?; + }, &*self.key.get().await?).await?; self.insert_directory_entry(attr.ino, DirectoryEntry { ino: parent, name: SecretString::from_str("$..").unwrap(), kind: FileType::Directory, - }).await?; + }, &*self.key.get().await?).await?; } } @@ -674,11 +696,10 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::new(name.expose_secret().to_owned()), kind: attr.kind, - }).await?; - let mut parent_attr = self.get_inode(parent).await?; - parent_attr.mtime = SystemTime::now(); - parent_attr.ctime = SystemTime::now(); - self.write_inode(&parent_attr).await?; + }, &*self.key.get().await?).await?; + self.update_inode(parent, SetFileAttr::default() + .with_mtime(SystemTime::now()) + .with_ctime(SystemTime::now())).await?; let handle = if attr.kind == FileType::RegularFile { if read || write { @@ -747,18 +768,23 @@ impl EncryptedFs { } let ino_str = attr.ino.to_string(); + // remove inode file - tokio::fs::remove_file(self.data_dir.join(INODES_DIR).join(&ino_str)).await?; + { + let map = self.serialize_inode_locks.write().unwrap(); + let lock = map.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))?; + } + // remove contents directory tokio::fs::remove_dir_all(self.data_dir.join(CONTENTS_DIR).join(&ino_str)).await?; // remove from parent directory - let name = crypto_util::normalize_end_encrypt_file_name(name, &self.cipher, &*self.key.get().await?); - tokio::fs::remove_file(self.data_dir.join(CONTENTS_DIR).join(parent.to_string()).join(name)).await?; + self.remove_directory_entry(parent, name).await?; - let mut parent_attr = self.get_inode(parent).await?; - parent_attr.mtime = SystemTime::now(); - parent_attr.ctime = SystemTime::now(); - self.write_inode(&parent_attr).await?; + self.update_inode(parent, SetFileAttr::default() + .with_mtime(SystemTime::now()) + .with_ctime(SystemTime::now())).await?; Ok(()) } @@ -778,17 +804,22 @@ impl EncryptedFs { let ino_str = attr.ino.to_string(); // remove inode file - tokio::fs::remove_file(self.data_dir.join(INODES_DIR).join(&ino_str)).await?; + { + let map = self.serialize_inode_locks.write().unwrap(); + let lock = map.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))?; + } + // remove contents file tokio::fs::remove_file(self.data_dir.join(CONTENTS_DIR).join(&ino_str)).await?; // remove from parent directory - let name = crypto_util::normalize_end_encrypt_file_name(name, &self.cipher, &*self.key.get().await?); - tokio::fs::remove_file(self.data_dir.join(CONTENTS_DIR).join(parent.to_string()).join(name)).await?; + // remove from parent contents + self.remove_directory_entry(parent, name).await?; - let mut parent_attr = self.get_inode(parent).await?; - parent_attr.mtime = SystemTime::now(); - parent_attr.ctime = SystemTime::now(); - self.write_inode(&parent_attr).await?; + self.update_inode(parent, SetFileAttr::default() + .with_mtime(SystemTime::now()) + .with_ctime(SystemTime::now())).await?; Ok(()) } @@ -814,7 +845,7 @@ impl EncryptedFs { } let iter = fs::read_dir(contents_dir)?; - Ok(DirectoryEntryIterator(iter.into_iter(), self.cipher.clone(), SecretVec::new((*self.key.get().await?).expose_secret().to_vec()))) + Ok(DirectoryEntryIterator(iter.into_iter(), self.cipher.clone(), 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. @@ -825,22 +856,23 @@ impl EncryptedFs { } let iter = fs::read_dir(contents_dir)?; - Ok(DirectoryEntryPlusIterator(iter.into_iter(), self.data_dir.join(INODES_DIR), self.cipher.clone(), SecretVec::new((*self.key.get().await?).expose_secret().to_vec()))) + Ok(DirectoryEntryPlusIterator(iter.into_iter(), self.data_dir.join(INODES_DIR), self.cipher.clone(), self.key.get().await?, + self.serialize_dir_entries_locks.clone(), self.serialize_inode_locks.clone())) } - async fn get_inode_from_storage(&self, ino: u64) -> FsResult { - let mut map_guard = self.serialize_inode_locks.lock().await; - let lock = map_guard.get_or_insert_with(ino, || Mutex::new(false)); - let _guard = lock.lock().await; + async fn get_inode_from_storage(&self, ino: u64, key: &SecretVec) -> FsResult { + let map_guard = self.serialize_inode_locks.read().unwrap(); + let lock = map_guard.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()); let file = OpenOptions::new().read(true).write(true).open(path).map_err(|_| { FsError::InodeNotFound })?; - Ok(bincode::deserialize_from::, FileAttr>(crypto_util::create_decryptor(file, &self.cipher, &*self.key.get().await?))?) + Ok(bincode::deserialize_from::, FileAttr>(crypto_util::create_decryptor(file, &self.cipher, key))?) } pub async fn get_inode(&self, ino: u64) -> FsResult { debug!("get inode"); - let mut attr = self.get_inode_from_storage(ino).await?; + let mut attr = self.get_inode_from_storage(ino, &*self.key.get().await?).await?; // merge time info and size with any open read handles let open_reads = { self.opened_files_for_read.read().await.contains_key(&ino) }; @@ -872,21 +904,21 @@ impl EncryptedFs { } pub async fn update_inode(&self, ino: u64, set_attr: SetFileAttr) -> FsResult<()> { - let mut map_serialize_update = self.serialize_update_inode_locks.lock().await; + 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 _guard_serialize_update = lock_serialize_update.lock().await; let mut attr = self.get_inode(ino).await?; merge_attr(&mut attr, set_attr); - self.write_inode(&attr).await?; + self.write_inode_to_storage(&attr, &*self.key.get().await?).await?; Ok(()) } - async fn write_inode(&self, attr: &FileAttr) -> Result<(), FsError> { - let mut map = self.serialize_inode_locks.lock().await; - let lock = map.get_or_insert_with(attr.ino, || Mutex::new(false)); - let _guard = lock.lock().await; + async fn write_inode_to_storage(&self, attr: &FileAttr, key: &SecretVec) -> 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 _guard = lock.write().unwrap(); let path = self.data_dir.join(INODES_DIR).join(attr.ino.to_string()); let file = OpenOptions::new() @@ -895,7 +927,7 @@ impl EncryptedFs { .create(true) .truncate(true) .open(&path)?; - bincode::serialize_into(crypto_util::create_encryptor(file, &self.cipher, &*self.key.get().await?), &attr)?; + bincode::serialize_into(crypto_util::create_encryptor(file, &self.cipher, key), &attr)?; Ok(()) } @@ -910,7 +942,7 @@ impl EncryptedFs { debug!("read"); // lock for reading let lock = { - let mut map_guard = self.read_write_inode_locks.lock().await; + let map_guard = self.read_write_inode_locks.lock().await; map_guard.get_or_insert_with(ino, || Mutex::new(false)) }; let _guard = lock.lock().await; @@ -1056,9 +1088,9 @@ impl EncryptedFs { if !valid_fh { return Err(FsError::InvalidFileHandle); } - debug!(serialize_inode_locks.size = self.serialize_inode_locks.lock().await.len()); - debug!(read_write_inode_locks.size = self.read_write_inode_locks.lock().await.len()); + debug!(serialize_inode_locks.size = self.serialize_inode_locks.read().unwrap().len()); debug!(serialize_update_inode_locks.size = self.serialize_update_inode_locks.lock().await.len()); + debug!(read_write_inode_locks.size = self.read_write_inode_locks.lock().await.len()); Ok(()) } @@ -1084,7 +1116,7 @@ impl EncryptedFs { debug!("write_all"); // lock for writing let lock = { - let mut map_guard = self.read_write_inode_locks.lock().await; + let map_guard = self.read_write_inode_locks.lock().await; map_guard.get_or_insert_with(ino, || Mutex::new(false)) }; let _guard = lock.lock().await; @@ -1168,7 +1200,7 @@ impl EncryptedFs { // update metadata let (ino, attr) = { let guard = self.write_handles.read().await; - let mut ctx = guard.get(&handle).unwrap().lock().await; + let ctx = guard.get(&handle).unwrap().lock().await; (ctx.ino, ctx.attr.clone()) }; self.update_inode(ino, attr.into()).await?; @@ -1326,7 +1358,7 @@ impl EncryptedFs { return Err(FsError::InvalidInodeType); } - let mut map_guard = self.read_write_inode_locks.lock().await; + let map_guard = self.read_write_inode_locks.lock().await; let mut handle = 0_u64; if read { let lock = map_guard.get_or_insert_with(ino, || Mutex::new(false)); @@ -1350,7 +1382,7 @@ impl EncryptedFs { pub async fn truncate(&self, ino: u64, size: u64) -> FsResult<()> { let lock = { - let mut map_guard = self.read_write_inode_locks.lock().await; + let map_guard = self.read_write_inode_locks.lock().await; map_guard.get_or_insert_with(ino, || Mutex::new(false)) }; let _guard = lock.lock().await; @@ -1556,7 +1588,7 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::new(new_name.expose_secret().to_owned()), kind: attr.kind, - }).await?; + }, &*self.key.get().await?).await?; let mut parent_attr = self.get_inode(parent).await?; parent_attr.mtime = SystemTime::now(); @@ -1574,7 +1606,7 @@ impl EncryptedFs { ino: new_parent, name: SecretString::from_str("$..").unwrap(), kind: FileType::Directory, - }).await?; + }, &*self.key.get().await?).await?; } Ok(()) @@ -1682,7 +1714,7 @@ impl EncryptedFs { let path = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); let file = OpenOptions::new().read(true).write(true).open(path)?; let decryptor = crypto_util::create_decryptor(file, &self.cipher, &*self.key.get().await?); - let attr = self.get_inode_from_storage(ino).await?; + let attr = self.get_inode_from_storage(ino, &*self.key.get().await?).await?; match op { ReadHandleContextOperation::Create { ino, lock } => { let attr: TimeAndSizeFileAttr = attr.into(); @@ -1734,7 +1766,7 @@ impl EncryptedFs { existing.encryptor.replace(encryptor); existing.path = path.clone(); if reset_size { - let attr = self.get_inode_from_storage(ino).await?; + let attr = self.get_inode_from_storage(ino, &*self.key.get().await?).await?; existing.attr.size = attr.size; debug!("resetting size to {}", attr.size.to_formatted_string(&Locale::en)); } @@ -1746,30 +1778,21 @@ impl EncryptedFs { async fn ensure_root_exists(&self) -> FsResult<()> { if !self.node_exists(ROOT_INODE) { - let mut attr = FileAttr { - ino: ROOT_INODE, - size: 0, - blocks: 0, - atime: SystemTime::now(), - mtime: SystemTime::now(), - ctime: SystemTime::now(), - crtime: SystemTime::now(), + let mut attr: FileAttr = CreateFileAttr { kind: FileType::Directory, perm: 0o755, - nlink: 2, uid: 0, gid: 0, rdev: 0, - blksize: 0, flags: 0, - }; - #[cfg(target_os = "linux")] { - let metadata = tokio::fs::metadata(self.data_dir.clone()).await?; - attr.uid = metadata.uid(); - attr.gid = metadata.gid(); + }.into(); + attr.ino = ROOT_INODE; + #[cfg(target_os = "linux")] unsafe { + attr.uid = libc::getuid(); + attr.gid = libc::getgid(); } - self.write_inode(&attr).await?; + self.write_inode_to_storage(&attr, &*self.key.get().await?).await?; // create the directory tokio::fs::create_dir(self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string())).await?; @@ -1779,25 +1802,30 @@ impl EncryptedFs { ino: attr.ino, name: SecretString::from_str("$.").unwrap(), kind: FileType::Directory, - }).await?; + }, &*self.key.get().await?).await?; } Ok(()) } - async fn insert_directory_entry(&self, parent: u64, entry: DirectoryEntry) -> FsResult<()> { + async fn insert_directory_entry(&self, parent: u64, entry: DirectoryEntry, key: &SecretVec) -> FsResult<()> { let parent_path = self.data_dir.join(CONTENTS_DIR).join(parent.to_string()); - // remove path separators from name let name = crypto_util::normalize_end_encrypt_file_name(&entry.name, &self.cipher, &*self.key.get().await?); + let file_path = parent_path.join(name); + + let map = self.serialize_dir_entries_locks.write().unwrap(); + let lock = map.get_or_insert_with(file_path.to_str().unwrap().to_string(), || std::sync::RwLock::new(false)); + let _guard = lock.write().unwrap(); + let file = OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(&parent_path.join(name))?; + .open(&file_path)?; // write inode and file type let entry = (entry.ino, entry.kind); - bincode::serialize_into(crypto_util::create_encryptor(file, &self.cipher, &*self.key.get().await?), &entry)?; + bincode::serialize_into(crypto_util::create_encryptor(file, &self.cipher, key), &entry)?; Ok(()) } @@ -1805,7 +1833,13 @@ 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_util::normalize_end_encrypt_file_name(name, &self.cipher, &*self.key.get().await?); - tokio::fs::remove_file(parent_path.join(name)).await?; + let file_path = parent_path.join(name); + + let map = self.serialize_dir_entries_locks.write().unwrap(); + let lock = map.get_or_insert_with(file_path.to_str().unwrap().to_string(), || std::sync::RwLock::new(false)); + let _guard = lock.write().unwrap(); + + fs::remove_file(file_path)?; Ok(()) } @@ -1930,3 +1964,17 @@ fn merge_attr(attr: &mut FileAttr, set_attr: SetFileAttr) { attr.flags = flags; } } + +fn check_password(data_dir: &PathBuf, password: &SecretString, cipher: &Cipher) -> FsResult<()> { + let salt = crypto_util::hash_secret(password); + let initial_key = crypto_util::derive_key(password, cipher, salt)?; + let enc_file = data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME); + let decryptor = crypto_util::create_decryptor(File::open(enc_file.clone())?, cipher, &initial_key); + let key_store: KeyStore = bincode::deserialize_from(decryptor).map_err(|_| FsError::InvalidPassword)?; + // check hash + if key_store.hash != crypto_util::hash(key_store.key.expose_secret()) { + return Err(FsError::InvalidPassword); + } + + Ok(()) +} diff --git a/src/encryptedfs_fuse3.rs b/src/encryptedfs_fuse3.rs index 1a87c8b2..c71c9561 100644 --- a/src/encryptedfs_fuse3.rs +++ b/src/encryptedfs_fuse3.rs @@ -15,7 +15,7 @@ use fuse3::raw::prelude::{DirectoryEntry, DirectoryEntryPlus, ReplyAttr, ReplyCo use fuse3::raw::{Filesystem, Request}; use futures_util::stream; use futures_util::stream::Iter; -use libc::{EACCES, EBADF, EIO, ENOENT, ENOTDIR, ENOTEMPTY, EPERM}; +use libc::{EACCES, EBADF, EEXIST, EIO, ENAMETOOLONG, ENOENT, ENOTDIR, ENOTEMPTY, EPERM}; use secrecy::{ExposeSecret, SecretString}; use tracing::{debug, error, instrument, trace, warn}; @@ -185,16 +185,15 @@ impl EncryptedFsFuse3 { attr.uid = req.uid; attr.gid = creation_gid(&parent_attr, req.gid); - match self.get_fs().create_nod(parent, &SecretString::from_str(name.to_str().unwrap()).unwrap(), attr, read, write).await { - Ok((fh, attr)) => Ok((fh, attr)), - Err(err) => { - error!(err = %err); - match err { - FsError::AlreadyExists => { Err(libc::EEXIST) } - _ => { return Err(ENOENT); } - } + let (fh, attr) = self.get_fs().create_nod(parent, &SecretString::from_str(name.to_str().unwrap()).unwrap(), attr, read, write).await.map_err(|err| { + error!(err = %err); + match err { + FsError::AlreadyExists => EEXIST, + FsError::Io { source } => if source.to_string().to_lowercase().contains("too long") { ENAMETOOLONG } else { EIO }, + _ => EIO, } - } + })?; + Ok((fh, attr)) } } @@ -251,7 +250,7 @@ impl Filesystem for EncryptedFsFuse3 { if name.len() > MAX_NAME_LENGTH as usize { warn!(name = %name.to_str().unwrap(), "name too long"); - return Err(libc::ENAMETOOLONG.into()); + return Err(ENAMETOOLONG.into()); } match self.get_fs().get_inode(parent).await { @@ -1068,26 +1067,20 @@ impl Filesystem for EncryptedFsFuse3 { } }; - return match self.create_nod(parent, mode, &req, name, read, write).await { - Ok((handle, attr)) => { - debug!(handle, "created handle"); - // TODO: implement flags - Ok(ReplyCreated { - ttl: TTL, - attr: attr.into(), - generation: 0, - fh: handle, - flags: 0, - }) - } - Err(err) => { - error!(err = %err); - Err(ENOENT.into()) - } - }; + let (handle, attr) = self.create_nod(parent, mode, &req, name, read, write).await.map_err(|err| { + error!(err = %err); + Errno::from(ENOENT) + })?; + debug!(handle, "created handle"); + Ok(ReplyCreated { + ttl: TTL, + attr: attr.into(), + generation: 0, + fh: handle, + flags: 0, + }) } - type DirEntryPlusStream<'a> = Iter> where Self: 'a; #[instrument(skip(self), err(level = Level::INFO))]