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