// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Asynchronous disk image helpers. use std::io; use std::sync::Arc; use std::time::Duration; use async_trait::async_trait; use base::AsRawDescriptors; use base::FileAllocate; use base::FileSetLen; use base::FileSync; use base::PunchHole; use base::RawDescriptor; use base::WriteZeroesAt; use cros_async::BackingMemory; use cros_async::BlockingPool; use cros_async::Executor; use crate::AsyncDisk; use crate::DiskFile; use crate::DiskGetLen; use crate::Error; use crate::Result; /// Async wrapper around a non-async `DiskFile` using a `BlockingPool`. /// /// This is meant to be a transitional type, not a long-term solution for async disk support. Disk /// formats should be migrated to support async instead (b/219595052). pub struct AsyncDiskFileWrapper { blocking_pool: BlockingPool, inner: Arc, } impl AsyncDiskFileWrapper { #[allow(dead_code)] // Only used if qcow or android-sparse features are enabled pub fn new(disk_file: T, _ex: &Executor) -> Self { Self { blocking_pool: BlockingPool::new(1, Duration::from_secs(10)), inner: Arc::new(disk_file), } } } impl DiskGetLen for AsyncDiskFileWrapper { fn get_len(&self) -> io::Result { self.inner.get_len() } } impl FileSetLen for AsyncDiskFileWrapper { fn set_len(&self, len: u64) -> io::Result<()> { self.inner.set_len(len) } } impl FileAllocate for AsyncDiskFileWrapper { fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { self.inner.allocate(offset, len) } } impl AsRawDescriptors for AsyncDiskFileWrapper { fn as_raw_descriptors(&self) -> Vec { self.inner.as_raw_descriptors() } } pub trait DiskFlush { /// Flush intermediary buffers and/or dirty state to file. fsync not required. fn flush(&self) -> io::Result<()>; } #[async_trait(?Send)] impl< T: 'static + DiskFile + DiskFlush + Send + Sync + FileAllocate + FileSetLen + FileSync + PunchHole + WriteZeroesAt, > AsyncDisk for AsyncDiskFileWrapper { async fn flush(&self) -> Result<()> { let inner_clone = self.inner.clone(); self.blocking_pool .spawn(move || inner_clone.flush().map_err(Error::IoFlush)) .await } async fn fsync(&self) -> Result<()> { let inner_clone = self.inner.clone(); self.blocking_pool .spawn(move || inner_clone.fsync().map_err(Error::IoFsync)) .await } async fn fdatasync(&self) -> Result<()> { let inner_clone = self.inner.clone(); self.blocking_pool .spawn(move || inner_clone.fdatasync().map_err(Error::IoFdatasync)) .await } async fn read_to_mem<'a>( &'a self, mut file_offset: u64, mem: Arc, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result { let inner_clone = self.inner.clone(); let mem_offsets: Vec = mem_offsets.collect(); self.blocking_pool .spawn(move || { let mut size = 0; for region in mem_offsets { let mem_slice = mem.get_volatile_slice(region).unwrap(); let bytes_read = inner_clone .read_at_volatile(mem_slice, file_offset) .map_err(Error::ReadingData)?; size += bytes_read; if bytes_read < mem_slice.size() { break; } file_offset += bytes_read as u64; } Ok(size) }) .await } async fn write_from_mem<'a>( &'a self, mut file_offset: u64, mem: Arc, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result { let inner_clone = self.inner.clone(); let mem_offsets: Vec = mem_offsets.collect(); self.blocking_pool .spawn(move || { let mut size = 0; for region in mem_offsets { let mem_slice = mem.get_volatile_slice(region).unwrap(); let bytes_written = inner_clone .write_at_volatile(mem_slice, file_offset) .map_err(Error::ReadingData)?; size += bytes_written; if bytes_written < mem_slice.size() { break; } file_offset += bytes_written as u64; } Ok(size) }) .await } async fn punch_hole(&self, file_offset: u64, length: u64) -> Result<()> { let inner_clone = self.inner.clone(); self.blocking_pool .spawn(move || { inner_clone .punch_hole(file_offset, length) .map_err(Error::IoPunchHole) }) .await } async fn write_zeroes_at(&self, file_offset: u64, length: u64) -> Result<()> { let inner_clone = self.inner.clone(); self.blocking_pool .spawn(move || { inner_clone .write_zeroes_all_at(file_offset, length as usize) .map_err(Error::WriteZeroes) }) .await } }