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