1 /** 2 * libf2fs.c 3 * 4 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 5 * http://www.samsung.com/ 6 * Copyright (c) 2019 Google Inc. 7 * http://www.google.com/ 8 * 9 * Dual licensed under the GPL or LGPL version 2 licenses. 10 */ 11 #define _LARGEFILE64_SOURCE 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <errno.h> 17 #include <unistd.h> 18 #include <fcntl.h> 19 #ifdef HAVE_MNTENT_H 20 #include <mntent.h> 21 #endif 22 #include <time.h> 23 #ifndef ANDROID_WINDOWS_HOST 24 #include <sys/stat.h> 25 #include <sys/mount.h> 26 #include <sys/ioctl.h> 27 #endif 28 #ifdef HAVE_LINUX_HDREG_H 29 #include <linux/hdreg.h> 30 #endif 31 32 #include <stdbool.h> 33 #include <assert.h> 34 #include <inttypes.h> 35 #include "f2fs_fs.h" 36 37 struct f2fs_configuration c; 38 39 #ifdef WITH_ANDROID 40 #include <sparse/sparse.h> 41 struct sparse_file *f2fs_sparse_file; 42 static char **blocks; 43 u_int64_t blocks_count; 44 static char *zeroed_block; 45 #endif 46 47 static int __get_device_fd(__u64 *offset) 48 { 49 __u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS; 50 int i; 51 52 for (i = 0; i < c.ndevs; i++) { 53 if (c.devices[i].start_blkaddr <= blk_addr && 54 c.devices[i].end_blkaddr >= blk_addr) { 55 *offset -= 56 c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS; 57 return c.devices[i].fd; 58 } 59 } 60 return -1; 61 } 62 63 #ifndef HAVE_LSEEK64 64 typedef off_t off64_t; 65 66 static inline off64_t lseek64(int fd, __u64 offset, int set) 67 { 68 return lseek(fd, offset, set); 69 } 70 #endif 71 72 /* ---------- dev_cache, Least Used First (LUF) policy ------------------- */ 73 /* 74 * Least used block will be the first victim to be replaced when max hash 75 * collision exceeds 76 */ 77 static bool *dcache_valid; /* is the cached block valid? */ 78 static off64_t *dcache_blk; /* which block it cached */ 79 static uint64_t *dcache_lastused; /* last used ticks for cache entries */ 80 static char *dcache_buf; /* cached block data */ 81 static uint64_t dcache_usetick; /* current use tick */ 82 83 static uint64_t dcache_raccess; 84 static uint64_t dcache_rhit; 85 static uint64_t dcache_rmiss; 86 static uint64_t dcache_rreplace; 87 88 static bool dcache_exit_registered = false; 89 90 /* 91 * Shadow config: 92 * 93 * Active set of the configurations. 94 * Global configuration 'dcache_config' will be transferred here when 95 * when dcache_init() is called 96 */ 97 static dev_cache_config_t dcache_config = {0, 16, 1}; 98 static bool dcache_initialized = false; 99 100 #define MIN_NUM_CACHE_ENTRY 1024L 101 #define MAX_MAX_HASH_COLLISION 16 102 103 static long dcache_relocate_offset0[] = { 104 20, -20, 40, -40, 80, -80, 160, -160, 105 320, -320, 640, -640, 1280, -1280, 2560, -2560, 106 }; 107 static int dcache_relocate_offset[16]; 108 109 static void dcache_print_statistics(void) 110 { 111 long i; 112 long useCnt; 113 114 /* Number of used cache entries */ 115 useCnt = 0; 116 for (i = 0; i < dcache_config.num_cache_entry; i++) 117 if (dcache_valid[i]) 118 ++useCnt; 119 120 /* 121 * c: number of cache entries 122 * u: used entries 123 * RA: number of read access blocks 124 * CH: cache hit 125 * CM: cache miss 126 * Repl: read cache replaced 127 */ 128 printf ("\nc, u, RA, CH, CM, Repl=\n"); 129 printf ("%ld %ld %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", 130 dcache_config.num_cache_entry, 131 useCnt, 132 dcache_raccess, 133 dcache_rhit, 134 dcache_rmiss, 135 dcache_rreplace); 136 } 137 138 void dcache_release(void) 139 { 140 if (!dcache_initialized) 141 return; 142 143 dcache_initialized = false; 144 145 if (c.cache_config.dbg_en) 146 dcache_print_statistics(); 147 148 if (dcache_blk != NULL) 149 free(dcache_blk); 150 if (dcache_lastused != NULL) 151 free(dcache_lastused); 152 if (dcache_buf != NULL) 153 free(dcache_buf); 154 if (dcache_valid != NULL) 155 free(dcache_valid); 156 dcache_config.num_cache_entry = 0; 157 dcache_blk = NULL; 158 dcache_lastused = NULL; 159 dcache_buf = NULL; 160 dcache_valid = NULL; 161 } 162 163 // return 0 for success, error code for failure. 164 static int dcache_alloc_all(long n) 165 { 166 if (n <= 0) 167 return -1; 168 if ((dcache_blk = (off64_t *) malloc(sizeof(off64_t) * n)) == NULL 169 || (dcache_lastused = (uint64_t *) 170 malloc(sizeof(uint64_t) * n)) == NULL 171 || (dcache_buf = (char *) malloc (F2FS_BLKSIZE * n)) == NULL 172 || (dcache_valid = (bool *) malloc(sizeof(bool) * n)) == NULL) 173 { 174 dcache_release(); 175 return -1; 176 } 177 dcache_config.num_cache_entry = n; 178 return 0; 179 } 180 181 static void dcache_relocate_init(void) 182 { 183 int i; 184 int n0 = (sizeof(dcache_relocate_offset0) 185 / sizeof(dcache_relocate_offset0[0])); 186 int n = (sizeof(dcache_relocate_offset) 187 / sizeof(dcache_relocate_offset[0])); 188 189 ASSERT(n == n0); 190 for (i = 0; i < n && i < dcache_config.max_hash_collision; i++) { 191 if (labs(dcache_relocate_offset0[i]) 192 > dcache_config.num_cache_entry / 2) { 193 dcache_config.max_hash_collision = i; 194 break; 195 } 196 dcache_relocate_offset[i] = 197 dcache_config.num_cache_entry 198 + dcache_relocate_offset0[i]; 199 } 200 } 201 202 void dcache_init(void) 203 { 204 long n; 205 206 if (c.cache_config.num_cache_entry <= 0) 207 return; 208 209 /* release previous cache init, if any */ 210 dcache_release(); 211 212 dcache_blk = NULL; 213 dcache_lastused = NULL; 214 dcache_buf = NULL; 215 dcache_valid = NULL; 216 217 dcache_config = c.cache_config; 218 219 n = max(MIN_NUM_CACHE_ENTRY, dcache_config.num_cache_entry); 220 221 /* halve alloc size until alloc succeed, or min cache reached */ 222 while (dcache_alloc_all(n) != 0 && n != MIN_NUM_CACHE_ENTRY) 223 n = max(MIN_NUM_CACHE_ENTRY, n/2); 224 225 /* must be the last: data dependent on num_cache_entry */ 226 dcache_relocate_init(); 227 dcache_initialized = true; 228 229 if (!dcache_exit_registered) { 230 dcache_exit_registered = true; 231 atexit(dcache_release); /* auto release */ 232 } 233 234 dcache_raccess = 0; 235 dcache_rhit = 0; 236 dcache_rmiss = 0; 237 dcache_rreplace = 0; 238 } 239 240 static inline char *dcache_addr(long entry) 241 { 242 return dcache_buf + F2FS_BLKSIZE * entry; 243 } 244 245 /* relocate on (n+1)-th collision */ 246 static inline long dcache_relocate(long entry, int n) 247 { 248 assert(dcache_config.num_cache_entry != 0); 249 return (entry + dcache_relocate_offset[n]) % 250 dcache_config.num_cache_entry; 251 } 252 253 static long dcache_find(off64_t blk) 254 { 255 register long n = dcache_config.num_cache_entry; 256 register unsigned m = dcache_config.max_hash_collision; 257 long entry, least_used, target; 258 unsigned try; 259 260 assert(n > 0); 261 target = least_used = entry = blk % n; /* simple modulo hash */ 262 263 for (try = 0; try < m; try++) { 264 if (!dcache_valid[target] || dcache_blk[target] == blk) 265 return target; /* found target or empty cache slot */ 266 if (dcache_lastused[target] < dcache_lastused[least_used]) 267 least_used = target; 268 target = dcache_relocate(entry, try); /* next target */ 269 } 270 return least_used; /* max search reached, return least used slot */ 271 } 272 273 /* Physical read into cache */ 274 static int dcache_io_read(int fd, long entry, off64_t offset, off64_t blk) 275 { 276 if (lseek64(fd, offset, SEEK_SET) < 0) { 277 MSG(0, "\n lseek64 fail.\n"); 278 return -1; 279 } 280 if (read(fd, dcache_buf + entry * F2FS_BLKSIZE, F2FS_BLKSIZE) < 0) { 281 MSG(0, "\n read() fail.\n"); 282 return -1; 283 } 284 dcache_lastused[entry] = ++dcache_usetick; 285 dcache_valid[entry] = true; 286 dcache_blk[entry] = blk; 287 return 0; 288 } 289 290 /* 291 * - Note: Read/Write are not symmetric: 292 * For read, we need to do it block by block, due to the cache nature: 293 * some blocks may be cached, and others don't. 294 * For write, since we always do a write-thru, we can join all writes into one, 295 * and write it once at the caller. This function updates the cache for write, but 296 * not the do a physical write. The caller is responsible for the physical write. 297 * - Note: We concentrate read/write together, due to the fact of similar structure to find 298 * the relavant cache entries 299 * - Return values: 300 * 0: success 301 * 1: cache not available (uninitialized) 302 * -1: error 303 */ 304 static int dcache_update_rw(int fd, void *buf, off64_t offset, 305 size_t byte_count, bool is_write) 306 { 307 off64_t blk; 308 int addr_in_blk; 309 off64_t start; 310 311 if (!dcache_initialized) 312 dcache_init(); /* auto initialize */ 313 314 if (!dcache_initialized) 315 return 1; /* not available */ 316 317 blk = offset / F2FS_BLKSIZE; 318 addr_in_blk = offset % F2FS_BLKSIZE; 319 start = blk * F2FS_BLKSIZE; 320 321 while (byte_count != 0) { 322 size_t cur_size = min(byte_count, 323 (size_t)(F2FS_BLKSIZE - addr_in_blk)); 324 long entry = dcache_find(blk); 325 326 if (!is_write) 327 ++dcache_raccess; 328 329 if (dcache_valid[entry] && dcache_blk[entry] == blk) { 330 /* cache hit */ 331 if (is_write) /* write: update cache */ 332 memcpy(dcache_addr(entry) + addr_in_blk, 333 buf, cur_size); 334 else 335 ++dcache_rhit; 336 } else { 337 /* cache miss */ 338 if (!is_write) { 339 int err; 340 ++dcache_rmiss; 341 if (dcache_valid[entry]) 342 ++dcache_rreplace; 343 /* read: physical I/O read into cache */ 344 err = dcache_io_read(fd, entry, start, blk); 345 if (err) 346 return err; 347 } 348 } 349 350 /* read: copy data from cache */ 351 /* write: nothing to do, since we don't do physical write. */ 352 if (!is_write) 353 memcpy(buf, dcache_addr(entry) + addr_in_blk, 354 cur_size); 355 356 /* next block */ 357 ++blk; 358 buf += cur_size; 359 start += F2FS_BLKSIZE; 360 byte_count -= cur_size; 361 addr_in_blk = 0; 362 } 363 return 0; 364 } 365 366 /* 367 * dcache_update_cache() just update cache, won't do physical I/O. 368 * Thus even no error, we need normal non-cache I/O for actual write 369 * 370 * return value: 1: cache not available 371 * 0: success, -1: I/O error 372 */ 373 int dcache_update_cache(int fd, void *buf, off64_t offset, size_t count) 374 { 375 return dcache_update_rw(fd, buf, offset, count, true); 376 } 377 378 /* handles read into cache + read into buffer */ 379 int dcache_read(int fd, void *buf, off64_t offset, size_t count) 380 { 381 return dcache_update_rw(fd, buf, offset, count, false); 382 } 383 384 /* 385 * IO interfaces 386 */ 387 int dev_read_version(void *buf, __u64 offset, size_t len) 388 { 389 if (c.sparse_mode) 390 return 0; 391 if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0) 392 return -1; 393 if (read(c.kd, buf, len) < 0) 394 return -1; 395 return 0; 396 } 397 398 #ifdef WITH_ANDROID 399 static int sparse_read_blk(__u64 block, int count, void *buf) 400 { 401 int i; 402 char *out = buf; 403 __u64 cur_block; 404 405 for (i = 0; i < count; ++i) { 406 cur_block = block + i; 407 if (blocks[cur_block]) 408 memcpy(out + (i * F2FS_BLKSIZE), 409 blocks[cur_block], F2FS_BLKSIZE); 410 else if (blocks) 411 memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE); 412 } 413 return 0; 414 } 415 416 static int sparse_write_blk(__u64 block, int count, const void *buf) 417 { 418 int i; 419 __u64 cur_block; 420 const char *in = buf; 421 422 for (i = 0; i < count; ++i) { 423 cur_block = block + i; 424 if (blocks[cur_block] == zeroed_block) 425 blocks[cur_block] = NULL; 426 if (!blocks[cur_block]) { 427 blocks[cur_block] = calloc(1, F2FS_BLKSIZE); 428 if (!blocks[cur_block]) 429 return -ENOMEM; 430 } 431 memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE), 432 F2FS_BLKSIZE); 433 } 434 return 0; 435 } 436 437 static int sparse_write_zeroed_blk(__u64 block, int count) 438 { 439 int i; 440 __u64 cur_block; 441 442 for (i = 0; i < count; ++i) { 443 cur_block = block + i; 444 if (blocks[cur_block]) 445 continue; 446 blocks[cur_block] = zeroed_block; 447 } 448 return 0; 449 } 450 451 #ifdef SPARSE_CALLBACK_USES_SIZE_T 452 static int sparse_import_segment(void *UNUSED(priv), const void *data, 453 size_t len, unsigned int block, unsigned int nr_blocks) 454 #else 455 static int sparse_import_segment(void *UNUSED(priv), const void *data, int len, 456 unsigned int block, unsigned int nr_blocks) 457 #endif 458 { 459 /* Ignore chunk headers, only write the data */ 460 if (!nr_blocks || len % F2FS_BLKSIZE) 461 return 0; 462 463 return sparse_write_blk(block, nr_blocks, data); 464 } 465 466 static int sparse_merge_blocks(uint64_t start, uint64_t num, int zero) 467 { 468 char *buf; 469 uint64_t i; 470 471 if (zero) { 472 blocks[start] = NULL; 473 return sparse_file_add_fill(f2fs_sparse_file, 0x0, 474 F2FS_BLKSIZE * num, start); 475 } 476 477 buf = calloc(num, F2FS_BLKSIZE); 478 if (!buf) { 479 fprintf(stderr, "failed to alloc %llu\n", 480 (unsigned long long)num * F2FS_BLKSIZE); 481 return -ENOMEM; 482 } 483 484 for (i = 0; i < num; i++) { 485 memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE); 486 free(blocks[start + i]); 487 blocks[start + i] = NULL; 488 } 489 490 /* free_sparse_blocks will release this buf. */ 491 blocks[start] = buf; 492 493 return sparse_file_add_data(f2fs_sparse_file, blocks[start], 494 F2FS_BLKSIZE * num, start); 495 } 496 #else 497 static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; } 498 static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; } 499 static int sparse_write_zeroed_blk(__u64 block, int count) { return 0; } 500 #endif 501 502 int dev_read(void *buf, __u64 offset, size_t len) 503 { 504 int fd; 505 int err; 506 507 if (c.sparse_mode) 508 return sparse_read_blk(offset / F2FS_BLKSIZE, 509 len / F2FS_BLKSIZE, buf); 510 511 fd = __get_device_fd(&offset); 512 if (fd < 0) 513 return fd; 514 515 /* err = 1: cache not available, fall back to non-cache R/W */ 516 /* err = 0: success, err=-1: I/O error */ 517 err = dcache_read(fd, buf, (off64_t)offset, len); 518 if (err <= 0) 519 return err; 520 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) 521 return -1; 522 if (read(fd, buf, len) < 0) 523 return -1; 524 return 0; 525 } 526 527 #ifdef POSIX_FADV_WILLNEED 528 int dev_readahead(__u64 offset, size_t len) 529 #else 530 int dev_readahead(__u64 offset, size_t UNUSED(len)) 531 #endif 532 { 533 int fd = __get_device_fd(&offset); 534 535 if (fd < 0) 536 return fd; 537 #ifdef POSIX_FADV_WILLNEED 538 return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED); 539 #else 540 return 0; 541 #endif 542 } 543 544 int dev_write(void *buf, __u64 offset, size_t len) 545 { 546 int fd; 547 548 if (c.dry_run) 549 return 0; 550 551 if (c.sparse_mode) 552 return sparse_write_blk(offset / F2FS_BLKSIZE, 553 len / F2FS_BLKSIZE, buf); 554 555 fd = __get_device_fd(&offset); 556 if (fd < 0) 557 return fd; 558 559 /* 560 * dcache_update_cache() just update cache, won't do I/O. 561 * Thus even no error, we need normal non-cache I/O for actual write 562 */ 563 if (dcache_update_cache(fd, buf, (off64_t)offset, len) < 0) 564 return -1; 565 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) 566 return -1; 567 if (write(fd, buf, len) < 0) 568 return -1; 569 return 0; 570 } 571 572 int dev_write_block(void *buf, __u64 blk_addr) 573 { 574 return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); 575 } 576 577 int dev_write_dump(void *buf, __u64 offset, size_t len) 578 { 579 if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0) 580 return -1; 581 if (write(c.dump_fd, buf, len) < 0) 582 return -1; 583 return 0; 584 } 585 586 int dev_fill(void *buf, __u64 offset, size_t len) 587 { 588 int fd; 589 590 if (c.sparse_mode) 591 return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE, 592 len / F2FS_BLKSIZE); 593 594 fd = __get_device_fd(&offset); 595 if (fd < 0) 596 return fd; 597 598 /* Only allow fill to zero */ 599 if (*((__u8*)buf)) 600 return -1; 601 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) 602 return -1; 603 if (write(fd, buf, len) < 0) 604 return -1; 605 return 0; 606 } 607 608 int dev_fill_block(void *buf, __u64 blk_addr) 609 { 610 return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); 611 } 612 613 int dev_read_block(void *buf, __u64 blk_addr) 614 { 615 return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); 616 } 617 618 int dev_reada_block(__u64 blk_addr) 619 { 620 return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); 621 } 622 623 int f2fs_fsync_device(void) 624 { 625 #ifndef ANDROID_WINDOWS_HOST 626 int i; 627 628 for (i = 0; i < c.ndevs; i++) { 629 if (fsync(c.devices[i].fd) < 0) { 630 MSG(0, "\tError: Could not conduct fsync!!!\n"); 631 return -1; 632 } 633 } 634 #endif 635 return 0; 636 } 637 638 int f2fs_init_sparse_file(void) 639 { 640 #ifdef WITH_ANDROID 641 if (c.func == MKFS) { 642 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size); 643 if (!f2fs_sparse_file) 644 return -1; 645 } else { 646 f2fs_sparse_file = sparse_file_import(c.devices[0].fd, 647 true, false); 648 if (!f2fs_sparse_file) 649 return -1; 650 651 c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0); 652 c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1))); 653 } 654 655 if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) { 656 MSG(0, "\tError: Corrupted sparse file\n"); 657 return -1; 658 } 659 blocks_count = c.device_size / F2FS_BLKSIZE; 660 blocks = calloc(blocks_count, sizeof(char *)); 661 if (!blocks) { 662 MSG(0, "\tError: Calloc Failed for blocks!!!\n"); 663 return -1; 664 } 665 666 zeroed_block = calloc(1, F2FS_BLKSIZE); 667 if (!zeroed_block) { 668 MSG(0, "\tError: Calloc Failed for zeroed block!!!\n"); 669 return -1; 670 } 671 672 return sparse_file_foreach_chunk(f2fs_sparse_file, true, false, 673 sparse_import_segment, NULL); 674 #else 675 MSG(0, "\tError: Sparse mode is only supported for android\n"); 676 return -1; 677 #endif 678 } 679 680 void f2fs_release_sparse_resource(void) 681 { 682 #ifdef WITH_ANDROID 683 int j; 684 685 if (c.sparse_mode) { 686 if (f2fs_sparse_file != NULL) { 687 sparse_file_destroy(f2fs_sparse_file); 688 f2fs_sparse_file = NULL; 689 } 690 for (j = 0; j < blocks_count; j++) 691 free(blocks[j]); 692 free(blocks); 693 blocks = NULL; 694 free(zeroed_block); 695 zeroed_block = NULL; 696 } 697 #endif 698 } 699 700 #define MAX_CHUNK_SIZE (1 * 1024 * 1024 * 1024ULL) 701 #define MAX_CHUNK_COUNT (MAX_CHUNK_SIZE / F2FS_BLKSIZE) 702 int f2fs_finalize_device(void) 703 { 704 int i; 705 int ret = 0; 706 707 #ifdef WITH_ANDROID 708 if (c.sparse_mode) { 709 int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0; 710 uint64_t j; 711 712 if (c.func != MKFS) { 713 sparse_file_destroy(f2fs_sparse_file); 714 ret = ftruncate(c.devices[0].fd, 0); 715 ASSERT(!ret); 716 lseek(c.devices[0].fd, 0, SEEK_SET); 717 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, 718 c.device_size); 719 } 720 721 for (j = 0; j < blocks_count; ++j) { 722 if (chunk_start != -1) { 723 if (j - chunk_start >= MAX_CHUNK_COUNT) { 724 ret = sparse_merge_blocks(chunk_start, 725 j - chunk_start, 0); 726 ASSERT(!ret); 727 chunk_start = -1; 728 } 729 } 730 731 if (chunk_start == -1) { 732 if (!blocks[j]) 733 continue; 734 735 if (blocks[j] == zeroed_block) { 736 ret = sparse_merge_blocks(j, 1, 1); 737 ASSERT(!ret); 738 } else { 739 chunk_start = j; 740 } 741 } else { 742 if (blocks[j] && blocks[j] != zeroed_block) 743 continue; 744 745 ret = sparse_merge_blocks(chunk_start, 746 j - chunk_start, 0); 747 ASSERT(!ret); 748 749 if (blocks[j] == zeroed_block) { 750 ret = sparse_merge_blocks(j, 1, 1); 751 ASSERT(!ret); 752 } 753 754 chunk_start = -1; 755 } 756 } 757 if (chunk_start != -1) { 758 ret = sparse_merge_blocks(chunk_start, 759 blocks_count - chunk_start, 0); 760 ASSERT(!ret); 761 } 762 763 sparse_file_write(f2fs_sparse_file, c.devices[0].fd, 764 /*gzip*/0, /*sparse*/1, /*crc*/0); 765 766 f2fs_release_sparse_resource(); 767 } 768 #endif 769 /* 770 * We should call fsync() to flush out all the dirty pages 771 * in the block device page cache. 772 */ 773 for (i = 0; i < c.ndevs; i++) { 774 #ifndef ANDROID_WINDOWS_HOST 775 ret = fsync(c.devices[i].fd); 776 if (ret < 0) { 777 MSG(0, "\tError: Could not conduct fsync!!!\n"); 778 break; 779 } 780 #endif 781 ret = close(c.devices[i].fd); 782 if (ret < 0) { 783 MSG(0, "\tError: Failed to close device file!!!\n"); 784 break; 785 } 786 free(c.devices[i].path); 787 } 788 close(c.kd); 789 790 return ret; 791 } 792