storage_isolated_driver_manager/
fvm.rsuse crate::Guid;
use anyhow::{Context, Result};
use device_watcher::recursive_wait_and_open;
use fidl::endpoints::Proxy as _;
use fidl_fuchsia_device::ControllerProxy;
use fidl_fuchsia_hardware_block::BlockMarker;
use fidl_fuchsia_hardware_block_partition::Guid as FidlGuid;
use fidl_fuchsia_hardware_block_volume::{VolumeManagerMarker, VolumeManagerProxy};
use fidl_fuchsia_io as fio;
use fuchsia_component::client::connect_to_named_protocol_at_dir_root;
use zx::sys::{zx_handle_t, zx_status_t};
use zx::{self as zx, AsHandleRef};
const FVM_DRIVER_PATH: &str = "fvm.cm";
extern "C" {
fn fvm_init(device: zx_handle_t, slice_size: usize) -> zx_status_t;
}
pub fn format_for_fvm(block_device: &fio::DirectoryProxy, fvm_slice_size: usize) -> Result<()> {
let device = connect_to_named_protocol_at_dir_root::<BlockMarker>(block_device, ".")?;
let device_raw = device.as_channel().raw_handle();
let status = unsafe { fvm_init(device_raw, fvm_slice_size) };
zx::ok(status).context("fvm_init failed")
}
pub async fn bind_fvm_driver(controller: &ControllerProxy) -> Result<()> {
controller.bind(FVM_DRIVER_PATH).await.context("fvm driver bind call failed").unwrap().unwrap();
Ok(())
}
pub async fn start_fvm_driver(
controller: &ControllerProxy,
block_device: &fio::DirectoryProxy,
) -> Result<VolumeManagerProxy> {
bind_fvm_driver(controller).await?;
const FVM_DEVICE_NAME: &str = "fvm";
recursive_wait_and_open::<VolumeManagerMarker>(block_device, FVM_DEVICE_NAME)
.await
.context("wait_for_fvm_driver wait failed")
}
pub async fn set_up_fvm(
controller: &ControllerProxy,
block_device: &fio::DirectoryProxy,
fvm_slice_size: usize,
) -> Result<VolumeManagerProxy> {
format_for_fvm(block_device, fvm_slice_size)?;
start_fvm_driver(controller, block_device).await
}
pub async fn create_fvm_volume(
volume_manager: &VolumeManagerProxy,
name: &str,
type_guid: &Guid,
instance_guid: &Guid,
volume_size: Option<u64>,
flags: u32,
) -> Result<()> {
let slice_count = match volume_size {
Some(volume_size) => {
let (status, info) =
volume_manager.get_info().await.context("Failed to get FVM info")?;
zx::ok(status).context("Get Info Error")?;
let slice_size = info.unwrap().slice_size;
assert!(slice_size > 0);
(volume_size + slice_size - 1) / slice_size
}
None => 1,
};
let type_guid = FidlGuid { value: type_guid.clone() };
let instance_guid = FidlGuid { value: instance_guid.clone() };
let status = volume_manager
.allocate_partition(slice_count, &type_guid, &instance_guid, name, flags)
.await?;
zx::ok(status).context("error allocating partition")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{wait_for_block_device, BlockDeviceMatcher};
use fidl_fuchsia_hardware_block_volume::{VolumeMarker, ALLOCATE_PARTITION_FLAG_INACTIVE};
use fuchsia_component::client::connect_to_protocol_at_path;
use ramdevice_client::RamdiskClient;
const BLOCK_SIZE: u64 = 512;
const BLOCK_COUNT: u64 = 64 * 1024 * 1024 / BLOCK_SIZE;
const FVM_SLICE_SIZE: usize = 1024 * 1024;
const INSTANCE_GUID: Guid = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f,
];
const TYPE_GUID: Guid = [
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0,
0xf0,
];
const VOLUME_NAME: &str = "volume-name";
#[fuchsia::test]
async fn set_up_fvm_test() {
let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
let fvm = set_up_fvm(
ramdisk.as_controller().expect("invalid controller"),
ramdisk.as_dir().expect("invalid directory proxy"),
FVM_SLICE_SIZE,
)
.await
.expect("Failed to set up FVM");
let fvm_info = fvm.get_info().await.unwrap();
zx::ok(fvm_info.0).unwrap();
let fvm_info = fvm_info.1.unwrap();
assert_eq!(fvm_info.slice_size, FVM_SLICE_SIZE as u64);
assert_eq!(fvm_info.assigned_slice_count, 0);
}
#[fuchsia::test]
async fn create_fvm_volume_without_volume_size_has_one_slice() {
let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
let fvm = set_up_fvm(
ramdisk.as_controller().expect("invalid controller"),
ramdisk.as_dir().expect("invalid directory proxy"),
FVM_SLICE_SIZE,
)
.await
.expect("Failed to set up FVM");
create_fvm_volume(
&fvm,
VOLUME_NAME,
&TYPE_GUID,
&INSTANCE_GUID,
None,
ALLOCATE_PARTITION_FLAG_INACTIVE,
)
.await
.expect("Failed to create fvm volume");
let block_device_path = wait_for_block_device(&[
BlockDeviceMatcher::TypeGuid(&TYPE_GUID),
BlockDeviceMatcher::InstanceGuid(&INSTANCE_GUID),
BlockDeviceMatcher::Name(VOLUME_NAME),
])
.await
.expect("Failed to find block device");
let volume =
connect_to_protocol_at_path::<VolumeMarker>(block_device_path.to_str().unwrap())
.unwrap();
let volume_info = volume.get_volume_info().await.unwrap();
zx::ok(volume_info.0).unwrap();
let volume_info = volume_info.2.unwrap();
assert_eq!(volume_info.partition_slice_count, 1);
}
#[fuchsia::test]
async fn create_fvm_volume_with_unaligned_volume_size_rounds_up_to_slice_multiple() {
let ramdisk = RamdiskClient::create(BLOCK_SIZE, BLOCK_COUNT).await.unwrap();
let fvm = set_up_fvm(
ramdisk.as_controller().expect("invalid controller"),
ramdisk.as_dir().expect("invalid directory proxy"),
FVM_SLICE_SIZE,
)
.await
.expect("Failed to set up FVM");
create_fvm_volume(
&fvm,
VOLUME_NAME,
&TYPE_GUID,
&INSTANCE_GUID,
Some((FVM_SLICE_SIZE * 5 + 4) as u64),
ALLOCATE_PARTITION_FLAG_INACTIVE,
)
.await
.expect("Failed to create fvm volume");
let block_device_path = wait_for_block_device(&[
BlockDeviceMatcher::TypeGuid(&TYPE_GUID),
BlockDeviceMatcher::InstanceGuid(&INSTANCE_GUID),
BlockDeviceMatcher::Name(VOLUME_NAME),
])
.await
.expect("Failed to find block device");
let volume =
connect_to_protocol_at_path::<VolumeMarker>(block_device_path.to_str().unwrap())
.unwrap();
let volume_info = volume.get_volume_info().await.unwrap();
zx::ok(volume_info.0).unwrap();
let volume_info = volume_info.2.unwrap();
assert_eq!(volume_info.partition_slice_count, 6);
}
}