Skip to main content

storage_stress_test_utils/
fvm.rs

1// Copyright 2020 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
5use fidl::endpoints::DiscoverableProtocolMarker;
6use fidl_fuchsia_io as fio;
7
8use fs_management::filesystem::{
9    DirBasedBlockConnector, Filesystem, ServingMultiVolumeFilesystem, ServingVolume,
10};
11use fs_management::{self, Fvm};
12use ramdevice_client::{RamdiskClient, RamdiskClientBuilder};
13pub use storage_isolated_driver_manager::Guid;
14use storage_isolated_driver_manager::create_random_guid;
15use zx::{Rights, Vmo};
16
17async fn create_ramdisk(vmo: &Vmo, ramdisk_block_size: u64) -> RamdiskClient {
18    let duplicated_handle = vmo.as_handle_ref().duplicate(Rights::SAME_RIGHTS).unwrap();
19    let duplicated_vmo = Vmo::from(duplicated_handle);
20
21    RamdiskClientBuilder::new_with_vmo(duplicated_vmo, Some(ramdisk_block_size))
22        .build()
23        .await
24        .unwrap()
25}
26
27/// A wrapper around a running FVM component instance backed by a ramdisk.
28pub struct FvmInstance {
29    ramdisk: RamdiskClient,
30    fvm_instance: ServingMultiVolumeFilesystem,
31}
32
33/// A wrapper around an opened volume in an FVM instance.
34/// This keeps the volume alive and provides access to its block connector.
35pub struct FvmVolume {
36    serving_volume: ServingVolume,
37    guid: Guid,
38}
39
40impl FvmVolume {
41    /// Creates a new `FvmVolume` wrapping the given `ServingVolume` and `Guid`.
42    pub fn new(serving_volume: ServingVolume, guid: Guid) -> Self {
43        Self { serving_volume, guid }
44    }
45
46    pub fn guid(&self) -> &Guid {
47        &self.guid
48    }
49
50    pub fn block_connector(&self) -> DirBasedBlockConnector {
51        let block_dir = fuchsia_fs::directory::clone(self.serving_volume.exposed_dir()).unwrap();
52        DirBasedBlockConnector::new(
53            block_dir,
54            format!("svc/{}", fidl_fuchsia_storage_block::BlockMarker::PROTOCOL_NAME),
55        )
56    }
57}
58
59impl FvmInstance {
60    /// Creates a new FVM instance.  If `fvm_slice_size` is specified, the device is formatted with
61    /// the specified slice size.  If not specified, `vmo` should contain an existing FVM format.
62    pub async fn new(vmo: &Vmo, ramdisk_block_size: u64, fvm_slice_size: Option<u64>) -> Self {
63        let ramdisk = create_ramdisk(&vmo, ramdisk_block_size).await;
64
65        let mut fs = Filesystem::from_boxed_config(
66            ramdisk.connector().unwrap(),
67            Box::new(Fvm { slice_size: fvm_slice_size.unwrap_or(0), ..Fvm::dynamic_child() }),
68        );
69
70        if fvm_slice_size.is_some() {
71            fs.format().await.unwrap();
72        }
73
74        Self { ramdisk, fvm_instance: fs.serve_multi_volume().await.unwrap() }
75    }
76
77    pub async fn new_volume(
78        &mut self,
79        name: &str,
80        type_guid: &Guid,
81        initial_volume_size: Option<u64>,
82    ) -> FvmVolume {
83        let instance_guid = create_random_guid();
84
85        let create_options = fidl_fuchsia_fs_startup::CreateOptions {
86            initial_size: initial_volume_size,
87            guid: Some(instance_guid.clone()),
88            type_guid: Some(type_guid.clone()),
89            ..fidl_fuchsia_fs_startup::CreateOptions::default()
90        };
91
92        let mount_options = fidl_fuchsia_fs_startup::MountOptions::default();
93
94        let serving_volume =
95            self.fvm_instance.create_volume(name, create_options, mount_options).await.unwrap();
96
97        FvmVolume { serving_volume, guid: instance_guid }
98    }
99
100    pub async fn open_volume(&self, name: &str) -> FvmVolume {
101        let guid = self.fvm_instance.get_volume_info(name).await.unwrap().guid;
102        let serving_volume = self
103            .fvm_instance
104            .open_volume(name, fidl_fuchsia_fs_startup::MountOptions::default())
105            .await
106            .unwrap();
107        FvmVolume::new(serving_volume, guid.unwrap())
108    }
109
110    pub fn ramdisk_get_dir(&self) -> Option<&fio::DirectoryProxy> {
111        Some(self.ramdisk.outgoing())
112    }
113
114    pub async fn free_space(&self) -> u64 {
115        let info = self.fvm_instance.get_info().await.unwrap();
116        (info.slice_count - info.assigned_slice_count) * info.slice_size
117    }
118}