1use crate::errors::FxfsError;
6use crate::lsm_tree::Query;
7use crate::lsm_tree::types::{ItemRef, LayerIterator};
8use crate::object_handle::ObjectHandle;
9use crate::object_store::object_record::{AttributeKey, ObjectKey, ObjectKeyData, ObjectValue};
10use crate::object_store::{AttributeId, DataObjectHandle, HandleOwner, StoreObjectHandle};
11use crate::serialized_types::{Versioned, VersionedLatest};
12use anyhow::{Context, Error};
13use fuchsia_merkle::{Hash, LeafHashCollector, MerkleVerifier};
14
15pub use blob_metadata::{
16 BlobFormat, BlobFormatV53, BlobMetadata, BlobMetadataUnversioned, BlobMetadataV53, MerkleLeaves,
17};
18
19pub trait FxfsBlobMetadataExt: Sized {
20 fn read_from<S: HandleOwner>(
23 blob_object: &StoreObjectHandle<S>,
24 ) -> impl std::future::Future<Output = Result<Self, Error>> + Send;
25
26 fn write_to<S: HandleOwner>(
29 &self,
30 blob_object: &DataObjectHandle<S>,
31 ) -> impl std::future::Future<Output = Result<(), Error>> + Send;
32
33 fn serialized_size(&self) -> Result<usize, Error>;
36
37 fn into_merkle_verifier(self, root: Hash) -> Result<MerkleVerifier, Error>;
39}
40
41impl FxfsBlobMetadataExt for BlobMetadata {
42 fn read_from<S: HandleOwner>(
43 blob_object: &StoreObjectHandle<S>,
44 ) -> impl std::future::Future<Output = Result<Self, Error>> + Send {
45 async move {
46 let store = blob_object.store();
47 let layer_set = store.tree().layer_set();
48 let mut merger = layer_set.merger();
49 static_assertions::const_assert!(
55 AttributeId::BLOB_MERKLE.raw() < AttributeId::BLOB_METADATA.raw()
56 );
57 let key = ObjectKey::attribute(
58 blob_object.object_id(),
59 AttributeId::BLOB_MERKLE,
60 AttributeKey::Attribute,
61 );
62 let iter = merger.query(Query::FullRange(&key)).await?;
63 match iter.get() {
64 Some(ItemRef {
65 key:
66 ObjectKey {
67 object_id,
68 data:
69 ObjectKeyData::Attribute(
70 AttributeId::BLOB_MERKLE,
71 AttributeKey::Attribute,
72 ),
73 },
74 value,
75 ..
76 }) if *object_id == blob_object.object_id() => match value {
77 ObjectValue::Attribute { .. } => {
78 let serialized_metadata = blob_object.read_attr_from_iter(iter).await?;
79 let old_metadata: BlobMetadataUnversioned =
80 bincode::deserialize_from(&*serialized_metadata)?;
81 Ok(Self::from(old_metadata))
82 }
83 _ => Err(FxfsError::Inconsistent.into()),
84 },
85 Some(ItemRef {
86 key:
87 ObjectKey {
88 object_id,
89 data:
90 ObjectKeyData::Attribute(
91 AttributeId::BLOB_METADATA,
92 AttributeKey::Attribute,
93 ),
94 },
95 value,
96 ..
97 }) if *object_id == blob_object.object_id() => match value {
98 ObjectValue::Attribute { .. } => {
99 let serialized_metadata = blob_object.read_attr_from_iter(iter).await?;
100 Ok(Self::deserialize_with_version(&mut &*serialized_metadata)?.0)
101 }
102 _ => Err(FxfsError::Inconsistent.into()),
103 },
104 Some(ItemRef {
105 key:
106 ObjectKey {
107 object_id,
108 data:
109 ObjectKeyData::Attribute(
110 AttributeId::FSVERITY_MERKLE,
111 AttributeKey::Attribute,
112 ),
113 },
114 ..
115 }) if *object_id == blob_object.object_id() => {
116 Err(FxfsError::Inconsistent.into())
122 }
123 _ => Ok(Self::empty()),
125 }
126 }
127 }
128
129 fn write_to<S: HandleOwner>(
130 &self,
131 blob_object: &DataObjectHandle<S>,
132 ) -> impl std::future::Future<Output = Result<(), Error>> + Send {
133 async move {
134 if self.is_empty() {
136 return Ok(());
137 }
138 let mut buf = Vec::new();
139 self.serialize_with_version(&mut buf)?;
140 blob_object
141 .write_attr(AttributeId::BLOB_METADATA, &buf)
142 .await
143 .context("Failed to write blob metadata attribute.")
144 }
145 }
146
147 fn serialized_size(&self) -> Result<usize, Error> {
148 if self.is_empty() {
149 return Ok(0);
150 }
151 struct CountingWriter(usize);
152 impl std::io::Write for CountingWriter {
153 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
154 self.0 += buf.len();
155 Ok(buf.len())
156 }
157 fn flush(&mut self) -> std::io::Result<()> {
158 Ok(())
159 }
160 }
161 let mut writer = CountingWriter(0);
162 self.serialize_with_version(&mut writer)?;
163 Ok(writer.0)
164 }
165
166 fn into_merkle_verifier(self, root: Hash) -> Result<MerkleVerifier, Error> {
167 Ok(BlobMetadataV53::into_merkle_verifier(self, root)?)
168 }
169}
170
171impl Versioned for BlobMetadataV53 {
172 fn max_serialized_size() -> Option<u64> {
173 None
175 }
176}
177
178#[derive(Default)]
179pub struct BlobMetadataLeafHashCollector(MerkleLeaves);
180
181impl BlobMetadataLeafHashCollector {
182 pub fn new() -> Self {
183 Self(Vec::new())
184 }
185}
186
187impl LeafHashCollector for BlobMetadataLeafHashCollector {
188 type Output = (Hash, MerkleLeaves);
189
190 fn add_leaf_hash(&mut self, hash: Hash) {
191 self.0.push(hash.into())
192 }
193
194 fn complete(mut self, root: Hash) -> Self::Output {
195 if self.0.len() == 1 {
197 debug_assert!(*root == self.0[0]);
198 self.0 = Vec::new();
199 }
200 (root, self.0)
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::BlobMetadata;
207 use crate::blob_metadata::{
208 BlobFormat, BlobMetadataLeafHashCollector, BlobMetadataUnversioned, FxfsBlobMetadataExt,
209 };
210 use crate::filesystem::{FxFilesystem, OpenFxFilesystem};
211 use crate::object_store::transaction::{LockKey, Options, lock_keys};
212 use crate::object_store::{
213 AttributeId, DataObjectHandle, Directory, HandleOptions, ObjectStore,
214 };
215 use assert_matches::assert_matches;
216 use fuchsia_merkle::MerkleRootBuilder;
217 use storage_device::DeviceHolder;
218 use storage_device::fake_device::FakeDevice;
219
220 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
221 const TEST_DEVICE_BLOCK_COUNT: u64 = 16 * 1024;
222 const TEST_OBJECT_NAME: &str = "foo";
223
224 async fn test_filesystem() -> OpenFxFilesystem {
225 let device =
226 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, TEST_DEVICE_BLOCK_SIZE));
227 FxFilesystem::new_empty(device).await.expect("new_empty failed")
228 }
229
230 async fn test_filesystem_and_empty_object() -> (OpenFxFilesystem, DataObjectHandle<ObjectStore>)
231 {
232 let fs = test_filesystem().await;
233 let store = fs.root_store();
234
235 let mut transaction = fs
236 .root_store()
237 .new_transaction(
238 lock_keys![LockKey::object(
239 store.store_object_id(),
240 store.root_directory_object_id()
241 )],
242 Options::default(),
243 )
244 .await
245 .expect("new_transaction failed");
246
247 let object =
248 ObjectStore::create_object(&store, &mut transaction, HandleOptions::default(), None)
249 .await
250 .expect("create_object failed");
251
252 let root_directory =
253 Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
254 root_directory
255 .add_child_file(&mut transaction, TEST_OBJECT_NAME, &object)
256 .await
257 .expect("add_child_file failed");
258
259 transaction.commit().await.expect("commit failed");
260
261 (fs, object)
262 }
263
264 #[fuchsia::test(threads = 3)]
265 async fn test_write_read_zstd() {
266 let (fs, object) = test_filesystem_and_empty_object().await;
267
268 let metadata = BlobMetadata {
269 merkle_leaves: vec![[1; 32], [2; 32], [3; 32], [4; 32]],
270 format: BlobFormat::ChunkedZstd {
271 uncompressed_size: 128 * 1024,
272 chunk_size: 32 * 1024,
273 compressed_offsets: vec![0, 100, 200, 400],
274 },
275 };
276 metadata.write_to(&object).await.expect("failed to write attribute");
277 let read_metadata =
278 BlobMetadata::read_from(&object).await.expect("failed to read attribute");
279 assert_eq!(read_metadata, metadata);
280
281 fs.close().await.expect("close failed");
282 }
283
284 #[fuchsia::test(threads = 3)]
285 async fn test_write_read_lz4() {
286 let (fs, object) = test_filesystem_and_empty_object().await;
287
288 let metadata = BlobMetadata {
289 merkle_leaves: vec![[1; 32], [2; 32], [3; 32], [4; 32]],
290 format: BlobFormat::ChunkedLz4 {
291 uncompressed_size: 128 * 1024,
292 chunk_size: 32 * 1024,
293 compressed_offsets: vec![0, 100, 200, 400],
294 },
295 };
296 metadata.write_to(&object).await.expect("failed to write attribute");
297 let read_metadata =
298 BlobMetadata::read_from(&object).await.expect("failed to read attribute");
299 assert_eq!(read_metadata, metadata);
300
301 fs.close().await.expect("close failed");
302 }
303
304 #[fuchsia::test(threads = 3)]
305 async fn test_empty_attribute_is_not_written() {
306 let (fs, object) = test_filesystem_and_empty_object().await;
307
308 BlobMetadata::empty().write_to(&object).await.expect("failed to write attribute");
309 let result = object
310 .read_attr(AttributeId::BLOB_METADATA)
311 .await
312 .expect("reading the attribute failed");
313 assert_eq!(result, None);
314
315 fs.close().await.expect("close failed");
316 }
317
318 #[fuchsia::test(threads = 3)]
319 async fn test_read_corrupt_attribute_fails() {
320 let (fs, object) = test_filesystem_and_empty_object().await;
321
322 object
323 .write_attr(AttributeId::BLOB_METADATA, b"garbage")
324 .await
325 .expect("failed to write attribute");
326 BlobMetadata::read_from(&object).await.expect_err("reading the metadata should fail");
327
328 fs.close().await.expect("close failed");
329 }
330
331 #[fuchsia::test(threads = 3)]
332 async fn test_read_unversioned_attribute() {
333 let (fs, object) = test_filesystem_and_empty_object().await;
334
335 let unversioned_metadata = BlobMetadataUnversioned {
336 hashes: vec![[1; 32], [2; 32]],
337 chunk_size: 32 * 1024,
338 compressed_offsets: vec![0],
339 uncompressed_size: 15 * 1024,
340 };
341 let mut buf = Vec::new();
342 bincode::serialize_into(&mut buf, &unversioned_metadata)
343 .expect("failed to serialize metadata");
344 object.write_attr(AttributeId::BLOB_MERKLE, &buf).await.expect("failed to write attribute");
345 let metadata = BlobMetadata::read_from(&object).await.expect("failed to read attribute");
346 assert_eq!(metadata, BlobMetadata::from(unversioned_metadata));
347
348 fs.close().await.expect("close failed");
349 }
350
351 #[fuchsia::test(threads = 3)]
352 async fn test_read_corrupt_unversioned_attribute_fails() {
353 let (fs, object) = test_filesystem_and_empty_object().await;
354
355 object
356 .write_attr(AttributeId::BLOB_MERKLE, b"garbage")
357 .await
358 .expect("failed to write attribute");
359 BlobMetadata::read_from(&object).await.expect_err("reading the metadata should fail");
360
361 fs.close().await.expect("close failed");
362 }
363
364 #[fuchsia::test(threads = 3)]
365 async fn test_fs_verity_hides_blob_metadata() {
366 let (fs, object) = test_filesystem_and_empty_object().await;
367
368 let metadata = BlobMetadata {
369 merkle_leaves: vec![[1; 32], [2; 32]],
370 format: BlobFormat::Uncompressed,
371 };
372 metadata.write_to(&object).await.expect("failed to write attribute");
373 object
374 .write_attr(AttributeId::FSVERITY_MERKLE, b"fs-verify")
375 .await
376 .expect("failed to write fs-verity attribute");
377 BlobMetadata::read_from(&object).await.expect_err("fs-verity should have been found");
378
379 fs.close().await.expect("close failed");
380 }
381
382 #[fuchsia::test]
383 async fn test_serialized_size() {
384 assert_matches!(BlobMetadata::empty().serialized_size(), Ok(0));
385 assert_matches!(
386 BlobMetadata {
387 merkle_leaves: vec![[54; 32], [55; 32]],
388 format: BlobFormat::Uncompressed,
389 }
390 .serialized_size(),
391 Ok(70)
396 );
397 assert_matches!(
398 BlobMetadata {
399 merkle_leaves: vec![[54; 32], [55; 32]],
400 format: BlobFormat::ChunkedZstd {
401 uncompressed_size: 128 * 1024,
402 chunk_size: 32 * 1024,
403 compressed_offsets: vec![0, 100, 200, 400],
404 },
405 }
406 .serialized_size(),
407 Ok(85)
416 );
417 }
418
419 #[fuchsia::test]
420 fn test_leaf_hash_collector_with_only_root() {
421 let data = vec![3; 4096];
422 let (_root, leaves) =
423 MerkleRootBuilder::new(BlobMetadataLeafHashCollector::new()).complete(&data);
424 assert!(leaves.is_empty());
425 }
426
427 #[fuchsia::test]
428 fn test_leaf_hash_collector_with_leaves() {
429 let data = vec![3; 12 * 1024];
430 let (_root, leaves) =
431 MerkleRootBuilder::new(BlobMetadataLeafHashCollector::new()).complete(&data);
432 assert_eq!(leaves.len(), 2);
433 }
434
435 #[fuchsia::test]
436 fn test_into_merkle_verifier_with_only_root() {
437 let data = vec![3; 4096];
438 let (root, leaves) =
439 MerkleRootBuilder::new(BlobMetadataLeafHashCollector::new()).complete(&data);
440 let metadata = BlobMetadata { merkle_leaves: leaves, format: BlobFormat::Uncompressed };
441 let verifier =
442 metadata.into_merkle_verifier(root).expect("failed to create merkle verifier");
443 verifier.verify(0, &data).expect("failed to verify data");
444 }
445
446 #[fuchsia::test]
447 fn test_into_merkle_verifier_with_leaves() {
448 let data = vec![3; 12 * 1024];
449 let (root, leaves) =
450 MerkleRootBuilder::new(BlobMetadataLeafHashCollector::new()).complete(&data);
451 let metadata = BlobMetadata { merkle_leaves: leaves, format: BlobFormat::Uncompressed };
452 let verifier =
453 metadata.into_merkle_verifier(root).expect("failed to create merkle verifier");
454 verifier.verify(0, &data).expect("failed to verify data");
455 }
456
457 #[fuchsia::test]
458 fn test_convert_unversioned_to_versioned() {
459 assert_eq!(
460 BlobMetadata::from(BlobMetadataUnversioned {
461 hashes: vec![[1; 32], [2; 32]],
462 chunk_size: 0,
463 compressed_offsets: vec![],
464 uncompressed_size: 15 * 1024,
465 }),
466 BlobMetadata {
467 merkle_leaves: vec![[1; 32], [2; 32]],
468 format: BlobFormat::Uncompressed,
469 }
470 );
471
472 assert_eq!(
473 BlobMetadata::from(BlobMetadataUnversioned {
474 hashes: vec![[1; 32], [2; 32], [3; 32], [4; 32]],
475 chunk_size: 32 * 1024,
476 compressed_offsets: vec![0, 100],
477 uncompressed_size: 33 * 1024,
478 }),
479 BlobMetadata {
480 merkle_leaves: vec![[1; 32], [2; 32], [3; 32], [4; 32]],
481 format: BlobFormat::ChunkedZstd {
482 uncompressed_size: 33 * 1024,
483 chunk_size: 32 * 1024,
484 compressed_offsets: vec![0, 100]
485 },
486 }
487 );
488 }
489
490 #[fuchsia::test]
491 fn test_merkle_serialization() {}
492}