blob: df5fc88a59319600402607df80bd3403db6c542f [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
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800297namespace {
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700298const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
299const uint32_t msleeptime_default = 1000; // 1 s
300const uint32_t max_msleeptime = 3600000; // 1 h
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800301
302const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
303const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
304
305const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
306const bool commit_on_full_default = true;
307
308static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
309 struct statvfs data;
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700310 uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
Satoshi Futenma18d10d42019-03-25 23:13:36 +0900311 uint64_t min_free_bytes =
312 GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800313 bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
314
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700315 struct timespec req;
316 req.tv_sec = msleeptime / 1000;
317 msleeptime %= 1000;
318 req.tv_nsec = msleeptime * 1000000;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800319 while (isCheckpointing) {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700320 uint64_t free_bytes = 0;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800321 if (is_fs_cp) {
322 statvfs(mnt_pnt.c_str(), &data);
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800323 free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800324 } else {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700325 std::string bow_device = fs_mgr_find_bow_device(blk_device);
326 if (!bow_device.empty()) {
327 std::string content;
328 if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
Qilin Tan5d0aaaf2020-01-02 19:07:47 +0800329 free_bytes = std::strtoull(content.c_str(), NULL, 10);
Paul Lawrence236e5e82019-06-25 14:44:33 -0700330 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800331 }
332 }
333 if (free_bytes < min_free_bytes) {
334 if (commit_on_full) {
335 LOG(INFO) << "Low space for checkpointing. Commiting changes";
336 cp_commitChanges();
337 break;
338 } else {
339 LOG(INFO) << "Low space for checkpointing. Rebooting";
340 cp_abortChanges("checkpoint,low_space", false);
341 break;
342 }
343 }
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700344 nanosleep(&req, NULL);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800345 }
346}
347
348} // namespace
349
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700350Status cp_prepareCheckpoint() {
Paul Lawrence4c757fb2019-10-23 09:36:18 -0700351 // Log to notify CTS - see b/137924328 for context
352 LOG(INFO) << "cp_prepareCheckpoint called";
Paul Lawrence1d57f682019-08-22 09:51:18 -0700353 std::lock_guard<std::mutex> lock(isCheckpointingLock);
Paul Lawrencedb086942019-02-19 14:18:54 -0800354 if (!isCheckpointing) {
355 return Status::ok();
356 }
357
Tom Cherry4c5bde22019-01-29 14:34:01 -0800358 Fstab mounts;
359 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700360 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800361 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700362
Tom Cherry4c5bde22019-01-29 14:34:01 -0800363 for (const auto& mount_rec : mounts) {
364 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700365 if (!fstab_rec) continue;
366
Tom Cherry4c5bde22019-01-29 14:34:01 -0800367 if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700368 android::base::unique_fd fd(
Tom Cherry4c5bde22019-01-29 14:34:01 -0800369 TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
Bernie Innocentiebe293a2019-03-28 15:24:30 +0900370 if (fd == -1) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800371 PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700372 continue;
373 }
374
375 struct fstrim_range range = {};
376 range.len = ULLONG_MAX;
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700377 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700378 if (ioctl(fd, FITRIM, &range)) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800379 PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700380 continue;
381 }
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700382 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
383 LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
384 << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700385
Tom Cherry4c5bde22019-01-29 14:34:01 -0800386 setBowState(mount_rec.blk_device, "1");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700387 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800388 if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
389 std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
Paul Lawrencee81f4c12019-03-29 13:06:34 -0700390 std::string(mount_rec.blk_device),
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800391 fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
392 .detach();
393 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700394 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700395 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700396}
397
398namespace {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700399const int kSectorSize = 512;
400
401typedef uint64_t sector_t;
402
403struct log_entry {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800404 sector_t source; // in sectors of size kSectorSize
405 sector_t dest; // in sectors of size kSectorSize
406 uint32_t size; // in bytes
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700407 uint32_t checksum;
408} __attribute__((packed));
409
Paul Lawrencef5077682019-01-18 10:28:34 -0800410struct log_sector_v1_0 {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700411 uint32_t magic;
Paul Lawrencef5077682019-01-18 10:28:34 -0800412 uint16_t header_version;
413 uint16_t header_size;
Paul Lawrence4f13a902019-01-10 13:06:07 -0800414 uint32_t block_size;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700415 uint32_t count;
416 uint32_t sequence;
Paul Lawrence27691c22018-11-20 14:07:59 -0800417 uint64_t sector0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700418} __attribute__((packed));
419
420// MAGIC is BOW in ascii
421const int kMagic = 0x00574f42;
Daniel Rosenberg52985932019-03-01 22:01:22 -0800422// Partially restored MAGIC is WOB in ascii
423const int kPartialRestoreMagic = 0x00424f57;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700424
425void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
426 static uint32_t table[0x100] = {
427 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
428 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
429 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
430 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
431 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
432 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
433 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
434 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
435 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
436 0xB6662D3D,
437
438 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
439 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
440 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
441 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
442 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
443 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
444 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
445 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
446 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
447 0xC0BA6CAD,
448
449 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
450 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
451 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
452 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
453 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
454 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
455 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
456 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
457 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
458 0x5BDEAE1D,
459
460 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
461 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
462 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
463 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
464 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
465 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
466 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
467 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
468 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
469 0x2D02EF8D};
470
471 for (size_t i = 0; i < n_bytes; ++i) {
472 *crc ^= ((uint8_t*)data)[i];
473 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
474 }
475}
476
Paul Lawrenced41a9392019-01-22 14:31:43 -0800477// A map of relocations.
478// The map must be initialized so that relocations[0] = 0
479// During restore, we replay the log records in reverse, copying from dest to
480// source
481// To validate, we must be able to read the 'dest' sectors as though they had
482// been copied but without actually copying. This map represents how the sectors
483// would have been moved. To read a sector s, find the index <= s and read
484// relocations[index] + s - index
485typedef std::map<sector_t, sector_t> Relocations;
Paul Lawrence27691c22018-11-20 14:07:59 -0800486
Paul Lawrenced41a9392019-01-22 14:31:43 -0800487void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
488 // Find first one we're equal to or greater than
489 auto s = --relocations.upper_bound(source);
490
491 // Take slice
492 Relocations slice;
493 slice[dest] = source - s->first + s->second;
494 ++s;
495
496 // Add rest of elements
497 for (; s != relocations.end() && s->first < source + count; ++s)
498 slice[dest - source + s->first] = s->second;
499
500 // Split range at end of dest
501 auto dest_end = --relocations.upper_bound(dest + count);
502 relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
503
504 // Remove all elements in [dest, dest + count)
505 relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
506
507 // Add new elements
508 relocations.insert(slice.begin(), slice.end());
Paul Lawrence27691c22018-11-20 14:07:59 -0800509}
510
Daniel Rosenberg52985932019-03-01 22:01:22 -0800511// A map of sectors that have been written to.
512// The final entry must always be False.
513// When we restart the restore after an interruption, we must take care that
514// when we copy from dest to source, that the block we copy to was not
515// previously copied from.
516// i e. A->B C->A; If we replay this sequence, we end up copying C->B
517// We must save our partial result whenever we finish a page, or when we copy
518// to a location that was copied from earlier (our source is an earlier dest)
519typedef std::map<sector_t, bool> Used_Sectors;
520
521bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
522 auto second_overlap = used_sectors.upper_bound(start);
523 auto first_overlap = --second_overlap;
524
525 if (first_overlap->second) {
526 return true;
527 } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
528 return true;
529 }
530 return false;
531}
532
533void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
534 auto start_pos = used_sectors.insert_or_assign(start, true).first;
535 auto end_pos = used_sectors.insert_or_assign(end, false).first;
536
537 if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
538 start_pos++;
539 }
540 if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
541 end_pos++;
542 }
543 if (start_pos->first < end_pos->first) {
544 used_sectors.erase(start_pos, end_pos);
545 }
546}
547
548// Restores the given log_entry's data from dest -> source
549// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
550void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
551 log_entry* le, std::vector<char>& buffer) {
552 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
553 uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
554 int count = (le->size - 1) / kSectorSize + 1;
555
556 if (checkCollision(used_sectors, le->source, le->source + count)) {
557 fsync(device_fd);
558 lseek64(device_fd, 0, SEEK_SET);
559 ls.count = index + 1;
560 ls.magic = kPartialRestoreMagic;
561 write(device_fd, &ls_buffer[0], ls.block_size);
562 fsync(device_fd);
563 used_sectors.clear();
564 used_sectors[0] = false;
565 }
566
567 markUsed(used_sectors, le->dest, le->dest + count);
568
569 if (index == 0 && ls.sequence != 0) {
570 log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
571 if (next->magic == kMagic) {
572 next->magic = kPartialRestoreMagic;
573 }
574 }
575
576 lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
577 write(device_fd, &buffer[0], le->size);
578
579 if (index == 0) {
580 fsync(device_fd);
581 }
582}
583
Paul Lawrenced41a9392019-01-22 14:31:43 -0800584// Read from the device
585// If we are validating, the read occurs as though the relocations had happened
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800586std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
587 sector_t sector, uint32_t size, uint32_t block_size) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800588 if (!validating) {
589 std::vector<char> buffer(size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800590 lseek64(device_fd, sector * kSectorSize, SEEK_SET);
591 read(device_fd, &buffer[0], size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800592 return buffer;
593 }
594
Paul Lawrence27691c22018-11-20 14:07:59 -0800595 std::vector<char> buffer(size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800596 for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
597 auto relocation = --relocations.upper_bound(sector);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800598 lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
599 SEEK_SET);
600 read(device_fd, &buffer[i], block_size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800601 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800602
603 return buffer;
604}
605
Paul Lawrence4f13a902019-01-10 13:06:07 -0800606} // namespace
607
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800608Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800609 bool validating = true;
610 std::string action = "Validating";
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800611 int restore_count = 0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700612
Paul Lawrence27691c22018-11-20 14:07:59 -0800613 for (;;) {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800614 Relocations relocations;
615 relocations[0] = 0;
Paul Lawrence27691c22018-11-20 14:07:59 -0800616 Status status = Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700617
Paul Lawrence27691c22018-11-20 14:07:59 -0800618 LOG(INFO) << action << " checkpoint on " << blockDevice;
Nick Kraleviche7e89ac2019-03-29 16:03:51 -0700619 base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
Paul Lawrence82b35052019-04-19 14:26:39 -0700620 if (device_fd < 0) return error("Cannot open " + blockDevice);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800621
Paul Lawrencef5077682019-01-18 10:28:34 -0800622 log_sector_v1_0 original_ls;
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800623 read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
Daniel Rosenberg52985932019-03-01 22:01:22 -0800624 if (original_ls.magic == kPartialRestoreMagic) {
625 validating = false;
626 action = "Restoring";
627 } else if (original_ls.magic != kMagic) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700628 return error(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700629 }
630
Paul Lawrence4f13a902019-01-10 13:06:07 -0800631 LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700632
Paul Lawrence4f13a902019-01-10 13:06:07 -0800633 for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800634 auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
635 original_ls.block_size, original_ls.block_size);
636 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
637
638 Used_Sectors used_sectors;
639 used_sectors[0] = false;
640
641 if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700642 status = error(EINVAL, "No magic");
Paul Lawrence27691c22018-11-20 14:07:59 -0800643 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700644 }
645
Paul Lawrence4f13a902019-01-10 13:06:07 -0800646 if (ls.block_size != original_ls.block_size) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700647 status = error(EINVAL, "Block size mismatch");
Paul Lawrence4f13a902019-01-10 13:06:07 -0800648 break;
649 }
650
Paul Lawrence27691c22018-11-20 14:07:59 -0800651 if ((int)ls.sequence != sequence) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700652 status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
653 " but got " + std::to_string(ls.sequence));
Paul Lawrence27691c22018-11-20 14:07:59 -0800654 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700655 }
656
Paul Lawrence27691c22018-11-20 14:07:59 -0800657 LOG(INFO) << action << " from log sector " << ls.sequence;
Paul Lawrencef5077682019-01-18 10:28:34 -0800658 for (log_entry* le =
Daniel Rosenberg52985932019-03-01 22:01:22 -0800659 reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
660 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
Paul Lawrencef5077682019-01-18 10:28:34 -0800661 // This is very noisy - limit to DEBUG only
Paul Lawrenced41a9392019-01-22 14:31:43 -0800662 LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
663 << " to " << le->source << " with checksum " << std::hex
664 << le->checksum;
Paul Lawrencef5077682019-01-18 10:28:34 -0800665
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800666 auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800667 ls.block_size);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800668 uint32_t checksum = le->source / (ls.block_size / kSectorSize);
669 for (size_t i = 0; i < le->size; i += ls.block_size) {
670 crc32(&buffer[i], ls.block_size, &checksum);
Paul Lawrence27691c22018-11-20 14:07:59 -0800671 }
672
673 if (le->checksum && checksum != le->checksum) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700674 status = error(EINVAL, "Checksums don't match");
Paul Lawrence27691c22018-11-20 14:07:59 -0800675 break;
676 }
677
Paul Lawrenced41a9392019-01-22 14:31:43 -0800678 if (validating) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800679 relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800680 } else {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800681 restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800682 restore_count++;
683 if (restore_limit && restore_count >= restore_limit) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700684 status = error(EAGAIN, "Hit the test limit");
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800685 break;
686 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800687 }
688 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700689 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800690
691 if (!status.isOk()) {
692 if (!validating) {
693 LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
694 return status;
695 }
696
697 LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800698 auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800699 original_ls.block_size, original_ls.block_size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800700 lseek64(device_fd, 0, SEEK_SET);
701 write(device_fd, &buffer[0], original_ls.block_size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800702 return Status::ok();
703 }
704
705 if (!validating) break;
706
707 validating = false;
708 action = "Restoring";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700709 }
710
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700711 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700712}
713
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700714Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700715 std::string oldContent, newContent;
716 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700717 struct stat st;
718 int result = stat(kMetadataCPFile.c_str(), &st);
719
720 // If the file doesn't exist, we aren't managing a checkpoint retry counter
721 if (result != 0) return Status::ok();
Paul Lawrence82b35052019-04-19 14:26:39 -0700722 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
723 return error("Failed to read checkpoint file");
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700724 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700725
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700726 if (!android::base::ParseInt(retryContent, &retry))
Paul Lawrence82b35052019-04-19 14:26:39 -0700727 return error(EINVAL, "Could not parse retry count");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700728 if (retry > 0) {
729 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700730
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700731 newContent = std::to_string(retry);
732 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700733 return error("Could not write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700734 }
735 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700736}
737
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100738void cp_resetCheckpoint() {
739 std::lock_guard<std::mutex> lock(isCheckpointingLock);
740 needsCheckpointWasCalled = false;
741}
742
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700743} // namespace vold
744} // namespace android