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