blob: ef2a145754b79ab742d474d28a566829e27c176a [file] [log] [blame]
Alan Stokes8c840442021-11-26 15:54:30 +00001/*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Alan Stokesda596932021-12-15 17:48:55 +000017//! Handle running odrefresh in the VM, with an async interface to allow cancellation
18
19use crate::fd_server_helper::FdServerConfig;
Alan Stokes8c840442021-11-26 15:54:30 +000020use crate::instance_starter::CompOsInstance;
Alan Stokes8c840442021-11-26 15:54:30 +000021use android_system_composd::aidl::android::system::composd::{
Alan Stokes81c96f32022-04-07 14:13:19 +010022 ICompilationTask::ICompilationTask,
23 ICompilationTaskCallback::{FailureReason::FailureReason, ICompilationTaskCallback},
Alan Stokes8c840442021-11-26 15:54:30 +000024};
Alan Stokes126fd512021-12-16 15:00:01 +000025use anyhow::{Context, Result};
Alan Stokes0e82b502022-08-08 14:44:48 +010026use binder::{Interface, Result as BinderResult, Strong};
Alan Stokes2d2e4db2022-01-28 16:41:52 +000027use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
Victor Hsiehe7698672022-09-23 16:22:28 -070028 CompilationMode::CompilationMode, ICompOsService, OdrefreshArgs::OdrefreshArgs,
Alan Stokes2d2e4db2022-01-28 16:41:52 +000029};
Victor Hsiehcb6d66b2022-05-10 16:12:06 -070030use compos_common::odrefresh::{
Victor Hsieh9807dcd2023-03-14 09:58:53 -070031 is_system_property_interesting, ExitCode, CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR,
32 PENDING_ARTIFACTS_SUBDIR,
Victor Hsiehcb6d66b2022-05-10 16:12:06 -070033};
Alan Stokes454069c2022-02-03 11:21:19 +000034use log::{error, info, warn};
Victor Hsieh9807dcd2023-03-14 09:58:53 -070035use odsign_proto::odsign_info::OdsignInfo;
36use protobuf::Message;
Alan Stokesda596932021-12-15 17:48:55 +000037use rustutils::system_properties;
Victor Hsieh9807dcd2023-03-14 09:58:53 -070038use std::fs::{remove_dir_all, File, OpenOptions};
39use std::os::fd::AsFd;
Alan Stokesda596932021-12-15 17:48:55 +000040use std::os::unix::fs::OpenOptionsExt;
Victor Hsiehbebcb872022-08-26 11:23:16 -070041use std::os::unix::io::{AsRawFd, OwnedFd};
Alan Stokesda596932021-12-15 17:48:55 +000042use std::path::Path;
Alan Stokes8c840442021-11-26 15:54:30 +000043use std::sync::{Arc, Mutex};
44use std::thread;
45
46#[derive(Clone)]
47pub struct OdrefreshTask {
48 running_task: Arc<Mutex<Option<RunningTask>>>,
49}
50
51impl Interface for OdrefreshTask {}
52
53impl ICompilationTask for OdrefreshTask {
54 fn cancel(&self) -> BinderResult<()> {
55 let task = self.take();
Alan Stokes0fc6ce52022-08-02 17:01:48 +010056 // Drop the VM, which should end compilation - and cause our thread to exit.
57 // Note that we don't do a graceful shutdown here; we've been asked to give up our resources
58 // ASAP, and the VM has not failed so we don't need to ensure VM logs are written.
Alan Stokes8c840442021-11-26 15:54:30 +000059 drop(task);
60 Ok(())
61 }
62}
63
Alan Stokesda596932021-12-15 17:48:55 +000064struct RunningTask {
65 callback: Strong<dyn ICompilationTaskCallback>,
66 #[allow(dead_code)] // Keeps the CompOS VM alive
Alan Stokes93863602022-08-03 17:23:25 +010067 comp_os: CompOsInstance,
Alan Stokesda596932021-12-15 17:48:55 +000068}
69
Alan Stokes8c840442021-11-26 15:54:30 +000070impl OdrefreshTask {
71 /// Return the current running task, if any, removing it from this CompilationTask.
72 /// Once removed, meaning the task has ended or been canceled, further calls will always return
73 /// None.
74 fn take(&self) -> Option<RunningTask> {
75 self.running_task.lock().unwrap().take()
76 }
77
78 pub fn start(
Alan Stokes93863602022-08-03 17:23:25 +010079 comp_os: CompOsInstance,
Alan Stokes2d2e4db2022-01-28 16:41:52 +000080 compilation_mode: CompilationMode,
Alan Stokesac9aa1a2021-12-14 11:32:13 +000081 target_dir_name: String,
Alan Stokes8c840442021-11-26 15:54:30 +000082 callback: &Strong<dyn ICompilationTaskCallback>,
83 ) -> Result<OdrefreshTask> {
84 let service = comp_os.get_service();
85 let task = RunningTask { comp_os, callback: callback.clone() };
86 let task = OdrefreshTask { running_task: Arc::new(Mutex::new(Some(task))) };
87
Alan Stokes2d2e4db2022-01-28 16:41:52 +000088 task.clone().start_thread(service, compilation_mode, target_dir_name);
Alan Stokes8c840442021-11-26 15:54:30 +000089
90 Ok(task)
91 }
92
Alan Stokes2d2e4db2022-01-28 16:41:52 +000093 fn start_thread(
94 self,
95 service: Strong<dyn ICompOsService>,
96 compilation_mode: CompilationMode,
97 target_dir_name: String,
98 ) {
Alan Stokes8c840442021-11-26 15:54:30 +000099 thread::spawn(move || {
Alan Stokes2d2e4db2022-01-28 16:41:52 +0000100 let exit_code = run_in_vm(service, compilation_mode, &target_dir_name);
Alan Stokes8c840442021-11-26 15:54:30 +0000101
102 let task = self.take();
103 // We don't do the callback if cancel has already happened.
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100104 if let Some(RunningTask { callback, comp_os }) = task {
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100105 // Make sure we keep our service alive until we have called the callback.
Alan Stokes93863602022-08-03 17:23:25 +0100106 let lazy_service_guard = comp_os.shutdown();
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100107
Alan Stokes8c840442021-11-26 15:54:30 +0000108 let result = match exit_code {
Alan Stokes454069c2022-02-03 11:21:19 +0000109 Ok(ExitCode::CompilationSuccess) => {
Victor Hsieh9807dcd2023-03-14 09:58:53 -0700110 if compilation_mode == CompilationMode::TEST_COMPILE {
111 info!("Compilation success");
112 callback.onSuccess()
113 } else {
114 // compos.info is generated only during NORMAL_COMPILE
115 if let Err(e) = enable_fsverity_to_all() {
116 let message =
117 format!("Unexpected failure when enabling fs-verity: {:?}", e);
118 error!("{}", message);
119 callback.onFailure(FailureReason::FailedToEnableFsverity, &message)
120 } else {
121 info!("Compilation success, fs-verity enabled");
122 callback.onSuccess()
123 }
124 }
Alan Stokes454069c2022-02-03 11:21:19 +0000125 }
Alan Stokes8c840442021-11-26 15:54:30 +0000126 Ok(exit_code) => {
Alan Stokes81c96f32022-04-07 14:13:19 +0100127 let message = format!("Unexpected odrefresh result: {:?}", exit_code);
128 error!("{}", message);
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100129 callback.onFailure(FailureReason::UnexpectedCompilationResult, &message)
Alan Stokes8c840442021-11-26 15:54:30 +0000130 }
131 Err(e) => {
Alan Stokes81c96f32022-04-07 14:13:19 +0100132 let message = format!("Running odrefresh failed: {:?}", e);
133 error!("{}", message);
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100134 callback.onFailure(FailureReason::CompilationFailed, &message)
Alan Stokes8c840442021-11-26 15:54:30 +0000135 }
136 };
137 if let Err(e) = result {
138 warn!("Failed to deliver callback: {:?}", e);
139 }
Alan Stokes0fc6ce52022-08-02 17:01:48 +0100140 drop(lazy_service_guard);
Alan Stokes8c840442021-11-26 15:54:30 +0000141 }
142 });
143 }
144}
145
Alan Stokes2d2e4db2022-01-28 16:41:52 +0000146fn run_in_vm(
147 service: Strong<dyn ICompOsService>,
148 compilation_mode: CompilationMode,
149 target_dir_name: &str,
150) -> Result<ExitCode> {
Victor Hsiehcb6d66b2022-05-10 16:12:06 -0700151 let mut names = Vec::new();
152 let mut values = Vec::new();
153 system_properties::foreach(|name, value| {
154 if is_system_property_interesting(name) {
155 names.push(name.to_owned());
156 values.push(value.to_owned());
157 }
158 })?;
159 service.initializeSystemProperties(&names, &values).context("initialize system properties")?;
160
Alan Stokes16fb8552022-02-10 15:07:27 +0000161 let output_root = Path::new(ODREFRESH_OUTPUT_ROOT_DIR);
Alan Stokes35bac3c2021-12-16 14:37:24 +0000162
163 // We need to remove the target directory because odrefresh running in compos will create it
164 // (and can't see the existing one, since authfs doesn't show it existing files in an output
165 // directory).
166 let target_path = output_root.join(target_dir_name);
Alan Stokesa4542ec2021-12-20 09:39:33 +0000167 if target_path.exists() {
168 remove_dir_all(&target_path)
169 .with_context(|| format!("Failed to delete {}", target_path.display()))?;
170 }
Alan Stokes35bac3c2021-12-16 14:37:24 +0000171
Victor Hsiehbebcb872022-08-26 11:23:16 -0700172 let staging_dir_fd = open_dir(composd_native::palette_create_odrefresh_staging_directory()?)?;
173 let system_dir_fd = open_dir(Path::new("/system"))?;
174 let output_dir_fd = open_dir(output_root)?;
175
176 // Get the raw FD before passing the ownership, since borrowing will violate the borrow check.
177 let system_dir_raw_fd = system_dir_fd.as_raw_fd();
178 let output_dir_raw_fd = output_dir_fd.as_raw_fd();
179 let staging_dir_raw_fd = staging_dir_fd.as_raw_fd();
Alan Stokesda596932021-12-15 17:48:55 +0000180
Victor Hsieha61ec2e2022-09-21 16:25:27 -0700181 // Get the /system_ext FD differently because it may not exist.
Victor Hsieh64d88622022-09-21 17:32:00 -0700182 let (system_ext_dir_raw_fd, ro_dir_fds) =
Victor Hsieha61ec2e2022-09-21 16:25:27 -0700183 if let Ok(system_ext_dir_fd) = open_dir(Path::new("/system_ext")) {
184 (system_ext_dir_fd.as_raw_fd(), vec![system_dir_fd, system_ext_dir_fd])
185 } else {
186 (-1, vec![system_dir_fd])
187 };
188
Alan Stokesda596932021-12-15 17:48:55 +0000189 // Spawn a fd_server to serve the FDs.
190 let fd_server_config = FdServerConfig {
Victor Hsieha61ec2e2022-09-21 16:25:27 -0700191 ro_dir_fds,
Victor Hsiehbebcb872022-08-26 11:23:16 -0700192 rw_dir_fds: vec![staging_dir_fd, output_dir_fd],
Alan Stokesda596932021-12-15 17:48:55 +0000193 ..Default::default()
194 };
195 let fd_server_raii = fd_server_config.into_fd_server()?;
196
Andrew Walbran014efb52022-02-03 17:43:11 +0000197 let zygote_arch = system_properties::read("ro.zygote")?.context("ro.zygote not set")?;
Victor Hsieh9bfbc5f2021-12-16 11:45:10 -0800198 let system_server_compiler_filter =
Andrew Walbran014efb52022-02-03 17:43:11 +0000199 system_properties::read("dalvik.vm.systemservercompilerfilter")?.unwrap_or_default();
Victor Hsiehe7698672022-09-23 16:22:28 -0700200
201 let args = OdrefreshArgs {
202 compilationMode: compilation_mode,
203 systemDirFd: system_dir_raw_fd,
204 systemExtDirFd: system_ext_dir_raw_fd,
205 outputDirFd: output_dir_raw_fd,
206 stagingDirFd: staging_dir_raw_fd,
207 targetDirName: target_dir_name.to_string(),
208 zygoteArch: zygote_arch,
209 systemServerCompilerFilter: system_server_compiler_filter,
210 };
211 let exit_code = service.odrefresh(&args)?;
Alan Stokesda596932021-12-15 17:48:55 +0000212
213 drop(fd_server_raii);
Alan Stokes126fd512021-12-16 15:00:01 +0000214 ExitCode::from_i32(exit_code.into())
Alan Stokesda596932021-12-15 17:48:55 +0000215}
216
Victor Hsieh9807dcd2023-03-14 09:58:53 -0700217/// Enable fs-verity to output artifacts according to compos.info in the pending directory. Any
218/// error before the completion will just abort, leaving the previous files enabled.
219fn enable_fsverity_to_all() -> Result<()> {
220 let odrefresh_current_dir = Path::new(ODREFRESH_OUTPUT_ROOT_DIR).join(CURRENT_ARTIFACTS_SUBDIR);
221 let pending_dir = Path::new(ODREFRESH_OUTPUT_ROOT_DIR).join(PENDING_ARTIFACTS_SUBDIR);
222 let mut reader =
223 File::open(&pending_dir.join("compos.info")).context("Failed to open compos.info")?;
224 let compos_info = OdsignInfo::parse_from_reader(&mut reader).context("Failed to parse")?;
225
226 for path_str in compos_info.file_hashes.keys() {
227 // Need to rebase the directory on to compos-pending first
228 if let Ok(relpath) = Path::new(path_str).strip_prefix(&odrefresh_current_dir) {
229 let path = pending_dir.join(relpath);
230 let file = File::open(&path).with_context(|| format!("Failed to open {:?}", path))?;
231 // We don't expect error. But when it happens, don't bother handle it here. For
232 // simplicity, just let odsign do the regular check.
233 fsverity::enable(file.as_fd())
234 .with_context(|| format!("Failed to enable fs-verity to {:?}", path))?;
235 } else {
236 warn!("Skip due to unexpected path: {}", path_str);
237 }
238 }
239 Ok(())
240}
241
Victor Hsiehbebcb872022-08-26 11:23:16 -0700242/// Returns an `OwnedFD` of the directory.
243fn open_dir(path: &Path) -> Result<OwnedFd> {
244 Ok(OwnedFd::from(
245 OpenOptions::new()
246 .custom_flags(libc::O_DIRECTORY)
247 .read(true) // O_DIRECTORY can only be opened with read
248 .open(path)
249 .with_context(|| format!("Failed to open {:?} directory as path fd", path))?,
250 ))
Alan Stokes8c840442021-11-26 15:54:30 +0000251}