fuchsia_pkg_testing/
blobfs.rs

1// Copyright 2021 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//! Fake implementation of blobfs for blobfs::Client.
6
7use fuchsia_hash::Hash;
8use futures::stream::TryStreamExt as _;
9use tempfile::TempDir;
10use {fidl_fuchsia_fxfs as ffxfs, fidl_fuchsia_io as fio, fuchsia_async as fasync};
11
12/// A fake blobfs backed by temporary storage.
13///
14/// The name of the blob file is not guaranteed to match the merkle root of the content.
15/// Be aware that this implementation does not send USER_0 signal, so `has_blob()` will always
16/// return false.
17pub struct Fake {
18    root: TempDir,
19    _reader_server: fasync::Task<()>,
20}
21
22impl Fake {
23    /// Creates a new fake blobfs and client.
24    /// Uses fuchsia_async::Task::spawn and so must be called with an executor installed.
25    ///
26    /// # Panics
27    ///
28    /// Panics on error
29    pub fn new() -> (Self, blobfs::Client) {
30        let root = TempDir::new().unwrap();
31
32        let (reader, reader_stream) =
33            fidl::endpoints::create_proxy_and_stream::<ffxfs::BlobReaderMarker>();
34        let reader_server = fasync::Task::spawn(serve_reader(root_proxy(&root), reader_stream));
35
36        let blobfs = blobfs::Client::new(root_proxy(&root), None, reader, None).unwrap();
37        let fake = Self { root, _reader_server: reader_server };
38        (fake, blobfs)
39    }
40
41    /// Add a new blob to fake blobfs.
42    ///
43    /// # Panics
44    ///
45    /// Panics on error
46    pub fn add_blob(&self, hash: Hash, data: impl AsRef<[u8]>) {
47        std::fs::write(self.root.path().join(hash.to_string()), data).unwrap();
48    }
49
50    /// Delete a blob from the fake blobfs.
51    ///
52    /// # Panics
53    ///
54    /// Panics on error
55    pub fn delete_blob(&self, hash: Hash) {
56        std::fs::remove_file(self.root.path().join(hash.to_string())).unwrap();
57    }
58}
59
60fn root_proxy(root: &TempDir) -> fio::DirectoryProxy {
61    fuchsia_fs::directory::open_in_namespace(root.path().to_str().unwrap(), fio::PERM_READABLE)
62        .unwrap()
63}
64
65async fn serve_reader(blobs: fio::DirectoryProxy, mut stream: ffxfs::BlobReaderRequestStream) {
66    while let Some(req) = stream.try_next().await.unwrap() {
67        match req {
68            ffxfs::BlobReaderRequest::GetVmo { blob_hash, responder } => {
69                match fuchsia_fs::directory::open_file(
70                    &blobs,
71                    &Hash::from(blob_hash).to_string(),
72                    fio::PERM_READABLE,
73                )
74                .await
75                {
76                    Ok(blob) => {
77                        let vmo = blob
78                            .get_backing_memory(fio::VmoFlags::READ)
79                            .await
80                            .unwrap()
81                            .map_err(zx::Status::from_raw)
82                            .unwrap();
83                        let () = responder.send(Ok(vmo)).unwrap();
84                    }
85                    Err(fuchsia_fs::node::OpenError::OpenError(status))
86                        if status == zx::Status::NOT_FOUND =>
87                    {
88                        let () = responder.send(Err(status.into_raw())).unwrap();
89                    }
90                    Err(e) => panic!("unexpected error {e:?}"),
91                }
92            }
93        }
94    }
95}