1pub const STORE_ID: coven_foundation::id_source::StoreId =
4 coven_foundation::id_source::StoreId(uuid::Uuid::from_bytes([0x10; 16]));
5pub const DEVICE: coven_foundation::id_source::DeviceId =
7 coven_foundation::id_source::DeviceId(4242);
8
9use crate::session::SessionState;
10use crate::*;
11use async_trait::async_trait;
12use coven_foundation::clock::ClockRef;
13use std::collections::BTreeMap;
14use std::sync::Arc;
15use std::time::Duration;
16use tokio::sync::Mutex;
17
18#[path = "memory_upload.rs"]
19mod memory_upload;
20use memory_upload::Pending;
21
22#[path = "memory_reads.rs"]
23mod memory_reads;
24pub use memory_reads::BodyFailure;
25#[path = "memory_listing.rs"]
26mod memory_listing;
27#[path = "memory_requests.rs"]
28mod memory_requests;
29pub use memory_requests::MemoryRequest;
30
31#[path = "memory_faults.rs"]
32mod memory_faults;
33#[path = "memory_objects.rs"]
34mod memory_objects;
35pub use memory_faults::{Faults, Garble};
36
37struct Object {
38 upload_id: Option<u64>,
39 bytes: Arc<[u8]>,
40 stored_at: coven_format::value::StorageTime,
41 revision: u64,
42}
43impl Object {
44 fn metadata(&self, path: &ObjectPath, time_resolution: Duration) -> StoredObject {
45 StoredObject {
46 path: path.clone(),
47 size: self.bytes.len() as u64,
48 stored_at: self.stored_at,
49 time_resolution,
50 object_id: path.as_str().into(),
51 revision: self.revision.to_string(),
52 replace_token: self.revision.to_string(),
53 checksum: None,
54 }
55 }
56}
57const OWNER_ACCOUNT: &str = "[email protected]";
59
60#[derive(Clone)]
61enum Account {
62 Owner,
63 Recipient(String),
64}
65enum AccountAccess {
66 Invited,
67 Joined,
68}
69struct MemoryState {
70 location_marker: Option<Vec<u8>>,
71 folder_name: String,
73 objects: memory_objects::MemoryObjects,
74 uploads: BTreeMap<u64, Pending>,
75 next: u64,
76 accounts: BTreeMap<String, AccountAccess>,
77 retained_access: BTreeMap<String, Vec<RetainedAccess>>,
78 faults: Faults,
79 creation_barrier: Option<Arc<tokio::sync::Barrier>>,
83 creation_gate: Option<Arc<tokio::sync::Semaphore>>,
84 part_gate: Option<Arc<tokio::sync::Semaphore>>,
85 read_gate: Option<Arc<tokio::sync::Semaphore>>,
86 ranges: Vec<ByteRange>,
87 reads: Vec<(ObjectPath, u64, u64)>,
88 moved: BTreeMap<ObjectPath, u64>,
90 received_buffers: Vec<usize>,
91 sent_bytes: u64,
92 largest_part: usize,
93 held_listing: Option<HeldRequest>,
94 held_read: Option<memory_reads::HeldRead>,
95 held_body: Option<memory_reads::HeldBody>,
96 held_creation: Option<HeldRequest>,
97 held_request: Option<HeldNext>,
98 held_access: Option<memory_requests::HeldAccess>,
99 rejected_tokens: std::collections::BTreeSet<String>,
100}
101
102impl MemoryState {
103 fn next_id(&mut self) -> Result<u64, StorageError> {
104 let id = self.next;
105 self.next = id
106 .checked_add(1)
107 .ok_or(StorageFailure::Protocol.with_source("memory ids exhausted"))?;
108 Ok(id)
109 }
110
111 fn refused_after_storing(&mut self, provider: CloudProvider) -> Result<(), StorageError> {
114 if self.faults.refuse_after_storing == 0 {
115 return Ok(());
116 }
117 self.faults.refuse_after_storing -= 1;
118 Err(crate::providers::ProviderResponse::injected(
119 provider,
120 StorageFailure::RateLimited,
121 429,
122 None,
123 ))
124 }
125}
126
127pub struct RequestWatch(tokio::sync::watch::Receiver<u64>);
130
131impl RequestWatch {
132 pub fn current(&mut self) -> u64 {
135 *self.0.borrow_and_update()
136 }
137
138 pub async fn changed(&mut self) -> Result<(), tokio::sync::watch::error::RecvError> {
141 self.0.changed().await
142 }
143
144 pub async fn wait_for(
147 &mut self,
148 reached: impl FnMut(&u64) -> bool,
149 ) -> Result<(), tokio::sync::watch::error::RecvError> {
150 self.0.wait_for(reached).await.map(drop)
151 }
152}
153
154struct HeldNext {
157 request: Option<MemoryRequest>,
158 started: tokio::sync::oneshot::Sender<()>,
159 resume: tokio::sync::oneshot::Receiver<()>,
160}
161
162struct HeldRequest {
163 prefix: ObjectPrefix,
164 listed: tokio::sync::oneshot::Sender<()>,
165 resume: tokio::sync::oneshot::Receiver<()>,
166}
167
168pub type MemoryStorage = StorageConnection<MemoryProvider>;
185
186#[derive(Clone)]
188pub struct MemoryProvider {
189 config: StorageConfig,
190 online: Arc<std::sync::atomic::AtomicBool>,
191 single_limit: u64,
192 part_size: usize,
193 independent_parts: bool,
196 whole_part_reads: bool,
198 listing_page: usize,
199 requests: Arc<tokio::sync::watch::Sender<u64>>,
200 request_trace: Arc<std::sync::Mutex<Vec<MemoryRequest>>>,
201 active_requests: Arc<std::sync::atomic::AtomicUsize>,
202 peak_requests: Arc<std::sync::atomic::AtomicUsize>,
203 account: Account,
204 clock: ClockRef,
205 time_resolution: Duration,
206 tokens: Arc<Mutex<Option<Arc<crate::HeldTokens>>>>,
207 s3_key: Arc<Mutex<Option<S3Credentials>>>,
208 state: Arc<Mutex<MemoryState>>,
209}
210impl Clone for MemoryStorage {
211 fn clone(&self) -> Self {
212 Self::with_gate(
213 self.provider.clone(),
214 self.device,
215 self.publications.clone(),
216 self.cooldown.clone(),
217 self.transfers.clone(),
218 self.provider.clock.clone(),
219 )
220 }
221}
222
223pub struct RefusedRefresh;
226#[async_trait]
227impl crate::TokenCustody for RefusedRefresh {
228 async fn replace(&self, _: &OAuthTokens) -> Result<OAuthTokens, StorageError> {
229 Err(StorageFailure::Authentication.with_source("this sign-in has no custody to refresh it"))
230 }
231}
232
233pub struct UnrestrictedPublications;
235impl PublicationGate for UnrestrictedPublications {
236 fn check(&self, _: &ObjectPath) -> Result<(), StorageError> {
237 Ok(())
238 }
239}
240
241pub fn admitted_part<'a>(
245 session: &mut UploadSession,
246 bytes: &'a [u8],
247) -> Result<PartPermit<'a>, StorageError> {
248 let part = session.next_part(bytes.len())?;
249 let publication =
250 PublicationRequest::new(Arc::new(UnrestrictedPublications), session.path().clone());
251 Ok(PartPermit::admit(session, bytes, part, publication))
252}
253
254impl MemoryStorage {
255 pub(crate) fn from_memory(provider: Arc<MemoryProvider>) -> Self {
258 let clock = provider.clock.clone();
259 Self::with_gate(
260 provider,
261 DEVICE,
262 Arc::new(UnrestrictedPublications),
263 Arc::new(crate::NoCooldown),
264 Arc::new(crate::TransferBudget::new(crate::TransferLimits::default())),
265 clock,
266 )
267 }
268 pub fn builder() -> MemoryStorageBuilder {
271 MemoryStorageBuilder::new()
272 }
273 pub fn for_recipient(owner: &Self, email: &str) -> Result<Self, StorageError> {
277 if owner.provider.config.provider() == CloudProvider::S3 || email.is_empty() {
278 return Err(StorageFailure::InvalidConfiguration
279 .with_source("recipient requires a sharing account"));
280 }
281 Ok(Self::from_memory(Arc::new(MemoryProvider {
282 config: owner.config(),
283 online: Arc::new(std::sync::atomic::AtomicBool::new(true)),
284 single_limit: owner.provider.single_limit,
285 part_size: owner.provider.part_size,
286 independent_parts: owner.provider.independent_parts,
287 whole_part_reads: owner.provider.whole_part_reads,
288 listing_page: owner.provider.listing_page,
289 requests: owner.provider.requests.clone(),
290 request_trace: owner.provider.request_trace.clone(),
291 active_requests: owner.provider.active_requests.clone(),
292 peak_requests: owner.provider.peak_requests.clone(),
293 account: Account::Recipient(email.to_ascii_lowercase()),
294 clock: owner.provider.clock.clone(),
295 time_resolution: owner.provider.time_resolution,
296 tokens: Arc::new(Mutex::new(None)),
297 s3_key: Arc::new(Mutex::new(None)),
298 state: owner.provider.state.clone(),
299 })))
300 }
301 pub fn for_device(&self) -> Self {
304 let mut device = (*self.provider).clone();
305 device.online = Arc::new(std::sync::atomic::AtomicBool::new(true));
306 device.tokens = Arc::new(Mutex::new(None));
307 device.s3_key = Arc::new(Mutex::new(None));
308 device.requests = Arc::new(tokio::sync::watch::channel(0).0);
309 device.request_trace = Arc::new(std::sync::Mutex::new(Vec::new()));
310 device.active_requests = Arc::new(std::sync::atomic::AtomicUsize::new(0));
311 device.peak_requests = Arc::new(std::sync::atomic::AtomicUsize::new(0));
312 Self::from_memory(Arc::new(device))
313 }
314 pub async fn connect_device(
317 &self,
318 config: StorageConfig,
319 auth: crate::ProviderAuth,
320 ) -> Result<Self, StorageError> {
321 crate::ConnectionCredentials { location: config.clone(), credentials: auth.credentials() }
322 .validate()?;
323 if !config.same_location(&self.provider.config) {
324 return Err(StorageFailure::InvalidConfiguration
325 .with_source("memory connector location mismatch"));
326 }
327 let mut connection = (*self.provider).clone();
328 connection.config = config;
329 connection.tokens = Arc::new(Mutex::new(None));
332 connection.s3_key = Arc::new(Mutex::new(None));
333 match auth {
334 crate::ProviderAuth::OAuth { tokens, custody } => {
335 *connection.tokens.lock().await =
336 Some(Arc::new(crate::HeldTokens::new(tokens, custody)));
337 }
338 crate::ProviderAuth::S3(keys) => connection.set_s3_credentials(keys).await?,
339 crate::ProviderAuth::CloudKit => {}
340 }
341 Ok(Self::from_memory(Arc::new(connection)))
342 }
343
344 pub fn set_online(&self, online: bool) {
346 self.provider.online.store(online, std::sync::atomic::Ordering::SeqCst);
347 }
348 pub fn subscribe_requests(&self) -> RequestWatch {
350 RequestWatch(self.provider.requests.subscribe())
351 }
352 pub fn peak_requests(&self) -> usize {
354 self.provider.peak_requests.load(std::sync::atomic::Ordering::SeqCst)
355 }
356 pub fn reset_request_peak(&self) {
358 self.provider.peak_requests.store(0, std::sync::atomic::Ordering::SeqCst);
359 }
360 pub fn request_count(&self) -> u64 {
362 *self.provider.requests.borrow()
363 }
364 pub async fn s3_credentials(&self) -> Option<S3Credentials> {
366 self.provider.s3_key.lock().await.clone()
367 }
368 pub async fn s3_access_key_id(&self) -> Option<String> {
370 self.s3_credentials().await.map(|key| key.access_key_id)
371 }
372 pub async fn ranges(&self) -> Vec<ByteRange> {
374 self.provider.state.lock().await.ranges.clone()
375 }
376 pub async fn reads(&self) -> Vec<(ObjectPath, u64, u64)> {
378 self.provider.state.lock().await.reads.clone()
379 }
380 pub async fn moved_bytes(&self, path: &ObjectPath) -> u64 {
384 self.provider.state.lock().await.moved.get(path).copied().unwrap_or(0)
385 }
386 pub async fn transferred(&self) -> (u64, usize) {
388 let state = self.provider.state.lock().await;
389 (state.sent_bytes, state.largest_part)
390 }
391 pub async fn corrupt_byte(&self, path: &ObjectPath, offset: usize) -> Result<(), StorageError> {
393 let mut state = self.provider.state.lock().await;
394 let object = state.objects.get_mut(path).ok_or(StorageFailure::NotFound)?;
395 let byte =
396 Arc::make_mut(&mut object.bytes).get_mut(offset).ok_or(StorageFailure::InvalidRange)?;
397 *byte ^= 1;
398 Ok(())
399 }
400 pub async fn plant(&self, path: &ObjectPath, bytes: &[u8]) -> Result<(), StorageError> {
403 let stored_at = self.provider.storage_time()?;
404 let mut state = self.provider.state.lock().await;
405 let revision = state.next_id()?;
406 state.objects.insert(
407 path.clone(),
408 Object { revision, upload_id: None, bytes: bytes.into(), stored_at },
409 );
410 Ok(())
411 }
412 pub async fn wait_for_object(&self, wanted: impl Fn(&ObjectPath) -> bool) -> ObjectPath {
417 let mut changes = self.provider.state.lock().await.objects.watch();
418 loop {
419 let state = self.provider.state.lock().await;
420 if let Some(path) = state.objects.keys().find(|path| wanted(path)) {
421 return path.clone();
422 }
423 drop(state);
424 changes.changed().await;
425 }
426 }
427 pub async fn reject_access_token(&self, token: &str) {
430 self.provider.state.lock().await.rejected_tokens.insert(token.into());
431 }
432 pub async fn set_faults(&self, faults: Faults) {
434 self.provider.state.lock().await.faults = faults;
435 }
436 pub async fn hold_creations(&self, barrier: Arc<tokio::sync::Barrier>) {
439 self.provider.state.lock().await.creation_barrier = Some(barrier);
440 }
441 pub async fn gate_creations(&self, gate: Arc<tokio::sync::Semaphore>) {
444 self.provider.state.lock().await.creation_gate = Some(gate);
445 }
446 pub async fn gate_parts(&self, gate: Arc<tokio::sync::Semaphore>) {
449 self.provider.state.lock().await.part_gate = Some(gate);
450 }
451 pub async fn gate_reads(&self, gate: Arc<tokio::sync::Semaphore>) {
454 self.provider.state.lock().await.read_gate = Some(gate);
455 }
456 pub async fn shared_with(&self, account: &str) -> bool {
459 self.provider.state.lock().await.accounts.contains_key(&account.to_ascii_lowercase())
460 }
461 pub async fn joined(&self, account: &str) -> bool {
464 matches!(
465 self.provider.state.lock().await.accounts.get(&account.to_ascii_lowercase()),
466 Some(AccountAccess::Joined)
467 )
468 }
469 pub async fn set_retained_access(&self, account: &str, shares: Vec<RetainedAccess>) {
471 let mut state = self.provider.state.lock().await;
472 let account = account.to_ascii_lowercase();
473 if shares.is_empty() {
474 state.retained_access.remove(&account);
475 } else {
476 state.retained_access.insert(account, shares);
477 }
478 }
479}
480
481impl MemoryProvider {
482 async fn refuse_file_upload(&self, path: &ObjectPath) -> Result<(), StorageError> {
487 if path.object_id().is_none() {
488 return Ok(());
489 }
490 let mut state = self.state.lock().await;
491 if state.faults.panic_file_uploads > 0 {
492 state.faults.panic_file_uploads -= 1;
493 drop(state);
494 panic!("the upload of {path:?} panicked");
495 }
496 if state.faults.refuse_file_uploads == 0 {
497 return Ok(());
498 }
499 state.faults.refuse_file_uploads -= 1;
500 let faults = &state.faults;
501 Err(self.injected((faults.failure, faults.response, faults.retry_after)))
502 }
503 fn session_id(&self, session: &UploadSession) -> Result<u64, StorageError> {
504 match session.state {
505 SessionState::Memory { id, .. } | SessionState::MemoryParts { id, .. } => Ok(id),
506 _ => Err(StorageFailure::SessionMismatch.into()),
507 }
508 }
509}
510
511#[async_trait]
512impl ProviderOps for MemoryProvider {
513 async fn create_location_marker(
514 &self,
515 store: coven_foundation::id_source::StoreId,
516 _device: coven_foundation::id_source::DeviceId,
517 ) -> Result<(), StorageError> {
518 self.before(MemoryRequest::Other("create_location_marker")).await?;
519 let mut state = self.state.lock().await;
520 if state.location_marker.is_some() {
521 return Err(StorageFailure::AlreadyExists.into());
522 }
523 state.location_marker = Some(store.to_string().into_bytes());
524 if std::mem::replace(&mut state.faults.lose_completion_reply, false) {
525 return Err(StorageFailure::Network.into());
526 }
527 Ok(())
528 }
529 async fn read_location_marker(&self) -> Result<Vec<u8>, StorageError> {
530 self.before(MemoryRequest::Other("read_location_marker")).await?;
531 self.state.lock().await.location_marker.clone().ok_or(StorageFailure::NotFound.into())
532 }
533 async fn folder_label(&self) -> Result<crate::FolderLabel, StorageError> {
536 self.before(MemoryRequest::Other("folder_label")).await?;
537 let id = match &self.config {
538 StorageConfig::Dropbox {
539 location: crate::DropboxLocation::SharedFolder { namespace_id },
540 ..
541 } => namespace_id,
542 StorageConfig::OneDrive { folder_id, .. } => folder_id,
543 _ => return Err(StorageFailure::ProviderUnavailable.into()),
544 };
545 let mut web_url = url::Url::parse("https://storage.example.test/folders/")
546 .expect("a fixed address parses");
547 web_url.path_segments_mut().expect("an https address holds paths").pop_if_empty().push(id);
548 Ok(crate::FolderLabel { name: self.state.lock().await.folder_name.clone(), web_url })
549 }
550 async fn account(&self) -> Result<String, StorageError> {
551 self.before_request(MemoryRequest::Other("account")).await?;
552 Ok(match &self.account {
553 Account::Owner => OWNER_ACCOUNT.into(),
554 Account::Recipient(account) => account.clone(),
555 })
556 }
557 async fn set_s3_credentials(&self, credentials: S3Credentials) -> Result<(), StorageError> {
558 credentials.validate()?;
559 if self.config.provider() != CloudProvider::S3 {
560 return Err(StorageFailure::InvalidConfiguration.with_source("invalid S3 key"));
561 }
562 *self.s3_key.lock().await = Some(credentials);
563 Ok(())
564 }
565 fn config(&self) -> StorageConfig {
566 self.config.clone()
567 }
568 async fn set_oauth_tokens(&self, tokens: OAuthTokens) -> Result<(), StorageError> {
569 if !self.config.provider().signs_in_with_oauth() {
570 return Err(
571 StorageFailure::InvalidConfiguration.with_source("provider does not use OAuth")
572 );
573 }
574 let mut held = self.tokens.lock().await;
575 match held.as_ref() {
576 Some(held) => held.install(tokens).await,
577 None => {
578 *held = Some(Arc::new(crate::HeldTokens::new(tokens, Arc::new(RefusedRefresh))))
579 }
580 }
581 Ok(())
582 }
583 fn single_request_limit(&self) -> u64 {
584 self.single_limit
585 }
586 fn read_part_size(&self) -> Option<std::num::NonZeroU64> {
587 std::num::NonZeroU64::new(self.part_size as u64).filter(|_| self.whole_part_reads)
588 }
589 #[expect(
590 clippy::map_err_ignore,
591 reason = "a send fails only when the receiver is gone, which this names"
592 )]
593 async fn create(
594 &self,
595 publication: &PublicationRequest,
596 path: &ObjectPath,
597 bytes: &[u8],
598 sent: &ByteProgress,
599 _device: coven_foundation::id_source::DeviceId,
600 ) -> Result<Option<StoredObject>, StorageError> {
601 publication.check()?;
602 sent.moved(bytes.len() as u64);
605 self.before(MemoryRequest::Create(path.clone())).await?;
606 self.refuse_file_upload(path).await?;
607 let mut state = self.state.lock().await;
608 if let Some((failure, status)) = state.faults.create_failures.get(path) {
609 return Err(crate::providers::ProviderResponse::injected(
610 self.config.provider(),
611 *failure,
612 *status,
613 None,
614 ));
615 }
616 let held = if state.held_creation.as_ref().is_some_and(|held| held.prefix.contains(path)) {
617 state.held_creation.take()
618 } else {
619 None
620 };
621 drop(state);
622 if let Some(held) = held {
623 held.listed
624 .send(())
625 .map_err(|_| StorageFailure::Protocol.with_source("creation observer dropped"))?;
626 held.resume
627 .await
628 .map_err(|_| StorageFailure::Protocol.with_source("creation release dropped"))?;
629 }
630 let mut state = self.state.lock().await;
631 if state.objects.contains_key(path) && !state.faults.overwrite_create {
632 if state.faults.fail_duplicate_cleanup {
633 return Err(StorageError::Cleanup {
634 operation: Box::new(StorageError::Failure(StorageFailure::AlreadyExists)),
635 cleanup: Box::new(StorageError::Failure(StorageFailure::Network)),
636 });
637 }
638 return Err(StorageFailure::AlreadyExists.into());
639 }
640 let revision = state.next_id()?;
641 let object = Object {
642 revision,
643 upload_id: None,
644 bytes: bytes.into(),
645 stored_at: self.storage_time()?,
646 };
647 let created = object.metadata(path, self.time_resolution);
648 state.objects.insert(path.clone(), object);
649 if std::mem::replace(&mut state.faults.lose_completion_reply, false) {
650 return Err(StorageFailure::Network.into());
651 }
652 state.refused_after_storing(self.config.provider())?;
653 Ok(Some(created))
654 }
655
656 async fn replace(
657 &self,
658 publication: &PublicationRequest,
659 previous: &StoredObject,
660 bytes: &[u8],
661 _device: coven_foundation::id_source::DeviceId,
662 ) -> Result<(), StorageError> {
663 let path = &previous.path;
664 publication.check()?;
665 self.before(MemoryRequest::Replace(path.clone())).await?;
666 let mut state = self.state.lock().await;
667 if state
668 .objects
669 .get(path)
670 .is_none_or(|object| object.revision.to_string() != previous.replace_token)
671 {
672 return Err(StorageFailure::Conflict.into());
673 }
674 if state.faults.refuse_replacements > 0 {
675 state.faults.refuse_replacements -= 1;
676 return Err(StorageFailure::Conflict.into());
677 }
678 let revision = state.next_id()?;
679 state.objects.insert(
680 path.clone(),
681 Object {
682 revision,
683 upload_id: None,
684 bytes: bytes.into(),
685 stored_at: self.storage_time()?,
686 },
687 );
688 if std::mem::replace(&mut state.faults.lose_completion_reply, false) {
689 return Err(StorageFailure::Network.into());
690 }
691 state.refused_after_storing(self.config.provider())
692 }
693 async fn status(&self, path: &ObjectPath) -> Result<Option<StoredObject>, StorageError> {
694 self.before(MemoryRequest::Status(path.clone())).await?;
695 let state = self.state.lock().await;
696 if let Some(failure) = state.faults.status_failures.get(path) {
697 return Err((*failure).into());
698 }
699 Ok(state.objects.get(path).map(|object| object.metadata(path, self.time_resolution)))
700 }
701 async fn stream_object(
702 &self,
703 path: &ObjectPath,
704 range: Option<ByteRange>,
705 _received: &ByteProgress,
708 ) -> Result<ObjectStream<'_>, StorageError> {
709 self.stream_recorded(path, range).await
710 }
711
712 #[expect(
713 clippy::map_err_ignore,
714 reason = "a send fails only when the receiver is gone, which this names"
715 )]
716 async fn list(&self, listing: &mut ObjectListing) -> Result<(), StorageError> {
717 let prefix = listing.prefix();
718 self.before(MemoryRequest::List(prefix.clone())).await?;
719 let now = self.storage_time()?;
720 let state = self.state.lock().await;
721 if let Some(failure) = state.faults.list_failures.get(prefix) {
722 return Err((*failure).into());
723 }
724 let objects: Vec<_> = state
725 .objects
726 .iter()
727 .filter(|(path, object)| {
728 !state.faults.hide_listings
729 && prefix.contains(path)
730 && state.faults.listing_lag.is_none_or(|lag| {
731 now.duration_since(object.stored_at).is_some_and(|age| age > lag)
732 })
733 })
734 .map(|(path, object)| object.metadata(path, self.time_resolution))
735 .collect::<Vec<_>>();
736 drop(state);
737 self.further_pages(MemoryRequest::List(prefix.clone()), objects.len()).await?;
738 let mut state = self.state.lock().await;
739 let hold = if state.held_listing.as_ref().is_some_and(|held| &held.prefix == prefix) {
740 state.held_listing.take()
741 } else {
742 None
743 };
744 drop(state);
745 if let Some(held) = hold {
746 held.listed
747 .send(())
748 .map_err(|_| StorageFailure::Protocol.with_source("listing observer dropped"))?;
749 held.resume
750 .await
751 .map_err(|_| StorageFailure::Protocol.with_source("listing release dropped"))?;
752 }
753 for object in objects {
754 listing.insert(object)?;
755 }
756 Ok(())
757 }
758 async fn delete(&self, path: &ObjectPath) -> Result<(), StorageError> {
759 self.before(MemoryRequest::Delete(path.clone())).await?;
760 let mut state = self.state.lock().await;
761 if let Some(failure) = state.faults.delete_failures.get(path) {
762 return Err((*failure).into());
763 }
764 state.objects.remove(path);
765 Ok(())
766 }
767 async fn grant_access(&self, account: &str) -> Result<AccessGrant, StorageError> {
768 self.hold_access(account, true).await;
769 self.before_request(MemoryRequest::Other("grant_access")).await?;
770 if self.config.provider() == CloudProvider::S3 {
771 return Ok(AccessGrant::CreateAccessKey);
772 }
773 if !matches!(self.account, Account::Owner) {
774 return Err(StorageFailure::NotStoreOwner.into());
775 }
776 if account.is_empty() {
777 return Err(StorageFailure::InvalidConfiguration.with_source("empty sharing account"));
778 }
779 if account.eq_ignore_ascii_case(OWNER_ACCOUNT) {
780 return Err(StorageFailure::InviteeIsStoreOwner.into());
781 }
782 if let Some(failure) = self.state.lock().await.faults.grant_failure {
783 return Err(failure.into());
784 }
785 let invitation = match self.config.provider() {
786 CloudProvider::CloudKit => StorageInvitation::new(
787 self.config(),
788 crate::invitation::InvitationAcceptance::CloudKitShare {
789 url: coven_crypto::SecretText::new("https://icloud.com/share/memory".into()),
790 },
791 )?,
792 CloudProvider::OneDrive => StorageInvitation::new(
793 self.config(),
794 crate::invitation::InvitationAcceptance::OneDriveShare {
795 token: coven_crypto::SecretText::new(account.to_ascii_lowercase()),
796 },
797 )?,
798 _ => StorageInvitation::for_account(self.config())?,
799 };
800 self.state
801 .lock()
802 .await
803 .accounts
804 .entry(account.to_ascii_lowercase())
805 .or_insert(AccountAccess::Invited);
806 Ok(AccessGrant::Granted { invitation })
807 }
808 async fn join(&self, invitation: &StorageInvitation) -> Result<(), StorageError> {
809 self.before_request(MemoryRequest::Other("join")).await?;
810 use crate::invitation::InvitationAcceptance as Acceptance;
811 if matches!(invitation.acceptance, Acceptance::Granted) {
812 return Ok(());
813 }
814 if let Acceptance::CloudKitShare { url } = &invitation.acceptance {
815 if url.as_str() != "https://icloud.com/share/memory" {
816 return Err(StorageFailure::InvitationMismatch.into());
817 }
818 }
819 if let Account::Recipient(email) = &self.account {
820 match &invitation.acceptance {
821 Acceptance::OneDriveShare { token } if token.as_str() != email => {
822 return Err(StorageFailure::InvitationMismatch.into())
823 }
824 _ => {}
825 }
826 let mut state = self.state.lock().await;
827 let access = state.accounts.get_mut(email).ok_or(StorageFailure::PermissionDenied)?;
828 *access = AccountAccess::Joined;
829 }
830 Ok(())
831 }
832 async fn leave(&self, invitation: &StorageInvitation) -> Result<(), StorageError> {
836 self.before_request(MemoryRequest::Other("leave")).await?;
837 use crate::invitation::InvitationAcceptance as Acceptance;
838 if let (Acceptance::DropboxMount, Account::Recipient(email)) =
839 (&invitation.acceptance, &self.account)
840 {
841 if let Some(access) = self.state.lock().await.accounts.get_mut(email) {
842 *access = AccountAccess::Invited;
843 }
844 }
845 Ok(())
846 }
847 async fn revoke_access(&self, account: &str) -> Result<Vec<RetainedAccess>, StorageError> {
848 self.hold_access(account, false).await;
849 self.before_request(MemoryRequest::Other("revoke_access")).await?;
850 if self.config.provider() == CloudProvider::S3 {
851 return Err(
852 StorageFailure::InvalidConfiguration.with_source("S3 has no account sharing")
853 );
854 }
855 if !matches!(self.account, Account::Owner) {
856 return Err(StorageFailure::NotStoreOwner.into());
857 }
858 let mut state = self.state.lock().await;
859 let account = account.to_ascii_lowercase();
860 if let Some(shares) = state.retained_access.get(&account) {
861 return Ok(shares.clone());
862 }
863 state.accounts.remove(&account);
864 Ok(Vec::new())
865 }
866 async fn begin_upload(
867 &self,
868 publication: &PublicationRequest,
869 target: &UploadTarget,
870 total: u64,
871 device: coven_foundation::id_source::DeviceId,
872 ) -> Result<UploadSession, StorageError> {
873 self.memory_begin_upload(publication, target, total, device).await
874 }
875 async fn device_uploads(
876 &self,
877 store: coven_foundation::id_source::StoreId,
878 device: coven_foundation::id_source::DeviceId,
879 ) -> Result<Vec<LeftUpload>, StorageError> {
880 self.memory_device_uploads(store, device).await
881 }
882 async fn settled_claim_uploads(
883 &self,
884 store: coven_foundation::id_source::StoreId,
885 through: u64,
886 ) -> Result<Vec<LeftUpload>, StorageError> {
887 self.memory_settled_claim_uploads(store, through).await
888 }
889 fn dates_floor_sessions(&self) -> bool {
890 true
891 }
892 async fn superseded_floor_uploads(
893 &self,
894 floor: &StoredObject,
895 ) -> Result<Vec<LeftUpload>, StorageError> {
896 self.memory_superseded_floor_uploads(floor).await
897 }
898 async fn end_left_upload(&self, upload: &LeftUpload) -> Result<(), StorageError> {
899 self.memory_end_left_upload(upload).await
900 }
901 async fn resume_upload(
902 &self,
903 publication: &PublicationRequest,
904 session: &mut UploadSession,
905 ) -> Result<(), StorageError> {
906 self.memory_resume_upload(publication, session).await
907 }
908 async fn upload_part(
909 &self,
910 publication: &PublicationRequest,
911 session: &mut UploadSession,
912 bytes: &[u8],
913 end: u64,
914 sent: &ByteProgress,
915 ) -> Result<(), StorageError> {
916 self.memory_upload_part(publication, session, bytes, end, sent).await
917 }
918 async fn send_independent_part(
919 &self,
920 publication: &PublicationRequest,
921 session: &UploadSession,
922 offset: u64,
923 bytes: &[u8],
924 sent: &ByteProgress,
925 ) -> Result<crate::ConfirmedPart, StorageError> {
926 self.memory_send_part(publication, session, offset, bytes, sent).await
927 }
928 async fn finish_upload(
929 &self,
930 publication: &PublicationRequest,
931 session: &mut UploadSession,
932 ) -> Result<(), StorageError> {
933 self.memory_finish_upload(publication, session).await
934 }
935 async fn abort_upload(
936 &self,
937 publication: &PublicationRequest,
938 session: &UploadSession,
939 ) -> Result<(), StorageError> {
940 self.memory_abort_upload(publication, session).await
941 }
942}
943
944#[path = "conformance.rs"]
945mod conformance;
946pub use conformance::Conformance;
947
948#[cfg(test)]
949#[path = "test_utils_tests.rs"]
950mod tests;
951
952#[path = "memory_connector.rs"]
953mod connector;
954pub use connector::{conforming_detection, DETECTED_S3_SETTINGS};
955
956#[path = "memory_storage_builder.rs"]
957mod builder;
958pub use builder::MemoryStorageBuilder;