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