Skip to main content

fxfs_platform/fuchsia/fxblob/
reader.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implements fuchsia.fxfs/BlobReader for reading blobs as VMOs from a [`BlobDirectory`].
6
7use crate::fxblob::directory::BlobDirectory;
8use anyhow::Error;
9use fuchsia_hash::Hash;
10
11use std::sync::Arc;
12
13impl BlobDirectory {
14    /// Get a pager-backed VMO for the blob identified by `hash` in this [`BlobDirectory`]. The blob
15    /// cannot be purged until all VMOs returned by this function are destroyed.
16    pub async fn get_blob_vmo(self: &Arc<Self>, hash: Hash) -> Result<zx::Vmo, Error> {
17        let (blob, vmo) = self.open_blob_get_vmo(&hash.into()).await?;
18        {
19            let mut guard = self.volume().pager().recorder();
20            if let Some(recorder) = &mut (*guard) {
21                let _ = recorder.record_open(blob);
22            }
23        }
24        Ok(vmo)
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use crate::fuchsia::fxblob::testing::{BlobFixture, new_blob_fixture};
32    use delivery_blob::CompressionMode;
33    use fidl_fuchsia_io::{self as fio};
34    use fuchsia_component_client::connect_to_protocol_at_dir_svc;
35
36    /// Read a blob using BlobReader API and return its contents as a boxed slice.
37    async fn read_blob(
38        blob_volume_outgoing_dir: &fio::DirectoryProxy,
39        hash: Hash,
40    ) -> Result<Vec<u8>, Error> {
41        let blob_proxy = connect_to_protocol_at_dir_svc::<fidl_fuchsia_fxfs::BlobReaderMarker>(
42            &blob_volume_outgoing_dir,
43        )
44        .expect("failed to connect to the BlobReader service");
45        let vmo = blob_proxy
46            .get_vmo(&hash.into())
47            .await
48            .expect("transport error on blobreader")
49            .map_err(zx::Status::from_raw)?;
50        let vmo_size = vmo.get_stream_size().expect("failed to get vmo size") as usize;
51        let mut buf = vec![0; vmo_size];
52        vmo.read(&mut buf[..], 0)?;
53        Ok(buf)
54    }
55
56    #[fuchsia::test(threads = 10)]
57    async fn test_blob_reader_uncompressed() {
58        const NEVER_COMPRESS: CompressionMode = CompressionMode::Never;
59        let fixture = new_blob_fixture().await;
60        let empty_blob_hash = fixture.write_blob(&[], NEVER_COMPRESS).await;
61        let short_data = b"This is some data";
62        let short_blob_hash = fixture.write_blob(short_data, NEVER_COMPRESS).await;
63        let long_data = &[0x65u8; 30000];
64        let long_blob_hash = fixture.write_blob(long_data, NEVER_COMPRESS).await;
65
66        assert_eq!(
67            &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"),
68            &[0u8; 0]
69        );
70        assert_eq!(
71            &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"),
72            short_data
73        );
74        assert_eq!(
75            &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"),
76            long_data
77        );
78        let missing_hash = Hash::from([0x77u8; 32]);
79        assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err());
80
81        fixture.close().await;
82    }
83
84    #[fuchsia::test(threads = 10)]
85    async fn test_blob_reader_compressed() {
86        const ALWAYS_COMPRESS: CompressionMode = CompressionMode::Always;
87        let fixture = new_blob_fixture().await;
88        let empty_blob_hash = fixture.write_blob(&[], ALWAYS_COMPRESS).await;
89        let short_data = b"This is some data";
90        let short_blob_hash = fixture.write_blob(short_data, ALWAYS_COMPRESS).await;
91        let long_data = &[0x65u8; 30000];
92        let long_blob_hash = fixture.write_blob(long_data, ALWAYS_COMPRESS).await;
93
94        assert_eq!(
95            &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"),
96            &[0u8; 0]
97        );
98        assert_eq!(
99            &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"),
100            short_data
101        );
102        assert_eq!(
103            &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"),
104            long_data
105        );
106        let missing_hash = Hash::from([0x77u8; 32]);
107        assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err());
108
109        fixture.close().await;
110    }
111}