xref: /aosp_15_r20/external/crosvm/disk/src/composite.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 use std::cmp::max;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::cmp::min;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashSet;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::convert::TryInto;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::OpenOptions;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::io::ErrorKind;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Read;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Seek;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::io::SeekFrom;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Write;
17*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Range;
18*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
19*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
20*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::atomic::AtomicBool;
21*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::atomic::Ordering;
22*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
23*bb4ee6a4SAndroid Build Coastguard Worker 
24*bb4ee6a4SAndroid Build Coastguard Worker use async_trait::async_trait;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptors;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::FileAllocate;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::FileReadWriteAtVolatile;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::FileSetLen;
29*bb4ee6a4SAndroid Build Coastguard Worker use base::RawDescriptor;
30*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileSlice;
31*bb4ee6a4SAndroid Build Coastguard Worker use crc32fast::Hasher;
32*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::BackingMemory;
33*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::Executor;
34*bb4ee6a4SAndroid Build Coastguard Worker use cros_async::MemRegionIter;
35*bb4ee6a4SAndroid Build Coastguard Worker use protobuf::Message;
36*bb4ee6a4SAndroid Build Coastguard Worker use protos::cdisk_spec;
37*bb4ee6a4SAndroid Build Coastguard Worker use protos::cdisk_spec::ComponentDisk;
38*bb4ee6a4SAndroid Build Coastguard Worker use protos::cdisk_spec::CompositeDisk;
39*bb4ee6a4SAndroid Build Coastguard Worker use protos::cdisk_spec::ReadWriteCapability;
40*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
41*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
42*bb4ee6a4SAndroid Build Coastguard Worker use uuid::Uuid;
43*bb4ee6a4SAndroid Build Coastguard Worker 
44*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt;
45*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::write_gpt_header;
46*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::write_protective_mbr;
47*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GptPartitionEntry;
48*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GPT_BEGINNING_SIZE;
49*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GPT_END_SIZE;
50*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GPT_HEADER_SIZE;
51*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GPT_NUM_PARTITIONS;
52*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::GPT_PARTITION_ENTRY_SIZE;
53*bb4ee6a4SAndroid Build Coastguard Worker use crate::gpt::SECTOR_SIZE;
54*bb4ee6a4SAndroid Build Coastguard Worker use crate::open_disk_file;
55*bb4ee6a4SAndroid Build Coastguard Worker use crate::AsyncDisk;
56*bb4ee6a4SAndroid Build Coastguard Worker use crate::DiskFile;
57*bb4ee6a4SAndroid Build Coastguard Worker use crate::DiskFileParams;
58*bb4ee6a4SAndroid Build Coastguard Worker use crate::DiskGetLen;
59*bb4ee6a4SAndroid Build Coastguard Worker use crate::ImageType;
60*bb4ee6a4SAndroid Build Coastguard Worker use crate::ToAsyncDisk;
61*bb4ee6a4SAndroid Build Coastguard Worker 
62*bb4ee6a4SAndroid Build Coastguard Worker /// The amount of padding needed between the last partition entry and the first partition, to align
63*bb4ee6a4SAndroid Build Coastguard Worker /// the partition appropriately. The two sectors are for the MBR and the GPT header.
64*bb4ee6a4SAndroid Build Coastguard Worker const PARTITION_ALIGNMENT_SIZE: usize = GPT_BEGINNING_SIZE as usize
65*bb4ee6a4SAndroid Build Coastguard Worker     - 2 * SECTOR_SIZE as usize
66*bb4ee6a4SAndroid Build Coastguard Worker     - GPT_NUM_PARTITIONS as usize * GPT_PARTITION_ENTRY_SIZE as usize;
67*bb4ee6a4SAndroid Build Coastguard Worker const HEADER_PADDING_LENGTH: usize = SECTOR_SIZE as usize - GPT_HEADER_SIZE as usize;
68*bb4ee6a4SAndroid Build Coastguard Worker // Keep all partitions 4k aligned for performance.
69*bb4ee6a4SAndroid Build Coastguard Worker const PARTITION_SIZE_SHIFT: u8 = 12;
70*bb4ee6a4SAndroid Build Coastguard Worker // Keep the disk size a multiple of 64k for crosvm's virtio_blk driver.
71*bb4ee6a4SAndroid Build Coastguard Worker const DISK_SIZE_SHIFT: u8 = 16;
72*bb4ee6a4SAndroid Build Coastguard Worker 
73*bb4ee6a4SAndroid Build Coastguard Worker // From https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs.
74*bb4ee6a4SAndroid Build Coastguard Worker const LINUX_FILESYSTEM_GUID: Uuid = Uuid::from_u128(0x0FC63DAF_8483_4772_8E79_3D69D8477DE4);
75*bb4ee6a4SAndroid Build Coastguard Worker const EFI_SYSTEM_PARTITION_GUID: Uuid = Uuid::from_u128(0xC12A7328_F81F_11D2_BA4B_00A0C93EC93B);
76*bb4ee6a4SAndroid Build Coastguard Worker 
77*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
78*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
79*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error {
80*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to use underlying disk: \"{0}\"")]
81*bb4ee6a4SAndroid Build Coastguard Worker     DiskError(Box<crate::Error>),
82*bb4ee6a4SAndroid Build Coastguard Worker     #[error("duplicate GPT partition label \"{0}\"")]
83*bb4ee6a4SAndroid Build Coastguard Worker     DuplicatePartitionLabel(String),
84*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write GPT header: \"{0}\"")]
85*bb4ee6a4SAndroid Build Coastguard Worker     GptError(gpt::Error),
86*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid magic header for composite disk format")]
87*bb4ee6a4SAndroid Build Coastguard Worker     InvalidMagicHeader,
88*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid partition path {0:?}")]
89*bb4ee6a4SAndroid Build Coastguard Worker     InvalidPath(PathBuf),
90*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to parse specification proto: \"{0}\"")]
91*bb4ee6a4SAndroid Build Coastguard Worker     InvalidProto(protobuf::Error),
92*bb4ee6a4SAndroid Build Coastguard Worker     #[error("invalid specification: \"{0}\"")]
93*bb4ee6a4SAndroid Build Coastguard Worker     InvalidSpecification(String),
94*bb4ee6a4SAndroid Build Coastguard Worker     #[error("no image files for partition {0:?}")]
95*bb4ee6a4SAndroid Build Coastguard Worker     NoImageFiles(PartitionInfo),
96*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to open component file \"{1}\": \"{0}\"")]
97*bb4ee6a4SAndroid Build Coastguard Worker     OpenFile(io::Error, String),
98*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to read specification: \"{0}\"")]
99*bb4ee6a4SAndroid Build Coastguard Worker     ReadSpecificationError(io::Error),
100*bb4ee6a4SAndroid Build Coastguard Worker     #[error("Read-write partition {0:?} size is not a multiple of {}.", 1 << PARTITION_SIZE_SHIFT)]
101*bb4ee6a4SAndroid Build Coastguard Worker     UnalignedReadWrite(PartitionInfo),
102*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unknown version {0} in specification")]
103*bb4ee6a4SAndroid Build Coastguard Worker     UnknownVersion(u64),
104*bb4ee6a4SAndroid Build Coastguard Worker     #[error("unsupported component disk type \"{0:?}\"")]
105*bb4ee6a4SAndroid Build Coastguard Worker     UnsupportedComponent(ImageType),
106*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write composite disk header: \"{0}\"")]
107*bb4ee6a4SAndroid Build Coastguard Worker     WriteHeader(io::Error),
108*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write specification proto: \"{0}\"")]
109*bb4ee6a4SAndroid Build Coastguard Worker     WriteProto(protobuf::Error),
110*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to write zero filler: \"{0}\"")]
111*bb4ee6a4SAndroid Build Coastguard Worker     WriteZeroFiller(io::Error),
112*bb4ee6a4SAndroid Build Coastguard Worker }
113*bb4ee6a4SAndroid Build Coastguard Worker 
114*bb4ee6a4SAndroid Build Coastguard Worker impl From<gpt::Error> for Error {
from(e: gpt::Error) -> Self115*bb4ee6a4SAndroid Build Coastguard Worker     fn from(e: gpt::Error) -> Self {
116*bb4ee6a4SAndroid Build Coastguard Worker         Self::GptError(e)
117*bb4ee6a4SAndroid Build Coastguard Worker     }
118*bb4ee6a4SAndroid Build Coastguard Worker }
119*bb4ee6a4SAndroid Build Coastguard Worker 
120*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>;
121*bb4ee6a4SAndroid Build Coastguard Worker 
122*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
123*bb4ee6a4SAndroid Build Coastguard Worker struct ComponentDiskPart {
124*bb4ee6a4SAndroid Build Coastguard Worker     file: Box<dyn DiskFile>,
125*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
126*bb4ee6a4SAndroid Build Coastguard Worker     length: u64,
127*bb4ee6a4SAndroid Build Coastguard Worker     needs_fsync: AtomicBool,
128*bb4ee6a4SAndroid Build Coastguard Worker }
129*bb4ee6a4SAndroid Build Coastguard Worker 
130*bb4ee6a4SAndroid Build Coastguard Worker impl ComponentDiskPart {
range(&self) -> Range<u64>131*bb4ee6a4SAndroid Build Coastguard Worker     fn range(&self) -> Range<u64> {
132*bb4ee6a4SAndroid Build Coastguard Worker         self.offset..(self.offset + self.length)
133*bb4ee6a4SAndroid Build Coastguard Worker     }
134*bb4ee6a4SAndroid Build Coastguard Worker }
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker /// Represents a composite virtual disk made out of multiple component files. This is described on
137*bb4ee6a4SAndroid Build Coastguard Worker /// disk by a protocol buffer file that lists out the component file locations and their offsets
138*bb4ee6a4SAndroid Build Coastguard Worker /// and lengths on the virtual disk. The spaces covered by the component disks must be contiguous
139*bb4ee6a4SAndroid Build Coastguard Worker /// and not overlapping.
140*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
141*bb4ee6a4SAndroid Build Coastguard Worker pub struct CompositeDiskFile {
142*bb4ee6a4SAndroid Build Coastguard Worker     component_disks: Vec<ComponentDiskPart>,
143*bb4ee6a4SAndroid Build Coastguard Worker     // We keep the root composite file open so that the file lock is not dropped.
144*bb4ee6a4SAndroid Build Coastguard Worker     _disk_spec_file: File,
145*bb4ee6a4SAndroid Build Coastguard Worker }
146*bb4ee6a4SAndroid Build Coastguard Worker 
147*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/271381851): implement `try_clone`. It allows virtio-blk to run multiple workers.
148*bb4ee6a4SAndroid Build Coastguard Worker impl DiskFile for CompositeDiskFile {}
149*bb4ee6a4SAndroid Build Coastguard Worker 
ranges_overlap(a: &Range<u64>, b: &Range<u64>) -> bool150*bb4ee6a4SAndroid Build Coastguard Worker fn ranges_overlap(a: &Range<u64>, b: &Range<u64>) -> bool {
151*bb4ee6a4SAndroid Build Coastguard Worker     range_intersection(a, b).is_some()
152*bb4ee6a4SAndroid Build Coastguard Worker }
153*bb4ee6a4SAndroid Build Coastguard Worker 
range_intersection(a: &Range<u64>, b: &Range<u64>) -> Option<Range<u64>>154*bb4ee6a4SAndroid Build Coastguard Worker fn range_intersection(a: &Range<u64>, b: &Range<u64>) -> Option<Range<u64>> {
155*bb4ee6a4SAndroid Build Coastguard Worker     let r = Range {
156*bb4ee6a4SAndroid Build Coastguard Worker         start: max(a.start, b.start),
157*bb4ee6a4SAndroid Build Coastguard Worker         end: min(a.end, b.end),
158*bb4ee6a4SAndroid Build Coastguard Worker     };
159*bb4ee6a4SAndroid Build Coastguard Worker     if r.is_empty() {
160*bb4ee6a4SAndroid Build Coastguard Worker         None
161*bb4ee6a4SAndroid Build Coastguard Worker     } else {
162*bb4ee6a4SAndroid Build Coastguard Worker         Some(r)
163*bb4ee6a4SAndroid Build Coastguard Worker     }
164*bb4ee6a4SAndroid Build Coastguard Worker }
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker /// The version of the composite disk format supported by this implementation.
167*bb4ee6a4SAndroid Build Coastguard Worker const COMPOSITE_DISK_VERSION: u64 = 2;
168*bb4ee6a4SAndroid Build Coastguard Worker 
169*bb4ee6a4SAndroid Build Coastguard Worker /// A magic string placed at the beginning of a composite disk file to identify it.
170*bb4ee6a4SAndroid Build Coastguard Worker pub const CDISK_MAGIC: &str = "composite_disk\x1d";
171*bb4ee6a4SAndroid Build Coastguard Worker 
172*bb4ee6a4SAndroid Build Coastguard Worker impl CompositeDiskFile {
new(mut disks: Vec<ComponentDiskPart>, disk_spec_file: File) -> Result<CompositeDiskFile>173*bb4ee6a4SAndroid Build Coastguard Worker     fn new(mut disks: Vec<ComponentDiskPart>, disk_spec_file: File) -> Result<CompositeDiskFile> {
174*bb4ee6a4SAndroid Build Coastguard Worker         disks.sort_by(|d1, d2| d1.offset.cmp(&d2.offset));
175*bb4ee6a4SAndroid Build Coastguard Worker         for s in disks.windows(2) {
176*bb4ee6a4SAndroid Build Coastguard Worker             if s[0].offset == s[1].offset {
177*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::InvalidSpecification(format!(
178*bb4ee6a4SAndroid Build Coastguard Worker                     "Two disks at offset {}",
179*bb4ee6a4SAndroid Build Coastguard Worker                     s[0].offset
180*bb4ee6a4SAndroid Build Coastguard Worker                 )));
181*bb4ee6a4SAndroid Build Coastguard Worker             }
182*bb4ee6a4SAndroid Build Coastguard Worker         }
183*bb4ee6a4SAndroid Build Coastguard Worker         Ok(CompositeDiskFile {
184*bb4ee6a4SAndroid Build Coastguard Worker             component_disks: disks,
185*bb4ee6a4SAndroid Build Coastguard Worker             _disk_spec_file: disk_spec_file,
186*bb4ee6a4SAndroid Build Coastguard Worker         })
187*bb4ee6a4SAndroid Build Coastguard Worker     }
188*bb4ee6a4SAndroid Build Coastguard Worker 
189*bb4ee6a4SAndroid Build Coastguard Worker     /// Set up a composite disk by reading the specification from a file. The file must consist of
190*bb4ee6a4SAndroid Build Coastguard Worker     /// the CDISK_MAGIC string followed by one binary instance of the CompositeDisk protocol
191*bb4ee6a4SAndroid Build Coastguard Worker     /// buffer. Returns an error if it could not read the file or if the specification was invalid.
from_file(mut file: File, params: DiskFileParams) -> Result<CompositeDiskFile>192*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_file(mut file: File, params: DiskFileParams) -> Result<CompositeDiskFile> {
193*bb4ee6a4SAndroid Build Coastguard Worker         file.seek(SeekFrom::Start(0))
194*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::ReadSpecificationError)?;
195*bb4ee6a4SAndroid Build Coastguard Worker         let mut magic_space = [0u8; CDISK_MAGIC.len()];
196*bb4ee6a4SAndroid Build Coastguard Worker         file.read_exact(&mut magic_space[..])
197*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(Error::ReadSpecificationError)?;
198*bb4ee6a4SAndroid Build Coastguard Worker         if magic_space != CDISK_MAGIC.as_bytes() {
199*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidMagicHeader);
200*bb4ee6a4SAndroid Build Coastguard Worker         }
201*bb4ee6a4SAndroid Build Coastguard Worker         let proto: cdisk_spec::CompositeDisk =
202*bb4ee6a4SAndroid Build Coastguard Worker             Message::parse_from_reader(&mut file).map_err(Error::InvalidProto)?;
203*bb4ee6a4SAndroid Build Coastguard Worker         if proto.version > COMPOSITE_DISK_VERSION {
204*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::UnknownVersion(proto.version));
205*bb4ee6a4SAndroid Build Coastguard Worker         }
206*bb4ee6a4SAndroid Build Coastguard Worker         let mut disks: Vec<ComponentDiskPart> = proto
207*bb4ee6a4SAndroid Build Coastguard Worker             .component_disks
208*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
209*bb4ee6a4SAndroid Build Coastguard Worker             .map(|disk| {
210*bb4ee6a4SAndroid Build Coastguard Worker                 let writable = !params.is_read_only
211*bb4ee6a4SAndroid Build Coastguard Worker                     && disk.read_write_capability
212*bb4ee6a4SAndroid Build Coastguard Worker                         == cdisk_spec::ReadWriteCapability::READ_WRITE.into();
213*bb4ee6a4SAndroid Build Coastguard Worker                 let component_path = PathBuf::from(&disk.file_path);
214*bb4ee6a4SAndroid Build Coastguard Worker                 let path = if component_path.is_relative() || proto.version > 1 {
215*bb4ee6a4SAndroid Build Coastguard Worker                     params.path.parent().unwrap().join(component_path)
216*bb4ee6a4SAndroid Build Coastguard Worker                 } else {
217*bb4ee6a4SAndroid Build Coastguard Worker                     component_path
218*bb4ee6a4SAndroid Build Coastguard Worker                 };
219*bb4ee6a4SAndroid Build Coastguard Worker 
220*bb4ee6a4SAndroid Build Coastguard Worker                 // Note that a read-only parts of a composite disk should NOT be marked sparse,
221*bb4ee6a4SAndroid Build Coastguard Worker                 // as the action of marking them sparse is a write. This may seem a little hacky,
222*bb4ee6a4SAndroid Build Coastguard Worker                 // and it is; however:
223*bb4ee6a4SAndroid Build Coastguard Worker                 //    (a)  there is not a good way to pass sparseness parameters per composite disk
224*bb4ee6a4SAndroid Build Coastguard Worker                 //         part (the proto does not have fields for it).
225*bb4ee6a4SAndroid Build Coastguard Worker                 //    (b)  this override of sorts always matches the correct user intent.
226*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(ComponentDiskPart {
227*bb4ee6a4SAndroid Build Coastguard Worker                     file: open_disk_file(DiskFileParams {
228*bb4ee6a4SAndroid Build Coastguard Worker                         path: path.to_owned(),
229*bb4ee6a4SAndroid Build Coastguard Worker                         is_read_only: !writable,
230*bb4ee6a4SAndroid Build Coastguard Worker                         is_sparse_file: params.is_sparse_file && writable,
231*bb4ee6a4SAndroid Build Coastguard Worker                         // TODO: Should pass `params.is_overlapped` through here. Needs testing.
232*bb4ee6a4SAndroid Build Coastguard Worker                         is_overlapped: false,
233*bb4ee6a4SAndroid Build Coastguard Worker                         is_direct: params.is_direct,
234*bb4ee6a4SAndroid Build Coastguard Worker                         lock: params.lock,
235*bb4ee6a4SAndroid Build Coastguard Worker                         depth: params.depth + 1,
236*bb4ee6a4SAndroid Build Coastguard Worker                     })
237*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(|e| Error::DiskError(Box::new(e)))?,
238*bb4ee6a4SAndroid Build Coastguard Worker                     offset: disk.offset,
239*bb4ee6a4SAndroid Build Coastguard Worker                     length: 0, // Assigned later
240*bb4ee6a4SAndroid Build Coastguard Worker                     needs_fsync: AtomicBool::new(false),
241*bb4ee6a4SAndroid Build Coastguard Worker                 })
242*bb4ee6a4SAndroid Build Coastguard Worker             })
243*bb4ee6a4SAndroid Build Coastguard Worker             .collect::<Result<Vec<ComponentDiskPart>>>()?;
244*bb4ee6a4SAndroid Build Coastguard Worker         disks.sort_by(|d1, d2| d1.offset.cmp(&d2.offset));
245*bb4ee6a4SAndroid Build Coastguard Worker         for i in 0..(disks.len() - 1) {
246*bb4ee6a4SAndroid Build Coastguard Worker             let length = disks[i + 1].offset - disks[i].offset;
247*bb4ee6a4SAndroid Build Coastguard Worker             if length == 0 {
248*bb4ee6a4SAndroid Build Coastguard Worker                 let text = format!("Two disks at offset {}", disks[i].offset);
249*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::InvalidSpecification(text));
250*bb4ee6a4SAndroid Build Coastguard Worker             }
251*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(disk) = disks.get_mut(i) {
252*bb4ee6a4SAndroid Build Coastguard Worker                 disk.length = length;
253*bb4ee6a4SAndroid Build Coastguard Worker             } else {
254*bb4ee6a4SAndroid Build Coastguard Worker                 let text = format!("Unable to set disk length {}", length);
255*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::InvalidSpecification(text));
256*bb4ee6a4SAndroid Build Coastguard Worker             }
257*bb4ee6a4SAndroid Build Coastguard Worker         }
258*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(last_disk) = disks.last_mut() {
259*bb4ee6a4SAndroid Build Coastguard Worker             if proto.length <= last_disk.offset {
260*bb4ee6a4SAndroid Build Coastguard Worker                 let text = format!(
261*bb4ee6a4SAndroid Build Coastguard Worker                     "Full size of disk doesn't match last offset. {} <= {}",
262*bb4ee6a4SAndroid Build Coastguard Worker                     proto.length, last_disk.offset
263*bb4ee6a4SAndroid Build Coastguard Worker                 );
264*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(Error::InvalidSpecification(text));
265*bb4ee6a4SAndroid Build Coastguard Worker             }
266*bb4ee6a4SAndroid Build Coastguard Worker             last_disk.length = proto.length - last_disk.offset;
267*bb4ee6a4SAndroid Build Coastguard Worker         } else {
268*bb4ee6a4SAndroid Build Coastguard Worker             let text = format!("Unable to set last disk length to end at {}", proto.length);
269*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::InvalidSpecification(text));
270*bb4ee6a4SAndroid Build Coastguard Worker         }
271*bb4ee6a4SAndroid Build Coastguard Worker 
272*bb4ee6a4SAndroid Build Coastguard Worker         CompositeDiskFile::new(disks, file)
273*bb4ee6a4SAndroid Build Coastguard Worker     }
274*bb4ee6a4SAndroid Build Coastguard Worker 
length(&self) -> u64275*bb4ee6a4SAndroid Build Coastguard Worker     fn length(&self) -> u64 {
276*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(disk) = self.component_disks.last() {
277*bb4ee6a4SAndroid Build Coastguard Worker             disk.offset + disk.length
278*bb4ee6a4SAndroid Build Coastguard Worker         } else {
279*bb4ee6a4SAndroid Build Coastguard Worker             0
280*bb4ee6a4SAndroid Build Coastguard Worker         }
281*bb4ee6a4SAndroid Build Coastguard Worker     }
282*bb4ee6a4SAndroid Build Coastguard Worker 
disk_at_offset(&self, offset: u64) -> io::Result<&ComponentDiskPart>283*bb4ee6a4SAndroid Build Coastguard Worker     fn disk_at_offset(&self, offset: u64) -> io::Result<&ComponentDiskPart> {
284*bb4ee6a4SAndroid Build Coastguard Worker         self.component_disks
285*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
286*bb4ee6a4SAndroid Build Coastguard Worker             .find(|disk| disk.range().contains(&offset))
287*bb4ee6a4SAndroid Build Coastguard Worker             .ok_or(io::Error::new(
288*bb4ee6a4SAndroid Build Coastguard Worker                 ErrorKind::InvalidData,
289*bb4ee6a4SAndroid Build Coastguard Worker                 format!("no disk at offset {}", offset),
290*bb4ee6a4SAndroid Build Coastguard Worker             ))
291*bb4ee6a4SAndroid Build Coastguard Worker     }
292*bb4ee6a4SAndroid Build Coastguard Worker }
293*bb4ee6a4SAndroid Build Coastguard Worker 
294*bb4ee6a4SAndroid Build Coastguard Worker impl DiskGetLen for CompositeDiskFile {
get_len(&self) -> io::Result<u64>295*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len(&self) -> io::Result<u64> {
296*bb4ee6a4SAndroid Build Coastguard Worker         Ok(self.length())
297*bb4ee6a4SAndroid Build Coastguard Worker     }
298*bb4ee6a4SAndroid Build Coastguard Worker }
299*bb4ee6a4SAndroid Build Coastguard Worker 
300*bb4ee6a4SAndroid Build Coastguard Worker impl FileSetLen for CompositeDiskFile {
set_len(&self, _len: u64) -> io::Result<()>301*bb4ee6a4SAndroid Build Coastguard Worker     fn set_len(&self, _len: u64) -> io::Result<()> {
302*bb4ee6a4SAndroid Build Coastguard Worker         Err(io::Error::new(ErrorKind::Other, "unsupported operation"))
303*bb4ee6a4SAndroid Build Coastguard Worker     }
304*bb4ee6a4SAndroid Build Coastguard Worker }
305*bb4ee6a4SAndroid Build Coastguard Worker 
306*bb4ee6a4SAndroid Build Coastguard Worker // Implements Read and Write targeting volatile storage for composite disks.
307*bb4ee6a4SAndroid Build Coastguard Worker //
308*bb4ee6a4SAndroid Build Coastguard Worker // Note that reads and writes will return early if crossing component disk boundaries.
309*bb4ee6a4SAndroid Build Coastguard Worker // This is allowed by the read and write specifications, which only say read and write
310*bb4ee6a4SAndroid Build Coastguard Worker // have to return how many bytes were actually read or written. Use read_exact_volatile
311*bb4ee6a4SAndroid Build Coastguard Worker // or write_all_volatile to make sure all bytes are received/transmitted.
312*bb4ee6a4SAndroid Build Coastguard Worker //
313*bb4ee6a4SAndroid Build Coastguard Worker // If one of the component disks does a partial read or write, that also gets passed
314*bb4ee6a4SAndroid Build Coastguard Worker // transparently to the parent.
315*bb4ee6a4SAndroid Build Coastguard Worker impl FileReadWriteAtVolatile for CompositeDiskFile {
read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize>316*bb4ee6a4SAndroid Build Coastguard Worker     fn read_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
317*bb4ee6a4SAndroid Build Coastguard Worker         let cursor_location = offset;
318*bb4ee6a4SAndroid Build Coastguard Worker         let disk = self.disk_at_offset(cursor_location)?;
319*bb4ee6a4SAndroid Build Coastguard Worker         let subslice = if cursor_location + slice.size() as u64 > disk.offset + disk.length {
320*bb4ee6a4SAndroid Build Coastguard Worker             let new_size = disk.offset + disk.length - cursor_location;
321*bb4ee6a4SAndroid Build Coastguard Worker             slice
322*bb4ee6a4SAndroid Build Coastguard Worker                 .sub_slice(0, new_size as usize)
323*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(|e| io::Error::new(ErrorKind::InvalidData, e.to_string()))?
324*bb4ee6a4SAndroid Build Coastguard Worker         } else {
325*bb4ee6a4SAndroid Build Coastguard Worker             slice
326*bb4ee6a4SAndroid Build Coastguard Worker         };
327*bb4ee6a4SAndroid Build Coastguard Worker         disk.file
328*bb4ee6a4SAndroid Build Coastguard Worker             .read_at_volatile(subslice, cursor_location - disk.offset)
329*bb4ee6a4SAndroid Build Coastguard Worker     }
write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize>330*bb4ee6a4SAndroid Build Coastguard Worker     fn write_at_volatile(&self, slice: VolatileSlice, offset: u64) -> io::Result<usize> {
331*bb4ee6a4SAndroid Build Coastguard Worker         let cursor_location = offset;
332*bb4ee6a4SAndroid Build Coastguard Worker         let disk = self.disk_at_offset(cursor_location)?;
333*bb4ee6a4SAndroid Build Coastguard Worker         let subslice = if cursor_location + slice.size() as u64 > disk.offset + disk.length {
334*bb4ee6a4SAndroid Build Coastguard Worker             let new_size = disk.offset + disk.length - cursor_location;
335*bb4ee6a4SAndroid Build Coastguard Worker             slice
336*bb4ee6a4SAndroid Build Coastguard Worker                 .sub_slice(0, new_size as usize)
337*bb4ee6a4SAndroid Build Coastguard Worker                 .map_err(|e| io::Error::new(ErrorKind::InvalidData, e.to_string()))?
338*bb4ee6a4SAndroid Build Coastguard Worker         } else {
339*bb4ee6a4SAndroid Build Coastguard Worker             slice
340*bb4ee6a4SAndroid Build Coastguard Worker         };
341*bb4ee6a4SAndroid Build Coastguard Worker 
342*bb4ee6a4SAndroid Build Coastguard Worker         let bytes = disk
343*bb4ee6a4SAndroid Build Coastguard Worker             .file
344*bb4ee6a4SAndroid Build Coastguard Worker             .write_at_volatile(subslice, cursor_location - disk.offset)?;
345*bb4ee6a4SAndroid Build Coastguard Worker         disk.needs_fsync.store(true, Ordering::SeqCst);
346*bb4ee6a4SAndroid Build Coastguard Worker         Ok(bytes)
347*bb4ee6a4SAndroid Build Coastguard Worker     }
348*bb4ee6a4SAndroid Build Coastguard Worker }
349*bb4ee6a4SAndroid Build Coastguard Worker 
350*bb4ee6a4SAndroid Build Coastguard Worker impl AsRawDescriptors for CompositeDiskFile {
as_raw_descriptors(&self) -> Vec<RawDescriptor>351*bb4ee6a4SAndroid Build Coastguard Worker     fn as_raw_descriptors(&self) -> Vec<RawDescriptor> {
352*bb4ee6a4SAndroid Build Coastguard Worker         self.component_disks
353*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
354*bb4ee6a4SAndroid Build Coastguard Worker             .flat_map(|d| d.file.as_raw_descriptors())
355*bb4ee6a4SAndroid Build Coastguard Worker             .collect()
356*bb4ee6a4SAndroid Build Coastguard Worker     }
357*bb4ee6a4SAndroid Build Coastguard Worker }
358*bb4ee6a4SAndroid Build Coastguard Worker 
359*bb4ee6a4SAndroid Build Coastguard Worker struct AsyncComponentDiskPart {
360*bb4ee6a4SAndroid Build Coastguard Worker     file: Box<dyn AsyncDisk>,
361*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
362*bb4ee6a4SAndroid Build Coastguard Worker     length: u64,
363*bb4ee6a4SAndroid Build Coastguard Worker     needs_fsync: AtomicBool,
364*bb4ee6a4SAndroid Build Coastguard Worker }
365*bb4ee6a4SAndroid Build Coastguard Worker 
366*bb4ee6a4SAndroid Build Coastguard Worker pub struct AsyncCompositeDiskFile {
367*bb4ee6a4SAndroid Build Coastguard Worker     component_disks: Vec<AsyncComponentDiskPart>,
368*bb4ee6a4SAndroid Build Coastguard Worker }
369*bb4ee6a4SAndroid Build Coastguard Worker 
370*bb4ee6a4SAndroid Build Coastguard Worker impl DiskGetLen for AsyncCompositeDiskFile {
get_len(&self) -> io::Result<u64>371*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len(&self) -> io::Result<u64> {
372*bb4ee6a4SAndroid Build Coastguard Worker         Ok(self.length())
373*bb4ee6a4SAndroid Build Coastguard Worker     }
374*bb4ee6a4SAndroid Build Coastguard Worker }
375*bb4ee6a4SAndroid Build Coastguard Worker 
376*bb4ee6a4SAndroid Build Coastguard Worker impl FileSetLen for AsyncCompositeDiskFile {
set_len(&self, _len: u64) -> io::Result<()>377*bb4ee6a4SAndroid Build Coastguard Worker     fn set_len(&self, _len: u64) -> io::Result<()> {
378*bb4ee6a4SAndroid Build Coastguard Worker         Err(io::Error::new(ErrorKind::Other, "unsupported operation"))
379*bb4ee6a4SAndroid Build Coastguard Worker     }
380*bb4ee6a4SAndroid Build Coastguard Worker }
381*bb4ee6a4SAndroid Build Coastguard Worker 
382*bb4ee6a4SAndroid Build Coastguard Worker impl FileAllocate for AsyncCompositeDiskFile {
allocate(&self, offset: u64, length: u64) -> io::Result<()>383*bb4ee6a4SAndroid Build Coastguard Worker     fn allocate(&self, offset: u64, length: u64) -> io::Result<()> {
384*bb4ee6a4SAndroid Build Coastguard Worker         let range = offset..(offset + length);
385*bb4ee6a4SAndroid Build Coastguard Worker         let disks = self
386*bb4ee6a4SAndroid Build Coastguard Worker             .component_disks
387*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
388*bb4ee6a4SAndroid Build Coastguard Worker             .filter(|disk| ranges_overlap(&disk.range(), &range));
389*bb4ee6a4SAndroid Build Coastguard Worker         for disk in disks {
390*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(intersection) = range_intersection(&range, &disk.range()) {
391*bb4ee6a4SAndroid Build Coastguard Worker                 disk.file.allocate(
392*bb4ee6a4SAndroid Build Coastguard Worker                     intersection.start - disk.offset,
393*bb4ee6a4SAndroid Build Coastguard Worker                     intersection.end - intersection.start,
394*bb4ee6a4SAndroid Build Coastguard Worker                 )?;
395*bb4ee6a4SAndroid Build Coastguard Worker                 disk.needs_fsync.store(true, Ordering::SeqCst);
396*bb4ee6a4SAndroid Build Coastguard Worker             }
397*bb4ee6a4SAndroid Build Coastguard Worker         }
398*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
399*bb4ee6a4SAndroid Build Coastguard Worker     }
400*bb4ee6a4SAndroid Build Coastguard Worker }
401*bb4ee6a4SAndroid Build Coastguard Worker 
402*bb4ee6a4SAndroid Build Coastguard Worker impl ToAsyncDisk for CompositeDiskFile {
to_async_disk(self: Box<Self>, ex: &Executor) -> crate::Result<Box<dyn AsyncDisk>>403*bb4ee6a4SAndroid Build Coastguard Worker     fn to_async_disk(self: Box<Self>, ex: &Executor) -> crate::Result<Box<dyn AsyncDisk>> {
404*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Box::new(AsyncCompositeDiskFile {
405*bb4ee6a4SAndroid Build Coastguard Worker             component_disks: self
406*bb4ee6a4SAndroid Build Coastguard Worker                 .component_disks
407*bb4ee6a4SAndroid Build Coastguard Worker                 .into_iter()
408*bb4ee6a4SAndroid Build Coastguard Worker                 .map(|disk| -> crate::Result<_> {
409*bb4ee6a4SAndroid Build Coastguard Worker                     Ok(AsyncComponentDiskPart {
410*bb4ee6a4SAndroid Build Coastguard Worker                         file: disk.file.to_async_disk(ex)?,
411*bb4ee6a4SAndroid Build Coastguard Worker                         offset: disk.offset,
412*bb4ee6a4SAndroid Build Coastguard Worker                         length: disk.length,
413*bb4ee6a4SAndroid Build Coastguard Worker                         needs_fsync: disk.needs_fsync,
414*bb4ee6a4SAndroid Build Coastguard Worker                     })
415*bb4ee6a4SAndroid Build Coastguard Worker                 })
416*bb4ee6a4SAndroid Build Coastguard Worker                 .collect::<crate::Result<Vec<_>>>()?,
417*bb4ee6a4SAndroid Build Coastguard Worker         }))
418*bb4ee6a4SAndroid Build Coastguard Worker     }
419*bb4ee6a4SAndroid Build Coastguard Worker }
420*bb4ee6a4SAndroid Build Coastguard Worker 
421*bb4ee6a4SAndroid Build Coastguard Worker impl AsyncComponentDiskPart {
range(&self) -> Range<u64>422*bb4ee6a4SAndroid Build Coastguard Worker     fn range(&self) -> Range<u64> {
423*bb4ee6a4SAndroid Build Coastguard Worker         self.offset..(self.offset + self.length)
424*bb4ee6a4SAndroid Build Coastguard Worker     }
425*bb4ee6a4SAndroid Build Coastguard Worker 
set_needs_fsync(&self)426*bb4ee6a4SAndroid Build Coastguard Worker     fn set_needs_fsync(&self) {
427*bb4ee6a4SAndroid Build Coastguard Worker         self.needs_fsync.store(true, Ordering::SeqCst);
428*bb4ee6a4SAndroid Build Coastguard Worker     }
429*bb4ee6a4SAndroid Build Coastguard Worker }
430*bb4ee6a4SAndroid Build Coastguard Worker 
431*bb4ee6a4SAndroid Build Coastguard Worker impl AsyncCompositeDiskFile {
length(&self) -> u64432*bb4ee6a4SAndroid Build Coastguard Worker     fn length(&self) -> u64 {
433*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(disk) = self.component_disks.last() {
434*bb4ee6a4SAndroid Build Coastguard Worker             disk.offset + disk.length
435*bb4ee6a4SAndroid Build Coastguard Worker         } else {
436*bb4ee6a4SAndroid Build Coastguard Worker             0
437*bb4ee6a4SAndroid Build Coastguard Worker         }
438*bb4ee6a4SAndroid Build Coastguard Worker     }
439*bb4ee6a4SAndroid Build Coastguard Worker 
disk_at_offset(&self, offset: u64) -> io::Result<&AsyncComponentDiskPart>440*bb4ee6a4SAndroid Build Coastguard Worker     fn disk_at_offset(&self, offset: u64) -> io::Result<&AsyncComponentDiskPart> {
441*bb4ee6a4SAndroid Build Coastguard Worker         self.component_disks
442*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
443*bb4ee6a4SAndroid Build Coastguard Worker             .find(|disk| disk.range().contains(&offset))
444*bb4ee6a4SAndroid Build Coastguard Worker             .ok_or(io::Error::new(
445*bb4ee6a4SAndroid Build Coastguard Worker                 ErrorKind::InvalidData,
446*bb4ee6a4SAndroid Build Coastguard Worker                 format!("no disk at offset {}", offset),
447*bb4ee6a4SAndroid Build Coastguard Worker             ))
448*bb4ee6a4SAndroid Build Coastguard Worker     }
449*bb4ee6a4SAndroid Build Coastguard Worker 
disks_in_range<'a>(&'a self, range: &Range<u64>) -> Vec<&'a AsyncComponentDiskPart>450*bb4ee6a4SAndroid Build Coastguard Worker     fn disks_in_range<'a>(&'a self, range: &Range<u64>) -> Vec<&'a AsyncComponentDiskPart> {
451*bb4ee6a4SAndroid Build Coastguard Worker         self.component_disks
452*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
453*bb4ee6a4SAndroid Build Coastguard Worker             .filter(|disk| ranges_overlap(&disk.range(), range))
454*bb4ee6a4SAndroid Build Coastguard Worker             .collect()
455*bb4ee6a4SAndroid Build Coastguard Worker     }
456*bb4ee6a4SAndroid Build Coastguard Worker }
457*bb4ee6a4SAndroid Build Coastguard Worker 
458*bb4ee6a4SAndroid Build Coastguard Worker #[async_trait(?Send)]
459*bb4ee6a4SAndroid Build Coastguard Worker impl AsyncDisk for AsyncCompositeDiskFile {
flush(&self) -> crate::Result<()>460*bb4ee6a4SAndroid Build Coastguard Worker     async fn flush(&self) -> crate::Result<()> {
461*bb4ee6a4SAndroid Build Coastguard Worker         futures::future::try_join_all(self.component_disks.iter().map(|c| c.file.flush())).await?;
462*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
463*bb4ee6a4SAndroid Build Coastguard Worker     }
464*bb4ee6a4SAndroid Build Coastguard Worker 
fsync(&self) -> crate::Result<()>465*bb4ee6a4SAndroid Build Coastguard Worker     async fn fsync(&self) -> crate::Result<()> {
466*bb4ee6a4SAndroid Build Coastguard Worker         // TODO: handle the disks concurrently
467*bb4ee6a4SAndroid Build Coastguard Worker         for disk in self.component_disks.iter() {
468*bb4ee6a4SAndroid Build Coastguard Worker             if disk.needs_fsync.fetch_and(false, Ordering::SeqCst) {
469*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) = disk.file.fsync().await {
470*bb4ee6a4SAndroid Build Coastguard Worker                     disk.set_needs_fsync();
471*bb4ee6a4SAndroid Build Coastguard Worker                     return Err(e);
472*bb4ee6a4SAndroid Build Coastguard Worker                 }
473*bb4ee6a4SAndroid Build Coastguard Worker             }
474*bb4ee6a4SAndroid Build Coastguard Worker         }
475*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
476*bb4ee6a4SAndroid Build Coastguard Worker     }
477*bb4ee6a4SAndroid Build Coastguard Worker 
fdatasync(&self) -> crate::Result<()>478*bb4ee6a4SAndroid Build Coastguard Worker     async fn fdatasync(&self) -> crate::Result<()> {
479*bb4ee6a4SAndroid Build Coastguard Worker         // AsyncCompositeDiskFile does not implement fdatasync for now. Fallback to fsync.
480*bb4ee6a4SAndroid Build Coastguard Worker         self.fsync().await
481*bb4ee6a4SAndroid Build Coastguard Worker     }
482*bb4ee6a4SAndroid Build Coastguard Worker 
read_to_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: MemRegionIter<'a>, ) -> crate::Result<usize>483*bb4ee6a4SAndroid Build Coastguard Worker     async fn read_to_mem<'a>(
484*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
485*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
486*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
487*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: MemRegionIter<'a>,
488*bb4ee6a4SAndroid Build Coastguard Worker     ) -> crate::Result<usize> {
489*bb4ee6a4SAndroid Build Coastguard Worker         let disk = self
490*bb4ee6a4SAndroid Build Coastguard Worker             .disk_at_offset(file_offset)
491*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(crate::Error::ReadingData)?;
492*bb4ee6a4SAndroid Build Coastguard Worker         let remaining_disk = disk.offset + disk.length - file_offset;
493*bb4ee6a4SAndroid Build Coastguard Worker         disk.file
494*bb4ee6a4SAndroid Build Coastguard Worker             .read_to_mem(
495*bb4ee6a4SAndroid Build Coastguard Worker                 file_offset - disk.offset,
496*bb4ee6a4SAndroid Build Coastguard Worker                 mem,
497*bb4ee6a4SAndroid Build Coastguard Worker                 mem_offsets.take_bytes(remaining_disk.try_into().unwrap()),
498*bb4ee6a4SAndroid Build Coastguard Worker             )
499*bb4ee6a4SAndroid Build Coastguard Worker             .await
500*bb4ee6a4SAndroid Build Coastguard Worker     }
501*bb4ee6a4SAndroid Build Coastguard Worker 
write_from_mem<'a>( &'a self, file_offset: u64, mem: Arc<dyn BackingMemory + Send + Sync>, mem_offsets: MemRegionIter<'a>, ) -> crate::Result<usize>502*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_from_mem<'a>(
503*bb4ee6a4SAndroid Build Coastguard Worker         &'a self,
504*bb4ee6a4SAndroid Build Coastguard Worker         file_offset: u64,
505*bb4ee6a4SAndroid Build Coastguard Worker         mem: Arc<dyn BackingMemory + Send + Sync>,
506*bb4ee6a4SAndroid Build Coastguard Worker         mem_offsets: MemRegionIter<'a>,
507*bb4ee6a4SAndroid Build Coastguard Worker     ) -> crate::Result<usize> {
508*bb4ee6a4SAndroid Build Coastguard Worker         let disk = self
509*bb4ee6a4SAndroid Build Coastguard Worker             .disk_at_offset(file_offset)
510*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(crate::Error::ReadingData)?;
511*bb4ee6a4SAndroid Build Coastguard Worker         let remaining_disk = disk.offset + disk.length - file_offset;
512*bb4ee6a4SAndroid Build Coastguard Worker         let n = disk
513*bb4ee6a4SAndroid Build Coastguard Worker             .file
514*bb4ee6a4SAndroid Build Coastguard Worker             .write_from_mem(
515*bb4ee6a4SAndroid Build Coastguard Worker                 file_offset - disk.offset,
516*bb4ee6a4SAndroid Build Coastguard Worker                 mem,
517*bb4ee6a4SAndroid Build Coastguard Worker                 mem_offsets.take_bytes(remaining_disk.try_into().unwrap()),
518*bb4ee6a4SAndroid Build Coastguard Worker             )
519*bb4ee6a4SAndroid Build Coastguard Worker             .await?;
520*bb4ee6a4SAndroid Build Coastguard Worker         disk.set_needs_fsync();
521*bb4ee6a4SAndroid Build Coastguard Worker         Ok(n)
522*bb4ee6a4SAndroid Build Coastguard Worker     }
523*bb4ee6a4SAndroid Build Coastguard Worker 
punch_hole(&self, file_offset: u64, length: u64) -> crate::Result<()>524*bb4ee6a4SAndroid Build Coastguard Worker     async fn punch_hole(&self, file_offset: u64, length: u64) -> crate::Result<()> {
525*bb4ee6a4SAndroid Build Coastguard Worker         let range = file_offset..(file_offset + length);
526*bb4ee6a4SAndroid Build Coastguard Worker         let disks = self.disks_in_range(&range);
527*bb4ee6a4SAndroid Build Coastguard Worker         for disk in disks {
528*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(intersection) = range_intersection(&range, &disk.range()) {
529*bb4ee6a4SAndroid Build Coastguard Worker                 disk.file
530*bb4ee6a4SAndroid Build Coastguard Worker                     .punch_hole(
531*bb4ee6a4SAndroid Build Coastguard Worker                         intersection.start - disk.offset,
532*bb4ee6a4SAndroid Build Coastguard Worker                         intersection.end - intersection.start,
533*bb4ee6a4SAndroid Build Coastguard Worker                     )
534*bb4ee6a4SAndroid Build Coastguard Worker                     .await?;
535*bb4ee6a4SAndroid Build Coastguard Worker                 disk.set_needs_fsync();
536*bb4ee6a4SAndroid Build Coastguard Worker             }
537*bb4ee6a4SAndroid Build Coastguard Worker         }
538*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
539*bb4ee6a4SAndroid Build Coastguard Worker     }
540*bb4ee6a4SAndroid Build Coastguard Worker 
write_zeroes_at(&self, file_offset: u64, length: u64) -> crate::Result<()>541*bb4ee6a4SAndroid Build Coastguard Worker     async fn write_zeroes_at(&self, file_offset: u64, length: u64) -> crate::Result<()> {
542*bb4ee6a4SAndroid Build Coastguard Worker         let range = file_offset..(file_offset + length);
543*bb4ee6a4SAndroid Build Coastguard Worker         let disks = self.disks_in_range(&range);
544*bb4ee6a4SAndroid Build Coastguard Worker         for disk in disks {
545*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(intersection) = range_intersection(&range, &disk.range()) {
546*bb4ee6a4SAndroid Build Coastguard Worker                 disk.file
547*bb4ee6a4SAndroid Build Coastguard Worker                     .write_zeroes_at(
548*bb4ee6a4SAndroid Build Coastguard Worker                         intersection.start - disk.offset,
549*bb4ee6a4SAndroid Build Coastguard Worker                         intersection.end - intersection.start,
550*bb4ee6a4SAndroid Build Coastguard Worker                     )
551*bb4ee6a4SAndroid Build Coastguard Worker                     .await?;
552*bb4ee6a4SAndroid Build Coastguard Worker                 disk.set_needs_fsync();
553*bb4ee6a4SAndroid Build Coastguard Worker             }
554*bb4ee6a4SAndroid Build Coastguard Worker         }
555*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
556*bb4ee6a4SAndroid Build Coastguard Worker     }
557*bb4ee6a4SAndroid Build Coastguard Worker }
558*bb4ee6a4SAndroid Build Coastguard Worker 
559*bb4ee6a4SAndroid Build Coastguard Worker /// Information about a partition to create.
560*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug, Eq, PartialEq)]
561*bb4ee6a4SAndroid Build Coastguard Worker pub struct PartitionInfo {
562*bb4ee6a4SAndroid Build Coastguard Worker     pub label: String,
563*bb4ee6a4SAndroid Build Coastguard Worker     pub path: PathBuf,
564*bb4ee6a4SAndroid Build Coastguard Worker     pub partition_type: ImagePartitionType,
565*bb4ee6a4SAndroid Build Coastguard Worker     pub writable: bool,
566*bb4ee6a4SAndroid Build Coastguard Worker     pub size: u64,
567*bb4ee6a4SAndroid Build Coastguard Worker     pub part_guid: Option<Uuid>,
568*bb4ee6a4SAndroid Build Coastguard Worker }
569*bb4ee6a4SAndroid Build Coastguard Worker 
570*bb4ee6a4SAndroid Build Coastguard Worker /// Round `val` up to the next multiple of 2**`align_log`.
align_to_power_of_2(val: u64, align_log: u8) -> u64571*bb4ee6a4SAndroid Build Coastguard Worker fn align_to_power_of_2(val: u64, align_log: u8) -> u64 {
572*bb4ee6a4SAndroid Build Coastguard Worker     let align = 1 << align_log;
573*bb4ee6a4SAndroid Build Coastguard Worker     ((val + (align - 1)) / align) * align
574*bb4ee6a4SAndroid Build Coastguard Worker }
575*bb4ee6a4SAndroid Build Coastguard Worker 
576*bb4ee6a4SAndroid Build Coastguard Worker impl PartitionInfo {
aligned_size(&self) -> u64577*bb4ee6a4SAndroid Build Coastguard Worker     fn aligned_size(&self) -> u64 {
578*bb4ee6a4SAndroid Build Coastguard Worker         align_to_power_of_2(self.size, PARTITION_SIZE_SHIFT)
579*bb4ee6a4SAndroid Build Coastguard Worker     }
580*bb4ee6a4SAndroid Build Coastguard Worker }
581*bb4ee6a4SAndroid Build Coastguard Worker 
582*bb4ee6a4SAndroid Build Coastguard Worker /// The type of partition.
583*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Copy, Clone, Debug, Eq, PartialEq)]
584*bb4ee6a4SAndroid Build Coastguard Worker pub enum ImagePartitionType {
585*bb4ee6a4SAndroid Build Coastguard Worker     LinuxFilesystem,
586*bb4ee6a4SAndroid Build Coastguard Worker     EfiSystemPartition,
587*bb4ee6a4SAndroid Build Coastguard Worker }
588*bb4ee6a4SAndroid Build Coastguard Worker 
589*bb4ee6a4SAndroid Build Coastguard Worker impl ImagePartitionType {
guid(self) -> Uuid590*bb4ee6a4SAndroid Build Coastguard Worker     fn guid(self) -> Uuid {
591*bb4ee6a4SAndroid Build Coastguard Worker         match self {
592*bb4ee6a4SAndroid Build Coastguard Worker             Self::LinuxFilesystem => LINUX_FILESYSTEM_GUID,
593*bb4ee6a4SAndroid Build Coastguard Worker             Self::EfiSystemPartition => EFI_SYSTEM_PARTITION_GUID,
594*bb4ee6a4SAndroid Build Coastguard Worker         }
595*bb4ee6a4SAndroid Build Coastguard Worker     }
596*bb4ee6a4SAndroid Build Coastguard Worker }
597*bb4ee6a4SAndroid Build Coastguard Worker 
598*bb4ee6a4SAndroid Build Coastguard Worker /// Write protective MBR and primary GPT table.
write_beginning( file: &mut impl Write, disk_guid: Uuid, partitions: &[u8], partition_entries_crc32: u32, secondary_table_offset: u64, disk_size: u64, ) -> Result<()>599*bb4ee6a4SAndroid Build Coastguard Worker fn write_beginning(
600*bb4ee6a4SAndroid Build Coastguard Worker     file: &mut impl Write,
601*bb4ee6a4SAndroid Build Coastguard Worker     disk_guid: Uuid,
602*bb4ee6a4SAndroid Build Coastguard Worker     partitions: &[u8],
603*bb4ee6a4SAndroid Build Coastguard Worker     partition_entries_crc32: u32,
604*bb4ee6a4SAndroid Build Coastguard Worker     secondary_table_offset: u64,
605*bb4ee6a4SAndroid Build Coastguard Worker     disk_size: u64,
606*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
607*bb4ee6a4SAndroid Build Coastguard Worker     // Write the protective MBR to the first sector.
608*bb4ee6a4SAndroid Build Coastguard Worker     write_protective_mbr(file, disk_size)?;
609*bb4ee6a4SAndroid Build Coastguard Worker 
610*bb4ee6a4SAndroid Build Coastguard Worker     // Write the GPT header, and pad out to the end of the sector.
611*bb4ee6a4SAndroid Build Coastguard Worker     write_gpt_header(
612*bb4ee6a4SAndroid Build Coastguard Worker         file,
613*bb4ee6a4SAndroid Build Coastguard Worker         disk_guid,
614*bb4ee6a4SAndroid Build Coastguard Worker         partition_entries_crc32,
615*bb4ee6a4SAndroid Build Coastguard Worker         secondary_table_offset,
616*bb4ee6a4SAndroid Build Coastguard Worker         false,
617*bb4ee6a4SAndroid Build Coastguard Worker     )?;
618*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(&[0; HEADER_PADDING_LENGTH])
619*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteHeader)?;
620*bb4ee6a4SAndroid Build Coastguard Worker 
621*bb4ee6a4SAndroid Build Coastguard Worker     // Write partition entries, including unused ones.
622*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(partitions).map_err(Error::WriteHeader)?;
623*bb4ee6a4SAndroid Build Coastguard Worker 
624*bb4ee6a4SAndroid Build Coastguard Worker     // Write zeroes to align the first partition appropriately.
625*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(&[0; PARTITION_ALIGNMENT_SIZE])
626*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteHeader)?;
627*bb4ee6a4SAndroid Build Coastguard Worker 
628*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
629*bb4ee6a4SAndroid Build Coastguard Worker }
630*bb4ee6a4SAndroid Build Coastguard Worker 
631*bb4ee6a4SAndroid Build Coastguard Worker /// Write secondary GPT table.
write_end( file: &mut impl Write, disk_guid: Uuid, partitions: &[u8], partition_entries_crc32: u32, secondary_table_offset: u64, disk_size: u64, ) -> Result<()>632*bb4ee6a4SAndroid Build Coastguard Worker fn write_end(
633*bb4ee6a4SAndroid Build Coastguard Worker     file: &mut impl Write,
634*bb4ee6a4SAndroid Build Coastguard Worker     disk_guid: Uuid,
635*bb4ee6a4SAndroid Build Coastguard Worker     partitions: &[u8],
636*bb4ee6a4SAndroid Build Coastguard Worker     partition_entries_crc32: u32,
637*bb4ee6a4SAndroid Build Coastguard Worker     secondary_table_offset: u64,
638*bb4ee6a4SAndroid Build Coastguard Worker     disk_size: u64,
639*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
640*bb4ee6a4SAndroid Build Coastguard Worker     // Write partition entries, including unused ones.
641*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(partitions).map_err(Error::WriteHeader)?;
642*bb4ee6a4SAndroid Build Coastguard Worker 
643*bb4ee6a4SAndroid Build Coastguard Worker     // Write the GPT header, and pad out to the end of the sector.
644*bb4ee6a4SAndroid Build Coastguard Worker     write_gpt_header(
645*bb4ee6a4SAndroid Build Coastguard Worker         file,
646*bb4ee6a4SAndroid Build Coastguard Worker         disk_guid,
647*bb4ee6a4SAndroid Build Coastguard Worker         partition_entries_crc32,
648*bb4ee6a4SAndroid Build Coastguard Worker         secondary_table_offset,
649*bb4ee6a4SAndroid Build Coastguard Worker         true,
650*bb4ee6a4SAndroid Build Coastguard Worker     )?;
651*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(&[0; HEADER_PADDING_LENGTH])
652*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteHeader)?;
653*bb4ee6a4SAndroid Build Coastguard Worker 
654*bb4ee6a4SAndroid Build Coastguard Worker     // Pad out to the aligned disk size.
655*bb4ee6a4SAndroid Build Coastguard Worker     let used_disk_size = secondary_table_offset + GPT_END_SIZE;
656*bb4ee6a4SAndroid Build Coastguard Worker     let padding = disk_size - used_disk_size;
657*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(&vec![0; padding as usize])
658*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteHeader)?;
659*bb4ee6a4SAndroid Build Coastguard Worker 
660*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
661*bb4ee6a4SAndroid Build Coastguard Worker }
662*bb4ee6a4SAndroid Build Coastguard Worker 
663*bb4ee6a4SAndroid Build Coastguard Worker /// Create the `GptPartitionEntry` for the given partition.
create_gpt_entry(partition: &PartitionInfo, offset: u64) -> GptPartitionEntry664*bb4ee6a4SAndroid Build Coastguard Worker fn create_gpt_entry(partition: &PartitionInfo, offset: u64) -> GptPartitionEntry {
665*bb4ee6a4SAndroid Build Coastguard Worker     let mut partition_name: Vec<u16> = partition.label.encode_utf16().collect();
666*bb4ee6a4SAndroid Build Coastguard Worker     partition_name.resize(36, 0);
667*bb4ee6a4SAndroid Build Coastguard Worker 
668*bb4ee6a4SAndroid Build Coastguard Worker     GptPartitionEntry {
669*bb4ee6a4SAndroid Build Coastguard Worker         partition_type_guid: partition.partition_type.guid(),
670*bb4ee6a4SAndroid Build Coastguard Worker         unique_partition_guid: partition.part_guid.unwrap_or(Uuid::new_v4()),
671*bb4ee6a4SAndroid Build Coastguard Worker         first_lba: offset / SECTOR_SIZE,
672*bb4ee6a4SAndroid Build Coastguard Worker         last_lba: (offset + partition.aligned_size()) / SECTOR_SIZE - 1,
673*bb4ee6a4SAndroid Build Coastguard Worker         attributes: 0,
674*bb4ee6a4SAndroid Build Coastguard Worker         partition_name: partition_name.try_into().unwrap(),
675*bb4ee6a4SAndroid Build Coastguard Worker     }
676*bb4ee6a4SAndroid Build Coastguard Worker }
677*bb4ee6a4SAndroid Build Coastguard Worker 
678*bb4ee6a4SAndroid Build Coastguard Worker /// Create one or more `ComponentDisk` proto messages for the given partition.
create_component_disks( partition: &PartitionInfo, offset: u64, zero_filler_path: &str, ) -> Result<Vec<ComponentDisk>>679*bb4ee6a4SAndroid Build Coastguard Worker fn create_component_disks(
680*bb4ee6a4SAndroid Build Coastguard Worker     partition: &PartitionInfo,
681*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
682*bb4ee6a4SAndroid Build Coastguard Worker     zero_filler_path: &str,
683*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Vec<ComponentDisk>> {
684*bb4ee6a4SAndroid Build Coastguard Worker     let aligned_size = partition.aligned_size();
685*bb4ee6a4SAndroid Build Coastguard Worker 
686*bb4ee6a4SAndroid Build Coastguard Worker     let mut component_disks = vec![ComponentDisk {
687*bb4ee6a4SAndroid Build Coastguard Worker         offset,
688*bb4ee6a4SAndroid Build Coastguard Worker         file_path: partition
689*bb4ee6a4SAndroid Build Coastguard Worker             .path
690*bb4ee6a4SAndroid Build Coastguard Worker             .to_str()
691*bb4ee6a4SAndroid Build Coastguard Worker             .ok_or_else(|| Error::InvalidPath(partition.path.to_owned()))?
692*bb4ee6a4SAndroid Build Coastguard Worker             .to_string(),
693*bb4ee6a4SAndroid Build Coastguard Worker         read_write_capability: if partition.writable {
694*bb4ee6a4SAndroid Build Coastguard Worker             ReadWriteCapability::READ_WRITE.into()
695*bb4ee6a4SAndroid Build Coastguard Worker         } else {
696*bb4ee6a4SAndroid Build Coastguard Worker             ReadWriteCapability::READ_ONLY.into()
697*bb4ee6a4SAndroid Build Coastguard Worker         },
698*bb4ee6a4SAndroid Build Coastguard Worker         ..ComponentDisk::new()
699*bb4ee6a4SAndroid Build Coastguard Worker     }];
700*bb4ee6a4SAndroid Build Coastguard Worker 
701*bb4ee6a4SAndroid Build Coastguard Worker     if partition.size != aligned_size {
702*bb4ee6a4SAndroid Build Coastguard Worker         if partition.writable {
703*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::UnalignedReadWrite(partition.to_owned()));
704*bb4ee6a4SAndroid Build Coastguard Worker         } else {
705*bb4ee6a4SAndroid Build Coastguard Worker             // Fill in the gap by reusing the zero filler file, because we know it is always bigger
706*bb4ee6a4SAndroid Build Coastguard Worker             // than the alignment size. Its size is 1 << PARTITION_SIZE_SHIFT (4k).
707*bb4ee6a4SAndroid Build Coastguard Worker             component_disks.push(ComponentDisk {
708*bb4ee6a4SAndroid Build Coastguard Worker                 offset: offset + partition.size,
709*bb4ee6a4SAndroid Build Coastguard Worker                 file_path: zero_filler_path.to_owned(),
710*bb4ee6a4SAndroid Build Coastguard Worker                 read_write_capability: ReadWriteCapability::READ_ONLY.into(),
711*bb4ee6a4SAndroid Build Coastguard Worker                 ..ComponentDisk::new()
712*bb4ee6a4SAndroid Build Coastguard Worker             });
713*bb4ee6a4SAndroid Build Coastguard Worker         }
714*bb4ee6a4SAndroid Build Coastguard Worker     }
715*bb4ee6a4SAndroid Build Coastguard Worker 
716*bb4ee6a4SAndroid Build Coastguard Worker     Ok(component_disks)
717*bb4ee6a4SAndroid Build Coastguard Worker }
718*bb4ee6a4SAndroid Build Coastguard Worker 
719*bb4ee6a4SAndroid Build Coastguard Worker /// Create a new composite disk image containing the given partitions, and write it out to the given
720*bb4ee6a4SAndroid Build Coastguard Worker /// files.
create_composite_disk( partitions: &[PartitionInfo], zero_filler_path: &Path, header_path: &Path, header_file: &mut File, footer_path: &Path, footer_file: &mut File, output_composite: &mut File, ) -> Result<()>721*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_composite_disk(
722*bb4ee6a4SAndroid Build Coastguard Worker     partitions: &[PartitionInfo],
723*bb4ee6a4SAndroid Build Coastguard Worker     zero_filler_path: &Path,
724*bb4ee6a4SAndroid Build Coastguard Worker     header_path: &Path,
725*bb4ee6a4SAndroid Build Coastguard Worker     header_file: &mut File,
726*bb4ee6a4SAndroid Build Coastguard Worker     footer_path: &Path,
727*bb4ee6a4SAndroid Build Coastguard Worker     footer_file: &mut File,
728*bb4ee6a4SAndroid Build Coastguard Worker     output_composite: &mut File,
729*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<()> {
730*bb4ee6a4SAndroid Build Coastguard Worker     let zero_filler_path = zero_filler_path
731*bb4ee6a4SAndroid Build Coastguard Worker         .to_str()
732*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or_else(|| Error::InvalidPath(zero_filler_path.to_owned()))?
733*bb4ee6a4SAndroid Build Coastguard Worker         .to_string();
734*bb4ee6a4SAndroid Build Coastguard Worker     let header_path = header_path
735*bb4ee6a4SAndroid Build Coastguard Worker         .to_str()
736*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or_else(|| Error::InvalidPath(header_path.to_owned()))?
737*bb4ee6a4SAndroid Build Coastguard Worker         .to_string();
738*bb4ee6a4SAndroid Build Coastguard Worker     let footer_path = footer_path
739*bb4ee6a4SAndroid Build Coastguard Worker         .to_str()
740*bb4ee6a4SAndroid Build Coastguard Worker         .ok_or_else(|| Error::InvalidPath(footer_path.to_owned()))?
741*bb4ee6a4SAndroid Build Coastguard Worker         .to_string();
742*bb4ee6a4SAndroid Build Coastguard Worker 
743*bb4ee6a4SAndroid Build Coastguard Worker     let mut composite_proto = CompositeDisk::new();
744*bb4ee6a4SAndroid Build Coastguard Worker     composite_proto.version = COMPOSITE_DISK_VERSION;
745*bb4ee6a4SAndroid Build Coastguard Worker     composite_proto.component_disks.push(ComponentDisk {
746*bb4ee6a4SAndroid Build Coastguard Worker         file_path: header_path,
747*bb4ee6a4SAndroid Build Coastguard Worker         offset: 0,
748*bb4ee6a4SAndroid Build Coastguard Worker         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
749*bb4ee6a4SAndroid Build Coastguard Worker         ..ComponentDisk::new()
750*bb4ee6a4SAndroid Build Coastguard Worker     });
751*bb4ee6a4SAndroid Build Coastguard Worker 
752*bb4ee6a4SAndroid Build Coastguard Worker     // Write partitions to a temporary buffer so that we can calculate the CRC, and construct the
753*bb4ee6a4SAndroid Build Coastguard Worker     // ComponentDisk proto messages at the same time.
754*bb4ee6a4SAndroid Build Coastguard Worker     let mut partitions_buffer =
755*bb4ee6a4SAndroid Build Coastguard Worker         [0u8; GPT_NUM_PARTITIONS as usize * GPT_PARTITION_ENTRY_SIZE as usize];
756*bb4ee6a4SAndroid Build Coastguard Worker     let mut writer: &mut [u8] = &mut partitions_buffer;
757*bb4ee6a4SAndroid Build Coastguard Worker     let mut next_disk_offset = GPT_BEGINNING_SIZE;
758*bb4ee6a4SAndroid Build Coastguard Worker     let mut labels = HashSet::with_capacity(partitions.len());
759*bb4ee6a4SAndroid Build Coastguard Worker     for partition in partitions {
760*bb4ee6a4SAndroid Build Coastguard Worker         let gpt_entry = create_gpt_entry(partition, next_disk_offset);
761*bb4ee6a4SAndroid Build Coastguard Worker         if !labels.insert(gpt_entry.partition_name) {
762*bb4ee6a4SAndroid Build Coastguard Worker             return Err(Error::DuplicatePartitionLabel(partition.label.clone()));
763*bb4ee6a4SAndroid Build Coastguard Worker         }
764*bb4ee6a4SAndroid Build Coastguard Worker         gpt_entry.write_bytes(&mut writer)?;
765*bb4ee6a4SAndroid Build Coastguard Worker 
766*bb4ee6a4SAndroid Build Coastguard Worker         for component_disk in
767*bb4ee6a4SAndroid Build Coastguard Worker             create_component_disks(partition, next_disk_offset, &zero_filler_path)?
768*bb4ee6a4SAndroid Build Coastguard Worker         {
769*bb4ee6a4SAndroid Build Coastguard Worker             composite_proto.component_disks.push(component_disk);
770*bb4ee6a4SAndroid Build Coastguard Worker         }
771*bb4ee6a4SAndroid Build Coastguard Worker 
772*bb4ee6a4SAndroid Build Coastguard Worker         next_disk_offset += partition.aligned_size();
773*bb4ee6a4SAndroid Build Coastguard Worker     }
774*bb4ee6a4SAndroid Build Coastguard Worker     let secondary_table_offset = next_disk_offset;
775*bb4ee6a4SAndroid Build Coastguard Worker     let disk_size = align_to_power_of_2(secondary_table_offset + GPT_END_SIZE, DISK_SIZE_SHIFT);
776*bb4ee6a4SAndroid Build Coastguard Worker 
777*bb4ee6a4SAndroid Build Coastguard Worker     composite_proto.component_disks.push(ComponentDisk {
778*bb4ee6a4SAndroid Build Coastguard Worker         file_path: footer_path,
779*bb4ee6a4SAndroid Build Coastguard Worker         offset: secondary_table_offset,
780*bb4ee6a4SAndroid Build Coastguard Worker         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
781*bb4ee6a4SAndroid Build Coastguard Worker         ..ComponentDisk::new()
782*bb4ee6a4SAndroid Build Coastguard Worker     });
783*bb4ee6a4SAndroid Build Coastguard Worker 
784*bb4ee6a4SAndroid Build Coastguard Worker     // Calculate CRC32 of partition entries.
785*bb4ee6a4SAndroid Build Coastguard Worker     let mut hasher = Hasher::new();
786*bb4ee6a4SAndroid Build Coastguard Worker     hasher.update(&partitions_buffer);
787*bb4ee6a4SAndroid Build Coastguard Worker     let partition_entries_crc32 = hasher.finalize();
788*bb4ee6a4SAndroid Build Coastguard Worker 
789*bb4ee6a4SAndroid Build Coastguard Worker     let disk_guid = Uuid::new_v4();
790*bb4ee6a4SAndroid Build Coastguard Worker     write_beginning(
791*bb4ee6a4SAndroid Build Coastguard Worker         header_file,
792*bb4ee6a4SAndroid Build Coastguard Worker         disk_guid,
793*bb4ee6a4SAndroid Build Coastguard Worker         &partitions_buffer,
794*bb4ee6a4SAndroid Build Coastguard Worker         partition_entries_crc32,
795*bb4ee6a4SAndroid Build Coastguard Worker         secondary_table_offset,
796*bb4ee6a4SAndroid Build Coastguard Worker         disk_size,
797*bb4ee6a4SAndroid Build Coastguard Worker     )?;
798*bb4ee6a4SAndroid Build Coastguard Worker     write_end(
799*bb4ee6a4SAndroid Build Coastguard Worker         footer_file,
800*bb4ee6a4SAndroid Build Coastguard Worker         disk_guid,
801*bb4ee6a4SAndroid Build Coastguard Worker         &partitions_buffer,
802*bb4ee6a4SAndroid Build Coastguard Worker         partition_entries_crc32,
803*bb4ee6a4SAndroid Build Coastguard Worker         secondary_table_offset,
804*bb4ee6a4SAndroid Build Coastguard Worker         disk_size,
805*bb4ee6a4SAndroid Build Coastguard Worker     )?;
806*bb4ee6a4SAndroid Build Coastguard Worker 
807*bb4ee6a4SAndroid Build Coastguard Worker     composite_proto.length = disk_size;
808*bb4ee6a4SAndroid Build Coastguard Worker     output_composite
809*bb4ee6a4SAndroid Build Coastguard Worker         .write_all(CDISK_MAGIC.as_bytes())
810*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteHeader)?;
811*bb4ee6a4SAndroid Build Coastguard Worker     composite_proto
812*bb4ee6a4SAndroid Build Coastguard Worker         .write_to_writer(output_composite)
813*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteProto)?;
814*bb4ee6a4SAndroid Build Coastguard Worker 
815*bb4ee6a4SAndroid Build Coastguard Worker     Ok(())
816*bb4ee6a4SAndroid Build Coastguard Worker }
817*bb4ee6a4SAndroid Build Coastguard Worker 
818*bb4ee6a4SAndroid Build Coastguard Worker /// Create a zero filler file which can be used to fill the gaps between partition files.
819*bb4ee6a4SAndroid Build Coastguard Worker /// The filler is sized to be big enough to fill the gaps. (1 << PARTITION_SIZE_SHIFT)
create_zero_filler<P: AsRef<Path>>(zero_filler_path: P) -> Result<()>820*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_zero_filler<P: AsRef<Path>>(zero_filler_path: P) -> Result<()> {
821*bb4ee6a4SAndroid Build Coastguard Worker     let f = OpenOptions::new()
822*bb4ee6a4SAndroid Build Coastguard Worker         .create(true)
823*bb4ee6a4SAndroid Build Coastguard Worker         .read(true)
824*bb4ee6a4SAndroid Build Coastguard Worker         .write(true)
825*bb4ee6a4SAndroid Build Coastguard Worker         .truncate(true)
826*bb4ee6a4SAndroid Build Coastguard Worker         .open(zero_filler_path.as_ref())
827*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteZeroFiller)?;
828*bb4ee6a4SAndroid Build Coastguard Worker     f.set_len(1 << PARTITION_SIZE_SHIFT)
829*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(Error::WriteZeroFiller)
830*bb4ee6a4SAndroid Build Coastguard Worker }
831*bb4ee6a4SAndroid Build Coastguard Worker 
832*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
833*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
834*bb4ee6a4SAndroid Build Coastguard Worker     use std::fs::OpenOptions;
835*bb4ee6a4SAndroid Build Coastguard Worker     use std::io::Write;
836*bb4ee6a4SAndroid Build Coastguard Worker     use std::matches;
837*bb4ee6a4SAndroid Build Coastguard Worker 
838*bb4ee6a4SAndroid Build Coastguard Worker     use base::AsRawDescriptor;
839*bb4ee6a4SAndroid Build Coastguard Worker     use tempfile::tempfile;
840*bb4ee6a4SAndroid Build Coastguard Worker 
841*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
842*bb4ee6a4SAndroid Build Coastguard Worker 
new_from_components(disks: Vec<ComponentDiskPart>) -> Result<CompositeDiskFile>843*bb4ee6a4SAndroid Build Coastguard Worker     fn new_from_components(disks: Vec<ComponentDiskPart>) -> Result<CompositeDiskFile> {
844*bb4ee6a4SAndroid Build Coastguard Worker         CompositeDiskFile::new(disks, tempfile().unwrap())
845*bb4ee6a4SAndroid Build Coastguard Worker     }
846*bb4ee6a4SAndroid Build Coastguard Worker 
847*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
block_duplicate_offset_disks()848*bb4ee6a4SAndroid Build Coastguard Worker     fn block_duplicate_offset_disks() {
849*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
850*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
851*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
852*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
853*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
854*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
855*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
856*bb4ee6a4SAndroid Build Coastguard Worker         };
857*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
858*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
859*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
860*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
861*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
862*bb4ee6a4SAndroid Build Coastguard Worker         };
863*bb4ee6a4SAndroid Build Coastguard Worker         assert!(new_from_components(vec![disk_part1, disk_part2]).is_err());
864*bb4ee6a4SAndroid Build Coastguard Worker     }
865*bb4ee6a4SAndroid Build Coastguard Worker 
866*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
get_len()867*bb4ee6a4SAndroid Build Coastguard Worker     fn get_len() {
868*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
869*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
870*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
871*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
872*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
873*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
874*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
875*bb4ee6a4SAndroid Build Coastguard Worker         };
876*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
877*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
878*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
879*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
880*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
881*bb4ee6a4SAndroid Build Coastguard Worker         };
882*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2]).unwrap();
883*bb4ee6a4SAndroid Build Coastguard Worker         let len = composite.get_len().unwrap();
884*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(len, 200);
885*bb4ee6a4SAndroid Build Coastguard Worker     }
886*bb4ee6a4SAndroid Build Coastguard Worker 
887*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_get_len()888*bb4ee6a4SAndroid Build Coastguard Worker     fn async_get_len() {
889*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
890*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
891*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
892*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
893*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
894*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
895*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
896*bb4ee6a4SAndroid Build Coastguard Worker         };
897*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
898*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
899*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
900*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
901*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
902*bb4ee6a4SAndroid Build Coastguard Worker         };
903*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2]).unwrap();
904*bb4ee6a4SAndroid Build Coastguard Worker 
905*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
906*bb4ee6a4SAndroid Build Coastguard Worker         let composite = Box::new(composite).to_async_disk(&ex).unwrap();
907*bb4ee6a4SAndroid Build Coastguard Worker         let len = composite.get_len().unwrap();
908*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(len, 200);
909*bb4ee6a4SAndroid Build Coastguard Worker     }
910*bb4ee6a4SAndroid Build Coastguard Worker 
911*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
single_file_passthrough()912*bb4ee6a4SAndroid Build Coastguard Worker     fn single_file_passthrough() {
913*bb4ee6a4SAndroid Build Coastguard Worker         let file = tempfile().unwrap();
914*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part = ComponentDiskPart {
915*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file),
916*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
917*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
918*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
919*bb4ee6a4SAndroid Build Coastguard Worker         };
920*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part]).unwrap();
921*bb4ee6a4SAndroid Build Coastguard Worker         let mut input_memory = [55u8; 5];
922*bb4ee6a4SAndroid Build Coastguard Worker         let input_volatile_memory = VolatileSlice::new(&mut input_memory[..]);
923*bb4ee6a4SAndroid Build Coastguard Worker         composite
924*bb4ee6a4SAndroid Build Coastguard Worker             .write_all_at_volatile(input_volatile_memory, 0)
925*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
926*bb4ee6a4SAndroid Build Coastguard Worker         let mut output_memory = [0u8; 5];
927*bb4ee6a4SAndroid Build Coastguard Worker         let output_volatile_memory = VolatileSlice::new(&mut output_memory[..]);
928*bb4ee6a4SAndroid Build Coastguard Worker         composite
929*bb4ee6a4SAndroid Build Coastguard Worker             .read_exact_at_volatile(output_volatile_memory, 0)
930*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
931*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(input_memory, output_memory);
932*bb4ee6a4SAndroid Build Coastguard Worker     }
933*bb4ee6a4SAndroid Build Coastguard Worker 
934*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_single_file_passthrough()935*bb4ee6a4SAndroid Build Coastguard Worker     fn async_single_file_passthrough() {
936*bb4ee6a4SAndroid Build Coastguard Worker         let file = tempfile().unwrap();
937*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part = ComponentDiskPart {
938*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file),
939*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
940*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
941*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
942*bb4ee6a4SAndroid Build Coastguard Worker         };
943*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part]).unwrap();
944*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
945*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(async {
946*bb4ee6a4SAndroid Build Coastguard Worker             let composite = Box::new(composite).to_async_disk(&ex).unwrap();
947*bb4ee6a4SAndroid Build Coastguard Worker             let expected = [55u8; 5];
948*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
949*bb4ee6a4SAndroid Build Coastguard Worker                 composite.write_double_buffered(0, &expected).await.unwrap(),
950*bb4ee6a4SAndroid Build Coastguard Worker                 5
951*bb4ee6a4SAndroid Build Coastguard Worker             );
952*bb4ee6a4SAndroid Build Coastguard Worker             let mut buf = [0u8; 5];
953*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
954*bb4ee6a4SAndroid Build Coastguard Worker                 composite
955*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(0, &mut buf[..])
956*bb4ee6a4SAndroid Build Coastguard Worker                     .await
957*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
958*bb4ee6a4SAndroid Build Coastguard Worker                 5
959*bb4ee6a4SAndroid Build Coastguard Worker             );
960*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(buf, expected);
961*bb4ee6a4SAndroid Build Coastguard Worker         })
962*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
963*bb4ee6a4SAndroid Build Coastguard Worker     }
964*bb4ee6a4SAndroid Build Coastguard Worker 
965*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
triple_file_descriptors()966*bb4ee6a4SAndroid Build Coastguard Worker     fn triple_file_descriptors() {
967*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
968*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
969*bb4ee6a4SAndroid Build Coastguard Worker         let file3 = tempfile().unwrap();
970*bb4ee6a4SAndroid Build Coastguard Worker         let mut in_descriptors = vec![
971*bb4ee6a4SAndroid Build Coastguard Worker             file1.as_raw_descriptor(),
972*bb4ee6a4SAndroid Build Coastguard Worker             file2.as_raw_descriptor(),
973*bb4ee6a4SAndroid Build Coastguard Worker             file3.as_raw_descriptor(),
974*bb4ee6a4SAndroid Build Coastguard Worker         ];
975*bb4ee6a4SAndroid Build Coastguard Worker         in_descriptors.sort_unstable();
976*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
977*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
978*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
979*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
980*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
981*bb4ee6a4SAndroid Build Coastguard Worker         };
982*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
983*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
984*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
985*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
986*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
987*bb4ee6a4SAndroid Build Coastguard Worker         };
988*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part3 = ComponentDiskPart {
989*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file3),
990*bb4ee6a4SAndroid Build Coastguard Worker             offset: 200,
991*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
992*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
993*bb4ee6a4SAndroid Build Coastguard Worker         };
994*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2, disk_part3]).unwrap();
995*bb4ee6a4SAndroid Build Coastguard Worker         let mut out_descriptors = composite.as_raw_descriptors();
996*bb4ee6a4SAndroid Build Coastguard Worker         out_descriptors.sort_unstable();
997*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(in_descriptors, out_descriptors);
998*bb4ee6a4SAndroid Build Coastguard Worker     }
999*bb4ee6a4SAndroid Build Coastguard Worker 
1000*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
triple_file_passthrough()1001*bb4ee6a4SAndroid Build Coastguard Worker     fn triple_file_passthrough() {
1002*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
1003*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
1004*bb4ee6a4SAndroid Build Coastguard Worker         let file3 = tempfile().unwrap();
1005*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
1006*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
1007*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
1008*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1009*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1010*bb4ee6a4SAndroid Build Coastguard Worker         };
1011*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
1012*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
1013*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
1014*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1015*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1016*bb4ee6a4SAndroid Build Coastguard Worker         };
1017*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part3 = ComponentDiskPart {
1018*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file3),
1019*bb4ee6a4SAndroid Build Coastguard Worker             offset: 200,
1020*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1021*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1022*bb4ee6a4SAndroid Build Coastguard Worker         };
1023*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2, disk_part3]).unwrap();
1024*bb4ee6a4SAndroid Build Coastguard Worker         let mut input_memory = [55u8; 200];
1025*bb4ee6a4SAndroid Build Coastguard Worker         let input_volatile_memory = VolatileSlice::new(&mut input_memory[..]);
1026*bb4ee6a4SAndroid Build Coastguard Worker         composite
1027*bb4ee6a4SAndroid Build Coastguard Worker             .write_all_at_volatile(input_volatile_memory, 50)
1028*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
1029*bb4ee6a4SAndroid Build Coastguard Worker         let mut output_memory = [0u8; 200];
1030*bb4ee6a4SAndroid Build Coastguard Worker         let output_volatile_memory = VolatileSlice::new(&mut output_memory[..]);
1031*bb4ee6a4SAndroid Build Coastguard Worker         composite
1032*bb4ee6a4SAndroid Build Coastguard Worker             .read_exact_at_volatile(output_volatile_memory, 50)
1033*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
1034*bb4ee6a4SAndroid Build Coastguard Worker         assert!(input_memory.iter().eq(output_memory.iter()));
1035*bb4ee6a4SAndroid Build Coastguard Worker     }
1036*bb4ee6a4SAndroid Build Coastguard Worker 
1037*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_triple_file_passthrough()1038*bb4ee6a4SAndroid Build Coastguard Worker     fn async_triple_file_passthrough() {
1039*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
1040*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
1041*bb4ee6a4SAndroid Build Coastguard Worker         let file3 = tempfile().unwrap();
1042*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
1043*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
1044*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
1045*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1046*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1047*bb4ee6a4SAndroid Build Coastguard Worker         };
1048*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
1049*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
1050*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
1051*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1052*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1053*bb4ee6a4SAndroid Build Coastguard Worker         };
1054*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part3 = ComponentDiskPart {
1055*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file3),
1056*bb4ee6a4SAndroid Build Coastguard Worker             offset: 200,
1057*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1058*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1059*bb4ee6a4SAndroid Build Coastguard Worker         };
1060*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2, disk_part3]).unwrap();
1061*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
1062*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(async {
1063*bb4ee6a4SAndroid Build Coastguard Worker             let composite = Box::new(composite).to_async_disk(&ex).unwrap();
1064*bb4ee6a4SAndroid Build Coastguard Worker 
1065*bb4ee6a4SAndroid Build Coastguard Worker             let expected = [55u8; 200];
1066*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1067*bb4ee6a4SAndroid Build Coastguard Worker                 composite.write_double_buffered(0, &expected).await.unwrap(),
1068*bb4ee6a4SAndroid Build Coastguard Worker                 100
1069*bb4ee6a4SAndroid Build Coastguard Worker             );
1070*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1071*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1072*bb4ee6a4SAndroid Build Coastguard Worker                     .write_double_buffered(100, &expected[100..])
1073*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1074*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1075*bb4ee6a4SAndroid Build Coastguard Worker                 100
1076*bb4ee6a4SAndroid Build Coastguard Worker             );
1077*bb4ee6a4SAndroid Build Coastguard Worker 
1078*bb4ee6a4SAndroid Build Coastguard Worker             let mut buf = [0u8; 200];
1079*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1080*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1081*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(0, &mut buf[..])
1082*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1083*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1084*bb4ee6a4SAndroid Build Coastguard Worker                 100
1085*bb4ee6a4SAndroid Build Coastguard Worker             );
1086*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1087*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1088*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(100, &mut buf[100..])
1089*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1090*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1091*bb4ee6a4SAndroid Build Coastguard Worker                 100
1092*bb4ee6a4SAndroid Build Coastguard Worker             );
1093*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(buf, expected);
1094*bb4ee6a4SAndroid Build Coastguard Worker         })
1095*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1096*bb4ee6a4SAndroid Build Coastguard Worker     }
1097*bb4ee6a4SAndroid Build Coastguard Worker 
1098*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_triple_file_punch_hole()1099*bb4ee6a4SAndroid Build Coastguard Worker     fn async_triple_file_punch_hole() {
1100*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
1101*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
1102*bb4ee6a4SAndroid Build Coastguard Worker         let file3 = tempfile().unwrap();
1103*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
1104*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
1105*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
1106*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1107*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1108*bb4ee6a4SAndroid Build Coastguard Worker         };
1109*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
1110*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
1111*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
1112*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1113*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1114*bb4ee6a4SAndroid Build Coastguard Worker         };
1115*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part3 = ComponentDiskPart {
1116*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file3),
1117*bb4ee6a4SAndroid Build Coastguard Worker             offset: 200,
1118*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1119*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1120*bb4ee6a4SAndroid Build Coastguard Worker         };
1121*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2, disk_part3]).unwrap();
1122*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
1123*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(async {
1124*bb4ee6a4SAndroid Build Coastguard Worker             let composite = Box::new(composite).to_async_disk(&ex).unwrap();
1125*bb4ee6a4SAndroid Build Coastguard Worker 
1126*bb4ee6a4SAndroid Build Coastguard Worker             let input = [55u8; 300];
1127*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1128*bb4ee6a4SAndroid Build Coastguard Worker                 composite.write_double_buffered(0, &input).await.unwrap(),
1129*bb4ee6a4SAndroid Build Coastguard Worker                 100
1130*bb4ee6a4SAndroid Build Coastguard Worker             );
1131*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1132*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1133*bb4ee6a4SAndroid Build Coastguard Worker                     .write_double_buffered(100, &input[100..])
1134*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1135*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1136*bb4ee6a4SAndroid Build Coastguard Worker                 100
1137*bb4ee6a4SAndroid Build Coastguard Worker             );
1138*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1139*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1140*bb4ee6a4SAndroid Build Coastguard Worker                     .write_double_buffered(200, &input[200..])
1141*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1142*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1143*bb4ee6a4SAndroid Build Coastguard Worker                 100
1144*bb4ee6a4SAndroid Build Coastguard Worker             );
1145*bb4ee6a4SAndroid Build Coastguard Worker 
1146*bb4ee6a4SAndroid Build Coastguard Worker             composite.punch_hole(50, 200).await.unwrap();
1147*bb4ee6a4SAndroid Build Coastguard Worker 
1148*bb4ee6a4SAndroid Build Coastguard Worker             let mut buf = [0u8; 300];
1149*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1150*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1151*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(0, &mut buf[..])
1152*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1153*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1154*bb4ee6a4SAndroid Build Coastguard Worker                 100
1155*bb4ee6a4SAndroid Build Coastguard Worker             );
1156*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1157*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1158*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(100, &mut buf[100..])
1159*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1160*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1161*bb4ee6a4SAndroid Build Coastguard Worker                 100
1162*bb4ee6a4SAndroid Build Coastguard Worker             );
1163*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1164*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1165*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(200, &mut buf[200..])
1166*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1167*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1168*bb4ee6a4SAndroid Build Coastguard Worker                 100
1169*bb4ee6a4SAndroid Build Coastguard Worker             );
1170*bb4ee6a4SAndroid Build Coastguard Worker 
1171*bb4ee6a4SAndroid Build Coastguard Worker             let mut expected = input;
1172*bb4ee6a4SAndroid Build Coastguard Worker             expected[50..250].iter_mut().for_each(|x| *x = 0);
1173*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(buf, expected);
1174*bb4ee6a4SAndroid Build Coastguard Worker         })
1175*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1176*bb4ee6a4SAndroid Build Coastguard Worker     }
1177*bb4ee6a4SAndroid Build Coastguard Worker 
1178*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_triple_file_write_zeroes()1179*bb4ee6a4SAndroid Build Coastguard Worker     fn async_triple_file_write_zeroes() {
1180*bb4ee6a4SAndroid Build Coastguard Worker         let file1 = tempfile().unwrap();
1181*bb4ee6a4SAndroid Build Coastguard Worker         let file2 = tempfile().unwrap();
1182*bb4ee6a4SAndroid Build Coastguard Worker         let file3 = tempfile().unwrap();
1183*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part1 = ComponentDiskPart {
1184*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file1),
1185*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
1186*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1187*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1188*bb4ee6a4SAndroid Build Coastguard Worker         };
1189*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part2 = ComponentDiskPart {
1190*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file2),
1191*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
1192*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1193*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1194*bb4ee6a4SAndroid Build Coastguard Worker         };
1195*bb4ee6a4SAndroid Build Coastguard Worker         let disk_part3 = ComponentDiskPart {
1196*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(file3),
1197*bb4ee6a4SAndroid Build Coastguard Worker             offset: 200,
1198*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1199*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1200*bb4ee6a4SAndroid Build Coastguard Worker         };
1201*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![disk_part1, disk_part2, disk_part3]).unwrap();
1202*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
1203*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(async {
1204*bb4ee6a4SAndroid Build Coastguard Worker             let composite = Box::new(composite).to_async_disk(&ex).unwrap();
1205*bb4ee6a4SAndroid Build Coastguard Worker 
1206*bb4ee6a4SAndroid Build Coastguard Worker             let input = [55u8; 300];
1207*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1208*bb4ee6a4SAndroid Build Coastguard Worker                 composite.write_double_buffered(0, &input).await.unwrap(),
1209*bb4ee6a4SAndroid Build Coastguard Worker                 100
1210*bb4ee6a4SAndroid Build Coastguard Worker             );
1211*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1212*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1213*bb4ee6a4SAndroid Build Coastguard Worker                     .write_double_buffered(100, &input[100..])
1214*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1215*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1216*bb4ee6a4SAndroid Build Coastguard Worker                 100
1217*bb4ee6a4SAndroid Build Coastguard Worker             );
1218*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1219*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1220*bb4ee6a4SAndroid Build Coastguard Worker                     .write_double_buffered(200, &input[200..])
1221*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1222*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1223*bb4ee6a4SAndroid Build Coastguard Worker                 100
1224*bb4ee6a4SAndroid Build Coastguard Worker             );
1225*bb4ee6a4SAndroid Build Coastguard Worker 
1226*bb4ee6a4SAndroid Build Coastguard Worker             composite.write_zeroes_at(50, 200).await.unwrap();
1227*bb4ee6a4SAndroid Build Coastguard Worker 
1228*bb4ee6a4SAndroid Build Coastguard Worker             let mut buf = [0u8; 300];
1229*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1230*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1231*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(0, &mut buf[..])
1232*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1233*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1234*bb4ee6a4SAndroid Build Coastguard Worker                 100
1235*bb4ee6a4SAndroid Build Coastguard Worker             );
1236*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1237*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1238*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(100, &mut buf[100..])
1239*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1240*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1241*bb4ee6a4SAndroid Build Coastguard Worker                 100
1242*bb4ee6a4SAndroid Build Coastguard Worker             );
1243*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
1244*bb4ee6a4SAndroid Build Coastguard Worker                 composite
1245*bb4ee6a4SAndroid Build Coastguard Worker                     .read_double_buffered(200, &mut buf[200..])
1246*bb4ee6a4SAndroid Build Coastguard Worker                     .await
1247*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap(),
1248*bb4ee6a4SAndroid Build Coastguard Worker                 100
1249*bb4ee6a4SAndroid Build Coastguard Worker             );
1250*bb4ee6a4SAndroid Build Coastguard Worker 
1251*bb4ee6a4SAndroid Build Coastguard Worker             let mut expected = input;
1252*bb4ee6a4SAndroid Build Coastguard Worker             expected[50..250].iter_mut().for_each(|x| *x = 0);
1253*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(buf, expected);
1254*bb4ee6a4SAndroid Build Coastguard Worker         })
1255*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1256*bb4ee6a4SAndroid Build Coastguard Worker     }
1257*bb4ee6a4SAndroid Build Coastguard Worker 
1258*bb4ee6a4SAndroid Build Coastguard Worker     // TODO: fsync on a RO file is legal, this test doesn't work as expected. Consider using a mock
1259*bb4ee6a4SAndroid Build Coastguard Worker     // DiskFile to detect the fsync calls.
1260*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
async_fsync_skips_unchanged_parts()1261*bb4ee6a4SAndroid Build Coastguard Worker     fn async_fsync_skips_unchanged_parts() {
1262*bb4ee6a4SAndroid Build Coastguard Worker         let mut rw_file = tempfile().unwrap();
1263*bb4ee6a4SAndroid Build Coastguard Worker         rw_file.write_all(&[0u8; 100]).unwrap();
1264*bb4ee6a4SAndroid Build Coastguard Worker         rw_file.seek(SeekFrom::Start(0)).unwrap();
1265*bb4ee6a4SAndroid Build Coastguard Worker         let mut ro_disk_image = tempfile::NamedTempFile::new().unwrap();
1266*bb4ee6a4SAndroid Build Coastguard Worker         ro_disk_image.write_all(&[0u8; 100]).unwrap();
1267*bb4ee6a4SAndroid Build Coastguard Worker         let ro_file = OpenOptions::new()
1268*bb4ee6a4SAndroid Build Coastguard Worker             .read(true)
1269*bb4ee6a4SAndroid Build Coastguard Worker             .open(ro_disk_image.path())
1270*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
1271*bb4ee6a4SAndroid Build Coastguard Worker 
1272*bb4ee6a4SAndroid Build Coastguard Worker         let rw_part = ComponentDiskPart {
1273*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(rw_file),
1274*bb4ee6a4SAndroid Build Coastguard Worker             offset: 0,
1275*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1276*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1277*bb4ee6a4SAndroid Build Coastguard Worker         };
1278*bb4ee6a4SAndroid Build Coastguard Worker         let ro_part = ComponentDiskPart {
1279*bb4ee6a4SAndroid Build Coastguard Worker             file: Box::new(ro_file),
1280*bb4ee6a4SAndroid Build Coastguard Worker             offset: 100,
1281*bb4ee6a4SAndroid Build Coastguard Worker             length: 100,
1282*bb4ee6a4SAndroid Build Coastguard Worker             needs_fsync: AtomicBool::new(false),
1283*bb4ee6a4SAndroid Build Coastguard Worker         };
1284*bb4ee6a4SAndroid Build Coastguard Worker         let composite = new_from_components(vec![rw_part, ro_part]).unwrap();
1285*bb4ee6a4SAndroid Build Coastguard Worker         let ex = Executor::new().unwrap();
1286*bb4ee6a4SAndroid Build Coastguard Worker         ex.run_until(async {
1287*bb4ee6a4SAndroid Build Coastguard Worker             let composite = Box::new(composite).to_async_disk(&ex).unwrap();
1288*bb4ee6a4SAndroid Build Coastguard Worker 
1289*bb4ee6a4SAndroid Build Coastguard Worker             // Write to the RW part so that some fsync operation will occur.
1290*bb4ee6a4SAndroid Build Coastguard Worker             composite.write_zeroes_at(0, 20).await.unwrap();
1291*bb4ee6a4SAndroid Build Coastguard Worker 
1292*bb4ee6a4SAndroid Build Coastguard Worker             // This is the test's assert. fsyncing should NOT touch a read-only disk part. On
1293*bb4ee6a4SAndroid Build Coastguard Worker             // Windows, this would be an error.
1294*bb4ee6a4SAndroid Build Coastguard Worker             composite.fsync().await.expect(
1295*bb4ee6a4SAndroid Build Coastguard Worker                 "Failed to fsync composite disk. \
1296*bb4ee6a4SAndroid Build Coastguard Worker                      This can happen if the disk writable state is wrong.",
1297*bb4ee6a4SAndroid Build Coastguard Worker             );
1298*bb4ee6a4SAndroid Build Coastguard Worker         })
1299*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1300*bb4ee6a4SAndroid Build Coastguard Worker     }
1301*bb4ee6a4SAndroid Build Coastguard Worker 
1302*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
beginning_size()1303*bb4ee6a4SAndroid Build Coastguard Worker     fn beginning_size() {
1304*bb4ee6a4SAndroid Build Coastguard Worker         let mut buffer = vec![];
1305*bb4ee6a4SAndroid Build Coastguard Worker         let partitions = [0u8; GPT_NUM_PARTITIONS as usize * GPT_PARTITION_ENTRY_SIZE as usize];
1306*bb4ee6a4SAndroid Build Coastguard Worker         let disk_size = 1000 * SECTOR_SIZE;
1307*bb4ee6a4SAndroid Build Coastguard Worker         write_beginning(
1308*bb4ee6a4SAndroid Build Coastguard Worker             &mut buffer,
1309*bb4ee6a4SAndroid Build Coastguard Worker             Uuid::from_u128(0x12345678_1234_5678_abcd_12345678abcd),
1310*bb4ee6a4SAndroid Build Coastguard Worker             &partitions,
1311*bb4ee6a4SAndroid Build Coastguard Worker             42,
1312*bb4ee6a4SAndroid Build Coastguard Worker             disk_size - GPT_END_SIZE,
1313*bb4ee6a4SAndroid Build Coastguard Worker             disk_size,
1314*bb4ee6a4SAndroid Build Coastguard Worker         )
1315*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1316*bb4ee6a4SAndroid Build Coastguard Worker 
1317*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(buffer.len(), GPT_BEGINNING_SIZE as usize);
1318*bb4ee6a4SAndroid Build Coastguard Worker     }
1319*bb4ee6a4SAndroid Build Coastguard Worker 
1320*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
end_size()1321*bb4ee6a4SAndroid Build Coastguard Worker     fn end_size() {
1322*bb4ee6a4SAndroid Build Coastguard Worker         let mut buffer = vec![];
1323*bb4ee6a4SAndroid Build Coastguard Worker         let partitions = [0u8; GPT_NUM_PARTITIONS as usize * GPT_PARTITION_ENTRY_SIZE as usize];
1324*bb4ee6a4SAndroid Build Coastguard Worker         let disk_size = 1000 * SECTOR_SIZE;
1325*bb4ee6a4SAndroid Build Coastguard Worker         write_end(
1326*bb4ee6a4SAndroid Build Coastguard Worker             &mut buffer,
1327*bb4ee6a4SAndroid Build Coastguard Worker             Uuid::from_u128(0x12345678_1234_5678_abcd_12345678abcd),
1328*bb4ee6a4SAndroid Build Coastguard Worker             &partitions,
1329*bb4ee6a4SAndroid Build Coastguard Worker             42,
1330*bb4ee6a4SAndroid Build Coastguard Worker             disk_size - GPT_END_SIZE,
1331*bb4ee6a4SAndroid Build Coastguard Worker             disk_size,
1332*bb4ee6a4SAndroid Build Coastguard Worker         )
1333*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1334*bb4ee6a4SAndroid Build Coastguard Worker 
1335*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(buffer.len(), GPT_END_SIZE as usize);
1336*bb4ee6a4SAndroid Build Coastguard Worker     }
1337*bb4ee6a4SAndroid Build Coastguard Worker 
1338*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
end_size_with_padding()1339*bb4ee6a4SAndroid Build Coastguard Worker     fn end_size_with_padding() {
1340*bb4ee6a4SAndroid Build Coastguard Worker         let mut buffer = vec![];
1341*bb4ee6a4SAndroid Build Coastguard Worker         let partitions = [0u8; GPT_NUM_PARTITIONS as usize * GPT_PARTITION_ENTRY_SIZE as usize];
1342*bb4ee6a4SAndroid Build Coastguard Worker         let disk_size = 1000 * SECTOR_SIZE;
1343*bb4ee6a4SAndroid Build Coastguard Worker         let padding = 3 * SECTOR_SIZE;
1344*bb4ee6a4SAndroid Build Coastguard Worker         write_end(
1345*bb4ee6a4SAndroid Build Coastguard Worker             &mut buffer,
1346*bb4ee6a4SAndroid Build Coastguard Worker             Uuid::from_u128(0x12345678_1234_5678_abcd_12345678abcd),
1347*bb4ee6a4SAndroid Build Coastguard Worker             &partitions,
1348*bb4ee6a4SAndroid Build Coastguard Worker             42,
1349*bb4ee6a4SAndroid Build Coastguard Worker             disk_size - GPT_END_SIZE - padding,
1350*bb4ee6a4SAndroid Build Coastguard Worker             disk_size,
1351*bb4ee6a4SAndroid Build Coastguard Worker         )
1352*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1353*bb4ee6a4SAndroid Build Coastguard Worker 
1354*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(buffer.len(), GPT_END_SIZE as usize + padding as usize);
1355*bb4ee6a4SAndroid Build Coastguard Worker     }
1356*bb4ee6a4SAndroid Build Coastguard Worker 
1357*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a composite disk image with no partitions.
1358*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
create_composite_disk_empty()1359*bb4ee6a4SAndroid Build Coastguard Worker     fn create_composite_disk_empty() {
1360*bb4ee6a4SAndroid Build Coastguard Worker         let mut header_image = tempfile().unwrap();
1361*bb4ee6a4SAndroid Build Coastguard Worker         let mut footer_image = tempfile().unwrap();
1362*bb4ee6a4SAndroid Build Coastguard Worker         let mut composite_image = tempfile().unwrap();
1363*bb4ee6a4SAndroid Build Coastguard Worker 
1364*bb4ee6a4SAndroid Build Coastguard Worker         create_composite_disk(
1365*bb4ee6a4SAndroid Build Coastguard Worker             &[],
1366*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/zero_filler.img"),
1367*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/header_path.img"),
1368*bb4ee6a4SAndroid Build Coastguard Worker             &mut header_image,
1369*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/footer_path.img"),
1370*bb4ee6a4SAndroid Build Coastguard Worker             &mut footer_image,
1371*bb4ee6a4SAndroid Build Coastguard Worker             &mut composite_image,
1372*bb4ee6a4SAndroid Build Coastguard Worker         )
1373*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1374*bb4ee6a4SAndroid Build Coastguard Worker     }
1375*bb4ee6a4SAndroid Build Coastguard Worker 
1376*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a composite disk image with two partitions.
1377*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
create_composite_disk_success()1378*bb4ee6a4SAndroid Build Coastguard Worker     fn create_composite_disk_success() {
1379*bb4ee6a4SAndroid Build Coastguard Worker         let mut header_image = tempfile().unwrap();
1380*bb4ee6a4SAndroid Build Coastguard Worker         let mut footer_image = tempfile().unwrap();
1381*bb4ee6a4SAndroid Build Coastguard Worker         let mut composite_image = tempfile().unwrap();
1382*bb4ee6a4SAndroid Build Coastguard Worker 
1383*bb4ee6a4SAndroid Build Coastguard Worker         create_composite_disk(
1384*bb4ee6a4SAndroid Build Coastguard Worker             &[
1385*bb4ee6a4SAndroid Build Coastguard Worker                 PartitionInfo {
1386*bb4ee6a4SAndroid Build Coastguard Worker                     label: "partition1".to_string(),
1387*bb4ee6a4SAndroid Build Coastguard Worker                     path: "/partition1.img".to_string().into(),
1388*bb4ee6a4SAndroid Build Coastguard Worker                     partition_type: ImagePartitionType::LinuxFilesystem,
1389*bb4ee6a4SAndroid Build Coastguard Worker                     writable: false,
1390*bb4ee6a4SAndroid Build Coastguard Worker                     // Needs small amount of padding.
1391*bb4ee6a4SAndroid Build Coastguard Worker                     size: 4000,
1392*bb4ee6a4SAndroid Build Coastguard Worker                     part_guid: None,
1393*bb4ee6a4SAndroid Build Coastguard Worker                 },
1394*bb4ee6a4SAndroid Build Coastguard Worker                 PartitionInfo {
1395*bb4ee6a4SAndroid Build Coastguard Worker                     label: "partition2".to_string(),
1396*bb4ee6a4SAndroid Build Coastguard Worker                     path: "/partition2.img".to_string().into(),
1397*bb4ee6a4SAndroid Build Coastguard Worker                     partition_type: ImagePartitionType::LinuxFilesystem,
1398*bb4ee6a4SAndroid Build Coastguard Worker                     writable: true,
1399*bb4ee6a4SAndroid Build Coastguard Worker                     // Needs no padding.
1400*bb4ee6a4SAndroid Build Coastguard Worker                     size: 4096,
1401*bb4ee6a4SAndroid Build Coastguard Worker                     part_guid: Some(Uuid::from_u128(0x4049C8DC_6C2B_C740_A95A_BDAA629D4378)),
1402*bb4ee6a4SAndroid Build Coastguard Worker                 },
1403*bb4ee6a4SAndroid Build Coastguard Worker             ],
1404*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/zero_filler.img"),
1405*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/header_path.img"),
1406*bb4ee6a4SAndroid Build Coastguard Worker             &mut header_image,
1407*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/footer_path.img"),
1408*bb4ee6a4SAndroid Build Coastguard Worker             &mut footer_image,
1409*bb4ee6a4SAndroid Build Coastguard Worker             &mut composite_image,
1410*bb4ee6a4SAndroid Build Coastguard Worker         )
1411*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
1412*bb4ee6a4SAndroid Build Coastguard Worker 
1413*bb4ee6a4SAndroid Build Coastguard Worker         // Check magic.
1414*bb4ee6a4SAndroid Build Coastguard Worker         composite_image.rewind().unwrap();
1415*bb4ee6a4SAndroid Build Coastguard Worker         let mut magic_space = [0u8; CDISK_MAGIC.len()];
1416*bb4ee6a4SAndroid Build Coastguard Worker         composite_image.read_exact(&mut magic_space[..]).unwrap();
1417*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(magic_space, CDISK_MAGIC.as_bytes());
1418*bb4ee6a4SAndroid Build Coastguard Worker         // Check proto.
1419*bb4ee6a4SAndroid Build Coastguard Worker         let proto = CompositeDisk::parse_from_reader(&mut composite_image).unwrap();
1420*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
1421*bb4ee6a4SAndroid Build Coastguard Worker             proto,
1422*bb4ee6a4SAndroid Build Coastguard Worker             CompositeDisk {
1423*bb4ee6a4SAndroid Build Coastguard Worker                 version: 2,
1424*bb4ee6a4SAndroid Build Coastguard Worker                 component_disks: vec![
1425*bb4ee6a4SAndroid Build Coastguard Worker                     ComponentDisk {
1426*bb4ee6a4SAndroid Build Coastguard Worker                         file_path: "/header_path.img".to_string(),
1427*bb4ee6a4SAndroid Build Coastguard Worker                         offset: 0,
1428*bb4ee6a4SAndroid Build Coastguard Worker                         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
1429*bb4ee6a4SAndroid Build Coastguard Worker                         ..ComponentDisk::new()
1430*bb4ee6a4SAndroid Build Coastguard Worker                     },
1431*bb4ee6a4SAndroid Build Coastguard Worker                     ComponentDisk {
1432*bb4ee6a4SAndroid Build Coastguard Worker                         file_path: "/partition1.img".to_string(),
1433*bb4ee6a4SAndroid Build Coastguard Worker                         offset: 0x5000, // GPT_BEGINNING_SIZE,
1434*bb4ee6a4SAndroid Build Coastguard Worker                         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
1435*bb4ee6a4SAndroid Build Coastguard Worker                         ..ComponentDisk::new()
1436*bb4ee6a4SAndroid Build Coastguard Worker                     },
1437*bb4ee6a4SAndroid Build Coastguard Worker                     ComponentDisk {
1438*bb4ee6a4SAndroid Build Coastguard Worker                         file_path: "/zero_filler.img".to_string(),
1439*bb4ee6a4SAndroid Build Coastguard Worker                         offset: 0x5fa0, // GPT_BEGINNING_SIZE + 4000,
1440*bb4ee6a4SAndroid Build Coastguard Worker                         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
1441*bb4ee6a4SAndroid Build Coastguard Worker                         ..ComponentDisk::new()
1442*bb4ee6a4SAndroid Build Coastguard Worker                     },
1443*bb4ee6a4SAndroid Build Coastguard Worker                     ComponentDisk {
1444*bb4ee6a4SAndroid Build Coastguard Worker                         file_path: "/partition2.img".to_string(),
1445*bb4ee6a4SAndroid Build Coastguard Worker                         offset: 0x6000, // GPT_BEGINNING_SIZE + 4096,
1446*bb4ee6a4SAndroid Build Coastguard Worker                         read_write_capability: ReadWriteCapability::READ_WRITE.into(),
1447*bb4ee6a4SAndroid Build Coastguard Worker                         ..ComponentDisk::new()
1448*bb4ee6a4SAndroid Build Coastguard Worker                     },
1449*bb4ee6a4SAndroid Build Coastguard Worker                     ComponentDisk {
1450*bb4ee6a4SAndroid Build Coastguard Worker                         file_path: "/footer_path.img".to_string(),
1451*bb4ee6a4SAndroid Build Coastguard Worker                         offset: 0x7000, // GPT_BEGINNING_SIZE + 4096 + 4096,
1452*bb4ee6a4SAndroid Build Coastguard Worker                         read_write_capability: ReadWriteCapability::READ_ONLY.into(),
1453*bb4ee6a4SAndroid Build Coastguard Worker                         ..ComponentDisk::new()
1454*bb4ee6a4SAndroid Build Coastguard Worker                     },
1455*bb4ee6a4SAndroid Build Coastguard Worker                 ],
1456*bb4ee6a4SAndroid Build Coastguard Worker                 length: 0x10000, // 1 << DISK_SIZE_SHIFT
1457*bb4ee6a4SAndroid Build Coastguard Worker                 ..CompositeDisk::new()
1458*bb4ee6a4SAndroid Build Coastguard Worker             }
1459*bb4ee6a4SAndroid Build Coastguard Worker         );
1460*bb4ee6a4SAndroid Build Coastguard Worker     }
1461*bb4ee6a4SAndroid Build Coastguard Worker 
1462*bb4ee6a4SAndroid Build Coastguard Worker     /// Attempts to create a composite disk image with two partitions with the same label.
1463*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
create_composite_disk_duplicate_label()1464*bb4ee6a4SAndroid Build Coastguard Worker     fn create_composite_disk_duplicate_label() {
1465*bb4ee6a4SAndroid Build Coastguard Worker         let mut header_image = tempfile().unwrap();
1466*bb4ee6a4SAndroid Build Coastguard Worker         let mut footer_image = tempfile().unwrap();
1467*bb4ee6a4SAndroid Build Coastguard Worker         let mut composite_image = tempfile().unwrap();
1468*bb4ee6a4SAndroid Build Coastguard Worker 
1469*bb4ee6a4SAndroid Build Coastguard Worker         let result = create_composite_disk(
1470*bb4ee6a4SAndroid Build Coastguard Worker             &[
1471*bb4ee6a4SAndroid Build Coastguard Worker                 PartitionInfo {
1472*bb4ee6a4SAndroid Build Coastguard Worker                     label: "label".to_string(),
1473*bb4ee6a4SAndroid Build Coastguard Worker                     path: "/partition1.img".to_string().into(),
1474*bb4ee6a4SAndroid Build Coastguard Worker                     partition_type: ImagePartitionType::LinuxFilesystem,
1475*bb4ee6a4SAndroid Build Coastguard Worker                     writable: false,
1476*bb4ee6a4SAndroid Build Coastguard Worker                     size: 0,
1477*bb4ee6a4SAndroid Build Coastguard Worker                     part_guid: None,
1478*bb4ee6a4SAndroid Build Coastguard Worker                 },
1479*bb4ee6a4SAndroid Build Coastguard Worker                 PartitionInfo {
1480*bb4ee6a4SAndroid Build Coastguard Worker                     label: "label".to_string(),
1481*bb4ee6a4SAndroid Build Coastguard Worker                     path: "/partition2.img".to_string().into(),
1482*bb4ee6a4SAndroid Build Coastguard Worker                     partition_type: ImagePartitionType::LinuxFilesystem,
1483*bb4ee6a4SAndroid Build Coastguard Worker                     writable: true,
1484*bb4ee6a4SAndroid Build Coastguard Worker                     size: 0,
1485*bb4ee6a4SAndroid Build Coastguard Worker                     part_guid: None,
1486*bb4ee6a4SAndroid Build Coastguard Worker                 },
1487*bb4ee6a4SAndroid Build Coastguard Worker             ],
1488*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/zero_filler.img"),
1489*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/header_path.img"),
1490*bb4ee6a4SAndroid Build Coastguard Worker             &mut header_image,
1491*bb4ee6a4SAndroid Build Coastguard Worker             Path::new("/footer_path.img"),
1492*bb4ee6a4SAndroid Build Coastguard Worker             &mut footer_image,
1493*bb4ee6a4SAndroid Build Coastguard Worker             &mut composite_image,
1494*bb4ee6a4SAndroid Build Coastguard Worker         );
1495*bb4ee6a4SAndroid Build Coastguard Worker         assert!(matches!(result, Err(Error::DuplicatePartitionLabel(label)) if label == "label"));
1496*bb4ee6a4SAndroid Build Coastguard Worker     }
1497*bb4ee6a4SAndroid Build Coastguard Worker }
1498