blob: 681ec590eb79581cb4a7da79b19f11d8147b9ebf [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;
Andrew Walbranb31c3f12021-08-03 13:17:00 +000018use anyhow::{anyhow, Context, Error};
Jiyong Park2cb63b12021-11-05 14:29:23 +090019use disk::{
20 create_composite_disk, create_disk_file, ImagePartitionType, PartitionInfo, MAX_NESTING_DEPTH,
21};
Andrew Walbran3eca16c2021-06-14 11:15:14 +000022use std::fs::{File, OpenOptions};
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};
Andrew Walbran3eca16c2021-06-14 11:15:14 +000025
Jiyong Park3f9b5092024-07-10 13:38:29 +090026use uuid::Uuid;
27
Andrew Walbran3eca16c2021-06-14 11:15:14 +000028/// Constructs a composite disk image for the given list of partitions, and opens it ready to use.
29///
Andrew Walbranfbb39d22021-07-28 17:01:25 +000030/// Returns the composite disk image file, and a list of files whose file descriptors must be passed
31/// to any process which wants to use it. This is necessary because the composite image contains
32/// paths of the form `/proc/self/fd/N` for the partition images.
Andrew Walbran3eca16c2021-06-14 11:15:14 +000033pub fn make_composite_image(
34 partitions: &[Partition],
Jooyung Han95884632021-07-06 22:27:54 +090035 zero_filler_path: &Path,
Andrew Walbran3eca16c2021-06-14 11:15:14 +000036 output_path: &Path,
37 header_path: &Path,
38 footer_path: &Path,
39) -> Result<(File, Vec<File>), Error> {
Andrew Walbranfbb39d22021-07-28 17:01:25 +000040 let (partitions, mut files) = convert_partitions(partitions)?;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000041
42 let mut composite_image = OpenOptions::new()
43 .create_new(true)
44 .read(true)
45 .write(true)
46 .open(output_path)
47 .with_context(|| format!("Failed to create composite image {:?}", output_path))?;
48 let mut header_file =
49 OpenOptions::new().create_new(true).read(true).write(true).open(header_path).with_context(
50 || format!("Failed to create composite image header {:?}", header_path),
51 )?;
52 let mut footer_file =
53 OpenOptions::new().create_new(true).read(true).write(true).open(footer_path).with_context(
54 || format!("Failed to create composite image header {:?}", footer_path),
55 )?;
Chris Wailes9b866f02022-11-16 15:17:16 -080056 let zero_filler_file = File::open(zero_filler_path).with_context(|| {
Andrew Walbranfbb39d22021-07-28 17:01:25 +000057 format!("Failed to open composite image zero filler {:?}", zero_filler_path)
58 })?;
Andrew Walbran3eca16c2021-06-14 11:15:14 +000059
60 create_composite_disk(
61 &partitions,
Andrew Walbranfbb39d22021-07-28 17:01:25 +000062 &fd_path_for_file(&zero_filler_file),
63 &fd_path_for_file(&header_file),
Andrew Walbran3eca16c2021-06-14 11:15:14 +000064 &mut header_file,
Andrew Walbranfbb39d22021-07-28 17:01:25 +000065 &fd_path_for_file(&footer_file),
Andrew Walbran3eca16c2021-06-14 11:15:14 +000066 &mut footer_file,
67 &mut composite_image,
68 )?;
69
70 // Re-open the composite image as read-only.
Chris Wailes9b866f02022-11-16 15:17:16 -080071 let composite_image = File::open(output_path)
Andrew Walbran3eca16c2021-06-14 11:15:14 +000072 .with_context(|| format!("Failed to open composite image {:?}", output_path))?;
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000073
Andrew Walbranfbb39d22021-07-28 17:01:25 +000074 files.push(header_file);
75 files.push(footer_file);
76 files.push(zero_filler_file);
77
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000078 Ok((composite_image, files))
79}
80
Jooyung Han631d5882021-07-29 06:34:05 +090081/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
Andrew Walbranfbb39d22021-07-28 17:01:25 +000082/// partition, returns the corresponding list of PartitionInfo and the list of files whose file
83/// descriptors must be passed to any process using the composite image.
Andrew Walbran3eca16c2021-06-14 11:15:14 +000084fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000085 // File descriptors to pass to child process.
86 let mut files = vec![];
87
88 let partitions = partitions
89 .iter()
90 .map(|partition| {
Jooyung Han631d5882021-07-29 06:34:05 +090091 // TODO(b/187187765): This shouldn't be an Option.
92 let file = partition
93 .image
94 .as_ref()
95 .context("Invalid partition image file descriptor")?
96 .as_ref()
97 .try_clone()
Andrei Homescu11333c62023-11-09 04:26:39 +000098 .context("Failed to clone partition image file descriptor")?
99 .into();
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000100 let path = fd_path_for_file(&file);
Richard Fung0383ab92022-03-24 18:16:25 +0000101 let size = get_partition_size(&file, &path)?;
Jooyung Han631d5882021-07-29 06:34:05 +0900102 files.push(file);
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000103
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000104 Ok(PartitionInfo {
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000105 label: partition.label.to_owned(),
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000106 path,
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000107 partition_type: ImagePartitionType::LinuxFilesystem,
108 writable: partition.writable,
Jooyung Han631d5882021-07-29 06:34:05 +0900109 size,
Jiyong Park3f9b5092024-07-10 13:38:29 +0900110 part_guid: partition.guid.as_deref().map(Uuid::parse_str).transpose()?,
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000111 })
112 })
113 .collect::<Result<_, Error>>()?;
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +0000114
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000115 Ok((partitions, files))
116}
117
Andrew Walbranfbb39d22021-07-28 17:01:25 +0000118fn fd_path_for_file(file: &File) -> PathBuf {
119 let fd = file.as_raw_fd();
120 format!("/proc/self/fd/{}", fd).into()
121}
122
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000123/// Find the size of the partition image in the given file by parsing the header.
124///
125/// This will work for raw, QCOW2, composite and Android sparse images.
Richard Fung0383ab92022-03-24 18:16:25 +0000126fn get_partition_size(partition: &File, path: &Path) -> Result<u64, Error> {
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000127 // TODO: Use `context` once disk::Error implements std::error::Error.
Elie Kheirallah6b376a72022-06-04 00:19:26 +0000128 // TODO: Add check for is_sparse_file
129 Ok(create_disk_file(
130 partition.try_clone()?,
131 /* is_sparse_file */ false,
132 MAX_NESTING_DEPTH,
133 path,
134 )
135 .map_err(|e| anyhow!("Failed to open partition image: {}", e))?
136 .get_len()?)
Andrew Walbran3eca16c2021-06-14 11:15:14 +0000137}