blob: 7fcce2c9c15dd29155e8735fe5c3e1e12541256f [file] [log] [blame]
Andreas Gampe01ad5982016-03-09 16:27:29 -08001/*
2 ** Copyright 2016, 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
Andreas Gamped089ca12016-06-27 14:25:30 -070017#include <fcntl.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080018#include <linux/unistd.h>
19#include <sys/mount.h>
Roland Levillainc19c6042018-12-18 12:15:12 +000020#include <sys/stat.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080021#include <sys/wait.h>
22
Alex Light53e94382021-03-09 16:39:55 -080023#include <array>
Nikita Ioffe97f013c2021-06-29 03:42:56 +010024#include <chrono>
Alex Lightcf7e6de2021-03-04 10:20:18 -080025#include <fstream>
Andreas Gamped089ca12016-06-27 14:25:30 -070026#include <sstream>
Nikita Ioffe97f013c2021-06-29 03:42:56 +010027#include <thread>
Andreas Gamped089ca12016-06-27 14:25:30 -070028
Alex Lightcf7e6de2021-03-04 10:20:18 -080029#include <android-base/file.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080030#include <android-base/logging.h>
31#include <android-base/macros.h>
Nikita Ioffe97f013c2021-06-29 03:42:56 +010032#include <android-base/parseint.h>
Nikita Ioffe2a42aee2021-04-07 16:25:49 +010033#include <android-base/scopeguard.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080034#include <android-base/stringprintf.h>
Nikita Ioffe97f013c2021-06-29 03:42:56 +010035#include <android-base/strings.h>
Alex Lightcf7e6de2021-03-04 10:20:18 -080036#include <android-base/unique_fd.h>
Nikita Ioffe97f013c2021-06-29 03:42:56 +010037#include <android/content/pm/IOtaDexopt.h>
38#include <binder/IServiceManager.h>
Andreas Gampee90127d2019-03-22 11:21:34 -070039#include <libdm/dm.h>
Roland Levillainc19c6042018-12-18 12:15:12 +000040#include <selinux/android.h>
41
Jeff Sharkey0274c972016-12-06 09:32:04 -070042#include "installd_constants.h"
43#include "otapreopt_utils.h"
Andreas Gampe548bdb92016-06-02 17:56:45 -070044
Andreas Gampe01ad5982016-03-09 16:27:29 -080045#ifndef LOG_TAG
46#define LOG_TAG "otapreopt"
47#endif
48
49using android::base::StringPrintf;
50
51namespace android {
52namespace installd {
53
Nikita Ioffe97f013c2021-06-29 03:42:56 +010054using namespace std::literals::chrono_literals;
55
Andreas Gamped089ca12016-06-27 14:25:30 -070056static void CloseDescriptor(int fd) {
57 if (fd >= 0) {
58 int result = close(fd);
59 UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor
60 // that we do *not* want.
61 }
62}
63
Alex Light53e94382021-03-09 16:39:55 -080064static void ActivateApexPackages() {
65 std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--otachroot-bootstrap"};
66 std::string apexd_error_msg;
Roland Levillain6520cca2019-02-01 13:15:58 +000067
Alex Light53e94382021-03-09 16:39:55 -080068 bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
69 if (!exec_result) {
70 PLOG(ERROR) << "Running otapreopt failed: " << apexd_error_msg;
71 exit(220);
Roland Levillain6520cca2019-02-01 13:15:58 +000072 }
73}
74
Nikita Ioffe2a42aee2021-04-07 16:25:49 +010075static void DeactivateApexPackages() {
76 std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
77 std::string apexd_error_msg;
78 bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
79 if (!exec_result) {
80 PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
81 }
82}
83
Andreas Gampe54b62c92019-03-21 11:34:19 -070084static void TryExtraMount(const char* name, const char* slot, const char* target) {
Andreas Gampee90127d2019-03-22 11:21:34 -070085 std::string partition_name = StringPrintf("%s%s", name, slot);
86
87 // See whether update_engine mounted a logical partition.
88 {
89 auto& dm = dm::DeviceMapper::Instance();
90 if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
91 std::string path;
92 if (dm.GetDmDevicePathByName(partition_name, &path)) {
93 int mount_result = mount(path.c_str(),
94 target,
95 "ext4",
96 MS_RDONLY,
97 /* data */ nullptr);
98 if (mount_result == 0) {
99 return;
100 }
101 }
102 }
103 }
104
105 // Fall back and attempt a direct mount.
106 std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
Andreas Gampe54b62c92019-03-21 11:34:19 -0700107 int mount_result = mount(block_device.c_str(),
108 target,
109 "ext4",
110 MS_RDONLY,
111 /* data */ nullptr);
112 UNUSED(mount_result);
113}
114
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100115static android::sp<android::content::pm::IOtaDexopt> GetDexoptService() {
116 auto binder = android::defaultServiceManager()->getService(android::String16("otadexopt"));
117 if (binder == nullptr) {
118 return nullptr;
119 }
120 return android::interface_cast<android::content::pm::IOtaDexopt>(binder);
121}
122
123static bool RunDexoptCommand(int argc, char **arg, const std::string& dexopt_cmd) {
124 // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc
125 // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1
126 std::vector<std::string> cmd;
127 cmd.reserve(argc);
128 cmd.push_back("/system/bin/otapreopt");
129
130 // The first parameter is the status file descriptor, skip.
131 for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
132 cmd.push_back(arg[i]);
133 }
134 for (const std::string& part : android::base::Split(dexopt_cmd, " ")) {
135 cmd.push_back(part);
136 }
137
138 // Fork and execute otapreopt in its own process.
139 std::string error_msg;
140 bool exec_result = Exec(cmd, &error_msg);
141 if (!exec_result) {
142 LOG(ERROR) << "Running otapreopt failed: " << error_msg;
143 }
144 return exec_result;
145}
146
Andreas Gamped089ca12016-06-27 14:25:30 -0700147// Entry for otapreopt_chroot. Expected parameters are:
148// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
149// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
150// be passed on to otapreopt in the chroot.
Andreas Gampe01ad5982016-03-09 16:27:29 -0800151static int otapreopt_chroot(const int argc, char **arg) {
Zach Riggle318853a2018-01-18 18:34:04 -0600152 // Validate arguments
153 // We need the command, status channel and target slot, at a minimum.
154 if(argc < 3) {
155 PLOG(ERROR) << "Not enough arguments.";
156 exit(208);
157 }
Andreas Gamped089ca12016-06-27 14:25:30 -0700158 // Close all file descriptors. They are coming from the caller, we do not want to pass them
159 // on across our fork/exec into a different domain.
160 // 1) Default descriptors.
161 CloseDescriptor(STDIN_FILENO);
162 CloseDescriptor(STDOUT_FILENO);
163 CloseDescriptor(STDERR_FILENO);
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100164
165 int fd;
166 if (!android::base::ParseInt(arg[1], &fd)) {
167 LOG(ERROR) << "Failed to parse " << arg[1];
168 exit(225);
169 }
170 // Add O_CLOEXEC to status channel, since we don't want to pass it across fork/exec, but we need
171 // to keep it open in otapreopt_chroot to report progress
172 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
173 PLOG(ERROR) << "Failed to set O_CLOEXEC on " << fd;
174 exit(226);
175 }
Andreas Gamped089ca12016-06-27 14:25:30 -0700176
Andreas Gampe01ad5982016-03-09 16:27:29 -0800177 // We need to run the otapreopt tool from the postinstall partition. As such, set up a
178 // mount namespace and change root.
179
180 // Create our own mount namespace.
181 if (unshare(CLONE_NEWNS) != 0) {
182 PLOG(ERROR) << "Failed to unshare() for otapreopt.";
183 exit(200);
184 }
185
186 // Make postinstall private, so that our changes don't propagate.
187 if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) {
188 PLOG(ERROR) << "Failed to mount private.";
189 exit(201);
190 }
191
192 // Bind mount necessary directories.
193 constexpr const char* kBindMounts[] = {
194 "/data", "/dev", "/proc", "/sys"
195 };
196 for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
197 std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
198 if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) {
199 PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i];
200 exit(202);
201 }
202 }
203
Andreas Gampefd12eda2016-07-12 09:47:17 -0700204 // Try to mount the vendor partition. update_engine doesn't do this for us, but we
205 // want it for vendor APKs.
206 // Notes:
207 // 1) We pretty much guess a name here and hope to find the partition by name.
208 // It is just as complicated and brittle to scan /proc/mounts. But this requires
209 // validating the target-slot so as not to try to mount some totally random path.
210 // 2) We're in a mount namespace here, so when we die, this will be cleaned up.
211 // 3) Ignore errors. Printing anything at this stage will open a file descriptor
212 // for logging.
213 if (!ValidateTargetSlotSuffix(arg[2])) {
214 LOG(ERROR) << "Target slot suffix not legal: " << arg[2];
215 exit(207);
216 }
Andreas Gampe54b62c92019-03-21 11:34:19 -0700217 TryExtraMount("vendor", arg[2], "/postinstall/vendor");
Andreas Gampeb87a1c72018-04-13 17:42:23 -0700218
219 // Try to mount the product partition. update_engine doesn't do this for us, but we
220 // want it for product APKs. Same notes as vendor above.
Andreas Gampe54b62c92019-03-21 11:34:19 -0700221 TryExtraMount("product", arg[2], "/postinstall/product");
Andreas Gampefd12eda2016-07-12 09:47:17 -0700222
Alex Light1a4c1da2021-04-15 15:21:17 -0700223 // Try to mount the system_ext partition. update_engine doesn't do this for
224 // us, but we want it for system_ext APKs. Same notes as vendor and product
225 // above.
226 TryExtraMount("system_ext", arg[2], "/postinstall/system_ext");
227
Alex Light2b307a02021-03-03 18:35:47 -0800228 constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
229 // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
230 if (mount("tmpfs", kPostInstallLinkerconfig, "tmpfs", 0, nullptr) != 0) {
231 PLOG(ERROR) << "Failed to mount a tmpfs for " << kPostInstallLinkerconfig;
232 exit(215);
233 }
234
Roland Levillainc19c6042018-12-18 12:15:12 +0000235 // Setup APEX mount point and its security context.
236 static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
237 // The following logic is similar to the one in system/core/rootdir/init.rc:
238 //
239 // mount tmpfs tmpfs /apex nodev noexec nosuid
240 // chmod 0755 /apex
241 // chown root root /apex
242 // restorecon /apex
243 //
Roland Levillain8d276812019-01-24 10:51:30 +0000244 // except we perform the `restorecon` step just after mounting the tmpfs
245 // filesystem in /postinstall/apex, so that this directory is correctly
246 // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in
247 // following operations (`chmod`, `chown`, etc.) following policies
248 // restricted to `postinstall_apex_mnt_dir`:
249 //
250 // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid
251 // restorecon /postinstall/apex
252 // chmod 0755 /postinstall/apex
253 // chown root root /postinstall/apex
254 //
Roland Levillainc19c6042018-12-18 12:15:12 +0000255 if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
256 != 0) {
257 PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
258 exit(209);
259 }
Roland Levillain8d276812019-01-24 10:51:30 +0000260 if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
261 PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
262 exit(214);
263 }
Roland Levillainc19c6042018-12-18 12:15:12 +0000264 if (chmod(kPostinstallApexDir, 0755) != 0) {
265 PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755";
266 exit(210);
267 }
268 if (chown(kPostinstallApexDir, 0, 0) != 0) {
269 PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
270 exit(211);
271 }
Roland Levillainc19c6042018-12-18 12:15:12 +0000272
Andreas Gampe01ad5982016-03-09 16:27:29 -0800273 // Chdir into /postinstall.
274 if (chdir("/postinstall") != 0) {
275 PLOG(ERROR) << "Unable to chdir into /postinstall.";
276 exit(203);
277 }
278
279 // Make /postinstall the root in our mount namespace.
280 if (chroot(".") != 0) {
281 PLOG(ERROR) << "Failed to chroot";
282 exit(204);
283 }
284
285 if (chdir("/") != 0) {
286 PLOG(ERROR) << "Unable to chdir into /.";
287 exit(205);
288 }
289
Nikita Ioffe2a42aee2021-04-07 16:25:49 +0100290 // Call apexd --unmount-all to free up loop and dm block devices, so that we can re-use
291 // them during the next invocation. Since otapreopt_chroot calls exit in case something goes
292 // wrong we need to register our own atexit handler.
293 // We want to register this handler before actually activating apex packages. This is mostly
294 // due to the fact that if fail to unmount apexes, then on the next run of otapreopt_chroot
295 // we will ask for new loop devices instead of re-using existing ones, and we really don't want
296 // to do that. :)
297 if (atexit(DeactivateApexPackages) != 0) {
298 LOG(ERROR) << "Failed to register atexit hander";
299 exit(206);
300 }
301
Roland Levillainc19c6042018-12-18 12:15:12 +0000302 // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
Martin Stjernholmf4caaa02019-07-17 22:14:14 +0100303 // the ART APEX, as it is required by otapreopt to run dex2oat.
Alex Light53e94382021-03-09 16:39:55 -0800304 ActivateApexPackages();
Roland Levillainc19c6042018-12-18 12:15:12 +0000305
Nikita Ioffe2a42aee2021-04-07 16:25:49 +0100306 auto cleanup = android::base::make_scope_guard([](){
307 std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
308 std::string apexd_error_msg;
309 bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
310 if (!exec_result) {
311 PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
312 }
313 });
Martin Stjernholmf4caaa02019-07-17 22:14:14 +0100314 // Check that an ART APEX has been activated; clean up and exit
Roland Levillain8a8ca152019-07-11 18:48:05 +0100315 // early otherwise.
Alex Light2b307a02021-03-03 18:35:47 -0800316 static constexpr const std::string_view kRequiredApexs[] = {
317 "com.android.art",
318 "com.android.runtime",
Alex Lightfbdc7262021-04-26 16:48:18 -0700319 "com.android.sdkext", // For derive_classpath
Alex Light2b307a02021-03-03 18:35:47 -0800320 };
Alex Light53e94382021-03-09 16:39:55 -0800321 std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
322 DIR* apex_dir = opendir("/apex");
323 if (apex_dir == nullptr) {
324 PLOG(ERROR) << "unable to open /apex";
325 exit(220);
326 }
327 for (dirent* entry = readdir(apex_dir); entry != nullptr; entry = readdir(apex_dir)) {
328 for (int i = 0; i < found_apexs.size(); i++) {
329 if (kRequiredApexs[i] == std::string_view(entry->d_name)) {
330 found_apexs[i] = true;
331 break;
332 }
Alex Light2b307a02021-03-03 18:35:47 -0800333 }
334 }
Alex Light53e94382021-03-09 16:39:55 -0800335 closedir(apex_dir);
336 auto it = std::find(found_apexs.cbegin(), found_apexs.cend(), false);
337 if (it != found_apexs.cend()) {
338 LOG(ERROR) << "No activated " << kRequiredApexs[std::distance(found_apexs.cbegin(), it)]
339 << " package!";
340 exit(221);
341 }
Alex Light2b307a02021-03-03 18:35:47 -0800342
343 // Setup /linkerconfig. Doing it after the chroot means it doesn't need its own category
344 if (selinux_android_restorecon("/linkerconfig", 0) < 0) {
345 PLOG(ERROR) << "Failed to restorecon /linkerconfig";
346 exit(219);
347 }
348 std::vector<std::string> linkerconfig_cmd{"/apex/com.android.runtime/bin/linkerconfig",
349 "--target", "/linkerconfig"};
350 std::string linkerconfig_error_msg;
351 bool linkerconfig_exec_result = Exec(linkerconfig_cmd, &linkerconfig_error_msg);
352 if (!linkerconfig_exec_result) {
353 LOG(ERROR) << "Running linkerconfig failed: " << linkerconfig_error_msg;
354 exit(218);
Roland Levillain8a8ca152019-07-11 18:48:05 +0100355 }
356
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100357 android::sp<android::content::pm::IOtaDexopt> dexopt = GetDexoptService();
358 if (dexopt == nullptr) {
359 LOG(ERROR) << "Failed to find otadexopt service";
360 exit(222);
361 }
362
363 android::base::borrowed_fd status_fd(fd);
Andreas Gampe01ad5982016-03-09 16:27:29 -0800364 // Now go on and run otapreopt.
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100365 constexpr const int kMaximumPackages = 1000;
366 for (int iter = 0; iter < kMaximumPackages; iter++) {
367 android::String16 cmd;
368 android::binder::Status status = dexopt->nextDexoptCommand(&cmd);
369 if (!status.isOk()) {
370 LOG(ERROR) << "Failed to retrieve next dexopt command";
371 // Should we fail instead?
372 exit(224);
373 }
374 if (!RunDexoptCommand(argc, arg, android::String8(cmd).string())) {
375 exit(213);
376 }
Andreas Gampe01ad5982016-03-09 16:27:29 -0800377
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100378 float progress;
379 status = dexopt->getProgress(&progress);
380 if (!status.isOk()) {
381 LOG(ERROR) << "Failed to retrieve dexopt progress";
382 continue;
383 }
384 LOG(VERBOSE) << "Progress: " << progress;
385 std::string progress_str = StringPrintf("global_progress %.2f\n", progress);
386 if (!android::base::WriteStringToFd(progress_str, status_fd)) {
387 PLOG(ERROR) << "Failed to write '" << progress_str << "' to " << status_fd.get();
388 }
Andreas Gamped089ca12016-06-27 14:25:30 -0700389
Nikita Ioffe97f013c2021-06-29 03:42:56 +0100390 bool done;
391 status = dexopt->isDone(&done);
392 if (!status.isOk()) {
393 LOG(WARNING) << "Failed to check if dexopt is done";
394 continue;
395 }
396 if (done) {
397 LOG(INFO) << "dexopt is done";
398 break;
399 }
400 std::this_thread::sleep_for(1s);
Roland Levillain94b41802019-01-18 11:56:50 +0000401 }
402
403 return 0;
Andreas Gampe01ad5982016-03-09 16:27:29 -0800404}
405
406} // namespace installd
407} // namespace android
408
409int main(const int argc, char *argv[]) {
410 return android::installd::otapreopt_chroot(argc, argv);
411}