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