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