blob: eb738a75af50b08edc62fe0663fe1ce283effb0e [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
15//! Functions for running `mk_cdisk`.
16
17mod config;
18
19use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition as AidlPartition;
20use anyhow::{bail, Context, Error};
21use command_fds::{CommandFdExt, FdMapping};
22use config::{Config, Partition};
23use log::info;
24use std::fs::File;
25use std::os::unix::io::AsRawFd;
26use std::panic;
27use std::path::Path;
28use std::process::{Command, Stdio};
29use std::str;
30use std::thread;
31
32const MK_CDISK_PATH: &str = "/apex/com.android.virt/bin/mk_cdisk";
33
34/// Calls `mk_cdisk` to construct a composite disk image for the given list of partitions, and opens
35/// it ready to use. Returns the composite disk image file, and a list of FD mappings which must be
36/// applied to any process which wants to use it. This is necessary because the composite image
37/// contains paths of the form `/proc/self/fd/N` for the partition images.
38pub fn make_composite_image(
39 partitions: &[AidlPartition],
40 output_filename: &Path,
41) -> Result<(File, Vec<File>), Error> {
42 let (config_json, files) = make_config_json(partitions)?;
43 let fd_mappings: Vec<_> = files
44 .iter()
45 .map(|file| FdMapping { parent_fd: file.as_raw_fd(), child_fd: file.as_raw_fd() })
46 .collect();
47
48 let mut command = Command::new(MK_CDISK_PATH);
49 command
50 .arg("-") // Read config JSON from stdin.
51 .arg(&output_filename)
52 .stdin(Stdio::piped())
53 .stdout(Stdio::piped())
54 .stderr(Stdio::piped());
55 command.fd_mappings(fd_mappings)?;
56 let mut child = command.spawn().context("Failed to spawn mk_cdisk")?;
57 let stdin = child.stdin.take().unwrap();
58
59 // Write config to stdin of mk_cdisk on a separate thread to avoid deadlock, as it may not read
60 // all of stdin before it blocks on writing to stdout.
61 let writer_thread = thread::spawn(move || config_json.write_json(&stdin));
62 info!("Running {:?}", command);
63 let output = child.wait_with_output()?;
64 match writer_thread.join() {
65 Ok(result) => result?,
66 Err(panic_payload) => panic::resume_unwind(panic_payload),
67 }
68
69 if !output.status.success() {
70 info!("mk_cdisk stdout: {}", str::from_utf8(&output.stdout)?);
71 info!("mk_cdisk stderr: {}", str::from_utf8(&output.stderr)?);
72 bail!("mk_cdisk exited with error {}", output.status);
73 }
74
75 let composite_image = File::open(&output_filename)
76 .with_context(|| format!("Failed to open composite image {:?}", output_filename))?;
77
78 Ok((composite_image, files))
79}
80
81/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
82/// partition, return the list of file descriptors which must be passed to the mk_cdisk child
83/// process and the JSON configuration for it.
84fn make_config_json(partitions: &[AidlPartition]) -> Result<(Config, Vec<File>), Error> {
85 // File descriptors to pass to child process.
86 let mut files = vec![];
87
88 let partitions = partitions
89 .iter()
90 .map(|partition| {
91 // 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()
98 .context("Failed to clone partition image file descriptor")?;
99 let fd = file.as_raw_fd();
100 files.push(file);
101
102 Ok(Partition {
103 writable: partition.writable,
104 label: partition.label.to_owned(),
105 path: format!("/proc/self/fd/{}", fd).into(),
106 })
107 })
108 .collect::<Result<_, Error>>()?;
109 let config_json = Config { partitions };
110
111 Ok((config_json, files))
112}