blob: 61035e572c4d4f3462a25672f11baa47330bc2cf [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;
50using android::fs_mgr::ReadDefaultFstab;
51using android::fs_mgr::ReadFstabFromFile;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070052using android::hardware::hidl_string;
53using android::hardware::boot::V1_0::BoolResult;
Daniel Rosenberg886915b2019-01-23 15:16:04 -080054using android::hardware::boot::V1_0::CommandResult;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070055using android::hardware::boot::V1_0::IBootControl;
56using android::hardware::boot::V1_0::Slot;
57
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070058namespace android {
59namespace vold {
60
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070061namespace {
62const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
63
Paul Lawrence82b35052019-04-19 14:26:39 -070064binder::Status error(const std::string& msg) {
65 PLOG(ERROR) << msg;
66 return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
67}
68
69binder::Status error(int error, const std::string& msg) {
70 LOG(ERROR) << msg;
71 return binder::Status::fromServiceSpecificError(error, String8(msg.c_str()));
72}
73
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070074bool setBowState(std::string const& block_device, std::string const& state) {
Paul Lawrence236e5e82019-06-25 14:44:33 -070075 std::string bow_device = fs_mgr_find_bow_device(block_device);
76 if (bow_device.empty()) return false;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070077
Paul Lawrence236e5e82019-06-25 14:44:33 -070078 if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
79 PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070080 return false;
81 }
82
83 return true;
84}
85
86} // namespace
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070087
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080088Status cp_supportsCheckpoint(bool& result) {
89 result = false;
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080090
Tom Cherry4c5bde22019-01-29 14:34:01 -080091 for (const auto& entry : fstab_default) {
92 if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
Daniel Rosenberg9b667fb2019-01-22 17:27:25 -080093 result = true;
94 return Status::ok();
95 }
96 }
97 return Status::ok();
98}
99
Paul Lawrencec5c79c52019-03-18 13:36:40 -0700100Status cp_supportsBlockCheckpoint(bool& result) {
101 result = false;
102
103 for (const auto& entry : fstab_default) {
104 if (entry.fs_mgr_flags.checkpoint_blk) {
105 result = true;
106 return Status::ok();
107 }
108 }
109 return Status::ok();
110}
111
112Status cp_supportsFileCheckpoint(bool& result) {
113 result = false;
114
115 for (const auto& entry : fstab_default) {
116 if (entry.fs_mgr_flags.checkpoint_fs) {
117 result = true;
118 return Status::ok();
119 }
120 }
121 return Status::ok();
122}
123
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700124Status cp_startCheckpoint(int retry) {
Paul Lawrencec2a145f2019-05-15 09:42:04 -0700125 bool result;
126 if (!cp_supportsCheckpoint(result).isOk() || !result)
127 return error(ENOTSUP, "Checkpoints not supported");
128
Paul Lawrence82b35052019-04-19 14:26:39 -0700129 if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700130 std::string content = std::to_string(retry + 1);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700131 if (retry == -1) {
132 sp<IBootControl> module = IBootControl::getService();
133 if (module) {
134 std::string suffix;
135 auto cb = [&suffix](hidl_string s) { suffix = s; };
136 if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
137 }
138 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700139 if (!android::base::WriteStringToFile(content, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700140 return error("Failed to write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700141 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700142}
143
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800144namespace {
145
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800146volatile bool isCheckpointing = false;
Paul Lawrence1d57f682019-08-22 09:51:18 -0700147
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100148volatile bool needsCheckpointWasCalled = false;
149
150// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
151// of isCheckpointing
Paul Lawrence1d57f682019-08-22 09:51:18 -0700152std::mutex isCheckpointingLock;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800153}
154
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700155Status cp_commitChanges() {
Paul Lawrence1d57f682019-08-22 09:51:18 -0700156 std::lock_guard<std::mutex> lock(isCheckpointingLock);
157
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800158 if (!isCheckpointing) {
159 return Status::ok();
160 }
Paul Lawrencea7972dc2019-06-12 12:03:01 -0700161 if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
162 LOG(WARNING)
163 << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
164 return Status::ok();
165 }
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800166 sp<IBootControl> module = IBootControl::getService();
167 if (module) {
168 CommandResult cr;
169 module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
Paul Lawrence82b35052019-04-19 14:26:39 -0700170 if (!cr.success)
171 return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800172 LOG(INFO) << "Marked slot as booted successfully.";
Tianjie Xu09de0ff2019-11-15 14:05:29 -0800173 // Clears the warm reset flag for next reboot.
174 if (!SetProperty("ota.warm_reset", "0")) {
175 LOG(WARNING) << "Failed to reset the warm reset flag";
176 }
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800177 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700178 // Must take action for list of mounted checkpointed things here
179 // To do this, we walk the list of mounted file systems.
180 // But we also need to get the matching fstab entries to see
181 // the original flags
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700182 std::string err_str;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700183
Tom Cherry4c5bde22019-01-29 14:34:01 -0800184 Fstab mounts;
185 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700186 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800187 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700188
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700189 // Walk mounted file systems
Tom Cherry4c5bde22019-01-29 14:34:01 -0800190 for (const auto& mount_rec : mounts) {
191 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700192 if (!fstab_rec) continue;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700193
Tom Cherry4c5bde22019-01-29 14:34:01 -0800194 if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
195 if (fstab_rec->fs_type == "f2fs") {
196 std::string options = mount_rec.fs_options + ",checkpoint=enable";
197 if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
Daniel Rosenberg14ca4ac2019-01-24 18:23:18 -0800198 MS_REMOUNT | fstab_rec->flags, options.c_str())) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700199 return error(EINVAL, "Failed to remount");
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800200 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700201 }
Tom Cherry4c5bde22019-01-29 14:34:01 -0800202 } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
203 if (!setBowState(mount_rec.blk_device, "2"))
Paul Lawrence82b35052019-04-19 14:26:39 -0700204 return error(EINVAL, "Failed to set bow state");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700205 }
206 }
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800207 SetProperty("vold.checkpoint_committed", "1");
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800208 LOG(INFO) << "Checkpoint has been committed.";
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800209 isCheckpointing = false;
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800210 if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
Paul Lawrence82b35052019-04-19 14:26:39 -0700211 return error(err_str.c_str());
212
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700213 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700214}
215
Daniel Rosenberga59e4392019-03-20 17:02:47 -0700216namespace {
217void abort_metadata_file() {
218 std::string oldContent, newContent;
219 int retry = 0;
220 struct stat st;
221 int result = stat(kMetadataCPFile.c_str(), &st);
222
223 // If the file doesn't exist, we aren't managing a checkpoint retry counter
224 if (result != 0) return;
225 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
226 PLOG(ERROR) << "Failed to read checkpoint file";
227 return;
228 }
229 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
230
231 if (!android::base::ParseInt(retryContent, &retry)) {
232 PLOG(ERROR) << "Could not parse retry count";
233 return;
234 }
235 if (retry > 0) {
236 newContent = "0";
237 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
238 PLOG(ERROR) << "Could not write checkpoint file";
239 }
240}
241} // namespace
242
243void cp_abortChanges(const std::string& message, bool retry) {
244 if (!cp_needsCheckpoint()) return;
245 if (!retry) abort_metadata_file();
246 android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700247}
248
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700249bool cp_needsRollback() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700250 std::string content;
251 bool ret;
252
253 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700254 if (ret) {
255 if (content == "0") return true;
256 if (content.substr(0, 3) == "-1 ") {
257 std::string oldSuffix = content.substr(3);
258 sp<IBootControl> module = IBootControl::getService();
259 std::string newSuffix;
260
261 if (module) {
262 auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
263 module->getSuffix(module->getCurrentSlot(), cb);
264 if (oldSuffix == newSuffix) return true;
265 }
266 }
267 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700268 return false;
269}
270
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700271bool cp_needsCheckpoint() {
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100272 std::lock_guard<std::mutex> lock(isCheckpointingLock);
273
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700274 // Make sure we only return true during boot. See b/138952436 for discussion
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100275 if (needsCheckpointWasCalled) return isCheckpointing;
276 needsCheckpointWasCalled = true;
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700277
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700278 bool ret;
279 std::string content;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700280 sp<IBootControl> module = IBootControl::getService();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700281
Daniel Rosenberg84203c12019-03-19 14:02:59 -0700282 if (isCheckpointing) return isCheckpointing;
283
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800284 if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
285 isCheckpointing = true;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700286 return true;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800287 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700288 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800289 if (ret) {
290 ret = content != "0";
291 isCheckpointing = ret;
292 return ret;
293 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700294 return false;
295}
296
David Anderson7e07c532020-06-10 23:51:17 -0700297bool cp_isCheckpointing() {
298 return isCheckpointing;
299}
300
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800301namespace {
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700302const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
303const uint32_t msleeptime_default = 1000; // 1 s
304const uint32_t max_msleeptime = 3600000; // 1 h
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800305
306const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
307const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
308
309const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
310const bool commit_on_full_default = true;
311
312static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
313 struct statvfs data;
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700314 uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
Satoshi Futenma18d10d42019-03-25 23:13:36 +0900315 uint64_t min_free_bytes =
316 GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800317 bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
318
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700319 struct timespec req;
320 req.tv_sec = msleeptime / 1000;
321 msleeptime %= 1000;
322 req.tv_nsec = msleeptime * 1000000;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800323 while (isCheckpointing) {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700324 uint64_t free_bytes = 0;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800325 if (is_fs_cp) {
326 statvfs(mnt_pnt.c_str(), &data);
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800327 free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800328 } else {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700329 std::string bow_device = fs_mgr_find_bow_device(blk_device);
330 if (!bow_device.empty()) {
331 std::string content;
332 if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800333 free_bytes = std::strtoull(content.c_str(), NULL, 10);
Paul Lawrence236e5e82019-06-25 14:44:33 -0700334 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800335 }
336 }
337 if (free_bytes < min_free_bytes) {
338 if (commit_on_full) {
339 LOG(INFO) << "Low space for checkpointing. Commiting changes";
340 cp_commitChanges();
341 break;
342 } else {
343 LOG(INFO) << "Low space for checkpointing. Rebooting";
344 cp_abortChanges("checkpoint,low_space", false);
345 break;
346 }
347 }
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700348 nanosleep(&req, NULL);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800349 }
350}
351
352} // namespace
353
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700354Status cp_prepareCheckpoint() {
Paul Lawrence4c757fb2019-10-23 09:36:18 -0700355 // Log to notify CTS - see b/137924328 for context
356 LOG(INFO) << "cp_prepareCheckpoint called";
Paul Lawrence1d57f682019-08-22 09:51:18 -0700357 std::lock_guard<std::mutex> lock(isCheckpointingLock);
Paul Lawrencedb086942019-02-19 14:18:54 -0800358 if (!isCheckpointing) {
359 return Status::ok();
360 }
361
Tom Cherry4c5bde22019-01-29 14:34:01 -0800362 Fstab mounts;
363 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700364 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800365 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700366
Tom Cherry4c5bde22019-01-29 14:34:01 -0800367 for (const auto& mount_rec : mounts) {
368 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700369 if (!fstab_rec) continue;
370
Tom Cherry4c5bde22019-01-29 14:34:01 -0800371 if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700372 android::base::unique_fd fd(
Tom Cherry4c5bde22019-01-29 14:34:01 -0800373 TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
Bernie Innocentiebe293a2019-03-28 15:24:30 +0900374 if (fd == -1) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800375 PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700376 continue;
377 }
378
379 struct fstrim_range range = {};
380 range.len = ULLONG_MAX;
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700381 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700382 if (ioctl(fd, FITRIM, &range)) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800383 PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700384 continue;
385 }
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700386 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
387 LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
388 << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700389
Tom Cherry4c5bde22019-01-29 14:34:01 -0800390 setBowState(mount_rec.blk_device, "1");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700391 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800392 if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
393 std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
Paul Lawrencee81f4c12019-03-29 13:06:34 -0700394 std::string(mount_rec.blk_device),
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800395 fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
396 .detach();
397 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700398 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700399 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700400}
401
402namespace {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700403const int kSectorSize = 512;
404
405typedef uint64_t sector_t;
406
407struct log_entry {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800408 sector_t source; // in sectors of size kSectorSize
409 sector_t dest; // in sectors of size kSectorSize
410 uint32_t size; // in bytes
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700411 uint32_t checksum;
412} __attribute__((packed));
413
Paul Lawrencef5077682019-01-18 10:28:34 -0800414struct log_sector_v1_0 {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700415 uint32_t magic;
Paul Lawrencef5077682019-01-18 10:28:34 -0800416 uint16_t header_version;
417 uint16_t header_size;
Paul Lawrence4f13a902019-01-10 13:06:07 -0800418 uint32_t block_size;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700419 uint32_t count;
420 uint32_t sequence;
Paul Lawrence27691c22018-11-20 14:07:59 -0800421 uint64_t sector0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700422} __attribute__((packed));
423
424// MAGIC is BOW in ascii
425const int kMagic = 0x00574f42;
Daniel Rosenberg52985932019-03-01 22:01:22 -0800426// Partially restored MAGIC is WOB in ascii
427const int kPartialRestoreMagic = 0x00424f57;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700428
429void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
430 static uint32_t table[0x100] = {
431 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
432 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
433 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
434 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
435 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
436 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
437 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
438 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
439 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
440 0xB6662D3D,
441
442 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
443 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
444 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
445 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
446 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
447 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
448 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
449 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
450 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
451 0xC0BA6CAD,
452
453 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
454 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
455 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
456 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
457 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
458 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
459 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
460 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
461 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
462 0x5BDEAE1D,
463
464 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
465 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
466 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
467 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
468 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
469 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
470 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
471 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
472 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
473 0x2D02EF8D};
474
475 for (size_t i = 0; i < n_bytes; ++i) {
476 *crc ^= ((uint8_t*)data)[i];
477 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
478 }
479}
480
Paul Lawrenced41a9392019-01-22 14:31:43 -0800481// A map of relocations.
482// The map must be initialized so that relocations[0] = 0
483// During restore, we replay the log records in reverse, copying from dest to
484// source
485// To validate, we must be able to read the 'dest' sectors as though they had
486// been copied but without actually copying. This map represents how the sectors
487// would have been moved. To read a sector s, find the index <= s and read
488// relocations[index] + s - index
489typedef std::map<sector_t, sector_t> Relocations;
Paul Lawrence27691c22018-11-20 14:07:59 -0800490
Paul Lawrenced41a9392019-01-22 14:31:43 -0800491void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
492 // Find first one we're equal to or greater than
493 auto s = --relocations.upper_bound(source);
494
495 // Take slice
496 Relocations slice;
497 slice[dest] = source - s->first + s->second;
498 ++s;
499
500 // Add rest of elements
501 for (; s != relocations.end() && s->first < source + count; ++s)
502 slice[dest - source + s->first] = s->second;
503
504 // Split range at end of dest
505 auto dest_end = --relocations.upper_bound(dest + count);
506 relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
507
508 // Remove all elements in [dest, dest + count)
509 relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
510
511 // Add new elements
512 relocations.insert(slice.begin(), slice.end());
Paul Lawrence27691c22018-11-20 14:07:59 -0800513}
514
Daniel Rosenberg52985932019-03-01 22:01:22 -0800515// A map of sectors that have been written to.
516// The final entry must always be False.
517// When we restart the restore after an interruption, we must take care that
518// when we copy from dest to source, that the block we copy to was not
519// previously copied from.
520// i e. A->B C->A; If we replay this sequence, we end up copying C->B
521// We must save our partial result whenever we finish a page, or when we copy
522// to a location that was copied from earlier (our source is an earlier dest)
523typedef std::map<sector_t, bool> Used_Sectors;
524
525bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
526 auto second_overlap = used_sectors.upper_bound(start);
527 auto first_overlap = --second_overlap;
528
529 if (first_overlap->second) {
530 return true;
531 } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
532 return true;
533 }
534 return false;
535}
536
537void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
538 auto start_pos = used_sectors.insert_or_assign(start, true).first;
539 auto end_pos = used_sectors.insert_or_assign(end, false).first;
540
541 if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
542 start_pos++;
543 }
544 if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
545 end_pos++;
546 }
547 if (start_pos->first < end_pos->first) {
548 used_sectors.erase(start_pos, end_pos);
549 }
550}
551
552// Restores the given log_entry's data from dest -> source
553// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
554void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
555 log_entry* le, std::vector<char>& buffer) {
556 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
557 uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
558 int count = (le->size - 1) / kSectorSize + 1;
559
560 if (checkCollision(used_sectors, le->source, le->source + count)) {
561 fsync(device_fd);
562 lseek64(device_fd, 0, SEEK_SET);
563 ls.count = index + 1;
564 ls.magic = kPartialRestoreMagic;
565 write(device_fd, &ls_buffer[0], ls.block_size);
566 fsync(device_fd);
567 used_sectors.clear();
568 used_sectors[0] = false;
569 }
570
571 markUsed(used_sectors, le->dest, le->dest + count);
572
573 if (index == 0 && ls.sequence != 0) {
574 log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
575 if (next->magic == kMagic) {
576 next->magic = kPartialRestoreMagic;
577 }
578 }
579
580 lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
581 write(device_fd, &buffer[0], le->size);
582
583 if (index == 0) {
584 fsync(device_fd);
585 }
586}
587
Paul Lawrenced41a9392019-01-22 14:31:43 -0800588// Read from the device
589// If we are validating, the read occurs as though the relocations had happened
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800590std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
591 sector_t sector, uint32_t size, uint32_t block_size) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800592 if (!validating) {
593 std::vector<char> buffer(size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800594 lseek64(device_fd, sector * kSectorSize, SEEK_SET);
595 read(device_fd, &buffer[0], size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800596 return buffer;
597 }
598
Paul Lawrence27691c22018-11-20 14:07:59 -0800599 std::vector<char> buffer(size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800600 for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
601 auto relocation = --relocations.upper_bound(sector);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800602 lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
603 SEEK_SET);
604 read(device_fd, &buffer[i], block_size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800605 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800606
607 return buffer;
608}
609
Paul Lawrence4f13a902019-01-10 13:06:07 -0800610} // namespace
611
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800612Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800613 bool validating = true;
614 std::string action = "Validating";
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800615 int restore_count = 0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700616
Paul Lawrence27691c22018-11-20 14:07:59 -0800617 for (;;) {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800618 Relocations relocations;
619 relocations[0] = 0;
Paul Lawrence27691c22018-11-20 14:07:59 -0800620 Status status = Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700621
Paul Lawrence27691c22018-11-20 14:07:59 -0800622 LOG(INFO) << action << " checkpoint on " << blockDevice;
Nick Kraleviche7e89ac2019-03-29 16:03:51 -0700623 base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
Paul Lawrence82b35052019-04-19 14:26:39 -0700624 if (device_fd < 0) return error("Cannot open " + blockDevice);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800625
Paul Lawrencef5077682019-01-18 10:28:34 -0800626 log_sector_v1_0 original_ls;
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800627 read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
Daniel Rosenberg52985932019-03-01 22:01:22 -0800628 if (original_ls.magic == kPartialRestoreMagic) {
629 validating = false;
630 action = "Restoring";
631 } else if (original_ls.magic != kMagic) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700632 return error(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700633 }
634
Paul Lawrence4f13a902019-01-10 13:06:07 -0800635 LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700636
Paul Lawrence4f13a902019-01-10 13:06:07 -0800637 for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800638 auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
639 original_ls.block_size, original_ls.block_size);
640 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
641
642 Used_Sectors used_sectors;
643 used_sectors[0] = false;
644
645 if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700646 status = error(EINVAL, "No magic");
Paul Lawrence27691c22018-11-20 14:07:59 -0800647 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700648 }
649
Paul Lawrence4f13a902019-01-10 13:06:07 -0800650 if (ls.block_size != original_ls.block_size) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700651 status = error(EINVAL, "Block size mismatch");
Paul Lawrence4f13a902019-01-10 13:06:07 -0800652 break;
653 }
654
Paul Lawrence27691c22018-11-20 14:07:59 -0800655 if ((int)ls.sequence != sequence) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700656 status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
657 " but got " + std::to_string(ls.sequence));
Paul Lawrence27691c22018-11-20 14:07:59 -0800658 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700659 }
660
Paul Lawrence27691c22018-11-20 14:07:59 -0800661 LOG(INFO) << action << " from log sector " << ls.sequence;
Paul Lawrencef5077682019-01-18 10:28:34 -0800662 for (log_entry* le =
Daniel Rosenberg52985932019-03-01 22:01:22 -0800663 reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
664 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
Paul Lawrencef5077682019-01-18 10:28:34 -0800665 // This is very noisy - limit to DEBUG only
Paul Lawrenced41a9392019-01-22 14:31:43 -0800666 LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
667 << " to " << le->source << " with checksum " << std::hex
668 << le->checksum;
Paul Lawrencef5077682019-01-18 10:28:34 -0800669
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800670 auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800671 ls.block_size);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800672 uint32_t checksum = le->source / (ls.block_size / kSectorSize);
673 for (size_t i = 0; i < le->size; i += ls.block_size) {
674 crc32(&buffer[i], ls.block_size, &checksum);
Paul Lawrence27691c22018-11-20 14:07:59 -0800675 }
676
677 if (le->checksum && checksum != le->checksum) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700678 status = error(EINVAL, "Checksums don't match");
Paul Lawrence27691c22018-11-20 14:07:59 -0800679 break;
680 }
681
Paul Lawrenced41a9392019-01-22 14:31:43 -0800682 if (validating) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800683 relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800684 } else {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800685 restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800686 restore_count++;
687 if (restore_limit && restore_count >= restore_limit) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700688 status = error(EAGAIN, "Hit the test limit");
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800689 break;
690 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800691 }
692 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700693 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800694
695 if (!status.isOk()) {
696 if (!validating) {
697 LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
698 return status;
699 }
700
701 LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800702 auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800703 original_ls.block_size, original_ls.block_size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800704 lseek64(device_fd, 0, SEEK_SET);
705 write(device_fd, &buffer[0], original_ls.block_size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800706 return Status::ok();
707 }
708
709 if (!validating) break;
710
711 validating = false;
712 action = "Restoring";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700713 }
714
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700715 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700716}
717
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700718Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700719 std::string oldContent, newContent;
720 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700721 struct stat st;
722 int result = stat(kMetadataCPFile.c_str(), &st);
723
724 // If the file doesn't exist, we aren't managing a checkpoint retry counter
725 if (result != 0) return Status::ok();
Paul Lawrence82b35052019-04-19 14:26:39 -0700726 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
727 return error("Failed to read checkpoint file");
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700728 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700729
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700730 if (!android::base::ParseInt(retryContent, &retry))
Paul Lawrence82b35052019-04-19 14:26:39 -0700731 return error(EINVAL, "Could not parse retry count");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700732 if (retry > 0) {
733 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700734
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700735 newContent = std::to_string(retry);
736 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700737 return error("Could not write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700738 }
739 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700740}
741
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100742void cp_resetCheckpoint() {
743 std::lock_guard<std::mutex> lock(isCheckpointingLock);
744 needsCheckpointWasCalled = false;
745}
746
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700747} // namespace vold
748} // namespace android