xref: /aosp_15_r20/external/crosvm/disk/src/disk.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2019 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! VM disk image file format I/O.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::cmp::min;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt::Debug;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Seek;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::io::SeekFrom;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
15*bb4ee6a4SAndroid Build Coastguard Worker 
16*bb4ee6a4SAndroid Build Coastguard Worker use async_trait::async_trait;
17*bb4ee6a4SAndroid Build Coastguard Worker use base::info;
18*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptors;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::FileAllocate;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::FileReadWriteAtVolatile;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::FileSetLen;
22*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::BackingMemory;
23*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::Executor;
24*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::IoSource;
25*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::MemRegionIter;
26*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error as ThisError;
27*bb4ee6a4SAndroid Build Coastguard Worker 
28*bb4ee6a4SAndroid Build Coastguard Worker mod asynchronous;
29*bb4ee6a4SAndroid Build Coastguard Worker #[allow(unused)]
30*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) use asynchronous::AsyncDiskFileWrapper;
31*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "qcow")]
32*bb4ee6a4SAndroid Build Coastguard Worker mod qcow;
33*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "qcow")]
34*bb4ee6a4SAndroid Build Coastguard Worker pub use qcow::QcowFile;
35*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "qcow")]
36*bb4ee6a4SAndroid Build Coastguard Worker pub use qcow::QCOW_MAGIC;
37*bb4ee6a4SAndroid Build Coastguard Worker mod sys;
38*bb4ee6a4SAndroid Build Coastguard Worker 
39*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
40*bb4ee6a4SAndroid Build Coastguard Worker mod composite;
41*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
42*bb4ee6a4SAndroid Build Coastguard Worker use composite::CompositeDiskFile;
43*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
44*bb4ee6a4SAndroid Build Coastguard Worker use composite::CDISK_MAGIC;
45*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
46*bb4ee6a4SAndroid Build Coastguard Worker mod gpt;
47*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
48*bb4ee6a4SAndroid Build Coastguard Worker pub use composite::create_composite_disk;
49*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
50*bb4ee6a4SAndroid Build Coastguard Worker pub use composite::create_zero_filler;
51*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
52*bb4ee6a4SAndroid Build Coastguard Worker pub use composite::Error as CompositeError;
53*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
54*bb4ee6a4SAndroid Build Coastguard Worker pub use composite::ImagePartitionType;
55*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
56*bb4ee6a4SAndroid Build Coastguard Worker pub use composite::PartitionInfo;
57*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "composite-disk")]
58*bb4ee6a4SAndroid Build Coastguard Worker pub use gpt::Error as GptError;
59*bb4ee6a4SAndroid Build Coastguard Worker 
60*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "android-sparse")]
61*bb4ee6a4SAndroid Build Coastguard Worker mod android_sparse;
62*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "android-sparse")]
63*bb4ee6a4SAndroid Build Coastguard Worker use android_sparse::AndroidSparse;
64*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "android-sparse")]
65*bb4ee6a4SAndroid Build Coastguard Worker use android_sparse::SPARSE_HEADER_MAGIC;
66*bb4ee6a4SAndroid Build Coastguard Worker use sys::read_from_disk;
67*bb4ee6a4SAndroid Build Coastguard Worker 
68*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "zstd")]
69*bb4ee6a4SAndroid Build Coastguard Worker mod zstd;
70*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "zstd")]
71*bb4ee6a4SAndroid Build Coastguard Worker use zstd::ZstdDisk;
72*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "zstd")]
73*bb4ee6a4SAndroid Build Coastguard Worker use zstd::ZSTD_FRAME_MAGIC;
74*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "zstd")]
75*bb4ee6a4SAndroid Build Coastguard Worker use zstd::ZSTD_SKIPPABLE_MAGIC_HIGH;
76*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "zstd")]
77*bb4ee6a4SAndroid Build Coastguard Worker use zstd::ZSTD_SKIPPABLE_MAGIC_LOW;
78*bb4ee6a4SAndroid Build Coastguard Worker 
79*bb4ee6a4SAndroid Build Coastguard Worker /// Nesting depth limit for disk formats that can open other disk files.
80*bb4ee6a4SAndroid Build Coastguard Worker const MAX_NESTING_DEPTH: u32 = 10;
81*bb4ee6a4SAndroid Build Coastguard Worker 
82*bb4ee6a4SAndroid Build Coastguard Worker #[derive(ThisError, Debug)]
83*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
84*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to create block device: {0}")]
85*bb4ee6a4SAndroid Build Coastguard Worker     BlockDeviceNew(base::Error),
86*bb4ee6a4SAndroid Build Coastguard Worker     #[error("requested file conversion not supported")]
87*bb4ee6a4SAndroid Build Coastguard Worker     ConversionNotSupported,
88*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "android-sparse")]
89*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure in android sparse disk: {0}")]
90*bb4ee6a4SAndroid Build Coastguard Worker     CreateAndroidSparseDisk(android_sparse::Error),
91*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "composite-disk")]
92*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure in composite disk: {0}")]
93*bb4ee6a4SAndroid Build Coastguard Worker     CreateCompositeDisk(composite::Error),
94*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "zstd")]
95*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure in zstd disk: {0}")]
96*bb4ee6a4SAndroid Build Coastguard Worker     CreateZstdDisk(anyhow::Error),
97*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure creating single file disk: {0}")]
98*bb4ee6a4SAndroid Build Coastguard Worker     CreateSingleFileDisk(cros_async::AsyncError),
99*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to set O_DIRECT on disk image: {0}")]
100*bb4ee6a4SAndroid Build Coastguard Worker     DirectFailed(base::Error),
101*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with fdatasync: {0}")]
102*bb4ee6a4SAndroid Build Coastguard Worker     Fdatasync(cros_async::AsyncError),
103*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with fsync: {0}")]
104*bb4ee6a4SAndroid Build Coastguard Worker     Fsync(cros_async::AsyncError),
105*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to lock file: {0}")]
106*bb4ee6a4SAndroid Build Coastguard Worker     LockFileFailure(base::Error),
107*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with fdatasync: {0}")]
108*bb4ee6a4SAndroid Build Coastguard Worker     IoFdatasync(io::Error),
109*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with flush: {0}")]
110*bb4ee6a4SAndroid Build Coastguard Worker     IoFlush(io::Error),
111*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with fsync: {0}")]
112*bb4ee6a4SAndroid Build Coastguard Worker     IoFsync(io::Error),
113*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure to punch hole: {0}")]
114*bb4ee6a4SAndroid Build Coastguard Worker     IoPunchHole(io::Error),
115*bb4ee6a4SAndroid Build Coastguard Worker     #[error("checking host fs type: {0}")]
116*bb4ee6a4SAndroid Build Coastguard Worker     HostFsType(base::Error),
117*bb4ee6a4SAndroid Build Coastguard Worker     #[error("maximum disk nesting depth exceeded")]
118*bb4ee6a4SAndroid Build Coastguard Worker     MaxNestingDepthExceeded,
119*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to open disk file \"{0}\": {1}")]
120*bb4ee6a4SAndroid Build Coastguard Worker     OpenFile(String, base::Error),
121*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure to punch hole: {0}")]
122*bb4ee6a4SAndroid Build Coastguard Worker     PunchHole(cros_async::AsyncError),
123*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure to punch hole for block device file: {0}")]
124*bb4ee6a4SAndroid Build Coastguard Worker     PunchHoleBlockDeviceFile(base::Error),
125*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "qcow")]
126*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure in qcow: {0}")]
127*bb4ee6a4SAndroid Build Coastguard Worker     QcowError(qcow::Error),
128*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read data: {0}")]
129*bb4ee6a4SAndroid Build Coastguard Worker     ReadingData(io::Error),
130*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read header: {0}")]
131*bb4ee6a4SAndroid Build Coastguard Worker     ReadingHeader(io::Error),
132*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read to memory: {0}")]
133*bb4ee6a4SAndroid Build Coastguard Worker     ReadToMem(cros_async::AsyncError),
134*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to seek file: {0}")]
135*bb4ee6a4SAndroid Build Coastguard Worker     SeekingFile(io::Error),
136*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to set file size: {0}")]
137*bb4ee6a4SAndroid Build Coastguard Worker     SettingFileSize(io::Error),
138*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unknown disk type")]
139*bb4ee6a4SAndroid Build Coastguard Worker     UnknownType,
140*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write from memory: {0}")]
141*bb4ee6a4SAndroid Build Coastguard Worker     WriteFromMem(cros_async::AsyncError),
142*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write from vec: {0}")]
143*bb4ee6a4SAndroid Build Coastguard Worker     WriteFromVec(cros_async::AsyncError),
144*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write zeroes: {0}")]
145*bb4ee6a4SAndroid Build Coastguard Worker     WriteZeroes(io::Error),
146*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write data: {0}")]
147*bb4ee6a4SAndroid Build Coastguard Worker     WritingData(io::Error),
148*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to convert to async: {0}")]
149*bb4ee6a4SAndroid Build Coastguard Worker     ToAsync(cros_async::AsyncError),
150*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(windows)]
151*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to set disk file sparse: {0}")]
152*bb4ee6a4SAndroid Build Coastguard Worker     SetSparseFailure(io::Error),
153*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failure with guest memory access: {0}")]
154*bb4ee6a4SAndroid Build Coastguard Worker     GuestMemory(cros_async::mem::Error),
155*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unsupported operation")]
156*bb4ee6a4SAndroid Build Coastguard Worker     UnsupportedOperation,
157*bb4ee6a4SAndroid Build Coastguard Worker }
158*bb4ee6a4SAndroid Build Coastguard Worker 
159*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>;
160*bb4ee6a4SAndroid Build Coastguard Worker 
161*bb4ee6a4SAndroid Build Coastguard Worker /// A trait for getting the length of a disk image or raw block device.
162*bb4ee6a4SAndroid Build Coastguard Worker pub trait DiskGetLen {
163*bb4ee6a4SAndroid Build Coastguard Worker     /// Get the current length of the disk in bytes.
get_len(&self) -> io::Result<u64>164*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len(&self) -> io::Result<u64>;
165*bb4ee6a4SAndroid Build Coastguard Worker }
166*bb4ee6a4SAndroid Build Coastguard Worker 
167*bb4ee6a4SAndroid Build Coastguard Worker impl DiskGetLen for File {
get_len(&self) -> io::Result<u64>168*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len(&self) -> io::Result<u64> {
169*bb4ee6a4SAndroid Build Coastguard Worker         let mut s = self;
170*bb4ee6a4SAndroid Build Coastguard Worker         let orig_seek = s.stream_position()?;
171*bb4ee6a4SAndroid Build Coastguard Worker         let end = s.seek(SeekFrom::End(0))?;
172*bb4ee6a4SAndroid Build Coastguard Worker         s.seek(SeekFrom::Start(orig_seek))?;
173*bb4ee6a4SAndroid Build Coastguard Worker         Ok(end)
174*bb4ee6a4SAndroid Build Coastguard Worker     }
175*bb4ee6a4SAndroid Build Coastguard Worker }
176*bb4ee6a4SAndroid Build Coastguard Worker 
177*bb4ee6a4SAndroid Build Coastguard Worker /// The prerequisites necessary to support a block device.
178*bb4ee6a4SAndroid Build Coastguard Worker pub trait DiskFile:
179*bb4ee6a4SAndroid Build Coastguard Worker     FileSetLen + DiskGetLen + FileReadWriteAtVolatile + ToAsyncDisk + Send + AsRawDescriptors + Debug
180*bb4ee6a4SAndroid Build Coastguard Worker {
181*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a new DiskFile instance that shares the same underlying disk file image. IO
182*bb4ee6a4SAndroid Build Coastguard Worker     /// operations to a DiskFile should affect all DiskFile instances with the same underlying disk
183*bb4ee6a4SAndroid Build Coastguard Worker     /// file image.
184*bb4ee6a4SAndroid Build Coastguard Worker     ///
185*bb4ee6a4SAndroid Build Coastguard Worker     /// `try_clone()` returns [`io::ErrorKind::Unsupported`] Error if a DiskFile does not support
186*bb4ee6a4SAndroid Build Coastguard Worker     /// creating an instance with the same underlying disk file image.
try_clone(&self) -> io::Result<Box<dyn DiskFile>>187*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> io::Result<Box<dyn DiskFile>> {
188*bb4ee6a4SAndroid Build Coastguard Worker         Err(io::Error::new(
189*bb4ee6a4SAndroid Build Coastguard Worker             io::ErrorKind::Unsupported,
190*bb4ee6a4SAndroid Build Coastguard Worker             "unsupported operation",
191*bb4ee6a4SAndroid Build Coastguard Worker         ))
192*bb4ee6a4SAndroid Build Coastguard Worker     }
193*bb4ee6a4SAndroid Build Coastguard Worker }
194*bb4ee6a4SAndroid Build Coastguard Worker 
195*bb4ee6a4SAndroid Build Coastguard Worker /// A `DiskFile` that can be converted for asychronous access.
196*bb4ee6a4SAndroid Build Coastguard Worker pub trait ToAsyncDisk: AsRawDescriptors + DiskGetLen + Send {
197*bb4ee6a4SAndroid Build Coastguard Worker     /// Convert a boxed self in to a box-wrapped implementaiton of AsyncDisk.
198*bb4ee6a4SAndroid Build Coastguard Worker     /// Used to convert a standard disk image to an async disk image. This conversion and the
199*bb4ee6a4SAndroid Build Coastguard Worker     /// inverse are needed so that the `Send` DiskImage can be given to the block thread where it is
200*bb4ee6a4SAndroid Build Coastguard Worker     /// converted to a non-`Send` AsyncDisk. The AsyncDisk can then be converted back and returned
201*bb4ee6a4SAndroid Build Coastguard Worker     /// to the main device thread if the block device is destroyed or reset.
to_async_disk(self: Box<Self>, ex: &Executor) -> Result<Box<dyn AsyncDisk>>202*bb4ee6a4SAndroid Build Coastguard Worker     fn to_async_disk(self: Box<Self>, ex: &Executor) -> Result<Box<dyn AsyncDisk>>;
203*bb4ee6a4SAndroid Build Coastguard Worker }
204*bb4ee6a4SAndroid Build Coastguard Worker 
205*bb4ee6a4SAndroid Build Coastguard Worker impl ToAsyncDisk for File {
to_async_disk(self: Box<Self>, ex: &Executor) -> Result<Box<dyn AsyncDisk>>206*bb4ee6a4SAndroid Build Coastguard Worker     fn to_async_disk(self: Box<Self>, ex: &Executor) -> Result<Box<dyn AsyncDisk>> {
207*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Box::new(SingleFileDisk::new(*self, ex)?))
208*bb4ee6a4SAndroid Build Coastguard Worker     }
209*bb4ee6a4SAndroid Build Coastguard Worker }
210*bb4ee6a4SAndroid Build Coastguard Worker 
211*bb4ee6a4SAndroid Build Coastguard Worker /// The variants of image files on the host that can be used as virtual disks.
212*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, PartialEq, Eq)]
213*bb4ee6a4SAndroid Build Coastguard Worker pub enum ImageType {
214*bb4ee6a4SAndroid Build Coastguard Worker     Raw,
215*bb4ee6a4SAndroid Build Coastguard Worker     Qcow2,
216*bb4ee6a4SAndroid Build Coastguard Worker     CompositeDisk,
217*bb4ee6a4SAndroid Build Coastguard Worker     AndroidSparse,
218*bb4ee6a4SAndroid Build Coastguard Worker     Zstd,
219*bb4ee6a4SAndroid Build Coastguard Worker }
220*bb4ee6a4SAndroid Build Coastguard Worker 
221*bb4ee6a4SAndroid Build Coastguard Worker /// Detect the type of an image file by checking for a valid header of the supported formats.
detect_image_type(file: &File, overlapped_mode: bool) -> Result<ImageType>222*bb4ee6a4SAndroid Build Coastguard Worker pub fn detect_image_type(file: &File, overlapped_mode: bool) -> Result<ImageType> {
223*bb4ee6a4SAndroid Build Coastguard Worker     let mut f = file;
224*bb4ee6a4SAndroid Build Coastguard Worker     let disk_size = f.get_len().map_err(Error::SeekingFile)?;
225*bb4ee6a4SAndroid Build Coastguard Worker     let orig_seek = f.stream_position().map_err(Error::SeekingFile)?;
226*bb4ee6a4SAndroid Build Coastguard Worker 
227*bb4ee6a4SAndroid Build Coastguard Worker     info!("disk size {}", disk_size);
228*bb4ee6a4SAndroid Build Coastguard Worker 
229*bb4ee6a4SAndroid Build Coastguard Worker     // Try to read the disk in a nicely-aligned block size unless the whole file is smaller.
230*bb4ee6a4SAndroid Build Coastguard Worker     const MAGIC_BLOCK_SIZE: usize = 4096;
231*bb4ee6a4SAndroid Build Coastguard Worker     #[repr(align(4096))]
232*bb4ee6a4SAndroid Build Coastguard Worker     struct BlockAlignedBuffer {
233*bb4ee6a4SAndroid Build Coastguard Worker         data: [u8; MAGIC_BLOCK_SIZE],
234*bb4ee6a4SAndroid Build Coastguard Worker     }
235*bb4ee6a4SAndroid Build Coastguard Worker     let mut magic = BlockAlignedBuffer {
236*bb4ee6a4SAndroid Build Coastguard Worker         data: [0u8; MAGIC_BLOCK_SIZE],
237*bb4ee6a4SAndroid Build Coastguard Worker     };
238*bb4ee6a4SAndroid Build Coastguard Worker     let magic_read_len = if disk_size > MAGIC_BLOCK_SIZE as u64 {
239*bb4ee6a4SAndroid Build Coastguard Worker         MAGIC_BLOCK_SIZE
240*bb4ee6a4SAndroid Build Coastguard Worker     } else {
241*bb4ee6a4SAndroid Build Coastguard Worker         // This cast is safe since we know disk_size is less than MAGIC_BLOCK_SIZE (4096) and
242*bb4ee6a4SAndroid Build Coastguard Worker         // therefore is representable in usize.
243*bb4ee6a4SAndroid Build Coastguard Worker         disk_size as usize
244*bb4ee6a4SAndroid Build Coastguard Worker     };
245*bb4ee6a4SAndroid Build Coastguard Worker 
246*bb4ee6a4SAndroid Build Coastguard Worker     read_from_disk(f, 0, &mut magic.data[0..magic_read_len], overlapped_mode)?;
247*bb4ee6a4SAndroid Build Coastguard Worker     f.seek(SeekFrom::Start(orig_seek))
248*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::SeekingFile)?;
249*bb4ee6a4SAndroid Build Coastguard Worker 
250*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "composite-disk")]
251*bb4ee6a4SAndroid Build Coastguard Worker     if let Some(cdisk_magic) = magic.data.get(0..CDISK_MAGIC.len()) {
252*bb4ee6a4SAndroid Build Coastguard Worker         if cdisk_magic == CDISK_MAGIC.as_bytes() {
253*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(ImageType::CompositeDisk);
254*bb4ee6a4SAndroid Build Coastguard Worker         }
255*bb4ee6a4SAndroid Build Coastguard Worker     }
256*bb4ee6a4SAndroid Build Coastguard Worker 
257*bb4ee6a4SAndroid Build Coastguard Worker     #[allow(unused_variables)] // magic4 is only used with the qcow/android-sparse/zstd features.
258*bb4ee6a4SAndroid Build Coastguard Worker     if let Some(magic4) = magic
259*bb4ee6a4SAndroid Build Coastguard Worker         .data
260*bb4ee6a4SAndroid Build Coastguard Worker         .get(0..4)
261*bb4ee6a4SAndroid Build Coastguard Worker         .and_then(|v| <&[u8] as std::convert::TryInto<[u8; 4]>>::try_into(v).ok())
262*bb4ee6a4SAndroid Build Coastguard Worker     {
263*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "qcow")]
264*bb4ee6a4SAndroid Build Coastguard Worker         if magic4 == QCOW_MAGIC.to_be_bytes() {
265*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(ImageType::Qcow2);
266*bb4ee6a4SAndroid Build Coastguard Worker         }
267*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "android-sparse")]
268*bb4ee6a4SAndroid Build Coastguard Worker         if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
269*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(ImageType::AndroidSparse);
270*bb4ee6a4SAndroid Build Coastguard Worker         }
271*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "zstd")]
272*bb4ee6a4SAndroid Build Coastguard Worker         if u32::from_le_bytes(magic4) == ZSTD_FRAME_MAGIC
273*bb4ee6a4SAndroid Build Coastguard Worker             || (u32::from_le_bytes(magic4) >= ZSTD_SKIPPABLE_MAGIC_LOW
274*bb4ee6a4SAndroid Build Coastguard Worker                 && u32::from_le_bytes(magic4) <= ZSTD_SKIPPABLE_MAGIC_HIGH)
275*bb4ee6a4SAndroid Build Coastguard Worker         {
276*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(ImageType::Zstd);
277*bb4ee6a4SAndroid Build Coastguard Worker         }
278*bb4ee6a4SAndroid Build Coastguard Worker     }
279*bb4ee6a4SAndroid Build Coastguard Worker 
280*bb4ee6a4SAndroid Build Coastguard Worker     Ok(ImageType::Raw)
281*bb4ee6a4SAndroid Build Coastguard Worker }
282*bb4ee6a4SAndroid Build Coastguard Worker 
283*bb4ee6a4SAndroid Build Coastguard Worker impl DiskFile for File {
try_clone(&self) -> io::Result<Box<dyn DiskFile>>284*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> io::Result<Box<dyn DiskFile>> {
285*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Box::new(self.try_clone()?))
286*bb4ee6a4SAndroid Build Coastguard Worker     }
287*bb4ee6a4SAndroid Build Coastguard Worker }
288*bb4ee6a4SAndroid Build Coastguard Worker 
289*bb4ee6a4SAndroid Build Coastguard Worker pub struct DiskFileParams {
290*bb4ee6a4SAndroid Build Coastguard Worker     pub path: PathBuf,
291*bb4ee6a4SAndroid Build Coastguard Worker     pub is_read_only: bool,
292*bb4ee6a4SAndroid Build Coastguard Worker     // Whether to call `base::set_sparse_file` on the file. Currently only affects Windows and is
293*bb4ee6a4SAndroid Build Coastguard Worker     // irrelevant for read only files.
294*bb4ee6a4SAndroid Build Coastguard Worker     pub is_sparse_file: bool,
295*bb4ee6a4SAndroid Build Coastguard Worker     // Whether to open the file in overlapped mode. Only affects Windows.
296*bb4ee6a4SAndroid Build Coastguard Worker     pub is_overlapped: bool,
297*bb4ee6a4SAndroid Build Coastguard Worker     // Whether to disable OS page caches / buffering.
298*bb4ee6a4SAndroid Build Coastguard Worker     pub is_direct: bool,
299*bb4ee6a4SAndroid Build Coastguard Worker     // Whether to lock the file.
300*bb4ee6a4SAndroid Build Coastguard Worker     pub lock: bool,
301*bb4ee6a4SAndroid Build Coastguard Worker     // The nesting depth of the file. Used to avoid infinite recursion. Users outside the disk
302*bb4ee6a4SAndroid Build Coastguard Worker     // crate should set this to zero.
303*bb4ee6a4SAndroid Build Coastguard Worker     pub depth: u32,
304*bb4ee6a4SAndroid Build Coastguard Worker }
305*bb4ee6a4SAndroid Build Coastguard Worker 
306*bb4ee6a4SAndroid Build Coastguard Worker /// Inspect the image file type and create an appropriate disk file to match it.
open_disk_file(params: DiskFileParams) -> Result<Box<dyn DiskFile>>307*bb4ee6a4SAndroid Build Coastguard Worker pub fn open_disk_file(params: DiskFileParams) -> Result<Box<dyn DiskFile>> {
308*bb4ee6a4SAndroid Build Coastguard Worker     if params.depth > MAX_NESTING_DEPTH {
309*bb4ee6a4SAndroid Build Coastguard Worker         return Err(Error::MaxNestingDepthExceeded);
310*bb4ee6a4SAndroid Build Coastguard Worker     }
311*bb4ee6a4SAndroid Build Coastguard Worker 
312*bb4ee6a4SAndroid Build Coastguard Worker     let raw_image = sys::open_raw_disk_image(&params)?;
313*bb4ee6a4SAndroid Build Coastguard Worker     let image_type = detect_image_type(&raw_image, params.is_overlapped)?;
314*bb4ee6a4SAndroid Build Coastguard Worker     Ok(match image_type {
315*bb4ee6a4SAndroid Build Coastguard Worker         ImageType::Raw => {
316*bb4ee6a4SAndroid Build Coastguard Worker             sys::apply_raw_disk_file_options(&raw_image, params.is_sparse_file)?;
317*bb4ee6a4SAndroid Build Coastguard Worker             Box::new(raw_image) as Box<dyn DiskFile>
318*bb4ee6a4SAndroid Build Coastguard Worker         }
319*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "qcow")]
320*bb4ee6a4SAndroid Build Coastguard Worker         ImageType::Qcow2 => Box::new(QcowFile::from(raw_image, params).map_err(Error::QcowError)?)
321*bb4ee6a4SAndroid Build Coastguard Worker             as Box<dyn DiskFile>,
322*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "composite-disk")]
323*bb4ee6a4SAndroid Build Coastguard Worker         ImageType::CompositeDisk => {
324*bb4ee6a4SAndroid Build Coastguard Worker             // Valid composite disk header present
325*bb4ee6a4SAndroid Build Coastguard Worker             Box::new(
326*bb4ee6a4SAndroid Build Coastguard Worker                 CompositeDiskFile::from_file(raw_image, params)
327*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(Error::CreateCompositeDisk)?,
328*bb4ee6a4SAndroid Build Coastguard Worker             ) as Box<dyn DiskFile>
329*bb4ee6a4SAndroid Build Coastguard Worker         }
330*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "android-sparse")]
331*bb4ee6a4SAndroid Build Coastguard Worker         ImageType::AndroidSparse => {
332*bb4ee6a4SAndroid Build Coastguard Worker             Box::new(AndroidSparse::from_file(raw_image).map_err(Error::CreateAndroidSparseDisk)?)
333*bb4ee6a4SAndroid Build Coastguard Worker                 as Box<dyn DiskFile>
334*bb4ee6a4SAndroid Build Coastguard Worker         }
335*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(feature = "zstd")]
336*bb4ee6a4SAndroid Build Coastguard Worker         ImageType::Zstd => Box::new(ZstdDisk::from_file(raw_image).map_err(Error::CreateZstdDisk)?)
337*bb4ee6a4SAndroid Build Coastguard Worker             as Box<dyn DiskFile>,
338*bb4ee6a4SAndroid Build Coastguard Worker         #[allow(unreachable_patterns)]
339*bb4ee6a4SAndroid Build Coastguard Worker         _ => return Err(Error::UnknownType),
340*bb4ee6a4SAndroid Build Coastguard Worker     })
341*bb4ee6a4SAndroid Build Coastguard Worker }
342*bb4ee6a4SAndroid Build Coastguard Worker 
343*bb4ee6a4SAndroid Build Coastguard Worker /// An asynchronously accessible disk.
344*bb4ee6a4SAndroid Build Coastguard Worker #[async_trait(?Send)]
345*bb4ee6a4SAndroid Build Coastguard Worker pub trait AsyncDisk: DiskGetLen + FileSetLen + FileAllocate {
346*bb4ee6a4SAndroid Build Coastguard Worker     /// Flush intermediary buffers and/or dirty state to file. fsync not required.
flush(&self) -> Result<()>347*bb4ee6a4SAndroid Build Coastguard Worker     async fn flush(&self) -> Result<()>;
348*bb4ee6a4SAndroid Build Coastguard Worker 
349*bb4ee6a4SAndroid Build Coastguard Worker     /// Asynchronously fsyncs any completed operations to the disk.
fsync(&self) -> Result<()>350*bb4ee6a4SAndroid Build Coastguard Worker     async fn fsync(&self) -> Result<()>;
351*bb4ee6a4SAndroid Build Coastguard Worker 
352*bb4ee6a4SAndroid Build Coastguard Worker     /// Asynchronously fdatasyncs any completed operations to the disk.
353*bb4ee6a4SAndroid Build Coastguard Worker     /// Note that an implementation may simply call fsync for fdatasync.
fdatasync(&self) -> Result<()>354*bb4ee6a4SAndroid Build Coastguard Worker     async fn fdatasync(&self) -> Result<()>;
355*bb4ee6a4SAndroid Build Coastguard Worker 
356*bb4ee6a4SAndroid Build Coastguard Worker     /// Reads from the file at 'file_offset' into memory `mem` at `mem_offsets`.
357*bb4ee6a4SAndroid Build Coastguard Worker     /// `mem_offsets` is similar to an iovec except relative to the start of `mem`.
read_to_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result<usize>358*bb4ee6a4SAndroid Build Coastguard Worker     async fn read_to_mem<'a>(
359*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
360*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
361*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
362*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: cros_async::MemRegionIter<'a>,
363*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize>;
364*bb4ee6a4SAndroid Build Coastguard Worker 
365*bb4ee6a4SAndroid Build Coastguard Worker     /// Writes to the file at 'file_offset' from memory `mem` at `mem_offsets`.
write_from_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result<usize>366*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_from_mem<'a>(
367*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
368*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
369*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
370*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: cros_async::MemRegionIter<'a>,
371*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize>;
372*bb4ee6a4SAndroid Build Coastguard Worker 
373*bb4ee6a4SAndroid Build Coastguard Worker     /// Replaces a range of bytes with a hole.
punch_hole(&self, file_offset: u64, length: u64) -> Result<()>374*bb4ee6a4SAndroid Build Coastguard Worker     async fn punch_hole(&self, file_offset: u64, length: u64) -> Result<()>;
375*bb4ee6a4SAndroid Build Coastguard Worker 
376*bb4ee6a4SAndroid Build Coastguard Worker     /// Writes up to `length` bytes of zeroes to the stream, returning how many bytes were written.
write_zeroes_at(&self, file_offset: u64, length: u64) -> Result<()>377*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_zeroes_at(&self, file_offset: u64, length: u64) -> Result<()>;
378*bb4ee6a4SAndroid Build Coastguard Worker 
379*bb4ee6a4SAndroid Build Coastguard Worker     /// Reads from the file at 'file_offset' into `buf`.
380*bb4ee6a4SAndroid Build Coastguard Worker     ///
381*bb4ee6a4SAndroid Build Coastguard Worker     /// Less efficient than `read_to_mem` because of extra copies and allocations.
read_double_buffered(&self, file_offset: u64, buf: &mut [u8]) -> Result<usize>382*bb4ee6a4SAndroid Build Coastguard Worker     async fn read_double_buffered(&self, file_offset: u64, buf: &mut [u8]) -> Result<usize> {
383*bb4ee6a4SAndroid Build Coastguard Worker         let backing_mem = Arc::new(cros_async::VecIoWrapper::from(vec![0u8; buf.len()]));
384*bb4ee6a4SAndroid Build Coastguard Worker         let region = cros_async::MemRegion {
385*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
386*bb4ee6a4SAndroid Build Coastguard Worker             len: buf.len(),
387*bb4ee6a4SAndroid Build Coastguard Worker         };
388*bb4ee6a4SAndroid Build Coastguard Worker         let n = self
389*bb4ee6a4SAndroid Build Coastguard Worker             .read_to_mem(
390*bb4ee6a4SAndroid Build Coastguard Worker                 file_offset,
391*bb4ee6a4SAndroid Build Coastguard Worker                 backing_mem.clone(),
392*bb4ee6a4SAndroid Build Coastguard Worker                 MemRegionIter::new(&[region]),
393*bb4ee6a4SAndroid Build Coastguard Worker             )
394*bb4ee6a4SAndroid Build Coastguard Worker             .await?;
395*bb4ee6a4SAndroid Build Coastguard Worker         backing_mem
396*bb4ee6a4SAndroid Build Coastguard Worker             .get_volatile_slice(region)
397*bb4ee6a4SAndroid Build Coastguard Worker             .expect("BUG: the VecIoWrapper shrank?")
398*bb4ee6a4SAndroid Build Coastguard Worker             .sub_slice(0, n)
399*bb4ee6a4SAndroid Build Coastguard Worker             .expect("BUG: read_to_mem return value too large?")
400*bb4ee6a4SAndroid Build Coastguard Worker             .copy_to(buf);
401*bb4ee6a4SAndroid Build Coastguard Worker         Ok(n)
402*bb4ee6a4SAndroid Build Coastguard Worker     }
403*bb4ee6a4SAndroid Build Coastguard Worker 
404*bb4ee6a4SAndroid Build Coastguard Worker     /// Writes to the file at 'file_offset' from `buf`.
405*bb4ee6a4SAndroid Build Coastguard Worker     ///
406*bb4ee6a4SAndroid Build Coastguard Worker     /// Less efficient than `write_from_mem` because of extra copies and allocations.
write_double_buffered(&self, file_offset: u64, buf: &[u8]) -> Result<usize>407*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_double_buffered(&self, file_offset: u64, buf: &[u8]) -> Result<usize> {
408*bb4ee6a4SAndroid Build Coastguard Worker         let backing_mem = Arc::new(cros_async::VecIoWrapper::from(buf.to_vec()));
409*bb4ee6a4SAndroid Build Coastguard Worker         let region = cros_async::MemRegion {
410*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
411*bb4ee6a4SAndroid Build Coastguard Worker             len: buf.len(),
412*bb4ee6a4SAndroid Build Coastguard Worker         };
413*bb4ee6a4SAndroid Build Coastguard Worker         self.write_from_mem(
414*bb4ee6a4SAndroid Build Coastguard Worker             file_offset,
415*bb4ee6a4SAndroid Build Coastguard Worker             backing_mem,
416*bb4ee6a4SAndroid Build Coastguard Worker             cros_async::MemRegionIter::new(&[region]),
417*bb4ee6a4SAndroid Build Coastguard Worker         )
418*bb4ee6a4SAndroid Build Coastguard Worker         .await
419*bb4ee6a4SAndroid Build Coastguard Worker     }
420*bb4ee6a4SAndroid Build Coastguard Worker }
421*bb4ee6a4SAndroid Build Coastguard Worker 
422*bb4ee6a4SAndroid Build Coastguard Worker /// A disk backed by a single file that implements `AsyncDisk` for access.
423*bb4ee6a4SAndroid Build Coastguard Worker pub struct SingleFileDisk {
424*bb4ee6a4SAndroid Build Coastguard Worker     inner: IoSource<File>,
425*bb4ee6a4SAndroid Build Coastguard Worker     // Whether the backed file is a block device since the punch-hole needs different operation.
426*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(any(target_os = "android", target_os = "linux"))]
427*bb4ee6a4SAndroid Build Coastguard Worker     is_block_device_file: bool,
428*bb4ee6a4SAndroid Build Coastguard Worker }
429*bb4ee6a4SAndroid Build Coastguard Worker 
430*bb4ee6a4SAndroid Build Coastguard Worker impl DiskGetLen for SingleFileDisk {
get_len(&self) -> io::Result<u64>431*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len(&self) -> io::Result<u64> {
432*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.as_source().get_len()
433*bb4ee6a4SAndroid Build Coastguard Worker     }
434*bb4ee6a4SAndroid Build Coastguard Worker }
435*bb4ee6a4SAndroid Build Coastguard Worker 
436*bb4ee6a4SAndroid Build Coastguard Worker impl FileSetLen for SingleFileDisk {
set_len(&self, len: u64) -> io::Result<()>437*bb4ee6a4SAndroid Build Coastguard Worker     fn set_len(&self, len: u64) -> io::Result<()> {
438*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.as_source().set_len(len)
439*bb4ee6a4SAndroid Build Coastguard Worker     }
440*bb4ee6a4SAndroid Build Coastguard Worker }
441*bb4ee6a4SAndroid Build Coastguard Worker 
442*bb4ee6a4SAndroid Build Coastguard Worker impl FileAllocate for SingleFileDisk {
allocate(&self, offset: u64, len: u64) -> io::Result<()>443*bb4ee6a4SAndroid Build Coastguard Worker     fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
444*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.as_source().allocate(offset, len)
445*bb4ee6a4SAndroid Build Coastguard Worker     }
446*bb4ee6a4SAndroid Build Coastguard Worker }
447*bb4ee6a4SAndroid Build Coastguard Worker 
448*bb4ee6a4SAndroid Build Coastguard Worker #[async_trait(?Send)]
449*bb4ee6a4SAndroid Build Coastguard Worker impl AsyncDisk for SingleFileDisk {
flush(&self) -> Result<()>450*bb4ee6a4SAndroid Build Coastguard Worker     async fn flush(&self) -> Result<()> {
451*bb4ee6a4SAndroid Build Coastguard Worker         // Nothing to flush, all file mutations are immediately sent to the OS.
452*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
453*bb4ee6a4SAndroid Build Coastguard Worker     }
454*bb4ee6a4SAndroid Build Coastguard Worker 
fsync(&self) -> Result<()>455*bb4ee6a4SAndroid Build Coastguard Worker     async fn fsync(&self) -> Result<()> {
456*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.fsync().await.map_err(Error::Fsync)
457*bb4ee6a4SAndroid Build Coastguard Worker     }
458*bb4ee6a4SAndroid Build Coastguard Worker 
fdatasync(&self) -> Result<()>459*bb4ee6a4SAndroid Build Coastguard Worker     async fn fdatasync(&self) -> Result<()> {
460*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.fdatasync().await.map_err(Error::Fdatasync)
461*bb4ee6a4SAndroid Build Coastguard Worker     }
462*bb4ee6a4SAndroid Build Coastguard Worker 
read_to_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result<usize>463*bb4ee6a4SAndroid Build Coastguard Worker     async fn read_to_mem<'a>(
464*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
465*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
466*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
467*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: cros_async::MemRegionIter<'a>,
468*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize> {
469*bb4ee6a4SAndroid Build Coastguard Worker         self.inner
470*bb4ee6a4SAndroid Build Coastguard Worker             .read_to_mem(Some(file_offset), mem, mem_offsets)
471*bb4ee6a4SAndroid Build Coastguard Worker             .await
472*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::ReadToMem)
473*bb4ee6a4SAndroid Build Coastguard Worker     }
474*bb4ee6a4SAndroid Build Coastguard Worker 
write_from_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: cros_async::MemRegionIter<'a>, ) -> Result<usize>475*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_from_mem<'a>(
476*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
477*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
478*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
479*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: cros_async::MemRegionIter<'a>,
480*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<usize> {
481*bb4ee6a4SAndroid Build Coastguard Worker         self.inner
482*bb4ee6a4SAndroid Build Coastguard Worker             .write_from_mem(Some(file_offset), mem, mem_offsets)
483*bb4ee6a4SAndroid Build Coastguard Worker             .await
484*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::WriteFromMem)
485*bb4ee6a4SAndroid Build Coastguard Worker     }
486*bb4ee6a4SAndroid Build Coastguard Worker 
punch_hole(&self, file_offset: u64, length: u64) -> Result<()>487*bb4ee6a4SAndroid Build Coastguard Worker     async fn punch_hole(&self, file_offset: u64, length: u64) -> Result<()> {
488*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(any(target_os = "android", target_os = "linux"))]
489*bb4ee6a4SAndroid Build Coastguard Worker         if self.is_block_device_file {
490*bb4ee6a4SAndroid Build Coastguard Worker             return base::linux::discard_block(self.inner.as_source(), file_offset, length)
491*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(Error::PunchHoleBlockDeviceFile);
492*bb4ee6a4SAndroid Build Coastguard Worker         }
493*bb4ee6a4SAndroid Build Coastguard Worker         self.inner
494*bb4ee6a4SAndroid Build Coastguard Worker             .punch_hole(file_offset, length)
495*bb4ee6a4SAndroid Build Coastguard Worker             .await
496*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::PunchHole)
497*bb4ee6a4SAndroid Build Coastguard Worker     }
498*bb4ee6a4SAndroid Build Coastguard Worker 
write_zeroes_at(&self, file_offset: u64, length: u64) -> Result<()>499*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_zeroes_at(&self, file_offset: u64, length: u64) -> Result<()> {
500*bb4ee6a4SAndroid Build Coastguard Worker         if self
501*bb4ee6a4SAndroid Build Coastguard Worker             .inner
502*bb4ee6a4SAndroid Build Coastguard Worker             .write_zeroes_at(file_offset, length)
503*bb4ee6a4SAndroid Build Coastguard Worker             .await
504*bb4ee6a4SAndroid Build Coastguard Worker             .is_ok()
505*bb4ee6a4SAndroid Build Coastguard Worker         {
506*bb4ee6a4SAndroid Build Coastguard Worker             return Ok(());
507*bb4ee6a4SAndroid Build Coastguard Worker         }
508*bb4ee6a4SAndroid Build Coastguard Worker 
509*bb4ee6a4SAndroid Build Coastguard Worker         // Fall back to filling zeros if more efficient write_zeroes_at doesn't work.
510*bb4ee6a4SAndroid Build Coastguard Worker         let buf_size = min(length, 0x10000);
511*bb4ee6a4SAndroid Build Coastguard Worker         let mut nwritten = 0;
512*bb4ee6a4SAndroid Build Coastguard Worker         while nwritten < length {
513*bb4ee6a4SAndroid Build Coastguard Worker             let remaining = length - nwritten;
514*bb4ee6a4SAndroid Build Coastguard Worker             let write_size = min(remaining, buf_size) as usize;
515*bb4ee6a4SAndroid Build Coastguard Worker             let buf = vec![0u8; write_size];
516*bb4ee6a4SAndroid Build Coastguard Worker             nwritten += self
517*bb4ee6a4SAndroid Build Coastguard Worker                 .inner
518*bb4ee6a4SAndroid Build Coastguard Worker                 .write_from_vec(Some(file_offset + nwritten), buf)
519*bb4ee6a4SAndroid Build Coastguard Worker                 .await
520*bb4ee6a4SAndroid Build Coastguard Worker                 .map(|(n, _)| n as u64)
521*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(Error::WriteFromVec)?;
522*bb4ee6a4SAndroid Build Coastguard Worker         }
523*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
524*bb4ee6a4SAndroid Build Coastguard Worker     }
525*bb4ee6a4SAndroid Build Coastguard Worker }
526