blob: b2b648fbee6431ca3f47e43d9ad2d89fc2dfdd2d [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.";
173 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700174 // Must take action for list of mounted checkpointed things here
175 // To do this, we walk the list of mounted file systems.
176 // But we also need to get the matching fstab entries to see
177 // the original flags
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700178 std::string err_str;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700179
Tom Cherry4c5bde22019-01-29 14:34:01 -0800180 Fstab mounts;
181 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700182 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800183 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700184
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700185 // Walk mounted file systems
Tom Cherry4c5bde22019-01-29 14:34:01 -0800186 for (const auto& mount_rec : mounts) {
187 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700188 if (!fstab_rec) continue;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700189
Tom Cherry4c5bde22019-01-29 14:34:01 -0800190 if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
191 if (fstab_rec->fs_type == "f2fs") {
192 std::string options = mount_rec.fs_options + ",checkpoint=enable";
193 if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
Daniel Rosenberg14ca4ac2019-01-24 18:23:18 -0800194 MS_REMOUNT | fstab_rec->flags, options.c_str())) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700195 return error(EINVAL, "Failed to remount");
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800196 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700197 }
Tom Cherry4c5bde22019-01-29 14:34:01 -0800198 } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
199 if (!setBowState(mount_rec.blk_device, "2"))
Paul Lawrence82b35052019-04-19 14:26:39 -0700200 return error(EINVAL, "Failed to set bow state");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700201 }
202 }
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800203 SetProperty("vold.checkpoint_committed", "1");
Daniel Rosenberg886915b2019-01-23 15:16:04 -0800204 LOG(INFO) << "Checkpoint has been committed.";
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800205 isCheckpointing = false;
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800206 if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
Paul Lawrence82b35052019-04-19 14:26:39 -0700207 return error(err_str.c_str());
208
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700209 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700210}
211
Daniel Rosenberga59e4392019-03-20 17:02:47 -0700212namespace {
213void abort_metadata_file() {
214 std::string oldContent, newContent;
215 int retry = 0;
216 struct stat st;
217 int result = stat(kMetadataCPFile.c_str(), &st);
218
219 // If the file doesn't exist, we aren't managing a checkpoint retry counter
220 if (result != 0) return;
221 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
222 PLOG(ERROR) << "Failed to read checkpoint file";
223 return;
224 }
225 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
226
227 if (!android::base::ParseInt(retryContent, &retry)) {
228 PLOG(ERROR) << "Could not parse retry count";
229 return;
230 }
231 if (retry > 0) {
232 newContent = "0";
233 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
234 PLOG(ERROR) << "Could not write checkpoint file";
235 }
236}
237} // namespace
238
239void cp_abortChanges(const std::string& message, bool retry) {
240 if (!cp_needsCheckpoint()) return;
241 if (!retry) abort_metadata_file();
242 android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700243}
244
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700245bool cp_needsRollback() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700246 std::string content;
247 bool ret;
248
249 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700250 if (ret) {
251 if (content == "0") return true;
252 if (content.substr(0, 3) == "-1 ") {
253 std::string oldSuffix = content.substr(3);
254 sp<IBootControl> module = IBootControl::getService();
255 std::string newSuffix;
256
257 if (module) {
258 auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
259 module->getSuffix(module->getCurrentSlot(), cb);
260 if (oldSuffix == newSuffix) return true;
261 }
262 }
263 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700264 return false;
265}
266
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700267bool cp_needsCheckpoint() {
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100268 std::lock_guard<std::mutex> lock(isCheckpointingLock);
269
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700270 // Make sure we only return true during boot. See b/138952436 for discussion
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100271 if (needsCheckpointWasCalled) return isCheckpointing;
272 needsCheckpointWasCalled = true;
Paul Lawrence9a6d1f72019-08-26 15:09:41 -0700273
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700274 bool ret;
275 std::string content;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700276 sp<IBootControl> module = IBootControl::getService();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700277
Daniel Rosenberg84203c12019-03-19 14:02:59 -0700278 if (isCheckpointing) return isCheckpointing;
279
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800280 if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
281 isCheckpointing = true;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700282 return true;
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800283 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700284 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergffa1bb02018-12-14 00:20:03 -0800285 if (ret) {
286 ret = content != "0";
287 isCheckpointing = ret;
288 return ret;
289 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700290 return false;
291}
292
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800293namespace {
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700294const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
295const uint32_t msleeptime_default = 1000; // 1 s
296const uint32_t max_msleeptime = 3600000; // 1 h
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800297
298const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
299const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
300
301const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
302const bool commit_on_full_default = true;
303
304static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
305 struct statvfs data;
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700306 uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
Satoshi Futenma18d10d42019-03-25 23:13:36 +0900307 uint64_t min_free_bytes =
308 GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800309 bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
310
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700311 struct timespec req;
312 req.tv_sec = msleeptime / 1000;
313 msleeptime %= 1000;
314 req.tv_nsec = msleeptime * 1000000;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800315 while (isCheckpointing) {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700316 uint64_t free_bytes = 0;
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800317 if (is_fs_cp) {
318 statvfs(mnt_pnt.c_str(), &data);
319 free_bytes = data.f_bavail * data.f_frsize;
320 } else {
Paul Lawrence236e5e82019-06-25 14:44:33 -0700321 std::string bow_device = fs_mgr_find_bow_device(blk_device);
322 if (!bow_device.empty()) {
323 std::string content;
324 if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
325 free_bytes = std::strtoul(content.c_str(), NULL, 10);
326 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800327 }
328 }
329 if (free_bytes < min_free_bytes) {
330 if (commit_on_full) {
331 LOG(INFO) << "Low space for checkpointing. Commiting changes";
332 cp_commitChanges();
333 break;
334 } else {
335 LOG(INFO) << "Low space for checkpointing. Rebooting";
336 cp_abortChanges("checkpoint,low_space", false);
337 break;
338 }
339 }
Daniel Rosenbergb7dddd02019-03-26 14:42:14 -0700340 nanosleep(&req, NULL);
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800341 }
342}
343
344} // namespace
345
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700346Status cp_prepareCheckpoint() {
Paul Lawrence4c757fb2019-10-23 09:36:18 -0700347 // Log to notify CTS - see b/137924328 for context
348 LOG(INFO) << "cp_prepareCheckpoint called";
Paul Lawrence1d57f682019-08-22 09:51:18 -0700349 std::lock_guard<std::mutex> lock(isCheckpointingLock);
Paul Lawrencedb086942019-02-19 14:18:54 -0800350 if (!isCheckpointing) {
351 return Status::ok();
352 }
353
Tom Cherry4c5bde22019-01-29 14:34:01 -0800354 Fstab mounts;
355 if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700356 return error(EINVAL, "Failed to get /proc/mounts");
Tom Cherry4c5bde22019-01-29 14:34:01 -0800357 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700358
Tom Cherry4c5bde22019-01-29 14:34:01 -0800359 for (const auto& mount_rec : mounts) {
360 const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700361 if (!fstab_rec) continue;
362
Tom Cherry4c5bde22019-01-29 14:34:01 -0800363 if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700364 android::base::unique_fd fd(
Tom Cherry4c5bde22019-01-29 14:34:01 -0800365 TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
Bernie Innocentiebe293a2019-03-28 15:24:30 +0900366 if (fd == -1) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800367 PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700368 continue;
369 }
370
371 struct fstrim_range range = {};
372 range.len = ULLONG_MAX;
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700373 nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700374 if (ioctl(fd, FITRIM, &range)) {
Tom Cherry4c5bde22019-01-29 14:34:01 -0800375 PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700376 continue;
377 }
Sandeep Patilf8da61f2019-04-15 08:45:27 -0700378 nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
379 LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
380 << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700381
Tom Cherry4c5bde22019-01-29 14:34:01 -0800382 setBowState(mount_rec.blk_device, "1");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700383 }
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800384 if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
385 std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
Paul Lawrencee81f4c12019-03-29 13:06:34 -0700386 std::string(mount_rec.blk_device),
Daniel Rosenberg8daeec02018-11-20 19:03:11 -0800387 fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
388 .detach();
389 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700390 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700391 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700392}
393
394namespace {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700395const int kSectorSize = 512;
396
397typedef uint64_t sector_t;
398
399struct log_entry {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800400 sector_t source; // in sectors of size kSectorSize
401 sector_t dest; // in sectors of size kSectorSize
402 uint32_t size; // in bytes
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700403 uint32_t checksum;
404} __attribute__((packed));
405
Paul Lawrencef5077682019-01-18 10:28:34 -0800406struct log_sector_v1_0 {
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700407 uint32_t magic;
Paul Lawrencef5077682019-01-18 10:28:34 -0800408 uint16_t header_version;
409 uint16_t header_size;
Paul Lawrence4f13a902019-01-10 13:06:07 -0800410 uint32_t block_size;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700411 uint32_t count;
412 uint32_t sequence;
Paul Lawrence27691c22018-11-20 14:07:59 -0800413 uint64_t sector0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700414} __attribute__((packed));
415
416// MAGIC is BOW in ascii
417const int kMagic = 0x00574f42;
Daniel Rosenberg52985932019-03-01 22:01:22 -0800418// Partially restored MAGIC is WOB in ascii
419const int kPartialRestoreMagic = 0x00424f57;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700420
421void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
422 static uint32_t table[0x100] = {
423 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
424 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
425 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
426 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
427 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
428 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
429 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
430 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
431 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
432 0xB6662D3D,
433
434 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
435 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
436 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
437 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
438 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
439 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
440 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
441 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
442 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
443 0xC0BA6CAD,
444
445 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
446 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
447 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
448 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
449 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
450 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
451 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
452 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
453 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
454 0x5BDEAE1D,
455
456 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
457 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
458 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
459 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
460 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
461 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
462 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
463 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
464 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
465 0x2D02EF8D};
466
467 for (size_t i = 0; i < n_bytes; ++i) {
468 *crc ^= ((uint8_t*)data)[i];
469 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
470 }
471}
472
Paul Lawrenced41a9392019-01-22 14:31:43 -0800473// A map of relocations.
474// The map must be initialized so that relocations[0] = 0
475// During restore, we replay the log records in reverse, copying from dest to
476// source
477// To validate, we must be able to read the 'dest' sectors as though they had
478// been copied but without actually copying. This map represents how the sectors
479// would have been moved. To read a sector s, find the index <= s and read
480// relocations[index] + s - index
481typedef std::map<sector_t, sector_t> Relocations;
Paul Lawrence27691c22018-11-20 14:07:59 -0800482
Paul Lawrenced41a9392019-01-22 14:31:43 -0800483void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
484 // Find first one we're equal to or greater than
485 auto s = --relocations.upper_bound(source);
486
487 // Take slice
488 Relocations slice;
489 slice[dest] = source - s->first + s->second;
490 ++s;
491
492 // Add rest of elements
493 for (; s != relocations.end() && s->first < source + count; ++s)
494 slice[dest - source + s->first] = s->second;
495
496 // Split range at end of dest
497 auto dest_end = --relocations.upper_bound(dest + count);
498 relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
499
500 // Remove all elements in [dest, dest + count)
501 relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
502
503 // Add new elements
504 relocations.insert(slice.begin(), slice.end());
Paul Lawrence27691c22018-11-20 14:07:59 -0800505}
506
Daniel Rosenberg52985932019-03-01 22:01:22 -0800507// A map of sectors that have been written to.
508// The final entry must always be False.
509// When we restart the restore after an interruption, we must take care that
510// when we copy from dest to source, that the block we copy to was not
511// previously copied from.
512// i e. A->B C->A; If we replay this sequence, we end up copying C->B
513// We must save our partial result whenever we finish a page, or when we copy
514// to a location that was copied from earlier (our source is an earlier dest)
515typedef std::map<sector_t, bool> Used_Sectors;
516
517bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
518 auto second_overlap = used_sectors.upper_bound(start);
519 auto first_overlap = --second_overlap;
520
521 if (first_overlap->second) {
522 return true;
523 } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
524 return true;
525 }
526 return false;
527}
528
529void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
530 auto start_pos = used_sectors.insert_or_assign(start, true).first;
531 auto end_pos = used_sectors.insert_or_assign(end, false).first;
532
533 if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
534 start_pos++;
535 }
536 if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
537 end_pos++;
538 }
539 if (start_pos->first < end_pos->first) {
540 used_sectors.erase(start_pos, end_pos);
541 }
542}
543
544// Restores the given log_entry's data from dest -> source
545// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
546void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
547 log_entry* le, std::vector<char>& buffer) {
548 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
549 uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
550 int count = (le->size - 1) / kSectorSize + 1;
551
552 if (checkCollision(used_sectors, le->source, le->source + count)) {
553 fsync(device_fd);
554 lseek64(device_fd, 0, SEEK_SET);
555 ls.count = index + 1;
556 ls.magic = kPartialRestoreMagic;
557 write(device_fd, &ls_buffer[0], ls.block_size);
558 fsync(device_fd);
559 used_sectors.clear();
560 used_sectors[0] = false;
561 }
562
563 markUsed(used_sectors, le->dest, le->dest + count);
564
565 if (index == 0 && ls.sequence != 0) {
566 log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
567 if (next->magic == kMagic) {
568 next->magic = kPartialRestoreMagic;
569 }
570 }
571
572 lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
573 write(device_fd, &buffer[0], le->size);
574
575 if (index == 0) {
576 fsync(device_fd);
577 }
578}
579
Paul Lawrenced41a9392019-01-22 14:31:43 -0800580// Read from the device
581// If we are validating, the read occurs as though the relocations had happened
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800582std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
583 sector_t sector, uint32_t size, uint32_t block_size) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800584 if (!validating) {
585 std::vector<char> buffer(size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800586 lseek64(device_fd, sector * kSectorSize, SEEK_SET);
587 read(device_fd, &buffer[0], size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800588 return buffer;
589 }
590
Paul Lawrence27691c22018-11-20 14:07:59 -0800591 std::vector<char> buffer(size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800592 for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
593 auto relocation = --relocations.upper_bound(sector);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800594 lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
595 SEEK_SET);
596 read(device_fd, &buffer[i], block_size);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800597 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800598
599 return buffer;
600}
601
Paul Lawrence4f13a902019-01-10 13:06:07 -0800602} // namespace
603
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800604Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
Paul Lawrence27691c22018-11-20 14:07:59 -0800605 bool validating = true;
606 std::string action = "Validating";
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800607 int restore_count = 0;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700608
Paul Lawrence27691c22018-11-20 14:07:59 -0800609 for (;;) {
Paul Lawrenced41a9392019-01-22 14:31:43 -0800610 Relocations relocations;
611 relocations[0] = 0;
Paul Lawrence27691c22018-11-20 14:07:59 -0800612 Status status = Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700613
Paul Lawrence27691c22018-11-20 14:07:59 -0800614 LOG(INFO) << action << " checkpoint on " << blockDevice;
Nick Kraleviche7e89ac2019-03-29 16:03:51 -0700615 base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
Paul Lawrence82b35052019-04-19 14:26:39 -0700616 if (device_fd < 0) return error("Cannot open " + blockDevice);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800617
Paul Lawrencef5077682019-01-18 10:28:34 -0800618 log_sector_v1_0 original_ls;
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800619 read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
Daniel Rosenberg52985932019-03-01 22:01:22 -0800620 if (original_ls.magic == kPartialRestoreMagic) {
621 validating = false;
622 action = "Restoring";
623 } else if (original_ls.magic != kMagic) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700624 return error(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700625 }
626
Paul Lawrence4f13a902019-01-10 13:06:07 -0800627 LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700628
Paul Lawrence4f13a902019-01-10 13:06:07 -0800629 for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800630 auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
631 original_ls.block_size, original_ls.block_size);
632 log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
633
634 Used_Sectors used_sectors;
635 used_sectors[0] = false;
636
637 if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700638 status = error(EINVAL, "No magic");
Paul Lawrence27691c22018-11-20 14:07:59 -0800639 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700640 }
641
Paul Lawrence4f13a902019-01-10 13:06:07 -0800642 if (ls.block_size != original_ls.block_size) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700643 status = error(EINVAL, "Block size mismatch");
Paul Lawrence4f13a902019-01-10 13:06:07 -0800644 break;
645 }
646
Paul Lawrence27691c22018-11-20 14:07:59 -0800647 if ((int)ls.sequence != sequence) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700648 status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
649 " but got " + std::to_string(ls.sequence));
Paul Lawrence27691c22018-11-20 14:07:59 -0800650 break;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700651 }
652
Paul Lawrence27691c22018-11-20 14:07:59 -0800653 LOG(INFO) << action << " from log sector " << ls.sequence;
Paul Lawrencef5077682019-01-18 10:28:34 -0800654 for (log_entry* le =
Daniel Rosenberg52985932019-03-01 22:01:22 -0800655 reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
656 le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
Paul Lawrencef5077682019-01-18 10:28:34 -0800657 // This is very noisy - limit to DEBUG only
Paul Lawrenced41a9392019-01-22 14:31:43 -0800658 LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
659 << " to " << le->source << " with checksum " << std::hex
660 << le->checksum;
Paul Lawrencef5077682019-01-18 10:28:34 -0800661
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800662 auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800663 ls.block_size);
Paul Lawrence4f13a902019-01-10 13:06:07 -0800664 uint32_t checksum = le->source / (ls.block_size / kSectorSize);
665 for (size_t i = 0; i < le->size; i += ls.block_size) {
666 crc32(&buffer[i], ls.block_size, &checksum);
Paul Lawrence27691c22018-11-20 14:07:59 -0800667 }
668
669 if (le->checksum && checksum != le->checksum) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700670 status = error(EINVAL, "Checksums don't match");
Paul Lawrence27691c22018-11-20 14:07:59 -0800671 break;
672 }
673
Paul Lawrenced41a9392019-01-22 14:31:43 -0800674 if (validating) {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800675 relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
Paul Lawrenced41a9392019-01-22 14:31:43 -0800676 } else {
Daniel Rosenberg52985932019-03-01 22:01:22 -0800677 restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800678 restore_count++;
679 if (restore_limit && restore_count >= restore_limit) {
Paul Lawrence82b35052019-04-19 14:26:39 -0700680 status = error(EAGAIN, "Hit the test limit");
Daniel Rosenbergdda59812019-03-06 17:45:17 -0800681 break;
682 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800683 }
684 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700685 }
Paul Lawrence27691c22018-11-20 14:07:59 -0800686
687 if (!status.isOk()) {
688 if (!validating) {
689 LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
690 return status;
691 }
692
693 LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800694 auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
Paul Lawrenced41a9392019-01-22 14:31:43 -0800695 original_ls.block_size, original_ls.block_size);
Daniel Rosenberg8271ae92019-03-04 21:46:31 -0800696 lseek64(device_fd, 0, SEEK_SET);
697 write(device_fd, &buffer[0], original_ls.block_size);
Paul Lawrence27691c22018-11-20 14:07:59 -0800698 return Status::ok();
699 }
700
701 if (!validating) break;
702
703 validating = false;
704 action = "Restoring";
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700705 }
706
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700707 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700708}
709
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700710Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700711 std::string oldContent, newContent;
712 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700713 struct stat st;
714 int result = stat(kMetadataCPFile.c_str(), &st);
715
716 // If the file doesn't exist, we aren't managing a checkpoint retry counter
717 if (result != 0) return Status::ok();
Paul Lawrence82b35052019-04-19 14:26:39 -0700718 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
719 return error("Failed to read checkpoint file");
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700720 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700721
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700722 if (!android::base::ParseInt(retryContent, &retry))
Paul Lawrence82b35052019-04-19 14:26:39 -0700723 return error(EINVAL, "Could not parse retry count");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700724 if (retry > 0) {
725 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700726
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700727 newContent = std::to_string(retry);
728 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
Paul Lawrence82b35052019-04-19 14:26:39 -0700729 return error("Could not write checkpoint file");
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700730 }
731 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700732}
733
Nikita Ioffea5798fc2019-10-11 16:38:21 +0100734void cp_resetCheckpoint() {
735 std::lock_guard<std::mutex> lock(isCheckpointingLock);
736 needsCheckpointWasCalled = false;
737}
738
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700739} // namespace vold
740} // namespace android