1use argon2::password_hash::rand_core::RngCore;
2use async_trait::async_trait;
3use futures_util::TryStreamExt;
4use lru::LruCache;
5use num_format::{Locale, ToFormattedString};
6use serde::{Deserialize, Serialize};
7use shush_rs::{ExposeSecret, SecretBox, SecretString, SecretVec};
8use std::backtrace::Backtrace;
9use std::collections::{HashMap, HashSet, VecDeque};
10use std::fmt::Debug;
11use std::fs::{DirEntry, File, OpenOptions, ReadDir};
12use std::io::{Read, Seek, SeekFrom, Write};
13use std::num::{NonZeroUsize, ParseIntError};
14use std::path::{Path, PathBuf};
15use std::str::FromStr;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::{Arc, LazyLock, Weak};
18use std::time::{Duration, SystemTime};
19use std::{fs, io};
20use thiserror::Error;
21use tokio::runtime::Runtime;
22use tokio::sync::{Mutex, RwLock};
23use tokio::task::{JoinError, JoinSet};
24use tokio_stream::wrappers::ReadDirStream;
25use tracing::{debug, error, info, instrument, warn, Level};
26
27use crate::arc_hashmap::ArcHashMap;
28use crate::crypto::read::{CryptoRead, CryptoReadSeek};
29use crate::crypto::write::{CryptoInnerWriter, CryptoWrite, CryptoWriteSeek};
30use crate::crypto::Cipher;
31use crate::expire_value::{ExpireValue, ValueProvider};
32use crate::{crypto, fs_util, stream_util};
33use bon::bon;
34
35mod bench;
36#[cfg(test)]
37mod test;
38
39pub(crate) const INODES_DIR: &str = "inodes";
40pub(crate) const CONTENTS_DIR: &str = "contents";
41pub(crate) const SECURITY_DIR: &str = "security";
42pub(crate) const KEY_ENC_FILENAME: &str = "key.enc";
43pub(crate) const KEY_SALT_FILENAME: &str = "key.salt";
44
45pub(crate) const LS_DIR: &str = "ls";
46pub(crate) const HASH_DIR: &str = "hash";
47
48pub(crate) const ROOT_INODE: u64 = 1;
49
50fn spawn_runtime() -> Runtime {
51 let runtime = tokio::runtime::Builder::new_multi_thread()
52 .enable_all()
53 .build()
54 .unwrap();
55 runtime
56}
57
58static DIR_ENTRIES_RT: LazyLock<Runtime> = LazyLock::new(spawn_runtime);
59static NOD_RT: LazyLock<Runtime> = LazyLock::new(spawn_runtime);
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
63pub struct FileAttr {
64 pub ino: u64,
66 pub size: u64,
68 pub blocks: u64,
70 pub atime: SystemTime,
72 pub mtime: SystemTime,
74 pub ctime: SystemTime,
76 pub crtime: SystemTime,
78 pub kind: FileType,
80 pub perm: u16,
82 pub nlink: u32,
84 pub uid: u32,
86 pub gid: u32,
88 pub rdev: u32,
90 pub blksize: u32,
92 pub flags: u32,
94}
95
96#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
98pub enum FileType {
99 Directory,
107 RegularFile,
109 }
114
115#[derive(Debug, Clone, Copy, Default)]
116pub struct SetFileAttr {
117 pub size: Option<u64>,
119 pub atime: Option<SystemTime>,
121 pub mtime: Option<SystemTime>,
123 pub ctime: Option<SystemTime>,
125 pub crtime: Option<SystemTime>,
127 pub perm: Option<u16>,
129 pub uid: Option<u32>,
131 pub gid: Option<u32>,
133 pub rdev: Option<u32>,
135 pub flags: Option<u32>,
137}
138
139impl SetFileAttr {
140 #[must_use]
141 pub const fn with_size(mut self, size: u64) -> Self {
142 self.size = Some(size);
143 self
144 }
145
146 #[must_use]
147 pub const fn with_atime(mut self, atime: SystemTime) -> Self {
148 self.atime = Some(atime);
149 self
150 }
151
152 #[must_use]
153 pub const fn with_mtime(mut self, mtime: SystemTime) -> Self {
154 self.mtime = Some(mtime);
155 self
156 }
157
158 #[must_use]
159 pub const fn with_ctime(mut self, ctime: SystemTime) -> Self {
160 self.ctime = Some(ctime);
161 self
162 }
163
164 #[must_use]
165 pub const fn with_crtime(mut self, crtime: SystemTime) -> Self {
166 self.crtime = Some(crtime);
167 self
168 }
169
170 #[must_use]
171 pub const fn with_perm(mut self, perm: u16) -> Self {
172 self.perm = Some(perm);
173 self
174 }
175
176 #[must_use]
177 pub const fn with_uid(mut self, uid: u32) -> Self {
178 self.uid = Some(uid);
179 self
180 }
181
182 #[must_use]
183 pub const fn with_gid(mut self, gid: u32) -> Self {
184 self.gid = Some(gid);
185 self
186 }
187
188 #[must_use]
189 pub const fn with_rdev(mut self, rdev: u32) -> Self {
190 self.rdev = Some(rdev);
191 self
192 }
193
194 #[must_use]
195 pub const fn with_flags(mut self, flags: u32) -> Self {
196 self.rdev = Some(flags);
197 self
198 }
199}
200
201#[derive(Debug, Clone)]
202pub struct CreateFileAttr {
203 pub kind: FileType,
205 pub perm: u16,
207 pub uid: u32,
209 pub gid: u32,
211 pub rdev: u32,
213 pub flags: u32,
215}
216
217impl From<CreateFileAttr> for FileAttr {
218 fn from(value: CreateFileAttr) -> Self {
219 let now = SystemTime::now();
220 Self {
221 ino: 0,
222 size: 0,
223 blocks: 0,
224 atime: now,
225 mtime: now,
226 ctime: now,
227 crtime: now,
228 kind: value.kind,
229 perm: value.perm,
230 nlink: if value.kind == FileType::Directory {
231 2
232 } else {
233 1
234 },
235 uid: value.uid,
236 gid: value.gid,
237 rdev: value.rdev,
238 blksize: 0,
239 flags: value.flags,
240 }
241 }
242}
243
244#[derive(Error, Debug)]
245pub enum FsError {
246 #[error("IO error: {source}")]
247 Io {
248 #[from]
249 source: io::Error,
250 backtrace: Backtrace,
251 },
252 #[error("serialize error: {source}")]
253 SerializeError {
254 #[from]
255 source: bincode::Error,
256 backtrace: Backtrace,
257 },
258 #[error("item not found: {0}")]
259 NotFound(&'static str),
260 #[error("inode not found")]
261 InodeNotFound,
262 #[error("invalid input")]
263 InvalidInput(&'static str),
264 #[error("invalid node type")]
265 InvalidInodeType,
266 #[error("invalid file handle")]
267 InvalidFileHandle,
268 #[error("already exists")]
269 AlreadyExists,
270 #[error("already open for write")]
271 AlreadyOpenForWrite,
272 #[error("not empty")]
273 NotEmpty,
274 #[error("other: {0}")]
275 Other(&'static str),
276 #[error("invalid password")]
277 InvalidPassword,
278 #[error("invalid structure of data directory")]
279 InvalidDataDirStructure,
280 #[error("crypto error: {source}")]
281 Crypto {
282 #[from]
283 source: crypto::Error,
284 backtrace: Backtrace,
285 },
286 #[error("keyring error: {source}")]
287 Keyring {
288 #[from]
289 source: keyring::Error,
290 backtrace: Backtrace,
291 },
292 #[error("parse int error: {source}")]
293 ParseIntError {
294 #[from]
295 source: ParseIntError,
296 backtrace: Backtrace,
297 },
298 #[error("tokio join error: {source}")]
299 JoinError {
300 #[from]
301 source: JoinError,
302 backtrace: Backtrace,
303 },
304 #[error("max filesize exceeded, max allowed {0}")]
305 MaxFilesizeExceeded(usize),
306 #[error("Read only mode is active.")]
307 ReadOnly,
308}
309
310#[derive(Debug, Clone)]
311struct TimesAndSizeFileAttr {
312 atime: SystemTime,
313 mtime: SystemTime,
314 ctime: SystemTime,
315 crtime: SystemTime,
316 size: u64,
317}
318
319impl TimesAndSizeFileAttr {
320 #[allow(dead_code)]
321 const fn new(
322 atime: SystemTime,
323 mtime: SystemTime,
324 ctime: SystemTime,
325 crtime: SystemTime,
326 size: u64,
327 ) -> Self {
328 Self {
329 atime,
330 mtime,
331 ctime,
332 crtime,
333 size,
334 }
335 }
336}
337
338impl From<FileAttr> for TimesAndSizeFileAttr {
339 fn from(value: FileAttr) -> Self {
340 Self {
341 atime: value.atime,
342 mtime: value.mtime,
343 ctime: value.ctime,
344 crtime: value.crtime,
345 size: value.size,
346 }
347 }
348}
349
350impl From<TimesAndSizeFileAttr> for SetFileAttr {
351 fn from(value: TimesAndSizeFileAttr) -> Self {
352 Self::default()
353 .with_atime(value.atime)
354 .with_mtime(value.mtime)
355 .with_ctime(value.ctime)
356 .with_crtime(value.crtime)
357 .with_size(value.size)
358 }
359}
360
361#[derive(Debug, Clone)]
362struct TimesFileAttr {
363 atime: SystemTime,
364 mtime: SystemTime,
365 ctime: SystemTime,
366 crtime: SystemTime,
367}
368
369impl TimesFileAttr {
370 #[allow(dead_code)]
371 const fn new(
372 atime: SystemTime,
373 mtime: SystemTime,
374 ctime: SystemTime,
375 crtime: SystemTime,
376 ) -> Self {
377 Self {
378 atime,
379 mtime,
380 ctime,
381 crtime,
382 }
383 }
384}
385
386impl From<FileAttr> for TimesFileAttr {
387 fn from(value: FileAttr) -> Self {
388 Self {
389 atime: value.atime,
390 mtime: value.mtime,
391 ctime: value.ctime,
392 crtime: value.crtime,
393 }
394 }
395}
396
397impl From<TimesFileAttr> for SetFileAttr {
398 fn from(value: TimesFileAttr) -> Self {
399 Self::default()
400 .with_atime(value.atime)
401 .with_mtime(value.mtime)
402 .with_ctime(value.ctime)
403 .with_crtime(value.crtime)
404 }
405}
406
407#[derive(Debug, Clone)]
408pub struct DirectoryEntry {
409 pub ino: u64,
410 pub name: SecretString,
411 pub kind: FileType,
412}
413
414impl PartialEq for DirectoryEntry {
415 fn eq(&self, other: &Self) -> bool {
416 self.ino == other.ino
417 && self.name.expose_secret() == other.name.expose_secret()
418 && self.kind == other.kind
419 }
420}
421
422#[derive(Debug)]
424pub struct DirectoryEntryPlus {
425 pub ino: u64,
426 pub name: SecretString,
427 pub kind: FileType,
428 pub attr: FileAttr,
429}
430
431impl PartialEq for DirectoryEntryPlus {
432 fn eq(&self, other: &Self) -> bool {
433 self.ino == other.ino
434 && self.name.expose_secret() == other.name.expose_secret()
435 && self.kind == other.kind
436 && self.attr == other.attr
437 }
438}
439
440pub type FsResult<T> = Result<T, FsError>;
441
442pub struct DirectoryEntryIterator(VecDeque<FsResult<DirectoryEntry>>);
443
444impl Iterator for DirectoryEntryIterator {
445 type Item = FsResult<DirectoryEntry>;
446
447 fn next(&mut self) -> Option<Self::Item> {
448 self.0.pop_front()
449 }
450}
451
452pub struct DirectoryEntryPlusIterator(VecDeque<FsResult<DirectoryEntryPlus>>);
453
454impl Iterator for DirectoryEntryPlusIterator {
455 type Item = FsResult<DirectoryEntryPlus>;
456
457 #[instrument(name = "DirectoryEntryPlusIterator::next", skip(self))]
458 fn next(&mut self) -> Option<Self::Item> {
459 self.0.pop_front()
460 }
461}
462
463struct ReadHandleContext {
464 ino: u64,
465 attr: TimesFileAttr,
466 reader: Option<Box<dyn CryptoReadSeek<File>>>,
467}
468
469enum ReadHandleContextOperation {
470 Create { ino: u64 },
471}
472
473impl ReadHandleContextOperation {
474 const fn get_ino(&self) -> u64 {
475 match *self {
476 Self::Create { ino, .. } => ino,
477 }
478 }
479}
480
481enum WriteHandleContextOperation {
482 Create { ino: u64 },
483}
484
485impl WriteHandleContextOperation {
486 const fn get_ino(&self) -> u64 {
487 match *self {
488 Self::Create { ino, .. } => ino,
489 }
490 }
491}
492
493struct WriteHandleContext {
494 ino: u64,
495 attr: TimesAndSizeFileAttr,
496 writer: Option<Box<dyn CryptoWriteSeek<File>>>,
497}
498
499struct KeyProvider {
500 key_path: PathBuf,
501 salt_path: PathBuf,
502 password_provider: Box<dyn PasswordProvider>,
503 cipher: Cipher,
504}
505
506#[async_trait]
507impl ValueProvider<SecretVec<u8>, FsError> for KeyProvider {
508 async fn provide(&self) -> Result<SecretVec<u8>, FsError> {
509 let password = self
510 .password_provider
511 .get_password()
512 .ok_or(FsError::InvalidPassword)?;
513 read_or_create_key(&self.key_path, &self.salt_path, &password, self.cipher)
514 }
515}
516
517pub trait PasswordProvider: Send + Sync + 'static {
518 fn get_password(&self) -> Option<SecretString>;
519}
520
521struct DirEntryNameCacheProvider {}
522#[async_trait]
523impl ValueProvider<Mutex<LruCache<String, SecretString>>, FsError> for DirEntryNameCacheProvider {
524 async fn provide(&self) -> Result<Mutex<LruCache<String, SecretString>>, FsError> {
525 Ok(Mutex::new(LruCache::new(NonZeroUsize::new(2000).unwrap())))
526 }
527}
528
529struct DirEntryMetaCacheProvider {}
530#[async_trait]
531impl ValueProvider<Mutex<DirEntryMetaCache>, FsError> for DirEntryMetaCacheProvider {
532 async fn provide(&self) -> Result<Mutex<DirEntryMetaCache>, FsError> {
533 Ok(Mutex::new(LruCache::new(NonZeroUsize::new(2000).unwrap())))
534 }
535}
536
537struct AttrCacheProvider {}
538#[async_trait]
539impl ValueProvider<RwLock<LruCache<u64, FileAttr>>, FsError> for AttrCacheProvider {
540 async fn provide(&self) -> Result<RwLock<LruCache<u64, FileAttr>>, FsError> {
541 Ok(RwLock::new(LruCache::new(NonZeroUsize::new(2000).unwrap())))
542 }
543}
544
545type DirEntryMetaCache = LruCache<String, (u64, FileType)>;
546
547pub struct EncryptedFs {
549 pub(crate) data_dir: PathBuf,
550 write_handles: RwLock<HashMap<u64, Mutex<WriteHandleContext>>>,
551 read_handles: RwLock<HashMap<u64, Mutex<ReadHandleContext>>>,
552 current_handle: AtomicU64,
553 cipher: Cipher,
554 opened_files_for_read: RwLock<HashMap<u64, HashSet<u64>>>,
556 opened_files_for_write: RwLock<HashMap<u64, u64>>,
557 serialize_inode_locks: Arc<ArcHashMap<u64, RwLock<bool>>>,
560 serialize_update_inode_locks: ArcHashMap<u64, Mutex<bool>>,
562 serialize_dir_entries_ls_locks: Arc<ArcHashMap<String, RwLock<bool>>>,
564 serialize_dir_entries_hash_locks: Arc<ArcHashMap<String, RwLock<bool>>>,
565 read_write_locks: ArcHashMap<u64, RwLock<bool>>,
566 key: ExpireValue<SecretVec<u8>, FsError, KeyProvider>,
567 self_weak: std::sync::Mutex<Option<Weak<Self>>>,
568 attr_cache: ExpireValue<RwLock<LruCache<u64, FileAttr>>, FsError, AttrCacheProvider>,
569 dir_entries_name_cache:
570 ExpireValue<Mutex<LruCache<String, SecretString>>, FsError, DirEntryNameCacheProvider>,
571 dir_entries_meta_cache:
572 ExpireValue<Mutex<DirEntryMetaCache>, FsError, DirEntryMetaCacheProvider>,
573 sizes_write: Mutex<HashMap<u64, AtomicU64>>,
574 sizes_read: Mutex<HashMap<u64, AtomicU64>>,
575 requested_read: Mutex<HashMap<u64, AtomicU64>>,
576 read_only: bool,
577}
578
579impl EncryptedFs {
580 #[allow(clippy::missing_panics_doc)]
581 #[allow(clippy::missing_errors_doc)]
582 pub async fn new(
583 data_dir: PathBuf,
584 password_provider: Box<dyn PasswordProvider>,
585 cipher: Cipher,
586 read_only: bool,
587 ) -> FsResult<Arc<Self>> {
588 let key_provider = KeyProvider {
589 key_path: data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME),
590 salt_path: data_dir.join(SECURITY_DIR).join(KEY_SALT_FILENAME),
591 password_provider,
592 cipher,
593 };
594 let key = ExpireValue::new(key_provider, Duration::from_secs(10 * 60));
595
596 ensure_structure_created(&data_dir.clone()).await?;
597 key.get().await?; let fs = Self {
600 data_dir,
601 write_handles: RwLock::new(HashMap::new()),
602 read_handles: RwLock::new(HashMap::new()),
603 current_handle: AtomicU64::new(1),
604 cipher,
605 opened_files_for_read: RwLock::new(HashMap::new()),
606 opened_files_for_write: RwLock::new(HashMap::new()),
607 serialize_inode_locks: Arc::new(ArcHashMap::default()),
608 serialize_update_inode_locks: ArcHashMap::default(),
609 serialize_dir_entries_ls_locks: Arc::new(ArcHashMap::default()),
610 serialize_dir_entries_hash_locks: Arc::new(ArcHashMap::default()),
611 key,
612 self_weak: std::sync::Mutex::new(None),
613 read_write_locks: ArcHashMap::default(),
614 attr_cache: ExpireValue::new(AttrCacheProvider {}, Duration::from_secs(10 * 60)),
616 dir_entries_name_cache: ExpireValue::new(
618 DirEntryNameCacheProvider {},
619 Duration::from_secs(10 * 60),
620 ),
621 dir_entries_meta_cache: ExpireValue::new(
623 DirEntryMetaCacheProvider {},
624 Duration::from_secs(10 * 60),
625 ),
626 sizes_write: Mutex::default(),
627 sizes_read: Mutex::default(),
628 requested_read: Mutex::default(),
629 read_only,
630 };
631
632 let arc = Arc::new(fs);
633 arc.self_weak
634 .lock()
635 .expect("cannot obtain lock")
636 .replace(Arc::downgrade(&arc));
637
638 arc.ensure_root_exists().await?;
639
640 Ok(arc)
641 }
642
643 pub fn exists(&self, ino: u64) -> bool {
644 self.ino_file(ino).is_file()
645 }
646
647 pub fn is_dir(&self, ino: u64) -> bool {
648 self.contents_path(ino).is_dir()
649 }
650
651 pub fn is_file(&self, ino: u64) -> bool {
652 self.contents_path(ino).is_file()
653 }
654
655 #[allow(dead_code)]
656 async fn is_read_only(&self) -> bool {
657 self.read_only
658 }
659
660 fn validate_filename(&self, secret_filename: &SecretBox<String>) -> FsResult<()> {
661 let filename = secret_filename.expose_secret().to_string();
662 if filename.contains('/') {
663 Err(FsError::InvalidInput("'/' not allowed in the filename"))
664 } else if filename.contains('\\') {
665 Err(FsError::InvalidInput("'\\' not allowed in the filename"))
666 } else {
667 Ok(())
668 }
669 }
670
671 #[allow(clippy::missing_panics_doc)]
673 #[allow(clippy::missing_errors_doc)]
674 #[allow(clippy::too_many_lines)]
675 pub async fn create(
676 &self,
677 parent: u64,
678 name: &SecretString,
679 create_attr: CreateFileAttr,
680 read: bool,
681 write: bool,
682 ) -> FsResult<(u64, FileAttr)> {
683 if self.read_only {
684 return Err(FsError::ReadOnly);
685 }
686 if *name.expose_secret() == "." || *name.expose_secret() == ".." {
687 return Err(FsError::InvalidInput("name cannot be '.' or '..'"));
688 }
689 if !self.exists(parent) {
690 return Err(FsError::InodeNotFound);
691 }
692 if self.exists_by_name(parent, name)? {
693 return Err(FsError::AlreadyExists);
694 }
695 self.validate_filename(name)?;
696
697 let self_clone = self
699 .self_weak
700 .lock()
701 .unwrap()
702 .as_ref()
703 .unwrap()
704 .upgrade()
705 .unwrap();
706 let name_clone = name.clone();
707 NOD_RT
708 .spawn(async move {
709 let mut attr: FileAttr = create_attr.into();
710 attr.ino = self_clone.generate_next_inode();
711
712 let fs = self_clone;
713 let mut join_set = JoinSet::new();
714
715 let self_clone = fs.clone();
717 self_clone.write_inode_to_storage(&attr).await?;
718
719 match attr.kind {
720 FileType::RegularFile => {
721 let self_clone = fs.clone();
722 join_set.spawn(async move {
723 let file = File::create(self_clone.contents_path(attr.ino))?;
725 file.sync_all()?;
729 File::open(
730 self_clone
731 .contents_path(attr.ino)
732 .parent()
733 .expect("oops, we don't have a parent"),
734 )?
735 .sync_all()?;
736 Ok::<(), FsError>(())
737 });
738 }
739 FileType::Directory => {
740 let self_clone = fs.clone();
741 let attr_clone = attr;
742 join_set.spawn(async move {
743 let contents_dir = self_clone.contents_path(attr.ino);
745 fs::create_dir(contents_dir.clone())?;
746 fs::create_dir(contents_dir.join(LS_DIR))?;
748 fs::create_dir(contents_dir.join(HASH_DIR))?;
751
752 self_clone
754 .insert_directory_entry(
755 attr_clone.ino,
756 &DirectoryEntry {
757 ino: attr_clone.ino,
758 name: SecretString::new(Box::new("$.".into())),
759 kind: FileType::Directory,
760 },
761 )
762 .await?;
763 self_clone
764 .insert_directory_entry(
765 attr_clone.ino,
766 &DirectoryEntry {
767 ino: parent,
768 name: SecretString::new(Box::new("$..".into())),
769 kind: FileType::Directory,
770 },
771 )
772 .await?;
773 Ok::<(), FsError>(())
774 });
775 }
776 }
777
778 let self_clone = fs.clone();
780 let attr_clone = attr;
781 join_set.spawn(async move {
782 self_clone
783 .insert_directory_entry(
784 parent,
785 &DirectoryEntry {
786 ino: attr_clone.ino,
787 name: name_clone,
788 kind: attr_clone.kind,
789 },
790 )
791 .await?;
792 Ok::<(), FsError>(())
793 });
794
795 let self_clone = fs.clone();
796 join_set.spawn(async move {
797 let now = SystemTime::now();
798 self_clone
799 .set_attr(
800 parent,
801 SetFileAttr::default()
802 .with_mtime(now)
803 .with_ctime(now)
804 .with_atime(now),
805 )
806 .await?;
807 Ok::<(), FsError>(())
808 });
809
810 while let Some(res) = join_set.join_next().await {
812 res??;
813 }
814
815 let self_clone = fs.clone();
816 let handle = if attr.kind == FileType::RegularFile {
817 if read || write {
818 self_clone.open(attr.ino, read, write).await?
819 } else {
820 0
822 }
823 } else {
824 0
826 };
827
828 Ok((handle, attr))
829 })
830 .await?
831 }
832
833 #[allow(clippy::missing_panics_doc)]
834 #[allow(clippy::missing_errors_doc)]
835 pub async fn find_by_name(
836 &self,
837 parent: u64,
838 name: &SecretString,
839 ) -> FsResult<Option<FileAttr>> {
840 if !self.exists(parent) {
841 return Err(FsError::InodeNotFound);
842 }
843 if !self.is_dir(parent) {
844 return Err(FsError::InvalidInodeType);
845 }
846 let hash = crypto::hash_file_name(name);
847 let hash_path = self.contents_path(parent).join(HASH_DIR).join(hash);
848 if !hash_path.is_file() {
849 return Ok(None);
850 }
851 let lock = self
852 .serialize_dir_entries_hash_locks
853 .get_or_insert_with(hash_path.to_str().unwrap().to_owned(), || {
854 RwLock::new(false)
855 });
856 let guard = lock.read().await;
857 let (ino, _, _): (u64, FileType, String) = bincode::deserialize_from(crypto::create_read(
858 File::open(hash_path)?,
859 self.cipher,
860 &*self.key.get().await?,
861 ))?;
862 drop(guard);
863 self.get_inode_from_cache_or_storage(ino).await.map(Some)
864 }
865
866 #[allow(clippy::missing_errors_doc)]
868 pub fn len(&self, ino: u64) -> FsResult<usize> {
869 if !self.is_dir(ino) {
870 return Err(FsError::InvalidInodeType);
871 }
872 let mut count = fs::read_dir(self.contents_path(ino).join(LS_DIR))?.count();
873 if ino == ROOT_INODE {
874 count -= 1;
876 } else {
877 count -= 2;
879 }
880 Ok(count)
881 }
882
883 #[allow(clippy::missing_panics_doc)]
885 #[allow(clippy::missing_errors_doc)]
886 pub async fn remove_dir(&self, parent: u64, name: &SecretString) -> FsResult<()> {
887 if self.read_only {
888 return Err(FsError::ReadOnly);
889 }
890 if !self.is_dir(parent) {
891 return Err(FsError::InvalidInodeType);
892 }
893
894 if !self.exists_by_name(parent, name)? {
895 return Err(FsError::NotFound("name not found"));
896 }
897
898 let attr = self
899 .find_by_name(parent, name)
900 .await?
901 .ok_or(FsError::NotFound("name not found"))?;
902 if !matches!(attr.kind, FileType::Directory) {
903 return Err(FsError::InvalidInodeType);
904 }
905 if self.len(attr.ino)? > 0 {
907 return Err(FsError::NotEmpty);
908 }
909 let self_clone = self
910 .self_weak
911 .lock()
912 .unwrap()
913 .as_ref()
914 .unwrap()
915 .upgrade()
916 .unwrap();
917 let name_clone = name.clone();
918 NOD_RT
919 .spawn(async move {
920 {
922 let lock = self_clone
923 .serialize_inode_locks
924 .get_or_insert_with(attr.ino, || RwLock::new(false));
925 let _guard = lock.write();
926 fs::remove_file(self_clone.ino_file(attr.ino))?;
927 }
928
929 fs::remove_dir_all(self_clone.contents_path(attr.ino))?;
931 self_clone
933 .remove_directory_entry(parent, &name_clone)
934 .await?;
935 self_clone
937 .attr_cache
938 .get()
939 .await?
940 .write()
941 .await
942 .demote(&attr.ino);
943
944 let now = SystemTime::now();
945 self_clone
946 .set_attr(
947 parent,
948 SetFileAttr::default()
949 .with_mtime(now)
950 .with_ctime(now)
951 .with_atime(now),
952 )
953 .await?;
954
955 Ok(())
956 })
957 .await?
958 }
959
960 #[allow(clippy::missing_panics_doc)]
962 #[allow(clippy::missing_errors_doc)]
963 pub async fn remove_file(&self, parent: u64, name: &SecretString) -> FsResult<()> {
964 if self.read_only {
965 return Err(FsError::ReadOnly);
966 }
967 if !self.is_dir(parent) {
968 return Err(FsError::InvalidInodeType);
969 }
970 if !self.exists_by_name(parent, name)? {
971 return Err(FsError::NotFound("name not found"));
972 }
973
974 let attr = self
975 .find_by_name(parent, name)
976 .await?
977 .ok_or(FsError::NotFound("name not found"))?;
978 if !matches!(attr.kind, FileType::RegularFile) {
979 return Err(FsError::InvalidInodeType);
980 }
981 let self_clone = self
983 .self_weak
984 .lock()
985 .unwrap()
986 .as_ref()
987 .unwrap()
988 .upgrade()
989 .unwrap();
990 let name_clone = name.clone();
991 NOD_RT
992 .spawn(async move {
993 {
995 let lock = self_clone
996 .serialize_inode_locks
997 .get_or_insert_with(attr.ino, || RwLock::new(false));
998 let _guard = lock.write();
999 fs::remove_file(self_clone.ino_file(attr.ino))?;
1000 }
1001
1002 fs::remove_file(self_clone.contents_path(attr.ino))?;
1004 self_clone
1006 .remove_directory_entry(parent, &name_clone)
1007 .await?;
1008 self_clone
1010 .attr_cache
1011 .get()
1012 .await?
1013 .write()
1014 .await
1015 .demote(&attr.ino);
1016
1017 let now = SystemTime::now();
1018 self_clone
1019 .set_attr(
1020 parent,
1021 SetFileAttr::default()
1022 .with_mtime(now)
1023 .with_ctime(now)
1024 .with_atime(now),
1025 )
1026 .await?;
1027
1028 Ok(())
1029 })
1030 .await?
1031 }
1032
1033 #[allow(clippy::missing_panics_doc)]
1034 #[allow(clippy::missing_errors_doc)]
1035 pub fn exists_by_name(&self, parent: u64, name: &SecretString) -> FsResult<bool> {
1036 if !self.exists(parent) {
1037 return Err(FsError::InodeNotFound);
1038 }
1039 if !self.is_dir(parent) {
1040 return Err(FsError::InvalidInodeType);
1041 }
1042 let hash = crypto::hash_file_name(name);
1043 let hash_path = self.contents_path(parent).join(HASH_DIR).join(hash);
1044 Ok(hash_path.is_file())
1045 }
1046
1047 #[allow(clippy::missing_errors_doc)]
1048 pub async fn read_dir(&self, ino: u64) -> FsResult<DirectoryEntryIterator> {
1049 if !self.is_dir(ino) {
1050 return Err(FsError::InvalidInodeType);
1051 }
1052 let ls_dir = self.contents_path(ino).join(LS_DIR);
1053 if !ls_dir.is_dir() {
1054 return Err(FsError::InvalidInodeType);
1055 }
1056
1057 let iter = fs::read_dir(ls_dir)?;
1058 let set_attr = SetFileAttr::default().with_atime(SystemTime::now());
1059 self.set_attr(ino, set_attr).await?;
1060 Ok(self.create_directory_entry_iterator(iter).await)
1061 }
1062
1063 pub async fn read_dir_plus(&self, ino: u64) -> FsResult<DirectoryEntryPlusIterator> {
1065 if !self.is_dir(ino) {
1066 return Err(FsError::InvalidInodeType);
1067 }
1068 let ls_dir = self.contents_path(ino).join(LS_DIR);
1069 if !ls_dir.is_dir() {
1070 return Err(FsError::InvalidInodeType);
1071 }
1072
1073 let iter = fs::read_dir(ls_dir)?;
1074 let set_attr = SetFileAttr::default().with_atime(SystemTime::now());
1075 self.set_attr(ino, set_attr).await?;
1076 Ok(self.create_directory_entry_plus_iterator(iter).await)
1077 }
1078
1079 async fn create_directory_entry_plus(
1080 &self,
1081 entry: io::Result<DirEntry>,
1082 ) -> FsResult<DirectoryEntryPlus> {
1083 let entry = self.create_directory_entry(entry).await?;
1084 let lock = self.serialize_inode_locks.clone();
1085 let lock_ino = lock.get_or_insert_with(entry.ino, || RwLock::new(false));
1086 let _ino_guard = lock_ino.read();
1087 let attr = self.get_inode_from_cache_or_storage(entry.ino).await?;
1088 Ok(DirectoryEntryPlus {
1089 ino: entry.ino,
1090 name: entry.name,
1091 kind: entry.kind,
1092 attr,
1093 })
1094 }
1095
1096 async fn create_directory_entry_plus_iterator(
1097 &self,
1098 read_dir: ReadDir,
1099 ) -> DirectoryEntryPlusIterator {
1100 #[allow(clippy::cast_possible_truncation)]
1101 let futures: Vec<_> = read_dir
1102 .into_iter()
1103 .map(|entry| {
1104 let fs = {
1105 self.self_weak
1106 .lock()
1107 .unwrap()
1108 .as_ref()
1109 .unwrap()
1110 .upgrade()
1111 .unwrap()
1112 };
1113 DIR_ENTRIES_RT.spawn(async move { fs.create_directory_entry_plus(entry).await })
1114 })
1115 .collect();
1116
1117 let mut res = VecDeque::with_capacity(futures.len());
1119 for f in futures {
1120 res.push_back(f.await.unwrap());
1121 }
1122 DirectoryEntryPlusIterator(res)
1123 }
1124
1125 async fn create_directory_entry(
1126 &self,
1127 entry: io::Result<DirEntry>,
1128 ) -> FsResult<DirectoryEntry> {
1129 if entry.is_err() {
1130 return Err(entry.err().unwrap().into());
1131 }
1132 if let Err(e) = entry {
1133 error!(err = %e, "reading directory entry");
1134 return Err(e.into());
1135 }
1136 let entry = entry.unwrap();
1137 let name = entry.file_name().to_string_lossy().to_string();
1138 let name = {
1139 if name == "$." {
1140 SecretString::new(Box::new(".".into()))
1141 } else if name == "$.." {
1142 SecretString::from_str("..").unwrap()
1143 } else {
1144 let lock = self.get_dir_entries_name_cache().await?;
1146 let mut cache = lock.lock().await;
1147 if let Some(name_cached) = cache.get(&name).cloned() {
1148 name_cached
1149 } else {
1150 drop(cache);
1151 if let Ok(decrypted_name) =
1152 crypto::decrypt_file_name(&name, self.cipher, &*self.key.get().await?)
1153 .map_err(|err| {
1154 error!(err = %err, "decrypting file name");
1155 err
1156 })
1157 {
1158 lock.lock().await.put(name.clone(), decrypted_name.clone());
1159 decrypted_name
1160 } else {
1161 return Err(FsError::InvalidInput("invalid file name"));
1162 }
1163 }
1164 }
1165 };
1166
1167 self.validate_filename(&name)?;
1168
1169 let file_path = entry.path().to_str().unwrap().to_owned();
1170 let lock = self.dir_entries_meta_cache.get().await?;
1172 let mut cache = lock.lock().await;
1173 if let Some((ino, kind)) = cache.get(&file_path) {
1174 return Ok(DirectoryEntry {
1175 ino: *ino,
1176 name,
1177 kind: *kind,
1178 });
1179 }
1180 drop(cache);
1181 let lock = self
1182 .serialize_dir_entries_ls_locks
1183 .get_or_insert_with(file_path.clone(), || RwLock::new(false));
1184 let guard = lock.read().await;
1185 let file = File::open(entry.path())?;
1186 let res: bincode::Result<(u64, FileType)> = bincode::deserialize_from(crypto::create_read(
1187 file,
1188 self.cipher,
1189 &*self.key.get().await?,
1190 ));
1191 drop(guard);
1192 if let Err(e) = res {
1193 error!(err = %e, "deserializing directory entry");
1194 return Err(e.into());
1195 }
1196 let (ino, kind): (u64, FileType) = res.unwrap();
1197 self.dir_entries_meta_cache
1199 .get()
1200 .await?
1201 .lock()
1202 .await
1203 .put(file_path, (ino, kind));
1204 Ok(DirectoryEntry { ino, name, kind })
1205 }
1206
1207 async fn get_dir_entries_name_cache(
1208 &self,
1209 ) -> FsResult<Arc<Mutex<LruCache<String, SecretString>>>> {
1210 self.dir_entries_name_cache.get().await
1211 }
1212
1213 async fn create_directory_entry_iterator(&self, read_dir: ReadDir) -> DirectoryEntryIterator {
1214 #[allow(clippy::cast_possible_truncation)]
1215 let futures: Vec<_> = read_dir
1216 .into_iter()
1217 .map(|entry| {
1218 let fs = {
1219 self.self_weak
1220 .lock()
1221 .unwrap()
1222 .as_ref()
1223 .unwrap()
1224 .upgrade()
1225 .unwrap()
1226 };
1227 DIR_ENTRIES_RT.spawn(async move { fs.create_directory_entry(entry).await })
1228 })
1229 .collect();
1230
1231 let mut res = VecDeque::with_capacity(futures.len());
1233 for f in futures {
1234 res.push_back(f.await.unwrap());
1235 }
1236 DirectoryEntryIterator(res)
1237 }
1238
1239 #[allow(clippy::missing_errors_doc)]
1240 async fn get_inode_from_storage(&self, ino: u64) -> FsResult<FileAttr> {
1241 let lock = self
1242 .serialize_inode_locks
1243 .get_or_insert_with(ino, || RwLock::new(false));
1244 let _guard = lock.read();
1245
1246 let path = self.ino_file(ino);
1247 if !path.is_file() {
1248 return Err(FsError::InodeNotFound);
1249 }
1250 let file = OpenOptions::new().read(true).open(path).map_err(|err| {
1251 error!(err = %err, "opening file");
1252 FsError::InodeNotFound
1253 })?;
1254 Ok(bincode::deserialize_from(crypto::create_read(
1255 file,
1256 self.cipher,
1257 &*self.key.get().await?,
1258 ))?)
1259 }
1260
1261 async fn get_inode_from_cache_or_storage(&self, ino: u64) -> FsResult<FileAttr> {
1262 let lock = self.attr_cache.get().await?;
1263 let mut guard = lock.write().await;
1264 let attr = guard.get(&ino);
1265 if let Some(attr) = attr {
1266 Ok(*attr)
1267 } else {
1268 drop(guard);
1269 let attr = self.get_inode_from_storage(ino).await?;
1270 let mut guard = lock.write().await;
1271 guard.put(ino, attr);
1272 Ok(attr)
1273 }
1274 }
1275
1276 #[allow(clippy::missing_errors_doc)]
1278 pub async fn get_attr(&self, ino: u64) -> FsResult<FileAttr> {
1279 let mut attr = self.get_inode_from_cache_or_storage(ino).await?;
1280
1281 let open_reads = { self.opened_files_for_read.read().await.contains_key(&ino) };
1283 if open_reads {
1284 let fhs = self.opened_files_for_read.read().await.get(&ino).cloned();
1285 if let Some(fhs) = fhs {
1286 for fh in fhs {
1287 let lock = self.read_handles.read().await;
1288 if let Some(ctx) = lock.get(&fh) {
1289 let set_atr: SetFileAttr = ctx.lock().await.attr.clone().into();
1290 merge_attr(&mut attr, &set_atr, false);
1291 }
1292 }
1293 }
1294 }
1295
1296 let open_writes = { self.opened_files_for_write.read().await.contains_key(&ino) };
1298 if open_writes {
1299 let fh = self.opened_files_for_write.read().await.get(&ino).copied();
1300 if let Some(fh) = fh {
1301 let lock = self.write_handles.read().await;
1302 if let Some(ctx) = lock.get(&fh) {
1303 let ctx = ctx.lock().await;
1304 merge_attr(&mut attr, &ctx.attr.clone().into(), false);
1305 }
1306 }
1307 }
1308
1309 Ok(attr)
1310 }
1311
1312 pub async fn set_attr(&self, ino: u64, set_attr: SetFileAttr) -> FsResult<()> {
1314 if self.read_only {
1315 return Err(FsError::ReadOnly);
1316 }
1317 self.set_attr2(ino, set_attr, false).await
1318 }
1319
1320 async fn set_attr2(
1321 &self,
1322 ino: u64,
1323 set_attr: SetFileAttr,
1324 overwrite_size: bool,
1325 ) -> FsResult<()> {
1326 let serialize_update_lock = self
1327 .serialize_update_inode_locks
1328 .get_or_insert_with(ino, || Mutex::new(false));
1329 let _serialize_update_guard = serialize_update_lock.lock().await;
1330
1331 let mut attr = self.get_attr(ino).await?;
1332 merge_attr(&mut attr, &set_attr, overwrite_size);
1333 let now = SystemTime::now();
1334 attr.ctime = now;
1335 attr.atime = now;
1336
1337 self.write_inode_to_storage(&attr).await?;
1338
1339 Ok(())
1340 }
1341
1342 async fn write_inode_to_storage(&self, attr: &FileAttr) -> Result<(), FsError> {
1343 let lock = self
1344 .serialize_inode_locks
1345 .get_or_insert_with(attr.ino, || RwLock::new(false));
1346 let guard = lock.write().await;
1347 crypto::atomic_serialize_encrypt_into(
1348 &self.ino_file(attr.ino),
1349 attr,
1350 self.cipher,
1351 &*self.key.get().await?,
1352 )?;
1353 drop(guard);
1354 {
1356 let lock = self.attr_cache.get().await?;
1357 let mut guard = lock.write().await;
1358 guard.put(attr.ino, *attr);
1359 }
1360 Ok(())
1361 }
1362
1363 #[instrument(skip(self, buf), fields(len = %buf.len()), ret(level = Level::DEBUG))]
1368 #[allow(clippy::missing_errors_doc)]
1369 #[allow(clippy::cast_possible_truncation)]
1370 pub async fn read(
1371 &self,
1372 ino: u64,
1373 offset: u64,
1374 buf: &mut [u8],
1375 handle: u64,
1376 ) -> FsResult<usize> {
1377 if !self.exists(ino) {
1378 return Err(FsError::InodeNotFound);
1379 }
1380 if !self.is_file(ino) {
1381 return Err(FsError::InvalidInodeType);
1382 }
1383 if !self.read_handles.read().await.contains_key(&handle) {
1384 return Err(FsError::InvalidFileHandle);
1385 }
1386
1387 let _size = self.get_attr(ino).await?.size;
1388
1389 let lock = self
1390 .read_write_locks
1391 .get_or_insert_with(ino, || RwLock::new(false));
1392 let _read_guard = lock.read().await;
1393
1394 let guard = self.read_handles.read().await;
1395 let mut ctx = guard.get(&handle).unwrap().lock().await;
1396
1397 if ctx.ino != ino {
1398 return Err(FsError::InvalidFileHandle);
1399 }
1400 if self.is_dir(ino) {
1401 return Err(FsError::InvalidInodeType);
1402 }
1403 if buf.is_empty() {
1404 return Ok(0);
1406 }
1407
1408 let (_buf, len) = {
1410 let reader = ctx.reader.as_mut().unwrap();
1411
1412 reader.seek(SeekFrom::Start(offset)).map_err(|err| {
1413 error!(err = %err, "seeking");
1414 err
1415 })?;
1416 let pos = reader.stream_position().map_err(|err| {
1417 error!(err = %err, "getting position");
1418 err
1419 })?;
1420 if pos != offset {
1421 return Ok(0);
1423 }
1424 #[allow(clippy::cast_possible_truncation)]
1426 let buf = if offset + buf.len() as u64 > self.cipher.max_plaintext_len() as u64 {
1427 warn!("reading more than max block size, truncating");
1428 buf.split_at_mut(self.cipher.max_plaintext_len() - offset as usize)
1429 .0
1430 } else {
1431 buf
1432 };
1433 let len = stream_util::read(reader, buf).map_err(|err| {
1434 error!(err = %err, "reading");
1435 err
1436 })?;
1437 (buf, len)
1438 };
1439
1440 ctx.attr.atime = SystemTime::now();
1441 drop(ctx);
1442
1443 Ok(len)
1473 }
1474
1475 #[allow(clippy::missing_panics_doc)]
1476 #[allow(clippy::too_many_lines)]
1477 pub async fn release(&self, handle: u64) -> FsResult<()> {
1478 if handle == 0 {
1479 return Ok(());
1482 }
1483 let mut valid_fh = false;
1484
1485 let ctx = { self.read_handles.write().await.remove(&handle) };
1487 if let Some(ctx) = ctx {
1488 let ctx = ctx.lock().await;
1489
1490 {
1491 let mut opened_files_for_read = self.opened_files_for_read.write().await;
1492 opened_files_for_read
1493 .get_mut(&ctx.ino)
1494 .expect("handle is missing")
1495 .remove(&handle);
1496 if opened_files_for_read
1497 .get(&ctx.ino)
1498 .expect("handle is missing")
1499 .is_empty()
1500 {
1501 opened_files_for_read.remove(&ctx.ino);
1502 }
1503 }
1504
1505 let set_attr: SetFileAttr = ctx.attr.clone().into();
1508 let ino = ctx.ino;
1509 drop(ctx);
1510 self.set_attr(ino, set_attr).await?;
1511
1512 valid_fh = true;
1513 }
1514
1515 let ctx = { self.write_handles.write().await.remove(&handle) };
1517 if let Some(ctx) = ctx {
1518 if self.read_only {
1519 return Err(FsError::ReadOnly);
1520 }
1521 let mut ctx = ctx.lock().await;
1522
1523 let mut writer = ctx.writer.take().unwrap();
1524 let lock = self
1525 .read_write_locks
1526 .get_or_insert_with(ctx.ino, || RwLock::new(false));
1527 let write_guard = lock.write().await;
1528 let file = writer.finish()?;
1529 file.sync_all()?;
1530 File::open(self.contents_path(ctx.ino).parent().unwrap())?.sync_all()?;
1531 let ino = ctx.ino;
1534 let attr = ctx.attr.clone();
1535 drop(ctx);
1536 self.set_attr(ino, attr.into()).await?;
1537 let attr = self.get_attr(ino).await?;
1538 {
1539 let write_size = self
1540 .sizes_write
1541 .lock()
1542 .await
1543 .get(&ino)
1544 .unwrap()
1545 .load(Ordering::SeqCst);
1546 info!("written for {ino} {write_size}");
1547 if attr.size != write_size {
1548 }
1550 let requested_read = self
1551 .requested_read
1552 .lock()
1553 .await
1554 .get(&ino)
1555 .unwrap()
1556 .load(Ordering::SeqCst);
1557 let read = self
1558 .sizes_read
1559 .lock()
1560 .await
1561 .get(&ino)
1562 .unwrap()
1563 .load(Ordering::SeqCst);
1564 if requested_read != read {
1565 error!(
1566 "size mismatch read, size {} requested {} read {}",
1567 attr.size, requested_read, read
1568 );
1569 }
1570 }
1571 self.sizes_write.lock().await.remove(&ino);
1572 self.sizes_read.lock().await.remove(&ino);
1573 self.requested_read.lock().await.remove(&ino);
1574 drop(write_guard);
1575 self.opened_files_for_write.write().await.remove(&ino);
1576 self.reset_handles(ino, Some(handle), true).await?;
1577
1578 valid_fh = true;
1579 }
1580
1581 if !valid_fh {
1582 return Err(FsError::InvalidFileHandle);
1583 }
1584 Ok(())
1585 }
1586
1587 pub async fn is_read_handle(&self, fh: u64) -> bool {
1589 self.read_handles.read().await.contains_key(&fh)
1590 }
1591
1592 pub async fn is_write_handle(&self, fh: u64) -> bool {
1594 self.write_handles.read().await.contains_key(&fh)
1595 }
1596
1597 #[instrument(skip(self, buf), fields(len = %buf.len()), ret(level = Level::DEBUG))]
1603 pub async fn write(&self, ino: u64, offset: u64, buf: &[u8], handle: u64) -> FsResult<usize> {
1604 if self.read_only {
1605 return Err(FsError::ReadOnly);
1606 }
1607 if !self.exists(ino) {
1608 return Err(FsError::InodeNotFound);
1609 }
1610 if !self.is_file(ino) {
1611 return Err(FsError::InvalidInodeType);
1612 }
1613 {
1614 if !self.write_handles.read().await.contains_key(&handle) {
1615 return Err(FsError::InvalidFileHandle);
1616 }
1617 }
1618 {
1619 let guard = self.write_handles.read().await;
1620 let ctx = guard.get(&handle).unwrap().lock().await;
1621 if ctx.ino != ino {
1622 return Err(FsError::InvalidFileHandle);
1623 }
1624 }
1625 if buf.is_empty() {
1626 return Ok(0);
1628 }
1629
1630 let lock = self
1631 .read_write_locks
1632 .get_or_insert_with(ino, || RwLock::new(false));
1633 let write_guard = lock.write().await;
1634
1635 let guard = self.write_handles.read().await;
1636 let mut ctx = guard.get(&handle).unwrap().lock().await;
1637
1638 let (pos, len) = {
1640 if offset > self.cipher.max_plaintext_len() as u64 {
1641 return Err(FsError::MaxFilesizeExceeded(
1642 self.cipher.max_plaintext_len(),
1643 ));
1644 }
1645 let writer = ctx.writer.as_mut().unwrap();
1646 let pos = writer.seek(SeekFrom::Start(offset)).map_err(|err| {
1647 error!(err = %err, "seeking");
1648 err
1649 })?;
1650 if offset != pos {
1651 return Ok(0);
1653 }
1654 #[allow(clippy::cast_possible_truncation)]
1656 let buf = if offset + buf.len() as u64 > self.cipher.max_plaintext_len() as u64 {
1657 warn!("writing more than max block size, truncating");
1658 &buf[..(self.cipher.max_plaintext_len() - offset as usize)]
1659 } else {
1660 buf
1661 };
1662 let len = writer.write(buf).map_err(|err| {
1663 error!(err = %err, "writing");
1664 err
1665 })?;
1666 (writer.stream_position()?, len)
1667 };
1668
1669 if pos > ctx.attr.size {
1671 debug!("setting new file size {}", pos);
1673 ctx.attr.size = pos;
1674 }
1675 let now = SystemTime::now();
1676 ctx.attr.mtime = now;
1677 ctx.attr.ctime = now;
1678 ctx.attr.atime = now;
1679 drop(ctx);
1680
1681 drop(write_guard);
1682 self.reset_handles(ino, Some(handle), true).await?;
1683
1684 self.sizes_write
1685 .lock()
1686 .await
1687 .get_mut(&ino)
1688 .unwrap()
1689 .fetch_add(len as u64, Ordering::SeqCst);
1690 if buf.len() != len {
1691 }
1696 Ok(len)
1707 }
1708
1709 #[allow(clippy::missing_panics_doc)]
1711 pub async fn flush(&self, handle: u64) -> FsResult<()> {
1712 if self.read_only {
1713 return Err(FsError::ReadOnly);
1714 }
1715 if handle == 0 {
1716 return Ok(());
1718 }
1719 let lock = self.read_handles.read().await;
1720 let mut valid_fh = lock.get(&handle).is_some();
1721 let lock = self.write_handles.read().await;
1722 if let Some(ctx) = lock.get(&handle) {
1723 let mut ctx = ctx.lock().await;
1724 let lock = self
1725 .read_write_locks
1726 .get_or_insert_with(ctx.ino, || RwLock::new(false));
1727 let write_guard = lock.write().await;
1728 ctx.writer.as_mut().expect("writer is missing").flush()?;
1729 File::open(self.contents_path(ctx.ino))?.sync_all()?;
1730 File::open(self.contents_path(ctx.ino).parent().unwrap())?.sync_all()?;
1731 drop(write_guard);
1732 let ino = ctx.ino;
1733 drop(ctx);
1734 self.reset_handles(ino, Some(handle), true).await?;
1735 valid_fh = true;
1736 }
1737
1738 if !valid_fh {
1739 return Err(FsError::InvalidFileHandle);
1740 }
1741
1742 Ok(())
1743 }
1744
1745 pub async fn copy_file_range(
1747 &self,
1748 file_range_req: &CopyFileRangeReq,
1749 size: usize,
1750 ) -> FsResult<usize> {
1751 if self.read_only {
1752 return Err(FsError::ReadOnly);
1753 }
1754 if self.is_dir(file_range_req.src_ino) || self.is_dir(file_range_req.dest_ino) {
1755 return Err(FsError::InvalidInodeType);
1756 }
1757
1758 let mut buf = vec![0; size];
1759 let len = self
1760 .read(
1761 file_range_req.src_ino,
1762 file_range_req.src_offset,
1763 &mut buf,
1764 file_range_req.src_fh,
1765 )
1766 .await?;
1767 if len == 0 {
1768 return Ok(0);
1769 }
1770 let mut copied = 0;
1771 while copied < size {
1772 let len = self
1773 .write(
1774 file_range_req.dest_ino,
1775 file_range_req.dest_offset,
1776 &buf[copied..len],
1777 file_range_req.dest_fh,
1778 )
1779 .await?;
1780 if len == 0 && copied < size {
1781 error!(len, "Failed to copy all read bytes");
1782 return Err(FsError::Other("Failed to copy all read bytes"));
1783 }
1784 copied += len;
1785 }
1786 Ok(len)
1787 }
1788
1789 #[allow(clippy::missing_panics_doc)]
1791 pub async fn open(&self, ino: u64, read: bool, write: bool) -> FsResult<u64> {
1792 if write && self.read_only {
1793 return Err(FsError::ReadOnly);
1794 }
1795 if !read && !write {
1796 return Err(FsError::InvalidInput(
1797 "read and write cannot be false at the same time",
1798 ));
1799 }
1800 if self.is_dir(ino) {
1801 return Err(FsError::InvalidInodeType);
1802 }
1803
1804 let mut handle: Option<u64> = None;
1805 if read {
1806 handle = Some(self.next_handle());
1807 self.do_with_read_handle(
1808 *handle.as_ref().unwrap(),
1809 ReadHandleContextOperation::Create { ino },
1810 )
1811 .await?;
1812 }
1813 if write {
1814 if self.opened_files_for_write.read().await.contains_key(&ino) {
1815 return Err(FsError::AlreadyOpenForWrite);
1816 }
1817 if handle.is_none() {
1818 handle = Some(self.next_handle());
1819 }
1820 let res = self
1821 .do_with_write_handle(
1822 *handle.as_ref().expect("handle is missing"),
1823 WriteHandleContextOperation::Create { ino },
1824 )
1825 .await;
1826 if res.is_err() && read {
1827 self.read_handles
1830 .write()
1831 .await
1832 .remove(handle.as_ref().unwrap());
1833 }
1834 res?;
1835 }
1836 let fh = handle.unwrap();
1837 self.sizes_write
1838 .lock()
1839 .await
1840 .entry(ino)
1841 .or_insert(AtomicU64::new(0));
1842 self.sizes_read
1843 .lock()
1844 .await
1845 .entry(ino)
1846 .or_insert(AtomicU64::new(0));
1847 self.requested_read
1848 .lock()
1849 .await
1850 .entry(ino)
1851 .or_insert(AtomicU64::new(0));
1852 Ok(fh)
1853 }
1854
1855 #[allow(clippy::missing_panics_doc)]
1857 #[allow(clippy::too_many_lines)]
1858 pub async fn set_len(&self, ino: u64, size: u64) -> FsResult<()> {
1859 if self.read_only {
1860 return Err(FsError::ReadOnly);
1861 }
1862 info!("truncate {ino} to {size}");
1863 let attr = self.get_attr(ino).await?;
1864 if matches!(attr.kind, FileType::Directory) {
1865 return Err(FsError::InvalidInodeType);
1866 }
1867
1868 if size == attr.size {
1869 return Ok(());
1871 }
1872
1873 let lock = self
1874 .read_write_locks
1875 .get_or_insert_with(ino, || RwLock::new(false));
1876 let _write_guard = lock.write().await;
1877
1878 self.flush_and_reset_writers(ino).await?;
1880
1881 let file_path = self.contents_path(ino);
1882 if size == 0 {
1883 debug!("truncate to zero");
1884 let file = File::create(&file_path)?;
1886 file.set_len(0)?;
1887 file.sync_all()?;
1888 } else {
1889 debug!("truncate size to {}", size.to_formatted_string(&Locale::en));
1890
1891 let mut file = fs_util::open_atomic_write(&file_path)?;
1892 {
1893 let mut reader = self.create_read(File::open(file_path.as_path())?).await?;
1895
1896 let mut writer = self.create_write(file).await?;
1897
1898 let len = if size > attr.size {
1899 attr.size
1901 } else {
1902 size
1904 };
1905 stream_util::copy_exact(&mut reader, &mut writer, len)?;
1906 if size > attr.size {
1907 stream_util::fill_zeros(&mut writer, size - attr.size)?;
1909 }
1910 file = writer.finish()?;
1911 }
1912 file.commit()?;
1913 }
1914 File::open(file_path.parent().unwrap())?.sync_all()?;
1915
1916 let now = SystemTime::now();
1917 let set_attr = SetFileAttr::default()
1918 .with_size(size)
1919 .with_mtime(now)
1920 .with_ctime(now)
1921 .with_atime(now);
1922 self.set_attr2(ino, set_attr, true).await?;
1923
1924 let attr = self.get_inode_from_storage(ino).await?;
1925 println!("attr 1: {:?}", attr.size);
1926 let attr = self.get_attr(ino).await?;
1927 println!("attr 1: {:?}", attr.size);
1928
1929 self.reset_handles(attr.ino, None, false).await?;
1931
1932 let attr = self.get_inode_from_storage(ino).await?;
1933 println!("attr 2: {:?}", attr.size);
1934 let attr = self.get_attr(ino).await?;
1935 println!("attr 2: {:?}", attr.size);
1936
1937 if size != attr.size {
1938 error!("error truncating file expected {size} actual {}", attr.size);
1939 }
1940
1941 Ok(())
1942 }
1943
1944 async fn flush_and_reset_writers(&self, ino: u64) -> FsResult<()> {
1951 if self.read_only {
1952 return Err(FsError::ReadOnly);
1953 }
1954 let opened_files_for_write_guard = self.opened_files_for_write.read().await;
1955 let handle = opened_files_for_write_guard.get(&ino);
1956 if let Some(handle) = handle {
1957 let write_handles_guard = self.write_handles.write().await;
1958 let ctx = write_handles_guard.get(handle);
1959 if let Some(lock) = ctx {
1960 let mut ctx = lock.lock().await;
1961
1962 let mut writer = ctx.writer.take().unwrap();
1963 let file = writer.finish()?;
1964 file.sync_all()?;
1965 File::open(self.contents_path(ctx.ino).parent().unwrap())?.sync_all()?;
1966 let handle = *handle;
1967 let set_attr: SetFileAttr = ctx.attr.clone().into();
1968 drop(ctx);
1969 drop(opened_files_for_write_guard);
1970 drop(write_handles_guard);
1971 self.set_attr(ino, set_attr).await?;
1972 self.reset_handles(ino, Some(handle), true).await?;
1973 let write_handles_guard = self.write_handles.write().await;
1974 let mut ctx = write_handles_guard.get(&handle).unwrap().lock().await;
1975 let writer = self
1976 .create_write_seek(
1977 OpenOptions::new()
1978 .read(true)
1979 .write(true)
1980 .open(self.contents_path(ino))?,
1981 )
1982 .await?;
1983 ctx.writer = Some(Box::new(writer));
1984 let attr = self.get_inode_from_storage(ino).await?;
1985 ctx.attr = attr.into();
1986 }
1987 }
1988 Ok(())
1989 }
1990
1991 #[allow(clippy::missing_panics_doc)]
1992 pub async fn rename(
1993 &self,
1994 parent: u64,
1995 name: &SecretString,
1996 new_parent: u64,
1997 new_name: &SecretString,
1998 ) -> FsResult<()> {
1999 if self.read_only {
2000 return Err(FsError::ReadOnly);
2001 }
2002 if !self.exists(parent) {
2003 return Err(FsError::InodeNotFound);
2004 }
2005 if !self.is_dir(parent) {
2006 return Err(FsError::InvalidInodeType);
2007 }
2008 if !self.exists(new_parent) {
2009 return Err(FsError::InodeNotFound);
2010 }
2011 if !self.is_dir(new_parent) {
2012 return Err(FsError::InvalidInodeType);
2013 }
2014 if !self.exists_by_name(parent, name)? {
2015 return Err(FsError::NotFound("name not found"));
2016 }
2017 self.validate_filename(new_name)?;
2018
2019 if parent == new_parent && name.expose_secret() == new_name.expose_secret() {
2020 return Ok(());
2022 }
2023
2024 if let Ok(Some(new_attr)) = self.find_by_name(new_parent, new_name).await {
2026 if new_attr.kind == FileType::Directory && self.len(new_attr.ino)? > 0 {
2027 return Err(FsError::NotEmpty);
2028 }
2029 }
2030
2031 let attr = self
2032 .find_by_name(parent, name)
2033 .await?
2034 .ok_or(FsError::NotFound("name not found"))?;
2035 self.remove_directory_entry(parent, name).await?;
2037 if self.exists_by_name(new_parent, new_name)? {
2039 self.remove_directory_entry(new_parent, new_name).await?;
2040 }
2041 self.insert_directory_entry(
2043 new_parent,
2044 &DirectoryEntry {
2045 ino: attr.ino,
2046 name: new_name.clone(),
2047 kind: attr.kind,
2048 },
2049 )
2050 .await?;
2051
2052 if attr.kind == FileType::Directory {
2053 self.insert_directory_entry(
2055 attr.ino,
2056 &DirectoryEntry {
2057 ino: new_parent,
2058 name: SecretBox::new(Box::new("$..".to_owned())),
2059 kind: FileType::Directory,
2060 },
2061 )
2062 .await?;
2063 }
2064
2065 let now = SystemTime::now();
2066 let set_attr = SetFileAttr::default()
2067 .with_mtime(now)
2068 .with_ctime(now)
2069 .with_atime(now);
2070 self.set_attr(parent, set_attr).await?;
2071
2072 let set_attr = SetFileAttr::default()
2073 .with_mtime(now)
2074 .with_ctime(now)
2075 .with_atime(now);
2076 self.set_attr(new_parent, set_attr).await?;
2077
2078 let set_attr = SetFileAttr::default().with_ctime(now).with_atime(now);
2079 self.set_attr(attr.ino, set_attr).await?;
2080
2081 Ok(())
2082 }
2083
2084 pub async fn create_write<W: CryptoInnerWriter + Seek + Send + Sync + 'static>(
2086 &self,
2087 file: W,
2088 ) -> FsResult<impl CryptoWrite<W>> {
2089 Ok(crypto::create_write(
2090 file,
2091 self.cipher,
2092 &*self.key.get().await?,
2093 ))
2094 }
2095
2096 pub async fn create_write_seek<W: Write + Seek + Read + Send + Sync + 'static>(
2098 &self,
2099 file: W,
2100 ) -> FsResult<impl CryptoWriteSeek<W>> {
2101 Ok(crypto::create_write_seek(
2102 file,
2103 self.cipher,
2104 &*self.key.get().await?,
2105 ))
2106 }
2107
2108 pub async fn create_read<R: Read + Send + Sync>(
2110 &self,
2111 reader: R,
2112 ) -> FsResult<impl CryptoRead<R>> {
2113 Ok(crypto::create_read(
2114 reader,
2115 self.cipher,
2116 &*self.key.get().await?,
2117 ))
2118 }
2119
2120 pub async fn create_read_seek<R: Read + Seek + Send + Sync>(
2122 &self,
2123 reader: R,
2124 ) -> FsResult<impl CryptoReadSeek<R>> {
2125 Ok(crypto::create_read_seek(
2126 reader,
2127 self.cipher,
2128 &*self.key.get().await?,
2129 ))
2130 }
2131
2132 pub async fn passwd(
2134 data_dir: &Path,
2135 old_password: SecretString,
2136 new_password: SecretString,
2137 cipher: Cipher,
2138 ) -> FsResult<()> {
2139 check_structure(data_dir, false).await?;
2140 let salt: Vec<u8> = bincode::deserialize_from(File::open(
2142 data_dir.join(SECURITY_DIR).join(KEY_SALT_FILENAME),
2143 )?)?;
2144 let initial_key = crypto::derive_key(&old_password, cipher, &salt)?;
2145 let enc_file = data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME);
2146 let reader = crypto::create_read(File::open(enc_file)?, cipher, &initial_key);
2147 let key: Vec<u8> =
2148 bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword)?;
2149 let key = SecretBox::new(Box::new(key));
2150 let new_key = crypto::derive_key(&new_password, cipher, &salt)?;
2152 crypto::atomic_serialize_encrypt_into(
2153 &data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME),
2154 &*key.expose_secret(),
2155 cipher,
2156 &new_key,
2157 )?;
2158 Ok(())
2159 }
2160
2161 fn next_handle(&self) -> u64 {
2162 self.current_handle
2163 .fetch_add(1, std::sync::atomic::Ordering::SeqCst)
2164 }
2165
2166 async fn reset_handles(
2175 &self,
2176 ino: u64,
2177 skip_write_fh: Option<u64>,
2178 save_attr: bool,
2179 ) -> FsResult<()> {
2180 let path = self.contents_path(ino);
2181
2182 let lock = self.opened_files_for_read.read().await;
2184 if let Some(set) = lock.get(&ino) {
2185 for handle in set.iter().filter(|h| skip_write_fh != Some(**h)) {
2186 let guard = self.read_handles.read().await;
2187 let ctx = guard.get(handle).unwrap().lock().await;
2188 let set_attr: SetFileAttr = ctx.attr.clone().into();
2189 drop(ctx);
2190 self.set_attr(ino, set_attr).await?;
2191 let attr = self.get_inode_from_storage(ino).await?;
2192 let mut ctx = guard.get(handle).unwrap().lock().await;
2193 let reader = self.create_read_seek(File::open(&path)?).await?;
2194 ctx.reader = Some(Box::new(reader));
2195 ctx.attr = attr.into();
2196 }
2197 }
2198
2199 let lock = self.opened_files_for_write.read().await;
2201 if let Some(fh) = lock.get(&ino) {
2202 if let Some(handle) = skip_write_fh {
2203 if *fh == handle {
2204 return Ok(());
2205 }
2206 }
2207 let lock = self.write_handles.read().await;
2208 if let Some(lock) = lock.get(fh) {
2209 let mut ctx = lock.lock().await;
2210 let writer = ctx.writer.as_mut().unwrap();
2211 let file = writer.finish()?;
2212 file.sync_all()?;
2213 File::open(self.contents_path(ctx.ino).parent().unwrap())?.sync_all()?;
2214 let set_attr: Option<SetFileAttr> = if save_attr {
2215 Some(ctx.attr.clone().into())
2216 } else {
2217 None
2218 };
2219 drop(ctx);
2220 if let Some(set_attr) = set_attr {
2221 self.set_attr(ino, set_attr).await?;
2222 }
2223 let writer = self
2224 .create_write_seek(OpenOptions::new().read(true).write(true).open(&path)?)
2225 .await?;
2226 let mut ctx = lock.lock().await;
2227 ctx.writer = Some(Box::new(writer));
2228 let attr = self.get_inode_from_storage(ino).await?;
2229 ctx.attr = attr.into();
2230 }
2231 }
2232
2233 Ok(())
2234 }
2235
2236 async fn do_with_read_handle(
2237 &self,
2238 handle: u64,
2239 op: ReadHandleContextOperation,
2240 ) -> FsResult<()> {
2241 let ino = op.get_ino();
2242 let path = self.contents_path(ino);
2243 let attr = self.get_inode_from_storage(ino).await?;
2244 match op {
2245 ReadHandleContextOperation::Create { ino } => {
2246 let attr: TimesFileAttr = attr.into();
2247 let reader = self.create_read_seek(File::open(&path)?).await?;
2248 let ctx = ReadHandleContext {
2249 ino,
2250 attr,
2251 reader: Some(Box::new(reader)),
2252 };
2253 self.read_handles
2254 .write()
2255 .await
2256 .insert(handle, Mutex::new(ctx));
2257 self.opened_files_for_read
2258 .write()
2259 .await
2260 .entry(ino)
2261 .or_insert_with(HashSet::new)
2262 .insert(handle);
2263 }
2264 }
2265 Ok(())
2266 }
2267
2268 async fn do_with_write_handle(
2269 &self,
2270 handle: u64,
2271 op: WriteHandleContextOperation,
2272 ) -> FsResult<()> {
2273 let ino = op.get_ino();
2274 let path = self.contents_path(ino);
2275 match op {
2276 WriteHandleContextOperation::Create { ino } => {
2277 let attr = self.get_attr(ino).await?.into();
2278 let writer = self
2279 .create_write_seek(OpenOptions::new().read(true).write(true).open(&path)?)
2280 .await?;
2281 let ctx = WriteHandleContext {
2282 ino,
2283 attr,
2284 writer: Some(Box::new(writer)),
2285 };
2286 self.write_handles
2287 .write()
2288 .await
2289 .insert(handle, Mutex::new(ctx));
2290 self.opened_files_for_write
2291 .write()
2292 .await
2293 .insert(ino, handle);
2294 }
2295 }
2296
2297 Ok(())
2298 }
2299
2300 async fn ensure_root_exists(&self) -> FsResult<()> {
2301 if !self.exists(ROOT_INODE) {
2302 let mut attr: FileAttr = CreateFileAttr {
2303 kind: FileType::Directory,
2304 perm: 0o755,
2305 uid: 0,
2306 gid: 0,
2307 rdev: 0,
2308 flags: 0,
2309 }
2310 .into();
2311 attr.ino = ROOT_INODE;
2312 #[cfg(any(target_os = "linux", target_os = "macos"))]
2313 unsafe {
2314 attr.uid = libc::getuid();
2315 attr.gid = libc::getgid();
2316 }
2317
2318 self.write_inode_to_storage(&attr).await?;
2319
2320 fs::create_dir(self.contents_path(attr.ino))?;
2322 fs::create_dir(self.contents_path(attr.ino).join(LS_DIR))?;
2323 fs::create_dir(self.contents_path(attr.ino).join(HASH_DIR))?;
2324
2325 self.insert_directory_entry(
2327 attr.ino,
2328 &DirectoryEntry {
2329 ino: attr.ino,
2330 name: SecretString::from_str("$.").unwrap(),
2331 kind: FileType::Directory,
2332 },
2333 )
2334 .await?;
2335 }
2336
2337 Ok(())
2338 }
2339
2340 async fn insert_directory_entry(
2341 &self,
2342 ino_contents_dir: u64,
2343 entry: &DirectoryEntry,
2344 ) -> FsResult<()> {
2345 let parent_path = self.contents_path(ino_contents_dir);
2346 let encrypted_name =
2347 crypto::encrypt_file_name(&entry.name, self.cipher, &*self.key.get().await?)?;
2348 let self_clone = self
2350 .self_weak
2351 .lock()
2352 .unwrap()
2353 .as_ref()
2354 .unwrap()
2355 .upgrade()
2356 .unwrap();
2357 let parent_path_clone = parent_path.clone();
2358 let encrypted_name_clone = encrypted_name.clone();
2359 let entry_clone = entry.clone();
2360 let h = tokio::spawn(async move {
2362 let file_path = parent_path_clone
2363 .join(LS_DIR)
2364 .join(encrypted_name_clone.clone());
2365 let lock = self_clone
2366 .serialize_dir_entries_ls_locks
2367 .get_or_insert_with(file_path.to_str().unwrap().to_owned(), || {
2368 RwLock::new(false)
2369 });
2370 let _guard = lock.write().await;
2371 let entry = (entry_clone.ino, entry_clone.kind);
2373 crypto::atomic_serialize_encrypt_into(
2374 &file_path,
2375 &entry,
2376 self_clone.cipher,
2377 &*self_clone.key.get().await?,
2378 )?;
2379 Ok::<(), FsError>(())
2380 });
2381 let self_clone = self
2383 .self_weak
2384 .lock()
2385 .unwrap()
2386 .as_ref()
2387 .unwrap()
2388 .upgrade()
2389 .unwrap();
2390 let entry_hash = entry.clone();
2391 tokio::spawn(async move {
2392 let name = crypto::hash_file_name(&entry_hash.name);
2393 let file_path = parent_path.join(HASH_DIR).join(name);
2394 let lock = self_clone
2395 .serialize_dir_entries_hash_locks
2396 .get_or_insert_with(file_path.to_str().unwrap().to_owned(), || {
2397 RwLock::new(false)
2398 });
2399 let _guard = lock.write().await;
2400 let entry = (entry_hash.ino, entry_hash.kind, encrypted_name);
2403 crypto::atomic_serialize_encrypt_into(
2404 &file_path,
2405 &entry,
2406 self_clone.cipher,
2407 &*self_clone.key.get().await?,
2408 )?;
2409 Ok::<(), FsError>(())
2410 })
2411 .await??;
2412 h.await??;
2413 Ok(())
2414 }
2415
2416 fn ino_file(&self, ino: u64) -> PathBuf {
2417 self.data_dir.join(INODES_DIR).join(ino.to_string())
2418 }
2419
2420 fn contents_path(&self, ino: u64) -> PathBuf {
2421 self.data_dir.join(CONTENTS_DIR).join(ino.to_string())
2422 }
2423
2424 async fn remove_directory_entry(&self, parent: u64, name: &SecretString) -> FsResult<()> {
2425 let parent_path = self.contents_path(parent);
2426 let name = crypto::hash_file_name(name);
2428 let path = parent_path.join(HASH_DIR).join(name);
2429 let lock = self
2430 .serialize_dir_entries_hash_locks
2431 .get_or_insert_with(path.to_str().unwrap().to_owned(), || RwLock::new(false));
2432 let guard = lock.write().await;
2433 let (_, _, name): (u64, FileType, String) =
2434 bincode::deserialize_from(crypto::create_read(
2435 File::open(path.clone())?,
2436 self.cipher,
2437 &*self.key.get().await?,
2438 ))?;
2439 fs::remove_file(path)?;
2440 drop(guard);
2441 let path = parent_path.join(LS_DIR).join(name);
2443 let lock = self
2444 .serialize_dir_entries_ls_locks
2445 .get_or_insert_with(path.to_str().unwrap().to_owned(), || RwLock::new(false));
2446 let _guard = lock.write().await;
2447 fs::remove_file(path)?;
2448 Ok(())
2449 }
2450
2451 fn generate_next_inode(&self) -> u64 {
2452 loop {
2453 let ino = crypto::create_rng().next_u64();
2454
2455 if ino <= ROOT_INODE {
2456 continue;
2457 }
2458 if self.exists(ino) {
2459 continue;
2460 }
2461
2462 return ino;
2463 }
2464 }
2465}
2466pub struct CopyFileRangeReq {
2467 src_ino: u64,
2468 src_offset: u64,
2469 dest_ino: u64,
2470 dest_offset: u64,
2471 src_fh: u64,
2472 dest_fh: u64,
2473}
2474
2475#[bon]
2476impl CopyFileRangeReq {
2477 #[builder]
2478 pub fn new(
2479 src_ino: u64,
2480 src_offset: u64,
2481 dest_ino: u64,
2482 dest_offset: u64,
2483 src_fh: u64,
2484 dest_fh: u64,
2485 ) -> Self {
2486 Self {
2487 src_ino,
2488 src_offset,
2489 dest_ino,
2490 dest_offset,
2491 src_fh,
2492 dest_fh,
2493 }
2494 }
2495}
2496
2497fn read_or_create_key(
2498 key_path: &PathBuf,
2499 salt_path: &PathBuf,
2500 password: &SecretString,
2501 cipher: Cipher,
2502) -> FsResult<SecretVec<u8>> {
2503 let salt = if salt_path.exists() {
2504 bincode::deserialize_from(File::open(salt_path)?).map_err(|_| FsError::InvalidPassword)?
2505 } else {
2506 let mut salt = vec![0; 16];
2507 crypto::create_rng().fill_bytes(&mut salt);
2508 let mut file = OpenOptions::new()
2509 .read(true)
2510 .write(true)
2511 .create(true)
2512 .truncate(true)
2513 .open(salt_path)?;
2514 bincode::serialize_into(&mut file, &salt)?;
2515 file.flush()?;
2516 file.sync_all()?;
2517 File::open(salt_path.parent().expect("oops, we don't have a parent"))?.sync_all()?;
2518 salt
2519 };
2520 let derived_key = crypto::derive_key(password, cipher, &salt)?;
2522 if key_path.exists() {
2523 let reader = crypto::create_read(File::open(key_path)?, cipher, &derived_key);
2525 let key: Vec<u8> =
2526 bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword)?;
2527 Ok(SecretBox::new(Box::new(key)))
2528 } else {
2529 let mut key: Vec<u8> = vec![];
2531 let key_len = cipher.key_len();
2532 key.resize(key_len, 0);
2533 crypto::create_rng().fill_bytes(&mut key);
2534 let mut writer = crypto::create_write(
2535 OpenOptions::new()
2536 .read(true)
2537 .write(true)
2538 .create(true)
2539 .truncate(true)
2540 .open(key_path)?,
2541 cipher,
2542 &derived_key,
2543 );
2544 bincode::serialize_into(&mut writer, &key)?;
2545 let file = writer.finish()?;
2546 file.sync_all()?;
2547 File::open(key_path.parent().unwrap())?.sync_all()?;
2548 Ok(SecretBox::new(Box::new(key)))
2549 }
2550}
2551
2552async fn ensure_structure_created(data_dir: &PathBuf) -> FsResult<()> {
2553 if data_dir.exists() {
2554 check_structure(data_dir, true).await?;
2555 } else {
2556 fs::create_dir_all(data_dir)?;
2557 }
2558
2559 let dirs = vec![INODES_DIR, CONTENTS_DIR, SECURITY_DIR];
2561 for dir in dirs {
2562 let path = data_dir.join(dir);
2563 if !path.exists() {
2564 fs::create_dir_all(path)?;
2565 }
2566 }
2567
2568 Ok(())
2569}
2570
2571async fn check_structure(data_dir: &Path, ignore_empty: bool) -> FsResult<()> {
2572 if !data_dir.exists() || !data_dir.is_dir() {
2573 return Err(FsError::InvalidDataDirStructure);
2574 }
2575 let mut vec = ReadDirStream::new(tokio::fs::read_dir(data_dir).await?)
2576 .try_collect::<Vec<_>>()
2577 .await?
2578 .iter()
2579 .map(|dir| dir.file_name().to_string_lossy().to_string())
2580 .collect::<Vec<String>>();
2581 if vec.is_empty() && ignore_empty {
2582 return Ok(());
2583 }
2584 if vec.len() != 3 {
2585 return Err(FsError::InvalidDataDirStructure);
2586 }
2587 vec.sort_unstable();
2589 let mut vec2 = vec![INODES_DIR, CONTENTS_DIR, SECURITY_DIR];
2590 vec2.sort_unstable();
2591 if vec != vec2
2592 || !data_dir.join(SECURITY_DIR).join(KEY_ENC_FILENAME).is_file()
2593 || !data_dir
2594 .join(SECURITY_DIR)
2595 .join(KEY_SALT_FILENAME)
2596 .is_file()
2597 {
2598 return Err(FsError::InvalidDataDirStructure);
2599 }
2600
2601 Ok(())
2602}
2603
2604fn merge_attr(attr: &mut FileAttr, set_attr: &SetFileAttr, overwrite_size: bool) {
2605 if let Some(size) = set_attr.size {
2606 if overwrite_size {
2607 attr.size = size;
2608 } else {
2609 attr.size = attr.size.max(size);
2610 }
2611 }
2612 if let Some(atime) = set_attr.atime {
2613 attr.atime = attr.atime.max(atime);
2614 }
2615 if let Some(mtime) = set_attr.mtime {
2616 attr.mtime = attr.mtime.max(mtime);
2617 }
2618 if let Some(ctime) = set_attr.ctime {
2619 attr.ctime = attr.ctime.max(ctime);
2620 }
2621 if let Some(crtime) = set_attr.crtime {
2622 attr.crtime = attr.crtime.max(crtime);
2623 }
2624 if let Some(perm) = set_attr.perm {
2625 attr.perm = perm;
2626 }
2627 if let Some(uid) = set_attr.uid {
2628 attr.uid = uid;
2629 }
2630 if let Some(gid) = set_attr.gid {
2631 attr.gid = gid;
2632 }
2633 if let Some(flags) = set_attr.flags {
2634 attr.flags = flags;
2635 }
2636}
2637
2638pub async fn write_all_string_to_fs(
2639 fs: &EncryptedFs,
2640 ino: u64,
2641 offset: u64,
2642 s: &str,
2643 fh: u64,
2644) -> FsResult<()> {
2645 write_all_bytes_to_fs(fs, ino, offset, s.as_bytes(), fh).await
2646}
2647
2648#[allow(clippy::missing_panics_doc)]
2649pub async fn write_all_bytes_to_fs(
2650 fs: &EncryptedFs,
2651 ino: u64,
2652 offset: u64,
2653 buf: &[u8],
2654 fh: u64,
2655) -> FsResult<()> {
2656 let mut pos = 0_usize;
2657 loop {
2658 let len = fs.write(ino, offset, &buf[pos..], fh).await?;
2659 pos += len;
2660 if pos == buf.len() {
2661 break;
2662 } else if len == 0 {
2663 return Err(FsError::Other("Failed to write all bytes"));
2664 }
2665 }
2666 fs.flush(fh).await?;
2667 Ok(())
2668}