blob: 379cf92043b9392af66c7c98013fbbc5e7bbf4ef [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 Lightcf7e6de2021-03-04 10:20:18 -080023#include <fstream>
Andreas Gamped089ca12016-06-27 14:25:30 -070024#include <sstream>
25
Alex Lightcf7e6de2021-03-04 10:20:18 -080026#include <android-base/file.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080027#include <android-base/logging.h>
28#include <android-base/macros.h>
29#include <android-base/stringprintf.h>
Alex Lightcf7e6de2021-03-04 10:20:18 -080030#include <android-base/unique_fd.h>
Andreas Gampee90127d2019-03-22 11:21:34 -070031#include <libdm/dm.h>
Roland Levillainc19c6042018-12-18 12:15:12 +000032#include <selinux/android.h>
33
Nikita Ioffe35d7d4c2021-03-03 19:21:06 +000034#include <apex_file_repository.h>
Alex Lightcf7e6de2021-03-04 10:20:18 -080035#include <apex_constants.h>
Roland Levillainc19c6042018-12-18 12:15:12 +000036#include <apexd.h>
Andreas Gampe01ad5982016-03-09 16:27:29 -080037
Jeff Sharkey0274c972016-12-06 09:32:04 -070038#include "installd_constants.h"
39#include "otapreopt_utils.h"
Andreas Gampe548bdb92016-06-02 17:56:45 -070040
Andreas Gampe01ad5982016-03-09 16:27:29 -080041#ifndef LOG_TAG
42#define LOG_TAG "otapreopt"
43#endif
44
45using android::base::StringPrintf;
46
47namespace android {
48namespace installd {
49
Andreas Gamped089ca12016-06-27 14:25:30 -070050static void CloseDescriptor(int fd) {
51 if (fd >= 0) {
52 int result = close(fd);
53 UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor
54 // that we do *not* want.
55 }
56}
57
58static void CloseDescriptor(const char* descriptor_string) {
59 int fd = -1;
60 std::istringstream stream(descriptor_string);
61 stream >> fd;
62 if (!stream.fail()) {
63 CloseDescriptor(fd);
64 }
65}
66
Roland Levillain6520cca2019-02-01 13:15:58 +000067static std::vector<apex::ApexFile> ActivateApexPackages() {
68 // The logic here is (partially) copied and adapted from
Jooyung Han361210e2019-12-05 18:14:39 +090069 // system/apex/apexd/apexd.cpp.
Roland Levillain6520cca2019-02-01 13:15:58 +000070 //
Jooyung Han361210e2019-12-05 18:14:39 +090071 // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir).
Nikita Ioffe35d7d4c2021-03-03 19:21:06 +000072 std::vector<std::string> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
Jooyung Han361210e2019-12-05 18:14:39 +090073 apex::kApexPackageVendorDir};
Nikita Ioffe35d7d4c2021-03-03 19:21:06 +000074 // Initialize ApexFileRepository used internally in ScanPackagesDirAndActivate.
75 // This is a quick fix to fix apex activation in otapreopt_chroot.
76 apex::ApexFileRepository::GetInstance().AddPreInstalledApex(apex_dirs);
Jooyung Han361210e2019-12-05 18:14:39 +090077 for (const auto& dir : apex_dirs) {
78 // Cast call to void to suppress warn_unused_result.
Nikita Ioffe35d7d4c2021-03-03 19:21:06 +000079 static_cast<void>(apex::ScanPackagesDirAndActivate(dir.c_str()));
Jooyung Han361210e2019-12-05 18:14:39 +090080 }
Nikita Ioffeb2342d82020-12-22 20:23:34 +000081 return apex::GetActivePackages();
Roland Levillain6520cca2019-02-01 13:15:58 +000082}
83
Alex Lightcf7e6de2021-03-04 10:20:18 -080084static void CreateApexInfoList(const std::vector<apex::ApexFile>& apex_files) {
85 // Setup the apex-info-list.xml file
86 const std::string apex_info_file = std::string(apex::kApexRoot) + "/" + apex::kApexInfoList;
87 std::fstream xml(apex_info_file.c_str(), std::ios::out | std::ios::trunc);
88 if (!xml.is_open()) {
89 PLOG(ERROR) << "Failed to open " << apex_info_file;
90 exit(216);
91 }
92
93 // we do not care about inactive apexs
94 std::vector<apex::ApexFile> inactive;
95 apex::CollectApexInfoList(xml, apex_files, inactive);
96 xml.flush();
97 xml.close();
98}
99
Roland Levillain6520cca2019-02-01 13:15:58 +0000100static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
101 for (const apex::ApexFile& apex_file : active_packages) {
102 const std::string& package_path = apex_file.GetPath();
Nikita Ioffeb2342d82020-12-22 20:23:34 +0000103 base::Result<void> status = apex::DeactivatePackage(package_path);
Bernie Innocenti018138f2020-02-06 21:39:04 +0900104 if (!status.ok()) {
Mohammad Samiul Islam7dcfd342019-06-25 10:25:30 +0100105 LOG(ERROR) << "Failed to deactivate " << package_path << ": "
106 << status.error();
Roland Levillain6520cca2019-02-01 13:15:58 +0000107 }
108 }
109}
110
Andreas Gampe54b62c92019-03-21 11:34:19 -0700111static void TryExtraMount(const char* name, const char* slot, const char* target) {
Andreas Gampee90127d2019-03-22 11:21:34 -0700112 std::string partition_name = StringPrintf("%s%s", name, slot);
113
114 // See whether update_engine mounted a logical partition.
115 {
116 auto& dm = dm::DeviceMapper::Instance();
117 if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
118 std::string path;
119 if (dm.GetDmDevicePathByName(partition_name, &path)) {
120 int mount_result = mount(path.c_str(),
121 target,
122 "ext4",
123 MS_RDONLY,
124 /* data */ nullptr);
125 if (mount_result == 0) {
126 return;
127 }
128 }
129 }
130 }
131
132 // Fall back and attempt a direct mount.
133 std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
Andreas Gampe54b62c92019-03-21 11:34:19 -0700134 int mount_result = mount(block_device.c_str(),
135 target,
136 "ext4",
137 MS_RDONLY,
138 /* data */ nullptr);
139 UNUSED(mount_result);
140}
141
Andreas Gamped089ca12016-06-27 14:25:30 -0700142// Entry for otapreopt_chroot. Expected parameters are:
143// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
144// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
145// be passed on to otapreopt in the chroot.
Andreas Gampe01ad5982016-03-09 16:27:29 -0800146static int otapreopt_chroot(const int argc, char **arg) {
Zach Riggle318853a2018-01-18 18:34:04 -0600147 // Validate arguments
148 // We need the command, status channel and target slot, at a minimum.
149 if(argc < 3) {
150 PLOG(ERROR) << "Not enough arguments.";
151 exit(208);
152 }
Andreas Gamped089ca12016-06-27 14:25:30 -0700153 // Close all file descriptors. They are coming from the caller, we do not want to pass them
154 // on across our fork/exec into a different domain.
155 // 1) Default descriptors.
156 CloseDescriptor(STDIN_FILENO);
157 CloseDescriptor(STDOUT_FILENO);
158 CloseDescriptor(STDERR_FILENO);
159 // 2) The status channel.
160 CloseDescriptor(arg[1]);
161
Andreas Gampe01ad5982016-03-09 16:27:29 -0800162 // We need to run the otapreopt tool from the postinstall partition. As such, set up a
163 // mount namespace and change root.
164
165 // Create our own mount namespace.
166 if (unshare(CLONE_NEWNS) != 0) {
167 PLOG(ERROR) << "Failed to unshare() for otapreopt.";
168 exit(200);
169 }
170
171 // Make postinstall private, so that our changes don't propagate.
172 if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) {
173 PLOG(ERROR) << "Failed to mount private.";
174 exit(201);
175 }
176
177 // Bind mount necessary directories.
178 constexpr const char* kBindMounts[] = {
179 "/data", "/dev", "/proc", "/sys"
180 };
181 for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
182 std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
183 if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) {
184 PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i];
185 exit(202);
186 }
187 }
188
Andreas Gampefd12eda2016-07-12 09:47:17 -0700189 // Try to mount the vendor partition. update_engine doesn't do this for us, but we
190 // want it for vendor APKs.
191 // Notes:
192 // 1) We pretty much guess a name here and hope to find the partition by name.
193 // It is just as complicated and brittle to scan /proc/mounts. But this requires
194 // validating the target-slot so as not to try to mount some totally random path.
195 // 2) We're in a mount namespace here, so when we die, this will be cleaned up.
196 // 3) Ignore errors. Printing anything at this stage will open a file descriptor
197 // for logging.
198 if (!ValidateTargetSlotSuffix(arg[2])) {
199 LOG(ERROR) << "Target slot suffix not legal: " << arg[2];
200 exit(207);
201 }
Andreas Gampe54b62c92019-03-21 11:34:19 -0700202 TryExtraMount("vendor", arg[2], "/postinstall/vendor");
Andreas Gampeb87a1c72018-04-13 17:42:23 -0700203
204 // Try to mount the product partition. update_engine doesn't do this for us, but we
205 // want it for product APKs. Same notes as vendor above.
Andreas Gampe54b62c92019-03-21 11:34:19 -0700206 TryExtraMount("product", arg[2], "/postinstall/product");
Andreas Gampefd12eda2016-07-12 09:47:17 -0700207
Alex Light2b307a02021-03-03 18:35:47 -0800208 constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
209 // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
210 if (mount("tmpfs", kPostInstallLinkerconfig, "tmpfs", 0, nullptr) != 0) {
211 PLOG(ERROR) << "Failed to mount a tmpfs for " << kPostInstallLinkerconfig;
212 exit(215);
213 }
214
Roland Levillainc19c6042018-12-18 12:15:12 +0000215 // Setup APEX mount point and its security context.
216 static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
217 // The following logic is similar to the one in system/core/rootdir/init.rc:
218 //
219 // mount tmpfs tmpfs /apex nodev noexec nosuid
220 // chmod 0755 /apex
221 // chown root root /apex
222 // restorecon /apex
223 //
Roland Levillain8d276812019-01-24 10:51:30 +0000224 // except we perform the `restorecon` step just after mounting the tmpfs
225 // filesystem in /postinstall/apex, so that this directory is correctly
226 // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in
227 // following operations (`chmod`, `chown`, etc.) following policies
228 // restricted to `postinstall_apex_mnt_dir`:
229 //
230 // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid
231 // restorecon /postinstall/apex
232 // chmod 0755 /postinstall/apex
233 // chown root root /postinstall/apex
234 //
Roland Levillainc19c6042018-12-18 12:15:12 +0000235 if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
236 != 0) {
237 PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
238 exit(209);
239 }
Roland Levillain8d276812019-01-24 10:51:30 +0000240 if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
241 PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
242 exit(214);
243 }
Roland Levillainc19c6042018-12-18 12:15:12 +0000244 if (chmod(kPostinstallApexDir, 0755) != 0) {
245 PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755";
246 exit(210);
247 }
248 if (chown(kPostinstallApexDir, 0, 0) != 0) {
249 PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
250 exit(211);
251 }
Roland Levillainc19c6042018-12-18 12:15:12 +0000252
Andreas Gampe01ad5982016-03-09 16:27:29 -0800253 // Chdir into /postinstall.
254 if (chdir("/postinstall") != 0) {
255 PLOG(ERROR) << "Unable to chdir into /postinstall.";
256 exit(203);
257 }
258
259 // Make /postinstall the root in our mount namespace.
260 if (chroot(".") != 0) {
261 PLOG(ERROR) << "Failed to chroot";
262 exit(204);
263 }
264
265 if (chdir("/") != 0) {
266 PLOG(ERROR) << "Unable to chdir into /.";
267 exit(205);
268 }
269
Roland Levillainc19c6042018-12-18 12:15:12 +0000270 // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
Martin Stjernholmf4caaa02019-07-17 22:14:14 +0100271 // the ART APEX, as it is required by otapreopt to run dex2oat.
Roland Levillain6520cca2019-02-01 13:15:58 +0000272 std::vector<apex::ApexFile> active_packages = ActivateApexPackages();
Alex Lightcf7e6de2021-03-04 10:20:18 -0800273 CreateApexInfoList(active_packages);
Roland Levillainc19c6042018-12-18 12:15:12 +0000274
Martin Stjernholmf4caaa02019-07-17 22:14:14 +0100275 // Check that an ART APEX has been activated; clean up and exit
Roland Levillain8a8ca152019-07-11 18:48:05 +0100276 // early otherwise.
Alex Light2b307a02021-03-03 18:35:47 -0800277 static constexpr const std::string_view kRequiredApexs[] = {
278 "com.android.art",
279 "com.android.runtime",
280 };
281 for (std::string_view apex : kRequiredApexs) {
282 if (std::none_of(active_packages.begin(), active_packages.end(),
283 [&](const apex::ApexFile& package) {
284 return package.GetManifest().name() == apex;
285 })) {
286 LOG(FATAL_WITHOUT_ABORT) << "No activated " << apex << " APEX package.";
287 DeactivateApexPackages(active_packages);
288 exit(217);
289 }
290 }
291
292 // Setup /linkerconfig. Doing it after the chroot means it doesn't need its own category
293 if (selinux_android_restorecon("/linkerconfig", 0) < 0) {
294 PLOG(ERROR) << "Failed to restorecon /linkerconfig";
295 exit(219);
296 }
297 std::vector<std::string> linkerconfig_cmd{"/apex/com.android.runtime/bin/linkerconfig",
298 "--target", "/linkerconfig"};
299 std::string linkerconfig_error_msg;
300 bool linkerconfig_exec_result = Exec(linkerconfig_cmd, &linkerconfig_error_msg);
301 if (!linkerconfig_exec_result) {
302 LOG(ERROR) << "Running linkerconfig failed: " << linkerconfig_error_msg;
303 exit(218);
Roland Levillain8a8ca152019-07-11 18:48:05 +0100304 }
305
Andreas Gampe01ad5982016-03-09 16:27:29 -0800306 // Now go on and run otapreopt.
307
Roland Levillain94b41802019-01-18 11:56:50 +0000308 // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc
309 // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1
310 std::vector<std::string> cmd;
311 cmd.reserve(argc);
312 cmd.push_back("/system/bin/otapreopt");
Andreas Gamped089ca12016-06-27 14:25:30 -0700313
314 // The first parameter is the status file descriptor, skip.
Roland Levillain94b41802019-01-18 11:56:50 +0000315 for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
316 cmd.push_back(arg[i]);
Andreas Gamped089ca12016-06-27 14:25:30 -0700317 }
Andreas Gampe01ad5982016-03-09 16:27:29 -0800318
Roland Levillain94b41802019-01-18 11:56:50 +0000319 // Fork and execute otapreopt in its own process.
320 std::string error_msg;
321 bool exec_result = Exec(cmd, &error_msg);
322 if (!exec_result) {
323 LOG(ERROR) << "Running otapreopt failed: " << error_msg;
324 }
325
Roland Levillain6520cca2019-02-01 13:15:58 +0000326 // Tear down the work down by the apexd logic. (i.e. deactivate packages).
327 DeactivateApexPackages(active_packages);
Roland Levillain94b41802019-01-18 11:56:50 +0000328
329 if (!exec_result) {
330 exit(213);
331 }
332
333 return 0;
Andreas Gampe01ad5982016-03-09 16:27:29 -0800334}
335
336} // namespace installd
337} // namespace android
338
339int main(const int argc, char *argv[]) {
340 return android::installd::otapreopt_chroot(argc, argv);
341}