Skip to main content

lb_rs/model/
server_ops.rs

1use super::errors::{DiffError, LbErrKind, LbResult};
2use super::server_meta::{IntoServerMeta, ServerMeta};
3use super::signed_meta::SignedMeta;
4use crate::model::clock::get_time;
5use crate::model::file_like::FileLike;
6use crate::model::file_metadata::FileDiff;
7use crate::model::lazy::{LazyStaged1, LazyTree};
8use crate::model::server_tree::ServerTree;
9use crate::model::tree_like::TreeLike;
10
11type LazyServerStaged1<'a> = LazyStaged1<ServerTree<'a>, Vec<ServerMeta>>;
12
13impl<'a> LazyTree<ServerTree<'a>> {
14    pub fn stage_diff_v2(
15        self, changes: Vec<FileDiff<SignedMeta>>,
16    ) -> LbResult<LazyServerStaged1<'a>> {
17        // Check new.id == old.id
18        for change in &changes {
19            if let Some(old) = &change.old {
20                if old.id() != change.new.id() {
21                    return Err(LbErrKind::Diff(DiffError::DiffMalformed))?;
22                }
23            }
24        }
25
26        // Check for changes to digest
27        for change in &changes {
28            match &change.old {
29                Some(old) => {
30                    if old.timestamped_value.value.document_hmac()
31                        != change.new.timestamped_value.value.document_hmac()
32                    {
33                        return Err(LbErrKind::Diff(DiffError::HmacModificationInvalid))?;
34                    }
35
36                    if old.timestamped_value.value.doc_size()
37                        != change.new.timestamped_value.value.doc_size()
38                    {
39                        return Err(LbErrKind::Diff(DiffError::SizeModificationInvalid))?;
40                    }
41                }
42                None => {
43                    if change.new.timestamped_value.value.doc_size().is_some() {
44                        return Err(LbErrKind::Diff(DiffError::SizeModificationInvalid))?;
45                    }
46                    if change.new.timestamped_value.value.document_hmac().is_some() {
47                        return Err(LbErrKind::Diff(DiffError::HmacModificationInvalid))?;
48                    }
49                }
50            }
51        }
52
53        // Check for race conditions and populate prior size
54        for change in &changes {
55            match &change.old {
56                Some(old) => {
57                    let current = &self
58                        .maybe_find(old.id())
59                        .ok_or(LbErrKind::Diff(DiffError::OldFileNotFound))?
60                        .file;
61
62                    if current != old {
63                        return Err(LbErrKind::Diff(DiffError::OldVersionIncorrect))?;
64                    }
65                }
66                None => {
67                    // if you're claiming this file is new, it must be globally unique
68                    if self.tree.files.maybe_find(change.new.id()).is_some() {
69                        return Err(LbErrKind::Diff(DiffError::OldVersionRequired))?;
70                    }
71                }
72            }
73        }
74
75        Ok(self.stage_unvalidated(changes))
76    }
77
78    pub fn stage_unvalidated(self, changes: Vec<FileDiff<SignedMeta>>) -> LazyServerStaged1<'a> {
79        let now = get_time().0 as u64;
80        let changes = changes
81            .into_iter()
82            .map(|change| change.new.add_time(now))
83            .collect();
84
85        self.stage(changes)
86    }
87}