1use super::*;
3use crate::session::{part_span, Confirmation, PlanStart, StartSent};
4
5pub(super) struct MemoryStart<'a> {
9 publication: &'a PublicationRequest,
10 sent: &'a StartSent,
11}
12
13impl MemoryStart<'_> {
14 pub(super) fn go(&self) -> Result<(), StorageError> {
15 self.publication.check()?;
16 self.sent.going();
17 Ok(())
18 }
19}
20
21pub(super) struct Pending {
22 device: coven_foundation::id_source::DeviceId,
23 begun: coven_format::value::StorageTime,
25 target: UploadTarget,
26 total: u64,
27 pub(super) bytes: Vec<u8>,
28 parts: BTreeMap<u64, Vec<u8>>,
31 part_size: usize,
32}
33impl Pending {
34 fn check(&self, session: &UploadSession) -> Result<(), StorageError> {
35 if self.target != session.target
36 || self.total != session.total
37 || self.part_size != session.part_size
38 {
39 return Err(StorageFailure::SessionMismatch.into());
40 }
41 let lost = match &session.state {
42 SessionState::MemoryParts { parts, .. } => {
43 parts.iter().any(|index| !self.parts.contains_key(index))
44 }
45 _ => {
46 (self.bytes.len() as u64) < session.confirmed
47 || self.bytes.len() as u64 > self.total
48 }
49 };
50 if lost {
51 return Err(
52 StorageFailure::Protocol.with_source("memory provider lost confirmed progress")
53 );
54 }
55 Ok(())
56 }
57 fn assembled(self) -> Result<Vec<u8>, StorageError> {
60 if self.parts.is_empty() {
61 return Ok(self.bytes);
62 }
63 let count = self.total.div_ceil(self.part_size as u64);
64 if self.parts.len() as u64 != count {
65 return Err(StorageFailure::InvalidPart.into());
66 }
67 Ok(self.parts.into_values().flatten().collect())
68 }
69}
70fn confirm_published(
71 state: &MemoryState,
72 session: &mut UploadSession,
73 id: u64,
74) -> Result<(), StorageError> {
75 let object = state.objects.get(session.path()).ok_or(StorageFailure::SessionExpired)?;
76 if object.upload_id != Some(id) || object.bytes.len() as u64 != session.total {
77 return Err(StorageFailure::AlreadyExists.into());
78 }
79 session.confirmed = session.total;
80 session.state = SessionState::Memory { id, complete: true };
81 Ok(())
82}
83
84fn owns_name(session: &UploadSession) -> bool {
88 session.path().device() == Some(session.device)
89}
90
91fn end_planned(state: &mut MemoryState, session: &UploadSession) {
94 state.uploads.retain(|_, pending| pending.target.path() != session.path());
95}
96
97fn check_target(state: &MemoryState, target: &UploadTarget) -> Result<(), StorageError> {
98 match target {
99 UploadTarget::Create(path) => {
100 if state.objects.contains_key(path) && !state.faults.overwrite_create {
101 return Err(StorageFailure::AlreadyExists.into());
102 }
103 }
104 UploadTarget::Replace(previous) => {
105 if state
106 .objects
107 .get(&previous.path)
108 .is_none_or(|object| object.revision.to_string() != previous.replace_token)
109 {
110 return Err(StorageFailure::Conflict.into());
111 }
112 }
113 }
114 Ok(())
115}
116
117impl MemoryStorage {
118 pub async fn started_upload(&self, path: &ObjectPath) -> Result<UploadSession, StorageError> {
122 let mut session = self.begin_upload(path, 4).await?;
123 self.upload_part(&mut session, b"part").await?;
124 Ok(session)
125 }
126 pub async fn holds(&self, session: &UploadSession) -> bool {
129 match session.state {
130 SessionState::Memory { id, .. } | SessionState::MemoryParts { id, .. } => {
131 self.provider.state.lock().await.uploads.contains_key(&id)
132 }
133 _ => false,
134 }
135 }
136 pub async fn unfinished_uploads(
139 &self,
140 ) -> Vec<(coven_foundation::id_source::DeviceId, ObjectPath)> {
141 let state = self.provider.state.lock().await;
142 state
143 .uploads
144 .values()
145 .map(|pending| (pending.device, pending.target.path().clone()))
146 .collect()
147 }
148}
149
150impl MemoryProvider {
151 pub(super) async fn memory_device_uploads(
153 &self,
154 store: coven_foundation::id_source::StoreId,
155 device: coven_foundation::id_source::DeviceId,
156 ) -> Result<Vec<LeftUpload>, StorageError> {
157 self.before(MemoryRequest::Other("device_uploads")).await?;
158 let state = self.state.lock().await;
159 Ok(state
160 .uploads
161 .iter()
162 .filter(|(_, pending)| {
163 pending.device == device && pending.target.path().store() == store
164 })
165 .map(|(id, _)| LeftUpload(crate::left_upload::Left::Memory(*id)))
166 .collect())
167 }
168 pub(super) async fn memory_settled_claim_uploads(
172 &self,
173 store: coven_foundation::id_source::StoreId,
174 through: u64,
175 ) -> Result<Vec<LeftUpload>, StorageError> {
176 self.before(MemoryRequest::Other("settled_claim_uploads")).await?;
177 let state = self.state.lock().await;
178 Ok(state
179 .uploads
180 .iter()
181 .filter(|(_, pending)| {
182 let path = pending.target.path();
183 path.store() == store
184 && path.slot_number().is_some_and(|slot| slot.get() <= through)
185 })
186 .map(|(id, _)| LeftUpload(crate::left_upload::Left::Memory(*id)))
187 .collect())
188 }
189 pub(super) async fn memory_superseded_floor_uploads(
193 &self,
194 floor: &StoredObject,
195 ) -> Result<Vec<LeftUpload>, StorageError> {
196 self.before(MemoryRequest::Other("superseded_floor_uploads")).await?;
197 let state = self.state.lock().await;
198 Ok(state
199 .uploads
200 .iter()
201 .filter(|(_, pending)| {
202 pending.target.path() == &floor.path
203 && pending.begun.seconds() < floor.stored_at.seconds()
204 })
205 .map(|(id, _)| LeftUpload(crate::left_upload::Left::Memory(*id)))
206 .collect())
207 }
208 pub(super) async fn memory_end_left_upload(
210 &self,
211 upload: &LeftUpload,
212 ) -> Result<(), StorageError> {
213 let crate::left_upload::Left::Memory(id) = upload.0 else {
214 return Err(StorageFailure::SessionMismatch.into());
215 };
216 self.before(MemoryRequest::Other("end_left_upload")).await?;
217 self.state.lock().await.uploads.remove(&id);
218 Ok(())
219 }
220 pub(super) async fn memory_begin_upload(
221 &self,
222 publication: &PublicationRequest,
223 target: &UploadTarget,
224 total: u64,
225 device: coven_foundation::id_source::DeviceId,
226 ) -> Result<UploadSession, StorageError> {
227 publication.check()?;
228 self.before(MemoryRequest::Other("begin_upload")).await?;
229 self.refuse_file_upload(target.path()).await?;
230 check_target(&*self.state.lock().await, target)?;
231 Ok(UploadSession {
233 device,
234 location: self.config(),
235 target: target.clone(),
236 total,
237 confirmed: 0,
238 part_size: self.part_size,
239 state: SessionState::MemoryPlanned { started: PlanStart::Unstarted },
240 })
241 }
242 pub(super) async fn memory_resume_upload(
243 &self,
244 publication: &PublicationRequest,
245 session: &mut UploadSession,
246 ) -> Result<(), StorageError> {
247 if let SessionState::MemoryPlanned { started } = session.state {
248 if started != PlanStart::MayHaveStarted {
252 return Ok(());
253 }
254 if !owns_name(session) {
255 return Err(StorageFailure::SessionIdentityUnknown.into());
256 }
257 publication.check()?;
258 self.before(MemoryRequest::Other("resume_upload")).await?;
259 end_planned(&mut *self.state.lock().await, session);
260 session.state = SessionState::MemoryPlanned { started: PlanStart::Unstarted };
261 return Ok(());
262 }
263 publication.check()?;
264 self.before(MemoryRequest::Other("resume_upload")).await?;
265 let id = self.session_id(session)?;
266 let state = self.state.lock().await;
267 match state.uploads.get(&id) {
268 Some(pending) => {
269 pending.check(session)?;
270 match &mut session.state {
271 SessionState::MemoryParts { parts, .. } => {
273 *parts = pending.parts.keys().copied().collect();
274 session.confirmed = pending
275 .parts
276 .keys()
277 .filter_map(|index| part_span(pending.total, pending.part_size, *index))
278 .map(|span| span.end - span.start)
279 .sum();
280 }
281 _ => session.confirmed = pending.bytes.len() as u64,
282 }
283 }
284 None => confirm_published(&state, session, id)?,
285 }
286 Ok(())
287 }
288 pub(super) async fn memory_upload_part(
289 &self,
290 publication: &PublicationRequest,
291 session: &mut UploadSession,
292 bytes: &[u8],
293 end: u64,
294 sent: &ByteProgress,
295 ) -> Result<(), StorageError> {
296 if let SessionState::MemoryPlanned { started } = &mut session.state {
297 if *started == PlanStart::MayHaveStarted {
300 return Err(StorageFailure::SessionIdentityUnknown.into());
301 }
302 *started = PlanStart::MayHaveStarted;
304 let start = StartSent::default();
305 if let Err(error) = self.initiate(publication, session, &start).await {
306 session.start_failed(&start);
307 return Err(error);
308 }
309 }
310 if matches!(session.state, SessionState::MemoryParts { .. }) {
311 let offset = end - bytes.len() as u64;
312 let part = self.memory_send_part(publication, session, offset, bytes, sent).await?;
313 return session.confirm(part);
314 }
315 publication.check()?;
316 sent.moved(bytes.len() as u64);
318 self.before(MemoryRequest::Other("upload_part")).await?;
319 let gate = self.state.lock().await.part_gate.clone();
320 if let Some(gate) = gate {
321 gate.acquire()
322 .await
323 .map_err(|closed| StorageFailure::Protocol.with_source(closed))?
324 .forget();
325 }
326 let id = self.session_id(session)?;
327 let mut state = self.state.lock().await;
328 let pending = state.uploads.get(&id).ok_or(StorageFailure::SessionExpired)?;
329 pending.check(session)?;
330 if pending.bytes.len() as u64 != session.confirmed {
331 return Err(StorageFailure::InvalidPart.into());
332 }
333 if std::mem::replace(&mut state.faults.drop_part, false) {
334 return Err(StorageFailure::Network.into());
335 }
336 state
337 .uploads
338 .get_mut(&id)
339 .ok_or(StorageFailure::SessionExpired)?
340 .bytes
341 .extend_from_slice(bytes);
342 if std::mem::replace(&mut state.faults.lose_part_reply, false) {
343 return Err(StorageFailure::Network.into());
344 }
345 state.sent_bytes += bytes.len() as u64;
346 state.largest_part = state.largest_part.max(bytes.len());
347 session.confirmed = end;
348 Ok(())
349 }
350 pub(super) async fn memory_send_part(
354 &self,
355 publication: &PublicationRequest,
356 session: &UploadSession,
357 offset: u64,
358 bytes: &[u8],
359 sent: &ByteProgress,
360 ) -> Result<crate::ConfirmedPart, StorageError> {
361 let SessionState::MemoryParts { id, .. } = session.state else {
362 return Err(StorageFailure::SessionMismatch.into());
363 };
364 publication.check()?;
365 sent.moved(bytes.len() as u64);
367 self.before(MemoryRequest::Other("upload_part")).await?;
368 let gate = self.state.lock().await.part_gate.clone();
369 if let Some(gate) = gate {
370 gate.acquire()
371 .await
372 .map_err(|closed| StorageFailure::Protocol.with_source(closed))?
373 .forget();
374 }
375 let index = offset / session.part_size as u64;
376 let mut state = self.state.lock().await;
377 let pending = state.uploads.get(&id).ok_or(StorageFailure::SessionExpired)?;
378 pending.check(session)?;
379 if !offset.is_multiple_of(session.part_size as u64)
380 || part_span(session.total, session.part_size, index)
381 .is_none_or(|span| span.end - span.start != bytes.len() as u64)
382 {
383 return Err(StorageFailure::InvalidPart.into());
384 }
385 if std::mem::replace(&mut state.faults.drop_part, false) {
386 return Err(StorageFailure::Network.into());
387 }
388 state
389 .uploads
390 .get_mut(&id)
391 .ok_or(StorageFailure::SessionExpired)?
392 .parts
393 .insert(index, bytes.to_vec());
394 if std::mem::replace(&mut state.faults.lose_part_reply, false) {
395 return Err(StorageFailure::Network.into());
396 }
397 state.sent_bytes += bytes.len() as u64;
398 state.largest_part = state.largest_part.max(bytes.len());
399 Ok(crate::ConfirmedPart(Confirmation::Memory { index }))
400 }
401 async fn initiate(
404 &self,
405 publication: &PublicationRequest,
406 session: &mut UploadSession,
407 sent: &StartSent,
408 ) -> Result<(), StorageError> {
409 let start = MemoryStart { publication, sent };
410 self.before_going(MemoryRequest::Other("initiate_upload"), Some(start)).await?;
411 let mut state = self.state.lock().await;
412 let id = state.next_id()?;
413 state.uploads.insert(
414 id,
415 Pending {
416 device: session.device,
417 begun: self.storage_time()?,
418 target: session.target.clone(),
419 total: session.total,
420 bytes: Vec::new(),
421 parts: BTreeMap::new(),
422 part_size: session.part_size,
423 },
424 );
425 if std::mem::replace(&mut state.faults.lose_initiation_reply, false) {
426 return Err(StorageFailure::Network.into());
427 }
428 session.state = if self.independent_parts {
429 SessionState::MemoryParts { id, parts: Vec::new(), complete: false }
430 } else {
431 SessionState::Memory { id, complete: false }
432 };
433 Ok(())
434 }
435 pub(super) async fn memory_finish_upload(
436 &self,
437 publication: &PublicationRequest,
438 session: &mut UploadSession,
439 ) -> Result<(), StorageError> {
440 publication.check()?;
441 self.before(MemoryRequest::Other("finish_upload")).await?;
442 if matches!(session.state, SessionState::MemoryPlanned { .. }) {
443 return Err(StorageFailure::InvalidPart.into());
445 }
446 let id = self.session_id(session)?;
447 let mut state = self.state.lock().await;
448 let Some(pending) = state.uploads.get(&id) else {
449 return confirm_published(&state, session, id);
450 };
451 pending.check(session)?;
452 let whole = match &session.state {
453 SessionState::MemoryParts { .. } => {
454 pending.parts.len() as u64 == session.total.div_ceil(session.part_size as u64)
455 }
456 _ => pending.bytes.len() as u64 == session.total,
457 };
458 if !whole || session.confirmed != session.total {
459 return Err(StorageFailure::InvalidPart.into());
460 }
461 check_target(&state, session.target())?;
462 if matches!(session.target(), UploadTarget::Replace(_))
463 && state.faults.refuse_replacements > 0
464 {
465 state.faults.refuse_replacements -= 1;
466 return Err(StorageFailure::Conflict.into());
467 }
468 let revision = state.next_id()?;
469 let pending = state.uploads.remove(&id).ok_or(StorageFailure::SessionExpired)?;
470 let path = pending.target.path().clone();
471 state.objects.insert(
472 path,
473 Object {
474 revision,
475 bytes: pending.assembled()?.into(),
476 stored_at: self.storage_time()?,
477 upload_id: Some(id),
478 },
479 );
480 if std::mem::replace(&mut state.faults.lose_completion_reply, false) {
481 return Err(StorageFailure::Network.into());
482 }
483 session.state = SessionState::Memory { id, complete: true };
484 Ok(())
485 }
486 pub(super) async fn memory_abort_upload(
487 &self,
488 publication: &PublicationRequest,
489 session: &UploadSession,
490 ) -> Result<(), StorageError> {
491 if let SessionState::MemoryPlanned { started } = session.state {
492 if started != PlanStart::MayHaveStarted {
495 return Ok(());
496 }
497 if !owns_name(session) {
498 return Err(StorageFailure::SessionIdentityUnknown.into());
499 }
500 }
501 publication.check()?;
502 self.before(MemoryRequest::Other("abort_upload")).await?;
503 let id = match session.state {
504 SessionState::MemoryPlanned { .. } => None,
505 _ => Some(self.session_id(session)?),
506 };
507 let mut state = self.state.lock().await;
508 if state.faults.abort_failures > 0 {
509 state.faults.abort_failures -= 1;
510 return Err(StorageFailure::Network.into());
511 }
512 let Some(id) = id else {
513 end_planned(&mut state, session);
514 return Ok(());
515 };
516 if let Some(pending) = state.uploads.get(&id) {
517 pending.check(session)?;
518 }
519 state.uploads.remove(&id);
520 Ok(())
521 }
522}
523
524#[cfg(test)]
525#[path = "memory_upload_tests.rs"]
526mod tests;