blob: 589b962134cc7422abbe1251ce86807147fd32b3 [file] [log] [blame]
Dario Freni3ff2c652018-08-10 19:55:32 +01001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "apexd"
18
Andreas Gampe9d016d52018-10-19 18:56:50 -070019#include "apexd.h"
Andreas Gampe27adafa2018-11-29 12:20:08 -080020#include "apexd_private.h"
Andreas Gampe9d016d52018-10-19 18:56:50 -070021
Andreas Gampe3de3e562018-11-14 08:42:48 -080022#include "apex_database.h"
Dario Freni5a259292018-08-14 17:49:00 +010023#include "apex_file.h"
Jiyong Park5e810232019-04-01 15:24:26 +090024#include "apex_key.h"
Dario Freniaeb233c2018-08-28 12:48:42 +010025#include "apex_manifest.h"
Nikita Ioffe4db13a52019-03-14 23:26:08 +000026#include "apex_shim.h"
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -070027#include "apexd_checkpoint.h"
Andreas Gampe225e1b02019-01-15 14:53:24 -080028#include "apexd_loop.h"
Andreas Gampef7663552019-01-03 09:22:11 -080029#include "apexd_prepostinstall.h"
Jiyong Park4d0f8322019-02-02 19:45:57 +090030#include "apexd_prop.h"
Martijn Coenencabc92f2019-01-11 10:50:35 +010031#include "apexd_session.h"
Martijn Coenen610909b2019-01-18 13:49:38 +010032#include "apexd_utils.h"
Andreas Gampecb1c8da2018-10-23 12:53:14 -070033#include "status_or.h"
Andreas Gampeb99abdd2018-10-19 19:59:17 -070034#include "string_log.h"
Dario Freni5a259292018-08-14 17:49:00 +010035
Luca Stefaniddcef062019-03-13 19:26:19 +010036#include <ApexProperties.sysprop.h>
Dario Freni3ff2c652018-08-10 19:55:32 +010037#include <android-base/file.h>
38#include <android-base/logging.h>
Andreas Gampe3de3e562018-11-14 08:42:48 -080039#include <android-base/macros.h>
Jiyong Park2c7c7282018-11-01 20:02:25 +090040#include <android-base/properties.h>
Andreas Gampe6802c612018-12-06 15:43:49 -080041#include <android-base/scopeguard.h>
Dario Freni3ff2c652018-08-10 19:55:32 +010042#include <android-base/stringprintf.h>
43#include <android-base/strings.h>
Dario Freni5a259292018-08-14 17:49:00 +010044#include <android-base/unique_fd.h>
Martijn Coenen2336ea92018-09-10 15:16:51 +020045#include <libavb/libavb.h>
46#include <libdm/dm.h>
47#include <libdm/dm_table.h>
48#include <libdm/dm_target.h>
Jiyong Parkc2f77eb2018-12-03 22:13:37 +090049#include <selinux/android.h>
Martijn Coenen2336ea92018-09-10 15:16:51 +020050
Dario Freni3ff2c652018-08-10 19:55:32 +010051#include <dirent.h>
Dario Freni5a259292018-08-14 17:49:00 +010052#include <fcntl.h>
Dario Freni3ff2c652018-08-10 19:55:32 +010053#include <linux/loop.h>
Dario Freni5a259292018-08-14 17:49:00 +010054#include <sys/ioctl.h>
Dario Freni3ff2c652018-08-10 19:55:32 +010055#include <sys/mount.h>
56#include <sys/stat.h>
57#include <sys/types.h>
Martijn Coenen2336ea92018-09-10 15:16:51 +020058#include <unistd.h>
Andreas Gampe9fe992c2018-11-06 12:38:17 -080059
60#include <algorithm>
Martijn Coenen2336ea92018-09-10 15:16:51 +020061#include <array>
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +000062#include <filesystem>
Martijn Coenen2336ea92018-09-10 15:16:51 +020063#include <fstream>
64#include <iomanip>
Dario Freni3ff2c652018-08-10 19:55:32 +010065#include <memory>
Nikita Ioffec7c27842019-03-26 19:35:15 +000066#include <optional>
Dario Freni5a259292018-08-14 17:49:00 +010067#include <string>
Nikita Ioffea8365762019-03-18 23:59:03 +000068#include <unordered_map>
Nikita Ioffe6a280af2019-02-04 15:28:57 +000069#include <unordered_set>
Dario Freni3ff2c652018-08-10 19:55:32 +010070
Dario Freniaeb233c2018-08-28 12:48:42 +010071using android::base::EndsWith;
Andreas Gampe7288cca2019-01-15 13:10:34 -080072using android::base::Join;
Andreas Gampee66772f2018-10-17 16:31:58 -070073using android::base::ReadFullyAtOffset;
Dario Freni7f0da582019-01-06 17:54:13 +000074using android::base::StartsWith;
Dario Freniaeb233c2018-08-28 12:48:42 +010075using android::base::StringPrintf;
76using android::base::unique_fd;
Martijn Coenen2336ea92018-09-10 15:16:51 +020077using android::dm::DeviceMapper;
Andreas Gampe82ac3d02018-12-20 10:17:00 -080078using android::dm::DmDeviceState;
Martijn Coenen2336ea92018-09-10 15:16:51 +020079using android::dm::DmTable;
80using android::dm::DmTargetVerity;
Dario Freni3ff2c652018-08-10 19:55:32 +010081
Martijn Coenencabc92f2019-01-11 10:50:35 +010082using apex::proto::SessionState;
83
Dario Freni3ff2c652018-08-10 19:55:32 +010084namespace android {
85namespace apex {
86
Andreas Gampe3de3e562018-11-14 08:42:48 -080087using MountedApexData = MountedApexDatabase::MountedApexData;
88
Andreas Gampe9d016d52018-10-19 18:56:50 -070089namespace {
90
Jiyong Park2c7c7282018-11-01 20:02:25 +090091// These should be in-sync with system/sepolicy/public/property_contexts
92static constexpr const char* kApexStatusSysprop = "apexd.status";
93static constexpr const char* kApexStatusStarting = "starting";
94static constexpr const char* kApexStatusReady = "ready";
Martijn Coenen2336ea92018-09-10 15:16:51 +020095
Gavin Corkerya41373a2019-09-26 12:53:45 +010096static constexpr const char* kBuildFingerprintSysprop = "ro.build.fingerprint";
97
Martijn Coenenafbb8622019-01-09 12:00:47 +010098static constexpr const char* kApexVerityOnSystemProp =
99 "persist.apexd.verity_on_system";
100static bool gForceDmVerityOnSystem =
101 android::base::GetBoolProperty(kApexVerityOnSystemProp, false);
102
Martijn Coenen96909542019-04-02 12:20:03 +0200103// This should be in UAPI, but it's not :-(
104static constexpr const char* kDmVerityRestartOnCorruption =
105 "restart_on_corruption";
106
Andreas Gampe3de3e562018-11-14 08:42:48 -0800107MountedApexDatabase gMountedApexes;
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -0700108
109CheckpointInterface* gVoldService;
Martijn Coenen44de00c2019-03-22 09:13:17 +0100110bool gSupportsFsCheckpoints = false;
111bool gInFsCheckpointMode = false;
Andreas Gampe3de3e562018-11-14 08:42:48 -0800112
Andreas Gampe65e1c752018-10-19 21:56:55 -0700113static constexpr size_t kLoopDeviceSetupAttempts = 3u;
Dario Freni3ff2c652018-08-10 19:55:32 +0100114
Nikita Ioffe2d94d8d2019-04-16 15:11:26 +0100115static const bool kUpdatable =
116 android::sysprop::ApexProperties::updatable().value_or(false);
117
Jiyong Parkef34c142019-02-25 01:13:18 +0900118bool gBootstrap = false;
119static const std::vector<const std::string> kBootstrapApexes = {
120 "com.android.runtime",
121 "com.android.tzdata",
122};
123
Nikita Ioffe7af550d2019-05-13 14:14:37 +0100124static constexpr const int kNumRetriesWhenCheckpointingEnabled = 1;
125
Jooyung Han40531a82019-04-05 15:34:13 +0900126bool isBootstrapApex(const ApexFile& apex) {
127 return std::find(kBootstrapApexes.begin(), kBootstrapApexes.end(),
128 apex.GetManifest().name()) != kBootstrapApexes.end();
129}
130
131// Pre-allocate loop devices so that we don't have to wait for them
132// later when actually activating APEXes.
133Status preAllocateLoopDevices() {
Nikita Ioffee44c58a2019-04-29 14:07:11 +0100134 auto scan = FindApexes(kApexPackageBuiltinDirs);
Jooyung Han40531a82019-04-05 15:34:13 +0900135 if (!scan.Ok()) {
136 return scan.ErrorStatus();
137 }
138
139 auto size = 0;
140 for (const auto& path : *scan) {
141 auto apexFile = ApexFile::Open(path);
142 if (!apexFile.Ok() || apexFile->IsFlattened()) {
143 continue;
144 }
145 size++;
146 // bootstrap Apexes may be activated on separate namespaces.
147 if (isBootstrapApex(*apexFile)) {
148 size++;
149 }
150 }
151
Jiyong Park85c3bd72019-05-03 08:34:35 +0900152 // note: do not call preAllocateLoopDevices() if size == 0
153 // or the device does not support updatable APEX.
Jooyung Han40531a82019-04-05 15:34:13 +0900154 // For devices (e.g. ARC) which doesn't support loop-control
155 // preAllocateLoopDevices() can cause problem when it tries
156 // to access /dev/loop-control.
Jiyong Park85c3bd72019-05-03 08:34:35 +0900157 if (size == 0 || !kUpdatable) {
Jooyung Han40531a82019-04-05 15:34:13 +0900158 return Status::Success();
159 }
160 return loop::preAllocateLoopDevices(size);
161}
162
Martijn Coenen2336ea92018-09-10 15:16:51 +0200163std::unique_ptr<DmTable> createVerityTable(const ApexVerityData& verity_data,
Martijn Coenen96909542019-04-02 12:20:03 +0200164 const std::string& loop,
165 bool restart_on_corruption) {
Martijn Coenen2336ea92018-09-10 15:16:51 +0200166 AvbHashtreeDescriptor* desc = verity_data.desc.get();
167 auto table = std::make_unique<DmTable>();
168
169 std::ostringstream hash_algorithm;
170 hash_algorithm << desc->hash_algorithm;
171
172 auto target = std::make_unique<DmTargetVerity>(
173 0, desc->image_size / 512, desc->dm_verity_version, loop, loop,
174 desc->data_block_size, desc->hash_block_size,
175 desc->image_size / desc->data_block_size,
176 desc->tree_offset / desc->hash_block_size, hash_algorithm.str(),
177 verity_data.root_digest, verity_data.salt);
178
179 target->IgnoreZeroBlocks();
Martijn Coenen96909542019-04-02 12:20:03 +0200180 if (restart_on_corruption) {
181 target->SetVerityMode(kDmVerityRestartOnCorruption);
182 }
Martijn Coenen2336ea92018-09-10 15:16:51 +0200183 table->AddTarget(std::move(target));
184
185 table->set_readonly(true);
186
187 return table;
188}
189
Nikita Ioffebfebf652019-04-05 22:00:00 +0100190enum WaitForDeviceMode {
191 kWaitToBeCreated = 0,
192 kWaitToBeDeleted,
193};
194
195Status waitForDevice(const std::string& device, const WaitForDeviceMode& mode) {
196 // TODO(b/122059364): Make this more efficient
197 // TODO: use std::chrono?
198
199 // Deleting a device might take more time, so wait a little bit longer.
200 size_t num_tries = mode == kWaitToBeCreated ? 10u : 15u;
201
202 LOG(DEBUG) << "Waiting for " << device << " to be "
203 << (mode == kWaitToBeCreated ? "created" : " deleted");
204 for (size_t i = 0; i < num_tries; ++i) {
205 StatusOr<bool> status = PathExists(device);
206 if (status.Ok()) {
207 if (mode == kWaitToBeCreated && *status) {
208 return Status::Success();
209 }
210 if (mode == kWaitToBeDeleted && !*status) {
211 return Status::Success();
212 }
213 }
214 if (i + 1 < num_tries) {
215 usleep(50000);
216 }
217 }
218
219 return Status::Fail(StringLog()
220 << "Failed to wait for device " << device << " to be "
221 << (mode == kWaitToBeCreated ? " created" : " deleted"));
222}
223
224// Deletes a dm-verity device with a given name and path.
225// Synchronizes on the device actually being deleted from userspace.
226Status DeleteVerityDevice(const std::string& name, const std::string& path) {
227 DeviceMapper& dm = DeviceMapper::Instance();
228 if (!dm.DeleteDevice(name)) {
229 return Status::Fail(StringLog() << "Failed to delete device " << name
230 << " with path " << path);
231 }
232 // Block until device is deleted from userspace.
233 return waitForDevice(path, kWaitToBeDeleted);
234}
235
236// Deletes dm-verity device with a given name.
237// See function above.
238Status DeleteVerityDevice(const std::string& name) {
239 DeviceMapper& dm = DeviceMapper::Instance();
240 std::string path;
241 if (!dm.GetDmDevicePathByName(name, &path)) {
242 return Status::Fail(StringLog()
243 << "Unable to get path for dm-verity device " << name);
244 }
245 return DeleteVerityDevice(name, path);
246}
247
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700248class DmVerityDevice {
249 public:
250 DmVerityDevice() : cleared_(true) {}
Chih-Hung Hsiehab73a442019-01-02 11:28:05 -0800251 explicit DmVerityDevice(const std::string& name)
252 : name_(name), cleared_(false) {}
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700253 DmVerityDevice(const std::string& name, const std::string& dev_path)
254 : name_(name), dev_path_(dev_path), cleared_(false) {}
255
Nikita Ioffe6bea4e52019-02-10 22:46:05 +0000256 DmVerityDevice(DmVerityDevice&& other) noexcept
257 : name_(std::move(other.name_)),
258 dev_path_(std::move(other.dev_path_)),
Dario Freni88b859b2018-11-06 17:23:36 +0000259 cleared_(other.cleared_) {
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700260 other.cleared_ = true;
261 }
262
Nikita Ioffe6bea4e52019-02-10 22:46:05 +0000263 DmVerityDevice& operator=(DmVerityDevice&& other) noexcept {
Jiyong Park9eb66662018-11-13 20:21:03 +0900264 name_ = other.name_;
265 dev_path_ = other.dev_path_;
266 cleared_ = other.cleared_;
267 other.cleared_ = true;
268 return *this;
269 }
270
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700271 ~DmVerityDevice() {
272 if (!cleared_) {
Nikita Ioffebfebf652019-04-05 22:00:00 +0100273 Status ret = DeleteVerityDevice(name_, dev_path_);
274 if (!ret.Ok()) {
275 LOG(ERROR) << ret.ErrorMessage();
276 }
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700277 }
278 }
279
Dario Freni88b859b2018-11-06 17:23:36 +0000280 const std::string& GetName() const { return name_; }
281 const std::string& GetDevPath() const { return dev_path_; }
282 void SetDevPath(const std::string& dev_path) { dev_path_ = dev_path; }
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700283
Dario Freni88b859b2018-11-06 17:23:36 +0000284 void Release() { cleared_ = true; }
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700285
286 private:
287 std::string name_;
288 std::string dev_path_;
289 bool cleared_;
290};
291
Dario Freni88b859b2018-11-06 17:23:36 +0000292StatusOr<DmVerityDevice> createVerityDevice(const std::string& name,
293 const DmTable& table) {
Martijn Coenen2336ea92018-09-10 15:16:51 +0200294 DeviceMapper& dm = DeviceMapper::Instance();
295
Andreas Gampe82ac3d02018-12-20 10:17:00 -0800296 if (dm.GetState(name) != DmDeviceState::INVALID) {
Nikita Ioffebfebf652019-04-05 22:00:00 +0100297 // TODO: since apexd tears down devices during unmount, can this happen?
Andreas Gampe82ac3d02018-12-20 10:17:00 -0800298 LOG(WARNING) << "Deleting existing dm device " << name;
Nikita Ioffebfebf652019-04-05 22:00:00 +0100299 const Status& status = DeleteVerityDevice(name);
300 if (!status.Ok()) {
301 // TODO: should we fail instead?
302 LOG(ERROR) << "Failed to delete device " << name << " : "
303 << status.ErrorMessage();
304 }
Andreas Gampe82ac3d02018-12-20 10:17:00 -0800305 }
Martijn Coenen2336ea92018-09-10 15:16:51 +0200306
307 if (!dm.CreateDevice(name, table)) {
Dario Freni88b859b2018-11-06 17:23:36 +0000308 return StatusOr<DmVerityDevice>::MakeError(
309 "Couldn't create verity device.");
Martijn Coenen2336ea92018-09-10 15:16:51 +0200310 }
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700311 DmVerityDevice dev(name);
Martijn Coenen2336ea92018-09-10 15:16:51 +0200312
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700313 std::string dev_path;
Martijn Coenen2336ea92018-09-10 15:16:51 +0200314 if (!dm.GetDmDevicePathByName(name, &dev_path)) {
Dario Freni88b859b2018-11-06 17:23:36 +0000315 return StatusOr<DmVerityDevice>::MakeError(
316 "Couldn't get verity device path!");
Martijn Coenen2336ea92018-09-10 15:16:51 +0200317 }
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700318 dev.SetDevPath(dev_path);
Martijn Coenen2336ea92018-09-10 15:16:51 +0200319
Andreas Gampeef9a1d72018-10-23 12:54:35 -0700320 return StatusOr<DmVerityDevice>(std::move(dev));
Martijn Coenen2336ea92018-09-10 15:16:51 +0200321}
322
Nikita Ioffe6a280af2019-02-04 15:28:57 +0000323Status RemovePreviouslyActiveApexFiles(
324 const std::unordered_set<std::string>& affected_packages,
325 const std::unordered_set<std::string>& files_to_keep) {
326 auto all_active_apex_files =
Nikita Ioffea8453da2019-01-30 21:29:13 +0000327 FindApexFilesByName(kActiveApexPackagesDataDir, false /* include_dirs */);
Nikita Ioffe6a280af2019-02-04 15:28:57 +0000328
329 if (!all_active_apex_files.Ok()) {
330 return all_active_apex_files.ErrorStatus();
331 }
332
333 for (const std::string& path : *all_active_apex_files) {
334 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
335 if (!apex_file.Ok()) {
336 return apex_file.ErrorStatus();
337 }
338
339 const std::string& package_name = apex_file->GetManifest().name();
340 if (affected_packages.find(package_name) == affected_packages.end()) {
341 // This apex belongs to a package that wasn't part of this stage sessions,
342 // hence it should be kept.
343 continue;
344 }
345
346 if (files_to_keep.find(apex_file->GetPath()) != files_to_keep.end()) {
347 // This is a path that was staged and should be kept.
348 continue;
349 }
350
351 LOG(DEBUG) << "Deleting previously active apex " << apex_file->GetPath();
352 if (unlink(apex_file->GetPath().c_str()) != 0) {
353 return Status::Fail(PStringLog()
354 << "Failed to unlink " << apex_file->GetPath());
355 }
356 }
357
358 return Status::Success();
359}
360
Martijn Coenen2caabb42019-04-02 11:42:02 +0200361// Reads the entire device to verify the image is authenticatic
362Status readVerityDevice(const std::string& verity_device,
363 uint64_t device_size) {
364 static constexpr int kBlockSize = 4096;
365 static constexpr size_t kBufSize = 1024 * kBlockSize;
366 std::vector<uint8_t> buffer(kBufSize);
367
368 unique_fd fd(TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY)));
369 if (fd.get() == -1) {
Nikita Ioffedcf8deb2019-04-03 16:40:13 +0100370 return Status::Fail(PStringLog() << "Can't open " << verity_device);
Martijn Coenen2caabb42019-04-02 11:42:02 +0200371 }
372
373 size_t bytes_left = device_size;
374 while (bytes_left > 0) {
375 size_t to_read = std::min(bytes_left, kBufSize);
376 if (!android::base::ReadFully(fd.get(), buffer.data(), to_read)) {
377 return Status::Fail(PStringLog() << "Can't verify " << verity_device
378 << "; corrupted?");
379 }
380 bytes_left -= to_read;
381 }
382
383 return Status::Success();
384}
385
Nikita Ioffe891723c2019-03-25 14:35:39 +0000386Status VerifyMountedImage(const ApexFile& apex,
387 const std::string& mount_point) {
388 auto status = apex.VerifyManifestMatches(mount_point);
389 if (!status.Ok()) {
390 return status;
391 }
392 if (shim::IsShimApex(apex)) {
Nikita Ioffe69a1bc52019-03-29 14:55:29 +0000393 return shim::ValidateShimApex(mount_point, apex);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000394 }
395 return Status::Success();
396}
397
Nikita Ioffe31557332019-03-20 16:15:02 +0000398StatusOr<MountedApexData> mountNonFlattened(const ApexFile& apex,
Nikita Ioffe891723c2019-03-25 14:35:39 +0000399 const std::string& mountPoint,
Martijn Coenen2caabb42019-04-02 11:42:02 +0200400 const std::string& device_name,
401 bool verifyImage) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000402 using StatusM = StatusOr<MountedApexData>;
Jiyong Parkd02c88c2018-11-13 19:23:32 +0900403 const std::string& full_path = apex.GetPath();
Andreas Gampe2c376bf2018-11-13 13:18:36 -0800404
Nikita Ioffe3d3769c2019-05-08 17:11:53 +0000405 if (!kUpdatable) {
406 return StatusM::Fail(StringLog()
407 << "Unable to mount non-flattened apex package "
408 << full_path << " because device doesn't support it");
409 }
410
Andreas Gampe225e1b02019-01-15 14:53:24 -0800411 loop::LoopbackDeviceUniqueFd loopbackDevice;
Dario Freni88b859b2018-11-06 17:23:36 +0000412 for (size_t attempts = 1;; ++attempts) {
Andreas Gampe225e1b02019-01-15 14:53:24 -0800413 StatusOr<loop::LoopbackDeviceUniqueFd> ret = loop::createLoopDevice(
414 full_path, apex.GetImageOffset(), apex.GetImageSize());
Andreas Gampe65e1c752018-10-19 21:56:55 -0700415 if (ret.Ok()) {
Andreas Gampecb1c8da2018-10-23 12:53:14 -0700416 loopbackDevice = std::move(*ret);
Andreas Gampe65e1c752018-10-19 21:56:55 -0700417 break;
418 }
419 if (attempts >= kLoopDeviceSetupAttempts) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000420 return StatusM::Fail(StringLog()
421 << "Could not create loop device for " << full_path
422 << ": " << ret.ErrorMessage());
Andreas Gampe65e1c752018-10-19 21:56:55 -0700423 }
Dario Freni5a259292018-08-14 17:49:00 +0100424 }
Andreas Gampecb1c8da2018-10-23 12:53:14 -0700425 LOG(VERBOSE) << "Loopback device created: " << loopbackDevice.name;
Dario Freni5a259292018-08-14 17:49:00 +0100426
Jiyong Park5e810232019-04-01 15:24:26 +0900427 auto verityData = apex.VerifyApexVerity();
Andreas Gampe50c6e4c2018-10-31 15:32:56 -0700428 if (!verityData.Ok()) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000429 return StatusM::Fail(StringLog()
430 << "Failed to verify Apex Verity data for "
431 << full_path << ": " << verityData.ErrorMessage());
Martijn Coenen2336ea92018-09-10 15:16:51 +0200432 }
Jiyong Park9eb66662018-11-13 20:21:03 +0900433 std::string blockDevice = loopbackDevice.name;
Nikita Ioffec7c27842019-03-26 19:35:15 +0000434 MountedApexData apex_data(loopbackDevice.name, apex.GetPath(), mountPoint,
Nikita Ioffe891723c2019-03-25 14:35:39 +0000435 device_name);
Martijn Coenen2336ea92018-09-10 15:16:51 +0200436
Jiyong Parkf94de552019-03-19 16:13:32 +0900437 // for APEXes in immutable partitions, we don't need to mount them on
438 // dm-verity because they are already in the dm-verity protected partition;
439 // system. However, note that we don't skip verification to ensure that APEXes
440 // are correctly signed.
Jiyong Park9eb66662018-11-13 20:21:03 +0900441 const bool mountOnVerity =
Jiyong Parkf94de552019-03-19 16:13:32 +0900442 gForceDmVerityOnSystem || !isPathForBuiltinApexes(full_path);
Jiyong Park9eb66662018-11-13 20:21:03 +0900443 DmVerityDevice verityDev;
444 if (mountOnVerity) {
Martijn Coenen96909542019-04-02 12:20:03 +0200445 auto verityTable =
446 createVerityTable(*verityData, loopbackDevice.name,
447 /* restart_on_corruption = */ !verifyImage);
Jiyong Park9eb66662018-11-13 20:21:03 +0900448 StatusOr<DmVerityDevice> verityDevRes =
Nikita Ioffe891723c2019-03-25 14:35:39 +0000449 createVerityDevice(device_name, *verityTable);
Jiyong Park9eb66662018-11-13 20:21:03 +0900450 if (!verityDevRes.Ok()) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000451 return StatusM::Fail(StringLog()
452 << "Failed to create Apex Verity device "
453 << full_path << ": " << verityDevRes.ErrorMessage());
Jiyong Park9eb66662018-11-13 20:21:03 +0900454 }
455 verityDev = std::move(*verityDevRes);
456 blockDevice = verityDev.GetDevPath();
Martijn Coenen2336ea92018-09-10 15:16:51 +0200457
Andreas Gampe225e1b02019-01-15 14:53:24 -0800458 Status readAheadStatus = loop::configureReadAhead(verityDev.GetDevPath());
Jiyong Park9eb66662018-11-13 20:21:03 +0900459 if (!readAheadStatus.Ok()) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000460 return StatusM::MakeError(readAheadStatus);
Jiyong Park9eb66662018-11-13 20:21:03 +0900461 }
Martijn Coenenbc400ae2018-11-08 14:05:49 +0100462 }
463
Martijn Coenen2caabb42019-04-02 11:42:02 +0200464 // TODO(b/122059364): Even though the kernel has created the verity
465 // device, we still depend on ueventd to run to actually create the
466 // device node in userspace. To solve this properly we should listen on
467 // the netlink socket for uevents, or use inotify. For now, this will
468 // have to do.
Nikita Ioffebfebf652019-04-05 22:00:00 +0100469 Status deviceStatus = waitForDevice(blockDevice, kWaitToBeCreated);
Martijn Coenen2caabb42019-04-02 11:42:02 +0200470 if (!deviceStatus.Ok()) {
471 return StatusM::MakeError(deviceStatus);
472 }
Martijn Coenenbdb3c172018-12-27 11:23:41 +0100473
Nikita Ioffe426dbc42019-04-11 16:09:48 +0100474 // TODO: consider moving this inside RunVerifyFnInsideTempMount.
Martijn Coenen2caabb42019-04-02 11:42:02 +0200475 if (mountOnVerity && verifyImage) {
476 Status verityStatus =
477 readVerityDevice(blockDevice, (*verityData).desc->image_size);
478 if (!verityStatus.Ok()) {
479 return StatusM::MakeError(verityStatus);
Jiyong Park9eb66662018-11-13 20:21:03 +0900480 }
Dario Freni3ff2c652018-08-10 19:55:32 +0100481 }
Martijn Coenen2caabb42019-04-02 11:42:02 +0200482
483 if (mount(blockDevice.c_str(), mountPoint.c_str(), "ext4",
484 MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY, nullptr) == 0) {
485 LOG(INFO) << "Successfully mounted package " << full_path << " on "
486 << mountPoint;
487 auto status = VerifyMountedImage(apex, mountPoint);
488 if (!status.Ok()) {
489 umount2(mountPoint.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH);
490 return StatusM::Fail(StringLog() << "Failed to verify " << full_path
491 << ": " << status.ErrorMessage());
492 }
493 // Time to accept the temporaries as good.
494 if (mountOnVerity) {
495 verityDev.Release();
496 }
497 loopbackDevice.CloseGood();
498
499 return StatusM(std::move(apex_data));
500 } else {
501 return StatusM::Fail(StringLog() << "Mounting failed for package "
502 << full_path << " : " << strerror(errno));
503 }
Dario Freni3ff2c652018-08-10 19:55:32 +0100504}
505
Nikita Ioffe31557332019-03-20 16:15:02 +0000506StatusOr<MountedApexData> mountFlattened(const ApexFile& apex,
507 const std::string& mountPoint) {
508 using StatusM = StatusOr<MountedApexData>;
Jiyong Parkf94de552019-03-19 16:13:32 +0900509 if (!isPathForBuiltinApexes(apex.GetPath())) {
Nikita Ioffe31557332019-03-20 16:15:02 +0000510 return StatusM::Fail(StringLog() << "Cannot activate flattened APEX "
511 << apex.GetPath());
Jiyong Parkd02c88c2018-11-13 19:23:32 +0900512 }
513
Jiyong Parkd02c88c2018-11-13 19:23:32 +0900514 if (mount(apex.GetPath().c_str(), mountPoint.c_str(), nullptr, MS_BIND,
515 nullptr) == 0) {
516 LOG(INFO) << "Successfully bind-mounted flattened package "
517 << apex.GetPath() << " on " << mountPoint;
518
Nikita Ioffec7c27842019-03-26 19:35:15 +0000519 MountedApexData apex_data("" /* loop_name */, apex.GetPath(), mountPoint,
520 "" /* device_name */);
Nikita Ioffe31557332019-03-20 16:15:02 +0000521 return StatusM(std::move(apex_data));
Jiyong Parkd02c88c2018-11-13 19:23:32 +0900522 }
Nikita Ioffe31557332019-03-20 16:15:02 +0000523 return StatusM::Fail(PStringLog() << "Mounting failed for flattened package "
524 << apex.GetPath());
Jiyong Parkd02c88c2018-11-13 19:23:32 +0900525}
526
Nikita Ioffe891723c2019-03-25 14:35:39 +0000527StatusOr<MountedApexData> MountPackageImpl(const ApexFile& apex,
528 const std::string& mountPoint,
Martijn Coenen2caabb42019-04-02 11:42:02 +0200529 const std::string& device_name,
530 bool verifyImage) {
Nikita Ioffe891723c2019-03-25 14:35:39 +0000531 using StatusM = StatusOr<MountedApexData>;
532 LOG(VERBOSE) << "Creating mount point: " << mountPoint;
533 // Note: the mount point could exist in case when the APEX was activated
534 // during the bootstrap phase (e.g., the runtime or tzdata APEX).
535 // Although we have separate mount namespaces to separate the early activated
536 // APEXes from the normally activate APEXes, the mount points themselves
537 // are shared across the two mount namespaces because /apex (a tmpfs) itself
538 // mounted at / which is (and has to be) a shared mount. Therefore, if apexd
539 // finds an empty directory under /apex, it's not a problem and apexd can use
540 // it.
541 auto exists = PathExists(mountPoint);
542 if (!exists.Ok()) {
543 return StatusM::MakeError(exists.ErrorStatus());
544 }
545 if (!*exists && mkdir(mountPoint.c_str(), kMkdirMode) != 0) {
546 return StatusM::Fail(PStringLog()
547 << "Could not create mount point " << mountPoint);
548 }
549 auto deleter = [&mountPoint]() {
550 if (rmdir(mountPoint.c_str()) != 0) {
551 PLOG(WARNING) << "Could not rmdir " << mountPoint;
552 }
553 };
554 auto scope_guard = android::base::make_scope_guard(deleter);
555 if (!IsEmptyDirectory(mountPoint)) {
556 return StatusM::Fail(PStringLog() << mountPoint << " is not empty");
557 }
558
559 StatusOr<MountedApexData> ret;
560 if (apex.IsFlattened()) {
561 ret = mountFlattened(apex, mountPoint);
562 } else {
Martijn Coenen2caabb42019-04-02 11:42:02 +0200563 ret = mountNonFlattened(apex, mountPoint, device_name, verifyImage);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000564 }
565 if (ret.Ok()) {
566 scope_guard.Disable(); // Accept the mount.
567 }
568 return ret;
569}
570
Martijn Coenen2caabb42019-04-02 11:42:02 +0200571StatusOr<MountedApexData> VerifyAndTempMountPackage(
572 const ApexFile& apex, const std::string& mount_point) {
Nikita Ioffe891723c2019-03-25 14:35:39 +0000573 const std::string& package_id = GetPackageId(apex.GetManifest());
574 LOG(DEBUG) << "Temp mounting " << package_id << " to " << mount_point;
Nikita Ioffebfebf652019-04-05 22:00:00 +0100575 const std::string& temp_device_name = package_id + ".tmp";
Martijn Coenen2caabb42019-04-02 11:42:02 +0200576 return MountPackageImpl(apex, mount_point, temp_device_name,
577 /* verifyImage = */ true);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000578}
579
580Status Unmount(const MountedApexData& data) {
581 // Lazily try to umount whatever is mounted.
582 if (umount2(data.mount_point.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
583 errno != EINVAL && errno != ENOENT) {
584 return Status::Fail(PStringLog()
585 << "Failed to unmount directory " << data.mount_point);
586 }
587 // Attempt to delete the folder. If the folder is retained, other
588 // data may be incorrect.
589 if (rmdir(data.mount_point.c_str()) != 0) {
590 PLOG(ERROR) << "Failed to rmdir directory " << data.mount_point;
591 }
592
593 // Try to free up the device-mapper device.
Nikita Ioffebfebf652019-04-05 22:00:00 +0100594 if (!data.device_name.empty()) {
595 const auto& status = DeleteVerityDevice(data.device_name);
596 if (!status.Ok()) {
597 LOG(DEBUG) << "Failed to free device " << data.device_name << " : "
598 << status.ErrorMessage();
599 }
600 }
Nikita Ioffe891723c2019-03-25 14:35:39 +0000601
602 // Try to free up the loop device.
603 if (!data.loop_name.empty()) {
604 auto log_fn = [](const std::string& path,
605 const std::string& id ATTRIBUTE_UNUSED) {
606 LOG(VERBOSE) << "Freeing loop device " << path << "for unmount.";
607 };
608 loop::DestroyLoopDevice(data.loop_name, log_fn);
609 }
Nikita Ioffebfebf652019-04-05 22:00:00 +0100610
Nikita Ioffe891723c2019-03-25 14:35:39 +0000611 return Status::Success();
612}
613
Nikita Ioffe426dbc42019-04-11 16:09:48 +0100614std::string GetPackageTempMountPoint(const ApexManifest& manifest) {
615 return StringPrintf("%s.tmp",
616 apexd_private::GetPackageMountPoint(manifest).c_str());
617}
618
619template <typename VerifyFn>
620Status RunVerifyFnInsideTempMount(const ApexFile& apex,
621 const VerifyFn& verify_fn) {
622 // Temp mount image of this apex to validate it was properly signed;
623 // this will also read the entire block device through dm-verity, so
624 // we can be sure there is no corruption.
625 const std::string& temp_mount_point =
626 GetPackageTempMountPoint(apex.GetManifest());
627
628 StatusOr<MountedApexData> mount_status =
629 VerifyAndTempMountPackage(apex, temp_mount_point);
630 if (!mount_status.Ok()) {
631 LOG(ERROR) << "Failed to temp mount to " << temp_mount_point << " : "
632 << mount_status.ErrorMessage();
633 return mount_status.ErrorStatus();
634 }
635 auto cleaner = [&]() {
636 LOG(DEBUG) << "Unmounting " << temp_mount_point;
637 Status status = Unmount(*mount_status);
638 if (!status.Ok()) {
639 LOG(WARNING) << "Failed to unmount " << temp_mount_point << " : "
640 << status.ErrorMessage();
641 }
642 };
643 auto scope_guard = android::base::make_scope_guard(cleaner);
644 return verify_fn(temp_mount_point);
645}
646
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800647template <typename HookFn, typename HookCall>
648Status PrePostinstallPackages(const std::vector<ApexFile>& apexes, HookFn fn,
649 HookCall call) {
Andreas Gampe17739142019-01-09 16:00:26 -0800650 if (apexes.empty()) {
651 return Status::Fail("Empty set of inputs");
652 }
653
654 // 1) Check whether the APEXes have hooks.
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800655 bool has_hooks = false;
Andreas Gampe17739142019-01-09 16:00:26 -0800656 for (const ApexFile& apex_file : apexes) {
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800657 if (!(apex_file.GetManifest().*fn)().empty()) {
658 has_hooks = true;
Andreas Gampe17739142019-01-09 16:00:26 -0800659 break;
660 }
661 }
662
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800663 // 2) If we found hooks, run the pre/post-install.
664 if (has_hooks) {
665 Status install_status = (*call)(apexes);
666 if (!install_status.Ok()) {
667 return install_status;
Andreas Gampe17739142019-01-09 16:00:26 -0800668 }
669 }
670
671 return Status::Success();
672}
673
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800674Status PreinstallPackages(const std::vector<ApexFile>& apexes) {
675 return PrePostinstallPackages(apexes, &ApexManifest::preinstallhook,
676 &StagePreInstall);
677}
678
679Status PostinstallPackages(const std::vector<ApexFile>& apexes) {
680 return PrePostinstallPackages(apexes, &ApexManifest::postinstallhook,
681 &StagePostInstall);
682}
683
684template <typename RetType, typename Fn>
685RetType HandlePackages(const std::vector<std::string>& paths, Fn fn) {
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800686 // 1) Open all APEXes.
687 std::vector<ApexFile> apex_files;
688 for (const std::string& path : paths) {
689 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
690 if (!apex_file.Ok()) {
Andreas Gampe7288cca2019-01-15 13:10:34 -0800691 return RetType::Fail(apex_file.ErrorMessage());
Andreas Gampef4c7e7c2019-01-14 12:33:34 -0800692 }
693 apex_files.emplace_back(std::move(*apex_file));
694 }
695
696 // 2) Dispatch.
697 return fn(apex_files);
698}
699
Nikita Ioffe4db13a52019-03-14 23:26:08 +0000700Status ValidateStagingShimApex(const ApexFile& to) {
Nikita Ioffe426dbc42019-04-11 16:09:48 +0100701 using android::base::StringPrintf;
702 auto system_shim = ApexFile::Open(
703 StringPrintf("%s/%s", kApexPackageSystemDir, shim::kSystemShimApexName));
704 if (!system_shim.Ok()) {
705 return system_shim.ErrorStatus();
Nikita Ioffe4db13a52019-03-14 23:26:08 +0000706 }
Nikita Ioffe426dbc42019-04-11 16:09:48 +0100707 auto verify_fn = [&](const std::string& system_apex_path) {
708 return shim::ValidateUpdate(system_apex_path, to.GetPath());
709 };
710 return RunVerifyFnInsideTempMount(*system_shim, verify_fn);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000711}
712
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100713// A version of apex verification that happens during boot.
714// This function should only verification checks that are necessary to run on
715// each boot. Try to avoid putting expensive checks inside this function.
716Status VerifyPackageBoot(const ApexFile& apex_file) {
Nikita Ioffe891723c2019-03-25 14:35:39 +0000717 if (apex_file.IsFlattened()) {
718 return Status::Fail("Can't upgrade flattened apex");
719 }
Jiyong Park5e810232019-04-01 15:24:26 +0900720 StatusOr<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
Nikita Ioffe891723c2019-03-25 14:35:39 +0000721 if (!verity_or.Ok()) {
722 return Status::Fail(verity_or.ErrorMessage());
723 }
724
725 if (shim::IsShimApex(apex_file)) {
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100726 // Validating shim is not a very cheap operation, but it's fine to perform
727 // it here since it only runs during CTS tests and will never be triggered
728 // during normal flow.
729 const auto& status = ValidateStagingShimApex(apex_file);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000730 if (!status.Ok()) {
731 return status;
732 }
733 }
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100734 return Status::Success();
735}
Nikita Ioffe891723c2019-03-25 14:35:39 +0000736
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100737// A version of apex verification that happens on submitStagedSession.
738// This function contains checks that might be expensive to perform, e.g. temp
739// mounting a package and reading entire dm-verity device, and shouldn't be run
740// during boot.
741Status VerifyPackageInstall(const ApexFile& apex_file) {
742 const auto& verify_package_boot_status = VerifyPackageBoot(apex_file);
743 if (!verify_package_boot_status.Ok()) {
744 return verify_package_boot_status;
745 }
Nikita Ioffe2d94d8d2019-04-16 15:11:26 +0100746 if (!kUpdatable) {
747 return Status::Fail(StringLog() << "Attempted to upgrade apex package "
748 << apex_file.GetPath()
749 << " on a device that doesn't support it");
750 }
751 StatusOr<ApexVerityData> verity_or = apex_file.VerifyApexVerity();
Nikita Ioffe891723c2019-03-25 14:35:39 +0000752
Nikita Ioffe426dbc42019-04-11 16:09:48 +0100753 constexpr const auto kSuccessFn = [](const std::string& _) {
754 return Status::Success();
755 };
756 return RunVerifyFnInsideTempMount(apex_file, kSuccessFn);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000757}
758
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100759template <typename VerifyApexFn>
Andreas Gampe7288cca2019-01-15 13:10:34 -0800760StatusOr<std::vector<ApexFile>> verifyPackages(
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100761 const std::vector<std::string>& paths, const VerifyApexFn& verify_apex_fn) {
Andreas Gampe7288cca2019-01-15 13:10:34 -0800762 if (paths.empty()) {
763 return StatusOr<std::vector<ApexFile>>::MakeError("Empty set of inputs");
764 }
765 LOG(DEBUG) << "verifyPackages() for " << Join(paths, ',');
766
767 using StatusT = StatusOr<std::vector<ApexFile>>;
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100768 auto verify_fn = [&](std::vector<ApexFile>& apexes) {
Andreas Gampe7288cca2019-01-15 13:10:34 -0800769 for (const ApexFile& apex_file : apexes) {
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100770 Status status = verify_apex_fn(apex_file);
Nikita Ioffe891723c2019-03-25 14:35:39 +0000771 if (!status.Ok()) {
772 return StatusT::MakeError(status);
Nikita Ioffe4db13a52019-03-14 23:26:08 +0000773 }
Andreas Gampe7288cca2019-01-15 13:10:34 -0800774 }
775 return StatusT(std::move(apexes));
776 };
777 return HandlePackages<StatusT>(paths, verify_fn);
778}
779
Dario Freni6dd4dd62019-01-18 12:45:44 +0000780StatusOr<ApexFile> verifySessionDir(const int session_id) {
Andreas Gampe7288cca2019-01-15 13:10:34 -0800781 std::string sessionDirPath = std::string(kStagedSessionsDir) + "/session_" +
782 std::to_string(session_id);
783 LOG(INFO) << "Scanning " << sessionDirPath
784 << " looking for packages to be validated";
785 StatusOr<std::vector<std::string>> scan =
786 FindApexFilesByName(sessionDirPath, /* include_dirs=*/false);
787 if (!scan.Ok()) {
788 LOG(WARNING) << scan.ErrorMessage();
Dario Freni6dd4dd62019-01-18 12:45:44 +0000789 return StatusOr<ApexFile>::MakeError(scan.ErrorMessage());
Andreas Gampe7288cca2019-01-15 13:10:34 -0800790 }
791
Andreas Gampe7288cca2019-01-15 13:10:34 -0800792 if (scan->size() > 1) {
Dario Freni6dd4dd62019-01-18 12:45:44 +0000793 return StatusOr<ApexFile>::MakeError(
Andreas Gampe7288cca2019-01-15 13:10:34 -0800794 "More than one APEX package found in the same session directory.");
795 }
796
Nikita Ioffe54e24cd2019-04-05 17:04:11 +0100797 auto verified = verifyPackages(*scan, VerifyPackageInstall);
Dario Freni6dd4dd62019-01-18 12:45:44 +0000798 if (!verified.Ok()) {
799 return StatusOr<ApexFile>::MakeError(verified.ErrorStatus());
800 }
801 return StatusOr<ApexFile>(std::move((*verified)[0]));
Andreas Gampe7288cca2019-01-15 13:10:34 -0800802}
803
Nikita Ioffe9bd28b92019-03-04 22:18:07 +0000804Status ClearSessions() {
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +0000805 auto sessions = ApexSession::GetSessions();
806 int cnt = 0;
807 for (ApexSession& session : sessions) {
Nikita Ioffe9bd28b92019-03-04 22:18:07 +0000808 Status status = session.DeleteSession();
809 if (!status.Ok()) {
810 return status;
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +0000811 }
Nikita Ioffe9bd28b92019-03-04 22:18:07 +0000812 cnt++;
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +0000813 }
Dario Freni3f5bedf2019-02-18 11:01:12 +0000814 if (cnt > 0) {
Nikita Ioffe9bd28b92019-03-04 22:18:07 +0000815 LOG(DEBUG) << "Deleted " << cnt << " sessions";
Dario Freni3f5bedf2019-02-18 11:01:12 +0000816 }
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +0000817 return Status::Success();
818}
819
Nikita Ioffea4dc3e82019-02-23 17:37:04 +0000820Status DeleteBackup() {
821 auto exists = PathExists(std::string(kApexBackupDir));
822 if (!exists.Ok()) {
823 return Status::Fail(StringLog() << "Can't clean " << kApexBackupDir << " : "
824 << exists.ErrorMessage());
825 }
826 if (!*exists) {
827 LOG(DEBUG) << kApexBackupDir << " does not exist. Nothing to clean";
828 return Status::Success();
829 }
830 return DeleteDirContent(std::string(kApexBackupDir));
831}
832
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000833Status BackupActivePackages() {
834 LOG(DEBUG) << "Initializing backup of " << kActiveApexPackagesDataDir;
835
836 // Previous restore might've delete backups folder.
837 auto create_status = createDirIfNeeded(kApexBackupDir, 0700);
838 if (!create_status.Ok()) {
839 return Status::Fail(StringLog()
840 << "Backup failed : " << create_status.ErrorMessage());
841 }
842
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000843 auto apex_active_exists = PathExists(std::string(kActiveApexPackagesDataDir));
844 if (!apex_active_exists.Ok()) {
845 return Status::Fail("Backup failed : " + apex_active_exists.ErrorMessage());
846 }
847 if (!*apex_active_exists) {
848 LOG(DEBUG) << kActiveApexPackagesDataDir
849 << " does not exist. Nothing to backup";
850 return Status::Success();
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000851 }
852
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000853 auto active_packages =
854 FindApexFilesByName(kActiveApexPackagesDataDir, false /* include_dirs */);
855 if (!active_packages.Ok()) {
856 return Status::Fail(StringLog() << "Backup failed : "
857 << active_packages.ErrorMessage());
858 }
859
Nikita Ioffea4dc3e82019-02-23 17:37:04 +0000860 auto cleanup_status = DeleteBackup();
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000861 if (!cleanup_status.Ok()) {
862 return Status::Fail(StringLog()
863 << "Backup failed : " << cleanup_status.ErrorMessage());
864 }
865
866 auto backup_path_fn = [](const ApexFile& apex_file) {
867 return StringPrintf("%s/%s%s", kApexBackupDir,
868 GetPackageId(apex_file.GetManifest()).c_str(),
869 kApexPackageSuffix);
870 };
871
872 auto deleter = []() {
873 auto status = DeleteDirContent(std::string(kApexBackupDir));
874 if (!status.Ok()) {
875 LOG(ERROR) << "Failed to cleanup " << kApexBackupDir << " : "
876 << status.ErrorMessage();
877 }
878 };
879 auto scope_guard = android::base::make_scope_guard(deleter);
880
881 for (const std::string& path : *active_packages) {
882 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
883 if (!apex_file.Ok()) {
884 return Status::Fail("Backup failed : " + apex_file.ErrorMessage());
885 }
886 const auto& dest_path = backup_path_fn(*apex_file);
887 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
888 return Status::Fail(PStringLog()
889 << "Failed to backup " << apex_file->GetPath());
890 }
891 }
892
893 scope_guard.Disable(); // Accept the backup.
894 return Status::Success();
895}
896
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100897Status DoRollback(ApexSession& session) {
Martijn Coenen44de00c2019-03-22 09:13:17 +0100898 if (gInFsCheckpointMode) {
899 // We will roll back automatically when we reboot
900 return Status::Success();
901 }
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100902 auto scope_guard = android::base::make_scope_guard([&]() {
903 auto st = session.UpdateStateAndCommit(SessionState::ROLLBACK_FAILED);
904 LOG(DEBUG) << "Marking " << session << " as failed to rollback";
905 if (!st.Ok()) {
906 LOG(WARNING) << "Failed to mark session " << session
907 << " as failed to rollback : " << st.ErrorMessage();
908 }
909 });
910
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000911 auto backup_exists = PathExists(std::string(kApexBackupDir));
912 if (!backup_exists.Ok()) {
913 return backup_exists.ErrorStatus();
914 }
915 if (!*backup_exists) {
916 return Status::Fail(StringLog() << kApexBackupDir << " does not exist");
917 }
918
919 struct stat stat_data;
920 if (stat(kActiveApexPackagesDataDir, &stat_data) != 0) {
921 return Status::Fail(PStringLog()
922 << "Failed to access " << kActiveApexPackagesDataDir);
923 }
924
925 LOG(DEBUG) << "Deleting existing packages in " << kActiveApexPackagesDataDir;
926 auto delete_status =
927 DeleteDirContent(std::string(kActiveApexPackagesDataDir));
928 if (!delete_status.Ok()) {
929 return delete_status;
930 }
931
932 LOG(DEBUG) << "Renaming " << kApexBackupDir << " to "
933 << kActiveApexPackagesDataDir;
934 if (rename(kApexBackupDir, kActiveApexPackagesDataDir) != 0) {
935 return Status::Fail(PStringLog() << "Failed to rename " << kApexBackupDir
936 << " to " << kActiveApexPackagesDataDir);
937 }
938
939 LOG(DEBUG) << "Restoring original permissions for "
940 << kActiveApexPackagesDataDir;
941 if (chmod(kActiveApexPackagesDataDir, stat_data.st_mode & ALLPERMS) != 0) {
942 // TODO: should we wipe out /data/apex/active if chmod fails?
943 return Status::Fail(PStringLog()
944 << "Failed to restore original permissions for "
945 << kActiveApexPackagesDataDir);
946 }
947
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100948 scope_guard.Disable(); // Rollback succeeded. Accept state.
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000949 return Status::Success();
950}
951
Martijn Coenen44de00c2019-03-22 09:13:17 +0100952Status RollbackStagedSession(ApexSession& session) {
953 // If the session is staged, it hasn't been activated yet, and we just need
954 // to update its state to prevent it from being activated later.
955 return session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
956}
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000957
Martijn Coenen44de00c2019-03-22 09:13:17 +0100958Status RollbackActivatedSession(ApexSession& session) {
959 if (gInFsCheckpointMode) {
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100960 LOG(DEBUG) << "Checkpoint mode is enabled";
Martijn Coenen44de00c2019-03-22 09:13:17 +0100961 // On checkpointing devices, our modifications on /data will be
962 // automatically rolled back when we abort changes. Updating the session
963 // state is pointless here, as it will be rolled back as well.
964 return Status::Success();
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000965 }
966
967 auto status =
968 session.UpdateStateAndCommit(SessionState::ROLLBACK_IN_PROGRESS);
969 if (!status.Ok()) {
970 // TODO: should we continue with a rollback?
971 return Status::Fail(StringLog() << "Rollback of session " << session
972 << " failed : " << status.ErrorMessage());
973 }
974
Nikita Ioffe6f87d542019-04-09 13:09:26 +0100975 status = DoRollback(session);
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000976 if (!status.Ok()) {
977 return Status::Fail(StringLog() << "Rollback of session " << session
978 << " failed : " << status.ErrorMessage());
979 }
980
981 status = session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
982 if (!status.Ok()) {
983 LOG(WARNING) << "Failed to mark session " << session
984 << " as rolled back : " << status.ErrorMessage();
985 }
986
987 return Status::Success();
988}
989
Martijn Coenen44de00c2019-03-22 09:13:17 +0100990Status RollbackSession(ApexSession& session) {
991 LOG(DEBUG) << "Initializing rollback of " << session;
992
993 switch (session.GetState()) {
994 case SessionState::ROLLBACK_IN_PROGRESS:
995 [[clang::fallthrough]];
996 case SessionState::ROLLED_BACK:
997 return Status::Success();
998 case SessionState::STAGED:
999 return RollbackStagedSession(session);
1000 case SessionState::ACTIVATED:
1001 return RollbackActivatedSession(session);
1002 default:
1003 return Status::Fail(StringLog() << "Can't restore session " << session
1004 << " : session is in a wrong state");
1005 }
1006}
1007
Nikita Ioffe2f6936c2019-02-26 20:03:32 +00001008Status ResumeRollback(ApexSession& session) {
1009 auto backup_exists = PathExists(std::string(kApexBackupDir));
1010 if (!backup_exists.Ok()) {
1011 return backup_exists.ErrorStatus();
1012 }
1013 if (*backup_exists) {
Nikita Ioffe6f87d542019-04-09 13:09:26 +01001014 auto rollback_status = DoRollback(session);
Nikita Ioffe2f6936c2019-02-26 20:03:32 +00001015 if (!rollback_status.Ok()) {
1016 return rollback_status;
1017 }
1018 }
1019 auto status = session.UpdateStateAndCommit(SessionState::ROLLED_BACK);
1020 if (!status.Ok()) {
1021 LOG(WARNING) << "Failed to mark session " << session
1022 << " as rolled back : " << status.ErrorMessage();
1023 }
1024 return Status::Success();
1025}
1026
Andreas Gampe70649452019-03-26 10:54:25 -07001027Status UnmountPackage(const ApexFile& apex, bool allow_latest) {
1028 LOG(VERBOSE) << "Unmounting " << GetPackageId(apex.GetManifest());
1029
1030 const ApexManifest& manifest = apex.GetManifest();
1031
Nikita Ioffec7c27842019-03-26 19:35:15 +00001032 std::optional<MountedApexData> data;
Andreas Gampe70649452019-03-26 10:54:25 -07001033 bool latest = false;
1034
Nikita Ioffec7c27842019-03-26 19:35:15 +00001035 auto fn = [&](const MountedApexData& d, bool l) {
1036 if (d.full_path == apex.GetPath()) {
1037 data.emplace(d);
1038 latest = l;
1039 }
1040 };
1041 gMountedApexes.ForallMountedApexes(manifest.name(), fn);
Andreas Gampe70649452019-03-26 10:54:25 -07001042
Nikita Ioffec7c27842019-03-26 19:35:15 +00001043 if (!data.has_value()) {
Andreas Gampe70649452019-03-26 10:54:25 -07001044 return Status::Fail(StringLog() << "Did not find " << apex.GetPath());
1045 }
1046
1047 if (latest) {
1048 if (!allow_latest) {
1049 return Status::Fail(StringLog()
1050 << "Package " << apex.GetPath() << " is active");
1051 }
1052 std::string mount_point = apexd_private::GetActiveMountPoint(manifest);
1053 LOG(VERBOSE) << "Unmounting and deleting " << mount_point;
1054 if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0) {
1055 return Status::Fail(PStringLog() << "Failed to unmount " << mount_point);
1056 }
1057 if (rmdir(mount_point.c_str()) != 0) {
1058 PLOG(ERROR) << "Could not rmdir " << mount_point;
1059 // Continue here.
1060 }
1061 }
1062
Andreas Gampe70649452019-03-26 10:54:25 -07001063 // Clean up gMountedApexes now, even though we're not fully done.
Andreas Gampe70649452019-03-26 10:54:25 -07001064 gMountedApexes.RemoveMountedApex(manifest.name(), apex.GetPath());
Nikita Ioffec7c27842019-03-26 19:35:15 +00001065 return Unmount(*data);
Andreas Gampe70649452019-03-26 10:54:25 -07001066}
Andreas Gampe27adafa2018-11-29 12:20:08 -08001067
Nikita Ioffe31557332019-03-20 16:15:02 +00001068} // namespace
1069
1070namespace apexd_private {
1071
1072Status MountPackage(const ApexFile& apex, const std::string& mountPoint) {
Nikita Ioffe891723c2019-03-25 14:35:39 +00001073 auto ret =
Martijn Coenen2caabb42019-04-02 11:42:02 +02001074 MountPackageImpl(apex, mountPoint, GetPackageId(apex.GetManifest()),
1075 /* verifyImage = */ false);
Nikita Ioffe31557332019-03-20 16:15:02 +00001076 if (!ret.Ok()) {
Nikita Ioffe31557332019-03-20 16:15:02 +00001077 return ret.ErrorStatus();
Andreas Gampe27adafa2018-11-29 12:20:08 -08001078 }
1079
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001080 gMountedApexes.AddMountedApex(apex.GetManifest().name(), false,
Nikita Ioffe31557332019-03-20 16:15:02 +00001081 std::move(*ret));
1082 return Status::Success();
1083}
1084
Andreas Gampe4510d492018-12-12 15:56:05 -08001085Status UnmountPackage(const ApexFile& apex) {
Andreas Gampe70649452019-03-26 10:54:25 -07001086 return android::apex::UnmountPackage(apex, /* allow_latest= */ false);
Andreas Gampe4510d492018-12-12 15:56:05 -08001087}
1088
Andreas Gampe27adafa2018-11-29 12:20:08 -08001089bool IsMounted(const std::string& name, const std::string& full_path) {
1090 bool found_mounted = false;
1091 gMountedApexes.ForallMountedApexes(
1092 name, [&](const MountedApexData& data, bool latest ATTRIBUTE_UNUSED) {
1093 if (full_path == data.full_path) {
1094 found_mounted = true;
1095 }
1096 });
1097 return found_mounted;
1098}
1099
1100std::string GetPackageMountPoint(const ApexManifest& manifest) {
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001101 return StringPrintf("%s/%s", kApexRoot, GetPackageId(manifest).c_str());
Andreas Gampe27adafa2018-11-29 12:20:08 -08001102}
1103
1104std::string GetActiveMountPoint(const ApexManifest& manifest) {
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001105 return StringPrintf("%s/%s", kApexRoot, manifest.name().c_str());
Andreas Gampe27adafa2018-11-29 12:20:08 -08001106}
1107
1108} // namespace apexd_private
1109
Nikita Ioffe2f6936c2019-02-26 20:03:32 +00001110Status resumeRollbackIfNeeded() {
1111 auto session = ApexSession::GetActiveSession();
1112 if (!session.Ok()) {
1113 return session.ErrorStatus();
1114 }
1115 if (!session->has_value()) {
1116 return Status::Success();
1117 }
1118 if ((**session).GetState() == SessionState::ROLLBACK_IN_PROGRESS) {
1119 // This means that phone was rebooted during the rollback. Resuming it.
1120 return ResumeRollback(**session);
1121 }
1122 return Status::Success();
1123}
1124
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001125Status activatePackageImpl(const ApexFile& apex_file) {
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001126 const ApexManifest& manifest = apex_file.GetManifest();
Andreas Gampe3de3e562018-11-14 08:42:48 -08001127
Jooyung Han40531a82019-04-05 15:34:13 +09001128 if (gBootstrap && !isBootstrapApex(apex_file)) {
Jiyong Parkef34c142019-02-25 01:13:18 +09001129 LOG(INFO) << "Skipped when bootstrapping";
1130 return Status::Success();
Nikita Ioffe2d94d8d2019-04-16 15:11:26 +01001131 } else if (!kUpdatable && !gBootstrap && isBootstrapApex(apex_file)) {
Luca Stefaniddcef062019-03-13 19:26:19 +01001132 LOG(INFO) << "Package already activated in bootstrap";
1133 return Status::Success();
Jiyong Parkef34c142019-02-25 01:13:18 +09001134 }
1135
Andreas Gampe3de3e562018-11-14 08:42:48 -08001136 // See whether we think it's active, and do not allow to activate the same
1137 // version. Also detect whether this is the highest version.
1138 // We roll this into a single check.
1139 bool is_newest_version = true;
1140 bool found_other_version = false;
Andreas Gampe4d102032018-11-19 16:18:33 -08001141 bool version_found_mounted = false;
Andreas Gampe3de3e562018-11-14 08:42:48 -08001142 {
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001143 uint64_t new_version = manifest.version();
Andreas Gampe4d102032018-11-19 16:18:33 -08001144 bool version_found_active = false;
Andreas Gampe3de3e562018-11-14 08:42:48 -08001145 gMountedApexes.ForallMountedApexes(
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001146 manifest.name(), [&](const MountedApexData& data, bool latest) {
Jiyong Park69c0f112018-11-22 20:38:05 +09001147 StatusOr<ApexFile> otherApex = ApexFile::Open(data.full_path);
1148 if (!otherApex.Ok()) {
Andreas Gampe3de3e562018-11-14 08:42:48 -08001149 return;
1150 }
1151 found_other_version = true;
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001152 if (static_cast<uint64_t>(otherApex->GetManifest().version()) ==
1153 new_version) {
Andreas Gampe4d102032018-11-19 16:18:33 -08001154 version_found_mounted = true;
1155 version_found_active = latest;
Andreas Gampe3de3e562018-11-14 08:42:48 -08001156 }
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001157 if (static_cast<uint64_t>(otherApex->GetManifest().version()) >
1158 new_version) {
Andreas Gampe3de3e562018-11-14 08:42:48 -08001159 is_newest_version = false;
1160 }
1161 });
Andreas Gampe4d102032018-11-19 16:18:33 -08001162 if (version_found_active) {
Nikita Ioffeb3527052019-02-21 17:36:42 +00001163 LOG(DEBUG) << "Package " << manifest.name() << " with version "
1164 << manifest.version() << " already active";
1165 return Status::Success();
Andreas Gampe3de3e562018-11-14 08:42:48 -08001166 }
1167 }
1168
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001169 const std::string& mountPoint = apexd_private::GetPackageMountPoint(manifest);
Andreas Gampe3de3e562018-11-14 08:42:48 -08001170
Andreas Gampe4d102032018-11-19 16:18:33 -08001171 if (!version_found_mounted) {
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001172 Status mountStatus = apexd_private::MountPackage(apex_file, mountPoint);
Andreas Gampe4d102032018-11-19 16:18:33 -08001173 if (!mountStatus.Ok()) {
1174 return mountStatus;
Andreas Gampe3de3e562018-11-14 08:42:48 -08001175 }
Andreas Gampe3de3e562018-11-14 08:42:48 -08001176 }
1177
Andreas Gampe4d102032018-11-19 16:18:33 -08001178 bool mounted_latest = false;
1179 if (is_newest_version) {
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001180 const Status& update_st = apexd_private::BindMount(
Andreas Gampe27adafa2018-11-29 12:20:08 -08001181 apexd_private::GetActiveMountPoint(manifest), mountPoint);
Andreas Gampe4d102032018-11-19 16:18:33 -08001182 mounted_latest = update_st.Ok();
1183 if (!update_st.Ok()) {
Nikita Ioffeb3527052019-02-21 17:36:42 +00001184 return Status::Fail(StringLog()
1185 << "Failed to update package " << manifest.name()
1186 << " to version " << manifest.version() << " : "
1187 << update_st.ErrorMessage());
Andreas Gampe4d102032018-11-19 16:18:33 -08001188 }
1189 }
Andreas Gampedeece7e2018-12-13 11:24:51 -08001190 if (mounted_latest) {
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001191 gMountedApexes.SetLatest(manifest.name(), apex_file.GetPath());
Andreas Gampe4d102032018-11-19 16:18:33 -08001192 }
Andreas Gampe4d102032018-11-19 16:18:33 -08001193
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001194 LOG(DEBUG) << "Successfully activated " << apex_file.GetPath()
Nikita Ioffeb3527052019-02-21 17:36:42 +00001195 << " package_name: " << manifest.name()
1196 << " version: " << manifest.version();
Andreas Gampe4d102032018-11-19 16:18:33 -08001197 return Status::Success();
Andreas Gampe3de3e562018-11-14 08:42:48 -08001198}
1199
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001200Status activatePackage(const std::string& full_path) {
1201 LOG(INFO) << "Trying to activate " << full_path;
1202
1203 StatusOr<ApexFile> apex_file = ApexFile::Open(full_path);
1204 if (!apex_file.Ok()) {
1205 return apex_file.ErrorStatus();
1206 }
1207 return activatePackageImpl(*apex_file);
1208}
1209
Andreas Gampe3de3e562018-11-14 08:42:48 -08001210Status deactivatePackage(const std::string& full_path) {
1211 LOG(INFO) << "Trying to deactivate " << full_path;
1212
Jiyong Park69c0f112018-11-22 20:38:05 +09001213 StatusOr<ApexFile> apexFile = ApexFile::Open(full_path);
1214 if (!apexFile.Ok()) {
1215 return apexFile.ErrorStatus();
Andreas Gampe3de3e562018-11-14 08:42:48 -08001216 }
Andreas Gampe3de3e562018-11-14 08:42:48 -08001217
Andreas Gampe70649452019-03-26 10:54:25 -07001218 return UnmountPackage(*apexFile, /* allow_latest= */ true);
Andreas Gampe3de3e562018-11-14 08:42:48 -08001219}
1220
Narayan Kamath5ea57782019-01-03 18:17:05 +00001221std::vector<ApexFile> getActivePackages() {
1222 std::vector<ApexFile> ret;
1223 gMountedApexes.ForallMountedApexes(
1224 [&](const std::string&, const MountedApexData& data, bool latest) {
1225 if (!latest) {
1226 return;
1227 }
Andreas Gampe0fa59af2018-11-16 11:12:11 -08001228
Narayan Kamath5ea57782019-01-03 18:17:05 +00001229 StatusOr<ApexFile> apexFile = ApexFile::Open(data.full_path);
1230 if (!apexFile.Ok()) {
1231 // TODO: Fail?
1232 return;
1233 }
Narayan Kamath5ea57782019-01-03 18:17:05 +00001234 ret.emplace_back(std::move(*apexFile));
1235 });
Andreas Gampe0fa59af2018-11-16 11:12:11 -08001236
1237 return ret;
1238}
1239
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001240namespace {
1241std::unordered_map<std::string, uint64_t> GetActivePackagesMap() {
1242 std::vector<ApexFile> active_packages = getActivePackages();
1243 std::unordered_map<std::string, uint64_t> ret;
1244 for (const auto& package : active_packages) {
1245 const ApexManifest& manifest = package.GetManifest();
1246 ret.insert({manifest.name(), manifest.version()});
1247 }
1248 return ret;
1249}
1250
1251} // namespace
1252
Gavin Corkery729905c2019-03-21 11:52:06 +00001253std::vector<ApexFile> getFactoryPackages() {
1254 std::vector<ApexFile> ret;
Nikita Ioffee44c58a2019-04-29 14:07:11 +01001255 for (const auto& dir : kApexPackageBuiltinDirs) {
1256 auto apex_files = FindApexFilesByName(dir, /* include_dirs=*/false);
1257 if (!apex_files.Ok()) {
1258 LOG(ERROR) << apex_files.ErrorMessage();
1259 continue;
Gavin Corkery729905c2019-03-21 11:52:06 +00001260 }
Nikita Ioffee44c58a2019-04-29 14:07:11 +01001261 for (const std::string& path : *apex_files) {
Gavin Corkery729905c2019-03-21 11:52:06 +00001262 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
1263 if (!apex_file.Ok()) {
1264 LOG(ERROR) << apex_file.ErrorMessage();
1265 } else {
1266 ret.emplace_back(std::move(*apex_file));
1267 }
1268 }
1269 }
1270 return ret;
1271}
1272
Narayan Kamath5ea57782019-01-03 18:17:05 +00001273StatusOr<ApexFile> getActivePackage(const std::string& packageName) {
1274 std::vector<ApexFile> packages = getActivePackages();
1275 for (ApexFile& apex : packages) {
Abhijeet Kaur216e36c2019-01-04 10:15:01 +00001276 if (apex.GetManifest().name() == packageName) {
Narayan Kamath5ea57782019-01-03 18:17:05 +00001277 return StatusOr<ApexFile>(std::move(apex));
1278 }
1279 }
1280
1281 return StatusOr<ApexFile>::MakeError(
1282 PStringLog() << "Cannot find matching package for: " << packageName);
1283}
1284
Nikita Ioffe463d4e82019-02-10 18:46:20 +00001285Status abortActiveSession() {
1286 auto session_or_none = ApexSession::GetActiveSession();
1287 if (!session_or_none.Ok()) {
1288 return session_or_none.ErrorStatus();
1289 }
1290 if (session_or_none->has_value()) {
Nikita Ioffe9ae986a2019-02-18 22:39:27 +00001291 auto& session = session_or_none->value();
Nikita Ioffe463d4e82019-02-10 18:46:20 +00001292 LOG(DEBUG) << "Aborting active session " << session;
1293 switch (session.GetState()) {
1294 case SessionState::VERIFIED:
1295 [[clang::fallthrough]];
1296 case SessionState::STAGED:
1297 return session.DeleteSession();
Nikita Ioffe9ae986a2019-02-18 22:39:27 +00001298 case SessionState::ACTIVATED:
Martijn Coenen44de00c2019-03-22 09:13:17 +01001299 return RollbackActivatedSession(session);
Nikita Ioffe463d4e82019-02-10 18:46:20 +00001300 default:
1301 return Status::Fail(StringLog()
1302 << "Session " << session << " can't be aborted");
1303 }
1304 } else {
1305 LOG(DEBUG) << "There are no active sessions";
1306 return Status::Success();
1307 }
1308}
1309
Nikita Ioffeb3527052019-02-21 17:36:42 +00001310Status scanPackagesDirAndActivate(const char* apex_package_dir) {
Roland Levillaine057c1e2018-10-09 11:14:39 +01001311 LOG(INFO) << "Scanning " << apex_package_dir << " looking for APEX packages.";
Dario Freni3ff2c652018-08-10 19:55:32 +01001312
Jiyong Parkf94de552019-03-19 16:13:32 +09001313 const bool scanBuiltinApexes = isPathForBuiltinApexes(apex_package_dir);
Andreas Gampe45557712019-01-09 10:51:04 -08001314 StatusOr<std::vector<std::string>> scan =
Jiyong Parkf94de552019-03-19 16:13:32 +09001315 FindApexFilesByName(apex_package_dir, scanBuiltinApexes);
Andreas Gampe45557712019-01-09 10:51:04 -08001316 if (!scan.Ok()) {
Nikita Ioffeb3527052019-02-21 17:36:42 +00001317 return Status::Fail(StringLog() << "Failed to scan " << apex_package_dir
1318 << " : " << scan.ErrorMessage());
Andreas Gampe45557712019-01-09 10:51:04 -08001319 }
Dario Freni3ff2c652018-08-10 19:55:32 +01001320
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001321 const auto& packages_with_code = GetActivePackagesMap();
1322
Nikita Ioffeb3527052019-02-21 17:36:42 +00001323 std::vector<std::string> failed_pkgs;
Nikita Ioffe3d3769c2019-05-08 17:11:53 +00001324 size_t activated_cnt = 0;
1325 size_t skipped_cnt = 0;
Andreas Gampe45557712019-01-09 10:51:04 -08001326 for (const std::string& name : *scan) {
1327 LOG(INFO) << "Found " << name;
1328
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001329 StatusOr<ApexFile> apex_file = ApexFile::Open(name);
1330 if (!apex_file.Ok()) {
1331 LOG(ERROR) << "Failed to activate " << name << " : "
1332 << apex_file.ErrorMessage();
1333 failed_pkgs.push_back(name);
1334 continue;
1335 }
1336
1337 uint64_t new_version =
1338 static_cast<uint64_t>(apex_file->GetManifest().version());
1339 const auto& it = packages_with_code.find(apex_file->GetManifest().name());
1340 if (it != packages_with_code.end() && it->second >= new_version) {
1341 LOG(INFO) << "Skipping activation of " << name
1342 << " same package with higher version " << it->second
1343 << " is already active";
Nikita Ioffe3d3769c2019-05-08 17:11:53 +00001344 skipped_cnt++;
1345 continue;
1346 }
1347
1348 if (!kUpdatable && !apex_file->IsFlattened()) {
1349 LOG(INFO) << "Skipping activation of non-flattened apex package " << name
1350 << " because device doesn't support it";
1351 skipped_cnt++;
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001352 continue;
1353 }
1354
1355 Status res = activatePackageImpl(*apex_file);
Andreas Gampe45557712019-01-09 10:51:04 -08001356 if (!res.Ok()) {
Nikita Ioffeb3527052019-02-21 17:36:42 +00001357 LOG(ERROR) << "Failed to activate " << name << " : "
1358 << res.ErrorMessage();
1359 failed_pkgs.push_back(name);
Nikita Ioffe3d3769c2019-05-08 17:11:53 +00001360 } else {
1361 activated_cnt++;
Andreas Gampe8eb187a2018-10-19 21:18:03 -07001362 }
Dario Freni3ff2c652018-08-10 19:55:32 +01001363 }
Nikita Ioffeb3527052019-02-21 17:36:42 +00001364
1365 if (!failed_pkgs.empty()) {
1366 return Status::Fail(StringLog()
1367 << "Failed to activate following packages : "
1368 << Join(failed_pkgs, ','));
1369 }
1370
Nikita Ioffe3d3769c2019-05-08 17:11:53 +00001371 LOG(INFO) << "Activated " << activated_cnt
1372 << " packages. Skipped: " << skipped_cnt;
Nikita Ioffeb3527052019-02-21 17:36:42 +00001373 return Status::Success();
Dario Freni3ff2c652018-08-10 19:55:32 +01001374}
Andreas Gampe9d016d52018-10-19 18:56:50 -07001375
Dario Freni7f0da582019-01-06 17:54:13 +00001376void scanStagedSessionsDirAndStage() {
Gavin Corkerya41373a2019-09-26 12:53:45 +01001377 using android::base::GetProperty;
Martijn Coenenc11b68b2019-01-15 11:28:11 +01001378 LOG(INFO) << "Scanning " << kApexSessionsDir
Dario Freni7f0da582019-01-06 17:54:13 +00001379 << " looking for sessions to be activated.";
Dario Freni7f0da582019-01-06 17:54:13 +00001380
Martijn Coenen610909b2019-01-18 13:49:38 +01001381 auto stagedSessions = ApexSession::GetSessionsInState(SessionState::STAGED);
1382 for (auto& session : stagedSessions) {
1383 auto sessionId = session.GetId();
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001384
1385 auto session_failed_fn = [&]() {
Dario Freni6dd4dd62019-01-18 12:45:44 +00001386 LOG(WARNING) << "Marking session " << sessionId << " as failed.";
Nikita Ioffe8a8a3562019-06-21 01:21:13 +01001387 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATION_FAILED);
1388 if (!st.Ok()) {
1389 LOG(WARNING) << "Failed to mark session " << sessionId
1390 << " as failed : " << st.ErrorMessage();
1391 }
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001392 };
1393 auto scope_guard = android::base::make_scope_guard(session_failed_fn);
1394
Gavin Corkerya41373a2019-09-26 12:53:45 +01001395 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
1396 if (session.GetBuildFingerprint().compare(build_fingerprint) != 0) {
1397 LOG(ERROR) << "APEX build fingerprint has changed";
1398 continue;
1399 }
1400
Dario Freni6dd4dd62019-01-18 12:45:44 +00001401 std::vector<std::string> dirsToScan;
1402 if (session.GetChildSessionIds().empty()) {
1403 dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" +
1404 std::to_string(sessionId));
1405 } else {
1406 for (auto childSessionId : session.GetChildSessionIds()) {
1407 dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" +
1408 std::to_string(childSessionId));
1409 }
1410 }
1411
1412 std::vector<std::string> apexes;
1413 bool scanSuccessful = true;
Nikita Ioffe6bea4e52019-02-10 22:46:05 +00001414 for (const auto& dirToScan : dirsToScan) {
Dario Freni6dd4dd62019-01-18 12:45:44 +00001415 StatusOr<std::vector<std::string>> scan =
1416 FindApexFilesByName(dirToScan, /* include_dirs=*/false);
1417 if (!scan.Ok()) {
1418 LOG(WARNING) << scan.ErrorMessage();
1419 scanSuccessful = false;
1420 break;
1421 }
1422
1423 if (scan->size() > 1) {
1424 LOG(WARNING) << "More than one APEX package found in the same session "
1425 << "directory " << dirToScan << ", skipping activation.";
1426 scanSuccessful = false;
1427 break;
1428 }
1429
1430 if (scan->empty()) {
1431 LOG(WARNING) << "No APEX packages found while scanning " << dirToScan
1432 << " session id: " << sessionId << ".";
1433 scanSuccessful = false;
1434 break;
1435 }
1436 apexes.push_back(std::move((*scan)[0]));
1437 }
1438
1439 if (!scanSuccessful) {
1440 continue;
1441 }
1442
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001443 // Run postinstall, if necessary.
Dario Freni6dd4dd62019-01-18 12:45:44 +00001444 Status postinstall_status = postinstallPackages(apexes);
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001445 if (!postinstall_status.Ok()) {
Martijn Coenen610909b2019-01-18 13:49:38 +01001446 LOG(ERROR) << "Postinstall failed for session "
1447 << std::to_string(sessionId) << ": "
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001448 << postinstall_status.ErrorMessage();
1449 continue;
1450 }
Andreas Gampe45557712019-01-09 10:51:04 -08001451
Nikita Ioffe936a9972019-02-13 02:11:21 +00001452 const Status result = stagePackages(apexes);
Andreas Gampe7288cca2019-01-15 13:10:34 -08001453 if (!result.Ok()) {
Dario Freni6dd4dd62019-01-18 12:45:44 +00001454 LOG(ERROR) << "Activation failed for packages " << Join(apexes, ',')
Andreas Gampe7288cca2019-01-15 13:10:34 -08001455 << ": " << result.ErrorMessage();
1456 continue;
Dario Freni7f0da582019-01-06 17:54:13 +00001457 }
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001458
1459 // Session was OK, release scopeguard.
1460 scope_guard.Disable();
Martijn Coenenc11b68b2019-01-15 11:28:11 +01001461
Nikita Ioffe8a8a3562019-06-21 01:21:13 +01001462 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATED);
1463 if (!st.Ok()) {
1464 LOG(ERROR) << "Failed to mark " << session
1465 << " as activated : " << st.ErrorMessage();
1466 }
Dario Freni7f0da582019-01-06 17:54:13 +00001467 }
Dario Freni7f0da582019-01-06 17:54:13 +00001468}
1469
Andreas Gampe0e435302018-12-21 15:40:19 -08001470Status preinstallPackages(const std::vector<std::string>& paths) {
Andreas Gampe7288cca2019-01-15 13:10:34 -08001471 if (paths.empty()) {
1472 return Status::Fail("Empty set of inputs");
1473 }
1474 LOG(DEBUG) << "preinstallPackages() for " << Join(paths, ',');
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001475 return HandlePackages<Status>(paths, PreinstallPackages);
1476}
Andreas Gampe0e435302018-12-21 15:40:19 -08001477
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001478Status postinstallPackages(const std::vector<std::string>& paths) {
Andreas Gampe7288cca2019-01-15 13:10:34 -08001479 if (paths.empty()) {
1480 return Status::Fail("Empty set of inputs");
1481 }
1482 LOG(DEBUG) << "postinstallPackages() for " << Join(paths, ',');
Andreas Gampef4c7e7c2019-01-14 12:33:34 -08001483 return HandlePackages<Status>(paths, PostinstallPackages);
Andreas Gampe0e435302018-12-21 15:40:19 -08001484}
1485
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001486namespace {
1487std::string StageDestPath(const ApexFile& apex_file) {
1488 return StringPrintf("%s/%s%s", kActiveApexPackagesDataDir,
1489 GetPackageId(apex_file.GetManifest()).c_str(),
1490 kApexPackageSuffix);
1491}
1492
1493std::vector<std::string> FilterUnnecessaryStagingPaths(
1494 const std::vector<std::string>& tmp_paths) {
Nikita Ioffea4dad0a2019-03-29 01:00:22 +00001495 const auto& packages_with_code = GetActivePackagesMap();
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001496
1497 auto filter_fn = [&packages_with_code](const std::string& path) {
1498 auto apex_file = ApexFile::Open(path);
1499 if (!apex_file.Ok()) {
1500 // Pretend that apex should be staged, so that stagePackages will fail
1501 // trying to open it.
1502 return true;
1503 }
1504 std::string dest_path = StageDestPath(*apex_file);
1505 if (access(dest_path.c_str(), F_OK) == 0) {
1506 LOG(DEBUG) << dest_path << " already exists. Skipping";
1507 return false;
1508 }
1509 const ApexManifest& manifest = apex_file->GetManifest();
1510 const auto& it = packages_with_code.find(manifest.name());
1511 uint64_t new_version = static_cast<uint64_t>(manifest.version());
1512 if (it != packages_with_code.end() && it->second == new_version) {
1513 LOG(DEBUG) << GetPackageId(manifest) << " is already active. Skipping";
1514 return false;
1515 }
1516 return true;
1517 };
1518
1519 std::vector<std::string> ret;
1520 std::copy_if(tmp_paths.begin(), tmp_paths.end(), std::back_inserter(ret),
1521 filter_fn);
1522 return ret;
1523}
1524
1525} // namespace
1526
Nikita Ioffe936a9972019-02-13 02:11:21 +00001527Status stagePackages(const std::vector<std::string>& tmpPaths) {
Andreas Gampe7288cca2019-01-15 13:10:34 -08001528 if (tmpPaths.empty()) {
1529 return Status::Fail("Empty set of inputs");
1530 }
1531 LOG(DEBUG) << "stagePackages() for " << Join(tmpPaths, ',');
Andreas Gampe0e435302018-12-21 15:40:19 -08001532
1533 // Note: this function is temporary. As such the code is not optimized, e.g.,
1534 // it will open ApexFiles multiple times.
1535
1536 // 1) Verify all packages.
Nikita Ioffe54e24cd2019-04-05 17:04:11 +01001537 auto verify_status = verifyPackages(tmpPaths, VerifyPackageBoot);
Andreas Gampe0e435302018-12-21 15:40:19 -08001538 if (!verify_status.Ok()) {
Dario Freni56231b42019-01-04 11:58:17 +00001539 return Status::Fail(verify_status.ErrorMessage());
Andreas Gampe0e435302018-12-21 15:40:19 -08001540 }
Andreas Gampe6802c612018-12-06 15:43:49 -08001541
Andreas Gampea00c5452018-12-10 13:38:33 -08001542 // 2) Now stage all of them.
1543
Nikita Ioffea8453da2019-01-30 21:29:13 +00001544 // Make sure that kActiveApexPackagesDataDir exists.
1545 auto create_dir_status =
1546 createDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0750);
1547 if (!create_dir_status.Ok()) {
1548 return Status::Fail(create_dir_status.ErrorMessage());
1549 }
1550
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001551 // 2) Filter out packages that do not require staging, e.g.:
1552 // a) Their /data/apex/active/package.apex@version already exists.
1553 // b) Such package is already active
1554 std::vector<std::string> paths_to_stage =
1555 FilterUnnecessaryStagingPaths(tmpPaths);
1556 if (paths_to_stage.empty()) {
1557 // Finish early if nothing to stage. Since stagePackages fails in case
1558 // tmpPaths is empty, it's fine to return Success here.
1559 return Status::Success();
1560 }
1561
1562 // 3) Now stage all of them.
Andreas Gampea00c5452018-12-10 13:38:33 -08001563
Andreas Gampe6802c612018-12-06 15:43:49 -08001564 // Ensure the APEX gets removed on failure.
Nikita Ioffe6a280af2019-02-04 15:28:57 +00001565 std::unordered_set<std::string> staged_files;
1566 auto deleter = [&staged_files]() {
1567 for (const std::string& staged_path : staged_files) {
Andreas Gampe0e435302018-12-21 15:40:19 -08001568 if (TEMP_FAILURE_RETRY(unlink(staged_path.c_str())) != 0) {
1569 PLOG(ERROR) << "Unable to unlink " << staged_path;
Andreas Gampea00c5452018-12-10 13:38:33 -08001570 }
Andreas Gampe6802c612018-12-06 15:43:49 -08001571 }
1572 };
1573 auto scope_guard = android::base::make_scope_guard(deleter);
1574
Nikita Ioffe6a280af2019-02-04 15:28:57 +00001575 std::unordered_set<std::string> staged_packages;
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001576 for (const std::string& path : paths_to_stage) {
Andreas Gampe0e435302018-12-21 15:40:19 -08001577 StatusOr<ApexFile> apex_file = ApexFile::Open(path);
1578 if (!apex_file.Ok()) {
1579 return apex_file.ErrorStatus();
1580 }
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001581 std::string dest_path = StageDestPath(*apex_file);
Andreas Gampe6802c612018-12-06 15:43:49 -08001582
Nikita Ioffe936a9972019-02-13 02:11:21 +00001583 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
1584 // TODO: Get correct binder error status.
1585 return Status::Fail(PStringLog()
1586 << "Unable to link " << apex_file->GetPath() << " to "
1587 << dest_path);
Andreas Gampea00c5452018-12-10 13:38:33 -08001588 }
Nikita Ioffe6a280af2019-02-04 15:28:57 +00001589 staged_files.insert(dest_path);
Nikita Ioffe2d2f8ea2019-03-20 13:28:54 +00001590 staged_packages.insert(apex_file->GetManifest().name());
Andreas Gampea00c5452018-12-10 13:38:33 -08001591
Dario Freni7f0da582019-01-06 17:54:13 +00001592 LOG(DEBUG) << "Success linking " << apex_file->GetPath() << " to "
Andreas Gampea00c5452018-12-10 13:38:33 -08001593 << dest_path;
Andreas Gampea00c5452018-12-10 13:38:33 -08001594 }
1595
Andreas Gampe6802c612018-12-06 15:43:49 -08001596 scope_guard.Disable(); // Accept the state.
Nikita Ioffe6a280af2019-02-04 15:28:57 +00001597
1598 return RemovePreviouslyActiveApexFiles(staged_packages, staged_files);
Andreas Gampeb99abdd2018-10-19 19:59:17 -07001599}
1600
Nikita Ioffe496a4a42019-03-05 16:32:51 +00001601Status unstagePackages(const std::vector<std::string>& paths) {
1602 if (paths.empty()) {
1603 return Status::Fail("Empty set of inputs");
1604 }
1605 LOG(DEBUG) << "unstagePackages() for " << Join(paths, ',');
1606
1607 // TODO: to make unstage safer, we can copy to be unstaged packages to a
1608 // temporary folder and restore state from it in case unstagePackages fails.
1609
1610 for (const std::string& path : paths) {
1611 if (access(path.c_str(), F_OK) != 0) {
1612 return Status::Fail(PStringLog() << "Can't access " << path);
1613 }
1614 }
1615
1616 for (const std::string& path : paths) {
1617 if (unlink(path.c_str()) != 0) {
1618 return Status::Fail(PStringLog() << "Can't unlink " << path);
1619 }
1620 }
1621
1622 return Status::Success();
1623}
1624
Martijn Coenen44de00c2019-03-22 09:13:17 +01001625Status rollbackStagedSessionIfAny() {
1626 auto session = ApexSession::GetActiveSession();
Nikita Ioffe7af550d2019-05-13 14:14:37 +01001627 if (!session.Ok()) {
1628 return session.ErrorStatus();
Martijn Coenen44de00c2019-03-22 09:13:17 +01001629 }
Nikita Ioffe7af550d2019-05-13 14:14:37 +01001630 if (!session->has_value()) {
1631 LOG(WARNING) << "No session to rollback";
1632 return Status::Success();
1633 }
1634 if ((*session)->GetState() == SessionState::STAGED) {
1635 LOG(INFO) << "Rolling back session " << **session;
1636 return RollbackStagedSession(**session);
1637 }
1638 return Status::Fail(StringLog() << "Can't rollback " << **session
1639 << " because it is not in STAGED state");
Martijn Coenen44de00c2019-03-22 09:13:17 +01001640}
1641
1642Status rollbackActiveSession() {
Nikita Ioffe9ae986a2019-02-18 22:39:27 +00001643 auto session = ApexSession::GetActiveSession();
1644 if (!session.Ok()) {
Nikita Ioffe6f87d542019-04-09 13:09:26 +01001645 return Status::Fail(StringLog() << "Failed to get active session : "
1646 << session.ErrorMessage());
Nikita Ioffe9ae986a2019-02-18 22:39:27 +00001647 } else if (!session->has_value()) {
1648 return Status::Fail(
1649 "Rollback requested, when there are no active sessions.");
1650 } else {
1651 return RollbackSession(*(*session));
1652 }
Zimuzo9cc0be42019-01-09 11:37:34 +00001653}
1654
Martijn Coenen44de00c2019-03-22 09:13:17 +01001655Status rollbackActiveSessionAndReboot() {
1656 auto status = rollbackActiveSession();
1657 if (!status.Ok()) {
1658 return status;
1659 }
1660 LOG(ERROR) << "Successfully rolled back. Time to reboot device.";
1661 if (gInFsCheckpointMode) {
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001662 Status res = gVoldService->AbortChanges("apexd_initiated" /* message */,
1663 false /* retry */);
1664 if (!res.Ok()) {
1665 LOG(ERROR) << res.ErrorMessage();
1666 }
Martijn Coenen44de00c2019-03-22 09:13:17 +01001667 }
1668 Reboot();
1669 return Status::Success();
1670}
1671
Jiyong Park715e23d2019-02-22 22:14:37 +09001672int onBootstrap() {
Jiyong Parkef34c142019-02-25 01:13:18 +09001673 gBootstrap = true;
1674
Jooyung Han40531a82019-04-05 15:34:13 +09001675 Status preAllocate = preAllocateLoopDevices();
1676 if (!preAllocate.Ok()) {
1677 LOG(ERROR) << "Failed to pre-allocate loop devices : "
1678 << preAllocate.ErrorMessage();
Jiyong Park4d0f8322019-02-02 19:45:57 +09001679 }
Jiyong Park715e23d2019-02-22 22:14:37 +09001680
Jiyong Park5e810232019-04-01 15:24:26 +09001681 Status status = collectApexKeys();
1682 if (!status.Ok()) {
1683 LOG(ERROR) << "Failed to collect APEX keys : " << status.ErrorMessage();
1684 return 1;
1685 }
1686
Jiyong Park715e23d2019-02-22 22:14:37 +09001687 // Activate built-in APEXes for processes launched before /data is mounted.
Nikita Ioffe8a8a3562019-06-21 01:21:13 +01001688 status = scanPackagesDirAndActivate(kApexPackageSystemDir);
1689 if (!status.Ok()) {
1690 LOG(ERROR) << "Failed to activate APEX files in " << kApexPackageSystemDir
1691 << " : " << status.ErrorMessage();
1692 return 1;
1693 }
Jiyong Park715e23d2019-02-22 22:14:37 +09001694 LOG(INFO) << "Bootstrapping done";
1695 return 0;
1696}
1697
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001698void onStart(CheckpointInterface* checkpoint_service) {
Jiyong Park715e23d2019-02-22 22:14:37 +09001699 LOG(INFO) << "Marking APEXd as starting";
1700 if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusStarting)) {
1701 PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to "
1702 << kApexStatusStarting;
1703 }
1704
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001705 if (checkpoint_service != nullptr) {
1706 gVoldService = checkpoint_service;
1707 StatusOr<bool> supports_fs_checkpoints =
1708 gVoldService->SupportsFsCheckpoints();
1709 if (supports_fs_checkpoints.Ok()) {
1710 gSupportsFsCheckpoints = *supports_fs_checkpoints;
1711 } else {
Martijn Coenen44de00c2019-03-22 09:13:17 +01001712 LOG(ERROR) << "Failed to check if filesystem checkpoints are supported: "
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001713 << supports_fs_checkpoints.ErrorMessage();
Martijn Coenen44de00c2019-03-22 09:13:17 +01001714 }
1715 if (gSupportsFsCheckpoints) {
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001716 StatusOr<bool> needs_checkpoint = gVoldService->NeedsCheckpoint();
1717 if (needs_checkpoint.Ok()) {
1718 gInFsCheckpointMode = *needs_checkpoint;
1719 } else {
Martijn Coenen44de00c2019-03-22 09:13:17 +01001720 LOG(ERROR) << "Failed to check if we're in filesystem checkpoint mode: "
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001721 << needs_checkpoint.ErrorMessage();
Martijn Coenen44de00c2019-03-22 09:13:17 +01001722 }
1723 }
Martijn Coenen44de00c2019-03-22 09:13:17 +01001724 }
1725
1726 // Ask whether we should roll back any staged sessions; this can happen if
1727 // we've exceeded the retry count on a device that supports filesystem
1728 // checkpointing.
1729 if (gSupportsFsCheckpoints) {
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001730 StatusOr<bool> needs_rollback = gVoldService->NeedsRollback();
1731 if (!needs_rollback.Ok()) {
Martijn Coenen44de00c2019-03-22 09:13:17 +01001732 LOG(ERROR) << "Failed to check if we need a rollback: "
Andreas Gampe6aaa2fe2019-03-29 14:13:59 -07001733 << needs_rollback.ErrorMessage();
1734 } else if (*needs_rollback) {
Nikita Ioffe7af550d2019-05-13 14:14:37 +01001735 LOG(INFO) << "Exceeded number of session retries ("
1736 << kNumRetriesWhenCheckpointingEnabled
1737 << "). Starting a rollback";
Martijn Coenen44de00c2019-03-22 09:13:17 +01001738 Status status = rollbackStagedSessionIfAny();
1739 if (!status.Ok()) {
1740 LOG(ERROR)
1741 << "Failed to roll back (as requested by fs checkpointing) : "
1742 << status.ErrorMessage();
1743 }
1744 }
1745 }
1746
Jiyong Park5e810232019-04-01 15:24:26 +09001747 Status status = collectApexKeys();
1748 if (!status.Ok()) {
1749 LOG(ERROR) << "Failed to collect APEX keys : " << status.ErrorMessage();
1750 return;
1751 }
1752
Jooyung Han451cc342019-04-12 04:52:42 +09001753 gMountedApexes.PopulateFromMounts();
1754
Jiyong Park715e23d2019-02-22 22:14:37 +09001755 // Activate APEXes from /data/apex. If one in the directory is newer than the
1756 // system one, the new one will eclipse the old one.
Jiyong Park715e23d2019-02-22 22:14:37 +09001757 scanStagedSessionsDirAndStage();
Jiyong Park5e810232019-04-01 15:24:26 +09001758 status = resumeRollbackIfNeeded();
Jiyong Park715e23d2019-02-22 22:14:37 +09001759 if (!status.Ok()) {
1760 LOG(ERROR) << "Failed to resume rollback : " << status.ErrorMessage();
1761 }
1762
1763 status = scanPackagesDirAndActivate(kActiveApexPackagesDataDir);
1764 if (!status.Ok()) {
1765 LOG(ERROR) << "Failed to activate packages from "
1766 << kActiveApexPackagesDataDir << " : " << status.ErrorMessage();
Martijn Coenen44de00c2019-03-22 09:13:17 +01001767 Status rollback_status = rollbackActiveSessionAndReboot();
1768 if (!rollback_status.Ok()) {
Jiyong Park715e23d2019-02-22 22:14:37 +09001769 // TODO: should we kill apexd in this case?
1770 LOG(ERROR) << "Failed to rollback : " << rollback_status.ErrorMessage();
1771 }
1772 }
Jiyong Parkf94de552019-03-19 16:13:32 +09001773
Nikita Ioffee44c58a2019-04-29 14:07:11 +01001774 for (const auto& dir : kApexPackageBuiltinDirs) {
Jiyong Parkf94de552019-03-19 16:13:32 +09001775 // TODO(b/123622800): if activation failed, rollback and reboot.
Nikita Ioffee44c58a2019-04-29 14:07:11 +01001776 status = scanPackagesDirAndActivate(dir.c_str());
Jiyong Parkf94de552019-03-19 16:13:32 +09001777 if (!status.Ok()) {
1778 // This should never happen. Like **really** never.
1779 // TODO: should we kill apexd in this case?
Jooyung Hanb594e9c2019-03-25 17:15:24 +09001780 LOG(ERROR) << "Failed to activate packages from " << dir << " : "
1781 << status.ErrorMessage();
Jiyong Parkf94de552019-03-19 16:13:32 +09001782 }
Jiyong Park715e23d2019-02-22 22:14:37 +09001783 }
Jiyong Park2c7c7282018-11-01 20:02:25 +09001784}
1785
1786void onAllPackagesReady() {
1787 // Set a system property to let other components to know that APEXs are
1788 // correctly mounted and ready to be used. Before using any file from APEXs,
1789 // they can query this system property to ensure that they are okay to
1790 // access. Or they may have a on-property trigger to delay a task until
1791 // APEXs become ready.
Andreas Gampe4b7668b2019-01-25 08:25:43 -08001792 LOG(INFO) << "Marking APEXd as ready";
Jiyong Park2c7c7282018-11-01 20:02:25 +09001793 if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusReady)) {
Dario Freni88b859b2018-11-06 17:23:36 +00001794 PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to "
1795 << kApexStatusReady;
Jiyong Park2c7c7282018-11-01 20:02:25 +09001796 }
1797}
1798
Dario Freniab5b6c42019-01-15 11:53:57 +00001799StatusOr<std::vector<ApexFile>> submitStagedSession(
1800 const int session_id, const std::vector<int>& child_session_ids) {
Gavin Corkerya41373a2019-09-26 12:53:45 +01001801 using android::base::GetProperty;
Martijn Coenen93727a42019-05-01 10:10:13 +02001802 bool needsBackup = true;
Nikita Ioffe9bd28b92019-03-04 22:18:07 +00001803 Status cleanup_status = ClearSessions();
Nikita Ioffe53c3dcd2019-02-08 17:39:00 +00001804 if (!cleanup_status.Ok()) {
1805 return StatusOr<std::vector<ApexFile>>::MakeError(cleanup_status);
1806 }
1807
Martijn Coenen93727a42019-05-01 10:10:13 +02001808 if (gSupportsFsCheckpoints) {
1809 Status checkpoint_status =
Nikita Ioffe7af550d2019-05-13 14:14:37 +01001810 gVoldService->StartCheckpoint(kNumRetriesWhenCheckpointingEnabled);
Martijn Coenen93727a42019-05-01 10:10:13 +02001811 if (!checkpoint_status.Ok()) {
1812 // The device supports checkpointing, but we could not start it;
1813 // log a warning, but do continue, since we can live without it.
1814 LOG(WARNING) << "Failed to start filesystem checkpoint on device that "
1815 "should support it: "
1816 << checkpoint_status.ErrorMessage();
1817 } else {
1818 needsBackup = false;
1819 }
1820 }
1821
1822 if (needsBackup) {
Martijn Coenen44de00c2019-03-22 09:13:17 +01001823 Status backup_status = BackupActivePackages();
1824 if (!backup_status.Ok()) {
1825 return StatusOr<std::vector<ApexFile>>::MakeError(backup_status);
1826 }
Nikita Ioffea82b0a82019-02-15 18:59:47 +00001827 }
1828
Dario Freni6dd4dd62019-01-18 12:45:44 +00001829 std::vector<int> ids_to_scan;
Andreas Gampe7288cca2019-01-15 13:10:34 -08001830 if (!child_session_ids.empty()) {
Dario Freni6dd4dd62019-01-18 12:45:44 +00001831 ids_to_scan = child_session_ids;
1832 } else {
1833 ids_to_scan = {session_id};
Andreas Gampe7288cca2019-01-15 13:10:34 -08001834 }
Andreas Gampe17739142019-01-09 16:00:26 -08001835
Dario Freni6dd4dd62019-01-18 12:45:44 +00001836 std::vector<ApexFile> ret;
1837 for (int id_to_scan : ids_to_scan) {
1838 auto verified = verifySessionDir(id_to_scan);
1839 if (!verified.Ok()) {
1840 return StatusOr<std::vector<ApexFile>>::MakeError(verified.ErrorStatus());
1841 }
1842 ret.push_back(std::move(*verified));
Martijn Coenencabc92f2019-01-11 10:50:35 +01001843 }
Andreas Gampe7288cca2019-01-15 13:10:34 -08001844
1845 // Run preinstall, if necessary.
Dario Freni6dd4dd62019-01-18 12:45:44 +00001846 Status preinstall_status = PreinstallPackages(ret);
Andreas Gampe7288cca2019-01-15 13:10:34 -08001847 if (!preinstall_status.Ok()) {
1848 return StatusOr<std::vector<ApexFile>>::MakeError(preinstall_status);
1849 }
1850
Martijn Coenen610909b2019-01-18 13:49:38 +01001851 auto session = ApexSession::CreateSession(session_id);
1852 if (!session.Ok()) {
1853 return StatusOr<std::vector<ApexFile>>::MakeError(session.ErrorMessage());
Andreas Gampe7288cca2019-01-15 13:10:34 -08001854 }
Dario Freni6dd4dd62019-01-18 12:45:44 +00001855 (*session).SetChildSessionIds(child_session_ids);
Gavin Corkerya41373a2019-09-26 12:53:45 +01001856 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
1857 (*session).SetBuildFingerprint(build_fingerprint);
Dario Frenif36c9622019-01-25 11:30:00 +00001858 Status commit_status =
1859 (*session).UpdateStateAndCommit(SessionState::VERIFIED);
Dario Freni6dd4dd62019-01-18 12:45:44 +00001860 if (!commit_status.Ok()) {
1861 return StatusOr<std::vector<ApexFile>>::MakeError(commit_status);
1862 }
Andreas Gampe7288cca2019-01-15 13:10:34 -08001863
Dario Freni6dd4dd62019-01-18 12:45:44 +00001864 return StatusOr<std::vector<ApexFile>>(std::move(ret));
Dario Freni56231b42019-01-04 11:58:17 +00001865}
1866
Dario Frenif36c9622019-01-25 11:30:00 +00001867Status markStagedSessionReady(const int session_id) {
1868 auto session = ApexSession::GetSession(session_id);
1869 if (!session.Ok()) {
1870 return session.ErrorStatus();
1871 }
1872 // We should only accept sessions in SessionState::VERIFIED or
1873 // SessionState::STAGED state. In the SessionState::STAGED case, this
1874 // function is effectively a no-op.
1875 auto session_state = (*session).GetState();
1876 if (session_state == SessionState::STAGED) {
1877 return Status::Success();
1878 }
1879 if (session_state == SessionState::VERIFIED) {
1880 return (*session).UpdateStateAndCommit(SessionState::STAGED);
1881 }
1882 return Status::Fail(StringLog() << "Invalid state for session " << session_id
1883 << ". Cannot mark it as ready.");
1884}
1885
Nikita Ioffea0c0ccb2019-02-12 22:00:41 +00001886Status markStagedSessionSuccessful(const int session_id) {
1887 auto session = ApexSession::GetSession(session_id);
1888 if (!session.Ok()) {
1889 return session.ErrorStatus();
1890 }
1891 // Only SessionState::ACTIVATED or SessionState::SUCCESS states are accepted.
1892 // In the SessionState::SUCCESS state, this function is a no-op.
Nikita Ioffea4dc3e82019-02-23 17:37:04 +00001893 if (session->GetState() == SessionState::SUCCESS) {
1894 return Status::Success();
1895 } else if (session->GetState() == SessionState::ACTIVATED) {
1896 auto cleanup_status = DeleteBackup();
1897 if (!cleanup_status.Ok()) {
1898 return Status::Fail(StringLog() << "Failed to mark session " << *session
1899 << " as successful : "
1900 << cleanup_status.ErrorMessage());
1901 }
1902 return session->UpdateStateAndCommit(SessionState::SUCCESS);
1903 } else {
1904 return Status::Fail(StringLog() << "Session " << *session
1905 << " can not be marked successful");
Nikita Ioffea0c0ccb2019-02-12 22:00:41 +00001906 }
1907}
1908
Jooyung Hanf7078292019-04-19 01:40:38 +09001909// Find dangling mounts and unmount them.
1910// If one is on /data/apex/active, remove it.
1911void unmountDanglingMounts() {
1912 std::multimap<std::string, MountedApexData> danglings;
1913 gMountedApexes.ForallMountedApexes([&](const std::string& package,
1914 const MountedApexData& data,
1915 bool latest) {
1916 if (!latest) {
1917 danglings.insert({package, data});
1918 }
1919 });
1920
1921 for (const auto& [package, data] : danglings) {
1922 const std::string& path = data.full_path;
1923 LOG(VERBOSE) << "Unmounting " << data.mount_point;
1924 gMountedApexes.RemoveMountedApex(package, path);
1925 if (auto st = Unmount(data); !st.Ok()) {
1926 LOG(ERROR) << st.ErrorMessage();
1927 }
1928 if (StartsWith(path, kActiveApexPackagesDataDir)) {
1929 LOG(VERBOSE) << "Deleting old APEX " << path;
1930 if (unlink(path.c_str()) != 0) {
1931 PLOG(ERROR) << "Failed to delete " << path;
1932 }
1933 }
1934 }
1935}
1936
Dario Freni3ff2c652018-08-10 19:55:32 +01001937} // namespace apex
1938} // namespace android