Skip to main content

coven_storage/
location_claim.rs

1//! A location every member reaches whole holds one store, which the location
2//! marker at its root names (ยง20.3). The marker is provider metadata,
3//! separate from encrypted store paths.
4
5use crate::{ProviderOps, Storage, StorageConnection, StorageError, StorageFailure};
6use coven_foundation::id_source::StoreId;
7
8pub(crate) const LOCATION_MARKER: &str = "coven-location";
9pub(crate) const LOCATION_MARKER_SIZE: usize = 36;
10
11pub(crate) fn occupied() -> StorageError {
12    StorageFailure::OwnLocationRequired.into()
13}
14
15/// Refuse a marker whose size isn't a store id's before reading its bytes:
16/// it is someone else's object.
17pub(crate) fn check_marker_size(size: u64) -> Result<(), StorageError> {
18    if size != LOCATION_MARKER_SIZE as u64 {
19        return Err(occupied());
20    }
21    Ok(())
22}
23
24impl<P: ProviderOps + ?Sized> StorageConnection<P> {
25    /// First setup claims the location after provider validation and before
26    /// publishing store state. An occupied marker is accepted only when its
27    /// exact bytes name this store. No failure removes or replaces the marker.
28    /// A location that holds several stores has no marker.
29    pub async fn claim_location(&self, store: StoreId) -> Result<(), StorageError> {
30        if !self.config().holds_one_store() {
31            return Ok(());
32        }
33        // A resend that finds the marker occupied reads it, as any occupied
34        // marker is read: its exact bytes settle it.
35        let created = self
36            .resent(move || async move {
37                self.cooling()?;
38                self.observed(
39                    self.send(
40                        crate::RequestKind::Create,
41                        LOCATION_MARKER_SIZE as u64,
42                        self.provider.create_location_marker(store, self.device),
43                    )
44                    .await,
45                )
46                .await
47            })
48            .await;
49        match created {
50            Ok(()) => Ok(()),
51            Err(error) if error.own_failure() == Some(StorageFailure::AlreadyExists) => {
52                self.check_location_claim(store).await
53            }
54            Err(error) => Err(error),
55        }
56    }
57
58    /// Reconnect, restore and join read the exact location marker without
59    /// listing the root or claiming an absent marker. Provider errors survive;
60    /// missing, malformed and other-store markers refuse this connection.
61    pub async fn check_location_claim(&self, store: StoreId) -> Result<(), StorageError> {
62        if !self.config().holds_one_store() {
63            return Ok(());
64        }
65        let read = self
66            .resent(move || async move {
67                self.cooling()?;
68                let read = self
69                    .send(crate::RequestKind::Read, 0, self.provider.read_location_marker())
70                    .await;
71                if let Ok(bytes) = &read {
72                    self.received(bytes.len() as u64);
73                }
74                self.observed(read).await
75            })
76            .await;
77        match read {
78            Ok(bytes) if bytes == store.to_string().as_bytes() => Ok(()),
79            Ok(_) => Err(occupied()),
80            Err(error) if error.failure() == StorageFailure::NotFound => Err(occupied()),
81            Err(error) => Err(error),
82        }
83    }
84}
85
86#[cfg(test)]
87#[path = "location_claim_tests.rs"]
88mod tests;