1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "apexd"
18
19 #include "apexd.h"
20 #include "apexd_private.h"
21
22 #include "apex_database.h"
23 #include "apex_file.h"
24 #include "apex_key.h"
25 #include "apex_manifest.h"
26 #include "apex_shim.h"
27 #include "apexd_checkpoint.h"
28 #include "apexd_loop.h"
29 #include "apexd_prepostinstall.h"
30 #include "apexd_prop.h"
31 #include "apexd_session.h"
32 #include "apexd_utils.h"
33 #include "status_or.h"
34 #include "string_log.h"
35
36 #include <ApexProperties.sysprop.h>
37 #include <android-base/file.h>
38 #include <android-base/logging.h>
39 #include <android-base/macros.h>
40 #include <android-base/properties.h>
41 #include <android-base/scopeguard.h>
42 #include <android-base/stringprintf.h>
43 #include <android-base/strings.h>
44 #include <android-base/unique_fd.h>
45 #include <libavb/libavb.h>
46 #include <libdm/dm.h>
47 #include <libdm/dm_table.h>
48 #include <libdm/dm_target.h>
49 #include <selinux/android.h>
50
51 #include <dirent.h>
52 #include <fcntl.h>
53 #include <linux/loop.h>
54 #include <sys/ioctl.h>
55 #include <sys/mount.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 #include <unistd.h>
59
60 #include <algorithm>
61 #include <array>
62 #include <filesystem>
63 #include <fstream>
64 #include <iomanip>
65 #include <memory>
66 #include <optional>
67 #include <string>
68 #include <unordered_map>
69 #include <unordered_set>
70
71 using android::base::EndsWith;
72 using android::base::Join;
73 using android::base::ReadFullyAtOffset;
74 using android::base::StartsWith;
75 using android::base::StringPrintf;
76 using android::base::unique_fd;
77 using android::dm::DeviceMapper;
78 using android::dm::DmDeviceState;
79 using android::dm::DmTable;
80 using android::dm::DmTargetVerity;
81
82 using apex::proto::SessionState;
83
84 namespace android {
85 namespace apex {
86
87 using MountedApexData = MountedApexDatabase::MountedApexData;
88
89 namespace {
90
91 // These should be in-sync with system/sepolicy/public/property_contexts
92 static constexpr const char* kApexStatusSysprop = "apexd.status";
93 static constexpr const char* kApexStatusStarting = "starting";
94 static constexpr const char* kApexStatusReady = "ready";
95
96 static constexpr const char* kApexVerityOnSystemProp =
97 "persist.apexd.verity_on_system";
98 static bool gForceDmVerityOnSystem =
99 android::base::GetBoolProperty(kApexVerityOnSystemProp, false);
100
101 // This should be in UAPI, but it's not :-(
102 static constexpr const char* kDmVerityRestartOnCorruption =
103 "restart_on_corruption";
104
105 MountedApexDatabase gMountedApexes;
106
107 CheckpointInterface* gVoldService;
108 bool gSupportsFsCheckpoints = false;
109 bool gInFsCheckpointMode = false;
110
111 static constexpr size_t kLoopDeviceSetupAttempts = 3u;
112
113 static const bool kUpdatable =
114 android::sysprop::ApexProperties::updatable().value_or(false);
115
116 bool gBootstrap = false;
117 static const std::vector<const std::string> kBootstrapApexes = {
118 "com.android.runtime",
119 "com.android.tzdata",
120 };
121
122 static constexpr const int kNumRetriesWhenCheckpointingEnabled = 1;
123
isBootstrapApex(const ApexFile & apex)124 bool isBootstrapApex(const ApexFile& apex) {
125 return std::find(kBootstrapApexes.begin(), kBootstrapApexes.end(),
126 apex.GetManifest().name()) != kBootstrapApexes.end();
127 }
128
129 // Pre-allocate loop devices so that we don't have to wait for them
130 // later when actually activating APEXes.
preAllocateLoopDevices()131 Status preAllocateLoopDevices() {
132 auto scan = FindApexes(kApexPackageBuiltinDirs);
133 if (!scan.Ok()) {
134 return scan.ErrorStatus();
135 }
136
137 auto size = 0;
138 for (const auto& path : *scan) {
139 auto apexFile = ApexFile::Open(path);
140 if (!apexFile.Ok() || apexFile->IsFlattened()) {
141 continue;
142 }
143 size++;
144 // bootstrap Apexes may be activated on separate namespaces.
145 if (isBootstrapApex(*apexFile)) {
146 size++;
147 }
148 }
149
150 // note: do not call preAllocateLoopDevices() if size == 0
151 // or the device does not support updatable APEX.
152 // For devices (e.g. ARC) which doesn't support loop-control
153 // preAllocateLoopDevices() can cause problem when it tries
154 // to access /dev/loop-control.
155 if (size == 0 || !kUpdatable) {
156 return Status::Success();
157 }
158 return loop::preAllocateLoopDevices(size);
159 }
160
createVerityTable(const ApexVerityData & verity_data,const std::string & loop,bool restart_on_corruption)161 std::unique_ptr<DmTable> createVerityTable(const ApexVerityData& verity_data,
162 const std::string& loop,
163 bool restart_on_corruption) {
164 AvbHashtreeDescriptor* desc = verity_data.desc.get();
165 auto table = std::make_unique<DmTable>();
166
167 std::ostringstream hash_algorithm;
168 hash_algorithm << desc->hash_algorithm;
169
170 auto target = std::make_unique<DmTargetVerity>(
171 0, desc->image_size / 512, desc->dm_verity_version, loop, loop,
172 desc->data_block_size, desc->hash_block_size,
173 desc->image_size / desc->data_block_size,
174 desc->tree_offset / desc->hash_block_size, hash_algorithm.str(),
175 verity_data.root_digest, verity_data.salt);
176
177 target->IgnoreZeroBlocks();
178 if (restart_on_corruption) {
179 target->SetVerityMode(kDmVerityRestartOnCorruption);
180 }
181 table->AddTarget(std::move(target));
182
183 table->set_readonly(true);
184
185 return table;
186 }
187
188 enum WaitForDeviceMode {
189 kWaitToBeCreated = 0,
190 kWaitToBeDeleted,
191 };
192
waitForDevice(const std::string & device,const WaitForDeviceMode & mode)193 Status waitForDevice(const std::string& device, const WaitForDeviceMode& mode) {
194 // TODO(b/122059364): Make this more efficient
195 // TODO: use std::chrono?
196
197 // Deleting a device might take more time, so wait a little bit longer.
198 size_t num_tries = mode == kWaitToBeCreated ? 10u : 15u;
199
200 LOG(DEBUG) << "Waiting for " << device << " to be "
201 << (mode == kWaitToBeCreated ? "created" : " deleted");
202 for (size_t i = 0; i < num_tries; ++i) {
203 StatusOr<bool> status = PathExists(device);
204 if (status.Ok()) {
205 if (mode == kWaitToBeCreated && *status) {
206 return Status::Success();
207 }
208 if (mode == kWaitToBeDeleted && !*status) {
209 return Status::Success();
210 }
211 }
212 if (i + 1 < num_tries) {
213 usleep(50000);
214 }
215 }
216
217 return Status::Fail(StringLog()
218 << "Failed to wait for device " << device << " to be "
219 << (mode == kWaitToBeCreated ? " created" : " deleted"));
220 }
221
222 // Deletes a dm-verity device with a given name and path.
223 // Synchronizes on the device actually being deleted from userspace.
DeleteVerityDevice(const std::string & name,const std::string & path)224 Status DeleteVerityDevice(const std::string& name, const std::string& path) {
225 DeviceMapper& dm = DeviceMapper::Instance();
226 if (!dm.DeleteDevice(name)) {
227 return Status::Fail(StringLog() << "Failed to delete device " << name
228 << " with path " << path);
229 }
230 // Block until device is deleted from userspace.
231 return waitForDevice(path, kWaitToBeDeleted);
232 }
233
234 // Deletes dm-verity device with a given name.
235 // See function above.
DeleteVerityDevice(const std::string & name)236 Status DeleteVerityDevice(const std::string& name) {
237 DeviceMapper& dm = DeviceMapper::Instance();
238 std::string path;
239 if (!dm.GetDmDevicePathByName(name, &path)) {
240 return Status::Fail(StringLog()
241 << "Unable to get path for dm-verity device " << name);
242 }
243 return DeleteVerityDevice(name, path);
244 }
245
246 class DmVerityDevice {
247 public:
DmVerityDevice()248 DmVerityDevice() : cleared_(true) {}
DmVerityDevice(const std::string & name)249 explicit DmVerityDevice(const std::string& name)
250 : name_(name), cleared_(false) {}
DmVerityDevice(const std::string & name,const std::string & dev_path)251 DmVerityDevice(const std::string& name, const std::string& dev_path)
252 : name_(name), dev_path_(dev_path), cleared_(false) {}
253
DmVerityDevice(DmVerityDevice && other)254 DmVerityDevice(DmVerityDevice&& other) noexcept
255 : name_(std::move(other.name_)),
256 dev_path_(std::move(other.dev_path_)),
257 cleared_(other.cleared_) {
258 other.cleared_ = true;
259 }
260
operator =(DmVerityDevice && other)261 DmVerityDevice& operator=(DmVerityDevice&& other) noexcept {
262 name_ = other.name_;
263 dev_path_ = other.dev_path_;
264 cleared_ = other.cleared_;
265 other.cleared_ = true;
266 return *this;
267 }
268
~DmVerityDevice()269 ~DmVerityDevice() {
270 if (!cleared_) {
271 Status ret = DeleteVerityDevice(name_, dev_path_);
272 if (!ret.Ok()) {
273 LOG(ERROR) << ret.ErrorMessage();
274 }
275 }
276 }
277
GetName() const278 const std::string& GetName() const { return name_; }
GetDevPath() const279 const std::string& GetDevPath() const { return dev_path_; }
SetDevPath(const std::string & dev_path)280 void SetDevPath(const std::string& dev_path) { dev_path_ = dev_path; }
281
Release()282 void Release() { cleared_ = true; }
283
284 private:
285 std::string name_;
286 std::string dev_path_;
287 bool cleared_;
288 };
289
createVerityDevice(const std::string & name,const DmTable & table)290 StatusOr<DmVerityDevice> createVerityDevice(const std::string& name,
291 const DmTable& table) {
292 DeviceMapper& dm = DeviceMapper::Instance();
293
294 if (dm.GetState(name) != DmDeviceState::INVALID) {
295 // TODO: since apexd tears down devices during unmount, can this happen?
296 LOG(WARNING) << "Deleting existing dm device " << name;
297 const Status& status = DeleteVerityDevice(name);
298 if (!status.Ok()) {
299 // TODO: should we fail instead?
300 LOG(ERROR) << "Failed to delete device " << name << " : "
301 << status.ErrorMessage();
302 }
303 }
304
305 if (!dm.CreateDevice(name, table)) {
306 return StatusOr<DmVerityDevice>::MakeError(
307 "Couldn't create verity device.");
308 }
309 DmVerityDevice dev(name);
310
311 std::string dev_path;
312 if (!dm.GetDmDevicePathByName(name, &dev_path)) {
313 return StatusOr<DmVerityDevice>::MakeError(
314 "Couldn't get verity device path!");
315 }
316 dev.SetDevPath(dev_path);
317
318 return StatusOr<DmVerityDevice>(std::move(dev));
319 }
320
RemovePreviouslyActiveApexFiles(const std::unordered_set<std::string> & affected_packages,const std::unordered_set<std::string> & files_to_keep)321 Status RemovePreviouslyActiveApexFiles(
322 const std::unordered_set<std::string>& affected_packages,
323 const std::unordered_set<std::string>& files_to_keep) {
324 auto all_active_apex_files =
325 FindApexFilesByName(kActiveApexPackagesDataDir, false /* include_dirs */);
326
327 if (!all_active_apex_files.Ok()) {
328 return all_active_apex_files.ErrorStatus();
329 }
330
331 for (const std::string& path : *all_active_apex_files) {
332 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
333 if (!apex_file.Ok()) {
334 return apex_file.ErrorStatus();
335 }
336
337 const std::string& package_name = apex_file->GetManifest().name();
338 if (affected_packages.find(package_name) == affected_packages.end()) {
339 // This apex belongs to a package that wasn't part of this stage sessions,
340 // hence it should be kept.
341 continue;
342 }
343
344 if (files_to_keep.find(apex_file->GetPath()) != files_to_keep.end()) {
345 // This is a path that was staged and should be kept.
346 continue;
347 }
348
349 LOG(DEBUG) << "Deleting previously active apex " << apex_file->GetPath();
350 if (unlink(apex_file->GetPath().c_str()) != 0) {
351 return Status::Fail(PStringLog()
352 << "Failed to unlink " << apex_file->GetPath());
353 }
354 }
355
356 return Status::Success();
357 }
358
359 // Reads the entire device to verify the image is authenticatic
readVerityDevice(const std::string & verity_device,uint64_t device_size)360 Status readVerityDevice(const std::string& verity_device,
361 uint64_t device_size) {
362 static constexpr int kBlockSize = 4096;
363 static constexpr size_t kBufSize = 1024 * kBlockSize;
364 std::vector<uint8_t> buffer(kBufSize);
365
366 unique_fd fd(TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY)));
367 if (fd.get() == -1) {
368 return Status::Fail(PStringLog() << "Can't open " << verity_device);
369 }
370
371 size_t bytes_left = device_size;
372 while (bytes_left > 0) {
373 size_t to_read = std::min(bytes_left, kBufSize);
374 if (!android::base::ReadFully(fd.get(), buffer.data(), to_read)) {
375 return Status::Fail(PStringLog() << "Can't verify " << verity_device
376 << "; corrupted?");
377 }
378 bytes_left -= to_read;
379 }
380
381 return Status::Success();
382 }
383
VerifyMountedImage(const ApexFile & apex,const std::string & mount_point)384 Status VerifyMountedImage(const ApexFile& apex,
385 const std::string& mount_point) {
386 auto status = apex.VerifyManifestMatches(mount_point);
387 if (!status.Ok()) {
388 return status;
389 }
390 if (shim::IsShimApex(apex)) {
391 return shim::ValidateShimApex(mount_point, apex);
392 }
393 return Status::Success();
394 }
395
mountNonFlattened(const ApexFile & apex,const std::string & mountPoint,const std::string & device_name,bool verifyImage)396 StatusOr<MountedApexData> mountNonFlattened(const ApexFile& apex,
397 const std::string& mountPoint,
398 const std::string& device_name,
399 bool verifyImage) {
400 using StatusM = StatusOr<MountedApexData>;
401 const std::string& full_path = apex.GetPath();
402
403 if (!kUpdatable) {
404 return StatusM::Fail(StringLog()
405 << "Unable to mount non-flattened apex package "
406 << full_path << " because device doesn't support it");
407 }
408
409 loop::LoopbackDeviceUniqueFd loopbackDevice;
410 for (size_t attempts = 1;; ++attempts) {
411 StatusOr<loop::LoopbackDeviceUniqueFd> ret = loop::createLoopDevice(
412 full_path, apex.GetImageOffset(), apex.GetImageSize());
413 if (ret.Ok()) {
414 loopbackDevice = std::move(*ret);
415 break;
416 }
417 if (attempts >= kLoopDeviceSetupAttempts) {
418 return StatusM::Fail(StringLog()
419 << "Could not create loop device for " << full_path
420 << ": " << ret.ErrorMessage());
421 }
422 }
423 LOG(VERBOSE) << "Loopback device created: " << loopbackDevice.name;
424
425 auto verityData = apex.VerifyApexVerity();
426 if (!verityData.Ok()) {
427 return StatusM::Fail(StringLog()
428 << "Failed to verify Apex Verity data for "
429 << full_path << ": " << verityData.ErrorMessage());
430 }
431 std::string blockDevice = loopbackDevice.name;
432 MountedApexData apex_data(loopbackDevice.name, apex.GetPath(), mountPoint,
433 device_name);
434
435 // for APEXes in immutable partitions, we don't need to mount them on
436 // dm-verity because they are already in the dm-verity protected partition;
437 // system. However, note that we don't skip verification to ensure that APEXes
438 // are correctly signed.
439 const bool mountOnVerity =
440 gForceDmVerityOnSystem || !isPathForBuiltinApexes(full_path);
441 DmVerityDevice verityDev;
442 if (mountOnVerity) {
443 auto verityTable =
444 createVerityTable(*verityData, loopbackDevice.name,
445 /* restart_on_corruption = */ !verifyImage);
446 StatusOr<DmVerityDevice> verityDevRes =
447 createVerityDevice(device_name, *verityTable);
448 if (!verityDevRes.Ok()) {
449 return StatusM::Fail(StringLog()
450 << "Failed to create Apex Verity device "
451 << full_path << ": " << verityDevRes.ErrorMessage());
452 }
453 verityDev = std::move(*verityDevRes);
454 blockDevice = verityDev.GetDevPath();
455
456 Status readAheadStatus = loop::configureReadAhead(verityDev.GetDevPath());
457 if (!readAheadStatus.Ok()) {
458 return StatusM::MakeError(readAheadStatus);
459 }
460 }
461
462 // TODO(b/122059364): Even though the kernel has created the verity
463 // device, we still depend on ueventd to run to actually create the
464 // device node in userspace. To solve this properly we should listen on
465 // the netlink socket for uevents, or use inotify. For now, this will
466 // have to do.
467 Status deviceStatus = waitForDevice(blockDevice, kWaitToBeCreated);
468 if (!deviceStatus.Ok()) {
469 return StatusM::MakeError(deviceStatus);
470 }
471
472 // TODO: consider moving this inside RunVerifyFnInsideTempMount.
473 if (mountOnVerity && verifyImage) {
474 Status verityStatus =
475 readVerityDevice(blockDevice, (*verityData).desc->image_size);
476 if (!verityStatus.Ok()) {
477 return StatusM::MakeError(verityStatus);
478 }
479 }
480
481 if (mount(blockDevice.c_str(), mountPoint.c_str(), "ext4",
482 MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY, nullptr) == 0) {
483 LOG(INFO) << "Successfully mounted package " << full_path << " on "
484 << mountPoint;
485 auto status = VerifyMountedImage(apex, mountPoint);
486 if (!status.Ok()) {
487 umount2(mountPoint.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH);
488 return StatusM::Fail(StringLog() << "Failed to verify " << full_path
489 << ": " << status.ErrorMessage());
490 }
491 // Time to accept the temporaries as good.
492 if (mountOnVerity) {
493 verityDev.Release();
494 }
495 loopbackDevice.CloseGood();
496
497 return StatusM(std::move(apex_data));
498 } else {
499 return StatusM::Fail(StringLog() << "Mounting failed for package "
500 << full_path << " : " << strerror(errno));
501 }
502 }
503
mountFlattened(const ApexFile & apex,const std::string & mountPoint)504 StatusOr<MountedApexData> mountFlattened(const ApexFile& apex,
505 const std::string& mountPoint) {
506 using StatusM = StatusOr<MountedApexData>;
507 if (!isPathForBuiltinApexes(apex.GetPath())) {
508 return StatusM::Fail(StringLog() << "Cannot activate flattened APEX "
509 << apex.GetPath());
510 }
511
512 if (mount(apex.GetPath().c_str(), mountPoint.c_str(), nullptr, MS_BIND,
513 nullptr) == 0) {
514 LOG(INFO) << "Successfully bind-mounted flattened package "
515 << apex.GetPath() << " on " << mountPoint;
516
517 MountedApexData apex_data("" /* loop_name */, apex.GetPath(), mountPoint,
518 "" /* device_name */);
519 return StatusM(std::move(apex_data));
520 }
521 return StatusM::Fail(PStringLog() << "Mounting failed for flattened package "
522 << apex.GetPath());
523 }
524
MountPackageImpl(const ApexFile & apex,const std::string & mountPoint,const std::string & device_name,bool verifyImage)525 StatusOr<MountedApexData> MountPackageImpl(const ApexFile& apex,
526 const std::string& mountPoint,
527 const std::string& device_name,
528 bool verifyImage) {
529 using StatusM = StatusOr<MountedApexData>;
530 LOG(VERBOSE) << "Creating mount point: " << mountPoint;
531 // Note: the mount point could exist in case when the APEX was activated
532 // during the bootstrap phase (e.g., the runtime or tzdata APEX).
533 // Although we have separate mount namespaces to separate the early activated
534 // APEXes from the normally activate APEXes, the mount points themselves
535 // are shared across the two mount namespaces because /apex (a tmpfs) itself
536 // mounted at / which is (and has to be) a shared mount. Therefore, if apexd
537 // finds an empty directory under /apex, it's not a problem and apexd can use
538 // it.
539 auto exists = PathExists(mountPoint);
540 if (!exists.Ok()) {
541 return StatusM::MakeError(exists.ErrorStatus());
542 }
543 if (!*exists && mkdir(mountPoint.c_str(), kMkdirMode) != 0) {
544 return StatusM::Fail(PStringLog()
545 << "Could not create mount point " << mountPoint);
546 }
547 auto deleter = [&mountPoint]() {
548 if (rmdir(mountPoint.c_str()) != 0) {
549 PLOG(WARNING) << "Could not rmdir " << mountPoint;
550 }
551 };
552 auto scope_guard = android::base::make_scope_guard(deleter);
553 if (!IsEmptyDirectory(mountPoint)) {
554 return StatusM::Fail(PStringLog() << mountPoint << " is not empty");
555 }
556
557 StatusOr<MountedApexData> ret;
558 if (apex.IsFlattened()) {
559 ret = mountFlattened(apex, mountPoint);
560 } else {
561 ret = mountNonFlattened(apex, mountPoint, device_name, verifyImage);
562 }
563 if (ret.Ok()) {
564 scope_guard.Disable(); // Accept the mount.
565 }
566 return ret;
567 }
568
VerifyAndTempMountPackage(const ApexFile & apex,const std::string & mount_point)569 StatusOr<MountedApexData> VerifyAndTempMountPackage(
570 const ApexFile& apex, const std::string& mount_point) {
571 const std::string& package_id = GetPackageId(apex.GetManifest());
572 LOG(DEBUG) << "Temp mounting " << package_id << " to " << mount_point;
573 const std::string& temp_device_name = package_id + ".tmp";
574 return MountPackageImpl(apex, mount_point, temp_device_name,
575 /* verifyImage = */ true);
576 }
577
Unmount(const MountedApexData & data)578 Status Unmount(const MountedApexData& data) {
579 // Lazily try to umount whatever is mounted.
580 if (umount2(data.mount_point.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
581 errno != EINVAL && errno != ENOENT) {
582 return Status::Fail(PStringLog()
583 << "Failed to unmount directory " << data.mount_point);
584 }
585 // Attempt to delete the folder. If the folder is retained, other
586 // data may be incorrect.
587 if (rmdir(data.mount_point.c_str()) != 0) {
588 PLOG(ERROR) << "Failed to rmdir directory " << data.mount_point;
589 }
590
591 // Try to free up the device-mapper device.
592 if (!data.device_name.empty()) {
593 const auto& status = DeleteVerityDevice(data.device_name);
594 if (!status.Ok()) {
595 LOG(DEBUG) << "Failed to free device " << data.device_name << " : "
596 << status.ErrorMessage();
597 }
598 }
599
600 // Try to free up the loop device.
601 if (!data.loop_name.empty()) {
602 auto log_fn = [](const std::string& path,
603 const std::string& id ATTRIBUTE_UNUSED) {
604 LOG(VERBOSE) << "Freeing loop device " << path << "for unmount.";
605 };
606 loop::DestroyLoopDevice(data.loop_name, log_fn);
607 }
608
609 return Status::Success();
610 }
611
GetPackageTempMountPoint(const ApexManifest & manifest)612 std::string GetPackageTempMountPoint(const ApexManifest& manifest) {
613 return StringPrintf("%s.tmp",
614 apexd_private::GetPackageMountPoint(manifest).c_str());
615 }
616
617 template <typename VerifyFn>
RunVerifyFnInsideTempMount(const ApexFile & apex,const VerifyFn & verify_fn)618 Status RunVerifyFnInsideTempMount(const ApexFile& apex,
619 const VerifyFn& verify_fn) {
620 // Temp mount image of this apex to validate it was properly signed;
621 // this will also read the entire block device through dm-verity, so
622 // we can be sure there is no corruption.
623 const std::string& temp_mount_point =
624 GetPackageTempMountPoint(apex.GetManifest());
625
626 StatusOr<MountedApexData> mount_status =
627 VerifyAndTempMountPackage(apex, temp_mount_point);
628 if (!mount_status.Ok()) {
629 LOG(ERROR) << "Failed to temp mount to " << temp_mount_point << " : "
630 << mount_status.ErrorMessage();
631 return mount_status.ErrorStatus();
632 }
633 auto cleaner = [&]() {
634 LOG(DEBUG) << "Unmounting " << temp_mount_point;
635 Status status = Unmount(*mount_status);
636 if (!status.Ok()) {
637 LOG(WARNING) << "Failed to unmount " << temp_mount_point << " : "
638 << status.ErrorMessage();
639 }
640 };
641 auto scope_guard = android::base::make_scope_guard(cleaner);
642 return verify_fn(temp_mount_point);
643 }
644
645 template <typename HookFn, typename HookCall>
PrePostinstallPackages(const std::vector<ApexFile> & apexes,HookFn fn,HookCall call)646 Status PrePostinstallPackages(const std::vector<ApexFile>& apexes, HookFn fn,
647 HookCall call) {
648 if (apexes.empty()) {
649 return Status::Fail("Empty set of inputs");
650 }
651
652 // 1) Check whether the APEXes have hooks.
653 bool has_hooks = false;
654 for (const ApexFile& apex_file : apexes) {
655 if (!(apex_file.GetManifest().*fn)().empty()) {
656 has_hooks = true;
657 break;
658 }
659 }
660
661 // 2) If we found hooks, run the pre/post-install.
662 if (has_hooks) {
663 Status install_status = (*call)(apexes);
664 if (!install_status.Ok()) {
665 return install_status;
666 }
667 }
668
669 return Status::Success();
670 }
671
PreinstallPackages(const std::vector<ApexFile> & apexes)672 Status PreinstallPackages(const std::vector<ApexFile>& apexes) {
673 return PrePostinstallPackages(apexes, &ApexManifest::preinstallhook,
674 &StagePreInstall);
675 }
676
PostinstallPackages(const std::vector<ApexFile> & apexes)677 Status PostinstallPackages(const std::vector<ApexFile>& apexes) {
678 return PrePostinstallPackages(apexes, &ApexManifest::postinstallhook,
679 &StagePostInstall);
680 }
681
682 template <typename RetType, typename Fn>
HandlePackages(const std::vector<std::string> & paths,Fn fn)683 RetType HandlePackages(const std::vector<std::string>& paths, Fn fn) {
684 // 1) Open all APEXes.
685 std::vector<ApexFile> apex_files;
686 for (const std::string& path : paths) {
687 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
688 if (!apex_file.Ok()) {
689 return RetType::Fail(apex_file.ErrorMessage());
690 }
691 apex_files.emplace_back(std::move(*apex_file));
692 }
693
694 // 2) Dispatch.
695 return fn(apex_files);
696 }
697
ValidateStagingShimApex(const ApexFile & to)698 Status ValidateStagingShimApex(const ApexFile& to) {
699 using android::base::StringPrintf;
700 auto system_shim = ApexFile::Open(
701 StringPrintf("%s/%s", kApexPackageSystemDir, shim::kSystemShimApexName));
702 if (!system_shim.Ok()) {
703 return system_shim.ErrorStatus();
704 }
705 auto verify_fn = [&](const std::string& system_apex_path) {
706 return shim::ValidateUpdate(system_apex_path, to.GetPath());
707 };
708 return RunVerifyFnInsideTempMount(*system_shim, verify_fn);
709 }
710
711 // A version of apex verification that happens during boot.
712 // This function should only verification checks that are necessary to run on
713 // each boot. Try to avoid putting expensive checks inside this function.
VerifyPackageBoot(const ApexFile & apex_file)714 Status VerifyPackageBoot(const ApexFile& apex_file) {
715 if (apex_file.IsFlattened()) {
716 return Status::Fail("Can't upgrade flattened apex");
717 }
718 StatusOr<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
719 if (!verity_or.Ok()) {
720 return Status::Fail(verity_or.ErrorMessage());
721 }
722
723 if (shim::IsShimApex(apex_file)) {
724 // Validating shim is not a very cheap operation, but it's fine to perform
725 // it here since it only runs during CTS tests and will never be triggered
726 // during normal flow.
727 const auto& status = ValidateStagingShimApex(apex_file);
728 if (!status.Ok()) {
729 return status;
730 }
731 }
732 return Status::Success();
733 }
734
735 // A version of apex verification that happens on submitStagedSession.
736 // This function contains checks that might be expensive to perform, e.g. temp
737 // mounting a package and reading entire dm-verity device, and shouldn't be run
738 // during boot.
VerifyPackageInstall(const ApexFile & apex_file)739 Status VerifyPackageInstall(const ApexFile& apex_file) {
740 const auto& verify_package_boot_status = VerifyPackageBoot(apex_file);
741 if (!verify_package_boot_status.Ok()) {
742 return verify_package_boot_status;
743 }
744 if (!kUpdatable) {
745 return Status::Fail(StringLog() << "Attempted to upgrade apex package "
746 << apex_file.GetPath()
747 << " on a device that doesn't support it");
748 }
749 StatusOr<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
750
751 constexpr const auto kSuccessFn = [](const std::string& _) {
752 return Status::Success();
753 };
754 return RunVerifyFnInsideTempMount(apex_file, kSuccessFn);
755 }
756
757 template <typename VerifyApexFn>
verifyPackages(const std::vector<std::string> & paths,const VerifyApexFn & verify_apex_fn)758 StatusOr<std::vector<ApexFile>> verifyPackages(
759 const std::vector<std::string>& paths, const VerifyApexFn& verify_apex_fn) {
760 if (paths.empty()) {
761 return StatusOr<std::vector<ApexFile>>::MakeError("Empty set of inputs");
762 }
763 LOG(DEBUG) << "verifyPackages() for " << Join(paths, ',');
764
765 using StatusT = StatusOr<std::vector<ApexFile>>;
766 auto verify_fn = [&](std::vector<ApexFile>& apexes) {
767 for (const ApexFile& apex_file : apexes) {
768 Status status = verify_apex_fn(apex_file);
769 if (!status.Ok()) {
770 return StatusT::MakeError(status);
771 }
772 }
773 return StatusT(std::move(apexes));
774 };
775 return HandlePackages<StatusT>(paths, verify_fn);
776 }
777
verifySessionDir(const int session_id)778 StatusOr<ApexFile> verifySessionDir(const int session_id) {
779 std::string sessionDirPath = std::string(kStagedSessionsDir) + "/session_" +
780 std::to_string(session_id);
781 LOG(INFO) << "Scanning " << sessionDirPath
782 << " looking for packages to be validated";
783 StatusOr<std::vector<std::string>> scan =
784 FindApexFilesByName(sessionDirPath, /* include_dirs=*/false);
785 if (!scan.Ok()) {
786 LOG(WARNING) << scan.ErrorMessage();
787 return StatusOr<ApexFile>::MakeError(scan.ErrorMessage());
788 }
789
790 if (scan->size() > 1) {
791 return StatusOr<ApexFile>::MakeError(
792 "More than one APEX package found in the same session directory.");
793 }
794
795 auto verified = verifyPackages(*scan, VerifyPackageInstall);
796 if (!verified.Ok()) {
797 return StatusOr<ApexFile>::MakeError(verified.ErrorStatus());
798 }
799 return StatusOr<ApexFile>(std::move((*verified)[0]));
800 }
801
ClearSessions()802 Status ClearSessions() {
803 auto sessions = ApexSession::GetSessions();
804 int cnt = 0;
805 for (ApexSession& session : sessions) {
806 Status status = session.DeleteSession();
807 if (!status.Ok()) {
808 return status;
809 }
810 cnt++;
811 }
812 if (cnt > 0) {
813 LOG(DEBUG) << "Deleted " << cnt << " sessions";
814 }
815 return Status::Success();
816 }
817
DeleteBackup()818 Status DeleteBackup() {
819 auto exists = PathExists(std::string(kApexBackupDir));
820 if (!exists.Ok()) {
821 return Status::Fail(StringLog() << "Can't clean " << kApexBackupDir << " : "
822 << exists.ErrorMessage());
823 }
824 if (!*exists) {
825 LOG(DEBUG) << kApexBackupDir << " does not exist. Nothing to clean";
826 return Status::Success();
827 }
828 return DeleteDirContent(std::string(kApexBackupDir));
829 }
830
BackupActivePackages()831 Status BackupActivePackages() {
832 LOG(DEBUG) << "Initializing backup of " << kActiveApexPackagesDataDir;
833
834 // Previous restore might've delete backups folder.
835 auto create_status = createDirIfNeeded(kApexBackupDir, 0700);
836 if (!create_status.Ok()) {
837 return Status::Fail(StringLog()
838 << "Backup failed : " << create_status.ErrorMessage());
839 }
840
841 auto apex_active_exists = PathExists(std::string(kActiveApexPackagesDataDir));
842 if (!apex_active_exists.Ok()) {
843 return Status::Fail("Backup failed : " + apex_active_exists.ErrorMessage());
844 }
845 if (!*apex_active_exists) {
846 LOG(DEBUG) << kActiveApexPackagesDataDir
847 << " does not exist. Nothing to backup";
848 return Status::Success();
849 }
850
851 auto active_packages =
852 FindApexFilesByName(kActiveApexPackagesDataDir, false /* include_dirs */);
853 if (!active_packages.Ok()) {
854 return Status::Fail(StringLog() << "Backup failed : "
855 << active_packages.ErrorMessage());
856 }
857
858 auto cleanup_status = DeleteBackup();
859 if (!cleanup_status.Ok()) {
860 return Status::Fail(StringLog()
861 << "Backup failed : " << cleanup_status.ErrorMessage());
862 }
863
864 auto backup_path_fn = [](const ApexFile& apex_file) {
865 return StringPrintf("%s/%s%s", kApexBackupDir,
866 GetPackageId(apex_file.GetManifest()).c_str(),
867 kApexPackageSuffix);
868 };
869
870 auto deleter = []() {
871 auto status = DeleteDirContent(std::string(kApexBackupDir));
872 if (!status.Ok()) {
873 LOG(ERROR) << "Failed to cleanup " << kApexBackupDir << " : "
874 << status.ErrorMessage();
875 }
876 };
877 auto scope_guard = android::base::make_scope_guard(deleter);
878
879 for (const std::string& path : *active_packages) {
880 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
881 if (!apex_file.Ok()) {
882 return Status::Fail("Backup failed : " + apex_file.ErrorMessage());
883 }
884 const auto& dest_path = backup_path_fn(*apex_file);
885 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
886 return Status::Fail(PStringLog()
887 << "Failed to backup " << apex_file->GetPath());
888 }
889 }
890
891 scope_guard.Disable(); // Accept the backup.
892 return Status::Success();
893 }
894
DoRollback(ApexSession & session)895 Status DoRollback(ApexSession& session) {
896 if (gInFsCheckpointMode) {
897 // We will roll back automatically when we reboot
898 return Status::Success();
899 }
900 auto scope_guard = android::base::make_scope_guard([&]() {
901 auto st = session.UpdateStateAndCommit(SessionState::ROLLBACK_FAILED);
902 LOG(DEBUG) << "Marking " << session << " as failed to rollback";
903 if (!st.Ok()) {
904 LOG(WARNING) << "Failed to mark session " << session
905 << " as failed to rollback : " << st.ErrorMessage();
906 }
907 });
908
909 auto backup_exists = PathExists(std::string(kApexBackupDir));
910 if (!backup_exists.Ok()) {
911 return backup_exists.ErrorStatus();
912 }
913 if (!*backup_exists) {
914 return Status::Fail(StringLog() << kApexBackupDir << " does not exist");
915 }
916
917 struct stat stat_data;
918 if (stat(kActiveApexPackagesDataDir, &stat_data) != 0) {
919 return Status::Fail(PStringLog()
920 << "Failed to access " << kActiveApexPackagesDataDir);
921 }
922
923 LOG(DEBUG) << "Deleting existing packages in " << kActiveApexPackagesDataDir;
924 auto delete_status =
925 DeleteDirContent(std::string(kActiveApexPackagesDataDir));
926 if (!delete_status.Ok()) {
927 return delete_status;
928 }
929
930 LOG(DEBUG) << "Renaming " << kApexBackupDir << " to "
931 << kActiveApexPackagesDataDir;
932 if (rename(kApexBackupDir, kActiveApexPackagesDataDir) != 0) {
933 return Status::Fail(PStringLog() << "Failed to rename " << kApexBackupDir
934 << " to " << kActiveApexPackagesDataDir);
935 }
936
937 LOG(DEBUG) << "Restoring original permissions for "
938 << kActiveApexPackagesDataDir;
939 if (chmod(kActiveApexPackagesDataDir, stat_data.st_mode & ALLPERMS) != 0) {
940 // TODO: should we wipe out /data/apex/active if chmod fails?
941 return Status::Fail(PStringLog()
942 << "Failed to restore original permissions for "
943 << kActiveApexPackagesDataDir);
944 }
945
946 scope_guard.Disable(); // Rollback succeeded. Accept state.
947 return Status::Success();
948 }
949
RollbackStagedSession(ApexSession & session)950 Status RollbackStagedSession(ApexSession& session) {
951 // If the session is staged, it hasn't been activated yet, and we just need
952 // to update its state to prevent it from being activated later.
953 return session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
954 }
955
RollbackActivatedSession(ApexSession & session)956 Status RollbackActivatedSession(ApexSession& session) {
957 if (gInFsCheckpointMode) {
958 LOG(DEBUG) << "Checkpoint mode is enabled";
959 // On checkpointing devices, our modifications on /data will be
960 // automatically rolled back when we abort changes. Updating the session
961 // state is pointless here, as it will be rolled back as well.
962 return Status::Success();
963 }
964
965 auto status =
966 session.UpdateStateAndCommit(SessionState::ROLLBACK_IN_PROGRESS);
967 if (!status.Ok()) {
968 // TODO: should we continue with a rollback?
969 return Status::Fail(StringLog() << "Rollback of session " << session
970 << " failed : " << status.ErrorMessage());
971 }
972
973 status = DoRollback(session);
974 if (!status.Ok()) {
975 return Status::Fail(StringLog() << "Rollback of session " << session
976 << " failed : " << status.ErrorMessage());
977 }
978
979 status = session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
980 if (!status.Ok()) {
981 LOG(WARNING) << "Failed to mark session " << session
982 << " as rolled back : " << status.ErrorMessage();
983 }
984
985 return Status::Success();
986 }
987
RollbackSession(ApexSession & session)988 Status RollbackSession(ApexSession& session) {
989 LOG(DEBUG) << "Initializing rollback of " << session;
990
991 switch (session.GetState()) {
992 case SessionState::ROLLBACK_IN_PROGRESS:
993 [[clang::fallthrough]];
994 case SessionState::ROLLED_BACK:
995 return Status::Success();
996 case SessionState::STAGED:
997 return RollbackStagedSession(session);
998 case SessionState::ACTIVATED:
999 return RollbackActivatedSession(session);
1000 default:
1001 return Status::Fail(StringLog() << "Can't restore session " << session
1002 << " : session is in a wrong state");
1003 }
1004 }
1005
ResumeRollback(ApexSession & session)1006 Status ResumeRollback(ApexSession& session) {
1007 auto backup_exists = PathExists(std::string(kApexBackupDir));
1008 if (!backup_exists.Ok()) {
1009 return backup_exists.ErrorStatus();
1010 }
1011 if (*backup_exists) {
1012 auto rollback_status = DoRollback(session);
1013 if (!rollback_status.Ok()) {
1014 return rollback_status;
1015 }
1016 }
1017 auto status = session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
1018 if (!status.Ok()) {
1019 LOG(WARNING) << "Failed to mark session " << session
1020 << " as rolled back : " << status.ErrorMessage();
1021 }
1022 return Status::Success();
1023 }
1024
UnmountPackage(const ApexFile & apex,bool allow_latest)1025 Status UnmountPackage(const ApexFile& apex, bool allow_latest) {
1026 LOG(VERBOSE) << "Unmounting " << GetPackageId(apex.GetManifest());
1027
1028 const ApexManifest& manifest = apex.GetManifest();
1029
1030 std::optional<MountedApexData> data;
1031 bool latest = false;
1032
1033 auto fn = [&](const MountedApexData& d, bool l) {
1034 if (d.full_path == apex.GetPath()) {
1035 data.emplace(d);
1036 latest = l;
1037 }
1038 };
1039 gMountedApexes.ForallMountedApexes(manifest.name(), fn);
1040
1041 if (!data.has_value()) {
1042 return Status::Fail(StringLog() << "Did not find " << apex.GetPath());
1043 }
1044
1045 if (latest) {
1046 if (!allow_latest) {
1047 return Status::Fail(StringLog()
1048 << "Package " << apex.GetPath() << " is active");
1049 }
1050 std::string mount_point = apexd_private::GetActiveMountPoint(manifest);
1051 LOG(VERBOSE) << "Unmounting and deleting " << mount_point;
1052 if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0) {
1053 return Status::Fail(PStringLog() << "Failed to unmount " << mount_point);
1054 }
1055 if (rmdir(mount_point.c_str()) != 0) {
1056 PLOG(ERROR) << "Could not rmdir " << mount_point;
1057 // Continue here.
1058 }
1059 }
1060
1061 // Clean up gMountedApexes now, even though we're not fully done.
1062 gMountedApexes.RemoveMountedApex(manifest.name(), apex.GetPath());
1063 return Unmount(*data);
1064 }
1065
1066 } // namespace
1067
1068 namespace apexd_private {
1069
MountPackage(const ApexFile & apex,const std::string & mountPoint)1070 Status MountPackage(const ApexFile& apex, const std::string& mountPoint) {
1071 auto ret =
1072 MountPackageImpl(apex, mountPoint, GetPackageId(apex.GetManifest()),
1073 /* verifyImage = */ false);
1074 if (!ret.Ok()) {
1075 return ret.ErrorStatus();
1076 }
1077
1078 gMountedApexes.AddMountedApex(apex.GetManifest().name(), false,
1079 std::move(*ret));
1080 return Status::Success();
1081 }
1082
UnmountPackage(const ApexFile & apex)1083 Status UnmountPackage(const ApexFile& apex) {
1084 return android::apex::UnmountPackage(apex, /* allow_latest= */ false);
1085 }
1086
IsMounted(const std::string & name,const std::string & full_path)1087 bool IsMounted(const std::string& name, const std::string& full_path) {
1088 bool found_mounted = false;
1089 gMountedApexes.ForallMountedApexes(
1090 name, [&](const MountedApexData& data, bool latest ATTRIBUTE_UNUSED) {
1091 if (full_path == data.full_path) {
1092 found_mounted = true;
1093 }
1094 });
1095 return found_mounted;
1096 }
1097
GetPackageMountPoint(const ApexManifest & manifest)1098 std::string GetPackageMountPoint(const ApexManifest& manifest) {
1099 return StringPrintf("%s/%s", kApexRoot, GetPackageId(manifest).c_str());
1100 }
1101
GetActiveMountPoint(const ApexManifest & manifest)1102 std::string GetActiveMountPoint(const ApexManifest& manifest) {
1103 return StringPrintf("%s/%s", kApexRoot, manifest.name().c_str());
1104 }
1105
1106 } // namespace apexd_private
1107
resumeRollbackIfNeeded()1108 Status resumeRollbackIfNeeded() {
1109 auto session = ApexSession::GetActiveSession();
1110 if (!session.Ok()) {
1111 return session.ErrorStatus();
1112 }
1113 if (!session->has_value()) {
1114 return Status::Success();
1115 }
1116 if ((**session).GetState() == SessionState::ROLLBACK_IN_PROGRESS) {
1117 // This means that phone was rebooted during the rollback. Resuming it.
1118 return ResumeRollback(**session);
1119 }
1120 return Status::Success();
1121 }
1122
activatePackageImpl(const ApexFile & apex_file)1123 Status activatePackageImpl(const ApexFile& apex_file) {
1124 const ApexManifest& manifest = apex_file.GetManifest();
1125
1126 if (gBootstrap && !isBootstrapApex(apex_file)) {
1127 LOG(INFO) << "Skipped when bootstrapping";
1128 return Status::Success();
1129 } else if (!kUpdatable && !gBootstrap && isBootstrapApex(apex_file)) {
1130 LOG(INFO) << "Package already activated in bootstrap";
1131 return Status::Success();
1132 }
1133
1134 // See whether we think it's active, and do not allow to activate the same
1135 // version. Also detect whether this is the highest version.
1136 // We roll this into a single check.
1137 bool is_newest_version = true;
1138 bool found_other_version = false;
1139 bool version_found_mounted = false;
1140 {
1141 uint64_t new_version = manifest.version();
1142 bool version_found_active = false;
1143 gMountedApexes.ForallMountedApexes(
1144 manifest.name(), [&](const MountedApexData& data, bool latest) {
1145 StatusOr<ApexFile> otherApex = ApexFile::Open(data.full_path);
1146 if (!otherApex.Ok()) {
1147 return;
1148 }
1149 found_other_version = true;
1150 if (static_cast<uint64_t>(otherApex->GetManifest().version()) ==
1151 new_version) {
1152 version_found_mounted = true;
1153 version_found_active = latest;
1154 }
1155 if (static_cast<uint64_t>(otherApex->GetManifest().version()) >
1156 new_version) {
1157 is_newest_version = false;
1158 }
1159 });
1160 if (version_found_active) {
1161 LOG(DEBUG) << "Package " << manifest.name() << " with version "
1162 << manifest.version() << " already active";
1163 return Status::Success();
1164 }
1165 }
1166
1167 const std::string& mountPoint = apexd_private::GetPackageMountPoint(manifest);
1168
1169 if (!version_found_mounted) {
1170 Status mountStatus = apexd_private::MountPackage(apex_file, mountPoint);
1171 if (!mountStatus.Ok()) {
1172 return mountStatus;
1173 }
1174 }
1175
1176 bool mounted_latest = false;
1177 if (is_newest_version) {
1178 const Status& update_st = apexd_private::BindMount(
1179 apexd_private::GetActiveMountPoint(manifest), mountPoint);
1180 mounted_latest = update_st.Ok();
1181 if (!update_st.Ok()) {
1182 return Status::Fail(StringLog()
1183 << "Failed to update package " << manifest.name()
1184 << " to version " << manifest.version() << " : "
1185 << update_st.ErrorMessage());
1186 }
1187 }
1188 if (mounted_latest) {
1189 gMountedApexes.SetLatest(manifest.name(), apex_file.GetPath());
1190 }
1191
1192 LOG(DEBUG) << "Successfully activated " << apex_file.GetPath()
1193 << " package_name: " << manifest.name()
1194 << " version: " << manifest.version();
1195 return Status::Success();
1196 }
1197
activatePackage(const std::string & full_path)1198 Status activatePackage(const std::string& full_path) {
1199 LOG(INFO) << "Trying to activate " << full_path;
1200
1201 StatusOr<ApexFile> apex_file = ApexFile::Open(full_path);
1202 if (!apex_file.Ok()) {
1203 return apex_file.ErrorStatus();
1204 }
1205 return activatePackageImpl(*apex_file);
1206 }
1207
deactivatePackage(const std::string & full_path)1208 Status deactivatePackage(const std::string& full_path) {
1209 LOG(INFO) << "Trying to deactivate " << full_path;
1210
1211 StatusOr<ApexFile> apexFile = ApexFile::Open(full_path);
1212 if (!apexFile.Ok()) {
1213 return apexFile.ErrorStatus();
1214 }
1215
1216 return UnmountPackage(*apexFile, /* allow_latest= */ true);
1217 }
1218
getActivePackages()1219 std::vector<ApexFile> getActivePackages() {
1220 std::vector<ApexFile> ret;
1221 gMountedApexes.ForallMountedApexes(
1222 [&](const std::string&, const MountedApexData& data, bool latest) {
1223 if (!latest) {
1224 return;
1225 }
1226
1227 StatusOr<ApexFile> apexFile = ApexFile::Open(data.full_path);
1228 if (!apexFile.Ok()) {
1229 // TODO: Fail?
1230 return;
1231 }
1232 ret.emplace_back(std::move(*apexFile));
1233 });
1234
1235 return ret;
1236 }
1237
1238 namespace {
GetActivePackagesMap()1239 std::unordered_map<std::string, uint64_t> GetActivePackagesMap() {
1240 std::vector<ApexFile> active_packages = getActivePackages();
1241 std::unordered_map<std::string, uint64_t> ret;
1242 for (const auto& package : active_packages) {
1243 const ApexManifest& manifest = package.GetManifest();
1244 ret.insert({manifest.name(), manifest.version()});
1245 }
1246 return ret;
1247 }
1248
1249 } // namespace
1250
getFactoryPackages()1251 std::vector<ApexFile> getFactoryPackages() {
1252 std::vector<ApexFile> ret;
1253 for (const auto& dir : kApexPackageBuiltinDirs) {
1254 auto apex_files = FindApexFilesByName(dir, /* include_dirs=*/false);
1255 if (!apex_files.Ok()) {
1256 LOG(ERROR) << apex_files.ErrorMessage();
1257 continue;
1258 }
1259 for (const std::string& path : *apex_files) {
1260 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
1261 if (!apex_file.Ok()) {
1262 LOG(ERROR) << apex_file.ErrorMessage();
1263 } else {
1264 ret.emplace_back(std::move(*apex_file));
1265 }
1266 }
1267 }
1268 return ret;
1269 }
1270
getActivePackage(const std::string & packageName)1271 StatusOr<ApexFile> getActivePackage(const std::string& packageName) {
1272 std::vector<ApexFile> packages = getActivePackages();
1273 for (ApexFile& apex : packages) {
1274 if (apex.GetManifest().name() == packageName) {
1275 return StatusOr<ApexFile>(std::move(apex));
1276 }
1277 }
1278
1279 return StatusOr<ApexFile>::MakeError(
1280 PStringLog() << "Cannot find matching package for: " << packageName);
1281 }
1282
abortActiveSession()1283 Status abortActiveSession() {
1284 auto session_or_none = ApexSession::GetActiveSession();
1285 if (!session_or_none.Ok()) {
1286 return session_or_none.ErrorStatus();
1287 }
1288 if (session_or_none->has_value()) {
1289 auto& session = session_or_none->value();
1290 LOG(DEBUG) << "Aborting active session " << session;
1291 switch (session.GetState()) {
1292 case SessionState::VERIFIED:
1293 [[clang::fallthrough]];
1294 case SessionState::STAGED:
1295 return session.DeleteSession();
1296 case SessionState::ACTIVATED:
1297 return RollbackActivatedSession(session);
1298 default:
1299 return Status::Fail(StringLog()
1300 << "Session " << session << " can't be aborted");
1301 }
1302 } else {
1303 LOG(DEBUG) << "There are no active sessions";
1304 return Status::Success();
1305 }
1306 }
1307
scanPackagesDirAndActivate(const char * apex_package_dir)1308 Status scanPackagesDirAndActivate(const char* apex_package_dir) {
1309 LOG(INFO) << "Scanning " << apex_package_dir << " looking for APEX packages.";
1310
1311 const bool scanBuiltinApexes = isPathForBuiltinApexes(apex_package_dir);
1312 StatusOr<std::vector<std::string>> scan =
1313 FindApexFilesByName(apex_package_dir, scanBuiltinApexes);
1314 if (!scan.Ok()) {
1315 return Status::Fail(StringLog() << "Failed to scan " << apex_package_dir
1316 << " : " << scan.ErrorMessage());
1317 }
1318
1319 const auto& packages_with_code = GetActivePackagesMap();
1320
1321 std::vector<std::string> failed_pkgs;
1322 size_t activated_cnt = 0;
1323 size_t skipped_cnt = 0;
1324 for (const std::string& name : *scan) {
1325 LOG(INFO) << "Found " << name;
1326
1327 StatusOr<ApexFile> apex_file = ApexFile::Open(name);
1328 if (!apex_file.Ok()) {
1329 LOG(ERROR) << "Failed to activate " << name << " : "
1330 << apex_file.ErrorMessage();
1331 failed_pkgs.push_back(name);
1332 continue;
1333 }
1334
1335 uint64_t new_version =
1336 static_cast<uint64_t>(apex_file->GetManifest().version());
1337 const auto& it = packages_with_code.find(apex_file->GetManifest().name());
1338 if (it != packages_with_code.end() && it->second >= new_version) {
1339 LOG(INFO) << "Skipping activation of " << name
1340 << " same package with higher version " << it->second
1341 << " is already active";
1342 skipped_cnt++;
1343 continue;
1344 }
1345
1346 if (!kUpdatable && !apex_file->IsFlattened()) {
1347 LOG(INFO) << "Skipping activation of non-flattened apex package " << name
1348 << " because device doesn't support it";
1349 skipped_cnt++;
1350 continue;
1351 }
1352
1353 Status res = activatePackageImpl(*apex_file);
1354 if (!res.Ok()) {
1355 LOG(ERROR) << "Failed to activate " << name << " : "
1356 << res.ErrorMessage();
1357 failed_pkgs.push_back(name);
1358 } else {
1359 activated_cnt++;
1360 }
1361 }
1362
1363 if (!failed_pkgs.empty()) {
1364 return Status::Fail(StringLog()
1365 << "Failed to activate following packages : "
1366 << Join(failed_pkgs, ','));
1367 }
1368
1369 LOG(INFO) << "Activated " << activated_cnt
1370 << " packages. Skipped: " << skipped_cnt;
1371 return Status::Success();
1372 }
1373
scanStagedSessionsDirAndStage()1374 void scanStagedSessionsDirAndStage() {
1375 LOG(INFO) << "Scanning " << kApexSessionsDir
1376 << " looking for sessions to be activated.";
1377
1378 auto stagedSessions = ApexSession::GetSessionsInState(SessionState::STAGED);
1379 for (auto& session : stagedSessions) {
1380 auto sessionId = session.GetId();
1381
1382 auto session_failed_fn = [&]() {
1383 LOG(WARNING) << "Marking session " << sessionId << " as failed.";
1384 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATION_FAILED);
1385 if (!st.Ok()) {
1386 LOG(WARNING) << "Failed to mark session " << sessionId
1387 << " as failed : " << st.ErrorMessage();
1388 }
1389 };
1390 auto scope_guard = android::base::make_scope_guard(session_failed_fn);
1391
1392 std::vector<std::string> dirsToScan;
1393 if (session.GetChildSessionIds().empty()) {
1394 dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" +
1395 std::to_string(sessionId));
1396 } else {
1397 for (auto childSessionId : session.GetChildSessionIds()) {
1398 dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" +
1399 std::to_string(childSessionId));
1400 }
1401 }
1402
1403 std::vector<std::string> apexes;
1404 bool scanSuccessful = true;
1405 for (const auto& dirToScan : dirsToScan) {
1406 StatusOr<std::vector<std::string>> scan =
1407 FindApexFilesByName(dirToScan, /* include_dirs=*/false);
1408 if (!scan.Ok()) {
1409 LOG(WARNING) << scan.ErrorMessage();
1410 scanSuccessful = false;
1411 break;
1412 }
1413
1414 if (scan->size() > 1) {
1415 LOG(WARNING) << "More than one APEX package found in the same session "
1416 << "directory " << dirToScan << ", skipping activation.";
1417 scanSuccessful = false;
1418 break;
1419 }
1420
1421 if (scan->empty()) {
1422 LOG(WARNING) << "No APEX packages found while scanning " << dirToScan
1423 << " session id: " << sessionId << ".";
1424 scanSuccessful = false;
1425 break;
1426 }
1427 apexes.push_back(std::move((*scan)[0]));
1428 }
1429
1430 if (!scanSuccessful) {
1431 continue;
1432 }
1433
1434 // Run postinstall, if necessary.
1435 Status postinstall_status = postinstallPackages(apexes);
1436 if (!postinstall_status.Ok()) {
1437 LOG(ERROR) << "Postinstall failed for session "
1438 << std::to_string(sessionId) << ": "
1439 << postinstall_status.ErrorMessage();
1440 continue;
1441 }
1442
1443 const Status result = stagePackages(apexes);
1444 if (!result.Ok()) {
1445 LOG(ERROR) << "Activation failed for packages " << Join(apexes, ',')
1446 << ": " << result.ErrorMessage();
1447 continue;
1448 }
1449
1450 // Session was OK, release scopeguard.
1451 scope_guard.Disable();
1452
1453 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATED);
1454 if (!st.Ok()) {
1455 LOG(ERROR) << "Failed to mark " << session
1456 << " as activated : " << st.ErrorMessage();
1457 }
1458 }
1459 }
1460
preinstallPackages(const std::vector<std::string> & paths)1461 Status preinstallPackages(const std::vector<std::string>& paths) {
1462 if (paths.empty()) {
1463 return Status::Fail("Empty set of inputs");
1464 }
1465 LOG(DEBUG) << "preinstallPackages() for " << Join(paths, ',');
1466 return HandlePackages<Status>(paths, PreinstallPackages);
1467 }
1468
postinstallPackages(const std::vector<std::string> & paths)1469 Status postinstallPackages(const std::vector<std::string>& paths) {
1470 if (paths.empty()) {
1471 return Status::Fail("Empty set of inputs");
1472 }
1473 LOG(DEBUG) << "postinstallPackages() for " << Join(paths, ',');
1474 return HandlePackages<Status>(paths, PostinstallPackages);
1475 }
1476
1477 namespace {
StageDestPath(const ApexFile & apex_file)1478 std::string StageDestPath(const ApexFile& apex_file) {
1479 return StringPrintf("%s/%s%s", kActiveApexPackagesDataDir,
1480 GetPackageId(apex_file.GetManifest()).c_str(),
1481 kApexPackageSuffix);
1482 }
1483
FilterUnnecessaryStagingPaths(const std::vector<std::string> & tmp_paths)1484 std::vector<std::string> FilterUnnecessaryStagingPaths(
1485 const std::vector<std::string>& tmp_paths) {
1486 const auto& packages_with_code = GetActivePackagesMap();
1487
1488 auto filter_fn = [&packages_with_code](const std::string& path) {
1489 auto apex_file = ApexFile::Open(path);
1490 if (!apex_file.Ok()) {
1491 // Pretend that apex should be staged, so that stagePackages will fail
1492 // trying to open it.
1493 return true;
1494 }
1495 std::string dest_path = StageDestPath(*apex_file);
1496 if (access(dest_path.c_str(), F_OK) == 0) {
1497 LOG(DEBUG) << dest_path << " already exists. Skipping";
1498 return false;
1499 }
1500 const ApexManifest& manifest = apex_file->GetManifest();
1501 const auto& it = packages_with_code.find(manifest.name());
1502 uint64_t new_version = static_cast<uint64_t>(manifest.version());
1503 if (it != packages_with_code.end() && it->second == new_version) {
1504 LOG(DEBUG) << GetPackageId(manifest) << " is already active. Skipping";
1505 return false;
1506 }
1507 return true;
1508 };
1509
1510 std::vector<std::string> ret;
1511 std::copy_if(tmp_paths.begin(), tmp_paths.end(), std::back_inserter(ret),
1512 filter_fn);
1513 return ret;
1514 }
1515
1516 } // namespace
1517
stagePackages(const std::vector<std::string> & tmpPaths)1518 Status stagePackages(const std::vector<std::string>& tmpPaths) {
1519 if (tmpPaths.empty()) {
1520 return Status::Fail("Empty set of inputs");
1521 }
1522 LOG(DEBUG) << "stagePackages() for " << Join(tmpPaths, ',');
1523
1524 // Note: this function is temporary. As such the code is not optimized, e.g.,
1525 // it will open ApexFiles multiple times.
1526
1527 // 1) Verify all packages.
1528 auto verify_status = verifyPackages(tmpPaths, VerifyPackageBoot);
1529 if (!verify_status.Ok()) {
1530 return Status::Fail(verify_status.ErrorMessage());
1531 }
1532
1533 // 2) Now stage all of them.
1534
1535 // Make sure that kActiveApexPackagesDataDir exists.
1536 auto create_dir_status =
1537 createDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0750);
1538 if (!create_dir_status.Ok()) {
1539 return Status::Fail(create_dir_status.ErrorMessage());
1540 }
1541
1542 // 2) Filter out packages that do not require staging, e.g.:
1543 // a) Their /data/apex/active/package.apex@version already exists.
1544 // b) Such package is already active
1545 std::vector<std::string> paths_to_stage =
1546 FilterUnnecessaryStagingPaths(tmpPaths);
1547 if (paths_to_stage.empty()) {
1548 // Finish early if nothing to stage. Since stagePackages fails in case
1549 // tmpPaths is empty, it's fine to return Success here.
1550 return Status::Success();
1551 }
1552
1553 // 3) Now stage all of them.
1554
1555 // Ensure the APEX gets removed on failure.
1556 std::unordered_set<std::string> staged_files;
1557 auto deleter = [&staged_files]() {
1558 for (const std::string& staged_path : staged_files) {
1559 if (TEMP_FAILURE_RETRY(unlink(staged_path.c_str())) != 0) {
1560 PLOG(ERROR) << "Unable to unlink " << staged_path;
1561 }
1562 }
1563 };
1564 auto scope_guard = android::base::make_scope_guard(deleter);
1565
1566 std::unordered_set<std::string> staged_packages;
1567 for (const std::string& path : paths_to_stage) {
1568 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
1569 if (!apex_file.Ok()) {
1570 return apex_file.ErrorStatus();
1571 }
1572 std::string dest_path = StageDestPath(*apex_file);
1573
1574 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
1575 // TODO: Get correct binder error status.
1576 return Status::Fail(PStringLog()
1577 << "Unable to link " << apex_file->GetPath() << " to "
1578 << dest_path);
1579 }
1580 staged_files.insert(dest_path);
1581 staged_packages.insert(apex_file->GetManifest().name());
1582
1583 LOG(DEBUG) << "Success linking " << apex_file->GetPath() << " to "
1584 << dest_path;
1585 }
1586
1587 scope_guard.Disable(); // Accept the state.
1588
1589 return RemovePreviouslyActiveApexFiles(staged_packages, staged_files);
1590 }
1591
unstagePackages(const std::vector<std::string> & paths)1592 Status unstagePackages(const std::vector<std::string>& paths) {
1593 if (paths.empty()) {
1594 return Status::Fail("Empty set of inputs");
1595 }
1596 LOG(DEBUG) << "unstagePackages() for " << Join(paths, ',');
1597
1598 // TODO: to make unstage safer, we can copy to be unstaged packages to a
1599 // temporary folder and restore state from it in case unstagePackages fails.
1600
1601 for (const std::string& path : paths) {
1602 if (access(path.c_str(), F_OK) != 0) {
1603 return Status::Fail(PStringLog() << "Can't access " << path);
1604 }
1605 }
1606
1607 for (const std::string& path : paths) {
1608 if (unlink(path.c_str()) != 0) {
1609 return Status::Fail(PStringLog() << "Can't unlink " << path);
1610 }
1611 }
1612
1613 return Status::Success();
1614 }
1615
rollbackStagedSessionIfAny()1616 Status rollbackStagedSessionIfAny() {
1617 auto session = ApexSession::GetActiveSession();
1618 if (!session.Ok()) {
1619 return session.ErrorStatus();
1620 }
1621 if (!session->has_value()) {
1622 LOG(WARNING) << "No session to rollback";
1623 return Status::Success();
1624 }
1625 if ((*session)->GetState() == SessionState::STAGED) {
1626 LOG(INFO) << "Rolling back session " << **session;
1627 return RollbackStagedSession(**session);
1628 }
1629 return Status::Fail(StringLog() << "Can't rollback " << **session
1630 << " because it is not in STAGED state");
1631 }
1632
rollbackActiveSession()1633 Status rollbackActiveSession() {
1634 auto session = ApexSession::GetActiveSession();
1635 if (!session.Ok()) {
1636 return Status::Fail(StringLog() << "Failed to get active session : "
1637 << session.ErrorMessage());
1638 } else if (!session->has_value()) {
1639 return Status::Fail(
1640 "Rollback requested, when there are no active sessions.");
1641 } else {
1642 return RollbackSession(*(*session));
1643 }
1644 }
1645
rollbackActiveSessionAndReboot()1646 Status rollbackActiveSessionAndReboot() {
1647 auto status = rollbackActiveSession();
1648 if (!status.Ok()) {
1649 return status;
1650 }
1651 LOG(ERROR) << "Successfully rolled back. Time to reboot device.";
1652 if (gInFsCheckpointMode) {
1653 Status res = gVoldService->AbortChanges("apexd_initiated" /* message */,
1654 false /* retry */);
1655 if (!res.Ok()) {
1656 LOG(ERROR) << res.ErrorMessage();
1657 }
1658 }
1659 Reboot();
1660 return Status::Success();
1661 }
1662
onBootstrap()1663 int onBootstrap() {
1664 gBootstrap = true;
1665
1666 Status preAllocate = preAllocateLoopDevices();
1667 if (!preAllocate.Ok()) {
1668 LOG(ERROR) << "Failed to pre-allocate loop devices : "
1669 << preAllocate.ErrorMessage();
1670 }
1671
1672 Status status = collectApexKeys();
1673 if (!status.Ok()) {
1674 LOG(ERROR) << "Failed to collect APEX keys : " << status.ErrorMessage();
1675 return 1;
1676 }
1677
1678 // Activate built-in APEXes for processes launched before /data is mounted.
1679 status = scanPackagesDirAndActivate(kApexPackageSystemDir);
1680 if (!status.Ok()) {
1681 LOG(ERROR) << "Failed to activate APEX files in " << kApexPackageSystemDir
1682 << " : " << status.ErrorMessage();
1683 return 1;
1684 }
1685 LOG(INFO) << "Bootstrapping done";
1686 return 0;
1687 }
1688
onStart(CheckpointInterface * checkpoint_service)1689 void onStart(CheckpointInterface* checkpoint_service) {
1690 LOG(INFO) << "Marking APEXd as starting";
1691 if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusStarting)) {
1692 PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to "
1693 << kApexStatusStarting;
1694 }
1695
1696 if (checkpoint_service != nullptr) {
1697 gVoldService = checkpoint_service;
1698 StatusOr<bool> supports_fs_checkpoints =
1699 gVoldService->SupportsFsCheckpoints();
1700 if (supports_fs_checkpoints.Ok()) {
1701 gSupportsFsCheckpoints = *supports_fs_checkpoints;
1702 } else {
1703 LOG(ERROR) << "Failed to check if filesystem checkpoints are supported: "
1704 << supports_fs_checkpoints.ErrorMessage();
1705 }
1706 if (gSupportsFsCheckpoints) {
1707 StatusOr<bool> needs_checkpoint = gVoldService->NeedsCheckpoint();
1708 if (needs_checkpoint.Ok()) {
1709 gInFsCheckpointMode = *needs_checkpoint;
1710 } else {
1711 LOG(ERROR) << "Failed to check if we're in filesystem checkpoint mode: "
1712 << needs_checkpoint.ErrorMessage();
1713 }
1714 }
1715 }
1716
1717 // Ask whether we should roll back any staged sessions; this can happen if
1718 // we've exceeded the retry count on a device that supports filesystem
1719 // checkpointing.
1720 if (gSupportsFsCheckpoints) {
1721 StatusOr<bool> needs_rollback = gVoldService->NeedsRollback();
1722 if (!needs_rollback.Ok()) {
1723 LOG(ERROR) << "Failed to check if we need a rollback: "
1724 << needs_rollback.ErrorMessage();
1725 } else if (*needs_rollback) {
1726 LOG(INFO) << "Exceeded number of session retries ("
1727 << kNumRetriesWhenCheckpointingEnabled
1728 << "). Starting a rollback";
1729 Status status = rollbackStagedSessionIfAny();
1730 if (!status.Ok()) {
1731 LOG(ERROR)
1732 << "Failed to roll back (as requested by fs checkpointing) : "
1733 << status.ErrorMessage();
1734 }
1735 }
1736 }
1737
1738 Status status = collectApexKeys();
1739 if (!status.Ok()) {
1740 LOG(ERROR) << "Failed to collect APEX keys : " << status.ErrorMessage();
1741 return;
1742 }
1743
1744 gMountedApexes.PopulateFromMounts();
1745
1746 // Activate APEXes from /data/apex. If one in the directory is newer than the
1747 // system one, the new one will eclipse the old one.
1748 scanStagedSessionsDirAndStage();
1749 status = resumeRollbackIfNeeded();
1750 if (!status.Ok()) {
1751 LOG(ERROR) << "Failed to resume rollback : " << status.ErrorMessage();
1752 }
1753
1754 status = scanPackagesDirAndActivate(kActiveApexPackagesDataDir);
1755 if (!status.Ok()) {
1756 LOG(ERROR) << "Failed to activate packages from "
1757 << kActiveApexPackagesDataDir << " : " << status.ErrorMessage();
1758 Status rollback_status = rollbackActiveSessionAndReboot();
1759 if (!rollback_status.Ok()) {
1760 // TODO: should we kill apexd in this case?
1761 LOG(ERROR) << "Failed to rollback : " << rollback_status.ErrorMessage();
1762 }
1763 }
1764
1765 for (const auto& dir : kApexPackageBuiltinDirs) {
1766 // TODO(b/123622800): if activation failed, rollback and reboot.
1767 status = scanPackagesDirAndActivate(dir.c_str());
1768 if (!status.Ok()) {
1769 // This should never happen. Like **really** never.
1770 // TODO: should we kill apexd in this case?
1771 LOG(ERROR) << "Failed to activate packages from " << dir << " : "
1772 << status.ErrorMessage();
1773 }
1774 }
1775 }
1776
onAllPackagesReady()1777 void onAllPackagesReady() {
1778 // Set a system property to let other components to know that APEXs are
1779 // correctly mounted and ready to be used. Before using any file from APEXs,
1780 // they can query this system property to ensure that they are okay to
1781 // access. Or they may have a on-property trigger to delay a task until
1782 // APEXs become ready.
1783 LOG(INFO) << "Marking APEXd as ready";
1784 if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusReady)) {
1785 PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to "
1786 << kApexStatusReady;
1787 }
1788 }
1789
submitStagedSession(const int session_id,const std::vector<int> & child_session_ids)1790 StatusOr<std::vector<ApexFile>> submitStagedSession(
1791 const int session_id, const std::vector<int>& child_session_ids) {
1792 bool needsBackup = true;
1793 Status cleanup_status = ClearSessions();
1794 if (!cleanup_status.Ok()) {
1795 return StatusOr<std::vector<ApexFile>>::MakeError(cleanup_status);
1796 }
1797
1798 if (gSupportsFsCheckpoints) {
1799 Status checkpoint_status =
1800 gVoldService->StartCheckpoint(kNumRetriesWhenCheckpointingEnabled);
1801 if (!checkpoint_status.Ok()) {
1802 // The device supports checkpointing, but we could not start it;
1803 // log a warning, but do continue, since we can live without it.
1804 LOG(WARNING) << "Failed to start filesystem checkpoint on device that "
1805 "should support it: "
1806 << checkpoint_status.ErrorMessage();
1807 } else {
1808 needsBackup = false;
1809 }
1810 }
1811
1812 if (needsBackup) {
1813 Status backup_status = BackupActivePackages();
1814 if (!backup_status.Ok()) {
1815 return StatusOr<std::vector<ApexFile>>::MakeError(backup_status);
1816 }
1817 }
1818
1819 std::vector<int> ids_to_scan;
1820 if (!child_session_ids.empty()) {
1821 ids_to_scan = child_session_ids;
1822 } else {
1823 ids_to_scan = {session_id};
1824 }
1825
1826 std::vector<ApexFile> ret;
1827 for (int id_to_scan : ids_to_scan) {
1828 auto verified = verifySessionDir(id_to_scan);
1829 if (!verified.Ok()) {
1830 return StatusOr<std::vector<ApexFile>>::MakeError(verified.ErrorStatus());
1831 }
1832 ret.push_back(std::move(*verified));
1833 }
1834
1835 // Run preinstall, if necessary.
1836 Status preinstall_status = PreinstallPackages(ret);
1837 if (!preinstall_status.Ok()) {
1838 return StatusOr<std::vector<ApexFile>>::MakeError(preinstall_status);
1839 }
1840
1841 auto session = ApexSession::CreateSession(session_id);
1842 if (!session.Ok()) {
1843 return StatusOr<std::vector<ApexFile>>::MakeError(session.ErrorMessage());
1844 }
1845 (*session).SetChildSessionIds(child_session_ids);
1846 Status commit_status =
1847 (*session).UpdateStateAndCommit(SessionState::VERIFIED);
1848 if (!commit_status.Ok()) {
1849 return StatusOr<std::vector<ApexFile>>::MakeError(commit_status);
1850 }
1851
1852 return StatusOr<std::vector<ApexFile>>(std::move(ret));
1853 }
1854
markStagedSessionReady(const int session_id)1855 Status markStagedSessionReady(const int session_id) {
1856 auto session = ApexSession::GetSession(session_id);
1857 if (!session.Ok()) {
1858 return session.ErrorStatus();
1859 }
1860 // We should only accept sessions in SessionState::VERIFIED or
1861 // SessionState::STAGED state. In the SessionState::STAGED case, this
1862 // function is effectively a no-op.
1863 auto session_state = (*session).GetState();
1864 if (session_state == SessionState::STAGED) {
1865 return Status::Success();
1866 }
1867 if (session_state == SessionState::VERIFIED) {
1868 return (*session).UpdateStateAndCommit(SessionState::STAGED);
1869 }
1870 return Status::Fail(StringLog() << "Invalid state for session " << session_id
1871 << ". Cannot mark it as ready.");
1872 }
1873
markStagedSessionSuccessful(const int session_id)1874 Status markStagedSessionSuccessful(const int session_id) {
1875 auto session = ApexSession::GetSession(session_id);
1876 if (!session.Ok()) {
1877 return session.ErrorStatus();
1878 }
1879 // Only SessionState::ACTIVATED or SessionState::SUCCESS states are accepted.
1880 // In the SessionState::SUCCESS state, this function is a no-op.
1881 if (session->GetState() == SessionState::SUCCESS) {
1882 return Status::Success();
1883 } else if (session->GetState() == SessionState::ACTIVATED) {
1884 auto cleanup_status = DeleteBackup();
1885 if (!cleanup_status.Ok()) {
1886 return Status::Fail(StringLog() << "Failed to mark session " << *session
1887 << " as successful : "
1888 << cleanup_status.ErrorMessage());
1889 }
1890 return session->UpdateStateAndCommit(SessionState::SUCCESS);
1891 } else {
1892 return Status::Fail(StringLog() << "Session " << *session
1893 << " can not be marked successful");
1894 }
1895 }
1896
1897 // Find dangling mounts and unmount them.
1898 // If one is on /data/apex/active, remove it.
unmountDanglingMounts()1899 void unmountDanglingMounts() {
1900 std::multimap<std::string, MountedApexData> danglings;
1901 gMountedApexes.ForallMountedApexes([&](const std::string& package,
1902 const MountedApexData& data,
1903 bool latest) {
1904 if (!latest) {
1905 danglings.insert({package, data});
1906 }
1907 });
1908
1909 for (const auto& [package, data] : danglings) {
1910 const std::string& path = data.full_path;
1911 LOG(VERBOSE) << "Unmounting " << data.mount_point;
1912 gMountedApexes.RemoveMountedApex(package, path);
1913 if (auto st = Unmount(data); !st.Ok()) {
1914 LOG(ERROR) << st.ErrorMessage();
1915 }
1916 if (StartsWith(path, kActiveApexPackagesDataDir)) {
1917 LOG(VERBOSE) << "Deleting old APEX " << path;
1918 if (unlink(path.c_str()) != 0) {
1919 PLOG(ERROR) << "Failed to delete " << path;
1920 }
1921 }
1922 }
1923 }
1924
1925 } // namespace apex
1926 } // namespace android
1927