1 #define _LARGEFILE64_SOURCE 2 3 #define LOG_TAG "f2fs_sparseblock" 4 5 #include <errno.h> 6 #include <f2fs_fs.h> 7 #include <fcntl.h> 8 #include <linux/types.h> 9 #include <malloc.h> 10 #include <string.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <unistd.h> 14 15 #include <log/log.h> 16 17 #include "f2fs_sparseblock.h" 18 19 #define D_DISP_u32(ptr, member) \ 20 do { \ 21 SLOGD("%-30s" "\t\t[0x%#08x : %u]\n", \ 22 #member, le32_to_cpu((ptr)->member), le32_to_cpu((ptr)->member) ); \ 23 } while (0); 24 25 #define D_DISP_u64(ptr, member) \ 26 do { \ 27 SLOGD("%-30s" "\t\t[0x%#016llx : %llu]\n", \ 28 #member, le64_to_cpu((ptr)->member), le64_to_cpu((ptr)->member) ); \ 29 } while (0); 30 31 #define segno_in_journal(jnl, i) ((jnl)->sit_j.entries[i].segno) 32 33 #define sit_in_journal(jnl, i) ((jnl)->sit_j.entries[i].se) 34 35 static void dbg_print_raw_sb_info(struct f2fs_super_block *sb) 36 { 37 SLOGD("\n"); 38 SLOGD("+--------------------------------------------------------+\n"); 39 SLOGD("| Super block |\n"); 40 SLOGD("+--------------------------------------------------------+\n"); 41 42 D_DISP_u32(sb, magic); 43 D_DISP_u32(sb, major_ver); 44 D_DISP_u32(sb, minor_ver); 45 D_DISP_u32(sb, log_sectorsize); 46 D_DISP_u32(sb, log_sectors_per_block); 47 48 D_DISP_u32(sb, log_blocksize); 49 D_DISP_u32(sb, log_blocks_per_seg); 50 D_DISP_u32(sb, segs_per_sec); 51 D_DISP_u32(sb, secs_per_zone); 52 D_DISP_u32(sb, checksum_offset); 53 D_DISP_u64(sb, block_count); 54 55 D_DISP_u32(sb, section_count); 56 D_DISP_u32(sb, segment_count); 57 D_DISP_u32(sb, segment_count_ckpt); 58 D_DISP_u32(sb, segment_count_sit); 59 D_DISP_u32(sb, segment_count_nat); 60 61 D_DISP_u32(sb, segment_count_ssa); 62 D_DISP_u32(sb, segment_count_main); 63 D_DISP_u32(sb, segment0_blkaddr); 64 65 D_DISP_u32(sb, cp_blkaddr); 66 D_DISP_u32(sb, sit_blkaddr); 67 D_DISP_u32(sb, nat_blkaddr); 68 D_DISP_u32(sb, ssa_blkaddr); 69 D_DISP_u32(sb, main_blkaddr); 70 71 D_DISP_u32(sb, root_ino); 72 D_DISP_u32(sb, node_ino); 73 D_DISP_u32(sb, meta_ino); 74 D_DISP_u32(sb, cp_payload); 75 SLOGD("\n"); 76 } 77 static void dbg_print_raw_ckpt_struct(struct f2fs_checkpoint *cp) 78 { 79 SLOGD("\n"); 80 SLOGD("+--------------------------------------------------------+\n"); 81 SLOGD("| Checkpoint |\n"); 82 SLOGD("+--------------------------------------------------------+\n"); 83 84 D_DISP_u64(cp, checkpoint_ver); 85 D_DISP_u64(cp, user_block_count); 86 D_DISP_u64(cp, valid_block_count); 87 D_DISP_u32(cp, rsvd_segment_count); 88 D_DISP_u32(cp, overprov_segment_count); 89 D_DISP_u32(cp, free_segment_count); 90 91 D_DISP_u32(cp, alloc_type[CURSEG_HOT_NODE]); 92 D_DISP_u32(cp, alloc_type[CURSEG_WARM_NODE]); 93 D_DISP_u32(cp, alloc_type[CURSEG_COLD_NODE]); 94 D_DISP_u32(cp, cur_node_segno[0]); 95 D_DISP_u32(cp, cur_node_segno[1]); 96 D_DISP_u32(cp, cur_node_segno[2]); 97 98 D_DISP_u32(cp, cur_node_blkoff[0]); 99 D_DISP_u32(cp, cur_node_blkoff[1]); 100 D_DISP_u32(cp, cur_node_blkoff[2]); 101 102 103 D_DISP_u32(cp, alloc_type[CURSEG_HOT_DATA]); 104 D_DISP_u32(cp, alloc_type[CURSEG_WARM_DATA]); 105 D_DISP_u32(cp, alloc_type[CURSEG_COLD_DATA]); 106 D_DISP_u32(cp, cur_data_segno[0]); 107 D_DISP_u32(cp, cur_data_segno[1]); 108 D_DISP_u32(cp, cur_data_segno[2]); 109 110 D_DISP_u32(cp, cur_data_blkoff[0]); 111 D_DISP_u32(cp, cur_data_blkoff[1]); 112 D_DISP_u32(cp, cur_data_blkoff[2]); 113 114 D_DISP_u32(cp, ckpt_flags); 115 D_DISP_u32(cp, cp_pack_total_block_count); 116 D_DISP_u32(cp, cp_pack_start_sum); 117 D_DISP_u32(cp, valid_node_count); 118 D_DISP_u32(cp, valid_inode_count); 119 D_DISP_u32(cp, next_free_nid); 120 D_DISP_u32(cp, sit_ver_bitmap_bytesize); 121 D_DISP_u32(cp, nat_ver_bitmap_bytesize); 122 D_DISP_u32(cp, checksum_offset); 123 D_DISP_u64(cp, elapsed_time); 124 125 D_DISP_u32(cp, sit_nat_version_bitmap[0]); 126 SLOGD("\n\n"); 127 } 128 129 static void dbg_print_info_struct(struct f2fs_info *info) 130 { 131 SLOGD("\n"); 132 SLOGD("+--------------------------------------------------------+\n"); 133 SLOGD("| F2FS_INFO |\n"); 134 SLOGD("+--------------------------------------------------------+\n"); 135 SLOGD("blocks_per_segment: %" PRIu64, info->blocks_per_segment); 136 SLOGD("block_size: %d", info->block_size); 137 SLOGD("sit_bmp loc: %p", info->sit_bmp); 138 SLOGD("sit_bmp_size: %d", info->sit_bmp_size); 139 SLOGD("blocks_per_sit: %" PRIu64, info->blocks_per_sit); 140 SLOGD("sit_blocks loc: %p", info->sit_blocks); 141 SLOGD("sit_sums loc: %p", info->sit_sums); 142 SLOGD("sit_sums num: %d", le16_to_cpu(info->sit_sums->journal.n_sits)); 143 unsigned int i; 144 for(i = 0; i < (le16_to_cpu(info->sit_sums->journal.n_sits)); i++) { 145 SLOGD("entry %d in journal entries is for segment %d", i, 146 le32_to_cpu(segno_in_journal(&info->sit_sums->journal, i))); 147 } 148 149 SLOGD("cp_blkaddr: %" PRIu64, info->cp_blkaddr); 150 SLOGD("cp_valid_cp_blkaddr: %" PRIu64, info->cp_valid_cp_blkaddr); 151 SLOGD("sit_blkaddr: %" PRIu64, info->sit_blkaddr); 152 SLOGD("nat_blkaddr: %" PRIu64, info->nat_blkaddr); 153 SLOGD("ssa_blkaddr: %" PRIu64, info->ssa_blkaddr); 154 SLOGD("main_blkaddr: %" PRIu64, info->main_blkaddr); 155 SLOGD("total_user_used: %" PRIu64, info->total_user_used); 156 SLOGD("total_blocks: %" PRIu64, info->total_blocks); 157 SLOGD("\n\n"); 158 } 159 160 161 /* read blocks */ 162 static int read_structure(int fd, unsigned long long start, void *buf, ssize_t len) 163 { 164 off64_t ret; 165 166 ret = lseek64(fd, start, SEEK_SET); 167 if (ret < 0) { 168 SLOGE("failed to seek\n"); 169 return ret; 170 } 171 172 ret = read(fd, buf, len); 173 if (ret < 0) { 174 SLOGE("failed to read\n"); 175 return ret; 176 } 177 if (ret != len) { 178 SLOGE("failed to read all\n"); 179 return -1; 180 } 181 return 0; 182 } 183 184 static int read_structure_blk(int fd, unsigned long long start_blk, void *buf, size_t len) 185 { 186 return read_structure(fd, F2FS_BLKSIZE*start_blk, buf, F2FS_BLKSIZE * len); 187 } 188 189 static int read_f2fs_sb(int fd, struct f2fs_super_block *sb) 190 { 191 int rc; 192 rc = read_structure(fd, F2FS_SUPER_OFFSET, sb, sizeof(*sb)); 193 if (le32_to_cpu(sb->magic) != F2FS_SUPER_MAGIC) { 194 SLOGE("Not a valid F2FS super block. Magic:%#08x != %#08x", 195 le32_to_cpu(sb->magic), F2FS_SUPER_MAGIC); 196 return -1; 197 } 198 return 0; 199 } 200 201 unsigned int get_f2fs_filesystem_size_sec(char *dev) 202 { 203 int fd; 204 if ((fd = open(dev, O_RDONLY)) < 0) { 205 SLOGE("Cannot open device to get filesystem size "); 206 return 0; 207 } 208 struct f2fs_super_block sb; 209 if(read_f2fs_sb(fd, &sb)) 210 return 0; 211 return (unsigned int)(le64_to_cpu(sb.block_count)*F2FS_BLKSIZE/DEFAULT_SECTOR_SIZE); 212 } 213 214 static struct f2fs_checkpoint *validate_checkpoint(block_t cp_addr, 215 unsigned long long *version, int fd) 216 { 217 unsigned char *cp_block_1, *cp_block_2; 218 struct f2fs_checkpoint *cp_block, *cp_ret; 219 u64 cp1_version = 0, cp2_version = 0; 220 221 cp_block_1 = malloc(F2FS_BLKSIZE); 222 if (!cp_block_1) 223 return NULL; 224 225 /* Read the 1st cp block in this CP pack */ 226 if (read_structure_blk(fd, cp_addr, cp_block_1, 1)) 227 goto invalid_cp1; 228 229 /* get the version number */ 230 cp_block = (struct f2fs_checkpoint *)cp_block_1; 231 232 cp1_version = le64_to_cpu(cp_block->checkpoint_ver); 233 234 cp_block_2 = malloc(F2FS_BLKSIZE); 235 if (!cp_block_2) { 236 goto invalid_cp1; 237 } 238 /* Read the 2nd cp block in this CP pack */ 239 cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; 240 if (read_structure_blk(fd, cp_addr, cp_block_2, 1)) { 241 goto invalid_cp2; 242 } 243 244 cp_block = (struct f2fs_checkpoint *)cp_block_2; 245 246 cp2_version = le64_to_cpu(cp_block->checkpoint_ver); 247 248 if (cp2_version == cp1_version) { 249 *version = cp2_version; 250 free(cp_block_2); 251 return (struct f2fs_checkpoint *)cp_block_1; 252 } 253 254 /* There must be something wrong with this checkpoint */ 255 invalid_cp2: 256 free(cp_block_2); 257 invalid_cp1: 258 free(cp_block_1); 259 return NULL; 260 } 261 262 int get_valid_checkpoint_info(int fd, struct f2fs_super_block *sb, struct f2fs_checkpoint **cp, struct f2fs_info *info) 263 { 264 struct f2fs_checkpoint *cp_block; 265 266 struct f2fs_checkpoint *cp1, *cp2, *cur_cp; 267 int cur_cp_no; 268 unsigned long blk_size; 269 unsigned long long cp1_version = 0, cp2_version = 0; 270 unsigned long long cp1_start_blk_no; 271 unsigned long long cp2_start_blk_no; 272 u32 bmp_size; 273 274 blk_size = 1U << le32_to_cpu(sb->log_blocksize); 275 276 /* 277 * Find valid cp by reading both packs and finding most recent one. 278 */ 279 cp1_start_blk_no = le32_to_cpu(sb->cp_blkaddr); 280 cp1 = validate_checkpoint(cp1_start_blk_no, &cp1_version, fd); 281 282 /* The second checkpoint pack should start at the next segment */ 283 cp2_start_blk_no = cp1_start_blk_no + (1 << le32_to_cpu(sb->log_blocks_per_seg)); 284 cp2 = validate_checkpoint(cp2_start_blk_no, &cp2_version, fd); 285 286 if (cp1 && cp2) { 287 if (ver_after(cp2_version, cp1_version)) { 288 cur_cp = cp2; 289 info->cp_valid_cp_blkaddr = cp2_start_blk_no; 290 free(cp1); 291 } else { 292 cur_cp = cp1; 293 info->cp_valid_cp_blkaddr = cp1_start_blk_no; 294 free(cp2); 295 } 296 } else if (cp1) { 297 cur_cp = cp1; 298 info->cp_valid_cp_blkaddr = cp1_start_blk_no; 299 } else if (cp2) { 300 cur_cp = cp2; 301 info->cp_valid_cp_blkaddr = cp2_start_blk_no; 302 } else { 303 goto fail_no_cp; 304 } 305 306 *cp = cur_cp; 307 308 return 0; 309 310 fail_no_cp: 311 SLOGE("Valid Checkpoint not found!!"); 312 return -EINVAL; 313 } 314 315 static int gather_sit_info(int fd, struct f2fs_info *info) 316 { 317 u64 num_segments = (info->total_blocks - info->main_blkaddr 318 + info->blocks_per_segment - 1) / info->blocks_per_segment; 319 u64 num_sit_blocks = (num_segments + SIT_ENTRY_PER_BLOCK - 1) / SIT_ENTRY_PER_BLOCK; 320 u64 sit_block; 321 322 info->sit_blocks = malloc(num_sit_blocks * sizeof(struct f2fs_sit_block)); 323 if (!info->sit_blocks) 324 return -1; 325 326 for(sit_block = 0; sit_block<num_sit_blocks; sit_block++) { 327 off64_t address = info->sit_blkaddr + sit_block; 328 329 if (f2fs_test_bit(sit_block, info->sit_bmp)) 330 address += info->blocks_per_sit; 331 332 SLOGD("Reading cache block starting at block %"PRIu64, address); 333 if (read_structure(fd, address * F2FS_BLKSIZE, &info->sit_blocks[sit_block], sizeof(struct f2fs_sit_block))) { 334 SLOGE("Could not read sit block at block %"PRIu64, address); 335 free(info->sit_blocks); 336 return -1; 337 } 338 } 339 return 0; 340 } 341 342 static inline int is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) 343 { 344 unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); 345 return !!(ckpt_flags & f); 346 } 347 348 static inline u64 sum_blk_addr(struct f2fs_checkpoint *cp, struct f2fs_info *info, int base, int type) 349 { 350 return info->cp_valid_cp_blkaddr + le32_to_cpu(cp->cp_pack_total_block_count) 351 - (base + 1) + type; 352 } 353 354 static int get_sit_summary(int fd, struct f2fs_info *info, struct f2fs_checkpoint *cp) 355 { 356 char buffer[F2FS_BLKSIZE]; 357 358 info->sit_sums = calloc(1, sizeof(struct f2fs_summary_block)); 359 if (!info->sit_sums) 360 return -1; 361 362 /* CURSEG_COLD_DATA where the journaled SIT entries are. */ 363 if (is_set_ckpt_flags(cp, CP_COMPACT_SUM_FLAG)) { 364 if (read_structure_blk(fd, info->cp_valid_cp_blkaddr + le32_to_cpu(cp->cp_pack_start_sum), buffer, 1)) 365 return -1; 366 memcpy(&info->sit_sums->journal.n_sits, &buffer[SUM_JOURNAL_SIZE], SUM_JOURNAL_SIZE); 367 } else { 368 u64 blk_addr; 369 if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) 370 blk_addr = sum_blk_addr(cp, info, NR_CURSEG_TYPE, CURSEG_COLD_DATA); 371 else 372 blk_addr = sum_blk_addr(cp, info, NR_CURSEG_DATA_TYPE, CURSEG_COLD_DATA); 373 374 if (read_structure_blk(fd, blk_addr, buffer, 1)) 375 return -1; 376 377 memcpy(info->sit_sums, buffer, sizeof(struct f2fs_summary_block)); 378 } 379 return 0; 380 } 381 382 struct f2fs_info *generate_f2fs_info(int fd) 383 { 384 struct f2fs_super_block *sb = NULL; 385 struct f2fs_checkpoint *cp = NULL; 386 struct f2fs_info *info; 387 388 info = calloc(1, sizeof(*info)); 389 if (!info) { 390 SLOGE("Out of memory!"); 391 return NULL; 392 } 393 394 sb = malloc(sizeof(*sb)); 395 if(!sb) { 396 SLOGE("Out of memory!"); 397 free(info); 398 return NULL; 399 } 400 if (read_f2fs_sb(fd, sb)) { 401 SLOGE("Failed to read superblock"); 402 free(info); 403 free(sb); 404 return NULL; 405 } 406 dbg_print_raw_sb_info(sb); 407 408 info->cp_blkaddr = le32_to_cpu(sb->cp_blkaddr); 409 info->sit_blkaddr = le32_to_cpu(sb->sit_blkaddr); 410 info->nat_blkaddr = le32_to_cpu(sb->nat_blkaddr); 411 info->ssa_blkaddr = le32_to_cpu(sb->ssa_blkaddr); 412 info->main_blkaddr = le32_to_cpu(sb->main_blkaddr); 413 info->block_size = F2FS_BLKSIZE; 414 info->total_blocks = sb->block_count; 415 info->blocks_per_sit = (le32_to_cpu(sb->segment_count_sit) >> 1) << le32_to_cpu(sb->log_blocks_per_seg); 416 info->blocks_per_segment = 1U << le32_to_cpu(sb->log_blocks_per_seg); 417 418 if (get_valid_checkpoint_info(fd, sb, &cp, info)) 419 goto error; 420 dbg_print_raw_ckpt_struct(cp); 421 422 info->total_user_used = le32_to_cpu(cp->valid_block_count); 423 424 u32 bmp_size = le32_to_cpu(cp->sit_ver_bitmap_bytesize); 425 426 /* get sit validity bitmap */ 427 info->sit_bmp = malloc(bmp_size); 428 if(!info->sit_bmp) { 429 SLOGE("Out of memory!"); 430 goto error; 431 } 432 433 info->sit_bmp_size = bmp_size; 434 if (read_structure(fd, info->cp_valid_cp_blkaddr * F2FS_BLKSIZE 435 + offsetof(struct f2fs_checkpoint, sit_nat_version_bitmap), 436 info->sit_bmp, bmp_size)) { 437 SLOGE("Error getting SIT validity bitmap"); 438 goto error; 439 } 440 441 if (gather_sit_info(fd , info)) { 442 SLOGE("Error getting SIT information"); 443 goto error; 444 } 445 if (get_sit_summary(fd, info, cp)) { 446 SLOGE("Error getting SIT entries in summary area"); 447 goto error; 448 } 449 dbg_print_info_struct(info); 450 return info; 451 error: 452 free(sb); 453 free(cp); 454 free_f2fs_info(info); 455 return NULL; 456 } 457 458 void free_f2fs_info(struct f2fs_info *info) 459 { 460 if (info) { 461 free(info->sit_blocks); 462 info->sit_blocks = NULL; 463 464 free(info->sit_bmp); 465 info->sit_bmp = NULL; 466 467 free(info->sit_sums); 468 info->sit_sums = NULL; 469 } 470 free(info); 471 } 472 473 u64 get_num_blocks_used(struct f2fs_info *info) 474 { 475 return info->main_blkaddr + info->total_user_used; 476 } 477 478 int f2fs_test_bit(unsigned int nr, const char *p) 479 { 480 int mask; 481 char *addr = (char *)p; 482 483 addr += (nr >> 3); 484 mask = 1 << (7 - (nr & 0x07)); 485 return (mask & *addr) != 0; 486 } 487 488 int run_on_used_blocks(u64 startblock, struct f2fs_info *info, int (*func)(u64 pos, void *data), void *data) { 489 struct f2fs_sit_block sit_block_cache; 490 struct f2fs_sit_entry * sit_entry; 491 u64 sit_block_num_cur = 0, segnum = 0, block_offset; 492 u64 block; 493 unsigned int used, found, started = 0, i; 494 495 block = startblock; 496 while (block < info->total_blocks) { 497 /* TODO: Save only relevant portions of metadata */ 498 if (block < info->main_blkaddr) { 499 if (func(block, data)) { 500 SLOGI("func error"); 501 return -1; 502 } 503 } else { 504 /* Main Section */ 505 segnum = (block - info->main_blkaddr)/info->blocks_per_segment; 506 507 /* check the SIT entries in the journal */ 508 found = 0; 509 for(i = 0; i < le16_to_cpu(info->sit_sums->journal.n_sits); i++) { 510 if (le32_to_cpu(segno_in_journal(&info->sit_sums->journal, i)) == segnum) { 511 sit_entry = &sit_in_journal(&info->sit_sums->journal, i); 512 found = 1; 513 break; 514 } 515 } 516 517 /* get SIT entry from SIT section */ 518 if (!found) { 519 sit_block_num_cur = segnum / SIT_ENTRY_PER_BLOCK; 520 sit_entry = &info->sit_blocks[sit_block_num_cur].entries[segnum % SIT_ENTRY_PER_BLOCK]; 521 } 522 523 block_offset = (block - info->main_blkaddr) % info->blocks_per_segment; 524 525 if (block_offset == 0 && GET_SIT_VBLOCKS(sit_entry) == 0) { 526 block += info->blocks_per_segment; 527 continue; 528 } 529 530 used = f2fs_test_bit(block_offset, (char *)sit_entry->valid_map); 531 if(used) 532 if (func(block, data)) 533 return -1; 534 } 535 536 block++; 537 } 538 return 0; 539 } 540 541 struct privdata 542 { 543 int count; 544 int infd; 545 int outfd; 546 char* buf; 547 char *zbuf; 548 int done; 549 struct f2fs_info *info; 550 }; 551 552 553 /* 554 * This is a simple test program. It performs a block to block copy of a 555 * filesystem, replacing blocks identified as unused with 0's. 556 */ 557 558 int copy_used(u64 pos, void *data) 559 { 560 struct privdata *d = data; 561 char *buf; 562 int pdone = (pos * 100) / d->info->total_blocks; 563 if (pdone > d->done) { 564 d->done = pdone; 565 printf("Done with %d percent\n", d->done); 566 } 567 568 d->count++; 569 buf = d->buf; 570 if(read_structure_blk(d->infd, (unsigned long long)pos, d->buf, 1)) { 571 printf("Error reading!!!\n"); 572 return -1; 573 } 574 575 off64_t ret; 576 ret = lseek64(d->outfd, pos * F2FS_BLKSIZE, SEEK_SET); 577 if (ret < 0) { 578 SLOGE("failed to seek\n"); 579 return ret; 580 } 581 582 ret = write(d->outfd, d->buf, F2FS_BLKSIZE); 583 if (ret < 0) { 584 SLOGE("failed to write\n"); 585 return ret; 586 } 587 if (ret != F2FS_BLKSIZE) { 588 SLOGE("failed to read all\n"); 589 return -1; 590 } 591 return 0; 592 } 593 594 int main(int argc, char **argv) 595 { 596 if (argc != 3) 597 printf("Usage: %s fs_file_in fs_file_out\n", argv[0]); 598 char *in = argv[1]; 599 char *out = argv[2]; 600 int infd, outfd; 601 602 if ((infd = open(in, O_RDONLY)) < 0) { 603 SLOGE("Cannot open device"); 604 return 0; 605 } 606 if ((outfd = open(out, O_WRONLY|O_CREAT, S_IRUSR | S_IWUSR)) < 0) { 607 SLOGE("Cannot open output"); 608 return 0; 609 } 610 611 struct privdata d; 612 d.infd = infd; 613 d.outfd = outfd; 614 d.count = 0; 615 struct f2fs_info *info = generate_f2fs_info(infd); 616 if (!info) { 617 printf("Failed to generate info!"); 618 return -1; 619 } 620 char *buf = malloc(F2FS_BLKSIZE); 621 char *zbuf = calloc(1, F2FS_BLKSIZE); 622 d.buf = buf; 623 d.zbuf = zbuf; 624 d.done = 0; 625 d.info = info; 626 int expected_count = get_num_blocks_used(info); 627 run_on_used_blocks(0, info, ©_used, &d); 628 printf("Copied %d blocks. Expected to copy %d\n", d.count, expected_count); 629 ftruncate64(outfd, info->total_blocks * F2FS_BLKSIZE); 630 free_f2fs_info(info); 631 free(buf); 632 free(zbuf); 633 close(infd); 634 close(outfd); 635 return 0; 636 } 637