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_async as fasync;
35    use fuchsia_component_client::connect_to_protocol_at_dir_svc;
36
37    /// Read a blob using BlobReader API and return its contents as a boxed slice.
38    async fn read_blob(
39        blob_volume_outgoing_dir: &fio::DirectoryProxy,
40        hash: Hash,
41    ) -> Result<Vec<u8>, Error> {
42        let blob_proxy = connect_to_protocol_at_dir_svc::<fidl_fuchsia_fxfs::BlobReaderMarker>(
43            &blob_volume_outgoing_dir,
44        )
45        .expect("failed to connect to the BlobReader service");
46        let vmo = blob_proxy
47            .get_vmo(&hash.into())
48            .await
49            .expect("transport error on blobreader")
50            .map_err(zx::Status::from_raw)?;
51        let vmo_size = vmo.get_stream_size().expect("failed to get vmo size") as usize;
52        let mut buf = vec![0; vmo_size];
53        vmo.read(&mut buf[..], 0)?;
54        Ok(buf)
55    }
56
57    #[fasync::run(10, test)]
58    async fn test_blob_reader_uncompressed() {
59        const NEVER_COMPRESS: CompressionMode = CompressionMode::Never;
60        let fixture = new_blob_fixture().await;
61        let empty_blob_hash = fixture.write_blob(&[], NEVER_COMPRESS).await;
62        let short_data = b"This is some data";
63        let short_blob_hash = fixture.write_blob(short_data, NEVER_COMPRESS).await;
64        let long_data = &[0x65u8; 30000];
65        let long_blob_hash = fixture.write_blob(long_data, NEVER_COMPRESS).await;
66
67        assert_eq!(
68            &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"),
69            &[0u8; 0]
70        );
71        assert_eq!(
72            &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"),
73            short_data
74        );
75        assert_eq!(
76            &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"),
77            long_data
78        );
79        let missing_hash = Hash::from([0x77u8; 32]);
80        assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err());
81
82        fixture.close().await;
83    }
84
85    #[fasync::run(10, test)]
86    async fn test_blob_reader_compressed() {
87        const ALWAYS_COMPRESS: CompressionMode = CompressionMode::Always;
88        let fixture = new_blob_fixture().await;
89        let empty_blob_hash = fixture.write_blob(&[], ALWAYS_COMPRESS).await;
90        let short_data = b"This is some data";
91        let short_blob_hash = fixture.write_blob(short_data, ALWAYS_COMPRESS).await;
92        let long_data = &[0x65u8; 30000];
93        let long_blob_hash = fixture.write_blob(long_data, ALWAYS_COMPRESS).await;
94
95        assert_eq!(
96            &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"),
97            &[0u8; 0]
98        );
99        assert_eq!(
100            &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"),
101            short_data
102        );
103        assert_eq!(
104            &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"),
105            long_data
106        );
107        let missing_hash = Hash::from([0x77u8; 32]);
108        assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err());
109
110        fixture.close().await;
111    }
112}