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(¶ms)?;
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