blob: 9b32d73cf415d1da9a6493183f4c68056340fbd4 [file] [log] [blame]
// Copyright 2021 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 {
fidl_fuchsia_hardware_block_partition::Guid,
fidl_fuchsia_hardware_block_volume::{VolumeMarker, VolumeProxy},
fuchsia_component::client::{connect_channel_to_service_at_path, connect_to_service_at_path},
fuchsia_zircon::{Channel, Status},
remote_block_device::{BufferSlice, MutableBufferSlice, RemoteBlockDevice},
std::path::PathBuf,
storage_stress_test_utils::fvm::get_volume_path,
};
fn fidl_to_status(result: Result<i32, fidl::Error>) -> Result<(), Status> {
match result {
Ok(code) => Status::ok(code),
Err(e) => {
if e.is_closed() {
Err(Status::PEER_CLOSED)
} else {
panic!("Unrecoverable connection error: {}", e);
}
}
}
}
fn anyhow_to_status(result: Result<(), anyhow::Error>) -> Result<(), Status> {
match result {
Ok(()) => Ok(()),
Err(e) => match e.downcast::<Status>() {
Ok(s) => Err(s),
Err(e) => panic!("Unrecoverable connection error: {:?}", e),
},
}
}
/// Represents a connection to a particular FVM volume.
/// Using this struct one can perform I/O, extend, shrink or destroy the volume.
pub struct VolumeConnection {
volume_proxy: VolumeProxy,
block_device: RemoteBlockDevice,
slice_size: u64,
}
impl VolumeConnection {
pub async fn new(block_path: PathBuf, volume_guid: Guid, slice_size: u64) -> Self {
let volume_path = get_volume_path(block_path, &volume_guid).await;
let volume_path = volume_path.to_str().unwrap();
let volume_proxy = connect_to_service_at_path::<VolumeMarker>(volume_path).unwrap();
// Connect to the Block FIDL protocol
let (client_end, server_end) = Channel::create().unwrap();
connect_channel_to_service_at_path(server_end, volume_path).unwrap();
let block_device = RemoteBlockDevice::new(client_end).unwrap();
Self { volume_proxy, block_device, slice_size }
}
// Writes a slice worth of data at the given offset.
pub async fn write_slice_at(&self, data: &[u8], slice_offset: u64) -> Result<(), Status> {
let offset = slice_offset * self.slice_size;
assert_eq!(data.len() as u64, self.slice_size);
let buffer_slice = BufferSlice::from(data);
let result = self.block_device.write_at(buffer_slice, offset).await;
return anyhow_to_status(result);
}
// Reads a slice worth of data from the given offset.
pub async fn read_slice_at(&self, slice_offset: u64) -> Result<Vec<u8>, Status> {
let mut data: Vec<u8> = Vec::with_capacity(self.slice_size as usize);
data.resize(self.slice_size as usize, 0);
let offset = slice_offset * self.slice_size;
assert_eq!(data.len() as u64, self.slice_size);
let buffer_slice = MutableBufferSlice::from(data.as_mut_slice());
let result = self.block_device.read_at(buffer_slice, offset).await;
let result = anyhow_to_status(result);
let result = result.map(|_| data);
return result;
}
// Adds slices to the volume at a given offset.
pub async fn extend(&self, start_slice: u64, slice_count: u64) -> Result<(), Status> {
let result = self.volume_proxy.extend(start_slice, slice_count).await;
return fidl_to_status(result);
}
// Removes slices from the volume at a given offset.
pub async fn shrink(&self, start_slice: u64, slice_count: u64) -> Result<(), Status> {
let result = self.volume_proxy.shrink(start_slice, slice_count).await;
return fidl_to_status(result);
}
// Destroys the volume, returning all slices to the volume manager.
pub async fn destroy(&self) -> Result<(), Status> {
let result = self.volume_proxy.destroy().await;
return fidl_to_status(result);
}
pub fn slice_size(&self) -> u64 {
self.slice_size
}
}