1 /* 2 * Copyright (C) 2016 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 #include "EncryptInplace.h" 18 19 #include <stdio.h> 20 #include <stdint.h> 21 #include <inttypes.h> 22 #include <time.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <fcntl.h> 26 #include <ext4_utils/ext4.h> 27 #include <ext4_utils/ext4_utils.h> 28 #include <f2fs_sparseblock.h> 29 30 #include <algorithm> 31 32 #include <android-base/logging.h> 33 #include <android-base/properties.h> 34 35 // HORRIBLE HACK, FIXME 36 #include "cryptfs.h" 37 38 // FIXME horrible cut-and-paste code 39 static inline int unix_read(int fd, void* buff, int len) 40 { 41 return TEMP_FAILURE_RETRY(read(fd, buff, len)); 42 } 43 44 static inline int unix_write(int fd, const void* buff, int len) 45 { 46 return TEMP_FAILURE_RETRY(write(fd, buff, len)); 47 } 48 49 #define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE) 50 51 /* aligned 32K writes tends to make flash happy. 52 * SD card association recommends it. 53 */ 54 #ifndef CONFIG_HW_DISK_ENCRYPTION 55 #define BLOCKS_AT_A_TIME 8 56 #else 57 #define BLOCKS_AT_A_TIME 1024 58 #endif 59 60 struct encryptGroupsData 61 { 62 int realfd; 63 int cryptofd; 64 off64_t numblocks; 65 off64_t one_pct, cur_pct, new_pct; 66 off64_t blocks_already_done, tot_numblocks; 67 off64_t used_blocks_already_done, tot_used_blocks; 68 char* real_blkdev, * crypto_blkdev; 69 int count; 70 off64_t offset; 71 char* buffer; 72 off64_t last_written_sector; 73 int completed; 74 time_t time_started; 75 int remaining_time; 76 bool set_progress_properties; 77 }; 78 79 static void update_progress(struct encryptGroupsData* data, int is_used) 80 { 81 data->blocks_already_done++; 82 83 if (is_used) { 84 data->used_blocks_already_done++; 85 } 86 if (data->tot_used_blocks) { 87 data->new_pct = data->used_blocks_already_done / data->one_pct; 88 } else { 89 data->new_pct = data->blocks_already_done / data->one_pct; 90 } 91 92 if (!data->set_progress_properties) return; 93 94 if (data->new_pct > data->cur_pct) { 95 char buf[8]; 96 data->cur_pct = data->new_pct; 97 snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct); 98 android::base::SetProperty("vold.encrypt_progress", buf); 99 } 100 101 if (data->cur_pct >= 5) { 102 struct timespec time_now; 103 if (clock_gettime(CLOCK_MONOTONIC, &time_now)) { 104 LOG(WARNING) << "Error getting time"; 105 } else { 106 double elapsed_time = difftime(time_now.tv_sec, data->time_started); 107 off64_t remaining_blocks = data->tot_used_blocks 108 - data->used_blocks_already_done; 109 int remaining_time = (int)(elapsed_time * remaining_blocks 110 / data->used_blocks_already_done); 111 112 // Change time only if not yet set, lower, or a lot higher for 113 // best user experience 114 if (data->remaining_time == -1 115 || remaining_time < data->remaining_time 116 || remaining_time > data->remaining_time + 60) { 117 char buf[8]; 118 snprintf(buf, sizeof(buf), "%d", remaining_time); 119 android::base::SetProperty("vold.encrypt_time_remaining", buf); 120 data->remaining_time = remaining_time; 121 } 122 } 123 } 124 } 125 126 static void log_progress(struct encryptGroupsData const* data, bool completed) 127 { 128 // Precondition - if completed data = 0 else data != 0 129 130 // Track progress so we can skip logging blocks 131 static off64_t offset = -1; 132 133 // Need to close existing 'Encrypting from' log? 134 if (completed || (offset != -1 && data->offset != offset)) { 135 LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE; 136 offset = -1; 137 } 138 139 // Need to start new 'Encrypting from' log? 140 if (!completed && offset != data->offset) { 141 LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE; 142 } 143 144 // Update offset 145 if (!completed) { 146 offset = data->offset + (off64_t)data->count * info.block_size; 147 } 148 } 149 150 static int flush_outstanding_data(struct encryptGroupsData* data) 151 { 152 if (data->count == 0) { 153 return 0; 154 } 155 156 LOG(VERBOSE) << "Copying " << data->count << " blocks at offset " << data->offset; 157 158 if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) { 159 LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt"; 160 return -1; 161 } 162 163 if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) { 164 LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev 165 << " for inplace encrypt"; 166 return -1; 167 } else { 168 log_progress(data, false); 169 } 170 171 data->count = 0; 172 data->last_written_sector = (data->offset + data->count) 173 / info.block_size * CRYPT_SECTOR_SIZE - 1; 174 return 0; 175 } 176 177 static int encrypt_groups(struct encryptGroupsData* data) 178 { 179 unsigned int i; 180 u8 *block_bitmap = 0; 181 unsigned int block; 182 off64_t ret; 183 int rc = -1; 184 185 data->buffer = (char*) malloc(info.block_size * BLOCKS_AT_A_TIME); 186 if (!data->buffer) { 187 LOG(ERROR) << "Failed to allocate crypto buffer"; 188 goto errout; 189 } 190 191 block_bitmap = (u8*) malloc(info.block_size); 192 if (!block_bitmap) { 193 LOG(ERROR) << "failed to allocate block bitmap"; 194 goto errout; 195 } 196 197 for (i = 0; i < aux_info.groups; ++i) { 198 LOG(INFO) << "Encrypting group " << i; 199 200 u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; 201 u32 block_count = std::min(info.blocks_per_group, 202 (u32)(aux_info.len_blocks - first_block)); 203 204 off64_t offset = (u64)info.block_size 205 * aux_info.bg_desc[i].bg_block_bitmap; 206 207 ret = pread64(data->realfd, block_bitmap, info.block_size, offset); 208 if (ret != (int)info.block_size) { 209 LOG(ERROR) << "failed to read all of block group bitmap " << i; 210 goto errout; 211 } 212 213 offset = (u64)info.block_size * first_block; 214 215 data->count = 0; 216 217 for (block = 0; block < block_count; block++) { 218 int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) ? 219 0 : bitmap_get_bit(block_bitmap, block); 220 update_progress(data, used); 221 if (used) { 222 if (data->count == 0) { 223 data->offset = offset; 224 } 225 data->count++; 226 } else { 227 if (flush_outstanding_data(data)) { 228 goto errout; 229 } 230 } 231 232 offset += info.block_size; 233 234 /* Write data if we are aligned or buffer size reached */ 235 if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 236 || data->count == BLOCKS_AT_A_TIME) { 237 if (flush_outstanding_data(data)) { 238 goto errout; 239 } 240 } 241 } 242 if (flush_outstanding_data(data)) { 243 goto errout; 244 } 245 } 246 247 data->completed = 1; 248 rc = 0; 249 250 errout: 251 log_progress(0, true); 252 free(data->buffer); 253 free(block_bitmap); 254 return rc; 255 } 256 257 static int cryptfs_enable_inplace_ext4(char* crypto_blkdev, char* real_blkdev, off64_t size, 258 off64_t* size_already_done, off64_t tot_size, 259 off64_t previously_encrypted_upto, 260 bool set_progress_properties) { 261 u32 i; 262 struct encryptGroupsData data; 263 int rc; // Can't initialize without causing warning -Wclobbered 264 int retries = RETRY_MOUNT_ATTEMPTS; 265 struct timespec time_started = {0}; 266 267 if (previously_encrypted_upto > *size_already_done) { 268 LOG(DEBUG) << "Not fast encrypting since resuming part way through"; 269 return -1; 270 } 271 272 memset(&data, 0, sizeof(data)); 273 data.real_blkdev = real_blkdev; 274 data.crypto_blkdev = crypto_blkdev; 275 data.set_progress_properties = set_progress_properties; 276 277 LOG(DEBUG) << "Opening" << real_blkdev; 278 if ( (data.realfd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) { 279 PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt"; 280 rc = -1; 281 goto errout; 282 } 283 284 LOG(DEBUG) << "Opening" << crypto_blkdev; 285 // Wait until the block device appears. Re-use the mount retry values since it is reasonable. 286 while ((data.cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { 287 if (--retries) { 288 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev 289 << " for ext4 inplace encrypt, retrying"; 290 sleep(RETRY_MOUNT_DELAY_SECONDS); 291 } else { 292 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev 293 << " for ext4 inplace encrypt"; 294 rc = ENABLE_INPLACE_ERR_DEV; 295 goto errout; 296 } 297 } 298 299 if (setjmp(setjmp_env)) { // NOLINT 300 LOG(ERROR) << "Reading ext4 extent caused an exception"; 301 rc = -1; 302 goto errout; 303 } 304 305 if (read_ext(data.realfd, 0) != 0) { 306 LOG(ERROR) << "Failed to read ext4 extent"; 307 rc = -1; 308 goto errout; 309 } 310 311 data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; 312 data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; 313 data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; 314 315 LOG(INFO) << "Encrypting ext4 filesystem in place..."; 316 317 data.tot_used_blocks = data.numblocks; 318 for (i = 0; i < aux_info.groups; ++i) { 319 data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count; 320 } 321 322 data.one_pct = data.tot_used_blocks / 100; 323 data.cur_pct = 0; 324 325 if (clock_gettime(CLOCK_MONOTONIC, &time_started)) { 326 LOG(WARNING) << "Error getting time at start"; 327 // Note - continue anyway - we'll run with 0 328 } 329 data.time_started = time_started.tv_sec; 330 data.remaining_time = -1; 331 332 rc = encrypt_groups(&data); 333 if (rc) { 334 LOG(ERROR) << "Error encrypting groups"; 335 goto errout; 336 } 337 338 *size_already_done += data.completed ? size : data.last_written_sector; 339 rc = 0; 340 341 errout: 342 close(data.realfd); 343 close(data.cryptofd); 344 345 return rc; 346 } 347 348 static void log_progress_f2fs(u64 block, bool completed) 349 { 350 // Precondition - if completed data = 0 else data != 0 351 352 // Track progress so we can skip logging blocks 353 static u64 last_block = (u64)-1; 354 355 // Need to close existing 'Encrypting from' log? 356 if (completed || (last_block != (u64)-1 && block != last_block + 1)) { 357 LOG(INFO) << "Encrypted to block " << last_block; 358 last_block = -1; 359 } 360 361 // Need to start new 'Encrypting from' log? 362 if (!completed && (last_block == (u64)-1 || block != last_block + 1)) { 363 LOG(INFO) << "Encrypting from block " << block; 364 } 365 366 // Update offset 367 if (!completed) { 368 last_block = block; 369 } 370 } 371 372 static int encrypt_one_block_f2fs(u64 pos, void *data) 373 { 374 struct encryptGroupsData *priv_dat = (struct encryptGroupsData *)data; 375 376 priv_dat->blocks_already_done = pos - 1; 377 update_progress(priv_dat, 1); 378 379 off64_t offset = pos * CRYPT_INPLACE_BUFSIZE; 380 381 if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) { 382 LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev 383 << " for f2fs inplace encrypt"; 384 return -1; 385 } 386 387 if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) { 388 LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev 389 << " for f2fs inplace encrypt"; 390 return -1; 391 } else { 392 log_progress_f2fs(pos, false); 393 } 394 395 return 0; 396 } 397 398 static int cryptfs_enable_inplace_f2fs(char* crypto_blkdev, char* real_blkdev, off64_t size, 399 off64_t* size_already_done, off64_t tot_size, 400 off64_t previously_encrypted_upto, 401 bool set_progress_properties) { 402 struct encryptGroupsData data; 403 struct f2fs_info *f2fs_info = NULL; 404 int rc = ENABLE_INPLACE_ERR_OTHER; 405 if (previously_encrypted_upto > *size_already_done) { 406 LOG(DEBUG) << "Not fast encrypting since resuming part way through"; 407 return ENABLE_INPLACE_ERR_OTHER; 408 } 409 memset(&data, 0, sizeof(data)); 410 data.real_blkdev = real_blkdev; 411 data.crypto_blkdev = crypto_blkdev; 412 data.set_progress_properties = set_progress_properties; 413 data.realfd = -1; 414 data.cryptofd = -1; 415 if ( (data.realfd = open64(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) { 416 PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt"; 417 goto errout; 418 } 419 if ( (data.cryptofd = open64(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { 420 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev 421 << " for f2fs inplace encrypt"; 422 rc = ENABLE_INPLACE_ERR_DEV; 423 goto errout; 424 } 425 426 f2fs_info = generate_f2fs_info(data.realfd); 427 if (!f2fs_info) 428 goto errout; 429 430 data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; 431 data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; 432 data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; 433 434 data.tot_used_blocks = get_num_blocks_used(f2fs_info); 435 436 data.one_pct = data.tot_used_blocks / 100; 437 data.cur_pct = 0; 438 data.time_started = time(NULL); 439 data.remaining_time = -1; 440 441 data.buffer = (char*) malloc(f2fs_info->block_size); 442 if (!data.buffer) { 443 LOG(ERROR) << "Failed to allocate crypto buffer"; 444 goto errout; 445 } 446 447 data.count = 0; 448 449 /* Currently, this either runs to completion, or hits a nonrecoverable error */ 450 rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data); 451 452 if (rc) { 453 LOG(ERROR) << "Error in running over f2fs blocks"; 454 rc = ENABLE_INPLACE_ERR_OTHER; 455 goto errout; 456 } 457 458 *size_already_done += size; 459 rc = 0; 460 461 errout: 462 if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev; 463 464 log_progress_f2fs(0, true); 465 free(f2fs_info); 466 free(data.buffer); 467 close(data.realfd); 468 close(data.cryptofd); 469 470 return rc; 471 } 472 473 static int cryptfs_enable_inplace_full(char* crypto_blkdev, char* real_blkdev, off64_t size, 474 off64_t* size_already_done, off64_t tot_size, 475 off64_t previously_encrypted_upto, 476 bool set_progress_properties) { 477 int realfd, cryptofd; 478 char *buf[CRYPT_INPLACE_BUFSIZE]; 479 int rc = ENABLE_INPLACE_ERR_OTHER; 480 off64_t numblocks, i, remainder; 481 off64_t one_pct, cur_pct, new_pct; 482 off64_t blocks_already_done, tot_numblocks; 483 484 if ( (realfd = open(real_blkdev, O_RDONLY|O_CLOEXEC)) < 0) { 485 PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt"; 486 return ENABLE_INPLACE_ERR_OTHER; 487 } 488 489 if ( (cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { 490 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt"; 491 close(realfd); 492 return ENABLE_INPLACE_ERR_DEV; 493 } 494 495 /* This is pretty much a simple loop of reading 4K, and writing 4K. 496 * The size passed in is the number of 512 byte sectors in the filesystem. 497 * So compute the number of whole 4K blocks we should read/write, 498 * and the remainder. 499 */ 500 numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; 501 remainder = size % CRYPT_SECTORS_PER_BUFSIZE; 502 tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; 503 blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; 504 505 LOG(ERROR) << "Encrypting filesystem in place..."; 506 507 i = previously_encrypted_upto + 1 - *size_already_done; 508 509 if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { 510 PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev; 511 goto errout; 512 } 513 514 if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { 515 PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev; 516 goto errout; 517 } 518 519 for (;i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) { 520 if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) { 521 PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev 522 << " for inplace encrypt"; 523 goto errout; 524 } 525 if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) { 526 PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev 527 << " for inplace encrypt"; 528 goto errout; 529 } else { 530 LOG(INFO) << "Encrypted 1 block at " << i; 531 } 532 } 533 534 one_pct = tot_numblocks / 100; 535 cur_pct = 0; 536 /* process the majority of the filesystem in blocks */ 537 for (i/=CRYPT_SECTORS_PER_BUFSIZE; i<numblocks; i++) { 538 new_pct = (i + blocks_already_done) / one_pct; 539 if (set_progress_properties && new_pct > cur_pct) { 540 char buf[8]; 541 542 cur_pct = new_pct; 543 snprintf(buf, sizeof(buf), "%" PRId64, cur_pct); 544 android::base::SetProperty("vold.encrypt_progress", buf); 545 } 546 if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { 547 PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt"; 548 goto errout; 549 } 550 if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { 551 PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt"; 552 goto errout; 553 } else { 554 LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at " 555 << i * CRYPT_SECTORS_PER_BUFSIZE; 556 } 557 } 558 559 /* Do any remaining sectors */ 560 for (i=0; i<remainder; i++) { 561 if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) { 562 LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev 563 << " for inplace encrypt"; 564 goto errout; 565 } 566 if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) { 567 LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev 568 << " for inplace encrypt"; 569 goto errout; 570 } else { 571 LOG(INFO) << "Encrypted 1 block at next location"; 572 } 573 } 574 575 *size_already_done += size; 576 rc = 0; 577 578 errout: 579 close(realfd); 580 close(cryptofd); 581 582 return rc; 583 } 584 585 /* returns on of the ENABLE_INPLACE_* return codes */ 586 int cryptfs_enable_inplace(char* crypto_blkdev, char* real_blkdev, off64_t size, 587 off64_t* size_already_done, off64_t tot_size, 588 off64_t previously_encrypted_upto, bool set_progress_properties) { 589 int rc_ext4, rc_f2fs, rc_full; 590 LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size 591 << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto 592 << ", " << set_progress_properties << ")"; 593 if (previously_encrypted_upto) { 594 LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto; 595 } 596 597 if (*size_already_done + size < previously_encrypted_upto) { 598 LOG(DEBUG) << "cryptfs_enable_inplace already done"; 599 *size_already_done += size; 600 return 0; 601 } 602 603 /* TODO: identify filesystem type. 604 * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and 605 * then we will drop down to cryptfs_enable_inplace_f2fs. 606 * */ 607 if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done, 608 tot_size, previously_encrypted_upto, 609 set_progress_properties)) == 0) { 610 LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success"; 611 return 0; 612 } 613 LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4; 614 615 if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done, 616 tot_size, previously_encrypted_upto, 617 set_progress_properties)) == 0) { 618 LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success"; 619 return 0; 620 } 621 LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs; 622 623 rc_full = 624 cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size, 625 previously_encrypted_upto, set_progress_properties); 626 LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full; 627 628 /* Hack for b/17898962, the following is the symptom... */ 629 if (rc_ext4 == ENABLE_INPLACE_ERR_DEV 630 && rc_f2fs == ENABLE_INPLACE_ERR_DEV 631 && rc_full == ENABLE_INPLACE_ERR_DEV) { 632 LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV"; 633 return ENABLE_INPLACE_ERR_DEV; 634 } 635 return rc_full; 636 } 637