1 // 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
15 //! Functions for creating a composite disk image.
16
17 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition;
18 use anyhow::{anyhow, Context, Error};
19 use disk::{
20 create_composite_disk, create_disk_file, ImagePartitionType, PartitionInfo, MAX_NESTING_DEPTH,
21 };
22 use std::fs::{File, OpenOptions};
23 use std::os::unix::io::AsRawFd;
24 use std::path::{Path, PathBuf};
25
26 /// Constructs a composite disk image for the given list of partitions, and opens it ready to use.
27 ///
28 /// Returns the composite disk image file, and a list of files whose file descriptors must be passed
29 /// to any process which wants to use it. This is necessary because the composite image contains
30 /// paths of the form `/proc/self/fd/N` for the partition images.
make_composite_image( partitions: &[Partition], zero_filler_path: &Path, output_path: &Path, header_path: &Path, footer_path: &Path, ) -> Result<(File, Vec<File>), Error>31 pub fn make_composite_image(
32 partitions: &[Partition],
33 zero_filler_path: &Path,
34 output_path: &Path,
35 header_path: &Path,
36 footer_path: &Path,
37 ) -> Result<(File, Vec<File>), Error> {
38 let (partitions, mut files) = convert_partitions(partitions)?;
39
40 let mut composite_image = OpenOptions::new()
41 .create_new(true)
42 .read(true)
43 .write(true)
44 .open(output_path)
45 .with_context(|| format!("Failed to create composite image {:?}", output_path))?;
46 let mut header_file =
47 OpenOptions::new().create_new(true).read(true).write(true).open(header_path).with_context(
48 || format!("Failed to create composite image header {:?}", header_path),
49 )?;
50 let mut footer_file =
51 OpenOptions::new().create_new(true).read(true).write(true).open(footer_path).with_context(
52 || format!("Failed to create composite image header {:?}", footer_path),
53 )?;
54 let zero_filler_file = File::open(zero_filler_path).with_context(|| {
55 format!("Failed to open composite image zero filler {:?}", zero_filler_path)
56 })?;
57
58 create_composite_disk(
59 &partitions,
60 &fd_path_for_file(&zero_filler_file),
61 &fd_path_for_file(&header_file),
62 &mut header_file,
63 &fd_path_for_file(&footer_file),
64 &mut footer_file,
65 &mut composite_image,
66 )?;
67
68 // Re-open the composite image as read-only.
69 let composite_image = File::open(output_path)
70 .with_context(|| format!("Failed to open composite image {:?}", output_path))?;
71
72 files.push(header_file);
73 files.push(footer_file);
74 files.push(zero_filler_file);
75
76 Ok((composite_image, files))
77 }
78
79 /// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
80 /// partition, returns the corresponding list of PartitionInfo and the list of files whose file
81 /// descriptors must be passed to any process using the composite image.
convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error>82 fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
83 // File descriptors to pass to child process.
84 let mut files = vec![];
85
86 let partitions = partitions
87 .iter()
88 .map(|partition| {
89 // TODO(b/187187765): This shouldn't be an Option.
90 let file = partition
91 .image
92 .as_ref()
93 .context("Invalid partition image file descriptor")?
94 .as_ref()
95 .try_clone()
96 .context("Failed to clone partition image file descriptor")?
97 .into();
98 let path = fd_path_for_file(&file);
99 let size = get_partition_size(&file, &path)?;
100 files.push(file);
101
102 Ok(PartitionInfo {
103 label: partition.label.to_owned(),
104 path,
105 partition_type: ImagePartitionType::LinuxFilesystem,
106 writable: partition.writable,
107 size,
108 })
109 })
110 .collect::<Result<_, Error>>()?;
111
112 Ok((partitions, files))
113 }
114
fd_path_for_file(file: &File) -> PathBuf115 fn fd_path_for_file(file: &File) -> PathBuf {
116 let fd = file.as_raw_fd();
117 format!("/proc/self/fd/{}", fd).into()
118 }
119
120 /// Find the size of the partition image in the given file by parsing the header.
121 ///
122 /// This will work for raw, QCOW2, composite and Android sparse images.
get_partition_size(partition: &File, path: &Path) -> Result<u64, Error>123 fn get_partition_size(partition: &File, path: &Path) -> Result<u64, Error> {
124 // TODO: Use `context` once disk::Error implements std::error::Error.
125 // TODO: Add check for is_sparse_file
126 Ok(create_disk_file(
127 partition.try_clone()?,
128 /* is_sparse_file */ false,
129 MAX_NESTING_DEPTH,
130 path,
131 )
132 .map_err(|e| anyhow!("Failed to open partition image: {}", e))?
133 .get_len()?)
134 }
135