blob: 755f0e3b3401350a61a764364cf6af7b3126f701 [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"
Daniel Rosenberg253b44e2019-02-01 19:25:47 -080019#include "VoldUtil.h"
Sandeep Patilf8da61f2019-04-15 08:45:27 -070020#include "VolumeManager.h"
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070021
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070022#include <fstream>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070023#include <list>
Paul Lawrence20400892018-10-03 14:14:52 -070024#include <memory>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070025#include <string>
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080026#include <thread>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070027#include <vector>
28
29#include <android-base/file.h>
30#include <android-base/logging.h>
31#include <android-base/parseint.h>
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -080032#include <android-base/properties.h>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070033#include <android-base/unique_fd.h>
Daniel Rosenbergd3992492018-10-02 17:40:44 -070034#include <android/hardware/boot/1.0/IBootControl.h>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070035#include <cutils/android_reboot.h>
36#include <fcntl.h>
37#include <fs_mgr.h>
38#include <linux/fs.h>
39#include <mntent.h>
40#include <sys/mount.h>
41#include <sys/stat.h>
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080042#include <sys/statvfs.h>
43#include <unistd.h>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070044
Daniel Rosenberg8daeec02018-11-20 19:03:11 -080045using android::base::GetBoolProperty;
46using android::base::GetUintProperty;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -080047using android::base::SetProperty;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070048using android::binder::Status;
Tom Cherry4c5bde22019-01-29 14:34:01 -080049using android::fs_mgr::Fstab;
Tom Cherry4c5bde22019-01-29 14:34:01 -080050using android::fs_mgr::ReadFstabFromFile;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070051using android::hardware::hidl_string;
52using android::hardware::boot::V1_0::BoolResult;
Daniel Rosenberg886915b2019-01-23 15:16:04 -080053using android::hardware::boot::V1_0::CommandResult;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070054using android::hardware::boot::V1_0::IBootControl;
55using android::hardware::boot::V1_0::Slot;
56
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070057namespace android {
58namespace vold {
59
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070060namespace {
61const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
62
Paul Lawrence82b35052019-04-19 14:26:39 -070063binder::Status error(const std::string& msg) {
64 PLOG(ERROR) << msg;
65 return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
66}
67
68binder::Status error(int error, const std::string& msg) {
69 LOG(ERROR) << msg;
70 return binder::Status::fromServiceSpecificError(error, String8(msg.c_str()));
71}
72
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070073bool setBowState(std::string const& block_device, std::string const& state) {
Paul Lawrence236e5e82019-06-25 14:44:33 -070074 std::string bow_device = fs_mgr_find_bow_device(block_device);
75 if (bow_device.empty()) return false;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070076
Paul Lawrence236e5e82019-06-25 14:44:33 -070077 if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
78 PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070079 return false;
80 }
81
82 return true;
83}
84
85} // namespace
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070086
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080087Status cp_supportsCheckpoint(bool& result) {
88 result = false;
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080089
Tom Cherry4c5bde22019-01-29 14:34:01 -080090 for (const auto& entry : fstab_default) {
91 if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080092 result = true;
93 return Status::ok();
94 }
95 }
96 return Status::ok();
97}
98
Paul Lawrencec5c79c52019-03-18 13:36:40 -070099Status cp_supportsBlockCheckpoint(bool& result) {
100 result = false;
101
102 for (const auto& entry : fstab_default) {
103 if (entry.fs_mgr_flags.checkpoint_blk) {
104 result = true;
105 return Status::ok();
106 }
107 }
108 return Status::ok();
109}
110
111Status cp_supportsFileCheckpoint(bool& result) {
112 result = false;
113
114 for (const auto& entry : fstab_default) {
115 if (entry.fs_mgr_flags.checkpoint_fs) {
116 result = true;
117 return Status::ok();
118 }
119 }
120 return Status::ok();
121}
122
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700123Status cp_startCheckpoint(int retry) {
Paul Lawrencec2a145f2019-05-15 09:42:04 -0700124 bool result;
125 if (!cp_supportsCheckpoint(result).isOk() || !result)
126 return error(ENOTSUP, "Checkpoints not supported");
127
Paul Lawrence82b35052019-04-19 14:26:39 -0700128 if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700129 std::string content = std::to_string(retry + 1);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700130 if (retry == -1) {
131 sp<IBootControl> module = IBootControl::getService();
132 if (module) {
133 std::string suffix;
134 auto cb = [&suffix](hidl_string s) { suffix = s; };
135 if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
136 }
137 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700138 if (!android::base::WriteStringToFile(content, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700139 return error("Failed to write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700140 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700141}
142
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800143namespace {
144
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800145volatile bool isCheckpointing = false;
Paul Lawrence1d57f682019-08-22 09:51:18 -0700146
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100147volatile bool needsCheckpointWasCalled = false;
148
149// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
150// of isCheckpointing
Paul Lawrence1d57f682019-08-22 09:51:18 -0700151std::mutex isCheckpointingLock;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800152}
153
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700154Status cp_commitChanges() {
Paul Lawrence1d57f682019-08-22 09:51:18 -0700155 std::lock_guard<std::mutex> lock(isCheckpointingLock);
156
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800157 if (!isCheckpointing) {
158 return Status::ok();
159 }
Paul Lawrencea7972dc2019-06-12 12:03:01 -0700160 if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
161 LOG(WARNING)
162 << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
163 return Status::ok();
164 }
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800165 sp<IBootControl> module = IBootControl::getService();
166 if (module) {
167 CommandResult cr;
168 module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
Paul Lawrence82b35052019-04-19 14:26:39 -0700169 if (!cr.success)
170 return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800171 LOG(INFO) << "Marked slot as booted successfully.";
Tianjie Xu09de0ff2019-11-15 14:05:29 -0800172 // Clears the warm reset flag for next reboot.
173 if (!SetProperty("ota.warm_reset", "0")) {
174 LOG(WARNING) << "Failed to reset the warm reset flag";
175 }
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800176 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700177 // Must take action for list of mounted checkpointed things here
178 // To do this, we walk the list of mounted file systems.
179 // But we also need to get the matching fstab entries to see
180 // the original flags
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700181 std::string err_str;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700182
Tom Cherry4c5bde22019-01-29 14:34:01 -0800183 Fstab mounts;
184 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700185 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800186 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700187
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700188 // Walk mounted file systems
Tom Cherry4c5bde22019-01-29 14:34:01 -0800189 for (const auto& mount_rec : mounts) {
190 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700191 if (!fstab_rec) continue;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700192
Tom Cherry4c5bde22019-01-29 14:34:01 -0800193 if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
194 if (fstab_rec->fs_type == "f2fs") {
195 std::string options = mount_rec.fs_options + ",checkpoint=enable";
196 if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
Daniel Rosenberg14ca4ac2019-01-24 18:23:18 -0800197 MS_REMOUNT | fstab_rec->flags, options.c_str())) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700198 return error(EINVAL, "Failed to remount");
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800199 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700200 }
Tom Cherry4c5bde22019-01-29 14:34:01 -0800201 } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
202 if (!setBowState(mount_rec.blk_device, "2"))
Paul Lawrence82b35052019-04-19 14:26:39 -0700203 return error(EINVAL, "Failed to set bow state");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700204 }
205 }
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800206 SetProperty("vold.checkpoint_committed", "1");
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800207 LOG(INFO) << "Checkpoint has been committed.";
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800208 isCheckpointing = false;
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800209 if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
Paul Lawrence82b35052019-04-19 14:26:39 -0700210 return error(err_str.c_str());
211
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700212 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700213}
214
Daniel Rosenberga59e4392019-03-20 17:02:47 -0700215namespace {
216void abort_metadata_file() {
217 std::string oldContent, newContent;
218 int retry = 0;
219 struct stat st;
220 int result = stat(kMetadataCPFile.c_str(), &st);
221
222 // If the file doesn't exist, we aren't managing a checkpoint retry counter
223 if (result != 0) return;
224 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
225 PLOG(ERROR) << "Failed to read checkpoint file";
226 return;
227 }
228 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
229
230 if (!android::base::ParseInt(retryContent, &retry)) {
231 PLOG(ERROR) << "Could not parse retry count";
232 return;
233 }
234 if (retry > 0) {
235 newContent = "0";
236 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
237 PLOG(ERROR) << "Could not write checkpoint file";
238 }
239}
240} // namespace
241
242void cp_abortChanges(const std::string& message, bool retry) {
243 if (!cp_needsCheckpoint()) return;
244 if (!retry) abort_metadata_file();
245 android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700246}
247
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700248bool cp_needsRollback() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700249 std::string content;
250 bool ret;
251
252 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700253 if (ret) {
254 if (content == "0") return true;
255 if (content.substr(0, 3) == "-1 ") {
256 std::string oldSuffix = content.substr(3);
257 sp<IBootControl> module = IBootControl::getService();
258 std::string newSuffix;
259
260 if (module) {
261 auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
262 module->getSuffix(module->getCurrentSlot(), cb);
263 if (oldSuffix == newSuffix) return true;
264 }
265 }
266 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700267 return false;
268}
269
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700270bool cp_needsCheckpoint() {
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100271 std::lock_guard<std::mutex> lock(isCheckpointingLock);
272
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700273 // Make sure we only return true during boot. See b/138952436 for discussion
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100274 if (needsCheckpointWasCalled) return isCheckpointing;
275 needsCheckpointWasCalled = true;
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700276
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700277 bool ret;
278 std::string content;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700279 sp<IBootControl> module = IBootControl::getService();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700280
Daniel Rosenberg84203c12019-03-19 14:02:59 -0700281 if (isCheckpointing) return isCheckpointing;
282
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800283 if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
284 isCheckpointing = true;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700285 return true;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800286 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700287 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800288 if (ret) {
289 ret = content != "0";
290 isCheckpointing = ret;
291 return ret;
292 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700293 return false;
294}
295
David Anderson23850d32020-06-10 23:51:17 -0700296bool cp_isCheckpointing() {
297 return isCheckpointing;
298}
299
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800300namespace {
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700301const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
302const uint32_t msleeptime_default = 1000; // 1 s
303const uint32_t max_msleeptime = 3600000; // 1 h
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800304
305const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
306const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
307
308const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
309const bool commit_on_full_default = true;
310
311static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
312 struct statvfs data;
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700313 uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
Satoshi Futenma18d10d42019-03-25 23:13:36 +0900314 uint64_t min_free_bytes =
315 GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800316 bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
317
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700318 struct timespec req;
319 req.tv_sec = msleeptime / 1000;
320 msleeptime %= 1000;
321 req.tv_nsec = msleeptime * 1000000;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800322 while (isCheckpointing) {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700323 uint64_t free_bytes = 0;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800324 if (is_fs_cp) {
325 statvfs(mnt_pnt.c_str(), &data);
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800326 free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800327 } else {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700328 std::string bow_device = fs_mgr_find_bow_device(blk_device);
329 if (!bow_device.empty()) {
330 std::string content;
331 if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800332 free_bytes = std::strtoull(content.c_str(), NULL, 10);
Paul Lawrence236e5e82019-06-25 14:44:33 -0700333 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800334 }
335 }
336 if (free_bytes < min_free_bytes) {
337 if (commit_on_full) {
338 LOG(INFO) << "Low space for checkpointing. Commiting changes";
339 cp_commitChanges();
340 break;
341 } else {
342 LOG(INFO) << "Low space for checkpointing. Rebooting";
343 cp_abortChanges("checkpoint,low_space", false);
344 break;
345 }
346 }
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700347 nanosleep(&req, NULL);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800348 }
349}
350
351} // namespace
352
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700353Status cp_prepareCheckpoint() {
Paul Lawrence4c757fb2019-10-23 09:36:18 -0700354 // Log to notify CTS - see b/137924328 for context
355 LOG(INFO) << "cp_prepareCheckpoint called";
Paul Lawrence1d57f682019-08-22 09:51:18 -0700356 std::lock_guard<std::mutex> lock(isCheckpointingLock);
Paul Lawrencedb086942019-02-19 14:18:54 -0800357 if (!isCheckpointing) {
358 return Status::ok();
359 }
360
Tom Cherry4c5bde22019-01-29 14:34:01 -0800361 Fstab mounts;
362 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700363 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800364 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700365
Tom Cherry4c5bde22019-01-29 14:34:01 -0800366 for (const auto& mount_rec : mounts) {
367 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700368 if (!fstab_rec) continue;
369
Tom Cherry4c5bde22019-01-29 14:34:01 -0800370 if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700371 android::base::unique_fd fd(
Tom Cherry4c5bde22019-01-29 14:34:01 -0800372 TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
Bernie Innocentiebe293a2019-03-28 15:24:30 +0900373 if (fd == -1) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800374 PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700375 continue;
376 }
377
378 struct fstrim_range range = {};
379 range.len = ULLONG_MAX;
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700380 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700381 if (ioctl(fd, FITRIM, &range)) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800382 PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700383 continue;
384 }
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700385 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
386 LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
387 << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700388
Tom Cherry4c5bde22019-01-29 14:34:01 -0800389 setBowState(mount_rec.blk_device, "1");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700390 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800391 if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
392 std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
Paul Lawrencee81f4c12019-03-29 13:06:34 -0700393 std::string(mount_rec.blk_device),
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800394 fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
395 .detach();
396 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700397 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700398 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700399}
400
401namespace {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700402const int kSectorSize = 512;
403
404typedef uint64_t sector_t;
405
406struct log_entry {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800407 sector_t source; // in sectors of size kSectorSize
408 sector_t dest; // in sectors of size kSectorSize
409 uint32_t size; // in bytes
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700410 uint32_t checksum;
411} __attribute__((packed));
412
Paul Lawrencef5077682019-01-18 10:28:34 -0800413struct log_sector_v1_0 {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700414 uint32_t magic;
Paul Lawrencef5077682019-01-18 10:28:34 -0800415 uint16_t header_version;
416 uint16_t header_size;
Paul Lawrence4f13a902019-01-10 13:06:07 -0800417 uint32_t block_size;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700418 uint32_t count;
419 uint32_t sequence;
Paul Lawrence27691c22018-11-20 14:07:59 -0800420 uint64_t sector0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700421} __attribute__((packed));
422
423// MAGIC is BOW in ascii
424const int kMagic = 0x00574f42;
Daniel Rosenberg52985932019-03-01 22:01:22 -0800425// Partially restored MAGIC is WOB in ascii
426const int kPartialRestoreMagic = 0x00424f57;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700427
428void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
429 static uint32_t table[0x100] = {
430 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
431 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
432 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
433 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
434 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
435 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
436 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
437 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
438 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
439 0xB6662D3D,
440
441 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
442 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
443 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
444 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
445 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
446 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
447 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
448 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
449 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
450 0xC0BA6CAD,
451
452 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
453 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
454 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
455 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
456 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
457 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
458 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
459 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
460 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
461 0x5BDEAE1D,
462
463 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
464 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
465 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
466 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
467 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
468 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
469 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
470 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
471 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
472 0x2D02EF8D};
473
474 for (size_t i = 0; i < n_bytes; ++i) {
475 *crc ^= ((uint8_t*)data)[i];
476 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
477 }
478}
479
Paul Lawrenced41a9392019-01-22 14:31:43 -0800480// A map of relocations.
481// The map must be initialized so that relocations[0] = 0
482// During restore, we replay the log records in reverse, copying from dest to
483// source
484// To validate, we must be able to read the 'dest' sectors as though they had
485// been copied but without actually copying. This map represents how the sectors
486// would have been moved. To read a sector s, find the index <= s and read
487// relocations[index] + s - index
488typedef std::map<sector_t, sector_t> Relocations;
Paul Lawrence27691c22018-11-20 14:07:59 -0800489
Paul Lawrenced41a9392019-01-22 14:31:43 -0800490void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
491 // Find first one we're equal to or greater than
492 auto s = --relocations.upper_bound(source);
493
494 // Take slice
495 Relocations slice;
496 slice[dest] = source - s->first + s->second;
497 ++s;
498
499 // Add rest of elements
500 for (; s != relocations.end() && s->first < source + count; ++s)
501 slice[dest - source + s->first] = s->second;
502
503 // Split range at end of dest
504 auto dest_end = --relocations.upper_bound(dest + count);
505 relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
506
507 // Remove all elements in [dest, dest + count)
508 relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
509
510 // Add new elements
511 relocations.insert(slice.begin(), slice.end());
Paul Lawrence27691c22018-11-20 14:07:59 -0800512}
513
Daniel Rosenberg52985932019-03-01 22:01:22 -0800514// A map of sectors that have been written to.
515// The final entry must always be False.
516// When we restart the restore after an interruption, we must take care that
517// when we copy from dest to source, that the block we copy to was not
518// previously copied from.
519// i e. A->B C->A; If we replay this sequence, we end up copying C->B
520// We must save our partial result whenever we finish a page, or when we copy
521// to a location that was copied from earlier (our source is an earlier dest)
522typedef std::map<sector_t, bool> Used_Sectors;
523
524bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
525 auto second_overlap = used_sectors.upper_bound(start);
526 auto first_overlap = --second_overlap;
527
528 if (first_overlap->second) {
529 return true;
530 } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
531 return true;
532 }
533 return false;
534}
535
536void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
537 auto start_pos = used_sectors.insert_or_assign(start, true).first;
538 auto end_pos = used_sectors.insert_or_assign(end, false).first;
539
540 if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
541 start_pos++;
542 }
543 if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
544 end_pos++;
545 }
546 if (start_pos->first < end_pos->first) {
547 used_sectors.erase(start_pos, end_pos);
548 }
549}
550
551// Restores the given log_entry's data from dest -> source
552// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
553void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
554 log_entry* le, std::vector<char>& buffer) {
555 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
556 uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
557 int count = (le->size - 1) / kSectorSize + 1;
558
559 if (checkCollision(used_sectors, le->source, le->source + count)) {
560 fsync(device_fd);
561 lseek64(device_fd, 0, SEEK_SET);
562 ls.count = index + 1;
563 ls.magic = kPartialRestoreMagic;
564 write(device_fd, &ls_buffer[0], ls.block_size);
565 fsync(device_fd);
566 used_sectors.clear();
567 used_sectors[0] = false;
568 }
569
570 markUsed(used_sectors, le->dest, le->dest + count);
571
572 if (index == 0 && ls.sequence != 0) {
573 log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
574 if (next->magic == kMagic) {
575 next->magic = kPartialRestoreMagic;
576 }
577 }
578
579 lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
580 write(device_fd, &buffer[0], le->size);
581
582 if (index == 0) {
583 fsync(device_fd);
584 }
585}
586
Paul Lawrenced41a9392019-01-22 14:31:43 -0800587// Read from the device
588// If we are validating, the read occurs as though the relocations had happened
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800589std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
590 sector_t sector, uint32_t size, uint32_t block_size) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800591 if (!validating) {
592 std::vector<char> buffer(size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800593 lseek64(device_fd, sector * kSectorSize, SEEK_SET);
594 read(device_fd, &buffer[0], size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800595 return buffer;
596 }
597
Paul Lawrence27691c22018-11-20 14:07:59 -0800598 std::vector<char> buffer(size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800599 for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
600 auto relocation = --relocations.upper_bound(sector);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800601 lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
602 SEEK_SET);
603 read(device_fd, &buffer[i], block_size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800604 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800605
606 return buffer;
607}
608
Paul Lawrence4f13a902019-01-10 13:06:07 -0800609} // namespace
610
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800611Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800612 bool validating = true;
613 std::string action = "Validating";
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800614 int restore_count = 0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700615
Paul Lawrence27691c22018-11-20 14:07:59 -0800616 for (;;) {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800617 Relocations relocations;
618 relocations[0] = 0;
Paul Lawrence27691c22018-11-20 14:07:59 -0800619 Status status = Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700620
Paul Lawrence27691c22018-11-20 14:07:59 -0800621 LOG(INFO) << action << " checkpoint on " << blockDevice;
Nick Kraleviche7e89ac2019-03-29 16:03:51 -0700622 base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
Paul Lawrence82b35052019-04-19 14:26:39 -0700623 if (device_fd < 0) return error("Cannot open " + blockDevice);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800624
Paul Lawrencef5077682019-01-18 10:28:34 -0800625 log_sector_v1_0 original_ls;
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800626 read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
Daniel Rosenberg52985932019-03-01 22:01:22 -0800627 if (original_ls.magic == kPartialRestoreMagic) {
628 validating = false;
629 action = "Restoring";
630 } else if (original_ls.magic != kMagic) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700631 return error(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700632 }
633
Paul Lawrence4f13a902019-01-10 13:06:07 -0800634 LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700635
Paul Lawrence4f13a902019-01-10 13:06:07 -0800636 for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800637 auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
638 original_ls.block_size, original_ls.block_size);
639 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
640
641 Used_Sectors used_sectors;
642 used_sectors[0] = false;
643
644 if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700645 status = error(EINVAL, "No magic");
Paul Lawrence27691c22018-11-20 14:07:59 -0800646 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700647 }
648
Paul Lawrence4f13a902019-01-10 13:06:07 -0800649 if (ls.block_size != original_ls.block_size) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700650 status = error(EINVAL, "Block size mismatch");
Paul Lawrence4f13a902019-01-10 13:06:07 -0800651 break;
652 }
653
Paul Lawrence27691c22018-11-20 14:07:59 -0800654 if ((int)ls.sequence != sequence) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700655 status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
656 " but got " + std::to_string(ls.sequence));
Paul Lawrence27691c22018-11-20 14:07:59 -0800657 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700658 }
659
Paul Lawrence27691c22018-11-20 14:07:59 -0800660 LOG(INFO) << action << " from log sector " << ls.sequence;
Paul Lawrencef5077682019-01-18 10:28:34 -0800661 for (log_entry* le =
Daniel Rosenberg52985932019-03-01 22:01:22 -0800662 reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
663 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
Paul Lawrencef5077682019-01-18 10:28:34 -0800664 // This is very noisy - limit to DEBUG only
Paul Lawrenced41a9392019-01-22 14:31:43 -0800665 LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
666 << " to " << le->source << " with checksum " << std::hex
667 << le->checksum;
Paul Lawrencef5077682019-01-18 10:28:34 -0800668
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800669 auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800670 ls.block_size);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800671 uint32_t checksum = le->source / (ls.block_size / kSectorSize);
672 for (size_t i = 0; i < le->size; i += ls.block_size) {
673 crc32(&buffer[i], ls.block_size, &checksum);
Paul Lawrence27691c22018-11-20 14:07:59 -0800674 }
675
676 if (le->checksum && checksum != le->checksum) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700677 status = error(EINVAL, "Checksums don't match");
Paul Lawrence27691c22018-11-20 14:07:59 -0800678 break;
679 }
680
Paul Lawrenced41a9392019-01-22 14:31:43 -0800681 if (validating) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800682 relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800683 } else {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800684 restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800685 restore_count++;
686 if (restore_limit && restore_count >= restore_limit) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700687 status = error(EAGAIN, "Hit the test limit");
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800688 break;
689 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800690 }
691 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700692 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800693
694 if (!status.isOk()) {
695 if (!validating) {
696 LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
697 return status;
698 }
699
700 LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800701 auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800702 original_ls.block_size, original_ls.block_size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800703 lseek64(device_fd, 0, SEEK_SET);
704 write(device_fd, &buffer[0], original_ls.block_size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800705 return Status::ok();
706 }
707
708 if (!validating) break;
709
710 validating = false;
711 action = "Restoring";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700712 }
713
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700714 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700715}
716
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700717Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700718 std::string oldContent, newContent;
719 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700720 struct stat st;
721 int result = stat(kMetadataCPFile.c_str(), &st);
722
723 // If the file doesn't exist, we aren't managing a checkpoint retry counter
724 if (result != 0) return Status::ok();
Paul Lawrence82b35052019-04-19 14:26:39 -0700725 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
726 return error("Failed to read checkpoint file");
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700727 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700728
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700729 if (!android::base::ParseInt(retryContent, &retry))
Paul Lawrence82b35052019-04-19 14:26:39 -0700730 return error(EINVAL, "Could not parse retry count");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700731 if (retry > 0) {
732 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700733
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700734 newContent = std::to_string(retry);
735 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700736 return error("Could not write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700737 }
738 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700739}
740
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100741void cp_resetCheckpoint() {
742 std::lock_guard<std::mutex> lock(isCheckpointingLock);
743 needsCheckpointWasCalled = false;
744}
745
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700746} // namespace vold
747} // namespace android