From 8d96e38c04610eb40344825b48e71ee643b70474 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 24 Apr 2024 03:42:49 +0300 Subject: [PATCH] implemented issue #1 allow multiple reads in parallel but only exclusive write --- Cargo.lock | 9 +- Cargo.toml | 3 +- examples/aes_stream_with_writer_seek.rs | 20 +-- examples/cryptostream.rs | 4 +- examples/pbkdf2.rs | 2 +- src/encryptedfs.rs | 158 +++++++++++++++--------- src/encryptedfs/encryptedfs_test.rs | 91 +++++++------- src/main.rs | 3 +- 8 files changed, 170 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcc36671..2ac698ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,7 +293,7 @@ checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encryptedfs" -version = "0.1.25" +version = "0.1.26" dependencies = [ "base64", "bincode", @@ -318,6 +318,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "weak-table", ] [[package]] @@ -1133,6 +1134,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "weak-table" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" + [[package]] name = "which" version = "6.0.1" diff --git a/Cargo.toml b/Cargo.toml index 3d74c47f..7513db67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "encryptedfs" description = "An encrypted file system that mounts with FUSE on Linux. It can be used to create encrypted directories." -version = "0.1.25" +version = "0.1.26" edition = "2021" license = "Apache-2.0" authors = ["Radu Marias "] @@ -37,6 +37,7 @@ strum = "0.26.2" strum_macros = "0.26.2" rpassword = "7.3.1" cryptostream = "0.3.2" +weak-table = "0.3.2" [package.metadata.aur] depends = ["fuse3"] diff --git a/examples/aes_stream_with_writer_seek.rs b/examples/aes_stream_with_writer_seek.rs index df2d30a5..4db8e68c 100644 --- a/examples/aes_stream_with_writer_seek.rs +++ b/examples/aes_stream_with_writer_seek.rs @@ -213,7 +213,7 @@ impl AesWriter { /// /// [be]: https://docs.rs/rust-crypto/0.2.36/crypto/symmetriccipher/trait.BlockEncryptor.html pub fn new(mut writer: W, mut reader: R, enc: E, first_write: bool) -> Result> { - let mut iv = vec![0u8; enc.block_size()]; + let mut iv = vec![0_u8; enc.block_size()]; if first_write { OsRng::new()?.fill_bytes(&mut iv); writer.write_all(&iv)?; @@ -235,7 +235,7 @@ impl AesWriter { self.reader.as_mut().unwrap().seek(SeekFrom::Start(self.block_size as u64))?; self.reader.as_mut().unwrap().seek(SeekFrom::Current((writer_size - self.block_size as u64) as i64))?; - let mut iv = vec![0u8; self.block_size]; + let mut iv = vec![0_u8; self.block_size]; self.reader.as_mut().unwrap().read_exact(&mut iv)?; self.enc.reset(&iv); @@ -249,17 +249,17 @@ impl AesWriter { // reset CbcDecryptor self.writer.as_mut().unwrap().seek(SeekFrom::Start((block_num - 1) * self.block_size as u64))?; - let mut iv = vec![0u8; self.block_size]; + let mut iv = vec![0_u8; self.block_size]; self.reader.as_mut().unwrap().seek(SeekFrom::Start((block_num - 1) * self.block_size as u64))?; self.reader.as_mut().unwrap().read_exact(&mut iv)?; self.writer.as_mut().unwrap().seek(SeekFrom::Current(iv.len() as i64))?; self.enc.reset(&iv); // skip remaining - let mut skip = vec![0u8; block_offset as usize]; + let mut skip = vec![0_u8; block_offset as usize]; self.reader.as_mut().unwrap().read_exact(&mut skip)?; let mut read_buf = RefReadBuffer::new(skip.as_slice()); - let mut out = [0u8; BUFFER_SIZE]; + let mut out = [0_u8; BUFFER_SIZE]; let mut write_buf = RefWriteBuffer::new(&mut out); loop { @@ -287,7 +287,7 @@ impl AesWriter { /// finished and padding added. fn encrypt_write(&mut self, buf: &[u8], eof: bool) -> Result { let mut read_buf = RefReadBuffer::new(buf); - let mut out = [0u8; BUFFER_SIZE]; + let mut out = [0_u8; BUFFER_SIZE]; let mut write_buf = RefWriteBuffer::new(&mut out); loop { let res = self.enc.encrypt(&mut read_buf, &mut write_buf, eof) @@ -471,7 +471,7 @@ impl AesReader { /// /// [bd]: https://docs.rs/rust-crypto/0.2.36/crypto/symmetriccipher/trait.BlockDecryptor.html pub fn new(mut reader: R, dec: D) -> Result> { - let mut iv = vec![0u8; dec.block_size()]; + let mut iv = vec![0_u8; dec.block_size()]; reader.read_exact(&mut iv)?; Ok(AesReader { reader, @@ -484,7 +484,7 @@ impl AesReader { /// Reads at max BUFFER_SIZE bytes, handles potential eof and returns the buffer as Vec fn fill_buf(&mut self) -> Result> { - let mut eof_buffer = vec![0u8; BUFFER_SIZE]; + let mut eof_buffer = vec![0_u8; BUFFER_SIZE]; let read = self.reader.read(&mut eof_buffer)?; self.eof = read == 0; eof_buffer.truncate(read); @@ -568,12 +568,12 @@ impl AesReader { let block_offset = offset % self.block_size as u64; // reset CbcDecryptor self.reader.seek(SeekFrom::Start((block_num - 1) * self.block_size as u64))?; - let mut iv = vec![0u8; self.block_size]; + let mut iv = vec![0_u8; self.block_size]; self.reader.read_exact(&mut iv)?; self.dec.reset(&iv); self.buffer = Vec::new(); self.eof = false; - let mut skip = vec![0u8; block_offset as usize]; + let mut skip = vec![0_u8; block_offset as usize]; self.read_exact(&mut skip)?; // subtract IV Ok(offset - 16) diff --git a/examples/cryptostream.rs b/examples/cryptostream.rs index 775f0847..3a0b0205 100644 --- a/examples/cryptostream.rs +++ b/examples/cryptostream.rs @@ -24,7 +24,7 @@ fn main() { let mut decryptor = read::Decryptor::new(src.as_slice(), Cipher::aes_128_cbc(), &key, &iv).unwrap(); - let mut decrypted = [0u8; 1024]; // a buffer to decrypt into + let mut decrypted = [0_u8; 1024]; // a buffer to decrypt into let mut bytes_decrypted = 0; loop { @@ -56,7 +56,7 @@ fn main() { let mut decryptor = read::Decryptor::new(file, Cipher::aes_128_cbc(), &key, &iv).unwrap(); - let mut decrypted = [0u8; 1024]; // a buffer to decrypt into + let mut decrypted = [0_u8; 1024]; // a buffer to decrypt into let mut bytes_decrypted = 0; loop { // Just read from the `Decryptor` as if it were any other `Read` impl, diff --git a/examples/pbkdf2.rs b/examples/pbkdf2.rs index f74d239d..dbfcafad 100644 --- a/examples/pbkdf2.rs +++ b/examples/pbkdf2.rs @@ -8,7 +8,7 @@ fn main() { let n = 600_000; // Expected value of generated key - // let mut key1 = [0u8; 20]; + // let mut key1 = [0_u8; 20]; // pbkdf2_hmac::(password, salt, n, &mut key1); // println!("{:?}", key1); diff --git a/src/encryptedfs.rs b/src/encryptedfs.rs index 0697ec91..c0bf344c 100644 --- a/src/encryptedfs.rs +++ b/src/encryptedfs.rs @@ -5,17 +5,20 @@ use std::fmt::Debug; use std::fs::{File, OpenOptions, ReadDir}; use std::io::{Read, Write}; use std::os::unix::fs::MetadataExt; -use std::path::{PathBuf}; +use std::path::PathBuf; +use std::sync::{Arc, Mutex, RwLock, Weak}; use std::sync::atomic::AtomicU64; use std::time::SystemTime; use cryptostream::{read, write}; +use cryptostream::read::Decryptor; use openssl::error::ErrorStack; use rand::{OsRng, Rng}; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter, EnumString}; use thiserror::Error; -use tracing::error; +use tracing::{error, instrument}; +use weak_table::WeakValueHashMap; #[cfg(test)] mod encryptedfs_test; @@ -224,16 +227,17 @@ pub struct DirectoryEntryPlusIterator(ReadDir, PathBuf, Cipher, Vec); impl Iterator for DirectoryEntryPlusIterator { type Item = FsResult; + #[instrument(name = "DirectoryEntryPlusIterator::next", skip(self))] fn next(&mut self) -> Option { let entry = self.0.next()?; if let Err(e) = entry { - error!("error reading directory entry: {e}"); + error!(err = %e, "reading directory entry"); return Some(Err(e.into())); } let entry = entry.unwrap(); let file = File::open(entry.path()); if let Err(e) = file { - error!("error opening file: {e}"); + error!(err = %e, "opening file"); return Some(Err(e.into())); } let file = file.unwrap(); @@ -247,20 +251,20 @@ impl Iterator for DirectoryEntryPlusIterator { } let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto_util::create_decryptor(file, &self.2, &self.3)); if let Err(e) = res { - error!("error deserializing directory entry: {e}"); + error!(err = %e, "deserializing directory entry"); return Some(Err(e.into())); } let (ino, kind): (u64, FileType) = res.unwrap(); let file = File::open(&self.1.join(ino.to_string())); if let Err(e) = file { - error!("error opening file: {e}"); + error!(err = %e, "opening file"); return Some(Err(e.into())); } let file = file.unwrap(); let attr = bincode::deserialize_from(crypto_util::create_decryptor(file, &self.2, &self.3)); if let Err(e) = attr { - error!("error deserializing file attr: {e}"); + error!(err = %e, "deserializing file attr"); return Some(Err(e.into())); } let attr = attr.unwrap(); @@ -276,12 +280,13 @@ impl Iterator for DirectoryEntryPlusIterator { /// Encrypted FS that stores encrypted files in a dedicated directory with a specific structure based on `inode`. pub struct EncryptedFs { pub(crate) data_dir: PathBuf, - write_handles: HashMap)>, - read_handles: HashMap)>, + write_handles: HashMap, Arc>)>, + read_handles: HashMap, Arc>)>, current_handle: AtomicU64, cipher: Cipher, key: Vec, opened_files_for_write: HashMap, + inode_lock: Mutex>>>, } impl EncryptedFs { @@ -298,6 +303,7 @@ impl EncryptedFs { cipher: cipher.clone(), key: read_or_create_key(path.join(SECURITY_DIR).join(KEY_ENC_FILENAME), password, &cipher, derive_key_hash_rounds)?, opened_files_for_write: HashMap::new(), + inode_lock: Mutex::new(WeakValueHashMap::new()), }; let _ = fs.ensure_root_exists(); fs.check_password()?; @@ -522,8 +528,8 @@ impl EncryptedFs { let mut attr: FileAttr = bincode::deserialize_from(crypto_util::create_decryptor(file, &self.cipher, &self.key))?; if self.opened_files_for_write.contains_key(&ino) { // merge time info and size with any open write handles - if let Some((attr_handle, _, _, _)) = self.write_handles.get(&self.opened_files_for_write.get(&ino).unwrap()) { - merge_attr_time_and_time_obj(&mut attr, &attr_handle); + if let Some((attr_handle, _, _, _, _)) = self.write_handles.get(&self.opened_files_for_write.get(&ino).unwrap()) { + merge_attr_time_and_set_size_obj(&mut attr, &attr_handle); } else { self.opened_files_for_write.remove(&ino); } @@ -559,7 +565,7 @@ impl EncryptedFs { if !self.read_handles.contains_key(&handle) { return Err(FsError::InvalidFileHandle); } - let (attr, position, _) = self.read_handles.get(&handle).unwrap(); + let (attr, position, _, lock) = self.read_handles.get(&handle).unwrap(); if attr.ino != ino { return Err(FsError::InvalidFileHandle); } @@ -575,15 +581,19 @@ impl EncryptedFs { return Ok(0); } + // lock for reading + let global_lock = self.inode_lock.lock().unwrap().get(&ino).unwrap(); + let _guard = global_lock.read().unwrap(); + if *position != offset { // in order to seek we need to read the bytes from current position until the offset if *position > offset { // if we need an offset before the current position, we can't seek back, we need // to read from the beginning until the desired offset - self.create_read_handle(ino, handle)?; + self.create_read_handle(ino, handle, lock.clone())?; } if offset > 0 { - let (_, position, decryptor) = + let (_, position, decryptor, _) = self.read_handles.get_mut(&handle).unwrap(); let mut buffer: [u8; 4096] = [0; 4096]; loop { @@ -602,7 +612,7 @@ impl EncryptedFs { } } } - let (attr, position, decryptor) = + let (attr, position, decryptor, _) = self.read_handles.get_mut(&handle).unwrap(); if offset + buf.len() as u64 > attr.size { buf = &mut buf[..(attr.size - offset) as usize]; @@ -610,7 +620,7 @@ impl EncryptedFs { decryptor.read_exact(&mut buf)?; *position += buf.len() as u64; - attr.atime = std::time::SystemTime::now(); + attr.atime = SystemTime::now(); Ok(buf.len()) } @@ -621,20 +631,20 @@ impl EncryptedFs { return Ok(()); } let mut valid_fh = false; - if let Some((attr, _, decryptor)) = self.read_handles.remove(&handle) { + if let Some((attr, _, decryptor, _)) = self.read_handles.remove(&handle) { // write attr only here to avoid serializing it multiple times while reading // merge time fields with existing data because it might got change while we kept the handle let mut attr_0 = self.get_inode(attr.ino)?; - merge_attr_time_and_time_obj(&mut attr_0, &attr); + merge_attr_time_and_set_size_obj(&mut attr_0, &attr); self.write_inode(&attr_0)?; decryptor.finish(); valid_fh = true; } - if let Some((attr, path, _, encryptor)) = self.write_handles.remove(&handle) { + if let Some((attr, path, _, encryptor, _)) = self.write_handles.remove(&handle) { // write attr only here to avoid serializing it multiple times while writing // merge time fields with existing data because it might got change while we kept the handle let mut attr_0 = self.get_inode(attr.ino)?; - merge_attr_time_and_time_obj(&mut attr_0, &attr); + merge_attr_time_and_set_size_obj(&mut attr_0, &attr); self.write_inode(&attr_0)?; self.write_inode(&attr_0)?; encryptor.finish()?; @@ -684,7 +694,7 @@ impl EncryptedFs { if self.is_dir(ino) { return Err(FsError::InvalidInodeType); } - let (attr, _, position, _) = + let (attr, _, position, _, _) = self.write_handles.get_mut(&handle).unwrap(); if attr.ino != ino { return Err(FsError::InvalidFileHandle); @@ -694,13 +704,19 @@ impl EncryptedFs { return Ok(()); } + // write lock to avoid writing from multiple threads + let global_lock = self.inode_lock.lock().unwrap().get(&ino).unwrap(); + let _guard = global_lock.write().unwrap(); + + let mut decryptor: Option> = None; + if *position != offset { // in order to seek we need to recreate all stream from the beginning until the desired position of file size // for that we create a new encryptor into a tmp file reading from original file and writing to tmp one // when we release the handle we will move this tmp file to the actual file // remove handle data because we will replace it with the tmp one - let (attr, path, _, encryptor) = + let (attr, path, _, encryptor, lock) = self.write_handles.remove(&handle).unwrap(); // finish the current writer so we flush all data to the file @@ -718,7 +734,7 @@ impl EncryptedFs { let tmp_path = self.data_dir.join(CONTENTS_DIR).join(tmp_path_str); let tmp_file = OpenOptions::new().read(true).write(true).create(true).open(tmp_path.clone())?; - let mut decryptor = crypto_util::create_decryptor(in_file, &self.cipher, &self.key); + let mut decryptor2 = crypto_util::create_decryptor(in_file, &self.cipher, &self.key); let mut encryptor = crypto_util::create_encryptor(tmp_file, &self.cipher, &self.key); let mut buffer: [u8; 4096] = [0; 4096]; @@ -732,8 +748,10 @@ impl EncryptedFs { } else { buffer.len() }; - if read_len > 0 { - decryptor.read_exact(&mut buffer[..read_len])?; + if read_len == 0 { + break; + } else if read_len > 0 { + decryptor2.read_exact(&mut buffer[..read_len])?; encryptor.write_all(&buffer[..read_len])?; pos_read += read_len as u64; position += read_len as u64; @@ -743,9 +761,11 @@ impl EncryptedFs { } } } - self.replace_handle_data(handle, attr, tmp_path, position, encryptor); + self.replace_handle_data(handle, attr, tmp_path, position, encryptor, lock.clone()); + + decryptor = Some(decryptor2); } - let (attr, _, position, encryptor) = + let (attr, _, position, encryptor, _) = self.write_handles.get_mut(&handle).unwrap(); // if offset is after current position (max file size) we fill up with zeros until offset @@ -765,22 +785,37 @@ impl EncryptedFs { encryptor.write_all(buf)?; *position += buf.len() as u64; + // todo: maybe move this to when we release the handle so if we get several writes on block after another before file end + // todo: we don't keep recreating the writer + // todo: but we need to be careful because we need to update the size of the file only when we release the handle // if position is before file end we copy the rest of the file from position to the end if *position < attr.size { - let mut buffer: [u8; 4096] = [0; 4096]; - let mut decryptor = crypto_util::create_decryptor(OpenOptions::new().read(true).open(self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()))?, - &self.cipher, &self.key); - // move read position to the desired position - loop { - let mut read_pos = 0u64; - let len = min(4096, *position - read_pos) as usize; - decryptor.read_exact(&mut buffer[..len])?; - read_pos += len as u64; - if read_pos == *position { - break; + let mut decryptor = if let Some(mut decryptor) = decryptor { + // reuse the existing decryptor + // skip written bytes + decryptor.read_exact(vec![0_u8; buf.len()].as_mut_slice())?; + decryptor + } else { + // create a new decryptor by reading from the beginning of the file + let mut decryptor = crypto_util::create_decryptor(OpenOptions::new().read(true).open(self.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()))?, + &self.cipher, &self.key); + // move read position to the desired position + let mut buffer: [u8; 4096] = [0; 4096]; + loop { + let mut read_pos = 0u64; + let len = min(4096, *position - read_pos) as usize; + decryptor.read_exact(&mut buffer[..len])?; + read_pos += len as u64; + if read_pos == *position { + break; + } } - } + + decryptor + }; + // copy the rest of the file + let mut buffer: [u8; 4096] = [0; 4096]; loop { let len = min(4096, attr.size - *position) as usize; decryptor.read_exact(&mut buffer[..len])?; @@ -795,8 +830,8 @@ impl EncryptedFs { let size = *position; attr.size = size; - attr.mtime = std::time::SystemTime::now(); - attr.ctime = std::time::SystemTime::now(); + attr.mtime = SystemTime::now(); + attr.ctime = SystemTime::now(); Ok(()) } @@ -810,7 +845,7 @@ impl EncryptedFs { if !self.write_handles.contains_key(&handle) { return Err(FsError::InvalidFileHandle); } - if let Some((_, _, _, encryptor)) = self.write_handles.get_mut(&handle) { + if let Some((_, _, _, encryptor, _)) = self.write_handles.get_mut(&handle) { encryptor.flush()?; } Ok(()) @@ -838,17 +873,22 @@ impl EncryptedFs { return Err(FsError::InvalidInodeType); } - let mut handle = 0u64; + let lock; + { + let mut map_guard = self.inode_lock.lock().unwrap(); + lock = map_guard.entry(ino).or_insert_with(|| Arc::new(RwLock::new(0))); + } + let mut handle = 0_u64; if read { handle = self.allocate_next_handle(); - self.create_read_handle(ino, handle)?; + self.create_read_handle(ino, handle, lock.clone())?; } if write { if self.opened_files_for_write.contains_key(&ino) { return Err(FsError::AlreadyOpenForWrite); } handle = self.allocate_next_handle(); - self.create_write_handle(ino, handle)?; + self.create_write_handle(ino, handle, lock)?; } Ok(handle) } @@ -1025,8 +1065,8 @@ impl EncryptedFs { fn recreate_read_handles(&mut self) { let keys: Vec = self.read_handles.keys().cloned().collect(); for key in keys { - let (attr, _, _) = self.read_handles.remove(&key).unwrap(); - self.create_read_handle(attr.ino, key).unwrap(); + let (attr, _, _, lock) = self.read_handles.remove(&key).unwrap(); + self.create_read_handle(attr.ino, key, lock).unwrap(); } } @@ -1038,31 +1078,31 @@ impl EncryptedFs { } } - fn create_read_handle(&mut self, ino: u64, handle: u64) -> FsResult { + fn create_read_handle(&mut self, ino: u64, handle: u64, lock: Arc>) -> FsResult { 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); let attr = self.get_inode(ino)?; // save attr also to avoid loading it multiple times while reading - self.read_handles.insert(handle, (TimeAndSizeFileAttr::from_file_attr(&attr), 0, decryptor)); + self.read_handles.insert(handle, (TimeAndSizeFileAttr::from_file_attr(&attr), 0, decryptor, lock)); Ok(handle) } - fn create_write_handle(&mut self, ino: u64, handle: u64) -> FsResult { + fn create_write_handle(&mut self, ino: u64, handle: u64, lock: Arc>) -> FsResult { let path = self.data_dir.join(CONTENTS_DIR).join(ino.to_string()); let file = OpenOptions::new().read(true).write(true).open(path.clone())?; let encryptor = crypto_util::create_encryptor(file, &self.cipher, &self.key); // save attr also to avoid loading it multiple times while writing let attr = self.get_inode(ino)?; - self.write_handles.insert(handle, (TimeAndSizeFileAttr::from_file_attr(&attr), path, 0, encryptor)); + self.write_handles.insert(handle, (TimeAndSizeFileAttr::from_file_attr(&attr), path, 0, encryptor, lock)); self.opened_files_for_write.insert(ino, handle); Ok(handle) } - fn replace_handle_data(&mut self, handle: u64, attr: TimeAndSizeFileAttr, new_path: PathBuf, position: u64, new_encryptor: write::Encryptor) { - self.write_handles.insert(handle, (attr, new_path, position, new_encryptor)); + fn replace_handle_data(&mut self, handle: u64, attr: TimeAndSizeFileAttr, new_path: PathBuf, position: u64, new_encryptor: write::Encryptor, lock: Arc>) { + self.write_handles.insert(handle, (attr, new_path, position, new_encryptor, lock)); } fn ensure_root_exists(&mut self) -> FsResult<()> { @@ -1167,28 +1207,26 @@ fn ensure_structure_created(data_dir: &PathBuf) -> FsResult<()> { fn merge_attr(attr: &mut FileAttr, perm: u16, atime: SystemTime, mtime: SystemTime, ctime: SystemTime, crtime: SystemTime, uid: u32, gid: u32, size: u64, nlink: u32, flags: u32) { attr.perm = perm; - merge_attr_time_and_time(attr, atime, mtime, ctime, crtime, size); + merge_attr_times_and_set_size(attr, atime, mtime, ctime, crtime, size); attr.uid = uid; attr.gid = gid; attr.nlink = nlink; attr.flags = flags; } -fn merge_attr_time_and_time(attr: &mut FileAttr, atime: SystemTime, mtime: SystemTime, ctime: SystemTime, crtime: SystemTime, size: u64) { +fn merge_attr_times_and_set_size(attr: &mut FileAttr, atime: SystemTime, mtime: SystemTime, ctime: SystemTime, crtime: SystemTime, size: u64) { attr.atime = max(attr.atime, atime); attr.mtime = max(attr.mtime, mtime); attr.ctime = max(attr.ctime, ctime); - attr.crtime = max(attr.ctime, crtime); - attr.crtime = max(attr.ctime, crtime); + attr.crtime = max(attr.crtime, crtime); attr.size = size; } -fn merge_attr_time_and_time_obj(attr: &mut FileAttr, from: &TimeAndSizeFileAttr) { +fn merge_attr_time_and_set_size_obj(attr: &mut FileAttr, from: &TimeAndSizeFileAttr) { attr.atime = max(attr.atime, from.atime); attr.mtime = max(attr.mtime, from.mtime); attr.ctime = max(attr.ctime, from.ctime); - attr.crtime = max(attr.ctime, from.crtime); - attr.crtime = max(attr.ctime, from.crtime); + attr.crtime = max(attr.crtime, from.crtime); attr.size = from.size; } diff --git a/src/encryptedfs/encryptedfs_test.rs b/src/encryptedfs/encryptedfs_test.rs index 0ba7abbb..3f8c31db 100644 --- a/src/encryptedfs/encryptedfs_test.rs +++ b/src/encryptedfs/encryptedfs_test.rs @@ -147,7 +147,7 @@ fn test_create_nod() { // directory in root let test_dir = "test-dir"; - let (fh, attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); assert_ne!(attr.ino, 0); assert!(fs.data_dir.join(INODES_DIR).join(attr.ino.to_string()).is_file()); assert!(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()).is_dir()); @@ -165,7 +165,7 @@ fn test_create_nod() { // directory in another directory let parent = attr.ino; let test_dir_2 = "test-dir-2"; - let (fh, attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); assert!(fs.data_dir.join(INODES_DIR).join(attr.ino.to_string()).is_file()); assert!(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()).is_dir()); assert!(fs.data_dir.join(CONTENTS_DIR).join(parent.to_string()).join(fs.normalize_end_encrypt_file_name(test_dir_2)).is_file()); @@ -202,10 +202,10 @@ fn test_read_dir() { // file and directory in root let test_file = "test-file"; - let (fh, file_attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, file_attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); let test_dir = "test-dir"; - let (fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); let mut entries: Vec> = fs.read_dir(dir_attr.ino).unwrap().collect(); entries.sort_by(|a, b| a.as_ref().unwrap().name.cmp(&b.as_ref().unwrap().name)); let entries: Vec = entries.into_iter().map(|e| e.unwrap()).collect(); @@ -250,10 +250,10 @@ fn test_read_dir() { // file and directory in another directory let parent = dir_attr.ino; let test_file_2 = "test-file-2"; - let (fh, file_attr) = fs.create_nod(parent, test_file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, file_attr) = fs.create_nod(parent, test_file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); let test_dir_2 = "test-dir-2"; - let (fh, dir_attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, dir_attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); let mut entries: Vec> = fs.read_dir(dir_attr.ino).unwrap().collect(); entries.sort_by(|a, b| a.as_ref().unwrap().name.cmp(&b.as_ref().unwrap().name)); let entries: Vec = entries.into_iter().map(|e| e.unwrap()).collect(); @@ -308,10 +308,10 @@ fn test_read_dir_plus() { // file and directory in root let test_file = "test-file"; - let (fh, file_attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, file_attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); let test_dir = "test-dir"; - let (fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); let mut entries: Vec> = fs.read_dir_plus(dir_attr.ino).unwrap().collect(); entries.sort_by(|a, b| a.as_ref().unwrap().name.cmp(&b.as_ref().unwrap().name)); let entries: Vec = entries.into_iter().map(|e| e.unwrap()).collect(); @@ -363,10 +363,10 @@ fn test_read_dir_plus() { let parent = dir_attr.ino; let attr_parent = dir_attr; let test_file_2 = "test-file-2"; - let (fh, file_attr) = fs.create_nod(parent, test_file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, file_attr) = fs.create_nod(parent, test_file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); let test_dir_2 = "test-dir-2"; - let (fh, dir_attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, dir_attr) = fs.create_nod(parent, test_dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); // for some reason the tv_nsec is not the same between what create_nod() and read_dir_plus() returns, so we reload it again let dir_attr = fs.get_inode(dir_attr.ino).unwrap(); let attr_parent = fs.get_inode(attr_parent.ino).unwrap(); @@ -441,9 +441,9 @@ fn test_remove_dir() { let fs = setup.fs.as_mut().unwrap(); let test_dir = "test-dir"; - let (fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_fh, dir_attr) = fs.create_nod(ROOT_INODE, test_dir, create_attr_from_type(FileType::Directory), false, false).unwrap(); let test_file = "test-file"; - let (fh, file_attr) = fs.create_nod(dir_attr.ino, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, file_attr) = fs.create_nod(dir_attr.ino, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); assert!(matches!(fs.remove_dir(ROOT_INODE, test_dir), Err(FsError::NotEmpty))); assert!(fs.data_dir.join(INODES_DIR).join(dir_attr.ino.to_string()).is_file()); @@ -465,7 +465,7 @@ fn test_remove_file() { let fs = setup.fs.as_mut().unwrap(); let test_file = "test-file"; - let (fh, attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); assert!(fs.remove_file(ROOT_INODE, test_file).is_ok()); assert_ne!(fs.data_dir.join(INODES_DIR).join(attr.ino.to_string()).is_file(), true); assert_ne!(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()).is_file(), true); @@ -499,29 +499,34 @@ fn test_write_all() { fs.release_handle(fh).unwrap(); assert_eq!(data, &read_to_string(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()), &fs)[5..]); - // offset before current position - // first write no bytes to the end to move the position - let fh = fs.open(attr.ino, false, true).unwrap(); - fs.write_all(attr.ino, 7, &[0u8; 0], fh).unwrap(); - let data = "42"; - fs.write_all(attr.ino, 5, data.as_bytes(), fh).unwrap(); - fs.flush(fh).unwrap(); - fs.release_handle(fh).unwrap(); - assert_eq!(data, &read_to_string(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()), &fs)[5..]); - // offset after file end let data = "37"; let fh = fs.open(attr.ino, false, true).unwrap(); fs.write_all(attr.ino, 42, data.as_bytes(), fh).unwrap(); fs.flush(fh).unwrap(); fs.release_handle(fh).unwrap(); - assert_eq!(format!("test-42{}37", " ".replace(" ", "\0")), + assert_eq!(format!("test-37{}37", "\0".repeat(35)), read_to_string(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()), &fs)); - // write before current position then write to the end, also check it preserves the content from - // the first write to offset to end of the file + // offset before current position, several blocks + // first write no bytes to the end to move the position let test_file_2 = "test-file-2"; let (fh, attr) = fs.create_nod(ROOT_INODE, test_file_2, create_attr_from_type(FileType::RegularFile), false, true).unwrap(); + let data = "test-42-37-42"; + fs.write_all(attr.ino, 0, data.as_bytes(), fh).unwrap(); + fs.write_all(attr.ino, data.len() as u64, &[0_u8; 0], fh).unwrap(); + let data1 = "01"; + fs.write_all(attr.ino, 5, data1.as_bytes(), fh).unwrap(); + let data2 = "02"; + fs.write_all(attr.ino, 8, data2.as_bytes(), fh).unwrap(); + fs.flush(fh).unwrap(); + fs.release_handle(fh).unwrap(); + assert_eq!("test-01-02-42", &read_to_string(fs.data_dir.join(CONTENTS_DIR).join(attr.ino.to_string()), &fs)); + + // write before current position then write to the end, also check it preserves the content from + // the first write to offset to end of the file + let test_file_3 = "test-file-3"; + let (fh, attr) = fs.create_nod(ROOT_INODE, test_file_3, create_attr_from_type(FileType::RegularFile), false, true).unwrap(); let data = "test-42-37"; fs.write_all(attr.ino, 0, data.as_bytes(), fh).unwrap(); fs.write_all(attr.ino, 5, b"37", fh).unwrap(); @@ -587,13 +592,13 @@ fn test_read() { fs.flush(fh).unwrap(); fs.release_handle(fh).unwrap(); let fh = fs.open(attr.ino, true, false).unwrap(); - fs.read(attr.ino, 0, &mut [0u8; 1], fh).unwrap(); + fs.read(attr.ino, 0, &mut [0_u8; 1], fh).unwrap(); let fh_2 = fs.open(attr.ino, false, true).unwrap(); let new_data = "37"; fs.write_all(attr.ino, 5, new_data.as_bytes(), fh_2).unwrap(); fs.flush(fh_2).unwrap(); fs.release_handle(fh_2).unwrap(); - let mut buf = [0u8; 2]; + let mut buf = [0_u8; 2]; fs.read(attr.ino, 5, &mut buf, fh).unwrap(); assert_eq!(new_data, String::from_utf8(buf.to_vec()).unwrap()); @@ -605,13 +610,13 @@ fn test_read() { fs.flush(fh).unwrap(); fs.release_handle(fh).unwrap(); let fh = fs.open(attr.ino, true, false).unwrap(); - fs.read(attr.ino, 8, &mut [0u8; 1], fh).unwrap(); + fs.read(attr.ino, 8, &mut [0_u8; 1], fh).unwrap(); let fh_2 = fs.open(attr.ino, false, true).unwrap(); let new_data = "37"; fs.write_all(attr.ino, 5, new_data.as_bytes(), fh_2).unwrap(); fs.flush(fh_2).unwrap(); fs.release_handle(fh_2).unwrap(); - let mut buf = [0u8; 2]; + let mut buf = [0_u8; 2]; fs.read(attr.ino, 5, &mut buf, fh).unwrap(); assert_eq!(new_data, String::from_utf8(buf.to_vec()).unwrap()); @@ -623,13 +628,13 @@ fn test_read() { fs.flush(fh).unwrap(); fs.release_handle(fh).unwrap(); let fh = fs.open(attr.ino, true, false).unwrap(); - fs.read(attr.ino, 7, &mut [0u8; 1], fh).unwrap(); + fs.read(attr.ino, 7, &mut [0_u8; 1], fh).unwrap(); let fh_2 = fs.open(attr.ino, false, true).unwrap(); let new_data = "37"; fs.write_all(attr.ino, 5, new_data.as_bytes(), fh_2).unwrap(); fs.flush(fh_2).unwrap(); fs.release_handle(fh_2).unwrap(); - let mut buf = [0u8; 2]; + let mut buf = [0_u8; 2]; fs.read(attr.ino, 8, &mut buf, fh).unwrap(); assert_eq!(new_data, String::from_utf8(buf.to_vec()).unwrap()); @@ -648,7 +653,7 @@ fn test_truncate() { run_test(TestSetup { data_path: format!("{TESTS_DATA_DIR}test_truncate") }, |setup| { let fs = setup.fs.as_mut().unwrap(); - let (fh, attr) = fs.create_nod(ROOT_INODE, "test-file", create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, attr) = fs.create_nod(ROOT_INODE, "test-file", create_attr_from_type(FileType::RegularFile), false, false).unwrap(); // size increase fs.truncate(attr.ino, 42).unwrap(); @@ -798,7 +803,7 @@ fn test_rename() { // file to existing file in same directory let new_parent = ROOT_INODE; let (_, attr) = fs.create_nod(ROOT_INODE, file_1, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, file_2, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); fs.rename(ROOT_INODE, file_1, new_parent, file_2).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, file_1), true); assert_eq!(fs.exists_by_name(new_parent, file_2), true); @@ -812,7 +817,7 @@ fn test_rename() { // directory to existing directory in same directory let new_parent = ROOT_INODE; let (_, attr) = fs.create_nod(ROOT_INODE, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, dir_2, create_attr_from_type(FileType::Directory), false, false).unwrap(); fs.rename(ROOT_INODE, dir_1, new_parent, dir_2).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, dir_1), true); assert_eq!(fs.exists_by_name(new_parent, dir_2), true); @@ -830,7 +835,7 @@ fn test_rename() { // file to existing file in another directory let new_parent = new_parent_attr.ino; let (_, attr) = fs.create_nod(ROOT_INODE, file_1, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, file_1, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, file_1, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); fs.rename(ROOT_INODE, file_1, new_parent, file_1).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, file_1), true); assert_eq!(fs.exists_by_name(new_parent, file_1), true); @@ -844,7 +849,7 @@ fn test_rename() { // directory to existing directory in another directory let new_parent = new_parent_attr.ino; let (_, attr) = fs.create_nod(ROOT_INODE, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); fs.rename(ROOT_INODE, dir_1, new_parent, dir_1).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, dir_1), true); assert_eq!(fs.exists_by_name(new_parent, dir_1), true); @@ -862,7 +867,7 @@ fn test_rename() { // overwriting directory with file let new_parent = ROOT_INODE; let (_, attr) = fs.create_nod(ROOT_INODE, file_1, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, dir_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); fs.rename(ROOT_INODE, file_1, new_parent, dir_1).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, file_1), true); assert_eq!(fs.exists_by_name(new_parent, dir_1), true); @@ -877,7 +882,7 @@ fn test_rename() { let new_parent = ROOT_INODE; let dir_3 = "dir-3"; let (_, attr) = fs.create_nod(ROOT_INODE, dir_3, create_attr_from_type(FileType::Directory), false, false).unwrap(); - let (_, attr_2) = fs.create_nod(new_parent, file_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); + let (_, _attr_2) = fs.create_nod(new_parent, file_1, create_attr_from_type(FileType::Directory), false, false).unwrap(); fs.rename(ROOT_INODE, dir_3, new_parent, file_1).unwrap(); assert_ne!(fs.exists_by_name(ROOT_INODE, dir_3), true); assert_eq!(fs.exists_by_name(new_parent, file_1), true); @@ -895,7 +900,7 @@ fn test_rename() { // overwriting non-empty directory let new_parent = ROOT_INODE; let (_, attr) = fs.create_nod(ROOT_INODE, dir_3, create_attr_from_type(FileType::Directory), false, false).unwrap(); - let attr_2 = new_parent_attr; + let _attr_2 = new_parent_attr; let name_2 = "dir-new-parent"; assert!(matches!(fs.rename(ROOT_INODE, dir_3, new_parent, name_2), Err(FsError::NotEmpty))); assert_eq!(fs.exists_by_name(ROOT_INODE, dir_3), true); @@ -961,7 +966,7 @@ fn test_open() { let fs = setup.fs.as_mut().unwrap(); let test_file = "test-file"; - let (fh, attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); + let (_fh, attr) = fs.create_nod(ROOT_INODE, test_file, create_attr_from_type(FileType::RegularFile), false, false).unwrap(); // single read let fh = fs.open(attr.ino, true, false).unwrap(); assert_ne!(fh, 0); @@ -969,7 +974,7 @@ fn test_open() { let fh_2 = fs.open(attr.ino, true, false).unwrap(); assert_ne!(fh_2, 0); // write and read - let fh_w = fs.open(attr.ino, false, true).unwrap(); + let _fh_w = fs.open(attr.ino, false, true).unwrap(); // ensure cannot open multiple write assert!(matches!(fs.open(attr.ino, false, true), Err(FsError::AlreadyOpenForWrite))); }); @@ -979,6 +984,6 @@ fn test_open() { // #[test] fn test_sample() { run_test(TestSetup { data_path: format!("{TESTS_DATA_DIR}test_sample") }, |setup| { - let fs = setup.fs.as_mut().unwrap(); + let _fs = setup.fs.as_mut().unwrap(); }); } diff --git a/src/main.rs b/src/main.rs index 849098b9..06daeb36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -279,12 +279,11 @@ async fn run_fuse(mountpoint: String, data_dir: &str, password: &str, cipher: Ci info!("Mounting FUSE filesystem"); match EncryptedFsFuse3::new(&data_dir, &password, cipher, derive_key_hash_rounds, direct_io, suid_support) { Err(FsError::InvalidPassword) => { + error!("Cannot decrypt data, maybe the password is wrong"); println!("Cannot decrypt data, maybe the password is wrong"); - process::exit(1); } Err(err) => { error!("{err}"); - process::exit(1); } Ok(fs) => Session::new(mount_options)