1 /*
2 * Copyright (C) 2018 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
17 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
18
19 #include "apexd.h"
20
21 #include <ApexProperties.sysprop.h>
22 #include <android-base/chrono_utils.h>
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <android-base/macros.h>
26 #include <android-base/parseint.h>
27 #include <android-base/properties.h>
28 #include <android-base/scopeguard.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31 #include <android-base/thread_annotations.h>
32 #include <android-base/unique_fd.h>
33 #include <dirent.h>
34 #include <fcntl.h>
35 #include <google/protobuf/util/message_differencer.h>
36 #include <libavb/libavb.h>
37 #include <libdm/dm.h>
38 #include <libdm/dm_table.h>
39 #include <libdm/dm_target.h>
40 #include <linux/f2fs.h>
41 #include <linux/loop.h>
42 #include <selinux/android.h>
43 #include <stdlib.h>
44 #include <sys/inotify.h>
45 #include <sys/ioctl.h>
46 #include <sys/mount.h>
47 #include <sys/stat.h>
48 #include <sys/sysinfo.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <utils/Trace.h>
52
53 #include <algorithm>
54 #include <array>
55 #include <chrono>
56 #include <cstdlib>
57 #include <filesystem>
58 #include <fstream>
59 #include <future>
60 #include <iomanip>
61 #include <iterator>
62 #include <memory>
63 #include <mutex>
64 #include <optional>
65 #include <queue>
66 #include <ranges>
67 #include <sstream>
68 #include <string>
69 #include <string_view>
70 #include <thread>
71 #include <unordered_map>
72 #include <unordered_set>
73
74 #include "VerityUtils.h"
75 #include "apex_constants.h"
76 #include "apex_database.h"
77 #include "apex_file.h"
78 #include "apex_file_repository.h"
79 #include "apex_manifest.h"
80 #include "apex_sha.h"
81 #include "apex_shim.h"
82 #include "apexd_brand_new_verifier.h"
83 #include "apexd_checkpoint.h"
84 #include "apexd_dm.h"
85 #include "apexd_image_manager.h"
86 #include "apexd_lifecycle.h"
87 #include "apexd_loop.h"
88 #include "apexd_metrics.h"
89 #include "apexd_private.h"
90 #include "apexd_rollback_utils.h"
91 #include "apexd_session.h"
92 #include "apexd_utils.h"
93 #include "apexd_vendor_apex.h"
94 #include "apexd_verity.h"
95 #include "com_android_apex.h"
96
97 using android::base::boot_clock;
98 using android::base::ConsumePrefix;
99 using android::base::ErrnoError;
100 using android::base::Error;
101 using android::base::GetProperty;
102 using android::base::Join;
103 using android::base::ParseUint;
104 using android::base::RemoveFileIfExists;
105 using android::base::Result;
106 using android::base::SetProperty;
107 using android::base::StartsWith;
108 using android::base::StringPrintf;
109 using android::base::unique_fd;
110 using android::dm::DeviceMapper;
111 using android::dm::DmDeviceState;
112 using android::dm::DmTable;
113 using android::dm::DmTargetVerity;
114 using ::apex::proto::ApexManifest;
115 using apex::proto::SessionState;
116 using google::protobuf::util::MessageDifferencer;
117
118 namespace android {
119 namespace apex {
120
121 using MountedApexData = MountedApexDatabase::MountedApexData;
122 Result<std::vector<ApexFile>> OpenApexFilesInSessionDirs(
123 int session_id, const std::vector<int>& child_session_ids);
124
125 Result<std::vector<std::string>> StagePackagesImpl(
126 const std::vector<std::string>& tmp_paths);
127
128 namespace {
129
130 static constexpr const char* kBuildFingerprintSysprop = "ro.build.fingerprint";
131
132 // This should be in UAPI, but it's not :-(
133 static constexpr const char* kDmVerityRestartOnCorruption =
134 "restart_on_corruption";
135
136 MountedApexDatabase gMountedApexes;
137
138 // Can be set by SetConfig()
139 std::optional<ApexdConfig> gConfig;
140
141 // Set by InitializeSessionManager
142 ApexSessionManager* gSessionManager;
143
144 CheckpointInterface* gVoldService;
145 bool gSupportsFsCheckpoints = false;
146 bool gInFsCheckpointMode = false;
147
148 // Process-wise global mutex to serialize install/staging functions:
149 // - submitStagedSession
150 // - markStagedSessionReady
151 // - installAndActivatePackage
152 // This is to ensure that there's no overlapping between install/staging.
153 // To be specific, we don't want to perform verification when there's a
154 // VERIFIED session, which is not yet fully staged.
155 struct Mutex : std::mutex {
operator !android::apex::__anon90848c0a0111::Mutex156 const Mutex& operator!() const { return *this; } // for negative capability
157 } gInstallLock;
158
159 // APEXEs for which a different version was activated than in the previous boot.
160 // This can happen in the following scenarios:
161 // 1. This APEX is part of the staged session that was applied during this
162 // boot.
163 // 2. This is a compressed APEX that was decompressed during this boot.
164 // 3. We failed to activate APEX from /data/apex/active and fallback to the
165 // pre-installed APEX.
166 std::set<std::string> gChangedActiveApexes;
167
168 static constexpr size_t kLoopDeviceSetupAttempts = 3u;
169
170 // Please DO NOT add new modules to this list without contacting
171 // mainline-modularization@ first.
__anon90848c0a0202() 172 static const std::vector<std::string> kBootstrapApexes = ([]() {
173 std::vector<std::string> ret = {
174 "com.android.i18n",
175 "com.android.runtime",
176 "com.android.tzdata",
177 #ifdef RELEASE_AVF_ENABLE_EARLY_VM
178 "com.android.virt",
179 #endif
180 };
181
182 auto vendor_vndk_ver = GetProperty("ro.vndk.version", "");
183 if (vendor_vndk_ver != "") {
184 ret.push_back("com.android.vndk.v" + vendor_vndk_ver);
185 }
186 auto product_vndk_ver = GetProperty("ro.product.vndk.version", "");
187 if (product_vndk_ver != "" && product_vndk_ver != vendor_vndk_ver) {
188 ret.push_back("com.android.vndk.v" + product_vndk_ver);
189 }
190 return ret;
191 })();
192
193 static constexpr const int kNumRetriesWhenCheckpointingEnabled = 1;
194
IsBootstrapApex(const ApexFile & apex)195 bool IsBootstrapApex(const ApexFile& apex) {
196 static std::vector<std::string> additional = []() {
197 std::vector<std::string> ret;
198 if (android::base::GetBoolProperty("ro.boot.apex.early_adbd", false)) {
199 ret.push_back("com.android.adbd");
200 }
201 return ret;
202 }();
203
204 if (apex.GetManifest().vendorbootstrap() || apex.GetManifest().bootstrap()) {
205 return true;
206 }
207
208 return std::find(kBootstrapApexes.begin(), kBootstrapApexes.end(),
209 apex.GetManifest().name()) != kBootstrapApexes.end() ||
210 std::find(additional.begin(), additional.end(),
211 apex.GetManifest().name()) != additional.end();
212 }
213
ReleaseF2fsCompressedBlocks(const std::string & file_path)214 void ReleaseF2fsCompressedBlocks(const std::string& file_path) {
215 unique_fd fd(
216 TEMP_FAILURE_RETRY(open(file_path.c_str(), O_RDONLY | O_CLOEXEC, 0)));
217 if (fd.get() == -1) {
218 PLOG(ERROR) << "Failed to open " << file_path;
219 return;
220 }
221 unsigned int flags;
222 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
223 PLOG(ERROR) << "Failed to call FS_IOC_GETFLAGS on " << file_path;
224 return;
225 }
226 if ((flags & FS_COMPR_FL) == 0) {
227 // Doesn't support f2fs-compression.
228 return;
229 }
230 uint64_t blk_cnt;
231 if (ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blk_cnt) == -1) {
232 PLOG(ERROR) << "Failed to call F2FS_IOC_RELEASE_COMPRESS_BLOCKS on "
233 << file_path;
234 }
235 LOG(INFO) << "Released " << blk_cnt << " compressed blocks from "
236 << file_path;
237 }
238
CreateVerityTable(const ApexVerityData & verity_data,const std::string & block_device,bool restart_on_corruption)239 std::unique_ptr<DmTable> CreateVerityTable(const ApexVerityData& verity_data,
240 const std::string& block_device,
241 bool restart_on_corruption) {
242 AvbHashtreeDescriptor* desc = verity_data.desc.get();
243 auto table = std::make_unique<DmTable>();
244
245 const uint64_t start = 0;
246 const uint64_t length = desc->image_size / 512; // in sectors
247
248 const std::string& hash_device = block_device;
249 const uint32_t num_data_blocks = desc->image_size / desc->data_block_size;
250 const uint32_t hash_start_block = desc->tree_offset / desc->hash_block_size;
251
252 auto target = std::make_unique<DmTargetVerity>(
253 start, length, desc->dm_verity_version, block_device, hash_device,
254 desc->data_block_size, desc->hash_block_size, num_data_blocks,
255 hash_start_block, verity_data.hash_algorithm, verity_data.root_digest,
256 verity_data.salt);
257
258 target->IgnoreZeroBlocks();
259 if (restart_on_corruption) {
260 target->SetVerityMode(kDmVerityRestartOnCorruption);
261 }
262 table->AddTarget(std::move(target));
263
264 table->set_readonly(true);
265
266 return table;
267 };
268
269 /**
270 * When we create hardlink for a new apex package in kActiveApexPackagesDataDir,
271 * there might be an older version of the same package already present in there.
272 * Since a new version of the same package is being installed on this boot, the
273 * old one needs to deleted so that we don't end up activating same package
274 * twice.
275 *
276 * @param affected_packages package names of the news apex that are being
277 * installed in this boot
278 * @param files_to_keep path to the new apex packages in
279 * kActiveApexPackagesDataDir
280 */
RemovePreviouslyActiveApexFiles(const std::vector<std::string> & affected_packages,const std::vector<std::string> & files_to_keep)281 Result<void> RemovePreviouslyActiveApexFiles(
282 const std::vector<std::string>& affected_packages,
283 const std::vector<std::string>& files_to_keep) {
284 auto all_active_apex_files =
285 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
286
287 if (!all_active_apex_files.ok()) {
288 return all_active_apex_files.error();
289 }
290
291 for (const std::string& path : *all_active_apex_files) {
292 if (std::ranges::contains(files_to_keep, path)) {
293 // This is a path that was staged and should be kept.
294 continue;
295 }
296
297 Result<ApexFile> apex_file = ApexFile::Open(path);
298 if (!apex_file.ok()) {
299 return apex_file.error();
300 }
301 const std::string& package_name = apex_file->GetManifest().name();
302 if (!std::ranges::contains(affected_packages, package_name)) {
303 // This apex belongs to a package that wasn't part of this stage sessions,
304 // hence it should be kept.
305 continue;
306 }
307
308 LOG(DEBUG) << "Deleting previously active apex " << apex_file->GetPath();
309 if (unlink(apex_file->GetPath().c_str()) != 0) {
310 return ErrnoError() << "Failed to unlink " << apex_file->GetPath();
311 }
312 }
313
314 return {};
315 }
316
317 // Reads the entire device to verify the image is authenticatic
ReadVerityDevice(const std::string & verity_device,uint64_t device_size)318 Result<void> ReadVerityDevice(const std::string& verity_device,
319 uint64_t device_size) {
320 static constexpr int kBlockSize = 4096;
321 static constexpr size_t kBufSize = 1024 * kBlockSize;
322 std::vector<uint8_t> buffer(kBufSize);
323
324 unique_fd fd(
325 TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY | O_CLOEXEC)));
326 if (fd.get() == -1) {
327 return ErrnoError() << "Can't open " << verity_device;
328 }
329
330 size_t bytes_left = device_size;
331 while (bytes_left > 0) {
332 size_t to_read = std::min(bytes_left, kBufSize);
333 if (!android::base::ReadFully(fd.get(), buffer.data(), to_read)) {
334 return ErrnoError() << "Can't verify " << verity_device << "; corrupted?";
335 }
336 bytes_left -= to_read;
337 }
338
339 return {};
340 }
341
VerifyMountedImage(const ApexFile & apex,const std::string & mount_point)342 Result<void> VerifyMountedImage(const ApexFile& apex,
343 const std::string& mount_point) {
344 // Verify that apex_manifest.pb inside mounted image matches the one in the
345 // outer .apex container.
346 Result<ApexManifest> verified_manifest =
347 ReadManifest(mount_point + "/" + kManifestFilenamePb);
348 if (!verified_manifest.ok()) {
349 return verified_manifest.error();
350 }
351 if (!MessageDifferencer::Equals(*verified_manifest, apex.GetManifest())) {
352 return Errorf(
353 "Manifest inside filesystem does not match manifest outside it");
354 }
355 if (shim::IsShimApex(apex)) {
356 return shim::ValidateShimApex(mount_point, apex);
357 }
358 return {};
359 }
360
MountPackageImpl(const ApexFile & apex,const std::string & mount_point,const std::string & device_name,bool verify_image,bool reuse_device)361 Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
362 const std::string& mount_point,
363 const std::string& device_name,
364 bool verify_image, bool reuse_device) {
365 auto tag = "MountPackageImpl: " + apex.GetManifest().name();
366 ATRACE_NAME(tag.c_str());
367 if (apex.IsCompressed()) {
368 return Error() << "Cannot directly mount compressed APEX "
369 << apex.GetPath();
370 }
371
372 LOG(VERBOSE) << "Creating mount point: " << mount_point;
373 auto time_started = boot_clock::now();
374 // Note: the mount point could exist in case when the APEX was activated
375 // during the bootstrap phase (e.g., the runtime or tzdata APEX).
376 // Although we have separate mount namespaces to separate the early activated
377 // APEXes from the normally activate APEXes, the mount points themselves
378 // are shared across the two mount namespaces because /apex (a tmpfs) itself
379 // mounted at / which is (and has to be) a shared mount. Therefore, if apexd
380 // finds an empty directory under /apex, it's not a problem and apexd can use
381 // it.
382 auto exists = PathExists(mount_point);
383 if (!exists.ok()) {
384 return exists.error();
385 }
386 if (!*exists && mkdir(mount_point.c_str(), kMkdirMode) != 0) {
387 return ErrnoError() << "Could not create mount point " << mount_point;
388 }
389 auto deleter = [&mount_point]() {
390 if (rmdir(mount_point.c_str()) != 0) {
391 PLOG(WARNING) << "Could not rmdir " << mount_point;
392 }
393 };
394 auto scope_guard = android::base::make_scope_guard(deleter);
395 if (!IsEmptyDirectory(mount_point)) {
396 return ErrnoError() << mount_point << " is not empty";
397 }
398
399 const std::string& full_path = apex.GetPath();
400
401 if (!apex.GetImageOffset() || !apex.GetImageSize()) {
402 return Error() << "Cannot create mount point without image offset and size";
403 }
404 loop::LoopbackDeviceUniqueFd loopback_device;
405 for (size_t attempts = 1;; ++attempts) {
406 Result<loop::LoopbackDeviceUniqueFd> ret =
407 loop::CreateAndConfigureLoopDevice(full_path,
408 apex.GetImageOffset().value(),
409 apex.GetImageSize().value());
410 if (ret.ok()) {
411 loopback_device = std::move(*ret);
412 break;
413 }
414 if (attempts >= kLoopDeviceSetupAttempts) {
415 return Error() << "Could not create loop device for " << full_path << ": "
416 << ret.error();
417 }
418 }
419 LOG(VERBOSE) << "Loopback device created: " << loopback_device.name;
420
421 auto verity_data = apex.VerifyApexVerity(apex.GetBundledPublicKey());
422 if (!verity_data.ok()) {
423 return Error() << "Failed to verify Apex Verity data for " << full_path
424 << ": " << verity_data.error();
425 }
426
427 auto& instance = ApexFileRepository::GetInstance();
428 if (instance.IsBlockApex(apex)) {
429 auto root_digest = instance.GetBlockApexRootDigest(apex.GetPath());
430 if (root_digest.has_value() &&
431 root_digest.value() != verity_data->root_digest) {
432 return Error() << "Failed to verify Apex Verity data for " << full_path
433 << ": root digest (" << verity_data->root_digest
434 << ") mismatches with the one (" << root_digest.value()
435 << ") specified in config";
436 }
437 }
438
439 std::string block_device = loopback_device.name;
440 MountedApexData apex_data(apex.GetManifest().version(), loopback_device.name,
441 apex.GetPath(), mount_point,
442 /* device_name = */ "");
443
444 // for APEXes in immutable partitions, we don't need to mount them on
445 // dm-verity because they are already in the dm-verity protected partition;
446 // system. However, note that we don't skip verification to ensure that APEXes
447 // are correctly signed.
448 const bool mount_on_verity = !instance.IsPreInstalledApex(apex) ||
449 // decompressed apexes are on /data
450 instance.IsDecompressedApex(apex) ||
451 // block apexes are from host
452 instance.IsBlockApex(apex);
453
454 DmDevice verity_dev;
455 if (mount_on_verity) {
456 auto verity_table =
457 CreateVerityTable(*verity_data, loopback_device.name,
458 /* restart_on_corruption = */ !verify_image);
459 Result<DmDevice> verity_dev_res =
460 CreateDmDevice(device_name, *verity_table, reuse_device);
461 if (!verity_dev_res.ok()) {
462 return Error() << "Failed to create Apex Verity device " << full_path
463 << ": " << verity_dev_res.error();
464 }
465 verity_dev = std::move(*verity_dev_res);
466 apex_data.device_name = device_name;
467 block_device = verity_dev.GetDevPath();
468
469 Result<void> read_ahead_status =
470 loop::ConfigureReadAhead(verity_dev.GetDevPath());
471 if (!read_ahead_status.ok()) {
472 return read_ahead_status.error();
473 }
474 }
475 // TODO(b/158467418): consider moving this inside RunVerifyFnInsideTempMount.
476 if (mount_on_verity && verify_image) {
477 Result<void> verity_status =
478 ReadVerityDevice(block_device, (*verity_data).desc->image_size);
479 if (!verity_status.ok()) {
480 return verity_status.error();
481 }
482 }
483
484 uint32_t mount_flags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
485 if (apex.GetManifest().nocode()) {
486 mount_flags |= MS_NOEXEC;
487 }
488
489 if (!apex.GetFsType()) {
490 return Error() << "Cannot mount package without FsType";
491 }
492 if (mount(block_device.c_str(), mount_point.c_str(),
493 apex.GetFsType().value().c_str(), mount_flags, nullptr) == 0) {
494 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
495 boot_clock::now() - time_started)
496 .count();
497 LOG(INFO) << "Successfully mounted package " << full_path << " on "
498 << mount_point << " duration=" << time_elapsed;
499 auto status = VerifyMountedImage(apex, mount_point);
500 if (!status.ok()) {
501 if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) {
502 PLOG(ERROR) << "Failed to umount " << mount_point;
503 }
504 return Error() << "Failed to verify " << full_path << ": "
505 << status.error();
506 }
507 // Time to accept the temporaries as good.
508 verity_dev.Release();
509 loopback_device.CloseGood();
510
511 scope_guard.Disable(); // Accept the mount.
512 return apex_data;
513 } else {
514 return ErrnoError() << "Mounting failed for package " << full_path;
515 }
516 }
517
IsMountBeforeDataEnabled()518 bool IsMountBeforeDataEnabled() { return gConfig->mount_before_data; }
519
520 } // namespace
521
Unmount(const MountedApexData & data,bool deferred)522 Result<void> Unmount(const MountedApexData& data, bool deferred) {
523 LOG(DEBUG) << "Unmounting " << data.full_path << " from mount point "
524 << data.mount_point << " deferred = " << deferred;
525 // Unmount whatever is mounted.
526 if (umount2(data.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0 &&
527 errno != EINVAL && errno != ENOENT) {
528 return ErrnoError() << "Failed to unmount directory " << data.mount_point;
529 }
530
531 if (!deferred) {
532 if (rmdir(data.mount_point.c_str()) != 0) {
533 PLOG(ERROR) << "Failed to rmdir " << data.mount_point;
534 }
535 }
536
537 // Try to free up the device-mapper device.
538 if (!data.device_name.empty()) {
539 const auto& result = DeleteDmDevice(data.device_name, deferred);
540 if (!result.ok()) {
541 return result;
542 }
543 }
544
545 // Try to free up the loop device.
546 auto log_fn = [](const std::string& path, const std::string& /*id*/) {
547 LOG(VERBOSE) << "Freeing loop device " << path << " for unmount.";
548 };
549
550 // Since we now use LO_FLAGS_AUTOCLEAR when configuring loop devices, in
551 // theory we don't need to manually call DestroyLoopDevice here even if
552 // |deferred| is false. However we prefer to call it to ensure the invariant
553 // of SubmitStagedSession (after it's done, loop devices created for temp
554 // mount are freed).
555 if (!data.loop_name.empty() && !deferred) {
556 loop::DestroyLoopDevice(data.loop_name, log_fn);
557 }
558
559 return {};
560 }
561
562 namespace {
563
RunVerifyFnInsideTempMounts(std::span<const ApexFile> apex_files,auto verify_fn)564 auto RunVerifyFnInsideTempMounts(std::span<const ApexFile> apex_files,
565 auto verify_fn)
566 -> decltype(verify_fn(std::vector<std::string>{})) {
567 // Temp mounts will be cleaned up on exit.
568 std::vector<MountedApexData> mounted_data;
__anon90848c0a0702() 569 auto guard = android::base::make_scope_guard([&]() {
570 for (const auto& data : mounted_data) {
571 if (auto result = Unmount(data, /*deferred=*/false); !result.ok()) {
572 LOG(WARNING) << "Failed to unmount " << data.mount_point << ": "
573 << result.error();
574 }
575 }
576 });
577
578 // Temp mounts all apexes.
579 // This will also read the entire block device for each apex,
580 // so we can be sure there is no corruption.
581 std::vector<std::string> mount_points;
582 for (const auto& apex : apex_files) {
583 auto mount_point =
584 apexd_private::GetPackageTempMountPoint(apex.GetManifest());
585 auto package_id = GetPackageId(apex.GetManifest());
586 auto device_name = package_id + ".tmp";
587
588 LOG(DEBUG) << "Temp mounting " << package_id << " to " << mount_point;
589 auto data = OR_RETURN(MountPackageImpl(apex, mount_point, device_name,
590 /*verify_image=*/true,
591 /*reuse_device=*/false));
592 mount_points.push_back(mount_point);
593 mounted_data.push_back(data);
594 }
595
596 // Invoke fn with mount_points.
597 return verify_fn(mount_points);
598 }
599
600 // Singluar variant of RunVerifyFnInsideTempMounts for convenience
RunVerifyFnInsideTempMount(const ApexFile & apex,auto verify_fn)601 auto RunVerifyFnInsideTempMount(const ApexFile& apex, auto verify_fn)
602 -> decltype(verify_fn(std::string{})) {
603 return RunVerifyFnInsideTempMounts(
604 Single(apex),
__anon90848c0a0802(const auto& mount_points) 605 [&](const auto& mount_points) { return verify_fn(mount_points[0]); });
606 }
607
608 // Converts a list of apex file paths into a list of ApexFile objects
609 //
610 // Returns error when trying to open empty set of inputs.
OpenApexFiles(const std::vector<std::string> & paths)611 Result<std::vector<ApexFile>> OpenApexFiles(
612 const std::vector<std::string>& paths) {
613 if (paths.empty()) {
614 return Errorf("Empty set of inputs");
615 }
616 std::vector<ApexFile> ret;
617 for (const std::string& path : paths) {
618 Result<ApexFile> apex_file = ApexFile::Open(path);
619 if (!apex_file.ok()) {
620 return apex_file.error();
621 }
622 ret.emplace_back(std::move(*apex_file));
623 }
624 return ret;
625 }
626
ValidateStagingShimApex(const ApexFile & to)627 Result<void> ValidateStagingShimApex(const ApexFile& to) {
628 using android::base::StringPrintf;
629 auto system_shim = ApexFile::Open(
630 StringPrintf("%s/%s", kApexPackageSystemDir, shim::kSystemShimApexName));
631 if (!system_shim.ok()) {
632 return system_shim.error();
633 }
634 auto verify_fn = [&](const std::string& system_apex_path) {
635 return shim::ValidateUpdate(system_apex_path, to.GetPath());
636 };
637 return RunVerifyFnInsideTempMount(*system_shim, verify_fn);
638 }
639
VerifyVndkVersion(const ApexFile & apex_file)640 Result<void> VerifyVndkVersion(const ApexFile& apex_file) {
641 const std::string& vndk_version = apex_file.GetManifest().vndkversion();
642 if (vndk_version.empty()) {
643 return {};
644 }
645
646 static std::string vendor_vndk_version = GetProperty("ro.vndk.version", "");
647 static std::string product_vndk_version =
648 GetProperty("ro.product.vndk.version", "");
649
650 const auto& instance = ApexFileRepository::GetInstance();
651 const auto& partition = OR_RETURN(instance.GetPartition(apex_file));
652 if (partition == ApexPartition::Vendor || partition == ApexPartition::Odm) {
653 if (vndk_version != vendor_vndk_version) {
654 return Error() << "vndkVersion(" << vndk_version
655 << ") doesn't match with device VNDK version("
656 << vendor_vndk_version << ")";
657 }
658 return {};
659 }
660 if (partition == ApexPartition::Product) {
661 if (vndk_version != product_vndk_version) {
662 return Error() << "vndkVersion(" << vndk_version
663 << ") doesn't match with device VNDK version("
664 << product_vndk_version << ")";
665 }
666 return {};
667 }
668 return Error() << "vndkVersion(" << vndk_version << ") is set";
669 }
670
671 // A version of apex verification that happens during boot.
672 // This function should only verification checks that are necessary to run on
673 // each boot. Try to avoid putting expensive checks inside this function.
VerifyPackageBoot(const ApexFile & apex_file)674 Result<void> VerifyPackageBoot(const ApexFile& apex_file) {
675 // TODO(ioffe): why do we need this here?
676 const auto& public_key =
677 OR_RETURN(apexd_private::GetVerifiedPublicKey(apex_file));
678 Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(public_key);
679 if (!verity_or.ok()) {
680 return verity_or.error();
681 }
682
683 if (shim::IsShimApex(apex_file)) {
684 // Validating shim is not a very cheap operation, but it's fine to perform
685 // it here since it only runs during CTS tests and will never be triggered
686 // during normal flow.
687 const auto& result = ValidateStagingShimApex(apex_file);
688 if (!result.ok()) {
689 return result;
690 }
691 }
692
693 if (auto result = VerifyVndkVersion(apex_file); !result.ok()) {
694 return result;
695 }
696
697 return {};
698 }
699
VerifyNoOverlapInSessions(std::span<const ApexFile> apex_files,std::span<const ApexSession> sessions)700 Result<void> VerifyNoOverlapInSessions(std::span<const ApexFile> apex_files,
701 std::span<const ApexSession> sessions) {
702 for (const auto& session : sessions) {
703 // We don't want to install/stage while another session is being staged.
704 if (session.GetState() == SessionState::VERIFIED) {
705 return Error() << "Session " << session.GetId() << " is being staged.";
706 }
707
708 // We don't want to install/stage if the same package is already staged.
709 if (session.GetState() == SessionState::STAGED) {
710 for (const auto& apex : apex_files) {
711 if (std::ranges::contains(session.GetApexNames(),
712 apex.GetManifest().name())) {
713 return Error() << "APEX " << apex.GetManifest().name()
714 << " is already staged by session " << session.GetId()
715 << ".";
716 }
717 }
718 }
719 }
720 return {}; // okay
721 }
722
723 struct VerificationResult {
724 std::map<std::string, std::vector<std::string>> apex_hals;
725 };
726
727 // A version of apex verification that happens on SubmitStagedSession.
728 // This function contains checks that might be expensive to perform, e.g. temp
729 // mounting a package and reading entire dm-verity device, and shouldn't be run
730 // during boot.
VerifyPackagesStagedInstall(const std::vector<ApexFile> & apex_files)731 Result<VerificationResult> VerifyPackagesStagedInstall(
732 const std::vector<ApexFile>& apex_files) {
733 for (const auto& apex_file : apex_files) {
734 OR_RETURN(VerifyPackageBoot(apex_file));
735
736 // Extra verification for brand-new APEX. The case that brand-new APEX is
737 // not enabled when there is install request for brand-new APEX is already
738 // covered in |VerifyPackageBoot|.
739 if (ApexFileRepository::IsBrandNewApexEnabled()) {
740 OR_RETURN(VerifyBrandNewPackageAgainstActive(apex_file));
741 }
742 }
743
744 auto sessions = gSessionManager->GetSessions();
745
746 // Check overlapping: reject if the same package is already staged
747 // or if there's a session being staged.
748 OR_RETURN(VerifyNoOverlapInSessions(apex_files, sessions));
749
750 // Since there can be multiple staged sessions, let's verify incoming APEXes
751 // with all staged apexes mounted.
752 std::vector<ApexFile> all_apex_files;
753 for (const auto& session : sessions) {
754 if (session.GetState() != SessionState::STAGED) {
755 continue;
756 }
757 auto session_id = session.GetId();
758 auto child_session_ids = session.GetChildSessionIds();
759 auto staged_apex_files = OpenApexFilesInSessionDirs(
760 session_id, {child_session_ids.begin(), child_session_ids.end()});
761 if (staged_apex_files.ok()) {
762 std::ranges::move(*staged_apex_files, std::back_inserter(all_apex_files));
763 } else {
764 // Let's not abort with a previously staged session
765 LOG(ERROR) << "Failed to open previously staged APEX files: "
766 << staged_apex_files.error();
767 }
768 }
769
770 // + incoming APEXes at the end.
771 for (const auto& apex_file : apex_files) {
772 all_apex_files.push_back(apex_file);
773 }
774
775 auto check_fn = [&](const std::vector<std::string>& mount_points)
776 -> Result<VerificationResult> {
777 VerificationResult result;
778 result.apex_hals = OR_RETURN(CheckVintf(all_apex_files, mount_points));
779 return result;
780 };
781 return RunVerifyFnInsideTempMounts(all_apex_files, check_fn);
782 }
783
DeleteBackup()784 Result<void> DeleteBackup() {
785 auto exists = PathExists(std::string(kApexBackupDir));
786 if (!exists.ok()) {
787 return Error() << "Can't clean " << kApexBackupDir << " : "
788 << exists.error();
789 }
790 if (!*exists) {
791 LOG(DEBUG) << kApexBackupDir << " does not exist. Nothing to clean";
792 return {};
793 }
794 return DeleteDirContent(std::string(kApexBackupDir));
795 }
796
BackupActivePackages()797 Result<void> BackupActivePackages() {
798 LOG(DEBUG) << "Initializing backup of " << gConfig->active_apex_data_dir;
799
800 // Previous restore might've delete backups folder.
801 auto create_status = CreateDirIfNeeded(kApexBackupDir, 0700);
802 if (!create_status.ok()) {
803 return Error() << "Backup failed : " << create_status.error();
804 }
805
806 auto apex_active_exists =
807 PathExists(std::string(gConfig->active_apex_data_dir));
808 if (!apex_active_exists.ok()) {
809 return Error() << "Backup failed : " << apex_active_exists.error();
810 }
811 if (!*apex_active_exists) {
812 LOG(DEBUG) << gConfig->active_apex_data_dir
813 << " does not exist. Nothing to backup";
814 return {};
815 }
816
817 auto active_packages =
818 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
819 if (!active_packages.ok()) {
820 return Error() << "Backup failed : " << active_packages.error();
821 }
822
823 auto cleanup_status = DeleteBackup();
824 if (!cleanup_status.ok()) {
825 return Error() << "Backup failed : " << cleanup_status.error();
826 }
827
828 auto backup_path_fn = [](const ApexFile& apex_file) {
829 return StringPrintf("%s/%s%s", kApexBackupDir,
830 GetPackageId(apex_file.GetManifest()).c_str(),
831 kApexPackageSuffix);
832 };
833
834 auto deleter = []() {
835 auto result = DeleteDirContent(std::string(kApexBackupDir));
836 if (!result.ok()) {
837 LOG(ERROR) << "Failed to cleanup " << kApexBackupDir << " : "
838 << result.error();
839 }
840 };
841 auto scope_guard = android::base::make_scope_guard(deleter);
842
843 for (const std::string& path : *active_packages) {
844 Result<ApexFile> apex_file = ApexFile::Open(path);
845 if (!apex_file.ok()) {
846 return Error() << "Backup failed : " << apex_file.error();
847 }
848 const auto& dest_path = backup_path_fn(*apex_file);
849 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
850 return ErrnoError() << "Failed to backup " << apex_file->GetPath();
851 }
852 }
853
854 scope_guard.Disable(); // Accept the backup.
855 return {};
856 }
857
RestoreActivePackages()858 Result<void> RestoreActivePackages() {
859 LOG(DEBUG) << "Initializing restore of " << gConfig->active_apex_data_dir;
860
861 auto backup_exists = PathExists(std::string(kApexBackupDir));
862 if (!backup_exists.ok()) {
863 return backup_exists.error();
864 }
865 if (!*backup_exists) {
866 return Error() << kApexBackupDir << " does not exist";
867 }
868
869 struct stat stat_data;
870 if (stat(gConfig->active_apex_data_dir, &stat_data) != 0) {
871 return ErrnoError() << "Failed to access " << gConfig->active_apex_data_dir;
872 }
873
874 LOG(DEBUG) << "Deleting existing packages in "
875 << gConfig->active_apex_data_dir;
876 auto delete_status =
877 DeleteDirContent(std::string(gConfig->active_apex_data_dir));
878 if (!delete_status.ok()) {
879 return delete_status;
880 }
881
882 LOG(DEBUG) << "Renaming " << kApexBackupDir << " to "
883 << gConfig->active_apex_data_dir;
884 if (rename(kApexBackupDir, gConfig->active_apex_data_dir) != 0) {
885 return ErrnoError() << "Failed to rename " << kApexBackupDir << " to "
886 << gConfig->active_apex_data_dir;
887 }
888
889 LOG(DEBUG) << "Restoring original permissions for "
890 << gConfig->active_apex_data_dir;
891 if (chmod(gConfig->active_apex_data_dir, stat_data.st_mode & ALLPERMS) != 0) {
892 return ErrnoError() << "Failed to restore original permissions for "
893 << gConfig->active_apex_data_dir;
894 }
895
896 return {};
897 }
898
UnmountPackage(const ApexFile & apex,bool allow_latest,bool deferred,bool detach_mount_point)899 Result<void> UnmountPackage(const ApexFile& apex, bool allow_latest,
900 bool deferred, bool detach_mount_point) {
901 LOG(INFO) << "Unmounting " << GetPackageId(apex.GetManifest())
902 << " allow_latest : " << allow_latest << " deferred : " << deferred
903 << " detach_mount_point : " << detach_mount_point;
904
905 const ApexManifest& manifest = apex.GetManifest();
906
907 std::optional<MountedApexData> data;
908 bool latest = false;
909
910 auto fn = [&](const MountedApexData& d, bool l) {
911 if (d.full_path == apex.GetPath()) {
912 data.emplace(d);
913 latest = l;
914 }
915 };
916 gMountedApexes.ForallMountedApexes(manifest.name(), fn);
917
918 if (!data) {
919 return Error() << "Did not find " << apex.GetPath();
920 }
921
922 // Concept of latest sharedlibs apex is somewhat blurred. Since this is only
923 // used in testing, it is ok to always allow unmounting sharedlibs apex.
924 if (latest && !manifest.providesharedapexlibs()) {
925 if (!allow_latest) {
926 return Error() << "Package " << apex.GetPath() << " is active";
927 }
928 std::string mount_point = apexd_private::GetActiveMountPoint(manifest);
929 LOG(INFO) << "Unmounting " << mount_point;
930 int flags = UMOUNT_NOFOLLOW;
931 if (detach_mount_point) {
932 flags |= MNT_DETACH;
933 }
934 if (umount2(mount_point.c_str(), flags) != 0) {
935 return ErrnoError() << "Failed to unmount " << mount_point;
936 }
937
938 if (!deferred) {
939 if (rmdir(mount_point.c_str()) != 0) {
940 PLOG(ERROR) << "Failed to rmdir " << mount_point;
941 }
942 }
943 }
944
945 // Clean up gMountedApexes now, even though we're not fully done.
946 gMountedApexes.RemoveMountedApex(manifest.name(), apex.GetPath());
947 return Unmount(*data, deferred);
948 }
949
950 } // namespace
951
SetConfig(const ApexdConfig & config)952 void SetConfig(const ApexdConfig& config) { gConfig = config; }
953
MountPackage(const ApexFile & apex,const std::string & mount_point,const std::string & device_name,bool reuse_device)954 Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point,
955 const std::string& device_name, bool reuse_device) {
956 auto ret = MountPackageImpl(apex, mount_point, device_name,
957 /* verify_image = */ false, reuse_device);
958 if (!ret.ok()) {
959 return ret.error();
960 }
961
962 gMountedApexes.AddMountedApex(apex.GetManifest().name(), *ret);
963 return {};
964 }
965
966 namespace apexd_private {
967
GetVerifiedPublicKey(const ApexFile & apex)968 Result<std::string> GetVerifiedPublicKey(const ApexFile& apex) {
969 auto preinstalled_public_key =
970 ApexFileRepository::GetInstance().GetPublicKey(apex.GetManifest().name());
971 if (preinstalled_public_key.ok()) {
972 return *preinstalled_public_key;
973 } else if (ApexFileRepository::IsBrandNewApexEnabled() &&
974 VerifyBrandNewPackageAgainstPreinstalled(apex).ok()) {
975 return apex.GetBundledPublicKey();
976 }
977 return Error() << "No preinstalled apex found for unverified package "
978 << apex.GetManifest().name();
979 }
980
IsMounted(const std::string & full_path)981 bool IsMounted(const std::string& full_path) {
982 bool found_mounted = false;
983 gMountedApexes.ForallMountedApexes([&](const std::string&,
984 const MountedApexData& data,
985 [[maybe_unused]] bool latest) {
986 if (full_path == data.full_path) {
987 found_mounted = true;
988 }
989 });
990 return found_mounted;
991 }
992
GetPackageMountPoint(const ApexManifest & manifest)993 std::string GetPackageMountPoint(const ApexManifest& manifest) {
994 return StringPrintf("%s/%s", kApexRoot, GetPackageId(manifest).c_str());
995 }
996
GetPackageTempMountPoint(const ApexManifest & manifest)997 std::string GetPackageTempMountPoint(const ApexManifest& manifest) {
998 return StringPrintf("%s.tmp", GetPackageMountPoint(manifest).c_str());
999 }
1000
GetActiveMountPoint(const ApexManifest & manifest)1001 std::string GetActiveMountPoint(const ApexManifest& manifest) {
1002 return StringPrintf("%s/%s", kApexRoot, manifest.name().c_str());
1003 }
1004
1005 } // namespace apexd_private
1006
ResumeRevertIfNeeded()1007 Result<void> ResumeRevertIfNeeded() {
1008 auto sessions =
1009 gSessionManager->GetSessionsInState(SessionState::REVERT_IN_PROGRESS);
1010 if (sessions.empty()) {
1011 return {};
1012 }
1013 return RevertActiveSessions("", "");
1014 }
1015
ContributeToSharedLibs(const std::string & mount_point)1016 Result<void> ContributeToSharedLibs(const std::string& mount_point) {
1017 for (const auto& lib_path : {"lib", "lib64"}) {
1018 std::string apex_lib_path = mount_point + "/" + lib_path;
1019 auto lib_dir = PathExists(apex_lib_path);
1020 if (!lib_dir.ok() || !*lib_dir) {
1021 continue;
1022 }
1023
1024 auto iter = std::filesystem::directory_iterator(apex_lib_path);
1025 std::error_code ec;
1026
1027 while (iter != std::filesystem::end(iter)) {
1028 const auto& lib_entry = *iter;
1029 if (!lib_entry.is_directory()) {
1030 iter = iter.increment(ec);
1031 if (ec) {
1032 return Error() << "Failed to scan " << apex_lib_path << " : "
1033 << ec.message();
1034 }
1035 continue;
1036 }
1037
1038 const auto library_name = lib_entry.path().filename();
1039 const std::string library_symlink_dir =
1040 StringPrintf("%s/%s/%s/%s", kApexRoot, kApexSharedLibsSubDir,
1041 lib_path, library_name.c_str());
1042
1043 auto symlink_dir = PathExists(library_symlink_dir);
1044 if (!symlink_dir.ok() || !*symlink_dir) {
1045 std::filesystem::create_directory(library_symlink_dir, ec);
1046 if (ec) {
1047 return Error() << "Failed to create directory " << library_symlink_dir
1048 << ": " << ec.message();
1049 }
1050 }
1051
1052 auto inner_iter =
1053 std::filesystem::directory_iterator(lib_entry.path().string());
1054
1055 while (inner_iter != std::filesystem::end(inner_iter)) {
1056 const auto& lib_items = *inner_iter;
1057 const auto hash_value = lib_items.path().filename();
1058 const std::string library_symlink_hash = StringPrintf(
1059 "%s/%s", library_symlink_dir.c_str(), hash_value.c_str());
1060
1061 auto hash_dir = PathExists(library_symlink_hash);
1062 if (hash_dir.ok() && *hash_dir) {
1063 // Compare file size for two library files with same name and hash
1064 // value
1065 auto existing_file_path =
1066 library_symlink_hash + "/" + library_name.string();
1067 auto existing_file_size = GetFileSize(existing_file_path);
1068 if (!existing_file_size.ok()) {
1069 return existing_file_size.error();
1070 }
1071
1072 auto new_file_path =
1073 lib_items.path().string() + "/" + library_name.string();
1074 auto new_file_size = GetFileSize(new_file_path);
1075 if (!new_file_size.ok()) {
1076 return new_file_size.error();
1077 }
1078
1079 if (*existing_file_size != *new_file_size) {
1080 return Error() << "There are two libraries with same hash and "
1081 "different file size : "
1082 << existing_file_path << " and " << new_file_path;
1083 }
1084
1085 inner_iter = inner_iter.increment(ec);
1086 if (ec) {
1087 return Error() << "Failed to scan " << lib_entry.path().string()
1088 << " : " << ec.message();
1089 }
1090 continue;
1091 }
1092 std::filesystem::create_directory_symlink(lib_items.path(),
1093 library_symlink_hash, ec);
1094 if (ec) {
1095 return Error() << "Failed to create symlink from " << lib_items.path()
1096 << " to " << library_symlink_hash << ec.message();
1097 }
1098
1099 inner_iter = inner_iter.increment(ec);
1100 if (ec) {
1101 return Error() << "Failed to scan " << lib_entry.path().string()
1102 << " : " << ec.message();
1103 }
1104 }
1105
1106 iter = iter.increment(ec);
1107 if (ec) {
1108 return Error() << "Failed to scan " << apex_lib_path << " : "
1109 << ec.message();
1110 }
1111 }
1112 }
1113
1114 return {};
1115 }
1116
IsValidPackageName(const std::string & package_name)1117 bool IsValidPackageName(const std::string& package_name) {
1118 return kBannedApexName.count(package_name) == 0;
1119 }
1120
1121 // Activates given APEX file.
1122 //
1123 // In a nutshel activation of an APEX consist of the following steps:
1124 // 1. Create loop devices that is backed by the given apex_file
1125 // 2. If apex_file resides on /data partition then create a dm-verity device
1126 // backed by the loop device created in step (1).
1127 // 3. Create a mount point under /apex for this APEX.
1128 // 4. Mount the dm-verity device on that mount point.
1129 // 4.1 In case APEX file comes from a partition that is already
1130 // dm-verity protected (e.g. /system) then we mount the loop device.
1131 //
1132 //
1133 // Note: this function only does the job to activate this single APEX.
1134 // In case this APEX file contributes to the /apex/sharedlibs mount point, then
1135 // you must also call ContributeToSharedLibs after finishing activating all
1136 // APEXes. See ActivateApexPackages for more context.
ActivatePackageImpl(const ApexFile & apex_file,const std::string & device_name,bool reuse_device)1137 Result<void> ActivatePackageImpl(const ApexFile& apex_file,
1138 const std::string& device_name,
1139 bool reuse_device) {
1140 ATRACE_NAME("ActivatePackageImpl");
1141 const ApexManifest& manifest = apex_file.GetManifest();
1142
1143 if (!IsValidPackageName(manifest.name())) {
1144 return Errorf("Package name {} is not allowed.", manifest.name());
1145 }
1146
1147 // Validate upgraded shim apex
1148 if (shim::IsShimApex(apex_file) &&
1149 !ApexFileRepository::GetInstance().IsPreInstalledApex(apex_file)) {
1150 // This is not cheap for shim apex, but it is fine here since we have
1151 // upgraded shim apex only during CTS tests.
1152 Result<void> result = VerifyPackageBoot(apex_file);
1153 if (!result.ok()) {
1154 LOG(ERROR) << "Failed to validate shim apex: " << apex_file.GetPath();
1155 return result;
1156 }
1157 }
1158
1159 // See whether we think it's active, and do not allow to activate the same
1160 // version. Also detect whether this is the highest version.
1161 // We roll this into a single check.
1162 bool version_found_mounted = false;
1163 {
1164 uint64_t new_version = manifest.version();
1165 bool version_found_active = false;
1166 gMountedApexes.ForallMountedApexes(
1167 manifest.name(), [&](const MountedApexData& data, bool latest) {
1168 Result<ApexFile> other_apex = ApexFile::Open(data.full_path);
1169 if (!other_apex.ok()) {
1170 return;
1171 }
1172 if (static_cast<uint64_t>(other_apex->GetManifest().version()) ==
1173 new_version) {
1174 version_found_mounted = true;
1175 version_found_active = latest;
1176 }
1177 });
1178 // If the package provides shared libraries to other APEXs, we need to
1179 // activate all versions available (i.e. preloaded on /system/apex and
1180 // available on /data/apex/active). The reason is that there might be some
1181 // APEXs loaded from /system/apex that reference the libraries contained on
1182 // the preloaded version of the apex providing shared libraries.
1183 if (version_found_active && !manifest.providesharedapexlibs()) {
1184 LOG(DEBUG) << "Package " << manifest.name() << " with version "
1185 << manifest.version() << " already active";
1186 return {};
1187 }
1188 }
1189
1190 const std::string& mount_point =
1191 apexd_private::GetPackageMountPoint(manifest);
1192
1193 if (!version_found_mounted) {
1194 auto mount_status =
1195 MountPackage(apex_file, mount_point, device_name, reuse_device);
1196 if (!mount_status.ok()) {
1197 return mount_status;
1198 }
1199 }
1200
1201 // Bind mount the latest version to /apex/<package_name>, unless the
1202 // package provides shared libraries to other APEXs.
1203 if (!manifest.providesharedapexlibs()) {
1204 auto st = gMountedApexes.DoIfLatest(
1205 manifest.name(), apex_file.GetPath(), [&]() -> Result<void> {
1206 return apexd_private::BindMount(
1207 apexd_private::GetActiveMountPoint(manifest), mount_point);
1208 });
1209 if (!st.ok()) {
1210 return Error() << "Failed to update package " << manifest.name()
1211 << " to version " << manifest.version() << " : "
1212 << st.error();
1213 }
1214 }
1215
1216 LOG(DEBUG) << "Successfully activated " << apex_file.GetPath()
1217 << " package_name: " << manifest.name()
1218 << " version: " << manifest.version();
1219 return {};
1220 }
1221
1222 // Wrapper around ActivatePackageImpl.
1223 // Do not use, this wrapper is going away.
ActivatePackage(const std::string & full_path)1224 Result<void> ActivatePackage(const std::string& full_path) {
1225 LOG(INFO) << "Trying to activate " << full_path;
1226
1227 Result<ApexFile> apex_file = ApexFile::Open(full_path);
1228 if (!apex_file.ok()) {
1229 return apex_file.error();
1230 }
1231 return ActivatePackageImpl(*apex_file, GetPackageId(apex_file->GetManifest()),
1232 /* reuse_device= */ false);
1233 }
1234
DeactivatePackage(const std::string & full_path)1235 Result<void> DeactivatePackage(const std::string& full_path) {
1236 LOG(INFO) << "Trying to deactivate " << full_path;
1237
1238 Result<ApexFile> apex_file = ApexFile::Open(full_path);
1239 if (!apex_file.ok()) {
1240 return apex_file.error();
1241 }
1242
1243 return UnmountPackage(*apex_file, /* allow_latest= */ true,
1244 /* deferred= */ false, /* detach_mount_point= */ false);
1245 }
1246
ScanApexFilesInSessionDirs(int session_id,const std::vector<int> & child_session_ids)1247 Result<std::vector<std::string>> ScanApexFilesInSessionDirs(
1248 int session_id, const std::vector<int>& child_session_ids) {
1249 std::vector<int> ids_to_scan;
1250 if (!child_session_ids.empty()) {
1251 ids_to_scan = child_session_ids;
1252 } else {
1253 ids_to_scan = {session_id};
1254 }
1255
1256 // Find apex files in the staging directory
1257 std::vector<std::string> apex_file_paths;
1258 for (int id_to_scan : ids_to_scan) {
1259 std::string session_dir_path = std::string(gConfig->staged_session_dir) +
1260 "/session_" + std::to_string(id_to_scan);
1261 Result<std::vector<std::string>> scan =
1262 FindFilesBySuffix(session_dir_path, {kApexPackageSuffix});
1263 if (!scan.ok()) {
1264 return scan.error();
1265 }
1266 if (scan->size() != 1) {
1267 return Error() << "Expected exactly one APEX file in directory "
1268 << session_dir_path << ". Found: " << scan->size();
1269 }
1270 std::string& apex_file_path = (*scan)[0];
1271 apex_file_paths.push_back(std::move(apex_file_path));
1272 }
1273 return apex_file_paths;
1274 }
1275
ScanSessionApexFiles(const ApexSession & session)1276 Result<std::vector<std::string>> ScanSessionApexFiles(
1277 const ApexSession& session) {
1278 auto child_session_ids =
1279 std::vector{std::from_range, session.GetChildSessionIds()};
1280 return ScanApexFilesInSessionDirs(session.GetId(), child_session_ids);
1281 }
1282
OpenApexFilesInSessionDirs(int session_id,const std::vector<int> & child_session_ids)1283 Result<std::vector<ApexFile>> OpenApexFilesInSessionDirs(
1284 int session_id, const std::vector<int>& child_session_ids) {
1285 auto apex_file_paths =
1286 OR_RETURN(ScanApexFilesInSessionDirs(session_id, child_session_ids));
1287 return OpenApexFiles(apex_file_paths);
1288 }
1289
GetStagedApexFiles(int session_id,const std::vector<int> & child_session_ids)1290 Result<std::vector<ApexFile>> GetStagedApexFiles(
1291 int session_id, const std::vector<int>& child_session_ids) {
1292 // We should only accept sessions in SessionState::STAGED state
1293 auto session = OR_RETURN(gSessionManager->GetSession(session_id));
1294 if (session.GetState() != SessionState::STAGED) {
1295 return Error() << "Session " << session_id << " is not in state STAGED";
1296 }
1297
1298 return OpenApexFilesInSessionDirs(session_id, child_session_ids);
1299 }
1300
MountAndDeriveClassPath(const std::vector<ApexFile> & apex_files)1301 Result<ClassPath> MountAndDeriveClassPath(
1302 const std::vector<ApexFile>& apex_files) {
1303 // Calculate classpaths of temp mounted staged apexs
1304 return RunVerifyFnInsideTempMounts(apex_files, [](const auto& mount_points) {
1305 return ClassPath::DeriveClassPath(mount_points);
1306 });
1307 }
1308
GetActivePackages()1309 std::vector<ApexFile> GetActivePackages() {
1310 std::vector<ApexFile> ret;
1311 gMountedApexes.ForallMountedApexes(
1312 [&](const std::string&, const MountedApexData& data, bool latest) {
1313 if (!latest) {
1314 return;
1315 }
1316
1317 Result<ApexFile> apex_file = ApexFile::Open(data.full_path);
1318 if (!apex_file.ok()) {
1319 return;
1320 }
1321 ret.emplace_back(std::move(*apex_file));
1322 });
1323
1324 return ret;
1325 }
1326
CalculateInactivePackages(const std::vector<ApexFile> & active)1327 std::vector<ApexFile> CalculateInactivePackages(
1328 const std::vector<ApexFile>& active) {
1329 std::vector<ApexFile> inactive = GetFactoryPackages();
1330 auto new_end = std::remove_if(
1331 inactive.begin(), inactive.end(), [&active](const ApexFile& apex) {
1332 return std::any_of(active.begin(), active.end(),
1333 [&apex](const ApexFile& active_apex) {
1334 return apex.GetPath() == active_apex.GetPath();
1335 });
1336 });
1337 inactive.erase(new_end, inactive.end());
1338 return inactive;
1339 }
1340
EmitApexInfoList(bool is_bootstrap)1341 Result<void> EmitApexInfoList(bool is_bootstrap) {
1342 std::vector<ApexFile> active{GetActivePackages()};
1343
1344 std::vector<ApexFile> inactive;
1345 // we skip for non-activated built-in apexes in bootstrap mode
1346 // in order to avoid boottime increase
1347 if (!is_bootstrap) {
1348 inactive = CalculateInactivePackages(active);
1349 }
1350
1351 std::stringstream xml;
1352 CollectApexInfoList(xml, active, inactive);
1353
1354 unique_fd fd(TEMP_FAILURE_RETRY(
1355 open(kApexInfoList, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)));
1356 if (fd.get() == -1) {
1357 return ErrnoErrorf("Can't open {}", kApexInfoList);
1358 }
1359 if (!android::base::WriteStringToFd(xml.str(), fd)) {
1360 return ErrnoErrorf("Can't write to {}", kApexInfoList);
1361 }
1362
1363 fd.reset();
1364 return RestoreconPath(kApexInfoList);
1365 }
1366
1367 namespace {
GetActivePackagesMap()1368 std::unordered_map<std::string, uint64_t> GetActivePackagesMap() {
1369 std::vector<ApexFile> active_packages = GetActivePackages();
1370 std::unordered_map<std::string, uint64_t> ret;
1371 for (const auto& package : active_packages) {
1372 const ApexManifest& manifest = package.GetManifest();
1373 ret.insert({manifest.name(), manifest.version()});
1374 }
1375 return ret;
1376 }
1377
1378 } // namespace
1379
GetFactoryPackages()1380 std::vector<ApexFile> GetFactoryPackages() {
1381 std::vector<ApexFile> ret;
1382
1383 // Decompressed APEX is considered factory package
1384 std::vector<std::string> decompressed_pkg_names;
1385 auto active_pkgs = GetActivePackages();
1386 for (ApexFile& apex : active_pkgs) {
1387 if (ApexFileRepository::GetInstance().IsDecompressedApex(apex)) {
1388 decompressed_pkg_names.push_back(apex.GetManifest().name());
1389 ret.emplace_back(std::move(apex));
1390 }
1391 }
1392
1393 const auto& file_repository = ApexFileRepository::GetInstance();
1394 for (const auto& ref : file_repository.GetPreInstalledApexFiles()) {
1395 Result<ApexFile> apex_file = ApexFile::Open(ref.get().GetPath());
1396 if (!apex_file.ok()) {
1397 LOG(ERROR) << apex_file.error();
1398 continue;
1399 }
1400 // Ignore compressed APEX if it has been decompressed already
1401 if (apex_file->IsCompressed() &&
1402 std::find(decompressed_pkg_names.begin(), decompressed_pkg_names.end(),
1403 apex_file->GetManifest().name()) !=
1404 decompressed_pkg_names.end()) {
1405 continue;
1406 }
1407
1408 ret.emplace_back(std::move(*apex_file));
1409 }
1410 return ret;
1411 }
1412
1413 /**
1414 * Abort individual staged session.
1415 *
1416 * Returns without error only if session was successfully aborted.
1417 **/
AbortStagedSession(int session_id)1418 Result<void> AbortStagedSession(int session_id) REQUIRES(!gInstallLock) {
1419 auto install_guard = std::scoped_lock{gInstallLock};
1420 auto session = gSessionManager->GetSession(session_id);
1421 if (!session.ok()) {
1422 return Error() << "No session found with id " << session_id;
1423 }
1424
1425 switch (session->GetState()) {
1426 case SessionState::VERIFIED:
1427 [[fallthrough]];
1428 case SessionState::STAGED:
1429 if (IsMountBeforeDataEnabled()) {
1430 for (const auto& image : session->GetApexImages()) {
1431 auto result = GetImageManager()->DeleteImage(image);
1432 if (!result.ok()) {
1433 // There's not much we can do with error. Let's log it. On boot
1434 // completion, dangling images (not referenced by any) will be
1435 // deleted anyway.
1436 LOG(ERROR) << result.error();
1437 }
1438 }
1439 }
1440 return session->DeleteSession();
1441 default:
1442 return Error() << "Session " << *session << " can't be aborted";
1443 }
1444 }
1445
1446 namespace {
1447
1448 enum ActivationMode { kBootstrapMode = 0, kBootMode, kOtaChrootMode, kVmMode };
1449
ActivateApexWorker(ActivationMode mode,std::queue<const ApexFile * > & apex_queue,std::mutex & mutex)1450 std::vector<Result<const ApexFile*>> ActivateApexWorker(
1451 ActivationMode mode, std::queue<const ApexFile*>& apex_queue,
1452 std::mutex& mutex) {
1453 ATRACE_NAME("ActivateApexWorker");
1454 std::vector<Result<const ApexFile*>> ret;
1455
1456 while (true) {
1457 const ApexFile* apex;
1458 {
1459 std::lock_guard lock(mutex);
1460 if (apex_queue.empty()) break;
1461 apex = apex_queue.front();
1462 apex_queue.pop();
1463 }
1464
1465 std::string device_name;
1466 if (mode == ActivationMode::kBootMode) {
1467 device_name = apex->GetManifest().name();
1468 } else {
1469 device_name = GetPackageId(apex->GetManifest());
1470 }
1471 if (mode == ActivationMode::kOtaChrootMode) {
1472 device_name += ".chroot";
1473 }
1474 bool reuse_device = mode == ActivationMode::kBootMode;
1475 auto res = ActivatePackageImpl(*apex, device_name, reuse_device);
1476 if (!res.ok()) {
1477 ret.push_back(Error() << "Failed to activate " << apex->GetPath() << "("
1478 << device_name << "): " << res.error());
1479 } else {
1480 ret.push_back({apex});
1481 }
1482 }
1483
1484 return ret;
1485 }
1486
ActivateApexPackages(const std::vector<ApexFileRef> & apexes,ActivationMode mode)1487 Result<void> ActivateApexPackages(const std::vector<ApexFileRef>& apexes,
1488 ActivationMode mode) {
1489 ATRACE_NAME("ActivateApexPackages");
1490 std::queue<const ApexFile*> apex_queue;
1491 std::mutex apex_queue_mutex;
1492
1493 for (const ApexFile& apex : apexes) {
1494 apex_queue.emplace(&apex);
1495 }
1496
1497 size_t worker_num =
1498 android::sysprop::ApexProperties::boot_activation_threads().value_or(0);
1499
1500 // Setting number of workers to the number of packages to load
1501 // This seems to provide the best performance
1502 if (worker_num == 0) {
1503 worker_num = apex_queue.size();
1504 }
1505 worker_num = std::min(apex_queue.size(), worker_num);
1506
1507 std::vector<std::future<std::vector<Result<const ApexFile*>>>> futures;
1508 futures.reserve(worker_num);
1509 for (size_t i = 0; i < worker_num; i++) {
1510 futures.push_back(std::async(std::launch::async, ActivateApexWorker,
1511 std::ref(mode), std::ref(apex_queue),
1512 std::ref(apex_queue_mutex)));
1513 }
1514
1515 size_t activated_cnt = 0;
1516 size_t failed_cnt = 0;
1517 std::string error_message;
1518 std::vector<const ApexFile*> activated_sharedlibs_apexes;
1519 for (size_t i = 0; i < futures.size(); i++) {
1520 for (const auto& res : futures[i].get()) {
1521 if (res.ok()) {
1522 ++activated_cnt;
1523 if (res.value()->GetManifest().providesharedapexlibs()) {
1524 activated_sharedlibs_apexes.push_back(res.value());
1525 }
1526 } else {
1527 ++failed_cnt;
1528 LOG(ERROR) << res.error();
1529 if (failed_cnt == 1) {
1530 error_message = res.error().message();
1531 }
1532 }
1533 }
1534 }
1535
1536 // We finished activation of APEX packages and now are ready to populate the
1537 // /apex/sharedlibs mount point. Since there can be multiple different APEXes
1538 // contributing to shared libs (at the point of writing this comment there can
1539 // be up 2 APEXes: pre-installed sharedlibs APEX and its updated counterpart)
1540 // we need to call ContributeToSharedLibs sequentially to avoid potential race
1541 // conditions. See b/240291921
1542 const auto& apex_repo = ApexFileRepository::GetInstance();
1543 // To make things simpler we also provide an order in which APEXes contribute
1544 // to sharedlibs.
1545 auto cmp = [&apex_repo](const auto& apex_a, const auto& apex_b) {
1546 // An APEX with higher version should contribute first
1547 if (apex_a->GetManifest().version() != apex_b->GetManifest().version()) {
1548 return apex_a->GetManifest().version() > apex_b->GetManifest().version();
1549 }
1550 // If they have the same version, then we pick the updated APEX first.
1551 return !apex_repo.IsPreInstalledApex(*apex_a);
1552 };
1553 std::sort(activated_sharedlibs_apexes.begin(),
1554 activated_sharedlibs_apexes.end(), cmp);
1555 for (const auto& sharedlibs_apex : activated_sharedlibs_apexes) {
1556 LOG(DEBUG) << "Populating sharedlibs with APEX "
1557 << sharedlibs_apex->GetPath() << " ( "
1558 << sharedlibs_apex->GetManifest().name()
1559 << " ) version : " << sharedlibs_apex->GetManifest().version();
1560 auto mount_point =
1561 apexd_private::GetPackageMountPoint(sharedlibs_apex->GetManifest());
1562 if (auto ret = ContributeToSharedLibs(mount_point); !ret.ok()) {
1563 LOG(ERROR) << "Failed to populate sharedlibs with APEX package "
1564 << sharedlibs_apex->GetPath() << " : " << ret.error();
1565 ++failed_cnt;
1566 if (failed_cnt == 1) {
1567 error_message = ret.error().message();
1568 }
1569 }
1570 }
1571
1572 if (failed_cnt > 0) {
1573 return Error() << "Failed to activate " << failed_cnt
1574 << " APEX packages. One of the errors: " << error_message;
1575 }
1576 LOG(INFO) << "Activated " << activated_cnt << " packages.";
1577 return {};
1578 }
1579
1580 // A fallback function in case some of the apexes failed to activate. For all
1581 // such apexes that were coming from /data partition we will attempt to activate
1582 // their corresponding pre-installed copies.
ActivateMissingApexes(const std::vector<ApexFileRef> & apexes,ActivationMode mode)1583 Result<void> ActivateMissingApexes(const std::vector<ApexFileRef>& apexes,
1584 ActivationMode mode) {
1585 LOG(INFO) << "Trying to activate pre-installed versions of missing apexes";
1586 const auto& file_repository = ApexFileRepository::GetInstance();
1587 const auto& activated_apexes = GetActivePackagesMap();
1588 std::vector<ApexFileRef> fallback_apexes;
1589 for (const auto& apex_ref : apexes) {
1590 const auto& apex = apex_ref.get();
1591 if (apex.GetManifest().providesharedapexlibs()) {
1592 // We must mount both versions of sharedlibs apex anyway. Not much we can
1593 // do here.
1594 continue;
1595 }
1596 if (file_repository.IsPreInstalledApex(apex)) {
1597 // We tried to activate pre-installed apex in the first place. No need to
1598 // try again.
1599 continue;
1600 }
1601 const std::string& name = apex.GetManifest().name();
1602 if (activated_apexes.find(name) == activated_apexes.end()) {
1603 fallback_apexes.push_back(file_repository.GetPreInstalledApex(name));
1604 }
1605 }
1606
1607 // Process compressed APEX, if any
1608 std::vector<ApexFileRef> compressed_apex;
1609 for (auto it = fallback_apexes.begin(); it != fallback_apexes.end();) {
1610 if (it->get().IsCompressed()) {
1611 compressed_apex.emplace_back(*it);
1612 it = fallback_apexes.erase(it);
1613 } else {
1614 it++;
1615 }
1616 }
1617 std::vector<ApexFile> decompressed_apex;
1618 if (!compressed_apex.empty()) {
1619 decompressed_apex = ProcessCompressedApex(
1620 compressed_apex,
1621 /* is_ota_chroot= */ mode == ActivationMode::kOtaChrootMode);
1622 for (const ApexFile& apex_file : decompressed_apex) {
1623 fallback_apexes.emplace_back(std::cref(apex_file));
1624 }
1625 }
1626 if (mode == kBootMode) {
1627 // Treat fallback to pre-installed APEXes as a change of the acitve APEX,
1628 // since we are already in a pretty dire situation, so it's better if we
1629 // drop all the caches.
1630 for (const auto& apex : fallback_apexes) {
1631 gChangedActiveApexes.insert(apex.get().GetManifest().name());
1632 }
1633 }
1634 return ActivateApexPackages(fallback_apexes, mode);
1635 }
1636
1637 } // namespace
1638
1639 /**
1640 * Snapshots data from base_dir/apexdata/<apex name> to
1641 * base_dir/apexrollback/<rollback id>/<apex name>.
1642 */
SnapshotDataDirectory(const std::string & base_dir,const int rollback_id,const std::string & apex_name,bool pre_restore=false)1643 Result<void> SnapshotDataDirectory(const std::string& base_dir,
1644 const int rollback_id,
1645 const std::string& apex_name,
1646 bool pre_restore = false) {
1647 auto rollback_path =
1648 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
1649 rollback_id, pre_restore ? kPreRestoreSuffix : "");
1650 const Result<void> result = CreateDirIfNeeded(rollback_path, 0700);
1651 if (!result.ok()) {
1652 return Error() << "Failed to create snapshot directory for rollback "
1653 << rollback_id << " : " << result.error();
1654 }
1655 auto from_path = StringPrintf("%s/%s/%s", base_dir.c_str(), kApexDataSubDir,
1656 apex_name.c_str());
1657 auto to_path =
1658 StringPrintf("%s/%s", rollback_path.c_str(), apex_name.c_str());
1659
1660 return ReplaceFiles(from_path, to_path);
1661 }
1662
1663 /**
1664 * Restores snapshot from base_dir/apexrollback/<rollback id>/<apex name>
1665 * to base_dir/apexdata/<apex name>.
1666 * Note the snapshot will be deleted after restoration succeeded.
1667 */
RestoreDataDirectory(const std::string & base_dir,const int rollback_id,const std::string & apex_name,bool pre_restore=false)1668 Result<void> RestoreDataDirectory(const std::string& base_dir,
1669 const int rollback_id,
1670 const std::string& apex_name,
1671 bool pre_restore = false) {
1672 auto from_path = StringPrintf(
1673 "%s/%s/%d%s/%s", base_dir.c_str(), kApexSnapshotSubDir, rollback_id,
1674 pre_restore ? kPreRestoreSuffix : "", apex_name.c_str());
1675 auto to_path = StringPrintf("%s/%s/%s", base_dir.c_str(), kApexDataSubDir,
1676 apex_name.c_str());
1677 Result<void> result = ReplaceFiles(from_path, to_path);
1678 if (!result.ok()) {
1679 return result;
1680 }
1681 result = RestoreconPath(to_path);
1682 if (!result.ok()) {
1683 return result;
1684 }
1685 result = DeleteDir(from_path);
1686 if (!result.ok()) {
1687 LOG(ERROR) << "Failed to delete the snapshot: " << result.error();
1688 }
1689 return {};
1690 }
1691
SnapshotOrRestoreDeIfNeeded(const std::string & base_dir,const ApexSession & session)1692 void SnapshotOrRestoreDeIfNeeded(const std::string& base_dir,
1693 const ApexSession& session) {
1694 if (session.HasRollbackEnabled()) {
1695 for (const auto& apex_name : session.GetApexNames()) {
1696 Result<void> result =
1697 SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name);
1698 if (!result.ok()) {
1699 LOG(ERROR) << "Snapshot failed for " << apex_name << ": "
1700 << result.error();
1701 }
1702 }
1703 } else if (session.IsRollback()) {
1704 for (const auto& apex_name : session.GetApexNames()) {
1705 if (!gSupportsFsCheckpoints) {
1706 // Snapshot before restore so this rollback can be reverted.
1707 SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name,
1708 true /* pre_restore */);
1709 }
1710 Result<void> result =
1711 RestoreDataDirectory(base_dir, session.GetRollbackId(), apex_name);
1712 if (!result.ok()) {
1713 LOG(ERROR) << "Restore of data failed for " << apex_name << ": "
1714 << result.error();
1715 }
1716 }
1717 }
1718 }
1719
SnapshotOrRestoreDeSysData()1720 void SnapshotOrRestoreDeSysData() {
1721 auto sessions = gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
1722
1723 for (const ApexSession& session : sessions) {
1724 SnapshotOrRestoreDeIfNeeded(kDeSysDataDir, session);
1725 }
1726 }
1727
SnapshotOrRestoreDeUserData()1728 int SnapshotOrRestoreDeUserData() {
1729 auto user_dirs = GetDeUserDirs();
1730
1731 if (!user_dirs.ok()) {
1732 LOG(ERROR) << "Error reading dirs " << user_dirs.error();
1733 return 1;
1734 }
1735
1736 auto sessions = gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
1737
1738 for (const ApexSession& session : sessions) {
1739 for (const auto& user_dir : *user_dirs) {
1740 SnapshotOrRestoreDeIfNeeded(user_dir, session);
1741 }
1742 }
1743
1744 return 0;
1745 }
1746
SnapshotCeData(const int user_id,const int rollback_id,const std::string & apex_name)1747 Result<void> SnapshotCeData(const int user_id, const int rollback_id,
1748 const std::string& apex_name) {
1749 auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
1750 return SnapshotDataDirectory(base_dir, rollback_id, apex_name);
1751 }
1752
RestoreCeData(const int user_id,const int rollback_id,const std::string & apex_name)1753 Result<void> RestoreCeData(const int user_id, const int rollback_id,
1754 const std::string& apex_name) {
1755 auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
1756 return RestoreDataDirectory(base_dir, rollback_id, apex_name);
1757 }
1758
DestroySnapshots(const std::string & base_dir,const int rollback_id)1759 Result<void> DestroySnapshots(const std::string& base_dir,
1760 const int rollback_id) {
1761 auto path = StringPrintf("%s/%s/%d", base_dir.c_str(), kApexSnapshotSubDir,
1762 rollback_id);
1763 return DeleteDir(path);
1764 }
1765
DestroyDeSnapshots(const int rollback_id)1766 Result<void> DestroyDeSnapshots(const int rollback_id) {
1767 DestroySnapshots(kDeSysDataDir, rollback_id);
1768
1769 auto user_dirs = GetDeUserDirs();
1770 if (!user_dirs.ok()) {
1771 return Error() << "Error reading user dirs " << user_dirs.error();
1772 }
1773
1774 for (const auto& user_dir : *user_dirs) {
1775 DestroySnapshots(user_dir, rollback_id);
1776 }
1777
1778 return {};
1779 }
1780
DestroyCeSnapshots(const int user_id,const int rollback_id)1781 Result<void> DestroyCeSnapshots(const int user_id, const int rollback_id) {
1782 auto path = StringPrintf("%s/%d/%s/%d", kCeDataDir, user_id,
1783 kApexSnapshotSubDir, rollback_id);
1784 return DeleteDir(path);
1785 }
1786
1787 /**
1788 * Deletes all credential-encrypted snapshots for the given user, except for
1789 * those listed in retain_rollback_ids.
1790 */
DestroyCeSnapshotsNotSpecified(int user_id,const std::vector<int> & retain_rollback_ids)1791 Result<void> DestroyCeSnapshotsNotSpecified(
1792 int user_id, const std::vector<int>& retain_rollback_ids) {
1793 auto snapshot_root =
1794 StringPrintf("%s/%d/%s", kCeDataDir, user_id, kApexSnapshotSubDir);
1795 auto snapshot_dirs = GetSubdirs(snapshot_root);
1796 if (!snapshot_dirs.ok()) {
1797 return Error() << "Error reading snapshot dirs " << snapshot_dirs.error();
1798 }
1799
1800 for (const auto& snapshot_dir : *snapshot_dirs) {
1801 uint snapshot_id;
1802 bool parse_ok = ParseUint(
1803 std::filesystem::path(snapshot_dir).filename().c_str(), &snapshot_id);
1804 if (parse_ok &&
1805 std::find(retain_rollback_ids.begin(), retain_rollback_ids.end(),
1806 snapshot_id) == retain_rollback_ids.end()) {
1807 Result<void> result = DeleteDir(snapshot_dir);
1808 if (!result.ok()) {
1809 return Error() << "Destroy CE snapshot failed for " << snapshot_dir
1810 << " : " << result.error();
1811 }
1812 }
1813 }
1814 return {};
1815 }
1816
RestorePreRestoreSnapshotsIfPresent(const std::string & base_dir,const ApexSession & session)1817 void RestorePreRestoreSnapshotsIfPresent(const std::string& base_dir,
1818 const ApexSession& session) {
1819 auto pre_restore_snapshot_path =
1820 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
1821 session.GetRollbackId(), kPreRestoreSuffix);
1822 if (PathExists(pre_restore_snapshot_path).ok()) {
1823 for (const auto& apex_name : session.GetApexNames()) {
1824 Result<void> result = RestoreDataDirectory(
1825 base_dir, session.GetRollbackId(), apex_name, true /* pre_restore */);
1826 if (!result.ok()) {
1827 LOG(ERROR) << "Restore of pre-restore snapshot failed for " << apex_name
1828 << ": " << result.error();
1829 }
1830 }
1831 }
1832 }
1833
RestoreDePreRestoreSnapshotsIfPresent(const ApexSession & session)1834 void RestoreDePreRestoreSnapshotsIfPresent(const ApexSession& session) {
1835 RestorePreRestoreSnapshotsIfPresent(kDeSysDataDir, session);
1836
1837 auto user_dirs = GetDeUserDirs();
1838 if (!user_dirs.ok()) {
1839 LOG(ERROR) << "Error reading user dirs to restore pre-restore snapshots"
1840 << user_dirs.error();
1841 }
1842
1843 for (const auto& user_dir : *user_dirs) {
1844 RestorePreRestoreSnapshotsIfPresent(user_dir, session);
1845 }
1846 }
1847
DeleteDePreRestoreSnapshots(const std::string & base_dir,const ApexSession & session)1848 void DeleteDePreRestoreSnapshots(const std::string& base_dir,
1849 const ApexSession& session) {
1850 auto pre_restore_snapshot_path =
1851 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
1852 session.GetRollbackId(), kPreRestoreSuffix);
1853 Result<void> result = DeleteDir(pre_restore_snapshot_path);
1854 if (!result.ok()) {
1855 LOG(ERROR) << "Deletion of pre-restore snapshot failed: " << result.error();
1856 }
1857 }
1858
DeleteDePreRestoreSnapshots(const ApexSession & session)1859 void DeleteDePreRestoreSnapshots(const ApexSession& session) {
1860 DeleteDePreRestoreSnapshots(kDeSysDataDir, session);
1861
1862 auto user_dirs = GetDeUserDirs();
1863 if (!user_dirs.ok()) {
1864 LOG(ERROR) << "Error reading user dirs to delete pre-restore snapshots"
1865 << user_dirs.error();
1866 }
1867
1868 for (const auto& user_dir : *user_dirs) {
1869 DeleteDePreRestoreSnapshots(user_dir, session);
1870 }
1871 }
1872
OnBootCompleted()1873 void OnBootCompleted() { ApexdLifecycle::GetInstance().MarkBootCompleted(); }
1874
1875 // Scans all STAGED sessions and activate them so that APEXes in those sessions
1876 // become available for activation. Sessions are updated to be ACTIVATED state,
1877 // or ACTIVATION_FAILED if something goes wrong.
1878 // Note that this doesn't abort with failed sessions. Apexd just marks them as
1879 // failed and continues activation process. It's higher level component (e.g.
1880 // system_server) that needs to handle the failures.
ActivateStagedSessions()1881 void ActivateStagedSessions() {
1882 LOG(INFO) << "Scanning " << GetSessionsDir()
1883 << " looking for sessions to be activated.";
1884
1885 auto sessions_to_activate =
1886 gSessionManager->GetSessionsInState(SessionState::STAGED);
1887 if (gSupportsFsCheckpoints) {
1888 // A session that is in the ACTIVATED state should still be re-activated if
1889 // fs checkpointing is supported. In this case, a session may be in the
1890 // ACTIVATED state yet the data/apex/active directory may have been
1891 // reverted. The session should be reverted in this scenario.
1892 auto activated_sessions =
1893 gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
1894 sessions_to_activate.insert(sessions_to_activate.end(),
1895 activated_sessions.begin(),
1896 activated_sessions.end());
1897 }
1898
1899 for (auto& session : sessions_to_activate) {
1900 auto session_id = session.GetId();
1901
1902 auto session_failed_fn = [&]() {
1903 LOG(WARNING) << "Marking session " << session_id << " as failed.";
1904 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATION_FAILED);
1905 if (!st.ok()) {
1906 LOG(WARNING) << "Failed to mark session " << session_id
1907 << " as failed : " << st.error();
1908 }
1909 };
1910 auto scope_guard = android::base::make_scope_guard(session_failed_fn);
1911
1912 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
1913 if (session.GetBuildFingerprint().compare(build_fingerprint) != 0) {
1914 auto error_message = "APEX build fingerprint has changed";
1915 LOG(ERROR) << error_message;
1916 session.SetErrorMessage(error_message);
1917 continue;
1918 }
1919
1920 // If device supports fs-checkpoint, then apex session should only be
1921 // installed when in checkpoint-mode. Otherwise, we will not be able to
1922 // revert /data on error.
1923 if (gSupportsFsCheckpoints && !gInFsCheckpointMode) {
1924 auto error_message =
1925 "Cannot install apex session if not in fs-checkpoint mode";
1926 LOG(ERROR) << error_message;
1927 session.SetErrorMessage(error_message);
1928 continue;
1929 }
1930
1931 auto apexes = ScanSessionApexFiles(session);
1932 if (!apexes.ok()) {
1933 LOG(WARNING) << apexes.error();
1934 session.SetErrorMessage(apexes.error().message());
1935 continue;
1936 }
1937
1938 auto packages = StagePackagesImpl(*apexes);
1939 if (!packages.ok()) {
1940 std::string error_message =
1941 std::format("Activation failed for packages {} : {}", *apexes,
1942 packages.error().message());
1943 LOG(ERROR) << error_message;
1944 session.SetErrorMessage(error_message);
1945 continue;
1946 }
1947
1948 // Session was OK, release scopeguard.
1949 scope_guard.Disable();
1950
1951 gChangedActiveApexes.insert_range(*packages);
1952
1953 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATED);
1954 if (!st.ok()) {
1955 LOG(ERROR) << "Failed to mark " << session
1956 << " as activated : " << st.error();
1957 }
1958 }
1959 }
1960
1961 namespace {
StageDestPath(const ApexFile & apex_file)1962 std::string StageDestPath(const ApexFile& apex_file) {
1963 return StringPrintf("%s/%s%s", gConfig->active_apex_data_dir,
1964 GetPackageId(apex_file.GetManifest()).c_str(),
1965 kApexPackageSuffix);
1966 }
1967
1968 } // namespace
1969
StagePackagesImpl(const std::vector<std::string> & tmp_paths)1970 Result<std::vector<std::string>> StagePackagesImpl(
1971 const std::vector<std::string>& tmp_paths) {
1972 if (tmp_paths.empty()) {
1973 return Error() << "Empty set of inputs";
1974 }
1975 LOG(DEBUG) << "StagePackagesImpl() for " << Join(tmp_paths, ',');
1976
1977 // Note: this function is temporary. As such the code is not optimized, e.g.,
1978 // it will open ApexFiles multiple times.
1979
1980 // 1) Verify all packages.
1981 Result<std::vector<ApexFile>> apex_files = OpenApexFiles(tmp_paths);
1982 if (!apex_files.ok()) {
1983 return apex_files.error();
1984 }
1985 for (const ApexFile& apex_file : *apex_files) {
1986 if (shim::IsShimApex(apex_file)) {
1987 // Shim apex will be validated on every boot. No need to do it here.
1988 continue;
1989 }
1990 Result<void> result = VerifyPackageBoot(apex_file);
1991 if (!result.ok()) {
1992 return result.error();
1993 }
1994 }
1995
1996 // Make sure that kActiveApexPackagesDataDir exists.
1997 auto create_dir_status =
1998 CreateDirIfNeeded(std::string(gConfig->active_apex_data_dir), 0755);
1999 if (!create_dir_status.ok()) {
2000 return create_dir_status.error();
2001 }
2002
2003 // 2) Now stage all of them.
2004
2005 // Ensure the APEX gets removed on failure.
2006 std::vector<std::string> staged_files;
2007 auto deleter = [&staged_files]() {
2008 for (const std::string& staged_path : staged_files) {
2009 if (TEMP_FAILURE_RETRY(unlink(staged_path.c_str())) != 0) {
2010 PLOG(ERROR) << "Unable to unlink " << staged_path;
2011 }
2012 }
2013 };
2014 auto scope_guard = android::base::make_scope_guard(deleter);
2015
2016 std::vector<std::string> staged_packages;
2017 for (const ApexFile& apex_file : *apex_files) {
2018 // move apex to /data/apex/active.
2019 std::string dest_path = StageDestPath(apex_file);
2020 if (access(dest_path.c_str(), F_OK) == 0) {
2021 LOG(DEBUG) << dest_path << " already exists. Deleting";
2022 if (TEMP_FAILURE_RETRY(unlink(dest_path.c_str())) != 0) {
2023 return ErrnoError() << "Failed to unlink " << dest_path;
2024 }
2025 }
2026
2027 if (link(apex_file.GetPath().c_str(), dest_path.c_str()) != 0) {
2028 return ErrnoError() << "Unable to link " << apex_file.GetPath() << " to "
2029 << dest_path;
2030 }
2031 staged_files.push_back(dest_path);
2032 staged_packages.push_back(apex_file.GetManifest().name());
2033
2034 LOG(DEBUG) << "Success linking " << apex_file.GetPath() << " to "
2035 << dest_path;
2036 }
2037
2038 scope_guard.Disable(); // Accept the state.
2039
2040 OR_RETURN(RemovePreviouslyActiveApexFiles(staged_packages, staged_files));
2041
2042 return staged_packages;
2043 }
2044
StagePackages(const std::vector<std::string> & tmp_paths)2045 Result<void> StagePackages(const std::vector<std::string>& tmp_paths) {
2046 OR_RETURN(StagePackagesImpl(tmp_paths));
2047 return {};
2048 }
2049
UnstagePackages(const std::vector<std::string> & paths)2050 Result<void> UnstagePackages(const std::vector<std::string>& paths) {
2051 if (paths.empty()) {
2052 return Errorf("Empty set of inputs");
2053 }
2054 LOG(DEBUG) << "UnstagePackages() for " << Join(paths, ',');
2055
2056 for (const std::string& path : paths) {
2057 auto apex = ApexFile::Open(path);
2058 if (!apex.ok()) {
2059 return apex.error();
2060 }
2061 if (ApexFileRepository::GetInstance().IsPreInstalledApex(*apex)) {
2062 return Error() << "Can't uninstall pre-installed apex " << path;
2063 }
2064 }
2065
2066 for (const std::string& path : paths) {
2067 if (unlink(path.c_str()) != 0) {
2068 return ErrnoError() << "Can't unlink " << path;
2069 }
2070 }
2071
2072 return {};
2073 }
2074
2075 /**
2076 * During apex installation, staged sessions located in /metadata/apex/sessions
2077 * mutate the active sessions in /data/apex/active. If some error occurs during
2078 * installation of apex, we need to revert /data/apex/active to its original
2079 * state and reboot.
2080 *
2081 * Also, we need to put staged sessions in /metadata/apex/sessions in
2082 * REVERTED state so that they do not get activated on next reboot.
2083 */
RevertActiveSessions(const std::string & crashing_native_process,const std::string & error_message)2084 Result<void> RevertActiveSessions(const std::string& crashing_native_process,
2085 const std::string& error_message) {
2086 // First check whenever there is anything to revert. If there is none, then
2087 // fail. This prevents apexd from boot looping a device in case a native
2088 // process is crashing and there are no apex updates.
2089 auto active_sessions = gSessionManager->GetSessions();
2090 active_sessions.erase(
2091 std::remove_if(active_sessions.begin(), active_sessions.end(),
2092 [](const auto& s) {
2093 return s.IsFinalized() ||
2094 s.GetState() == SessionState::UNKNOWN;
2095 }),
2096 active_sessions.end());
2097 if (active_sessions.empty()) {
2098 return Error() << "Revert requested, when there are no active sessions.";
2099 }
2100
2101 for (auto& session : active_sessions) {
2102 if (!crashing_native_process.empty()) {
2103 session.SetCrashingNativeProcess(crashing_native_process);
2104 }
2105 if (!error_message.empty()) {
2106 session.SetErrorMessage(error_message);
2107 }
2108 auto status =
2109 session.UpdateStateAndCommit(SessionState::REVERT_IN_PROGRESS);
2110 if (!status.ok()) {
2111 return Error() << "Revert of session " << session
2112 << " failed : " << status.error();
2113 }
2114 }
2115
2116 if (!gSupportsFsCheckpoints) {
2117 auto restore_status = RestoreActivePackages();
2118 if (!restore_status.ok()) {
2119 for (auto& session : active_sessions) {
2120 auto st = session.UpdateStateAndCommit(SessionState::REVERT_FAILED);
2121 LOG(DEBUG) << "Marking " << session << " as failed to revert";
2122 if (!st.ok()) {
2123 LOG(WARNING) << "Failed to mark session " << session
2124 << " as failed to revert : " << st.error();
2125 }
2126 }
2127 return restore_status;
2128 }
2129 } else {
2130 LOG(INFO) << "Not restoring active packages in checkpoint mode.";
2131 }
2132
2133 for (auto& session : active_sessions) {
2134 if (!gSupportsFsCheckpoints && session.IsRollback()) {
2135 // If snapshots have already been restored, undo that by restoring the
2136 // pre-restore snapshot.
2137 RestoreDePreRestoreSnapshotsIfPresent(session);
2138 }
2139
2140 auto status = session.UpdateStateAndCommit(SessionState::REVERTED);
2141 if (!status.ok()) {
2142 LOG(WARNING) << "Failed to mark session " << session
2143 << " as reverted : " << status.error();
2144 }
2145 }
2146
2147 return {};
2148 }
2149
RevertActiveSessionsAndReboot(const std::string & crashing_native_process,const std::string & error_message)2150 Result<void> RevertActiveSessionsAndReboot(
2151 const std::string& crashing_native_process,
2152 const std::string& error_message) {
2153 auto status = RevertActiveSessions(crashing_native_process, error_message);
2154 if (!status.ok()) {
2155 return status;
2156 }
2157 LOG(ERROR) << "Successfully reverted. Time to reboot device.";
2158 if (gInFsCheckpointMode) {
2159 Result<void> res = gVoldService->AbortChanges(
2160 "apexd_initiated" /* message */, false /* retry */);
2161 if (!res.ok()) {
2162 LOG(ERROR) << res.error();
2163 }
2164 }
2165 Reboot();
2166 return {};
2167 }
2168
CreateSharedLibsApexDir()2169 Result<void> CreateSharedLibsApexDir() {
2170 // Creates /apex/sharedlibs/lib{,64} for SharedLibs APEXes.
2171 std::string shared_libs_sub_dir =
2172 StringPrintf("%s/%s", kApexRoot, kApexSharedLibsSubDir);
2173 auto dir_exists = PathExists(shared_libs_sub_dir);
2174 if (!dir_exists.ok() || !*dir_exists) {
2175 std::error_code error_code;
2176 std::filesystem::create_directory(shared_libs_sub_dir, error_code);
2177 if (error_code) {
2178 return Error() << "Failed to create directory " << shared_libs_sub_dir
2179 << ": " << error_code.message();
2180 }
2181 }
2182 for (const auto& lib_path : {"lib", "lib64"}) {
2183 std::string apex_lib_path =
2184 StringPrintf("%s/%s", shared_libs_sub_dir.c_str(), lib_path);
2185 auto lib_dir_exists = PathExists(apex_lib_path);
2186 if (!lib_dir_exists.ok() || !*lib_dir_exists) {
2187 std::error_code error_code;
2188 std::filesystem::create_directory(apex_lib_path, error_code);
2189 if (error_code) {
2190 return Error() << "Failed to create directory " << apex_lib_path << ": "
2191 << error_code.message();
2192 }
2193 }
2194 }
2195
2196 return {};
2197 }
2198
PrepareResources(size_t loop_device_cnt,const std::vector<std::string> & apex_names)2199 void PrepareResources(size_t loop_device_cnt,
2200 const std::vector<std::string>& apex_names) {
2201 LOG(INFO) << "Need to pre-allocate " << loop_device_cnt << " loop devices";
2202 if (auto res = loop::PreAllocateLoopDevices(loop_device_cnt); !res.ok()) {
2203 LOG(ERROR) << "Failed to pre-allocate loop devices : " << res.error();
2204 }
2205
2206 DeviceMapper& dm = DeviceMapper::Instance();
2207 // Create empty dm device for each found APEX.
2208 // This is a boot time optimization that makes use of the fact that user space
2209 // paths will be created by ueventd before apexd is started, and hence
2210 // reducing the time to activate APEXEs on /data.
2211 // Note: since at this point we don't know which APEXes are updated, we are
2212 // optimistically creating a verity device for all of them. Once boot
2213 // finishes, apexd will clean up unused devices.
2214 // TODO(b/192241176): move to apexd_verity.{h,cpp}
2215 for (const auto& name : apex_names) {
2216 if (!dm.CreatePlaceholderDevice(name)) {
2217 LOG(ERROR) << "Failed to create empty device " << name;
2218 }
2219 }
2220 }
2221
OnBootstrap()2222 int OnBootstrap() {
2223 ATRACE_NAME("OnBootstrap");
2224 auto time_started = boot_clock::now();
2225
2226 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2227 Result<void> status =
2228 instance.AddPreInstalledApexParallel(gConfig->builtin_dirs);
2229 if (!status.ok()) {
2230 LOG(ERROR) << "Failed to collect APEX keys : " << status.error();
2231 return 1;
2232 }
2233
2234 std::vector<ApexFileRef> activation_list;
2235
2236 if (IsMountBeforeDataEnabled()) {
2237 activation_list = SelectApexForActivation();
2238 } else {
2239 const auto& pre_installed_apexes = instance.GetPreInstalledApexFiles();
2240 size_t loop_device_cnt = pre_installed_apexes.size();
2241 std::vector<std::string> apex_names;
2242 apex_names.reserve(loop_device_cnt);
2243 // Find all bootstrap apexes
2244 for (const auto& apex : pre_installed_apexes) {
2245 apex_names.push_back(apex.get().GetManifest().name());
2246 if (IsBootstrapApex(apex.get())) {
2247 LOG(INFO) << "Found bootstrap APEX " << apex.get().GetPath();
2248 activation_list.push_back(apex);
2249 loop_device_cnt++;
2250 }
2251 if (apex.get().GetManifest().providesharedapexlibs()) {
2252 LOG(INFO) << "Found sharedlibs APEX " << apex.get().GetPath();
2253 // Sharedlis APEX might be mounted 2 times:
2254 // * Pre-installed sharedlibs APEX will be mounted in OnStart
2255 // * Updated sharedlibs APEX (if it exists) will be mounted in OnStart
2256 //
2257 // We already counted a loop device for one of these 2 mounts, need to
2258 // add 1 more.
2259 loop_device_cnt++;
2260 }
2261 }
2262 PrepareResources(loop_device_cnt, apex_names);
2263 }
2264
2265 auto ret =
2266 ActivateApexPackages(activation_list, ActivationMode::kBootstrapMode);
2267 if (!ret.ok()) {
2268 LOG(ERROR) << "Failed to activate apexes: " << ret.error();
2269 return 1;
2270 }
2271
2272 OnAllPackagesActivated(/*is_bootstrap=*/true);
2273 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2274 boot_clock::now() - time_started)
2275 .count();
2276 LOG(INFO) << "OnBootstrap done, duration=" << time_elapsed;
2277 return 0;
2278 }
2279
InitializeVold(CheckpointInterface * checkpoint_service)2280 void InitializeVold(CheckpointInterface* checkpoint_service) {
2281 if (checkpoint_service == nullptr) {
2282 // For tests to reset global states because tests that change global states
2283 // may affect other tests.
2284 gVoldService = nullptr;
2285 gSupportsFsCheckpoints = false;
2286 gInFsCheckpointMode = false;
2287 return;
2288 }
2289 gVoldService = checkpoint_service;
2290 Result<bool> supports_fs_checkpoints = gVoldService->SupportsFsCheckpoints();
2291 if (supports_fs_checkpoints.ok()) {
2292 gSupportsFsCheckpoints = *supports_fs_checkpoints;
2293 } else {
2294 LOG(ERROR) << "Failed to check if filesystem checkpoints are supported: "
2295 << supports_fs_checkpoints.error();
2296 }
2297 if (gSupportsFsCheckpoints) {
2298 Result<bool> needs_checkpoint = gVoldService->NeedsCheckpoint();
2299 if (needs_checkpoint.ok()) {
2300 gInFsCheckpointMode = *needs_checkpoint;
2301 } else {
2302 LOG(ERROR) << "Failed to check if we're in filesystem checkpoint mode: "
2303 << needs_checkpoint.error();
2304 }
2305 }
2306 }
2307
InitializeSessionManager(ApexSessionManager * session_manager)2308 void InitializeSessionManager(ApexSessionManager* session_manager) {
2309 gSessionManager = session_manager;
2310 }
2311
Initialize(CheckpointInterface * checkpoint_service)2312 void Initialize(CheckpointInterface* checkpoint_service) {
2313 InitializeVold(checkpoint_service);
2314 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2315 Result<void> status = instance.AddPreInstalledApex(gConfig->builtin_dirs);
2316 if (!status.ok()) {
2317 LOG(ERROR) << "Failed to collect pre-installed APEX files : "
2318 << status.error();
2319 return;
2320 }
2321
2322 if (ApexFileRepository::IsBrandNewApexEnabled()) {
2323 Result<void> result = instance.AddBrandNewApexCredentialAndBlocklist(
2324 kPartitionToBrandNewApexConfigDirs);
2325 CHECK(result.ok()) << "Failed to collect pre-installed public keys and "
2326 "blocklists for brand-new APEX";
2327 }
2328
2329 gMountedApexes.PopulateFromMounts(
2330 {gConfig->active_apex_data_dir, gConfig->decompression_dir});
2331 }
2332
2333 // Note: Pre-installed apex are initialized in Initialize(CheckpointInterface*)
2334 // TODO(b/172911822): Consolidate this with Initialize() when
2335 // ApexFileRepository can act as cache and re-scanning is not expensive
InitializeDataApex()2336 void InitializeDataApex() {
2337 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2338 Result<void> status = instance.AddDataApex(kActiveApexPackagesDataDir);
2339 if (!status.ok()) {
2340 LOG(ERROR) << "Failed to collect data APEX files : " << status.error();
2341 return;
2342 }
2343 }
2344
2345 /**
2346 * For every package X, there can be at most two APEX, pre-installed vs
2347 * installed on data. We usually select only one of these APEX for each package
2348 * based on the following conditions:
2349 * - Package X must be pre-installed on one of the built-in directories.
2350 * - If there are multiple APEX, we select the one with highest version.
2351 * - If there are multiple with same version, we give priority to APEX on
2352 * /data partition.
2353 *
2354 * Typically, only one APEX is activated for each package, but APEX that provide
2355 * shared libs are exceptions. We have to activate both APEX for them.
2356 *
2357 * @return list of ApexFile that needs to be activated
2358 */
SelectApexForActivation()2359 std::vector<ApexFileRef> SelectApexForActivation() {
2360 LOG(INFO) << "Selecting APEX for activation";
2361 std::vector<ApexFileRef> activation_list;
2362 const auto& instance = ApexFileRepository::GetInstance();
2363 const auto& all_apex = instance.AllApexFilesByName();
2364 activation_list.reserve(all_apex.size());
2365 // For every package X, select which APEX to activate
2366 for (auto& apex_it : all_apex) {
2367 const std::string& package_name = apex_it.first;
2368 const std::vector<ApexFileRef>& apex_files = apex_it.second;
2369
2370 if (apex_files.size() > 2 || apex_files.size() == 0) {
2371 LOG(FATAL) << "Unexpectedly found more than two versions or none for "
2372 "APEX package "
2373 << package_name;
2374 continue;
2375 }
2376
2377 if (apex_files.size() == 1) {
2378 LOG(DEBUG) << "Selecting the only APEX: " << package_name << " "
2379 << apex_files[0].get().GetPath();
2380 activation_list.emplace_back(apex_files[0]);
2381 continue;
2382 }
2383
2384 // TODO(b/179497746): Now that we are dealing with list of reference, this
2385 // selection process can be simplified by sorting the vector.
2386
2387 // Given an APEX A and the version of the other APEX B, should we activate
2388 // it?
2389 auto select_apex = [&instance, &activation_list](
2390 const ApexFileRef& a_ref,
2391 const int version_b) mutable {
2392 const ApexFile& a = a_ref.get();
2393 // If A has higher version than B, then it should be activated
2394 const bool higher_version = a.GetManifest().version() > version_b;
2395 // If A has same version as B, then data version should get activated
2396 const bool same_version_priority_to_data =
2397 a.GetManifest().version() == version_b &&
2398 !instance.IsPreInstalledApex(a);
2399
2400 // APEX that provides shared library are special:
2401 // - if preinstalled version is lower than data version, both versions
2402 // are activated.
2403 // - if preinstalled version is equal to data version, data version only
2404 // is activated.
2405 // - if preinstalled version is higher than data version, preinstalled
2406 // version only is activated.
2407 const bool provides_shared_apex_libs =
2408 a.GetManifest().providesharedapexlibs();
2409 bool activate = false;
2410 if (provides_shared_apex_libs) {
2411 // preinstalled version gets activated in all cases except when same
2412 // version as data.
2413 if (instance.IsPreInstalledApex(a) &&
2414 (a.GetManifest().version() != version_b)) {
2415 LOG(DEBUG) << "Activating preinstalled shared libs APEX: "
2416 << a.GetManifest().name() << " " << a.GetPath();
2417 activate = true;
2418 }
2419 // data version gets activated in all cases except when its version
2420 // is lower than preinstalled version.
2421 if (!instance.IsPreInstalledApex(a) &&
2422 (a.GetManifest().version() >= version_b)) {
2423 LOG(DEBUG) << "Activating shared libs APEX: "
2424 << a.GetManifest().name() << " " << a.GetPath();
2425 activate = true;
2426 }
2427 } else if (higher_version || same_version_priority_to_data) {
2428 LOG(DEBUG) << "Selecting between two APEX: " << a.GetManifest().name()
2429 << " " << a.GetPath();
2430 activate = true;
2431 }
2432 if (activate) {
2433 activation_list.emplace_back(a_ref);
2434 }
2435 };
2436 const int version_0 = apex_files[0].get().GetManifest().version();
2437 const int version_1 = apex_files[1].get().GetManifest().version();
2438 select_apex(apex_files[0].get(), version_1);
2439 select_apex(apex_files[1].get(), version_0);
2440 }
2441 return activation_list;
2442 }
2443
2444 namespace {
2445
OpenAndValidateDecompressedApex(const ApexFile & capex,const std::string & apex_path)2446 Result<ApexFile> OpenAndValidateDecompressedApex(const ApexFile& capex,
2447 const std::string& apex_path) {
2448 auto apex = ApexFile::Open(apex_path);
2449 if (!apex.ok()) {
2450 return Error() << "Failed to open decompressed APEX: " << apex.error();
2451 }
2452 auto result = ValidateDecompressedApex(capex, *apex);
2453 if (!result.ok()) {
2454 return result.error();
2455 }
2456 auto ctx = GetfileconPath(apex_path);
2457 if (!ctx.ok()) {
2458 return ctx.error();
2459 }
2460 if (!StartsWith(*ctx, gConfig->active_apex_selinux_ctx)) {
2461 return Error() << apex_path << " has wrong SELinux context " << *ctx;
2462 }
2463 return std::move(*apex);
2464 }
2465
2466 // Process a single compressed APEX. Returns the decompressed APEX if
2467 // successful.
ProcessCompressedApex(const ApexFile & capex,bool is_ota_chroot)2468 Result<ApexFile> ProcessCompressedApex(const ApexFile& capex,
2469 bool is_ota_chroot) {
2470 LOG(INFO) << "Processing compressed APEX " << capex.GetPath();
2471 const auto decompressed_apex_path =
2472 StringPrintf("%s/%s%s", gConfig->decompression_dir,
2473 GetPackageId(capex.GetManifest()).c_str(),
2474 kDecompressedApexPackageSuffix);
2475 // Check if decompressed APEX already exist
2476 auto decompressed_path_exists = PathExists(decompressed_apex_path);
2477 if (decompressed_path_exists.ok() && *decompressed_path_exists) {
2478 // Check if existing decompressed APEX is valid
2479 auto result =
2480 OpenAndValidateDecompressedApex(capex, decompressed_apex_path);
2481 if (result.ok()) {
2482 LOG(INFO) << "Skipping decompression for " << capex.GetPath();
2483 return result;
2484 }
2485 // Do not delete existing decompressed APEX when is_ota_chroot is true
2486 if (!is_ota_chroot) {
2487 // Existing decompressed APEX is not valid. We will have to redecompress
2488 LOG(WARNING) << "Existing decompressed APEX is invalid: "
2489 << result.error();
2490 RemoveFileIfExists(decompressed_apex_path);
2491 }
2492 }
2493
2494 // We can also reuse existing OTA APEX, depending on situation
2495 auto ota_apex_path = StringPrintf("%s/%s%s", gConfig->decompression_dir,
2496 GetPackageId(capex.GetManifest()).c_str(),
2497 kOtaApexPackageSuffix);
2498 auto ota_path_exists = PathExists(ota_apex_path);
2499 if (ota_path_exists.ok() && *ota_path_exists) {
2500 if (is_ota_chroot) {
2501 // During ota_chroot, we try to reuse ota APEX as is
2502 auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path);
2503 if (result.ok()) {
2504 LOG(INFO) << "Skipping decompression for " << ota_apex_path;
2505 return result;
2506 }
2507 // Existing ota_apex is not valid. We will have to decompress
2508 LOG(WARNING) << "Existing decompressed OTA APEX is invalid: "
2509 << result.error();
2510 RemoveFileIfExists(ota_apex_path);
2511 } else {
2512 // During boot, we can avoid decompression by renaming OTA apex
2513 // to expected decompressed_apex path
2514
2515 // Check if ota_apex APEX is valid
2516 auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path);
2517 if (result.ok()) {
2518 // ota_apex matches with capex. Slot has been switched.
2519
2520 // Rename ota_apex to expected decompressed_apex path
2521 if (rename(ota_apex_path.c_str(), decompressed_apex_path.c_str()) ==
2522 0) {
2523 // Check if renamed decompressed APEX is valid
2524 result =
2525 OpenAndValidateDecompressedApex(capex, decompressed_apex_path);
2526 if (result.ok()) {
2527 LOG(INFO) << "Renamed " << ota_apex_path << " to "
2528 << decompressed_apex_path;
2529 return result;
2530 }
2531 // Renamed ota_apex is not valid. We will have to decompress
2532 LOG(WARNING) << "Renamed decompressed APEX from " << ota_apex_path
2533 << " to " << decompressed_apex_path
2534 << " is invalid: " << result.error();
2535 RemoveFileIfExists(decompressed_apex_path);
2536 } else {
2537 PLOG(ERROR) << "Failed to rename file " << ota_apex_path;
2538 }
2539 }
2540 }
2541 }
2542
2543 // There was no way to avoid decompression
2544
2545 // Clean up reserved space before decompressing capex
2546 if (auto ret = DeleteDirContent(gConfig->ota_reserved_dir); !ret.ok()) {
2547 LOG(ERROR) << "Failed to clean up reserved space: " << ret.error();
2548 }
2549
2550 auto decompression_dest =
2551 is_ota_chroot ? ota_apex_path : decompressed_apex_path;
2552 auto scope_guard = android::base::make_scope_guard(
2553 [&]() { RemoveFileIfExists(decompression_dest); });
2554
2555 auto decompression_result = capex.Decompress(decompression_dest);
2556 if (!decompression_result.ok()) {
2557 return Error() << "Failed to decompress : " << capex.GetPath().c_str()
2558 << " " << decompression_result.error();
2559 }
2560
2561 // Fix label of decompressed file
2562 auto restore = RestoreconPath(decompression_dest);
2563 if (!restore.ok()) {
2564 return restore.error();
2565 }
2566
2567 // Validate the newly decompressed APEX
2568 auto return_apex = OpenAndValidateDecompressedApex(capex, decompression_dest);
2569 if (!return_apex.ok()) {
2570 return Error() << "Failed to decompress CAPEX: " << return_apex.error();
2571 }
2572
2573 gChangedActiveApexes.insert(return_apex->GetManifest().name());
2574 /// Release compressed blocks in case decompression_dest is on f2fs-compressed
2575 // filesystem.
2576 ReleaseF2fsCompressedBlocks(decompression_dest);
2577
2578 scope_guard.Disable();
2579 return return_apex;
2580 }
2581 } // namespace
2582
2583 /**
2584 * For each compressed APEX, decompress it to kApexDecompressedDir
2585 * and return the decompressed APEX.
2586 *
2587 * Returns list of decompressed APEX.
2588 */
ProcessCompressedApex(const std::vector<ApexFileRef> & compressed_apex,bool is_ota_chroot)2589 std::vector<ApexFile> ProcessCompressedApex(
2590 const std::vector<ApexFileRef>& compressed_apex, bool is_ota_chroot) {
2591 LOG(INFO) << "Processing compressed APEX";
2592
2593 std::vector<ApexFile> decompressed_apex_list;
2594 for (const ApexFile& capex : compressed_apex) {
2595 if (!capex.IsCompressed()) {
2596 continue;
2597 }
2598
2599 auto decompressed_apex = ProcessCompressedApex(capex, is_ota_chroot);
2600 if (decompressed_apex.ok()) {
2601 decompressed_apex_list.emplace_back(std::move(*decompressed_apex));
2602 continue;
2603 }
2604 LOG(ERROR) << "Failed to process compressed APEX: "
2605 << decompressed_apex.error();
2606 }
2607 return decompressed_apex_list;
2608 }
2609
ValidateDecompressedApex(const ApexFile & capex,const ApexFile & apex)2610 Result<void> ValidateDecompressedApex(const ApexFile& capex,
2611 const ApexFile& apex) {
2612 // Decompressed APEX must have same public key as CAPEX
2613 if (capex.GetBundledPublicKey() != apex.GetBundledPublicKey()) {
2614 return Error()
2615 << "Public key of compressed APEX is different than original "
2616 << "APEX for " << apex.GetPath();
2617 }
2618 // Decompressed APEX must have same version as CAPEX
2619 if (capex.GetManifest().version() != apex.GetManifest().version()) {
2620 return Error()
2621 << "Compressed APEX has different version than decompressed APEX "
2622 << apex.GetPath();
2623 }
2624 // Decompressed APEX must have same root digest as what is stored in CAPEX
2625 auto apex_verity = apex.VerifyApexVerity(apex.GetBundledPublicKey());
2626 if (!apex_verity.ok() ||
2627 capex.GetManifest().capexmetadata().originalapexdigest() !=
2628 apex_verity->root_digest) {
2629 return Error() << "Root digest of " << apex.GetPath()
2630 << " does not match with" << " expected root digest in "
2631 << capex.GetPath();
2632 }
2633 return {};
2634 }
2635
OnStart()2636 void OnStart() {
2637 ATRACE_NAME("OnStart");
2638 LOG(INFO) << "Marking APEXd as starting";
2639 auto time_started = boot_clock::now();
2640 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusStarting)) {
2641 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
2642 << kApexStatusStarting;
2643 }
2644
2645 // Ask whether we should revert any active sessions; this can happen if
2646 // we've exceeded the retry count on a device that supports filesystem
2647 // checkpointing.
2648 if (gSupportsFsCheckpoints) {
2649 Result<bool> needs_revert = gVoldService->NeedsRollback();
2650 if (!needs_revert.ok()) {
2651 LOG(ERROR) << "Failed to check if we need a revert: "
2652 << needs_revert.error();
2653 } else if (*needs_revert) {
2654 LOG(INFO) << "Exceeded number of session retries ("
2655 << kNumRetriesWhenCheckpointingEnabled
2656 << "). Starting a revert";
2657 RevertActiveSessions("", "");
2658 }
2659 }
2660
2661 // Create directories for APEX shared libraries.
2662 auto sharedlibs_apex_dir = CreateSharedLibsApexDir();
2663 if (!sharedlibs_apex_dir.ok()) {
2664 LOG(ERROR) << sharedlibs_apex_dir.error();
2665 }
2666
2667 // If there is any new apex to be installed on /data/app-staging, hardlink
2668 // them to /data/apex/active first.
2669 ActivateStagedSessions();
2670 if (auto status = ApexFileRepository::GetInstance().AddDataApex(
2671 gConfig->active_apex_data_dir);
2672 !status.ok()) {
2673 LOG(ERROR) << "Failed to collect data APEX files : " << status.error();
2674 }
2675
2676 auto status = ResumeRevertIfNeeded();
2677 if (!status.ok()) {
2678 LOG(ERROR) << "Failed to resume revert : " << status.error();
2679 }
2680
2681 // Group every ApexFile on device by name
2682 auto activation_list = SelectApexForActivation();
2683
2684 // Process compressed APEX, if any
2685 std::vector<ApexFileRef> compressed_apex;
2686 for (auto it = activation_list.begin(); it != activation_list.end();) {
2687 if (it->get().IsCompressed()) {
2688 compressed_apex.emplace_back(*it);
2689 it = activation_list.erase(it);
2690 } else {
2691 it++;
2692 }
2693 }
2694 std::vector<ApexFile> decompressed_apex;
2695 if (!compressed_apex.empty()) {
2696 decompressed_apex =
2697 ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ false);
2698 for (const ApexFile& apex_file : decompressed_apex) {
2699 activation_list.emplace_back(std::cref(apex_file));
2700 }
2701 }
2702
2703 // TODO(b/179248390): activate parallelly if possible
2704 auto activate_status =
2705 ActivateApexPackages(activation_list, ActivationMode::kBootMode);
2706 if (!activate_status.ok()) {
2707 std::string error_message =
2708 StringPrintf("Failed to activate packages: %s",
2709 activate_status.error().message().c_str());
2710 LOG(ERROR) << error_message;
2711 Result<void> revert_status =
2712 RevertActiveSessionsAndReboot("", error_message);
2713 if (!revert_status.ok()) {
2714 LOG(ERROR) << "Failed to revert : " << revert_status.error();
2715 }
2716 auto retry_status =
2717 ActivateMissingApexes(activation_list, ActivationMode::kBootMode);
2718 if (!retry_status.ok()) {
2719 LOG(ERROR) << retry_status.error();
2720 }
2721 }
2722
2723 // Clean up inactive APEXes on /data. We don't need them anyway.
2724 RemoveInactiveDataApex();
2725
2726 // Now that APEXes are mounted, snapshot or restore DE_sys data.
2727 SnapshotOrRestoreDeSysData();
2728
2729 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2730 boot_clock::now() - time_started)
2731 .count();
2732 LOG(INFO) << "OnStart done, duration=" << time_elapsed;
2733 }
2734
OnAllPackagesActivated(bool is_bootstrap)2735 void OnAllPackagesActivated(bool is_bootstrap) {
2736 auto result = EmitApexInfoList(is_bootstrap);
2737 if (!result.ok()) {
2738 LOG(ERROR) << "cannot emit apex info list: " << result.error();
2739 }
2740
2741 // Because apexd in bootstrap mode runs in blocking mode
2742 // we don't have to set as activated.
2743 if (is_bootstrap) {
2744 return;
2745 }
2746
2747 // Set a system property to let other components know that APEXs are
2748 // activated, but are not yet ready to be used. init is expected to wait
2749 // for this status before performing configuration based on activated
2750 // apexes. Other components that need to use APEXs should wait for the
2751 // ready state instead.
2752 LOG(INFO) << "Marking APEXd as activated";
2753 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusActivated)) {
2754 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
2755 << kApexStatusActivated;
2756 }
2757 }
2758
OnAllPackagesReady()2759 void OnAllPackagesReady() {
2760 // Set a system property to let other components know that APEXs are
2761 // correctly mounted and ready to be used. Before using any file from APEXs,
2762 // they can query this system property to ensure that they are okay to
2763 // access. Or they may have a on-property trigger to delay a task until
2764 // APEXs become ready.
2765 LOG(INFO) << "Marking APEXd as ready";
2766 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusReady)) {
2767 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
2768 << kApexStatusReady;
2769 }
2770 // Since apexd.status property is a system property, we expose yet another
2771 // property as system_restricted_prop so that, for example, vendor can rely on
2772 // the "ready" event.
2773 if (!SetProperty(kApexAllReadyProp, "true")) {
2774 PLOG(ERROR) << "Failed to set " << kApexAllReadyProp << " to true";
2775 }
2776 }
2777
SubmitStagedSession(const int session_id,const std::vector<int> & child_session_ids,const bool has_rollback_enabled,const bool is_rollback,const int rollback_id)2778 Result<std::vector<ApexFile>> SubmitStagedSession(
2779 const int session_id, const std::vector<int>& child_session_ids,
2780 const bool has_rollback_enabled, const bool is_rollback,
2781 const int rollback_id) REQUIRES(!gInstallLock) {
2782 auto install_guard = std::scoped_lock{gInstallLock};
2783 auto event = InstallRequestedEvent(InstallType::Staged, is_rollback);
2784
2785 if (session_id == 0) {
2786 return Error() << "Session id was not provided.";
2787 }
2788 if (has_rollback_enabled && is_rollback) {
2789 return Error() << "Cannot set session " << session_id << " as both a"
2790 << " rollback and enabled for rollback.";
2791 }
2792
2793 if (!gSupportsFsCheckpoints) {
2794 Result<void> backup_status = BackupActivePackages();
2795 if (!backup_status.ok()) {
2796 // Do not proceed with staged install without backup
2797 return backup_status.error();
2798 }
2799 }
2800
2801 auto ret =
2802 OR_RETURN(OpenApexFilesInSessionDirs(session_id, child_session_ids));
2803 event.AddFiles(ret);
2804
2805 auto result = OR_RETURN(VerifyPackagesStagedInstall(ret));
2806 event.AddHals(result.apex_hals);
2807
2808 std::vector<std::string> apex_images;
2809 if (IsMountBeforeDataEnabled()) {
2810 apex_images = OR_RETURN(GetImageManager()->PinApexFiles(ret));
2811 }
2812
2813 // The incoming session is now verified by apexd. From now on, apexd keeps its
2814 // own session data. The session should be marked as "ready" so that it
2815 // becomes STAGED. On next reboot, STAGED sessions become ACTIVATED, which
2816 // means the APEXes in those sessions are in "active" state and to be
2817 // activated.
2818 //
2819 // SubmitStagedSession MarkStagedSessionReady
2820 // | |
2821 // V V
2822 // VERIFIED (created) ---------------> STAGED
2823 // |
2824 // | <-- ActivateStagedSessions
2825 // V
2826 // ACTIVATED
2827 //
2828
2829 auto session = gSessionManager->CreateSession(session_id);
2830 if (!session.ok()) {
2831 return session.error();
2832 }
2833 (*session).SetChildSessionIds(child_session_ids);
2834 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
2835 (*session).SetBuildFingerprint(build_fingerprint);
2836 session->SetHasRollbackEnabled(has_rollback_enabled);
2837 session->SetIsRollback(is_rollback);
2838 session->SetRollbackId(rollback_id);
2839 for (const auto& apex_file : ret) {
2840 session->AddApexName(apex_file.GetManifest().name());
2841 }
2842 session->SetApexFileHashes(event.GetFileHashes());
2843 session->SetApexImages(apex_images);
2844 Result<void> commit_status =
2845 (*session).UpdateStateAndCommit(SessionState::VERIFIED);
2846 if (!commit_status.ok()) {
2847 return commit_status.error();
2848 }
2849
2850 for (const auto& apex : ret) {
2851 // Release compressed blocks in case /data is f2fs-compressed filesystem.
2852 ReleaseF2fsCompressedBlocks(apex.GetPath());
2853 }
2854
2855 event.MarkSucceeded();
2856
2857 return ret;
2858 }
2859
MarkStagedSessionReady(const int session_id)2860 Result<void> MarkStagedSessionReady(const int session_id)
2861 REQUIRES(!gInstallLock) {
2862 auto install_guard = std::scoped_lock{gInstallLock};
2863 auto session = gSessionManager->GetSession(session_id);
2864 if (!session.ok()) {
2865 return session.error();
2866 }
2867 // We should only accept sessions in SessionState::VERIFIED or
2868 // SessionState::STAGED state. In the SessionState::STAGED case, this
2869 // function is effectively a no-op.
2870 auto session_state = (*session).GetState();
2871 if (session_state == SessionState::STAGED) {
2872 return {};
2873 }
2874 if (session_state == SessionState::VERIFIED) {
2875 return (*session).UpdateStateAndCommit(SessionState::STAGED);
2876 }
2877 return Error() << "Invalid state for session " << session_id
2878 << ". Cannot mark it as ready.";
2879 }
2880
MarkStagedSessionSuccessful(const int session_id)2881 Result<void> MarkStagedSessionSuccessful(const int session_id) {
2882 auto session = gSessionManager->GetSession(session_id);
2883 if (!session.ok()) {
2884 return session.error();
2885 }
2886 // Only SessionState::ACTIVATED or SessionState::SUCCESS states are accepted.
2887 // In the SessionState::SUCCESS state, this function is a no-op.
2888 if (session->GetState() == SessionState::SUCCESS) {
2889 return {};
2890 } else if (session->GetState() == SessionState::ACTIVATED) {
2891 // TODO: Handle activated apexes still unavailable to apexd at this time.
2892 // This is because apexd is started before this activation with a linker
2893 // configuration which doesn't know about statsd
2894 SendSessionApexInstallationEndedAtom(*session, InstallResult::Success);
2895 auto cleanup_status = DeleteBackup();
2896 if (!cleanup_status.ok()) {
2897 return Error() << "Failed to mark session " << *session
2898 << " as successful : " << cleanup_status.error();
2899 }
2900 if (session->IsRollback() && !gSupportsFsCheckpoints) {
2901 DeleteDePreRestoreSnapshots(*session);
2902 }
2903 return session->UpdateStateAndCommit(SessionState::SUCCESS);
2904 } else {
2905 return Error() << "Session " << *session << " can not be marked successful";
2906 }
2907 }
2908
2909 // Removes APEXes on /data that have not been activated
RemoveInactiveDataApex()2910 void RemoveInactiveDataApex() {
2911 std::vector<std::string> all_apex_files;
2912 Result<std::vector<std::string>> active_apex =
2913 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
2914 if (!active_apex.ok()) {
2915 LOG(ERROR) << "Failed to scan " << gConfig->active_apex_data_dir << " : "
2916 << active_apex.error();
2917 } else {
2918 all_apex_files.insert(all_apex_files.end(),
2919 std::make_move_iterator(active_apex->begin()),
2920 std::make_move_iterator(active_apex->end()));
2921 }
2922 Result<std::vector<std::string>> decompressed_apex = FindFilesBySuffix(
2923 gConfig->decompression_dir, {kDecompressedApexPackageSuffix});
2924 if (!decompressed_apex.ok()) {
2925 LOG(ERROR) << "Failed to scan " << gConfig->decompression_dir << " : "
2926 << decompressed_apex.error();
2927 } else {
2928 all_apex_files.insert(all_apex_files.end(),
2929 std::make_move_iterator(decompressed_apex->begin()),
2930 std::make_move_iterator(decompressed_apex->end()));
2931 }
2932
2933 for (const auto& path : all_apex_files) {
2934 if (!apexd_private::IsMounted(path)) {
2935 LOG(INFO) << "Removing inactive data APEX " << path;
2936 if (unlink(path.c_str()) != 0) {
2937 PLOG(ERROR) << "Failed to unlink inactive data APEX " << path;
2938 }
2939 }
2940 }
2941 }
2942
IsApexDevice(const std::string & dev_name)2943 bool IsApexDevice(const std::string& dev_name) {
2944 auto& repo = ApexFileRepository::GetInstance();
2945 for (const auto& apex : repo.GetPreInstalledApexFiles()) {
2946 if (StartsWith(dev_name, apex.get().GetManifest().name())) {
2947 return true;
2948 }
2949 }
2950 return false;
2951 }
2952
2953 // TODO(b/192241176): move to apexd_verity.{h,cpp}.
DeleteUnusedVerityDevices()2954 void DeleteUnusedVerityDevices() {
2955 DeviceMapper& dm = DeviceMapper::Instance();
2956 std::vector<DeviceMapper::DmBlockDevice> all_devices;
2957 if (!dm.GetAvailableDevices(&all_devices)) {
2958 LOG(WARNING) << "Failed to fetch dm devices";
2959 return;
2960 }
2961 for (const auto& dev : all_devices) {
2962 auto state = dm.GetState(dev.name());
2963 if (state == DmDeviceState::SUSPENDED && IsApexDevice(dev.name())) {
2964 LOG(INFO) << "Deleting unused dm device " << dev.name();
2965 auto res = DeleteDmDevice(dev.name(), /* deferred= */ false);
2966 if (!res.ok()) {
2967 LOG(WARNING) << res.error();
2968 }
2969 }
2970 }
2971 }
2972
BootCompletedCleanup()2973 void BootCompletedCleanup() {
2974 gSessionManager->DeleteFinalizedSessions();
2975 DeleteUnusedVerityDevices();
2976 }
2977
UnmountAll(bool also_include_staged_apexes)2978 int UnmountAll(bool also_include_staged_apexes) {
2979 std::vector<std::string> data_dirs = {gConfig->active_apex_data_dir,
2980 gConfig->decompression_dir};
2981
2982 if (also_include_staged_apexes) {
2983 for (const ApexSession& session :
2984 gSessionManager->GetSessionsInState(SessionState::STAGED)) {
2985 std::vector<std::string> dirs_to_scan =
2986 session.GetStagedApexDirs(gConfig->staged_session_dir);
2987 std::move(dirs_to_scan.begin(), dirs_to_scan.end(),
2988 std::back_inserter(data_dirs));
2989 }
2990 }
2991
2992 gMountedApexes.PopulateFromMounts(data_dirs);
2993 int ret = 0;
2994 gMountedApexes.ForallMountedApexes([&](const std::string& /*package*/,
2995 const MountedApexData& data,
2996 bool latest) {
2997 LOG(INFO) << "Unmounting " << data.full_path << " mounted on "
2998 << data.mount_point;
2999 auto apex = ApexFile::Open(data.full_path);
3000 if (!apex.ok()) {
3001 LOG(ERROR) << "Failed to open " << data.full_path << " : "
3002 << apex.error();
3003 ret = 1;
3004 return;
3005 }
3006 if (latest && !apex->GetManifest().providesharedapexlibs()) {
3007 auto pos = data.mount_point.find('@');
3008 CHECK(pos != std::string::npos);
3009 std::string bind_mount = data.mount_point.substr(0, pos);
3010 if (umount2(bind_mount.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0) {
3011 PLOG(ERROR) << "Failed to unmount bind-mount " << bind_mount;
3012 ret = 1;
3013 return;
3014 }
3015 }
3016 if (auto status = Unmount(data, /* deferred= */ true); !status.ok()) {
3017 LOG(ERROR) << "Failed to unmount " << data.mount_point << " : "
3018 << status.error();
3019 ret = 1;
3020 }
3021 });
3022 return ret;
3023 }
3024
3025 // Given a single new APEX incoming via OTA, should we allocate space for it?
ShouldAllocateSpaceForDecompression(const std::string & new_apex_name,const int64_t new_apex_version,const ApexFileRepository & instance,const MountedApexDatabase & db)3026 bool ShouldAllocateSpaceForDecompression(const std::string& new_apex_name,
3027 const int64_t new_apex_version,
3028 const ApexFileRepository& instance,
3029 const MountedApexDatabase& db) {
3030 // An apex at most will have two versions on device: pre-installed and data.
3031
3032 // Check if there is a pre-installed version for the new apex.
3033 if (!instance.HasPreInstalledVersion(new_apex_name)) {
3034 // We are introducing a new APEX that doesn't exist at all
3035 return true;
3036 }
3037
3038 // Check if there is a data apex
3039 // If the current active apex is preinstalled, then it means no data apex.
3040 auto current_active = db.GetLatestMountedApex(new_apex_name);
3041 if (!current_active) {
3042 LOG(ERROR) << "Failed to get mount data for : " << new_apex_name
3043 << " is preinstalled, but not activated.";
3044 return true;
3045 }
3046 auto current_active_apex_file = ApexFile::Open(current_active->full_path);
3047 if (!current_active_apex_file.ok()) {
3048 LOG(ERROR) << "Failed to open " << current_active->full_path << " : "
3049 << current_active_apex_file.error();
3050 return true;
3051 }
3052 if (instance.IsPreInstalledApex(*current_active_apex_file)) {
3053 return true;
3054 }
3055
3056 // From here on, data apex exists. So we should compare directly against data
3057 // apex.
3058 const int64_t data_version =
3059 current_active_apex_file->GetManifest().version();
3060 // We only decompress the new_apex if it has higher version than data apex.
3061 return new_apex_version > data_version;
3062 }
3063
CalculateSizeForCompressedApex(const std::vector<std::tuple<std::string,int64_t,int64_t>> & compressed_apexes)3064 int64_t CalculateSizeForCompressedApex(
3065 const std::vector<std::tuple<std::string, int64_t, int64_t>>&
3066 compressed_apexes) {
3067 const auto& instance = ApexFileRepository::GetInstance();
3068 int64_t result = 0;
3069 for (const auto& compressed_apex : compressed_apexes) {
3070 std::string module_name;
3071 int64_t version_code;
3072 int64_t decompressed_size;
3073 std::tie(module_name, version_code, decompressed_size) = compressed_apex;
3074 if (ShouldAllocateSpaceForDecompression(module_name, version_code, instance,
3075 gMountedApexes)) {
3076 result += decompressed_size;
3077 }
3078 }
3079 return result;
3080 }
3081
CastPartition(ApexPartition in)3082 std::string CastPartition(ApexPartition in) {
3083 switch (in) {
3084 case ApexPartition::System:
3085 return "SYSTEM";
3086 case ApexPartition::SystemExt:
3087 return "SYSTEM_EXT";
3088 case ApexPartition::Product:
3089 return "PRODUCT";
3090 case ApexPartition::Vendor:
3091 return "VENDOR";
3092 case ApexPartition::Odm:
3093 return "ODM";
3094 }
3095 }
3096
CollectApexInfoList(std::ostream & os,const std::vector<ApexFile> & active_apexs,const std::vector<ApexFile> & inactive_apexs)3097 void CollectApexInfoList(std::ostream& os,
3098 const std::vector<ApexFile>& active_apexs,
3099 const std::vector<ApexFile>& inactive_apexs) {
3100 std::vector<com::android::apex::ApexInfo> apex_infos;
3101
3102 auto convert_to_autogen = [&apex_infos](const ApexFile& apex,
3103 bool is_active) {
3104 auto& instance = ApexFileRepository::GetInstance();
3105
3106 auto preinstalled_path =
3107 instance.GetPreinstalledPath(apex.GetManifest().name());
3108 std::optional<std::string> preinstalled_module_path;
3109 if (preinstalled_path.ok()) {
3110 preinstalled_module_path = *preinstalled_path;
3111 }
3112
3113 auto partition = CastPartition(OR_FATAL(instance.GetPartition(apex)));
3114
3115 std::optional<int64_t> mtime =
3116 instance.GetBlockApexLastUpdateSeconds(apex.GetPath());
3117 if (!mtime.has_value()) {
3118 struct stat stat_buf;
3119 if (stat(apex.GetPath().c_str(), &stat_buf) == 0) {
3120 mtime.emplace(stat_buf.st_mtime);
3121 } else {
3122 PLOG(WARNING) << "Failed to stat " << apex.GetPath();
3123 }
3124 }
3125 com::android::apex::ApexInfo apex_info(
3126 apex.GetManifest().name(), apex.GetPath(), preinstalled_module_path,
3127 apex.GetManifest().version(), apex.GetManifest().versionname(),
3128 instance.IsPreInstalledApex(apex), is_active, mtime,
3129 apex.GetManifest().providesharedapexlibs(), partition);
3130 apex_infos.emplace_back(std::move(apex_info));
3131 };
3132 for (const auto& apex : active_apexs) {
3133 convert_to_autogen(apex, /* is_active= */ true);
3134 }
3135 for (const auto& apex : inactive_apexs) {
3136 convert_to_autogen(apex, /* is_active= */ false);
3137 }
3138 com::android::apex::ApexInfoList apex_info_list(apex_infos);
3139 com::android::apex::write(os, apex_info_list);
3140 }
3141
3142 // Reserve |size| bytes in |dest_dir| by creating a zero-filled file.
3143 // Also, we always clean up ota_apex that has been processed as
3144 // part of pre-reboot decompression whenever we reserve space.
ReserveSpaceForCompressedApex(int64_t size,const std::string & dest_dir)3145 Result<void> ReserveSpaceForCompressedApex(int64_t size,
3146 const std::string& dest_dir) {
3147 if (size < 0) {
3148 return Error() << "Cannot reserve negative byte of space";
3149 }
3150
3151 // Since we are reserving space, then we must be preparing for a new OTA.
3152 // Clean up any processed ota_apex from previous OTA.
3153 auto ota_apex_files =
3154 FindFilesBySuffix(gConfig->decompression_dir, {kOtaApexPackageSuffix});
3155 if (!ota_apex_files.ok()) {
3156 return Error() << "Failed to clean up ota_apex: " << ota_apex_files.error();
3157 }
3158 for (const std::string& ota_apex : *ota_apex_files) {
3159 RemoveFileIfExists(ota_apex);
3160 }
3161
3162 auto file_path = StringPrintf("%s/full.tmp", dest_dir.c_str());
3163 if (size == 0) {
3164 LOG(INFO) << "Cleaning up reserved space for compressed APEX";
3165 // Ota is being cancelled. Clean up reserved space
3166 RemoveFileIfExists(file_path);
3167 return {};
3168 }
3169
3170 LOG(INFO) << "Reserving " << size << " bytes for compressed APEX";
3171 unique_fd dest_fd(
3172 open(file_path.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT, 0644));
3173 if (dest_fd.get() == -1) {
3174 return ErrnoError() << "Failed to open file for reservation "
3175 << file_path.c_str();
3176 }
3177
3178 // Resize to required size, posix_fallocate will not shrink files so resize
3179 // is needed.
3180 std::error_code ec;
3181 std::filesystem::resize_file(file_path, size, ec);
3182 if (ec) {
3183 RemoveFileIfExists(file_path);
3184 return ErrnoError() << "Failed to resize file " << file_path.c_str()
3185 << " : " << ec.message();
3186 }
3187
3188 // Allocate blocks for the requested size.
3189 // resize_file will create sparse file with 0 blocks on filesystems that
3190 // supports sparse files.
3191 if ((errno = posix_fallocate(dest_fd.get(), 0, size))) {
3192 RemoveFileIfExists(file_path);
3193 return ErrnoError() << "Failed to allocate blocks for file "
3194 << file_path.c_str();
3195 }
3196
3197 return {};
3198 }
3199
3200 // Adds block apexes if system property is set.
AddBlockApex(ApexFileRepository & instance)3201 Result<int> AddBlockApex(ApexFileRepository& instance) {
3202 auto prop = GetProperty(gConfig->vm_payload_metadata_partition_prop, "");
3203 if (prop != "") {
3204 auto block_count = instance.AddBlockApex(prop);
3205 if (!block_count.ok()) {
3206 return Error() << "Failed to scan block APEX files: "
3207 << block_count.error();
3208 }
3209 return block_count;
3210 } else {
3211 LOG(INFO) << "No block apex metadata partition found, not adding block "
3212 << "apexes";
3213 }
3214 return 0;
3215 }
3216
3217 // When running in the VM mode, we follow the minimal start-up operations.
3218 // - CreateSharedLibsApexDir
3219 // - AddPreInstalledApex: note that CAPEXes are not supported in the VM mode
3220 // - AddBlockApex
3221 // - ActivateApexPackages
3222 // - setprop apexd.status: activated/ready
OnStartInVmMode()3223 int OnStartInVmMode() {
3224 Result<void> loop_ready = WaitForFile("/dev/loop-control", 20s);
3225 if (!loop_ready.ok()) {
3226 LOG(ERROR) << loop_ready.error();
3227 }
3228
3229 // Create directories for APEX shared libraries.
3230 if (auto status = CreateSharedLibsApexDir(); !status.ok()) {
3231 LOG(ERROR) << "Failed to create /apex/sharedlibs : " << status.ok();
3232 return 1;
3233 }
3234
3235 auto& instance = ApexFileRepository::GetInstance();
3236
3237 // Scan pre-installed apexes
3238 if (auto status = instance.AddPreInstalledApex(gConfig->builtin_dirs);
3239 !status.ok()) {
3240 LOG(ERROR) << "Failed to scan pre-installed APEX files: " << status.error();
3241 return 1;
3242 }
3243
3244 if (auto status = AddBlockApex(instance); !status.ok()) {
3245 LOG(ERROR) << "Failed to scan host APEX files: " << status.error();
3246 return 1;
3247 }
3248
3249 if (auto status = ActivateApexPackages(SelectApexForActivation(),
3250 ActivationMode::kVmMode);
3251 !status.ok()) {
3252 LOG(ERROR) << "Failed to activate apex packages : " << status.error();
3253 return 1;
3254 }
3255
3256 OnAllPackagesActivated(false);
3257 // In VM mode, we don't run a separate --snapshotde mode.
3258 // Instead, we mark apexd.status "ready" right now.
3259 OnAllPackagesReady();
3260 return 0;
3261 }
3262
OnOtaChrootBootstrap(bool also_include_staged_apexes)3263 int OnOtaChrootBootstrap(bool also_include_staged_apexes) {
3264 auto& instance = ApexFileRepository::GetInstance();
3265 if (auto status = instance.AddPreInstalledApex(gConfig->builtin_dirs);
3266 !status.ok()) {
3267 LOG(ERROR) << "Failed to scan pre-installed apexes from "
3268 << std::format("{}", gConfig->builtin_dirs | std::views::values);
3269 return 1;
3270 }
3271 if (also_include_staged_apexes) {
3272 // Scan staged dirs, and then scan the active dir. If a module is in both a
3273 // staged dir and the active dir, the APEX with a higher version will be
3274 // picked. If the versions are equal, the APEX in staged dir will be picked.
3275 //
3276 // The result is an approximation of what the active dir will actually have
3277 // after the reboot. In case of a downgrade install, it differs from the
3278 // actual, but this is not a supported case.
3279 for (const ApexSession& session :
3280 gSessionManager->GetSessionsInState(SessionState::STAGED)) {
3281 std::vector<std::string> dirs_to_scan =
3282 session.GetStagedApexDirs(gConfig->staged_session_dir);
3283 for (const std::string& dir_to_scan : dirs_to_scan) {
3284 if (auto status = instance.AddDataApex(dir_to_scan); !status.ok()) {
3285 LOG(ERROR) << "Failed to scan staged apexes from " << dir_to_scan;
3286 return 1;
3287 }
3288 }
3289 }
3290 }
3291 if (auto status = instance.AddDataApex(gConfig->active_apex_data_dir);
3292 !status.ok()) {
3293 LOG(ERROR) << "Failed to scan upgraded apexes from "
3294 << gConfig->active_apex_data_dir;
3295 // Fail early because we know we will be wasting cycles generating garbage
3296 // if we continue.
3297 return 1;
3298 }
3299
3300 // Create directories for APEX shared libraries.
3301 if (auto status = CreateSharedLibsApexDir(); !status.ok()) {
3302 LOG(ERROR) << "Failed to create /apex/sharedlibs : " << status.ok();
3303 return 1;
3304 }
3305
3306 auto activation_list = SelectApexForActivation();
3307
3308 // TODO(b/179497746): This is the third time we are duplicating this code
3309 // block. This will be easier to dedup once we start opening ApexFiles via
3310 // ApexFileRepository. That way, ProcessCompressedApex can return list of
3311 // ApexFileRef, instead of ApexFile.
3312
3313 // Process compressed APEX, if any
3314 std::vector<ApexFileRef> compressed_apex;
3315 for (auto it = activation_list.begin(); it != activation_list.end();) {
3316 if (it->get().IsCompressed()) {
3317 compressed_apex.emplace_back(*it);
3318 it = activation_list.erase(it);
3319 } else {
3320 it++;
3321 }
3322 }
3323 std::vector<ApexFile> decompressed_apex;
3324 if (!compressed_apex.empty()) {
3325 decompressed_apex =
3326 ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ true);
3327
3328 for (const ApexFile& apex_file : decompressed_apex) {
3329 activation_list.emplace_back(std::cref(apex_file));
3330 }
3331 }
3332
3333 auto activate_status =
3334 ActivateApexPackages(activation_list, ActivationMode::kOtaChrootMode);
3335 if (!activate_status.ok()) {
3336 LOG(ERROR) << "Failed to activate apex packages : "
3337 << activate_status.error();
3338 auto retry_status =
3339 ActivateMissingApexes(activation_list, ActivationMode::kOtaChrootMode);
3340 if (!retry_status.ok()) {
3341 LOG(ERROR) << retry_status.error();
3342 }
3343 }
3344
3345 if (auto status = EmitApexInfoList(/*is_bootstrap*/ false); !status.ok()) {
3346 LOG(ERROR) << status.error();
3347 }
3348
3349 return 0;
3350 }
3351
GetApexDatabaseForTesting()3352 android::apex::MountedApexDatabase& GetApexDatabaseForTesting() {
3353 return gMountedApexes;
3354 }
3355
3356 // A version of apex verification that happens during non-staged APEX
3357 // installation.
VerifyPackageNonStagedInstall(const ApexFile & apex_file,bool force)3358 Result<VerificationResult> VerifyPackageNonStagedInstall(
3359 const ApexFile& apex_file, bool force) {
3360 OR_RETURN(VerifyPackageBoot(apex_file));
3361
3362 auto sessions = gSessionManager->GetSessions();
3363
3364 // Check overlapping: reject if the same package is already staged
3365 // or if there's a session being staged.
3366 OR_RETURN(VerifyNoOverlapInSessions(Single(apex_file), sessions));
3367
3368 auto check_fn =
3369 [&apex_file,
3370 &force](const std::string& mount_point) -> Result<VerificationResult> {
3371 if (force) {
3372 return {};
3373 }
3374 VerificationResult result;
3375 if (access((mount_point + "/app").c_str(), F_OK) == 0) {
3376 return Error() << apex_file.GetPath() << " contains app inside";
3377 }
3378 if (access((mount_point + "/priv-app").c_str(), F_OK) == 0) {
3379 return Error() << apex_file.GetPath() << " contains priv-app inside";
3380 }
3381 result.apex_hals =
3382 OR_RETURN(CheckVintf(Single(apex_file), Single(mount_point)));
3383 return result;
3384 };
3385 return RunVerifyFnInsideTempMount(apex_file, check_fn);
3386 }
3387
CheckSupportsNonStagedInstall(const ApexFile & new_apex,bool force)3388 Result<void> CheckSupportsNonStagedInstall(const ApexFile& new_apex,
3389 bool force) {
3390 const auto& new_manifest = new_apex.GetManifest();
3391
3392 if (!force) {
3393 if (!new_manifest.supportsrebootlessupdate()) {
3394 return Error() << new_apex.GetPath()
3395 << " does not support non-staged update";
3396 }
3397
3398 // Check if update will impact linkerconfig.
3399
3400 // Updates to shared libs APEXes must be done via staged install flow.
3401 if (new_manifest.providesharedapexlibs()) {
3402 return Error() << new_apex.GetPath() << " is a shared libs APEX";
3403 }
3404
3405 // This APEX provides native libs to other parts of the platform. It can
3406 // only be updated via staged install flow.
3407 if (new_manifest.providenativelibs_size() > 0) {
3408 return Error() << new_apex.GetPath() << " provides native libs";
3409 }
3410
3411 // This APEX requires libs provided by dynamic common library APEX, hence it
3412 // can only be installed using staged install flow.
3413 if (new_manifest.requiresharedapexlibs_size() > 0) {
3414 return Error() << new_apex.GetPath() << " requires shared apex libs";
3415 }
3416
3417 // We don't allow non-staged updates of APEXES that have java libs inside.
3418 if (new_manifest.jnilibs_size() > 0) {
3419 return Error() << new_apex.GetPath() << " requires JNI libs";
3420 }
3421 }
3422
3423 auto expected_public_key =
3424 ApexFileRepository::GetInstance().GetPublicKey(new_manifest.name());
3425 if (!expected_public_key.ok()) {
3426 return expected_public_key.error();
3427 }
3428 auto verity_data = new_apex.VerifyApexVerity(*expected_public_key);
3429 if (!verity_data.ok()) {
3430 return verity_data.error();
3431 }
3432 return {};
3433 }
3434
ComputePackageIdMinor(const ApexFile & apex)3435 Result<size_t> ComputePackageIdMinor(const ApexFile& apex) {
3436 static constexpr size_t kMaxVerityDevicesPerApexName = 3u;
3437 DeviceMapper& dm = DeviceMapper::Instance();
3438 std::vector<DeviceMapper::DmBlockDevice> dm_devices;
3439 if (!dm.GetAvailableDevices(&dm_devices)) {
3440 return Error() << "Failed to list dm devices";
3441 }
3442 size_t devices = 0;
3443 size_t next_minor = 1;
3444 for (const auto& dm_device : dm_devices) {
3445 std::string_view dm_name(dm_device.name());
3446 // Format is <module_name>@<version_code>[_<minor>]
3447 if (!ConsumePrefix(&dm_name, apex.GetManifest().name())) {
3448 continue;
3449 }
3450 devices++;
3451 auto pos = dm_name.find_last_of('_');
3452 if (pos == std::string_view::npos) {
3453 continue;
3454 }
3455 size_t minor;
3456 if (!ParseUint(std::string(dm_name.substr(pos + 1)), &minor)) {
3457 return Error() << "Unexpected dm device name " << dm_device.name();
3458 }
3459 if (next_minor < minor + 1) {
3460 next_minor = minor + 1;
3461 }
3462 }
3463 if (devices > kMaxVerityDevicesPerApexName) {
3464 return Error() << "There are too many (" << devices
3465 << ") dm block devices associated with package "
3466 << apex.GetManifest().name();
3467 }
3468 while (true) {
3469 std::string target_file =
3470 StringPrintf("%s/%s_%zu.apex", gConfig->active_apex_data_dir,
3471 GetPackageId(apex.GetManifest()).c_str(), next_minor);
3472 if (access(target_file.c_str(), F_OK) == 0) {
3473 next_minor++;
3474 } else {
3475 break;
3476 }
3477 }
3478
3479 return next_minor;
3480 }
3481
3482 // TODO(b/238820991) Handle failures
UnloadApexFromInit(const std::string & apex_name)3483 Result<void> UnloadApexFromInit(const std::string& apex_name) {
3484 if (!SetProperty(kCtlApexUnloadSysprop, apex_name)) {
3485 // When failed to SetProperty(), there's nothing we can do here.
3486 // Log error and return early to avoid indefinite waiting for ack.
3487 return Error() << "Failed to set " << kCtlApexUnloadSysprop << " to "
3488 << apex_name;
3489 }
3490 SetProperty("apex." + apex_name + ".ready", "false");
3491 return {};
3492 }
3493
3494 // TODO(b/238820991) Handle failures
LoadApexFromInit(const std::string & apex_name)3495 Result<void> LoadApexFromInit(const std::string& apex_name) {
3496 if (!SetProperty(kCtlApexLoadSysprop, apex_name)) {
3497 // When failed to SetProperty(), there's nothing we can do here.
3498 // Log error and return early to avoid indefinite waiting for ack.
3499 return Error() << "Failed to set " << kCtlApexLoadSysprop << " to "
3500 << apex_name;
3501 }
3502 SetProperty("apex." + apex_name + ".ready", "true");
3503 return {};
3504 }
3505
InstallPackage(const std::string & package_path,bool force)3506 Result<ApexFile> InstallPackage(const std::string& package_path, bool force)
3507 REQUIRES(!gInstallLock) {
3508 auto install_guard = std::scoped_lock{gInstallLock};
3509 auto event = InstallRequestedEvent(InstallType::NonStaged,
3510 /*is_rollback=*/false);
3511
3512 auto temp_apex = ApexFile::Open(package_path);
3513 if (!temp_apex.ok()) {
3514 return temp_apex.error();
3515 }
3516
3517 event.AddFiles(Single(*temp_apex));
3518
3519 const std::string& module_name = temp_apex->GetManifest().name();
3520 // Don't allow non-staged update if there are no active versions of this APEX.
3521 auto cur_mounted_data = gMountedApexes.GetLatestMountedApex(module_name);
3522 if (!cur_mounted_data.has_value()) {
3523 return Error() << "No active version found for package " << module_name;
3524 }
3525
3526 auto cur_apex = ApexFile::Open(cur_mounted_data->full_path);
3527 if (!cur_apex.ok()) {
3528 return cur_apex.error();
3529 }
3530
3531 // Do a quick check if this APEX can be installed without a reboot.
3532 // Note that passing this check doesn't guarantee that APEX will be
3533 // successfully installed.
3534 if (auto r = CheckSupportsNonStagedInstall(*temp_apex, force); !r.ok()) {
3535 return r.error();
3536 }
3537
3538 // 1. Verify that APEX is correct. This is a heavy check that involves
3539 // mounting an APEX on a temporary mount point and reading the entire
3540 // dm-verity block device.
3541 auto result = OR_RETURN(VerifyPackageNonStagedInstall(*temp_apex, force));
3542 event.AddHals(result.apex_hals);
3543
3544 // 2. Compute params for mounting new apex.
3545 auto new_id_minor = ComputePackageIdMinor(*temp_apex);
3546 if (!new_id_minor.ok()) {
3547 return new_id_minor.error();
3548 }
3549
3550 std::string new_id = GetPackageId(temp_apex->GetManifest()) + "_" +
3551 std::to_string(*new_id_minor);
3552
3553 // Before unmounting the current apex, unload it from the init process:
3554 // terminates services started from the apex and init scripts read from the
3555 // apex.
3556 OR_RETURN(UnloadApexFromInit(module_name));
3557
3558 // And then reload it from the init process whether it succeeds or not.
3559 auto reload_apex = android::base::make_scope_guard([&]() {
3560 if (auto status = LoadApexFromInit(module_name); !status.ok()) {
3561 LOG(ERROR) << "Failed to load apex " << module_name << " : "
3562 << status.error().message();
3563 }
3564 });
3565
3566 // 2. Unmount currently active APEX.
3567 if (auto res =
3568 UnmountPackage(*cur_apex, /* allow_latest= */ true,
3569 /* deferred= */ true, /* detach_mount_point= */ force);
3570 !res.ok()) {
3571 return res.error();
3572 }
3573
3574 // 3. Hard link to final destination.
3575 std::string target_file =
3576 StringPrintf("%s/%s.apex", gConfig->active_apex_data_dir, new_id.c_str());
3577
3578 auto guard = android::base::make_scope_guard([&]() {
3579 if (unlink(target_file.c_str()) != 0 && errno != ENOENT) {
3580 PLOG(ERROR) << "Failed to unlink " << target_file;
3581 }
3582 // We can't really rely on the fact that dm-verity device backing up
3583 // previously active APEX is still around. We need to create a new one.
3584 std::string old_new_id = GetPackageId(temp_apex->GetManifest()) + "_" +
3585 std::to_string(*new_id_minor + 1);
3586 auto res = ActivatePackageImpl(*cur_apex, old_new_id,
3587 /* reuse_device= */ false);
3588 if (!res.ok()) {
3589 // At this point not much we can do... :(
3590 LOG(ERROR) << res.error();
3591 }
3592 });
3593
3594 // At this point it should be safe to hard link |temp_apex| to
3595 // |params->target_file|. In case reboot happens during one of the stages
3596 // below, then on next boot apexd will pick up the new verified APEX.
3597 if (link(package_path.c_str(), target_file.c_str()) != 0) {
3598 return ErrnoError() << "Failed to link " << package_path << " to "
3599 << target_file;
3600 }
3601
3602 auto new_apex = ApexFile::Open(target_file);
3603 if (!new_apex.ok()) {
3604 return new_apex.error();
3605 }
3606
3607 // 4. And activate new one.
3608 auto activate_status = ActivatePackageImpl(*new_apex, new_id,
3609 /* reuse_device= */ false);
3610 if (!activate_status.ok()) {
3611 return activate_status.error();
3612 }
3613
3614 // Accept the install.
3615 guard.Disable();
3616
3617 // 4. Now we can unlink old APEX if it's not pre-installed.
3618 if (!ApexFileRepository::GetInstance().IsPreInstalledApex(*cur_apex)) {
3619 if (unlink(cur_mounted_data->full_path.c_str()) != 0) {
3620 PLOG(ERROR) << "Failed to unlink " << cur_mounted_data->full_path;
3621 }
3622 }
3623
3624 if (auto res = EmitApexInfoList(/*is_bootstrap*/ false); !res.ok()) {
3625 LOG(ERROR) << res.error();
3626 }
3627
3628 // Release compressed blocks in case target_file is on f2fs-compressed
3629 // filesystem.
3630 ReleaseF2fsCompressedBlocks(target_file);
3631
3632 event.MarkSucceeded();
3633
3634 return new_apex;
3635 }
3636
IsActiveApexChanged(const ApexFile & apex)3637 bool IsActiveApexChanged(const ApexFile& apex) {
3638 return gChangedActiveApexes.find(apex.GetManifest().name()) !=
3639 gChangedActiveApexes.end();
3640 }
3641
GetChangedActiveApexesForTesting()3642 std::set<std::string>& GetChangedActiveApexesForTesting() {
3643 return gChangedActiveApexes;
3644 }
3645
GetSessionManager()3646 ApexSessionManager* GetSessionManager() { return gSessionManager; }
3647
3648 } // namespace apex
3649 } // namespace android
3650