1pub(crate) mod monitor;
2
3mod credentials;
4mod metadata;
5mod params;
6mod vault;
7mod worker;
8
9#[cfg(test)]
10mod tests;
11
12pub use self::{credentials::Credentials, metadata::Metadata, params::RepositoryParams};
13
14pub(crate) use self::{
15 metadata::{data_version, quota},
16 vault::Vault,
17};
18
19use self::monitor::RepositoryMonitor;
20use crate::{
21 access_control::{Access, AccessChange, AccessKeys, AccessMode, AccessSecrets, LocalSecret},
22 block_tracker::BlockRequestMode,
23 branch::{Branch, BranchShared},
24 crypto::{PasswordSalt, sign::PublicKey},
25 db::{self, DatabaseId},
26 debug::DebugPrinter,
27 directory::{Directory, DirectoryFallback, DirectoryLocking, EntryRef, EntryType},
28 error::{Error, Result},
29 event::{Event, EventSender},
30 file::File,
31 joint_directory::{JointDirectory, JointEntryRef, MissingVersionStrategy},
32 path,
33 progress::Progress,
34 protocol::{BLOCK_SIZE, RootNodeFilter, StorageSize},
35 store,
36 sync::stream::Throttle,
37 version_vector::VersionVector,
38};
39use camino::Utf8Path;
40use deadlock::{BlockingMutex, BlockingRwLock};
41use futures_util::{StreamExt, stream};
42use futures_util::{TryStreamExt, future};
43use metrics::{NoopRecorder, Recorder};
44use scoped_task::ScopedJoinHandle;
45use state_monitor::StateMonitor;
46use std::{
47 borrow::Cow,
48 path::{Path, PathBuf},
49 pin::pin,
50 sync::Arc,
51 time::SystemTime,
52};
53use tokio::{
54 sync::broadcast::{self, error::RecvError},
55 time::Duration,
56};
57use tracing::instrument::Instrument;
58
59const EVENT_CHANNEL_CAPACITY: usize = 10000;
60
61pub struct Repository {
62 shared: Arc<Shared>,
63 worker_handle: BlockingMutex<Option<ScopedJoinHandle<()>>>,
64 progress_reporter_handle: BlockingMutex<Option<ScopedJoinHandle<()>>>,
65}
66
67pub fn database_files(store_path: impl AsRef<Path>) -> Vec<PathBuf> {
78 ["", "-wal", "-shm"]
80 .into_iter()
81 .map(|suffix| {
82 let mut path = store_path.as_ref().as_os_str().to_owned();
83 path.push(suffix);
84 path.into()
85 })
86 .collect()
87}
88
89impl Repository {
90 pub async fn create(params: &RepositoryParams<impl Recorder>, access: Access) -> Result<Self> {
92 let pool = params.create().await?;
93 let device_id = params.device_id();
94 let monitor = params.monitor();
95
96 let mut tx = pool.begin_write().await?;
97
98 let local_keys = metadata::initialize_access_secrets(&mut tx, &access).await?;
99 let writer_id =
100 metadata::get_or_generate_writer_id(&mut tx, local_keys.write.as_deref()).await?;
101 metadata::set_device_id(&mut tx, &device_id).await?;
102
103 tx.commit().await?;
104
105 let credentials = Credentials {
106 secrets: access.secrets(),
107 writer_id,
108 };
109
110 Self::new(pool, credentials, monitor).init().await
111 }
112
113 pub async fn open(
115 params: &RepositoryParams<impl Recorder>,
116 local_secret: Option<LocalSecret>,
117 access_mode: AccessMode,
118 ) -> Result<Self> {
119 let pool = params.open().await?;
120 let monitor = params.monitor();
121 let device_id = params.device_id();
122
123 let mut tx = pool.begin_write().await?;
124
125 let (secrets, local_key) =
126 metadata::get_access_secrets(&mut tx, local_secret.as_ref()).await?;
127
128 let secrets = secrets.with_mode(access_mode);
129
130 let writer_id = if metadata::check_device_id(&mut tx, &device_id).await? {
131 if secrets.can_write() {
132 metadata::get_or_generate_writer_id(&mut tx, local_key.as_deref()).await?
133 } else {
134 metadata::generate_writer_id()
135 }
136 } else {
137 let writer_id = metadata::generate_writer_id();
144
145 metadata::set_device_id(&mut tx, &device_id).await?;
146 metadata::set_writer_id(&mut tx, &writer_id, local_key.as_deref()).await?;
147
148 writer_id
149 };
150
151 tx.commit().await?;
152
153 let credentials = Credentials { secrets, writer_id };
154
155 Self::new(pool, credentials, monitor).init().await
156 }
157
158 fn new(pool: db::Pool, credentials: Credentials, monitor: RepositoryMonitor) -> Self {
159 Self {
160 shared: Arc::new(Shared::new(pool, credentials, monitor)),
161 worker_handle: BlockingMutex::new(None),
162 progress_reporter_handle: BlockingMutex::new(None),
163 }
164 }
165
166 async fn init(self) -> Result<Self> {
167 let credentials = self.credentials();
168
169 if let Some(keys) = credentials
170 .secrets
171 .write_secrets()
172 .map(|secrets| &secrets.write_keys)
173 {
174 self.shared
175 .vault
176 .store()
177 .migrate_data(credentials.writer_id, keys)
178 .await?;
179 }
180
181 {
182 let mut conn = self.shared.vault.store().db().acquire().await?;
183 if let Some(block_expiration) = metadata::block_expiration::get(&mut conn).await? {
184 self.shared
185 .vault
186 .set_block_expiration(Some(block_expiration))?;
187 }
188 }
189
190 tracing::debug!(
191 parent: self.shared.vault.monitor.span(),
192 access = ?credentials.secrets.access_mode(),
193 writer_id = ?credentials.writer_id,
194 "Repository opened"
195 );
196
197 *self.worker_handle.lock().unwrap() = Some(spawn_worker(self.shared.clone()));
198
199 *self.progress_reporter_handle.lock().unwrap() = Some(scoped_task::spawn(
200 report_sync_progress(self.shared.vault.clone())
201 .instrument(self.shared.vault.monitor.span().clone()),
202 ));
203
204 Ok(self)
205 }
206
207 pub async fn database_id(&self) -> Result<DatabaseId> {
208 Ok(metadata::get_or_generate_database_id(self.db()).await?)
209 }
210
211 pub async fn requires_local_secret_for_reading(&self) -> Result<bool> {
212 let mut conn = self.db().acquire().await?;
213 Ok(metadata::requires_local_secret_for_reading(&mut conn).await?)
214 }
215
216 pub async fn requires_local_secret_for_writing(&self) -> Result<bool> {
217 let mut conn = self.db().acquire().await?;
218 Ok(metadata::requires_local_secret_for_writing(&mut conn).await?)
219 }
220
221 pub fn store_path(&self) -> &Path {
222 self.shared.vault.store().path()
223 }
224
225 pub async fn set_access(
245 &self,
246 read_change: Option<AccessChange>,
247 write_change: Option<AccessChange>,
248 ) -> Result<()> {
249 let mut tx = self.db().begin_write().await?;
250
251 if let Some(change) = read_change {
252 self.set_read_access(&mut tx, change).await?;
253 }
254
255 if let Some(change) = write_change {
256 self.set_write_access(&mut tx, change).await?;
257 }
258
259 tx.commit().await?;
260
261 Ok(())
262 }
263
264 async fn set_read_access(
265 &self,
266 tx: &mut db::WriteTransaction,
267 change: AccessChange,
268 ) -> Result<()> {
269 let local = match &change {
270 AccessChange::Enable(Some(local_secret)) => {
271 Some(metadata::secret_to_key_and_salt(local_secret))
272 }
273 AccessChange::Enable(None) => None,
274 AccessChange::Disable => {
275 metadata::remove_read_key(tx).await?;
276 return Ok(());
277 }
278 };
279
280 let (id, read_key) = {
281 let cred = self.shared.credentials.read().unwrap();
282 (
283 *cred.secrets.id(),
284 cred.secrets
285 .read_key()
286 .ok_or(Error::PermissionDenied)?
287 .clone(),
288 )
289 };
290
291 metadata::set_read_key(
292 tx,
293 &id,
294 &read_key,
295 local.as_ref().map(|(k, s)| (k.as_ref(), s.as_ref())),
296 )
297 .await?;
298
299 Ok(())
300 }
301
302 async fn set_write_access(
303 &self,
304 tx: &mut db::WriteTransaction,
305 change: AccessChange,
306 ) -> Result<()> {
307 let local = match &change {
308 AccessChange::Enable(Some(local_secret)) => {
309 Some(metadata::secret_to_key_and_salt(local_secret))
310 }
311 AccessChange::Enable(None) => None,
312 AccessChange::Disable => {
313 metadata::remove_write_key(tx).await?;
314 return Ok(());
315 }
316 };
317
318 let (write_secrets, writer_id) = {
319 let cred = self.shared.credentials.read().unwrap();
320 (
321 cred.secrets
322 .write_secrets()
323 .ok_or(Error::PermissionDenied)?
324 .clone(),
325 cred.writer_id,
326 )
327 };
328
329 metadata::set_write_key(
330 tx,
331 &write_secrets,
332 local.as_ref().map(|(k, s)| (k.as_ref(), s.as_ref())),
333 )
334 .await?;
335 metadata::set_writer_id(tx, &writer_id, local.as_ref().map(|(k, _)| k.as_ref())).await?;
336
337 Ok(())
338 }
339
340 pub fn credentials(&self) -> Credentials {
344 self.shared.credentials.read().unwrap().clone()
345 }
346
347 pub fn secrets(&self) -> AccessSecrets {
348 self.shared.credentials.read().unwrap().secrets.clone()
349 }
350
351 pub fn access_mode(&self) -> AccessMode {
353 self.shared
354 .credentials
355 .read()
356 .unwrap()
357 .secrets
358 .access_mode()
359 }
360
361 pub async fn set_access_mode(
367 &self,
368 access_mode: AccessMode,
369 local_secret: Option<LocalSecret>,
370 ) -> Result<()> {
371 let old_secrets = {
374 let creds = self.shared.credentials.read().unwrap();
375
376 if creds.secrets.access_mode() == access_mode {
377 return Ok(());
378 }
379
380 creds.secrets.clone()
381 };
382
383 let mut tx = self.db().begin_write().await?;
384
385 let (secrets, local_key) = if old_secrets.access_mode() >= access_mode {
386 (old_secrets, None)
387 } else {
388 let (new_secrets, local_key) =
389 metadata::get_access_secrets(&mut tx, local_secret.as_ref()).await?;
390
391 if new_secrets.access_mode() > old_secrets.access_mode() {
392 (new_secrets, local_key)
393 } else {
394 (old_secrets, None)
395 }
396 };
397
398 let secrets = secrets.with_mode(access_mode);
399
400 let writer_id = if secrets.can_write() {
401 metadata::get_or_generate_writer_id(&mut tx, local_key.as_deref()).await?
402 } else {
403 metadata::generate_writer_id()
404 };
405
406 tx.commit().await?;
407
408 if let Some(write_keys) = secrets.write_secrets().map(|secrets| &secrets.write_keys) {
411 self.shared
412 .vault
413 .store()
414 .migrate_data(writer_id, write_keys)
415 .await?;
416 }
417
418 self.update_credentials(Credentials { secrets, writer_id });
419
420 Ok(())
421 }
422
423 pub async fn set_credentials(&self, credentials: Credentials) -> Result<()> {
444 let expected_id = {
446 let mut conn = self.db().acquire().await?;
447 metadata::get_repository_id(&mut conn).await?
448 };
449
450 if credentials.secrets.id() != &expected_id {
451 return Err(Error::PermissionDenied);
452 }
453
454 if let Some(write_secrets) = credentials.secrets.write_secrets() {
455 self.shared
456 .vault
457 .store()
458 .migrate_data(credentials.writer_id, &write_secrets.write_keys)
459 .await?;
460 }
461
462 self.update_credentials(credentials);
463
464 Ok(())
465 }
466
467 pub async fn unlock_secrets(&self, local_secret: LocalSecret) -> Result<AccessSecrets> {
468 let mut tx = self.db().begin_write().await?;
469 Ok(metadata::get_access_secrets(&mut tx, Some(&local_secret))
470 .await?
471 .0)
472 }
473
474 pub fn metadata(&self) -> Metadata {
477 self.shared.vault.metadata()
478 }
479
480 pub async fn set_quota(&self, quota: Option<StorageSize>) -> Result<()> {
482 self.shared.vault.set_quota(quota).await
483 }
484
485 pub async fn quota(&self) -> Result<Option<StorageSize>> {
487 self.shared.vault.quota().await
488 }
489
490 pub async fn set_block_expiration(&self, block_expiration: Option<Duration>) -> Result<()> {
493 let mut tx = self.db().begin_write().await?;
494 metadata::block_expiration::set(&mut tx, block_expiration).await?;
495 tx.commit().await?;
496
497 self.shared.vault.set_block_expiration(block_expiration)
498 }
499
500 pub fn block_expiration(&self) -> Option<Duration> {
502 self.shared.vault.block_expiration()
503 }
504
505 pub fn last_block_expiration_time(&self) -> Option<SystemTime> {
508 self.shared.vault.last_block_expiration_time()
509 }
510
511 pub async fn size(&self) -> Result<StorageSize> {
513 self.shared.vault.size().await
514 }
515
516 pub fn handle(&self) -> RepositoryHandle {
517 RepositoryHandle {
518 vault: self.shared.vault.clone(),
519 }
520 }
521
522 pub async fn get_read_password_salt(&self) -> Result<PasswordSalt> {
523 let mut tx = self.db().begin_write().await?;
524 Ok(metadata::get_password_salt(&mut tx, metadata::KeyType::Read).await?)
525 }
526
527 pub async fn get_write_password_salt(&self) -> Result<PasswordSalt> {
528 let mut tx = self.db().begin_write().await?;
529 Ok(metadata::get_password_salt(&mut tx, metadata::KeyType::Write).await?)
530 }
531
532 pub fn monitor(&self) -> &StateMonitor {
534 self.shared.vault.monitor.node()
535 }
536
537 pub async fn export(&self, dst: &Path) -> Result<()> {
542 struct Cleanup<'a> {
545 path: &'a Path,
546 armed: bool,
547 }
548
549 impl Drop for Cleanup<'_> {
550 fn drop(&mut self) {
551 if !self.armed {
552 return;
553 }
554
555 if let Err(error) = std::fs::remove_file(self.path) {
556 tracing::error!(
557 path = ?self.path,
558 ?error,
559 "failed to delete partially exported repository",
560 );
561 }
562 }
563 }
564
565 let mut cleanup = Cleanup {
566 path: dst,
567 armed: true,
568 };
569
570 self.shared.vault.store().export(dst).await?;
572
573 let pool = db::open(dst).await?;
575 let credentials = self.credentials().with_mode(AccessMode::Read);
576 let access_mode = credentials.secrets.access_mode();
577 let monitor = RepositoryMonitor::new(StateMonitor::make_root(), &NoopRecorder);
578 let repo = Self::new(pool, credentials, monitor);
579
580 match access_mode {
581 AccessMode::Blind => {
582 repo.set_access(Some(AccessChange::Disable), Some(AccessChange::Disable))
583 .await?
584 }
585 AccessMode::Read => {
586 repo.set_access(
587 Some(AccessChange::Enable(None)),
588 Some(AccessChange::Disable),
589 )
590 .await?
591 }
592 AccessMode::Write => unreachable!(),
593 }
594
595 repo.close().await?;
596
597 cleanup.armed = false;
598
599 Ok(())
600 }
601
602 pub async fn lookup_type<P: AsRef<Utf8Path>>(&self, path: P) -> Result<EntryType> {
605 match path::decompose(path.as_ref()) {
606 Some((parent, name)) => {
607 let parent = self.open_directory(parent).await?;
608 Ok(parent.lookup_unique(name)?.entry_type())
609 }
610 None => Ok(EntryType::Directory),
611 }
612 }
613
614 pub async fn open_file<P: AsRef<Utf8Path>>(&self, path: P) -> Result<File> {
616 let (parent, name) = path::decompose(path.as_ref()).ok_or(Error::EntryIsDirectory)?;
617
618 self.cd(parent)
619 .await?
620 .lookup_unique(name)?
621 .file()?
622 .open()
623 .await
624 }
625
626 pub async fn open_file_version<P: AsRef<Utf8Path>>(
628 &self,
629 path: P,
630 branch_id: &PublicKey,
631 ) -> Result<File> {
632 let (parent, name) = path::decompose(path.as_ref()).ok_or(Error::EntryIsDirectory)?;
633
634 self.cd(parent)
635 .await?
636 .lookup_version(name, branch_id)?
637 .open()
638 .await
639 }
640
641 pub async fn open_directory<P: AsRef<Utf8Path>>(&self, path: P) -> Result<JointDirectory> {
643 self.cd(path).await
644 }
645
646 pub async fn create_file<P: AsRef<Utf8Path>>(&self, path: P) -> Result<File> {
648 let file = self
649 .local_branch()?
650 .ensure_file_exists(path.as_ref())
651 .await?;
652
653 Ok(file)
654 }
655
656 pub async fn create_directory<P: AsRef<Utf8Path>>(&self, path: P) -> Result<Directory> {
658 let dir = self
659 .local_branch()?
660 .ensure_directory_exists(path.as_ref())
661 .await?;
662
663 Ok(dir)
664 }
665
666 pub async fn remove_entry<P: AsRef<Utf8Path>>(&self, path: P) -> Result<()> {
668 let (parent, name) = path::decompose(path.as_ref()).ok_or(Error::OperationNotSupported)?;
669 let mut parent = self.cd(parent).await?;
670 parent.remove_entry(name).await?;
671
672 Ok(())
673 }
674
675 pub async fn remove_entry_recursively<P: AsRef<Utf8Path>>(&self, path: P) -> Result<()> {
677 let (parent, name) = path::decompose(path.as_ref()).ok_or(Error::OperationNotSupported)?;
678 let mut parent = self.cd(parent).await?;
679 parent.remove_entry_recursively(name).await?;
680
681 Ok(())
682 }
683
684 pub async fn move_entry<S: AsRef<Utf8Path>, D: AsRef<Utf8Path>>(
687 &self,
688 src: S,
689 dst: D,
690 ) -> Result<()> {
691 let (src_dir_path, src_name) = path::decompose(src.as_ref()).ok_or(Error::EntryNotFound)?;
692 let (dst_dir_path, dst_name) = path::decompose(dst.as_ref()).ok_or(Error::EntryNotFound)?;
693
694 let local_branch = self.local_branch()?;
695 let src_joint_dir = self.cd(src_dir_path).await?;
696
697 let (mut src_dir, src_name, src_type) = match src_joint_dir.lookup_unique(src_name)? {
699 JointEntryRef::File(entry) => {
700 let src_name = entry.name().to_string();
701
702 let mut file = entry.open().await?;
703 file.fork(local_branch.clone()).await?;
704
705 (file.parent().await?, Cow::Owned(src_name), EntryType::File)
706 }
707 JointEntryRef::Directory(entry) => {
708 let mut dir_to_move = entry
709 .open_with(MissingVersionStrategy::Skip, DirectoryFallback::Disabled)
710 .await?;
711
712 let src_dir = dir_to_move
713 .merge()
714 .await?
715 .1
716 .parent()
717 .await?
718 .ok_or(Error::OperationNotSupported )?;
719
720 (src_dir, Cow::Borrowed(src_name), EntryType::Directory)
721 }
722 };
723
724 let src_entry = src_dir.lookup(&src_name)?.clone_data();
725
726 let mut dst_joint_dir = self.cd(&dst_dir_path).await?;
727 let dst_dir = dst_joint_dir
728 .local_version_mut()
729 .ok_or(Error::PermissionDenied)?;
730
731 let dst_old_entry = dst_dir.lookup(dst_name);
732
733 let dst_old_vv = match (src_type, dst_old_entry) {
736 (EntryType::File | EntryType::Directory, Ok(EntryRef::Tombstone(old_entry))) => {
737 old_entry.version_vector().clone()
738 }
739 (EntryType::File | EntryType::Directory, Err(Error::EntryNotFound)) => {
740 VersionVector::new()
741 }
742 (EntryType::File | EntryType::Directory, Err(error)) => return Err(error),
743 (EntryType::File, Ok(EntryRef::File(old_entry))) => old_entry.version_vector().clone(),
744 (EntryType::Directory, Ok(EntryRef::Directory(old_entry))) => {
745 if old_entry
746 .open(DirectoryFallback::Disabled)
747 .await?
748 .entries()
749 .all(|entry| entry.is_tombstone())
750 {
751 old_entry.version_vector().clone()
752 } else {
753 return Err(Error::DirectoryNotEmpty);
754 }
755 }
756 (EntryType::File, Ok(EntryRef::Directory(_))) => return Err(Error::EntryIsDirectory),
757 (EntryType::Directory, Ok(EntryRef::File(_))) => return Err(Error::EntryIsFile),
758 };
759
760 let dst_vv = dst_old_vv
761 .merged(src_entry.version_vector())
762 .incremented(*local_branch.id());
763
764 src_dir
765 .move_entry(&src_name, src_entry, dst_dir, dst_name, dst_vv)
766 .await?;
767
768 Ok(())
769 }
770
771 pub fn local_branch(&self) -> Result<Branch> {
774 self.shared.local_branch()
775 }
776
777 #[cfg(test)]
780 pub fn get_branch(&self, id: PublicKey) -> Result<Branch> {
781 self.shared.get_branch(id)
782 }
783
784 pub async fn get_branch_version_vector(&self, writer_id: &PublicKey) -> Result<VersionVector> {
786 Ok(self
787 .shared
788 .vault
789 .store()
790 .acquire_read()
791 .await?
792 .load_latest_approved_root_node(writer_id, RootNodeFilter::Any)
793 .await?
794 .proof
795 .into_version_vector())
796 }
797
798 pub async fn get_merged_version_vector(&self) -> Result<VersionVector> {
800 Ok(self
801 .shared
802 .vault
803 .store()
804 .acquire_read()
805 .await?
806 .load_latest_approved_root_nodes()
807 .try_fold(VersionVector::default(), |mut merged, node| {
808 merged.merge(&node.proof.version_vector);
809 future::ready(Ok(merged))
810 })
811 .await?)
812 }
813
814 pub fn subscribe(&self) -> broadcast::Receiver<Event> {
816 self.shared.vault.event_tx.subscribe()
817 }
818
819 pub async fn sync_progress(&self) -> Result<Progress> {
822 Ok(self.shared.vault.store().sync_progress().await?)
823 }
824
825 pub async fn check_integrity(&self) -> Result<bool> {
828 Ok(self.shared.vault.store().check_integrity().await?)
829 }
830
831 async fn root(&self) -> Result<JointDirectory> {
833 let local_branch = self.local_branch()?;
834 let branches = self.shared.load_branches().await?;
835
836 let branches = if local_branch.keys().write().is_some()
841 && branches
842 .iter()
843 .all(|branch| branch.id() != local_branch.id())
844 {
845 let mut branches = branches;
846 branches.push(local_branch.clone());
847 branches
848 } else {
849 branches
850 };
851
852 let mut dirs = Vec::new();
853
854 for branch in branches {
855 let dir = match branch
856 .open_root(DirectoryLocking::Enabled, DirectoryFallback::Enabled)
857 .await
858 {
859 Ok(dir) => dir,
860 Err(error @ Error::Store(store::Error::BranchNotFound)) => {
861 tracing::trace!(
862 branch_id = ?branch.id(),
863 ?error,
864 "Failed to open root directory"
865 );
866 continue;
869 }
870 Err(error @ Error::Store(store::Error::BlockNotFound)) => {
871 tracing::trace!(
872 branch_id = ?branch.id(),
873 ?error,
874 "Failed to open root directory"
875 );
876 continue;
879 }
880 Err(error) => {
881 tracing::error!(
882 branch_id = ?branch.id(),
883 ?error,
884 "Failed to open root directory"
885 );
886 return Err(error);
887 }
888 };
889
890 dirs.push(dir);
891 }
892
893 Ok(JointDirectory::new(Some(local_branch), dirs))
894 }
895
896 pub async fn cd<P: AsRef<Utf8Path>>(&self, path: P) -> Result<JointDirectory> {
897 self.root().await?.cd(path).await
898 }
899
900 pub async fn close(&self) -> Result<()> {
903 for task in [&self.worker_handle, &self.progress_reporter_handle] {
906 let task = task.lock().unwrap().take();
907 if let Some(task) = task {
908 task.abort();
909 task.await.ok();
910 }
911 }
912
913 self.shared.vault.store().close().await?;
914
915 Ok(())
916 }
917
918 pub async fn debug_print_root(&self) {
919 self.debug_print(DebugPrinter::new()).await
920 }
921
922 pub async fn debug_print(&self, print: DebugPrinter) {
923 print.display(&"Repository");
924
925 let branches = match self.shared.load_branches().await {
926 Ok(branches) => branches,
927 Err(error) => {
928 print.display(&format_args!("failed to load branches: {error:?}"));
929 return;
930 }
931 };
932
933 let writer_id = self.shared.credentials.read().unwrap().writer_id;
934
935 for branch in branches {
936 let print = print.indent();
937 let local = if branch.id() == &writer_id {
938 " (local)"
939 } else {
940 ""
941 };
942 print.display(&format_args!(
943 "Branch ID: {:?}{}, root block ID:{:?}",
944 branch.id(),
945 local,
946 branch.root_block_id().await
947 ));
948 let print = print.indent();
949 print.display(&format_args!(
950 "/, vv: {:?}",
951 branch.version_vector().await.unwrap_or_default()
952 ));
953 branch.debug_print(print.indent()).await;
954 }
955
956 print.display(&"Index");
957 let print = print.indent();
958 self.shared.vault.debug_print(print).await;
959 }
960
961 pub async fn count_blocks(&self) -> Result<u64> {
964 Ok(self.shared.vault.store().count_blocks().await?)
965 }
966
967 fn db(&self) -> &db::Pool {
968 self.shared.vault.store().db()
969 }
970
971 fn update_credentials(&self, credentials: Credentials) {
972 tracing::debug!(
973 parent: self.shared.vault.monitor.span(),
974 access = ?credentials.secrets.access_mode(),
975 writer_id = ?credentials.writer_id,
976 "Repository access mode changed"
977 );
978
979 self.shared
980 .vault
981 .block_tracker
982 .set_request_mode(block_request_mode(&credentials.secrets));
983
984 *self.shared.credentials.write().unwrap() = credentials;
985 *self.worker_handle.lock().unwrap() = Some(spawn_worker(self.shared.clone()));
986 }
987}
988
989pub struct RepositoryHandle {
990 pub(crate) vault: Vault,
991}
992
993struct Shared {
994 vault: Vault,
995 credentials: BlockingRwLock<Credentials>,
996 branch_shared: BranchShared,
997}
998
999impl Shared {
1000 fn new(pool: db::Pool, credentials: Credentials, monitor: RepositoryMonitor) -> Self {
1001 let event_tx = EventSender::new(EVENT_CHANNEL_CAPACITY);
1002 let vault = Vault::new(*credentials.secrets.id(), event_tx, pool, monitor);
1003
1004 vault
1005 .block_tracker
1006 .set_request_mode(block_request_mode(&credentials.secrets));
1007
1008 Self {
1009 vault,
1010 credentials: BlockingRwLock::new(credentials),
1011 branch_shared: BranchShared::new(),
1012 }
1013 }
1014
1015 pub fn local_branch(&self) -> Result<Branch> {
1016 let credentials = self.credentials.read().unwrap();
1017
1018 Ok(self.make_branch(
1019 credentials.writer_id,
1020 credentials.secrets.keys().ok_or(Error::PermissionDenied)?,
1021 ))
1022 }
1023
1024 pub fn get_branch(&self, id: PublicKey) -> Result<Branch> {
1025 let credentials = self.credentials.read().unwrap();
1026 let keys = credentials.secrets.keys().ok_or(Error::PermissionDenied)?;
1027
1028 let keys = if id == credentials.writer_id {
1030 keys
1031 } else {
1032 keys.read_only()
1033 };
1034
1035 Ok(self.make_branch(id, keys))
1036 }
1037
1038 fn make_branch(&self, id: PublicKey, keys: AccessKeys) -> Branch {
1039 Branch::new(
1040 id,
1041 self.vault.store().clone(),
1042 keys,
1043 self.branch_shared.clone(),
1044 self.vault.event_tx.clone(),
1045 )
1046 }
1047
1048 pub async fn load_branches(&self) -> Result<Vec<Branch>> {
1049 self.vault
1050 .store()
1051 .acquire_read()
1052 .await?
1053 .load_latest_approved_root_nodes()
1054 .err_into()
1055 .and_then(|root_node| future::ready(self.get_branch(root_node.proof.writer_id)))
1056 .try_collect()
1057 .await
1058 }
1059}
1060
1061fn spawn_worker(shared: Arc<Shared>) -> ScopedJoinHandle<()> {
1062 let span = shared.vault.monitor.span().clone();
1063 scoped_task::spawn(worker::run(shared).instrument(span))
1064}
1065
1066async fn report_sync_progress(vault: Vault) {
1067 let mut prev_progress = Progress { value: 0, total: 0 };
1068
1069 let events = stream::unfold(vault.event_tx.subscribe(), |mut rx| async move {
1070 match rx.recv().await {
1071 Ok(_) | Err(RecvError::Lagged(_)) => Some(((), rx)),
1072 Err(RecvError::Closed) => None,
1073 }
1074 });
1075 let events = Throttle::new(events, Duration::from_secs(1));
1076 let mut events = pin!(events);
1077
1078 while events.next().await.is_some() {
1079 let next_progress = match vault.store().sync_progress().await {
1080 Ok(progress) => progress,
1081 Err(error) => {
1082 tracing::error!("Failed to retrieve sync progress: {:?}", error);
1083 continue;
1084 }
1085 };
1086
1087 if next_progress != prev_progress {
1088 prev_progress = next_progress;
1089 tracing::debug!(
1090 "Sync progress: {} bytes ({:.1})",
1091 prev_progress * BLOCK_SIZE as u64,
1092 prev_progress.percent()
1093 );
1094 }
1095 }
1096}
1097
1098fn block_request_mode(secrets: &AccessSecrets) -> BlockRequestMode {
1099 if secrets.can_read() {
1100 BlockRequestMode::Lazy
1101 } else {
1102 BlockRequestMode::Greedy
1103 }
1104}