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