blob: 3825af95eed08704c32469da02625e4f7a1a3878 [file] [log] [blame]
Daniel Rosenberg65f99c92018-08-28 01:58:49 -07001/*
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 "Checkpoint"
18#include "Checkpoint.h"
Eric Biggersb615f3b2022-11-09 05:48:45 +000019#include "FsCrypt.h"
20#include "KeyStorage.h"
Daniel Rosenberg253b44e2019-02-01 19:25:47 -080021#include "VoldUtil.h"
Sandeep Patilf8da61f2019-04-15 08:45:27 -070022#include "VolumeManager.h"
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070023
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070024#include <fstream>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070025#include <list>
Paul Lawrence20400892018-10-03 14:14:52 -070026#include <memory>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070027#include <string>
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080028#include <thread>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070029#include <vector>
30
Kelvin Zhangdec03ab2022-06-21 14:31:01 -070031#include <BootControlClient.h>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070032#include <android-base/file.h>
33#include <android-base/logging.h>
34#include <android-base/parseint.h>
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -080035#include <android-base/properties.h>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070036#include <android-base/unique_fd.h>
37#include <cutils/android_reboot.h>
38#include <fcntl.h>
39#include <fs_mgr.h>
40#include <linux/fs.h>
41#include <mntent.h>
42#include <sys/mount.h>
43#include <sys/stat.h>
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080044#include <sys/statvfs.h>
45#include <unistd.h>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070046
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080047using android::base::GetBoolProperty;
48using android::base::GetUintProperty;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -080049using android::base::SetProperty;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070050using android::binder::Status;
Tom Cherry4c5bde22019-01-29 14:34:01 -080051using android::fs_mgr::Fstab;
Tom Cherry4c5bde22019-01-29 14:34:01 -080052using android::fs_mgr::ReadFstabFromFile;
Kelvin Zhangdec03ab2022-06-21 14:31:01 -070053using android::hal::BootControlClient;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070054
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070055namespace android {
56namespace vold {
57
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070058namespace {
59const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
60
Paul Lawrence82b35052019-04-19 14:26:39 -070061binder::Status error(const std::string& msg) {
62 PLOG(ERROR) << msg;
63 return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
64}
65
66binder::Status error(int error, const std::string& msg) {
67 LOG(ERROR) << msg;
68 return binder::Status::fromServiceSpecificError(error, String8(msg.c_str()));
69}
70
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070071bool setBowState(std::string const& block_device, std::string const& state) {
Paul Lawrence236e5e82019-06-25 14:44:33 -070072 std::string bow_device = fs_mgr_find_bow_device(block_device);
73 if (bow_device.empty()) return false;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070074
Paul Lawrence236e5e82019-06-25 14:44:33 -070075 if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
76 PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070077 return false;
78 }
79
80 return true;
81}
82
Eric Biggersb615f3b2022-11-09 05:48:45 +000083// Do any work that was deferred until the userdata filesystem checkpoint was
84// committed. This work involves the deletion of resources that aren't covered
85// by the userdata filesystem checkpoint, e.g. Keystore keys.
86void DoCheckpointCommittedWork() {
87 // Take the crypt lock to provide synchronization with the Binder calls that
88 // operate on key directories.
89 std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getCryptLock());
90
91 DeferredCommitKeystoreKeys();
92 fscrypt_deferred_fixate_ce_keys();
93}
94
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070095} // namespace
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070096
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080097Status cp_supportsCheckpoint(bool& result) {
98 result = false;
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080099
Tom Cherry4c5bde22019-01-29 14:34:01 -0800100 for (const auto& entry : fstab_default) {
101 if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -0800102 result = true;
103 return Status::ok();
104 }
105 }
106 return Status::ok();
107}
108
Paul Lawrencec5c79c52019-03-18 13:36:40 -0700109Status cp_supportsBlockCheckpoint(bool& result) {
110 result = false;
111
112 for (const auto& entry : fstab_default) {
113 if (entry.fs_mgr_flags.checkpoint_blk) {
114 result = true;
115 return Status::ok();
116 }
117 }
118 return Status::ok();
119}
120
121Status cp_supportsFileCheckpoint(bool& result) {
122 result = false;
123
124 for (const auto& entry : fstab_default) {
125 if (entry.fs_mgr_flags.checkpoint_fs) {
126 result = true;
127 return Status::ok();
128 }
129 }
130 return Status::ok();
131}
132
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700133Status cp_startCheckpoint(int retry) {
Paul Lawrencec2a145f2019-05-15 09:42:04 -0700134 bool result;
135 if (!cp_supportsCheckpoint(result).isOk() || !result)
136 return error(ENOTSUP, "Checkpoints not supported");
137
Paul Lawrence82b35052019-04-19 14:26:39 -0700138 if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700139 std::string content = std::to_string(retry + 1);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700140 if (retry == -1) {
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700141 auto module = BootControlClient::WaitForService();
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700142 if (module) {
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700143 std::string suffix = module->GetSuffix(module->GetCurrentSlot());
144 if (!suffix.empty()) content += " " + suffix;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700145 }
146 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700147 if (!android::base::WriteStringToFile(content, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700148 return error("Failed to write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700149 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700150}
151
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800152namespace {
153
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800154volatile bool isCheckpointing = false;
Paul Lawrence1d57f682019-08-22 09:51:18 -0700155
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100156volatile bool needsCheckpointWasCalled = false;
157
158// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
159// of isCheckpointing
Paul Lawrence1d57f682019-08-22 09:51:18 -0700160std::mutex isCheckpointingLock;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800161}
162
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700163Status cp_commitChanges() {
Paul Lawrence1d57f682019-08-22 09:51:18 -0700164 std::lock_guard<std::mutex> lock(isCheckpointingLock);
165
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800166 if (!isCheckpointing) {
167 return Status::ok();
168 }
Paul Lawrencea7972dc2019-06-12 12:03:01 -0700169 if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
170 LOG(WARNING)
171 << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
172 return Status::ok();
173 }
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700174 auto module = BootControlClient::WaitForService();
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800175 if (module) {
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700176 auto cr = module->MarkBootSuccessful();
Paul Lawrence82b35052019-04-19 14:26:39 -0700177 if (!cr.success)
178 return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800179 LOG(INFO) << "Marked slot as booted successfully.";
Tianjie Xu09de0ff2019-11-15 14:05:29 -0800180 // Clears the warm reset flag for next reboot.
181 if (!SetProperty("ota.warm_reset", "0")) {
182 LOG(WARNING) << "Failed to reset the warm reset flag";
183 }
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700184 } else {
185 LOG(ERROR) << "Failed to get BootControl HAL, not marking slot as successful.";
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800186 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700187 // Must take action for list of mounted checkpointed things here
188 // To do this, we walk the list of mounted file systems.
189 // But we also need to get the matching fstab entries to see
190 // the original flags
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700191 std::string err_str;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700192
Tom Cherry4c5bde22019-01-29 14:34:01 -0800193 Fstab mounts;
194 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700195 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800196 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700197
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700198 // Walk mounted file systems
Tom Cherry4c5bde22019-01-29 14:34:01 -0800199 for (const auto& mount_rec : mounts) {
200 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700201 if (!fstab_rec) continue;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700202
Tom Cherry4c5bde22019-01-29 14:34:01 -0800203 if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
204 if (fstab_rec->fs_type == "f2fs") {
205 std::string options = mount_rec.fs_options + ",checkpoint=enable";
206 if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
Daniel Rosenberg14ca4ac2019-01-24 18:23:18 -0800207 MS_REMOUNT | fstab_rec->flags, options.c_str())) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700208 return error(EINVAL, "Failed to remount");
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800209 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700210 }
Tom Cherry4c5bde22019-01-29 14:34:01 -0800211 } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
212 if (!setBowState(mount_rec.blk_device, "2"))
Paul Lawrence82b35052019-04-19 14:26:39 -0700213 return error(EINVAL, "Failed to set bow state");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700214 }
215 }
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800216 SetProperty("vold.checkpoint_committed", "1");
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800217 LOG(INFO) << "Checkpoint has been committed.";
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800218 isCheckpointing = false;
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800219 if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
Paul Lawrence82b35052019-04-19 14:26:39 -0700220 return error(err_str.c_str());
221
Eric Biggersb615f3b2022-11-09 05:48:45 +0000222 std::thread(DoCheckpointCommittedWork).detach();
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700223 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700224}
225
Daniel Rosenberga59e4392019-03-20 17:02:47 -0700226namespace {
227void abort_metadata_file() {
228 std::string oldContent, newContent;
229 int retry = 0;
230 struct stat st;
231 int result = stat(kMetadataCPFile.c_str(), &st);
232
233 // If the file doesn't exist, we aren't managing a checkpoint retry counter
234 if (result != 0) return;
235 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
236 PLOG(ERROR) << "Failed to read checkpoint file";
237 return;
238 }
239 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
240
241 if (!android::base::ParseInt(retryContent, &retry)) {
242 PLOG(ERROR) << "Could not parse retry count";
243 return;
244 }
245 if (retry > 0) {
246 newContent = "0";
247 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
248 PLOG(ERROR) << "Could not write checkpoint file";
249 }
250}
251} // namespace
252
253void cp_abortChanges(const std::string& message, bool retry) {
254 if (!cp_needsCheckpoint()) return;
255 if (!retry) abort_metadata_file();
256 android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700257}
258
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700259bool cp_needsRollback() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700260 std::string content;
261 bool ret;
262
263 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700264 if (ret) {
265 if (content == "0") return true;
266 if (content.substr(0, 3) == "-1 ") {
267 std::string oldSuffix = content.substr(3);
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700268 auto module = BootControlClient::WaitForService();
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700269 std::string newSuffix;
270
271 if (module) {
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700272 newSuffix = module->GetSuffix(module->GetCurrentSlot());
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700273 if (oldSuffix == newSuffix) return true;
274 }
275 }
276 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700277 return false;
278}
279
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700280bool cp_needsCheckpoint() {
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100281 std::lock_guard<std::mutex> lock(isCheckpointingLock);
282
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700283 // Make sure we only return true during boot. See b/138952436 for discussion
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100284 if (needsCheckpointWasCalled) return isCheckpointing;
285 needsCheckpointWasCalled = true;
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700286
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700287 bool ret;
288 std::string content;
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700289 auto module = BootControlClient::WaitForService();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700290
Daniel Rosenberg84203c12019-03-19 14:02:59 -0700291 if (isCheckpointing) return isCheckpointing;
Kelvin Zhangdec03ab2022-06-21 14:31:01 -0700292 // In case of INVALID slot or other failures, we do not perform checkpoint.
293 if (module && !module->IsSlotMarkedSuccessful(module->GetCurrentSlot()).value_or(true)) {
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800294 isCheckpointing = true;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700295 return true;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800296 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700297 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800298 if (ret) {
299 ret = content != "0";
300 isCheckpointing = ret;
301 return ret;
302 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700303 return false;
304}
305
David Anderson23850d32020-06-10 23:51:17 -0700306bool cp_isCheckpointing() {
307 return isCheckpointing;
308}
309
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800310namespace {
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700311const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
312const uint32_t msleeptime_default = 1000; // 1 s
313const uint32_t max_msleeptime = 3600000; // 1 h
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800314
315const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
316const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
317
318const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
319const bool commit_on_full_default = true;
320
321static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
322 struct statvfs data;
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700323 uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
Satoshi Futenma18d10d42019-03-25 23:13:36 +0900324 uint64_t min_free_bytes =
325 GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800326 bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
327
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700328 struct timespec req;
329 req.tv_sec = msleeptime / 1000;
330 msleeptime %= 1000;
331 req.tv_nsec = msleeptime * 1000000;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800332 while (isCheckpointing) {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700333 uint64_t free_bytes = 0;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800334 if (is_fs_cp) {
335 statvfs(mnt_pnt.c_str(), &data);
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800336 free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800337 } else {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700338 std::string bow_device = fs_mgr_find_bow_device(blk_device);
339 if (!bow_device.empty()) {
340 std::string content;
341 if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800342 free_bytes = std::strtoull(content.c_str(), NULL, 10);
Paul Lawrence236e5e82019-06-25 14:44:33 -0700343 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800344 }
345 }
346 if (free_bytes < min_free_bytes) {
347 if (commit_on_full) {
348 LOG(INFO) << "Low space for checkpointing. Commiting changes";
349 cp_commitChanges();
350 break;
351 } else {
352 LOG(INFO) << "Low space for checkpointing. Rebooting";
353 cp_abortChanges("checkpoint,low_space", false);
354 break;
355 }
356 }
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700357 nanosleep(&req, NULL);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800358 }
359}
360
361} // namespace
362
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700363Status cp_prepareCheckpoint() {
Paul Lawrence4c757fb2019-10-23 09:36:18 -0700364 // Log to notify CTS - see b/137924328 for context
365 LOG(INFO) << "cp_prepareCheckpoint called";
Paul Lawrence1d57f682019-08-22 09:51:18 -0700366 std::lock_guard<std::mutex> lock(isCheckpointingLock);
Paul Lawrencedb086942019-02-19 14:18:54 -0800367 if (!isCheckpointing) {
368 return Status::ok();
369 }
370
Tom Cherry4c5bde22019-01-29 14:34:01 -0800371 Fstab mounts;
372 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700373 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800374 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700375
Tom Cherry4c5bde22019-01-29 14:34:01 -0800376 for (const auto& mount_rec : mounts) {
377 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700378 if (!fstab_rec) continue;
379
Tom Cherry4c5bde22019-01-29 14:34:01 -0800380 if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700381 android::base::unique_fd fd(
Tom Cherry4c5bde22019-01-29 14:34:01 -0800382 TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
Bernie Innocentiebe293a2019-03-28 15:24:30 +0900383 if (fd == -1) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800384 PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700385 continue;
386 }
387
388 struct fstrim_range range = {};
389 range.len = ULLONG_MAX;
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700390 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700391 if (ioctl(fd, FITRIM, &range)) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800392 PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700393 continue;
394 }
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700395 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
396 LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
397 << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700398
Tom Cherry4c5bde22019-01-29 14:34:01 -0800399 setBowState(mount_rec.blk_device, "1");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700400 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800401 if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
402 std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
Paul Lawrencee81f4c12019-03-29 13:06:34 -0700403 std::string(mount_rec.blk_device),
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800404 fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
405 .detach();
406 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700407 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700408 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700409}
410
411namespace {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700412const int kSectorSize = 512;
413
414typedef uint64_t sector_t;
415
416struct log_entry {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800417 sector_t source; // in sectors of size kSectorSize
418 sector_t dest; // in sectors of size kSectorSize
419 uint32_t size; // in bytes
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700420 uint32_t checksum;
421} __attribute__((packed));
422
Paul Lawrencef5077682019-01-18 10:28:34 -0800423struct log_sector_v1_0 {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700424 uint32_t magic;
Paul Lawrencef5077682019-01-18 10:28:34 -0800425 uint16_t header_version;
426 uint16_t header_size;
Paul Lawrence4f13a902019-01-10 13:06:07 -0800427 uint32_t block_size;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700428 uint32_t count;
429 uint32_t sequence;
Paul Lawrence27691c22018-11-20 14:07:59 -0800430 uint64_t sector0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700431} __attribute__((packed));
432
433// MAGIC is BOW in ascii
434const int kMagic = 0x00574f42;
Daniel Rosenberg52985932019-03-01 22:01:22 -0800435// Partially restored MAGIC is WOB in ascii
436const int kPartialRestoreMagic = 0x00424f57;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700437
438void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
439 static uint32_t table[0x100] = {
440 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
441 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
442 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
443 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
444 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
445 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
446 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
447 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
448 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
449 0xB6662D3D,
450
451 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
452 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
453 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
454 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
455 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
456 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
457 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
458 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
459 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
460 0xC0BA6CAD,
461
462 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
463 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
464 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
465 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
466 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
467 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
468 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
469 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
470 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
471 0x5BDEAE1D,
472
473 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
474 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
475 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
476 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
477 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
478 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
479 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
480 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
481 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
482 0x2D02EF8D};
483
484 for (size_t i = 0; i < n_bytes; ++i) {
485 *crc ^= ((uint8_t*)data)[i];
486 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
487 }
488}
489
Paul Lawrenced41a9392019-01-22 14:31:43 -0800490// A map of relocations.
491// The map must be initialized so that relocations[0] = 0
492// During restore, we replay the log records in reverse, copying from dest to
493// source
494// To validate, we must be able to read the 'dest' sectors as though they had
495// been copied but without actually copying. This map represents how the sectors
496// would have been moved. To read a sector s, find the index <= s and read
497// relocations[index] + s - index
498typedef std::map<sector_t, sector_t> Relocations;
Paul Lawrence27691c22018-11-20 14:07:59 -0800499
Paul Lawrenced41a9392019-01-22 14:31:43 -0800500void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
501 // Find first one we're equal to or greater than
502 auto s = --relocations.upper_bound(source);
503
504 // Take slice
505 Relocations slice;
506 slice[dest] = source - s->first + s->second;
507 ++s;
508
509 // Add rest of elements
510 for (; s != relocations.end() && s->first < source + count; ++s)
511 slice[dest - source + s->first] = s->second;
512
513 // Split range at end of dest
514 auto dest_end = --relocations.upper_bound(dest + count);
515 relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
516
517 // Remove all elements in [dest, dest + count)
518 relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
519
520 // Add new elements
521 relocations.insert(slice.begin(), slice.end());
Paul Lawrence27691c22018-11-20 14:07:59 -0800522}
523
Daniel Rosenberg52985932019-03-01 22:01:22 -0800524// A map of sectors that have been written to.
525// The final entry must always be False.
526// When we restart the restore after an interruption, we must take care that
527// when we copy from dest to source, that the block we copy to was not
528// previously copied from.
529// i e. A->B C->A; If we replay this sequence, we end up copying C->B
530// We must save our partial result whenever we finish a page, or when we copy
531// to a location that was copied from earlier (our source is an earlier dest)
532typedef std::map<sector_t, bool> Used_Sectors;
533
534bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
535 auto second_overlap = used_sectors.upper_bound(start);
536 auto first_overlap = --second_overlap;
537
538 if (first_overlap->second) {
539 return true;
540 } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
541 return true;
542 }
543 return false;
544}
545
546void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
547 auto start_pos = used_sectors.insert_or_assign(start, true).first;
548 auto end_pos = used_sectors.insert_or_assign(end, false).first;
549
550 if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
551 start_pos++;
552 }
553 if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
554 end_pos++;
555 }
556 if (start_pos->first < end_pos->first) {
557 used_sectors.erase(start_pos, end_pos);
558 }
559}
560
561// Restores the given log_entry's data from dest -> source
562// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
563void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
564 log_entry* le, std::vector<char>& buffer) {
565 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
566 uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
567 int count = (le->size - 1) / kSectorSize + 1;
568
569 if (checkCollision(used_sectors, le->source, le->source + count)) {
570 fsync(device_fd);
571 lseek64(device_fd, 0, SEEK_SET);
572 ls.count = index + 1;
573 ls.magic = kPartialRestoreMagic;
574 write(device_fd, &ls_buffer[0], ls.block_size);
575 fsync(device_fd);
576 used_sectors.clear();
577 used_sectors[0] = false;
578 }
579
580 markUsed(used_sectors, le->dest, le->dest + count);
581
582 if (index == 0 && ls.sequence != 0) {
583 log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
584 if (next->magic == kMagic) {
585 next->magic = kPartialRestoreMagic;
586 }
587 }
588
589 lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
590 write(device_fd, &buffer[0], le->size);
591
592 if (index == 0) {
593 fsync(device_fd);
594 }
595}
596
Paul Lawrenced41a9392019-01-22 14:31:43 -0800597// Read from the device
598// If we are validating, the read occurs as though the relocations had happened
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800599std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
600 sector_t sector, uint32_t size, uint32_t block_size) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800601 if (!validating) {
602 std::vector<char> buffer(size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800603 lseek64(device_fd, sector * kSectorSize, SEEK_SET);
604 read(device_fd, &buffer[0], size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800605 return buffer;
606 }
607
Paul Lawrence27691c22018-11-20 14:07:59 -0800608 std::vector<char> buffer(size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800609 for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
610 auto relocation = --relocations.upper_bound(sector);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800611 lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
612 SEEK_SET);
613 read(device_fd, &buffer[i], block_size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800614 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800615
616 return buffer;
617}
618
Paul Lawrence4f13a902019-01-10 13:06:07 -0800619} // namespace
620
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800621Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800622 bool validating = true;
623 std::string action = "Validating";
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800624 int restore_count = 0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700625
Paul Lawrence27691c22018-11-20 14:07:59 -0800626 for (;;) {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800627 Relocations relocations;
628 relocations[0] = 0;
Paul Lawrence27691c22018-11-20 14:07:59 -0800629 Status status = Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700630
Paul Lawrence27691c22018-11-20 14:07:59 -0800631 LOG(INFO) << action << " checkpoint on " << blockDevice;
Nick Kraleviche7e89ac2019-03-29 16:03:51 -0700632 base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
Paul Lawrence82b35052019-04-19 14:26:39 -0700633 if (device_fd < 0) return error("Cannot open " + blockDevice);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800634
Paul Lawrencef5077682019-01-18 10:28:34 -0800635 log_sector_v1_0 original_ls;
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800636 read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
Daniel Rosenberg52985932019-03-01 22:01:22 -0800637 if (original_ls.magic == kPartialRestoreMagic) {
638 validating = false;
639 action = "Restoring";
640 } else if (original_ls.magic != kMagic) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700641 return error(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700642 }
643
Paul Lawrence4f13a902019-01-10 13:06:07 -0800644 LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700645
Paul Lawrence4f13a902019-01-10 13:06:07 -0800646 for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800647 auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
648 original_ls.block_size, original_ls.block_size);
649 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
650
651 Used_Sectors used_sectors;
652 used_sectors[0] = false;
653
654 if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700655 status = error(EINVAL, "No magic");
Paul Lawrence27691c22018-11-20 14:07:59 -0800656 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700657 }
658
Paul Lawrence4f13a902019-01-10 13:06:07 -0800659 if (ls.block_size != original_ls.block_size) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700660 status = error(EINVAL, "Block size mismatch");
Paul Lawrence4f13a902019-01-10 13:06:07 -0800661 break;
662 }
663
Paul Lawrence27691c22018-11-20 14:07:59 -0800664 if ((int)ls.sequence != sequence) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700665 status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
666 " but got " + std::to_string(ls.sequence));
Paul Lawrence27691c22018-11-20 14:07:59 -0800667 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700668 }
669
Paul Lawrence27691c22018-11-20 14:07:59 -0800670 LOG(INFO) << action << " from log sector " << ls.sequence;
Paul Lawrencef5077682019-01-18 10:28:34 -0800671 for (log_entry* le =
Daniel Rosenberg52985932019-03-01 22:01:22 -0800672 reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
673 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
Paul Lawrencef5077682019-01-18 10:28:34 -0800674 // This is very noisy - limit to DEBUG only
Paul Lawrenced41a9392019-01-22 14:31:43 -0800675 LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
676 << " to " << le->source << " with checksum " << std::hex
677 << le->checksum;
Paul Lawrencef5077682019-01-18 10:28:34 -0800678
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800679 auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800680 ls.block_size);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800681 uint32_t checksum = le->source / (ls.block_size / kSectorSize);
682 for (size_t i = 0; i < le->size; i += ls.block_size) {
683 crc32(&buffer[i], ls.block_size, &checksum);
Paul Lawrence27691c22018-11-20 14:07:59 -0800684 }
685
686 if (le->checksum && checksum != le->checksum) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700687 status = error(EINVAL, "Checksums don't match");
Paul Lawrence27691c22018-11-20 14:07:59 -0800688 break;
689 }
690
Paul Lawrenced41a9392019-01-22 14:31:43 -0800691 if (validating) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800692 relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800693 } else {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800694 restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800695 restore_count++;
696 if (restore_limit && restore_count >= restore_limit) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700697 status = error(EAGAIN, "Hit the test limit");
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800698 break;
699 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800700 }
701 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700702 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800703
704 if (!status.isOk()) {
705 if (!validating) {
706 LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
707 return status;
708 }
709
710 LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800711 auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800712 original_ls.block_size, original_ls.block_size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800713 lseek64(device_fd, 0, SEEK_SET);
714 write(device_fd, &buffer[0], original_ls.block_size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800715 return Status::ok();
716 }
717
718 if (!validating) break;
719
720 validating = false;
721 action = "Restoring";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700722 }
723
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700724 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700725}
726
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700727Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700728 std::string oldContent, newContent;
729 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700730 struct stat st;
731 int result = stat(kMetadataCPFile.c_str(), &st);
732
733 // If the file doesn't exist, we aren't managing a checkpoint retry counter
734 if (result != 0) return Status::ok();
Paul Lawrence82b35052019-04-19 14:26:39 -0700735 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
736 return error("Failed to read checkpoint file");
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700737 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700738
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700739 if (!android::base::ParseInt(retryContent, &retry))
Paul Lawrence82b35052019-04-19 14:26:39 -0700740 return error(EINVAL, "Could not parse retry count");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700741 if (retry > 0) {
742 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700743
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700744 newContent = std::to_string(retry);
745 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700746 return error("Could not write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700747 }
748 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700749}
750
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100751void cp_resetCheckpoint() {
752 std::lock_guard<std::mutex> lock(isCheckpointingLock);
753 needsCheckpointWasCalled = false;
754}
755
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700756} // namespace vold
757} // namespace android