krabka_remote_storage/worm/verify/
manifest_read.rs1use {
10 crate::worm::{
11 error::WormError,
12 manifest::{MANIFEST_FORMAT_VERSION, SegmentManifest},
13 },
14 krabka_object_store::{ObjectStoreError, read_capped},
15 object_store::{ObjectStore, path::Path},
16 std::sync::Arc,
17};
18
19pub const MAX_MANIFEST_BYTES: u64 = 1024 * 1024;
26
27pub(super) enum ManifestRead {
29 Decoded(Box<SegmentManifest>),
31 Rejected(String),
33}
34
35pub(super) async fn read_manifest(
37 store: &Arc<dyn ObjectStore>,
38 key: &str,
39) -> Result<ManifestRead, WormError> {
40 let bytes = match read_capped(store, &Path::from(key), MAX_MANIFEST_BYTES).await {
41 Ok(bytes) => bytes,
42 Err(ObjectStoreError::TooLarge {
45 size, max_bytes, ..
46 }) => {
47 return Ok(ManifestRead::Rejected(format!(
48 "manifest object is {size} bytes, above the {max_bytes} byte cap"
49 )));
50 }
51 Err(e) => {
52 return Err(WormError::Archive(format!(
53 "cannot read manifest `{key}`: {e}"
54 )));
55 }
56 };
57 let manifest: SegmentManifest = match serde_json::from_slice(&bytes) {
58 Ok(manifest) => manifest,
59 Err(e) => {
60 return Ok(ManifestRead::Rejected(format!(
61 "manifest does not decode: {e}"
62 )));
63 }
64 };
65 if let Some(reason) = format_version_problem(manifest.body.format_version) {
66 return Ok(ManifestRead::Rejected(reason));
67 }
68 Ok(ManifestRead::Decoded(Box::new(manifest)))
69}
70
71fn format_version_problem(version: u32) -> Option<String> {
74 match version {
75 MANIFEST_FORMAT_VERSION => None,
76 1 => Some(format!(
77 "manifest format version 1 predates krabka 1.0, whose verifier reads only version \
78 {MANIFEST_FORMAT_VERSION}"
79 )),
80 other => Some(format!(
81 "unsupported manifest format version {other}: this build reads only version \
82 {MANIFEST_FORMAT_VERSION}"
83 )),
84 }
85}
86
87pub(super) struct KeyedManifest {
89 pub key: String,
90 pub manifest: SegmentManifest,
91}
92
93#[cfg(test)]
94mod tests {
95 use {
96 super::*,
97 crate::worm::verify::test_support::{Archive, put_raw},
98 assert2::check,
99 bytes::Bytes,
100 };
101
102 #[yare::parameterized(
103 pre_1_0 = {
104 1,
105 "manifest format version 1 predates krabka 1.0, whose verifier reads only version 2",
106 },
107 older = { 0, "unsupported manifest format version 0: this build reads only version 2" },
108 future = { 3, "unsupported manifest format version 3: this build reads only version 2" },
109 )]
110 #[test_macro(tokio::test)]
111 async fn a_manifest_of_any_version_but_the_current_one_is_a_break(
112 version: u64,
113 expected: &str,
114 ) {
115 let archive = Archive::build(&[1]).await;
116 let segment = &archive.segments[0];
117 let mut value = serde_json::to_value(&segment.manifest).unwrap();
118 value["body"]["format_version"] = serde_json::json!(version);
119 put_raw(
120 &archive.ops,
121 &segment.manifest_key,
122 Bytes::from(serde_json::to_vec(&value).unwrap()),
123 )
124 .await;
125
126 let report = archive.verify().await;
127
128 check!(report.first_break().map(|found| found.reason.as_str()) == Some(expected));
129 }
130
131 #[test]
132 fn only_the_current_manifest_version_is_read() {
133 check!(MANIFEST_FORMAT_VERSION == 2);
134 check!(format_version_problem(MANIFEST_FORMAT_VERSION) == None);
135 }
136
137 #[tokio::test]
138 async fn an_oversized_manifest_object_is_a_break_and_not_an_error() {
139 let archive = Archive::build(&[1]).await;
140 let huge = vec![b'{'; usize::try_from(MAX_MANIFEST_BYTES).unwrap() + 1];
141 put_raw(
142 &archive.ops,
143 &archive.segments[0].manifest_key,
144 Bytes::from(huge),
145 )
146 .await;
147
148 let report = archive.verify().await;
149
150 check!(!report.ok());
151 match report.first_break() {
152 Some(found) => {
153 check!(found.reason.contains("above the"));
154 }
155 None => {
156 check!(false, "an oversized manifest must record a break");
157 }
158 }
159 }
160}