| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| use { |
| crate::{ |
| fuchsia::errors::map_to_status, |
| fxblob::{ |
| blob::FxBlob, |
| directory::{BlobDirectory, Identifier}, |
| }, |
| }, |
| anyhow::Error, |
| fidl_fuchsia_io::{self as fio}, |
| fuchsia_hash::Hash, |
| fuchsia_zircon::{self as zx}, |
| fxfs::errors::FxfsError, |
| std::sync::Arc, |
| }; |
| |
| /// Implementation for VMO-backed FIDL interface for reading blobs |
| impl BlobDirectory { |
| pub async fn get_blob_vmo(self: &Arc<Self>, hash: Hash) -> Result<zx::Vmo, Error> { |
| let id = Identifier::from_hash(hash); |
| let node = self.lookup(fio::OpenFlags::RIGHT_READABLE, id).await.map_err(map_to_status)?; |
| let any_blob = node.clone().into_any(); |
| let blob = any_blob.downcast_ref::<FxBlob>().ok_or(FxfsError::Internal)?; |
| let vmo = blob.create_child_vmo()?; |
| Ok(vmo) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| crate::fuchsia::fxblob::testing::{new_blob_fixture, BlobFixture}, |
| delivery_blob::CompressionMode, |
| fidl_fuchsia_io::{self as fio}, |
| fuchsia_async as fasync, |
| fuchsia_component::client::connect_to_protocol_at_dir_svc, |
| }; |
| |
| /// Read a blob using BlobReader API and return its contents as a boxed slice. |
| async fn read_blob( |
| blob_volume_outgoing_dir: &fio::DirectoryProxy, |
| hash: Hash, |
| ) -> Result<Vec<u8>, Error> { |
| let blob_proxy = connect_to_protocol_at_dir_svc::<fidl_fuchsia_fxfs::BlobReaderMarker>( |
| &blob_volume_outgoing_dir, |
| ) |
| .expect("failed to connect to the BlobReader service"); |
| let vmo = blob_proxy |
| .get_vmo(&hash.into()) |
| .await |
| .expect("transport error on blobreader") |
| .map_err(zx::Status::from_raw)?; |
| let vmo_size = vmo.get_content_size().expect("failed to get vmo size") as usize; |
| let mut buf = vec![0; vmo_size]; |
| vmo.read(&mut buf[..], 0)?; |
| Ok(buf) |
| } |
| |
| #[fasync::run(10, test)] |
| async fn test_blob_reader_uncompressed() { |
| const NEVER_COMPRESS: CompressionMode = CompressionMode::Never; |
| let fixture = new_blob_fixture().await; |
| let empty_blob_hash = fixture.write_blob(&[], NEVER_COMPRESS).await; |
| let short_data = b"This is some data"; |
| let short_blob_hash = fixture.write_blob(short_data, NEVER_COMPRESS).await; |
| let long_data = &[0x65u8; 30000]; |
| let long_blob_hash = fixture.write_blob(long_data, NEVER_COMPRESS).await; |
| |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"), |
| &[0u8; 0] |
| ); |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"), |
| short_data |
| ); |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"), |
| long_data |
| ); |
| let missing_hash = Hash::from([0x77u8; 32]); |
| assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err()); |
| |
| fixture.close().await; |
| } |
| |
| #[fasync::run(10, test)] |
| async fn test_blob_reader_compressed() { |
| const ALWAYS_COMPRESS: CompressionMode = CompressionMode::Always; |
| let fixture = new_blob_fixture().await; |
| let empty_blob_hash = fixture.write_blob(&[], ALWAYS_COMPRESS).await; |
| let short_data = b"This is some data"; |
| let short_blob_hash = fixture.write_blob(short_data, ALWAYS_COMPRESS).await; |
| let long_data = &[0x65u8; 30000]; |
| let long_blob_hash = fixture.write_blob(long_data, ALWAYS_COMPRESS).await; |
| |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), empty_blob_hash).await.expect("read empty"), |
| &[0u8; 0] |
| ); |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), short_blob_hash).await.expect("read short"), |
| short_data |
| ); |
| assert_eq!( |
| &*read_blob(fixture.volume_out_dir(), long_blob_hash).await.expect("read long"), |
| long_data |
| ); |
| let missing_hash = Hash::from([0x77u8; 32]); |
| assert!(read_blob(fixture.volume_out_dir(), missing_hash).await.is_err()); |
| |
| fixture.close().await; |
| } |
| } |