Home | History | Annotate | Download | only in f2fs_utils
      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, &copy_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