Skip to main content

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