1mod file;
17mod fs;
18
19pub use file::File;
20pub use fs::BijouFs;
21
22#[cfg(feature = "fuse")]
23mod fuse;
24#[cfg(feature = "fuse")]
25pub use fuse::BijouFuse;
26
27use crate::{
28 algo::Algorithm,
29 anyhow, bail,
30 crypto::{cast_key, crypto_error, split_nonce_tag, xchacha20_siv},
31 db::{consts, Database, DatabaseKey, RawKeyType},
32 error::ResultExt,
33 fs::{
34 config::Config, obtain_metadata, path::Component, DirItem, FileKind, Inode, LowLevelFile,
35 RawFileMeta, RawFileSystem, UnixPerms,
36 },
37 id_lock::IdLock,
38 path::Path,
39 serde_ext,
40 sodium::{
41 aead::XCHACHA20_POLY1305_IETF as AEAD,
42 kdf::BLAKE2B as KDF,
43 pwhash::{Limit, ARGON2_ID13 as PWHASH},
44 utils,
45 },
46 Context, ErrorKind, FileId, FileMeta, OpenOptions, Result, SecretBytes,
47};
48use bijou_rocksdb::{
49 DBIteratorWithThreadMode, DBPinnableSlice, DBWithThreadMode, Direction, IteratorMode,
50 ReadOptions, SingleThreaded, WriteBatch,
51};
52use chrono::{DateTime, Utc};
53use dashmap::DashMap;
54use ring::{
55 error::Unspecified,
56 hkdf::{self, KeyType, Prk},
57};
58use serde::{Deserialize, Serialize};
59use std::{
60 path::{Path as StdPath, PathBuf as StdPathBuf},
61 sync::{atomic::AtomicU32, Arc},
62};
63use tracing::{info, trace};
64
65pub const SYMBOLIC_MAX_DEPTH: u32 = 40;
66
67#[derive(Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69struct KeyStore {
70 version: u32,
71
72 #[serde(with = "serde_ext::base64")]
73 salt: [u8; PWHASH.salt_len],
74 #[serde(with = "serde_ext::base64")]
75 nonce: [u8; AEAD.nonce_len],
76 #[serde(with = "serde_ext::base64")]
77 tag: [u8; AEAD.tag_len],
78
79 ops_limit: usize,
80 mem_limit: usize,
81
82 #[serde(with = "serde_ext::base64")]
83 master_key: [u8; KDF.key_len],
84}
85
86pub struct Bijou {
90 path: StdPathBuf,
91
92 db: Arc<Database>,
93 raw_fs: Arc<dyn RawFileSystem + Send + Sync>,
94 algo: Arc<dyn Algorithm + Send + Sync>,
95
96 config: Config,
97
98 content_key: hkdf::Prk,
99 file_name_key: Option<SecretBytes>,
100
101 file_lock: Arc<IdLock<RawFileMeta>>,
109
110 file_open_counts: Arc<DashMap<FileId, Arc<AtomicU32>>>,
116}
117
118impl Bijou {
119 const KDF_CTX: [u8; 8] = *b"@bijoufs";
120
121 pub fn create(
131 path: impl AsRef<StdPath>,
132 password: impl Into<SecretBytes>,
133 config: Config,
134 ops_limit: Limit,
135 mem_limit: Limit,
136 ) -> Result<()> {
137 info!("creating Bijou");
138
139 let password = password.into();
140
141 let path = path.as_ref();
142 if path.exists() {
143 if !path.is_dir() || path.read_dir().wrap()?.next().is_some() {
144 bail!(@AlreadyExists "not an empty directory: {}", path.display());
145 }
146 } else {
147 std::fs::create_dir(path)
148 .context("failed to create directory")
149 .kind(ErrorKind::AlreadyExists)?;
150 }
151
152 let master_key = KDF.gen_key();
154 let prk = KDF.prk(master_key.clone(), Self::KDF_CTX.as_slice());
155 let config_key = prk.derive(0, AEAD.key_len)?;
156
157 let salt = utils::gen_rand_bytes::<{ PWHASH.salt_len }>();
158
159 let mut key = [0; AEAD.key_len];
160 PWHASH.derive_key(&mut key, &password, &salt, ops_limit, mem_limit)?;
161 drop(password);
162 let nonce = utils::gen_rand_bytes::<{ AEAD.nonce_len }>();
163 let mut tag = [0; AEAD.tag_len];
164
165 let mut encrypted_master_key = [0; KDF.key_len];
166 AEAD.encrypt(
167 &mut encrypted_master_key,
168 &mut tag,
169 &master_key,
170 Some(b"bijou"),
171 &nonce,
172 &key,
173 )?;
174 drop(master_key);
175
176 let keystore = KeyStore {
177 version: 0,
178
179 salt,
180 nonce,
181 tag,
182
183 ops_limit: ops_limit.eval(PWHASH.ops_limits),
184 mem_limit: mem_limit.eval(PWHASH.mem_limits),
185
186 master_key: encrypted_master_key,
187 };
188 (|| {
189 serde_json::to_writer_pretty(
190 std::fs::File::create(path.join("keystore.json")).wrap()?,
191 &keystore,
192 )
193 .wrap()
194 })()
195 .context("failed to save keystore.json")?;
196
197 let mut bytes = serde_json::to_vec(&config).wrap()?;
198 let nonce = utils::gen_rand_bytes::<{ AEAD.nonce_len }>();
199 let mut tag = [0; AEAD.tag_len];
200 AEAD.encrypt_inplace(&mut bytes, &mut tag, &nonce, None, &config_key)?;
201 drop(config_key);
202 bytes = nonce
203 .into_iter()
204 .chain(bytes.into_iter())
205 .chain(tag.into_iter())
206 .collect::<Vec<_>>();
207 std::fs::write(path.join("config.json"), bytes).context("failed to save config.json")?;
208
209 Ok(())
210 }
211
212 pub fn open(path: impl Into<StdPathBuf>, password: impl Into<SecretBytes>) -> Result<Self> {
220 let password = password.into();
221
222 let path = path.into();
223 if !path.is_dir() {
224 bail!(@NotFound "directory not found: {}", path.display());
225 }
226
227 let file_lock = Arc::default();
228
229 let mut keystore: KeyStore = (|| {
230 serde_json::from_reader(std::fs::File::open(path.join("keystore.json")).wrap()?).wrap()
231 })()
232 .context("failed to read keystore.json")?;
233 if keystore.version > 0 {
234 bail!(@IncompatibleVersion "keystore version {} is not supported", keystore.version);
235 }
236
237 let mut key = [0; AEAD.key_len];
238 PWHASH.derive_key(
239 &mut key,
240 &password,
241 &keystore.salt,
242 Limit::Custom(keystore.ops_limit),
243 Limit::Custom(keystore.mem_limit),
244 )?;
245
246 let mut master_key: SecretBytes = SecretBytes::move_from(&mut keystore.master_key);
247 AEAD.decrypt_inplace(
248 &mut master_key,
249 &keystore.tag,
250 Some(b"bijou"),
251 &keystore.nonce,
252 &key,
253 )
254 .context("incorrect password")?;
255 let mk = KDF.prk(master_key, Self::KDF_CTX.as_slice());
256
257 let config_key = mk.derive(0, AEAD.key_len)?;
258 let content_key_bytes = mk.derive(1, hkdf::KeyType::len(&hkdf::HKDF_SHA256))?;
259
260 let content_key = Prk::new_less_safe(hkdf::HKDF_SHA256, &content_key_bytes);
261 drop(content_key_bytes);
262
263 let mut config =
264 std::fs::read(path.join("config.json")).context("failed to read config.json")?;
265 let (nonce, config, tag) = split_nonce_tag(&mut config, AEAD.nonce_len, AEAD.tag_len);
270 AEAD.decrypt_inplace(config, tag, None, nonce, &config_key)?;
271 drop(config_key);
272 let config: Config = serde_json::from_slice(config).context("failed to parse config")?;
273
274 info!("config: {config:?}");
275
276 let file_name_key = if config.encrypt_file_name {
277 Some(mk.derive(2, hkdf::KeyType::len(&hkdf::HKDF_SHA256))?)
278 } else {
279 None
280 };
281
282 let db_key = if config.encrypt_db {
283 Some(mk.derive(3, Database::KEYBYTES)?)
284 } else {
285 None
286 };
287
288 let data_dir = path.join("data");
289 if !data_dir.is_dir() {
290 std::fs::create_dir_all(&data_dir).context("failed to create data directory")?;
291 }
292
293 let db = Arc::new(Database::open(path.join("db"), db_key)?);
294 let raw_fs = config
295 .storage
296 .build(&db, &data_dir)
297 .context("failed to build storage")?;
298
299 info!("launching Bijou");
300
301 let file_open_counts = Arc::new(DashMap::<FileId, Arc<AtomicU32>>::new());
302
303 let mut result = Self {
304 path,
305
306 db,
307 raw_fs,
308 algo: config.to_algorithm()?,
309
310 config,
311
312 content_key,
313 file_name_key,
314
315 file_lock,
316 file_open_counts,
317 };
318 result.init()?;
319 Ok(result)
320 }
321
322 pub fn path(&self) -> &StdPath {
324 &self.path
325 }
326
327 fn child_key<T>(&self, key: DatabaseKey<T>, name: &str) -> Result<DatabaseKey<DirItem>> {
328 if let Some(file_name_key) = &self.file_name_key {
329 if name != "." && name != ".." {
330 let mut name = name.as_bytes().to_vec();
332 let tag = xchacha20_siv::encrypt_detached(
333 &mut name,
334 key.key.as_slice(),
335 cast_key(file_name_key),
336 )
337 .map_err(crypto_error)?;
338 name.extend(tag.0);
339 return Ok(key.derive(consts::DIR_DERIVE).derive(&name).typed());
340 }
341 }
342
343 Ok(key
344 .derive(consts::DIR_DERIVE)
345 .derive(name.as_bytes())
346 .typed())
347 }
348
349 fn init(&mut self) -> Result<()> {
350 let root_id = FileId::ROOT;
351 let root_key = self.get_key(root_id);
352 if !root_key.exists()? {
353 let now = Utc::now();
354 let attrs = FileMeta {
355 id: root_id,
356 kind: FileKind::Directory,
357
358 size: 0,
359
360 accessed: now,
361 modified: now,
362
363 nlinks: 2,
364
365 perms: if self.config.unix_perms {
366 Some(UnixPerms {
367 mode: 0o755,
368 uid: 0,
369 gid: 0,
370 })
371 } else {
372 None
373 },
374 };
375
376 let mut batch = self.db.batch();
377 root_key.put_batch(&mut batch, &attrs)?;
378 self.child_key(root_key.clone(), ".")?.put_batch(
379 &mut batch,
380 &DirItem {
381 id: root_id,
382 kind: FileKind::Directory,
383 },
384 )?;
385 self.child_key(root_key, "..")?.put_batch(
386 &mut batch,
387 &DirItem {
388 id: root_id,
389 kind: FileKind::Directory,
390 },
391 )?;
392
393 batch.commit()?;
394 }
395
396 Ok(())
397 }
398
399 pub fn root(&self) -> Inode {
401 Inode::ROOT
402 }
403
404 fn allocate_id(&self) -> Result<FileId> {
405 let mut id = FileId::gen();
406
407 while self.get_key(id).exists()? {
408 id = FileId::gen();
410 }
411 Ok(id)
412 }
413
414 pub fn lookup(&self, parent: FileId, name: &str) -> Result<FileId> {
418 Ok(self
419 .child_key(self.get_key(parent), name)?
420 .get()?
421 .kind(ErrorKind::NotFound)?
422 .id)
423 }
424
425 fn get_key(&self, file: FileId) -> DatabaseKey<FileMeta> {
426 self.db.key(consts::FILE_ROOT).derive(file).typed()
427 }
428
429 fn get_raw_meta(&self, key: &DatabaseKey<FileMeta>) -> Result<FileMeta> {
430 key.get()?.kind(ErrorKind::NotFound)
431 }
432
433 pub fn get_meta(&self, file: FileId) -> Result<FileMeta> {
435 obtain_metadata(&self.get_key(file), self.algo.as_ref(), || {
436 self.raw_fs.stat(file)
437 })
438 }
439
440 pub fn make_node(
444 &self,
445 parent: FileId,
446 name: &str,
447 kind: FileKind,
448 symlink: Option<String>,
449 perms: Option<UnixPerms>,
450 ) -> Result<FileMeta> {
451 trace!(%parent, name, ?kind, "make node");
452 let lock = self.file_lock.get(parent);
453 let _guard = lock.write().unwrap();
454
455 let mut batch = self.db.batch();
456
457 let parent_key = self.get_key(parent);
458 let child_key = self.child_key(parent_key.clone(), name)?;
459 if child_key.exists()? {
460 bail!(@AlreadyExists? "file already exists: {name}");
461 }
462
463 let now = Utc::now();
464
465 let mut parent_meta = self.get_raw_meta(&parent_key)?;
466 parent_meta.modified = now;
467 parent_meta.nlinks += (kind == FileKind::Directory) as u32;
468 parent_key.put_batch(&mut batch, &parent_meta)?;
469
470 let id = self.allocate_id()?;
471 let key = self.get_key(id);
472 let meta = FileMeta {
473 id,
474 kind,
475
476 size: 0,
477
478 accessed: now,
479 modified: now,
480
481 nlinks: if kind == FileKind::Directory { 2 } else { 1 },
482
483 perms: perms.filter(|_| self.config.unix_perms),
484 };
485 key.put_batch(&mut batch, &meta)?;
486
487 match kind {
488 FileKind::Directory => {
489 self.child_key(key.clone(), ".")?.put_batch(
490 &mut batch,
491 &DirItem {
492 id,
493 kind: FileKind::Directory,
494 },
495 )?;
496 self.child_key(key, "..")?.put_batch(
497 &mut batch,
498 &DirItem {
499 id: parent,
500 kind: FileKind::Directory,
501 },
502 )?;
503 }
504 FileKind::Symlink => {
505 let Some(target) = symlink else {
506 bail!(@InvalidInput "symlink target must not be None");
507 };
508 key.derive(consts::SYMLINK_DERIVE)
509 .typed::<String>()
510 .put_batch(&mut batch, &target)?;
511 }
512 _ => {}
513 }
514
515 child_key.put_batch(
516 &mut batch,
517 &DirItem {
518 id,
519 kind: meta.kind,
520 },
521 )?;
522
523 batch.commit()?;
524
525 if kind == FileKind::File {
526 self.raw_fs.create(id)?;
527 }
528
529 Ok(meta)
530 }
531
532 pub fn link(&self, file: FileId, parent: FileId, name: &str) -> Result<FileMeta> {
534 trace!(%parent, name, "link");
535
536 let lock = self.file_lock.get(parent);
537 let _guard = lock.write().unwrap();
538
539 let mut batch = self.db.batch();
540
541 let key = self.get_key(file);
542 let mut meta = self.get_raw_meta(&key)?;
543 if meta.kind == FileKind::Directory {
544 bail!(@InvalidInput? "creating hard link to directory");
545 }
546 meta.nlinks += 1;
547 key.put_batch(&mut batch, &meta)?;
548
549 let parent_key = self.get_key(parent);
550 let child_key = self.child_key(parent_key, name)?;
551 if child_key.exists()? {
552 bail!(@AlreadyExists? "file already exists: {name}");
553 }
554 child_key.put_batch(
555 &mut batch,
556 &DirItem {
557 id: file,
558 kind: meta.kind,
559 },
560 )?;
561
562 batch.commit()?;
563
564 Ok(meta)
565 }
566
567 fn derive_key(&self, file: FileId) -> Result<SecretBytes> {
568 let mut bytes = SecretBytes::allocate(self.algo.key_size());
569 struct DummyKey(usize);
570 impl KeyType for DummyKey {
571 fn len(&self) -> usize {
572 self.0
573 }
574 }
575 (|| -> Result<(), Unspecified> {
576 self.content_key
577 .expand(&[file.as_ref()], DummyKey(self.algo.key_size()))?
578 .fill(&mut bytes)
579 })()
580 .map_err(|_| anyhow!(@CryptoError "failed to derive key"))?;
581
582 Ok(bytes)
583 }
584
585 fn open_inner(&self, meta: FileMeta, options: &OpenOptions) -> Result<LowLevelFile> {
586 let flags = options.to_flags();
587 let raw_file = self
588 .raw_fs
589 .open(meta.id, options.clone().read(true).to_flags())?;
590 let key = self.get_key(meta.id);
591
592 Ok(LowLevelFile::new(
593 raw_file,
594 Arc::clone(&self.algo),
595 self.algo.key(self.derive_key(meta.id)?)?,
596 key,
597 flags,
598 self.file_lock
599 .get_or_try_insert(meta.id, || self.raw_fs.stat(meta.id))?,
600 Arc::clone(&self.file_open_counts.entry(meta.id).or_default()),
601 ))
602 }
603
604 pub fn open_file_direct(&self, file: FileId, options: &OpenOptions) -> Result<LowLevelFile> {
613 let meta = self.get_raw_meta(&self.get_key(file))?;
614 self.open_inner(meta, options)
615 }
616
617 pub fn open_file(
623 &self,
624 parent: FileId,
625 name: &str,
626 options: &OpenOptions,
627 perms: Option<UnixPerms>,
628 ) -> Result<LowLevelFile> {
629 if options.truncate && !options.write {
630 bail!(@InvalidInput? "cannot specify truncate without write")
631 }
632 match self.child_key(self.get_key(parent), name)?.get()? {
633 Some(item) => {
634 if options.create_new {
635 bail!(@AlreadyExists? "requiring create_new but file already exists: {name}");
636 }
637 self.open_file_direct(item.id, options)
638 }
639 None => {
640 if options.create || options.create_new {
641 let meta = self.make_node(parent, name, FileKind::File, None, perms)?;
642 self.open_inner(meta, options)
643 } else {
644 bail!(@NotFound? "file not found: {name}");
645 }
646 }
647 }
648 }
649
650 pub(crate) fn resolve_inner(
651 &self,
652 mut stack: Vec<FileId>,
653 path: &Path,
654 depth: &mut u32,
655 ) -> Result<FileId> {
656 for comp in path.components() {
657 match comp {
658 Component::RootDir => {
659 stack.truncate(1);
660 }
661 Component::CurDir => {}
662 Component::ParentDir => {
663 if stack.len() > 1 {
664 stack.pop();
665 }
666 }
667 other => {
668 let id = self.lookup(*stack.last().unwrap(), other.as_str())?;
669 let id = match self.read_link(id) {
670 Ok(path) => {
671 *depth += 1;
672 if *depth > SYMBOLIC_MAX_DEPTH {
673 bail!(@FilesystemLoop? "too many levels of symbolic links");
674 }
675 self.resolve_inner(stack.clone(), Path::new(&path), depth)?
677 }
678 Err(err) if err.kind() == ErrorKind::InvalidInput => {
679 id
681 }
682 Err(err) => return Err(err),
683 };
684 stack.push(id);
685 }
686 }
687 }
688
689 Ok(stack.into_iter().rev().next().unwrap())
690 }
691
692 pub fn resolve(&self, path: impl AsRef<Path>) -> Result<FileId> {
694 self.resolve_inner(vec![FileId::ROOT], path.as_ref(), &mut 0)
695 }
696
697 pub fn resolve_parent<'a>(&self, path: &'a Path) -> Result<(FileId, Option<&'a str>)> {
706 let mut stack = vec![(FileId::ROOT, "")];
707 let mut current_name = None;
708 let mut symlink_depth = 0;
709 for comp in path.components() {
710 match comp {
711 Component::RootDir => {
712 stack.truncate(1);
713 current_name = None;
714 }
715 Component::CurDir => {}
716 Component::ParentDir => {
717 if stack.len() == 1 {
718 current_name = None;
719 } else {
720 current_name = Some(stack.pop().unwrap().1);
721 }
722 }
723 Component::Normal(name) => {
724 if let Some(parent_name) = current_name {
725 let parent = self.resolve_inner(
726 stack.iter().map(|it| it.0).collect(),
727 Path::new(parent_name),
728 &mut symlink_depth,
729 )?;
730 stack.push((parent, parent_name));
731 }
732 current_name = Some(name);
733 }
734 }
735 }
736 Ok((stack.into_iter().rev().next().unwrap().0, current_name))
737 }
738
739 pub fn resolve_parent_nonroot<'a>(&self, path: &'a Path) -> Result<(FileId, &'a str)> {
746 let (parent, name) = self.resolve_parent(path)?;
747 let Some(name) = name else {
748 bail!(@InvalidInput "expected non-root path, got `{path}`");
749 };
750 Ok((parent, name))
751 }
752
753 pub fn read_dir(&self, id: FileId) -> Result<DirIterator> {
764 let key = self.get_key(id);
765 if key.get()?.kind(ErrorKind::NotFound)?.kind != FileKind::Directory {
766 bail!(@NotADirectory "not a directory");
767 }
768 let mut opts = ReadOptions::default();
769 opts.set_iterate_upper_bound(key.clone().derive(consts::DIR_DERIVE_UPPER).key.to_vec());
770 Ok(DirIterator {
771 key: key.derive(consts::DIR_DERIVE).key,
772 inner: self.db.0.iterator_opt(IteratorMode::Start, opts),
773 decrypt: self.file_name_key.as_ref().map(|key| (id, cast_key(key))),
775 })
776 }
777
778 fn unlink_inner(
779 &self,
780 batch: &mut WriteBatch,
781 parent: FileId,
782 name: &str,
783 ) -> Result<Option<FileId>> {
784 trace!(%parent, name, "unlink");
785
786 let child = self.lookup(parent, name)?;
787
788 let key = self.get_key(child);
789 let mut meta = self.get_raw_meta(&key)?;
790 let is_dir = meta.kind == FileKind::Directory;
791
792 if is_dir && self.read_dir(child)?.reset().nth(2).is_some() {
793 bail!(@NotEmpty? "trying to unlink non-empty directory: {name}");
794 }
795
796 let parent_key = self.get_key(parent);
797 let mut parent_meta = self.get_raw_meta(&parent_key)?;
798
799 parent_meta.modified = Utc::now();
800 parent_meta.nlinks -= is_dir as u32;
801 parent_key.put_batch(batch, &parent_meta)?;
802
803 self.child_key(parent_key, name)?.delete_batch(batch);
804
805 if meta.kind == FileKind::Directory {
806 meta.nlinks = 0;
807
808 self.child_key(key.clone(), ".")?.delete_batch(batch);
809 self.child_key(key.clone(), "..")?.delete_batch(batch);
810
811 key.delete_batch(batch);
814 } else {
815 assert!(meta.nlinks > 0);
820 meta.nlinks -= 1;
821
822 if meta.nlinks == 0 {
823 key.delete_batch(batch);
824 for item in key.range_iter(consts::XATTR_DERIVE, consts::XATTR_DERIVE_UPPER) {
829 let item = item.wrap()?;
830 batch.delete(&item.0);
831 }
832 if meta.kind == FileKind::Symlink {
833 key.derive(consts::SYMLINK_DERIVE).delete_batch(batch);
834 } else {
835 self.raw_fs.unlink(child)?;
836 }
837 } else {
838 key.put_batch(batch, &meta)?;
839 }
840 }
841
842 Ok(if meta.nlinks == 0 { Some(child) } else { None })
843 }
844
845 pub fn unlink(&self, parent: FileId, name: &str) -> Result<Option<FileId>> {
850 let parent_lock = self.file_lock.get(parent);
851 let _guard = parent_lock.write().unwrap();
852
853 let mut batch = self.db.batch();
854 let removed = self.unlink_inner(&mut batch, parent, name)?;
855 batch.commit()?;
856
857 Ok(removed)
858 }
859
860 pub fn rename(
865 &self,
866 parent: FileId,
867 name: &str,
868 new_parent: FileId,
869 new_name: &str,
870 ) -> Result<Option<FileId>> {
871 trace!(%parent, name, %new_parent, new_name, "rename");
872
873 if parent == new_parent && name == new_name {
874 return Ok(None);
875 }
876
877 let parent_key = self.get_key(parent);
878 let new_parent_key = self.get_key(new_parent);
879
880 let parent_lock = self.file_lock.get(parent);
881 let new_parent_lock = self.file_lock.get(new_parent);
882 let _guard = parent_lock.write().unwrap();
883 let _guard2 = if parent == new_parent {
884 None
885 } else {
886 Some(new_parent_lock.write().unwrap())
887 };
888
889 let mut batch = self.db.batch();
890
891 let old_child_dir_key = self.child_key(parent_key.clone(), name)?;
892 let new_child_dir_key = self.child_key(new_parent_key.clone(), new_name)?;
893
894 let dir_item = old_child_dir_key.get()?.kind(ErrorKind::NotFound)?;
895 let child = self.get_key(dir_item.id);
896 let meta = self.get_raw_meta(&child)?;
897
898 let mut removed = None;
899
900 if new_child_dir_key.exists()? {
901 removed = self.unlink_inner(&mut batch, new_parent, new_name)?;
902 }
903
904 old_child_dir_key.delete_batch(&mut batch);
905 new_child_dir_key.put_batch(&mut batch, &dir_item)?;
906
907 let now = Utc::now();
908
909 if meta.kind == FileKind::Directory {
910 self.child_key(child, "..")?.put_batch(
911 &mut batch,
912 &DirItem {
913 id: new_parent,
914 kind: FileKind::Directory,
915 },
916 )?;
917 }
918
919 let mut parent_meta = self.get_raw_meta(&parent_key)?;
920 parent_meta.nlinks -= (meta.kind == FileKind::Directory) as u32;
921 parent_meta.modified = now;
922 parent_key.put_batch(&mut batch, &parent_meta)?;
923
924 let mut new_parent_meta = self.get_raw_meta(&new_parent_key)?;
925 new_parent_meta.nlinks += (meta.kind == FileKind::Directory) as u32;
926 new_parent_meta.modified = now;
927 new_parent_key.put_batch(&mut batch, &new_parent_meta)?;
928
929 batch.commit()?;
930
931 Ok(removed)
932 }
933
934 pub fn set_len(&self, file: FileId, len: u64) -> Result<()> {
939 trace!(%file, len, "set length");
940 self.open_file_direct(file, OpenOptions::new().write(true))?
941 .set_len(len)
942 }
943
944 pub fn read_link(&self, file: FileId) -> Result<String> {
946 trace!(%file, "read link");
947 let key = self.get_key(file);
948 let meta = self.get_raw_meta(&key)?;
949 if meta.kind != FileKind::Symlink {
950 bail!(@InvalidInput? "not a symlink");
951 }
952
953 key.derive(consts::SYMLINK_DERIVE)
954 .typed::<String>()
955 .get()?
956 .kind(ErrorKind::NotFound)
957 }
958
959 pub fn set_times(
961 &self,
962 file: FileId,
963 accessed: DateTime<Utc>,
964 modified: DateTime<Utc>,
965 ) -> Result<()> {
966 let key = self.get_key(file);
967 let mut meta = self.get_raw_meta(&key)?;
968 meta.accessed = accessed;
969 meta.modified = modified;
970 key.put(&meta)?;
971
972 Ok(())
973 }
974
975 pub fn set_perms(
977 &self,
978 id: FileId,
979 mode: Option<u16>,
980 uid: Option<u32>,
981 gid: Option<u32>,
982 ) -> Result<()> {
983 let key = self.get_key(id);
984 let mut meta = self.get_raw_meta(&key)?;
985 meta.perms = Some(UnixPerms {
986 mode: mode
987 .or_else(|| meta.perms.as_ref().map(|it| it.mode))
988 .unwrap_or(0o640),
989 uid: uid
990 .or_else(|| meta.perms.as_ref().map(|it| it.uid))
991 .unwrap_or(0),
992 gid: gid
993 .or_else(|| meta.perms.as_ref().map(|it| it.gid))
994 .unwrap_or(0),
995 });
996 key.put(&meta)?;
997
998 Ok(())
999 }
1000
1001 pub fn set_xattr(&self, id: FileId, name: &str, value: &[u8]) -> Result<()> {
1003 self.get_key(id)
1004 .derive(consts::XATTR_DERIVE)
1005 .derive(name)
1006 .write(value)
1007 }
1008
1009 pub fn get_xattr<R>(
1011 &self,
1012 id: FileId,
1013 name: &str,
1014 cb: impl FnOnce(Result<Option<DBPinnableSlice>>) -> R,
1015 ) -> R {
1016 if self.config.disable_xattr_gets {
1017 return cb(Err(anyhow!(@Unsupported "xattr gets are disabled")));
1018 }
1019 cb(self
1020 .get_key(id)
1021 .derive(consts::XATTR_DERIVE)
1022 .derive(name)
1023 .read())
1024 }
1025
1026 pub fn remove_xattr(&self, id: FileId, name: &str) -> Result<()> {
1028 self.get_key(id)
1029 .derive(consts::XATTR_DERIVE)
1030 .derive(name)
1031 .delete()
1032 }
1033
1034 pub fn xattrs(&self, id: FileId) -> Result<Vec<String>> {
1037 let mut result = Vec::new();
1038 let key = self.get_key(id);
1039 let iter = key.range_iter(consts::XATTR_DERIVE, consts::XATTR_DERIVE_UPPER);
1040 let len =
1041 consts::FILE_ROOT.len() + std::mem::size_of::<FileId>() + consts::XATTR_DERIVE.len();
1042 for entry in iter {
1043 let (key, _value) = entry.wrap()?;
1044 let name = &key[len..];
1045 result.push(String::from_utf8(name.to_vec()).unwrap());
1046 }
1047
1048 Ok(result)
1049 }
1050}
1051
1052pub struct DirIterator<'db> {
1054 key: RawKeyType,
1055 inner: DBIteratorWithThreadMode<'db, DBWithThreadMode<SingleThreaded>>,
1056 decrypt: Option<(FileId, &'db xchacha20_siv::Key)>,
1057}
1058impl DirIterator<'_> {
1059 pub fn reset(&mut self) -> &mut Self {
1060 self.inner
1061 .set_mode(IteratorMode::From(&self.key, Direction::Forward));
1062 self
1063 }
1064}
1065impl Iterator for DirIterator<'_> {
1066 type Item = Result<(String, DirItem)>;
1067
1068 fn next(&mut self) -> Option<Self::Item> {
1069 self.inner.next().map(|result| {
1070 let (mut key, value) = result.wrap()?;
1071 let name = &mut key[consts::FILE_ROOT.len()
1072 + std::mem::size_of::<FileId>()
1073 + consts::DIR_DERIVE.len()..];
1074 if let Some((id, key)) = &self.decrypt {
1075 if name != b"." && name != b".." {
1076 assert!(name.len() > xchacha20_siv::ABYTES);
1077 let (name, tag) = name.split_at_mut(name.len() - xchacha20_siv::ABYTES);
1078 xchacha20_siv::decrypt_inplace(name, cast_key(tag), id.as_ref(), key)
1079 .map_err(|_| anyhow!(@CryptoError "failed to decrypt filename"))?;
1080 return Ok((
1081 String::from_utf8(name.to_vec()).unwrap(),
1082 postcard::from_bytes(&value).wrap()?,
1083 ));
1084 }
1085 }
1086 Ok((
1087 String::from_utf8(name.to_vec()).unwrap(),
1088 postcard::from_bytes(&value).wrap()?,
1089 ))
1090 })
1091 }
1092}