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