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