blob: e238acf91d5f33cd62dbb65652efd8ccdad9a7e1 [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"
19
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070020#include <fstream>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070021#include <list>
Paul Lawrence20400892018-10-03 14:14:52 -070022#include <memory>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070023#include <string>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070024#include <vector>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <android-base/parseint.h>
29#include <android-base/unique_fd.h>
Daniel Rosenbergd3992492018-10-02 17:40:44 -070030#include <android/hardware/boot/1.0/IBootControl.h>
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070031#include <cutils/android_reboot.h>
32#include <fcntl.h>
33#include <fs_mgr.h>
34#include <linux/fs.h>
35#include <mntent.h>
36#include <sys/mount.h>
37#include <sys/stat.h>
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070038
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070039using android::binder::Status;
Daniel Rosenbergd3992492018-10-02 17:40:44 -070040using android::hardware::hidl_string;
41using android::hardware::boot::V1_0::BoolResult;
42using android::hardware::boot::V1_0::IBootControl;
43using android::hardware::boot::V1_0::Slot;
44
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070045namespace android {
46namespace vold {
47
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070048namespace {
49const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
50
51bool setBowState(std::string const& block_device, std::string const& state) {
52 if (block_device.substr(0, 5) != "/dev/") {
53 LOG(ERROR) << "Expected block device, got " << block_device;
54 return false;
55 }
56
57 std::string state_filename = std::string("/sys/") + block_device.substr(5) + "/bow/state";
58 if (!android::base::WriteStringToFile(state, state_filename)) {
59 PLOG(ERROR) << "Failed to write to file " << state_filename;
60 return false;
61 }
62
63 return true;
64}
65
66} // namespace
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070067
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070068Status cp_startCheckpoint(int retry) {
69 if (retry < -1) return Status::fromExceptionCode(EINVAL, "Retry count must be more than -1");
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -070070 std::string content = std::to_string(retry + 1);
Daniel Rosenbergd3992492018-10-02 17:40:44 -070071 if (retry == -1) {
72 sp<IBootControl> module = IBootControl::getService();
73 if (module) {
74 std::string suffix;
75 auto cb = [&suffix](hidl_string s) { suffix = s; };
76 if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
77 }
78 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070079 if (!android::base::WriteStringToFile(content, kMetadataCPFile))
80 return Status::fromExceptionCode(errno, "Failed to write checkpoint file");
81 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070082}
83
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070084Status cp_commitChanges() {
Daniel Rosenberg4b86df12018-11-08 22:18:37 -080085 if (!cp_needsCheckpoint()) return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070086 // Must take action for list of mounted checkpointed things here
87 // To do this, we walk the list of mounted file systems.
88 // But we also need to get the matching fstab entries to see
89 // the original flags
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070090 std::string err_str;
Paul Lawrence20400892018-10-03 14:14:52 -070091 auto fstab_default = std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>{
92 fs_mgr_read_fstab_default(), fs_mgr_free_fstab};
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070093 if (!fstab_default) return Status::fromExceptionCode(EINVAL, "Failed to get fstab");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070094
Paul Lawrence20400892018-10-03 14:14:52 -070095 auto mounts = std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>{
96 fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab};
Daniel Rosenberg73680ec2018-10-10 18:52:04 -070097 if (!mounts) return Status::fromExceptionCode(EINVAL, "Failed to get /proc/mounts");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -070098
Paul Lawrence1abb2fe2018-09-21 10:49:57 -070099 // Walk mounted file systems
100 for (int i = 0; i < mounts->num_entries; ++i) {
101 const fstab_rec* mount_rec = &mounts->recs[i];
Paul Lawrence20400892018-10-03 14:14:52 -0700102 const fstab_rec* fstab_rec =
103 fs_mgr_get_entry_for_mount_point(fstab_default.get(), mount_rec->mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700104 if (!fstab_rec) continue;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700105
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700106 if (fs_mgr_is_checkpoint_fs(fstab_rec)) {
107 if (!strcmp(fstab_rec->fs_type, "f2fs")) {
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800108 if (mount(mount_rec->blk_device, mount_rec->mount_point, "none",
109 MS_REMOUNT | fstab_rec->flags, "checkpoint=enable")) {
110 return Status::fromExceptionCode(EINVAL, "Failed to remount");
111 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700112 }
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700113 } else if (fs_mgr_is_checkpoint_blk(fstab_rec)) {
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800114 if (!setBowState(mount_rec->blk_device, "2"))
115 return Status::fromExceptionCode(EINVAL, "Failed to set bow state");
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700116 }
117 }
Daniel Rosenberg4b86df12018-11-08 22:18:37 -0800118 if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700119 return Status::fromExceptionCode(errno, err_str.c_str());
120 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700121}
122
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700123Status cp_abortChanges() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700124 android_reboot(ANDROID_RB_RESTART2, 0, nullptr);
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700125 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700126}
127
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700128bool cp_needsRollback() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700129 std::string content;
130 bool ret;
131
132 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700133 if (ret) {
134 if (content == "0") return true;
135 if (content.substr(0, 3) == "-1 ") {
136 std::string oldSuffix = content.substr(3);
137 sp<IBootControl> module = IBootControl::getService();
138 std::string newSuffix;
139
140 if (module) {
141 auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
142 module->getSuffix(module->getCurrentSlot(), cb);
143 if (oldSuffix == newSuffix) return true;
144 }
145 }
146 }
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700147 return false;
148}
149
Daniel Rosenberg80d1ca52018-10-09 19:26:57 -0700150bool cp_needsCheckpoint() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700151 bool ret;
152 std::string content;
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700153 sp<IBootControl> module = IBootControl::getService();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700154
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700155 if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE)
156 return true;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700157 ret = android::base::ReadFileToString(kMetadataCPFile, &content);
158 if (ret) return content != "0";
159 return false;
160}
161
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700162Status cp_prepareCheckpoint() {
Paul Lawrence20400892018-10-03 14:14:52 -0700163 auto fstab_default = std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>{
164 fs_mgr_read_fstab_default(), fs_mgr_free_fstab};
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700165 if (!fstab_default) return Status::fromExceptionCode(EINVAL, "Failed to get fstab");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700166
Paul Lawrence20400892018-10-03 14:14:52 -0700167 auto mounts = std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>{
168 fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab};
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700169 if (!mounts) return Status::fromExceptionCode(EINVAL, "Failed to get /proc/mounts");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700170
171 for (int i = 0; i < mounts->num_entries; ++i) {
172 const fstab_rec* mount_rec = &mounts->recs[i];
Paul Lawrence20400892018-10-03 14:14:52 -0700173 const fstab_rec* fstab_rec =
174 fs_mgr_get_entry_for_mount_point(fstab_default.get(), mount_rec->mount_point);
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700175 if (!fstab_rec) continue;
176
177 if (fs_mgr_is_checkpoint_blk(fstab_rec)) {
178 android::base::unique_fd fd(
179 TEMP_FAILURE_RETRY(open(mount_rec->mount_point, O_RDONLY | O_CLOEXEC)));
180 if (!fd) {
181 PLOG(ERROR) << "Failed to open mount point" << mount_rec->mount_point;
182 continue;
183 }
184
185 struct fstrim_range range = {};
186 range.len = ULLONG_MAX;
187 if (ioctl(fd, FITRIM, &range)) {
188 PLOG(ERROR) << "Failed to trim " << mount_rec->mount_point;
189 continue;
190 }
191
192 setBowState(mount_rec->blk_device, "1");
193 }
194 }
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700195 return Status::ok();
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700196}
197
198namespace {
199const int kBlockSize = 4096;
200const int kSectorSize = 512;
201
202typedef uint64_t sector_t;
203
204struct log_entry {
205 sector_t source;
206 sector_t dest;
207 uint32_t size;
208 uint32_t checksum;
209} __attribute__((packed));
210
211struct log_sector {
212 uint32_t magic;
213 uint32_t count;
214 uint32_t sequence;
215 struct log_entry entries[];
216} __attribute__((packed));
217
218// MAGIC is BOW in ascii
219const int kMagic = 0x00574f42;
220
221void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
222 static uint32_t table[0x100] = {
223 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
224 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
225 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
226 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
227 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
228 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
229 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
230 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
231 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
232 0xB6662D3D,
233
234 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
235 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
236 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
237 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
238 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
239 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
240 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
241 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
242 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
243 0xC0BA6CAD,
244
245 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
246 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
247 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
248 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
249 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
250 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
251 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
252 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
253 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
254 0x5BDEAE1D,
255
256 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
257 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
258 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
259 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
260 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
261 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
262 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
263 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
264 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
265 0x2D02EF8D};
266
267 for (size_t i = 0; i < n_bytes; ++i) {
268 *crc ^= ((uint8_t*)data)[i];
269 *crc = table[(uint8_t)*crc] ^ *crc >> 8;
270 }
271}
272
273} // namespace
274
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700275Status cp_restoreCheckpoint(const std::string& blockDevice) {
276 LOG(INFO) << "Restoring checkpoint on " << blockDevice;
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700277 std::fstream device(blockDevice, std::ios::binary | std::ios::in | std::ios::out);
278 if (!device) {
279 PLOG(ERROR) << "Cannot open " << blockDevice;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700280 return Status::fromExceptionCode(errno, ("Cannot open " + blockDevice).c_str());
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700281 }
282 char buffer[kBlockSize];
283 device.read(buffer, kBlockSize);
284 log_sector& ls = *(log_sector*)buffer;
285 if (ls.magic != kMagic) {
286 LOG(ERROR) << "No magic";
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700287 return Status::fromExceptionCode(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700288 }
289
290 LOG(INFO) << "Restoring " << ls.sequence << " log sectors";
291
292 for (int sequence = ls.sequence; sequence >= 0; sequence--) {
293 char buffer[kBlockSize];
294 device.seekg(0);
295 device.read(buffer, kBlockSize);
296 log_sector& ls = *(log_sector*)buffer;
297 if (ls.magic != kMagic) {
298 LOG(ERROR) << "No magic!";
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700299 return Status::fromExceptionCode(EINVAL, "No magic");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700300 }
301
302 if ((int)ls.sequence != sequence) {
303 LOG(ERROR) << "Expecting log sector " << sequence << " but got " << ls.sequence;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700304 return Status::fromExceptionCode(
305 EINVAL, ("Expecting log sector " + std::to_string(sequence) + " but got " +
306 std::to_string(ls.sequence))
307 .c_str());
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700308 }
309
310 LOG(INFO) << "Restoring from log sector " << ls.sequence;
311
312 for (log_entry* le = &ls.entries[ls.count - 1]; le >= ls.entries; --le) {
313 LOG(INFO) << "Restoring " << le->size << " bytes from sector " << le->dest << " to "
314 << le->source << " with checksum " << std::hex << le->checksum;
315 std::vector<char> buffer(le->size);
316 device.seekg(le->dest * kSectorSize);
317 device.read(&buffer[0], le->size);
318
319 uint32_t checksum = le->source / (kBlockSize / kSectorSize);
320 for (size_t i = 0; i < le->size; i += kBlockSize) {
321 crc32(&buffer[i], kBlockSize, &checksum);
322 }
323
324 if (le->checksum && checksum != le->checksum) {
325 LOG(ERROR) << "Checksums don't match " << std::hex << checksum;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700326 return Status::fromExceptionCode(EINVAL, "Checksums don't match");
Paul Lawrence1abb2fe2018-09-21 10:49:57 -0700327 }
328
329 device.seekg(le->source * kSectorSize);
330 device.write(&buffer[0], le->size);
331 }
332 }
333
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700334 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700335}
336
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700337Status cp_markBootAttempt() {
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700338 std::string oldContent, newContent;
339 int retry = 0;
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700340 struct stat st;
341 int result = stat(kMetadataCPFile.c_str(), &st);
342
343 // If the file doesn't exist, we aren't managing a checkpoint retry counter
344 if (result != 0) return Status::ok();
345 if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
346 PLOG(ERROR) << "Failed to read checkpoint file";
347 return Status::fromExceptionCode(errno, "Failed to read checkpoint file");
348 }
Daniel Rosenbergd3992492018-10-02 17:40:44 -0700349 std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700350
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700351 if (!android::base::ParseInt(retryContent, &retry))
352 return Status::fromExceptionCode(EINVAL, "Could not parse retry count");
353 if (retry > 0) {
354 retry--;
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700355
Daniel Rosenberg73680ec2018-10-10 18:52:04 -0700356 newContent = std::to_string(retry);
357 if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
358 return Status::fromExceptionCode(errno, "Could not write checkpoint file");
359 }
360 return Status::ok();
Daniel Rosenberg65f99c92018-08-28 01:58:49 -0700361}
362
363} // namespace vold
364} // namespace android