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