blob: 121915045b0357931d2ad3254531f8dd0f186de4 [file] [log] [blame]
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +00001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Andrew Walbran3eca16c2021-06-14 11:15:14 +000015//! Functions for creating a composite disk image.
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000016
Andrew Walbran3eca16c2021-06-14 11:15:14 +000017use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition;
Frederick Maylec4748972024-09-05 19:20:39 -070018use anyhow::{bail, Context, Error};
19use disk::{create_composite_disk, ImagePartitionType, PartitionInfo};
Andrew Walbran3eca16c2021-06-14 11:15:14 +000020use std::fs::{File, OpenOptions};
Frederick Maylec4748972024-09-05 19:20:39 -070021use std::io::ErrorKind;
22use std::os::unix::fs::FileExt;
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000023use std::os::unix::io::AsRawFd;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000024use std::path::{Path, PathBuf};
Frederick Maylec4748972024-09-05 19:20:39 -070025use zerocopy::AsBytes;
26use zerocopy::FromBytes;
27use zerocopy::FromZeroes;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000028
Jiyong Park3f9b5092024-07-10 13:38:29 +090029use uuid::Uuid;
30
Andrew Walbran3eca16c2021-06-14 11:15:14 +000031/// Constructs a composite disk image for the given list of partitions, and opens it ready to use.
32///
Andrew Walbranfbb39d22021-07-28 17:01:25 +000033/// Returns the composite disk image file, and a list of files whose file descriptors must be passed
34/// to any process which wants to use it. This is necessary because the composite image contains
35/// paths of the form `/proc/self/fd/N` for the partition images.
Andrew Walbran3eca16c2021-06-14 11:15:14 +000036pub fn make_composite_image(
37 partitions: &[Partition],
Jooyung Han95884632021-07-06 22:27:54 +090038 zero_filler_path: &Path,
Andrew Walbran3eca16c2021-06-14 11:15:14 +000039 output_path: &Path,
40 header_path: &Path,
41 footer_path: &Path,
42) -> Result<(File, Vec<File>), Error> {
Andrew Walbranfbb39d22021-07-28 17:01:25 +000043 let (partitions, mut files) = convert_partitions(partitions)?;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000044
45 let mut composite_image = OpenOptions::new()
46 .create_new(true)
47 .read(true)
48 .write(true)
49 .open(output_path)
50 .with_context(|| format!("Failed to create composite image {:?}", output_path))?;
51 let mut header_file =
52 OpenOptions::new().create_new(true).read(true).write(true).open(header_path).with_context(
53 || format!("Failed to create composite image header {:?}", header_path),
54 )?;
55 let mut footer_file =
56 OpenOptions::new().create_new(true).read(true).write(true).open(footer_path).with_context(
57 || format!("Failed to create composite image header {:?}", footer_path),
58 )?;
Chris Wailes9b866f02022-11-16 15:17:16 -080059 let zero_filler_file = File::open(zero_filler_path).with_context(|| {
Andrew Walbranfbb39d22021-07-28 17:01:25 +000060 format!("Failed to open composite image zero filler {:?}", zero_filler_path)
61 })?;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000062
63 create_composite_disk(
64 &partitions,
Andrew Walbranfbb39d22021-07-28 17:01:25 +000065 &fd_path_for_file(&zero_filler_file),
66 &fd_path_for_file(&header_file),
Andrew Walbran3eca16c2021-06-14 11:15:14 +000067 &mut header_file,
Andrew Walbranfbb39d22021-07-28 17:01:25 +000068 &fd_path_for_file(&footer_file),
Andrew Walbran3eca16c2021-06-14 11:15:14 +000069 &mut footer_file,
70 &mut composite_image,
71 )?;
72
73 // Re-open the composite image as read-only.
Chris Wailes9b866f02022-11-16 15:17:16 -080074 let composite_image = File::open(output_path)
Andrew Walbran3eca16c2021-06-14 11:15:14 +000075 .with_context(|| format!("Failed to open composite image {:?}", output_path))?;
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000076
Andrew Walbranfbb39d22021-07-28 17:01:25 +000077 files.push(header_file);
78 files.push(footer_file);
79 files.push(zero_filler_file);
80
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000081 Ok((composite_image, files))
82}
83
Jooyung Han631d5882021-07-29 06:34:05 +090084/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
Andrew Walbranfbb39d22021-07-28 17:01:25 +000085/// partition, returns the corresponding list of PartitionInfo and the list of files whose file
86/// descriptors must be passed to any process using the composite image.
Andrew Walbran3eca16c2021-06-14 11:15:14 +000087fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000088 // File descriptors to pass to child process.
89 let mut files = vec![];
90
91 let partitions = partitions
92 .iter()
93 .map(|partition| {
Jooyung Han631d5882021-07-29 06:34:05 +090094 // TODO(b/187187765): This shouldn't be an Option.
95 let file = partition
96 .image
97 .as_ref()
98 .context("Invalid partition image file descriptor")?
99 .as_ref()
100 .try_clone()
Andrei Homescu11333c62023-11-09 04:26:39 +0000101 .context("Failed to clone partition image file descriptor")?
102 .into();
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000103 let path = fd_path_for_file(&file);
Frederick Maylec4748972024-09-05 19:20:39 -0700104 let size = get_partition_size(&file)?;
Jooyung Han631d5882021-07-29 06:34:05 +0900105 files.push(file);
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000106
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000107 Ok(PartitionInfo {
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000108 label: partition.label.to_owned(),
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000109 path,
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000110 partition_type: ImagePartitionType::LinuxFilesystem,
111 writable: partition.writable,
Jooyung Han631d5882021-07-29 06:34:05 +0900112 size,
Jiyong Park3f9b5092024-07-10 13:38:29 +0900113 part_guid: partition.guid.as_deref().map(Uuid::parse_str).transpose()?,
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000114 })
115 })
116 .collect::<Result<_, Error>>()?;
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000117
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000118 Ok((partitions, files))
119}
120
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000121fn fd_path_for_file(file: &File) -> PathBuf {
122 let fd = file.as_raw_fd();
123 format!("/proc/self/fd/{}", fd).into()
124}
125
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000126/// Find the size of the partition image in the given file by parsing the header.
127///
Frederick Maylec4748972024-09-05 19:20:39 -0700128/// This will work for raw and Android sparse images. QCOW2 and composite images aren't supported.
129fn get_partition_size(file: &File) -> Result<u64, Error> {
130 match detect_image_type(file).context("failed to detect partition image type")? {
131 ImageType::Raw => Ok(file.metadata().context("failed to get metadata")?.len()),
132 ImageType::AndroidSparse => {
133 // Source: system/core/libsparse/sparse_format.h
134 #[repr(C)]
135 #[derive(Clone, Copy, Debug, AsBytes, FromZeroes, FromBytes)]
136 struct SparseHeader {
137 magic: u32,
138 major_version: u16,
139 minor_version: u16,
140 file_hdr_sz: u16,
141 chunk_hdr_size: u16,
142 blk_sz: u32,
143 total_blks: u32,
144 total_chunks: u32,
145 image_checksum: u32,
146 }
147 let mut header = SparseHeader::new_zeroed();
148 file.read_exact_at(header.as_bytes_mut(), 0)
149 .context("failed to read android sparse header")?;
150 let len = u64::from(header.total_blks)
151 .checked_mul(header.blk_sz.into())
152 .context("android sparse image len too big")?;
153 Ok(len)
154 }
155 t => bail!("unsupported partition image type: {t:?}"),
156 }
157}
158
159/// Image file types we can detect.
160#[derive(Debug, PartialEq, Eq)]
161enum ImageType {
162 Raw,
163 Qcow2,
164 CompositeDisk,
165 AndroidSparse,
166}
167
168/// Detect image type by looking for magic bytes.
169fn detect_image_type(file: &File) -> std::io::Result<ImageType> {
170 const CDISK_MAGIC: &str = "composite_disk\x1d";
171 const QCOW_MAGIC: u32 = 0x5146_49fb;
172 const SPARSE_HEADER_MAGIC: u32 = 0xed26ff3a;
173
174 let mut magic4 = [0u8; 4];
175 match file.read_exact_at(&mut magic4[..], 0) {
176 Ok(()) => {}
177 Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
178 Err(e) => return Err(e),
179 }
180 if magic4 == QCOW_MAGIC.to_be_bytes() {
181 return Ok(ImageType::Qcow2);
182 }
183 if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
184 return Ok(ImageType::AndroidSparse);
185 }
186
187 let mut buf = [0u8; CDISK_MAGIC.len()];
188 match file.read_exact_at(buf.as_bytes_mut(), 0) {
189 Ok(()) => {}
190 Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
191 Err(e) => return Err(e),
192 }
193 if buf == CDISK_MAGIC.as_bytes() {
194 return Ok(ImageType::CompositeDisk);
195 }
196
197 Ok(ImageType::Raw)
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000198}