blob: 37428eba654006262cddbb363d0410dc3f7c3b3d [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
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000017use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition as AidlPartition;
18use anyhow::{bail, Context, Error};
19use command_fds::{CommandFdExt, FdMapping};
Andrew Walbran0c4d3df2021-05-27 14:00:42 +000020use compositediskconfig::{Config, Partition};
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000021use log::info;
22use std::fs::File;
23use std::os::unix::io::AsRawFd;
24use std::panic;
25use std::path::Path;
26use std::process::{Command, Stdio};
27use std::str;
28use std::thread;
29
30const MK_CDISK_PATH: &str = "/apex/com.android.virt/bin/mk_cdisk";
31
32/// Calls `mk_cdisk` to construct a composite disk image for the given list of partitions, and opens
33/// it ready to use. Returns the composite disk image file, and a list of FD mappings which must be
34/// applied to any process which wants to use it. This is necessary because the composite image
35/// contains paths of the form `/proc/self/fd/N` for the partition images.
36pub fn make_composite_image(
37 partitions: &[AidlPartition],
38 output_filename: &Path,
39) -> Result<(File, Vec<File>), Error> {
40 let (config_json, files) = make_config_json(partitions)?;
41 let fd_mappings: Vec<_> = files
42 .iter()
43 .map(|file| FdMapping { parent_fd: file.as_raw_fd(), child_fd: file.as_raw_fd() })
44 .collect();
45
46 let mut command = Command::new(MK_CDISK_PATH);
47 command
48 .arg("-") // Read config JSON from stdin.
49 .arg(&output_filename)
50 .stdin(Stdio::piped())
51 .stdout(Stdio::piped())
52 .stderr(Stdio::piped());
53 command.fd_mappings(fd_mappings)?;
54 let mut child = command.spawn().context("Failed to spawn mk_cdisk")?;
55 let stdin = child.stdin.take().unwrap();
56
57 // Write config to stdin of mk_cdisk on a separate thread to avoid deadlock, as it may not read
58 // all of stdin before it blocks on writing to stdout.
Andrew Walbran0c4d3df2021-05-27 14:00:42 +000059 let writer_thread = thread::spawn(move || {
60 config_json.write_json(&stdin).context("Failed to write config JSON for mk_cdisk")
61 });
Andrew Walbranf5fbb7d2021-05-12 17:15:48 +000062 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}