1 /**
2 * main.c
3 *
4 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com/
6 * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
7 * : implement defrag.f2fs
8 * Copyright (C) 2015 Huawei Ltd.
9 * Hou Pengyang <houpengyang@huawei.com>
10 * Liu Shuoran <liushuoran@huawei.com>
11 * Jaegeuk Kim <jaegeuk@kernel.org>
12 * : add sload.f2fs
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 */
18 #include "fsck.h"
19 #include <libgen.h>
20 #include <ctype.h>
21
22 struct f2fs_fsck gfsck;
23
fsck_usage()24 void fsck_usage()
25 {
26 MSG(0, "\nUsage: fsck.f2fs [options] device\n");
27 MSG(0, "[options]:\n");
28 MSG(0, " -a check/fix potential corruption, reported by f2fs\n");
29 MSG(0, " -d debug level [default:0]\n");
30 MSG(0, " -f check/fix entire partition\n");
31 MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n");
32 MSG(0, " -t show directory tree\n");
33 exit(1);
34 }
35
dump_usage()36 void dump_usage()
37 {
38 MSG(0, "\nUsage: dump.f2fs [options] device\n");
39 MSG(0, "[options]:\n");
40 MSG(0, " -d debug level [default:0]\n");
41 MSG(0, " -i inode no (hex)\n");
42 MSG(0, " -n [NAT dump segno from #1~#2 (decimal), for all 0~-1]\n");
43 MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
44 MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
45 MSG(0, " -b blk_addr (in 4KB)\n");
46
47 exit(1);
48 }
49
defrag_usage()50 void defrag_usage()
51 {
52 MSG(0, "\nUsage: defrag.f2fs [options] device\n");
53 MSG(0, "[options]:\n");
54 MSG(0, " -d debug level [default:0]\n");
55 MSG(0, " -s start block address [default: main_blkaddr]\n");
56 MSG(0, " -l length [default:512 (2MB)]\n");
57 MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n");
58 MSG(0, " -i set direction as shrink [default: expand]\n");
59 exit(1);
60 }
61
resize_usage()62 void resize_usage()
63 {
64 MSG(0, "\nUsage: resize.f2fs [options] device\n");
65 MSG(0, "[options]:\n");
66 MSG(0, " -d debug level [default:0]\n");
67 MSG(0, " -t target sectors [default: device size]\n");
68 exit(1);
69 }
70
sload_usage()71 void sload_usage()
72 {
73 MSG(0, "\nUsage: sload.f2fs [options] device\n");
74 MSG(0, "[options]:\n");
75 MSG(0, " -f source directory [path of the source directory]\n");
76 MSG(0, " -t mount point [prefix of target fs path, default:/]\n");
77 MSG(0, " -d debug level [default:0]\n");
78 exit(1);
79 }
80
is_digits(char * optarg)81 static int is_digits(char *optarg)
82 {
83 unsigned int i;
84
85 for (i = 0; i < strlen(optarg); i++)
86 if (!isdigit(optarg[i]))
87 break;
88 return i == strlen(optarg);
89 }
90
error_out(char * prog)91 static void error_out(char *prog)
92 {
93 if (!strcmp("fsck.f2fs", prog))
94 fsck_usage();
95 else if (!strcmp("dump.f2fs", prog))
96 dump_usage();
97 else if (!strcmp("defrag.f2fs", prog))
98 defrag_usage();
99 else if (!strcmp("resize.f2fs", prog))
100 resize_usage();
101 else if (!strcmp("sload.f2fs", prog))
102 sload_usage();
103 else
104 MSG(0, "\nWrong progam.\n");
105 }
106
f2fs_parse_options(int argc,char * argv[])107 void f2fs_parse_options(int argc, char *argv[])
108 {
109 int option = 0;
110 char *prog = basename(argv[0]);
111 int err = NOERROR;
112
113 if (argc < 2) {
114 MSG(0, "\tError: Device not specified\n");
115 error_out(prog);
116 }
117
118 if (!strcmp("fsck.f2fs", prog)) {
119 const char *option_string = ":ad:fp:t";
120
121 c.func = FSCK;
122 while ((option = getopt(argc, argv, option_string)) != EOF) {
123 switch (option) {
124 case 'a':
125 c.auto_fix = 1;
126 MSG(0, "Info: Fix the reported corruption.\n");
127 break;
128 case 'p':
129 /* preen mode has different levels:
130 * 0: default level, the same as -a
131 * 1: check meta
132 */
133 if (optarg[0] == '-') {
134 c.preen_mode = PREEN_MODE_0;
135 optind--;
136 break;
137 } else if (!is_digits(optarg)) {
138 err = EWRONG_OPT;
139 break;
140 }
141 c.preen_mode = atoi(optarg);
142 if (c.preen_mode < 0)
143 c.preen_mode = PREEN_MODE_0;
144 else if (c.preen_mode >= PREEN_MODE_MAX)
145 c.preen_mode = PREEN_MODE_MAX - 1;
146 if (c.preen_mode == PREEN_MODE_0)
147 c.auto_fix = 1;
148 MSG(0, "Info: Fix the reported corruption in "
149 "preen mode %d\n", c.preen_mode);
150 break;
151 case 'd':
152 if (optarg[0] == '-') {
153 err = ENEED_ARG;
154 break;
155 } else if (!is_digits(optarg)) {
156 err = EWRONG_OPT;
157 break;
158 }
159 c.dbg_lv = atoi(optarg);
160 MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
161 break;
162 case 'f':
163 c.fix_on = 1;
164 MSG(0, "Info: Force to fix corruption\n");
165 break;
166 case 't':
167 c.show_dentry = 1;
168 break;
169
170
171 case ':':
172 if (optopt == 'p') {
173 MSG(0, "Info: Use default preen mode\n");
174 c.preen_mode = PREEN_MODE_0;
175 c.auto_fix = 1;
176 } else {
177 option = optopt;
178 err = ENEED_ARG;
179 break;
180 }
181 break;
182 case '?':
183 option = optopt;
184 default:
185 err = EUNKNOWN_OPT;
186 break;
187 }
188 if (err != NOERROR)
189 break;
190 }
191 } else if (!strcmp("dump.f2fs", prog)) {
192 const char *option_string = "d:i:n:s:a:b:";
193 static struct dump_option dump_opt = {
194 .nid = 0, /* default root ino */
195 .start_nat = -1,
196 .end_nat = -1,
197 .start_sit = -1,
198 .end_sit = -1,
199 .start_ssa = -1,
200 .end_ssa = -1,
201 .blk_addr = -1,
202 };
203
204 c.func = DUMP;
205 while ((option = getopt(argc, argv, option_string)) != EOF) {
206 int ret = 0;
207
208 switch (option) {
209 case 'd':
210 if (!is_digits(optarg)) {
211 err = EWRONG_OPT;
212 break;
213 }
214 c.dbg_lv = atoi(optarg);
215 MSG(0, "Info: Debug level = %d\n",
216 c.dbg_lv);
217 break;
218 case 'i':
219 if (strncmp(optarg, "0x", 2))
220 ret = sscanf(optarg, "%d",
221 &dump_opt.nid);
222 else
223 ret = sscanf(optarg, "%x",
224 &dump_opt.nid);
225 break;
226 case 'n':
227 ret = sscanf(optarg, "%d~%d",
228 &dump_opt.start_nat,
229 &dump_opt.end_nat);
230 break;
231 case 's':
232 ret = sscanf(optarg, "%d~%d",
233 &dump_opt.start_sit,
234 &dump_opt.end_sit);
235 break;
236 case 'a':
237 ret = sscanf(optarg, "%d~%d",
238 &dump_opt.start_ssa,
239 &dump_opt.end_ssa);
240 break;
241 case 'b':
242 if (strncmp(optarg, "0x", 2))
243 ret = sscanf(optarg, "%d",
244 &dump_opt.blk_addr);
245 else
246 ret = sscanf(optarg, "%x",
247 &dump_opt.blk_addr);
248 break;
249 default:
250 err = EUNKNOWN_OPT;
251 break;
252 }
253 ASSERT(ret >= 0);
254 if (err != NOERROR)
255 break;
256 }
257
258 c.private = &dump_opt;
259 } else if (!strcmp("defrag.f2fs", prog)) {
260 const char *option_string = "d:s:l:t:i";
261
262 c.func = DEFRAG;
263 while ((option = getopt(argc, argv, option_string)) != EOF) {
264 int ret = 0;
265
266 switch (option) {
267 case 'd':
268 if (!is_digits(optarg)) {
269 err = EWRONG_OPT;
270 break;
271 }
272 c.dbg_lv = atoi(optarg);
273 MSG(0, "Info: Debug level = %d\n",
274 c.dbg_lv);
275 break;
276 case 's':
277 if (strncmp(optarg, "0x", 2))
278 ret = sscanf(optarg, "%"PRIu64"",
279 &c.defrag_start);
280 else
281 ret = sscanf(optarg, "%"PRIx64"",
282 &c.defrag_start);
283 break;
284 case 'l':
285 if (strncmp(optarg, "0x", 2))
286 ret = sscanf(optarg, "%"PRIu64"",
287 &c.defrag_len);
288 else
289 ret = sscanf(optarg, "%"PRIx64"",
290 &c.defrag_len);
291 break;
292 case 't':
293 if (strncmp(optarg, "0x", 2))
294 ret = sscanf(optarg, "%"PRIu64"",
295 &c.defrag_target);
296 else
297 ret = sscanf(optarg, "%"PRIx64"",
298 &c.defrag_target);
299 break;
300 case 'i':
301 c.defrag_shrink = 1;
302 break;
303 default:
304 err = EUNKNOWN_OPT;
305 break;
306 }
307 ASSERT(ret >= 0);
308 if (err != NOERROR)
309 break;
310 }
311 } else if (!strcmp("resize.f2fs", prog)) {
312 const char *option_string = "d:t:";
313
314 c.func = RESIZE;
315 while ((option = getopt(argc, argv, option_string)) != EOF) {
316 int ret = 0;
317
318 switch (option) {
319 case 'd':
320 if (!is_digits(optarg)) {
321 err = EWRONG_OPT;
322 break;
323 }
324 c.dbg_lv = atoi(optarg);
325 MSG(0, "Info: Debug level = %d\n",
326 c.dbg_lv);
327 break;
328 case 't':
329 if (strncmp(optarg, "0x", 2))
330 ret = sscanf(optarg, "%"PRIu64"",
331 &c.target_sectors);
332 else
333 ret = sscanf(optarg, "%"PRIx64"",
334 &c.target_sectors);
335 break;
336 default:
337 err = EUNKNOWN_OPT;
338 break;
339 }
340 ASSERT(ret >= 0);
341 if (err != NOERROR)
342 break;
343 }
344 } else if (!strcmp("sload.f2fs", prog)) {
345 const char *option_string = "d:f:t:";
346
347 c.func = SLOAD;
348 while ((option = getopt(argc, argv, option_string)) != EOF) {
349 switch (option) {
350 case 'd':
351 if (!is_digits(optarg)) {
352 err = EWRONG_OPT;
353 break;
354 }
355 c.dbg_lv = atoi(optarg);
356 MSG(0, "Info: Debug level = %d\n",
357 c.dbg_lv);
358 break;
359 case 'f':
360 c.from_dir = (char *)optarg;
361 break;
362 case 't':
363 c.mount_point = (char *)optarg;
364 break;
365 default:
366 err = EUNKNOWN_OPT;
367 break;
368 }
369 if (err != NOERROR)
370 break;
371 }
372 }
373
374 if (optind >= argc) {
375 MSG(0, "\tError: Device not specified\n");
376 error_out(prog);
377 }
378
379 c.devices[0].path = strdup(argv[optind]);
380 if (argc > (optind + 1)) {
381 c.dbg_lv = 0;
382 err = EUNKNOWN_ARG;
383 }
384 if (err == NOERROR)
385 return;
386
387 /* print out error */
388 switch (err) {
389 case EWRONG_OPT:
390 MSG(0, "\tError: Wrong option -%c %s\n", option, optarg);
391 break;
392 case ENEED_ARG:
393 MSG(0, "\tError: Need argument for -%c\n", option);
394 break;
395 case EUNKNOWN_OPT:
396 MSG(0, "\tError: Unknown option %c\n", option);
397 break;
398 case EUNKNOWN_ARG:
399 MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
400 break;
401 }
402 error_out(prog);
403 }
404
do_fsck(struct f2fs_sb_info * sbi)405 static void do_fsck(struct f2fs_sb_info *sbi)
406 {
407 struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
408 u32 flag = le32_to_cpu(ckpt->ckpt_flags);
409 u32 blk_cnt;
410
411 fsck_init(sbi);
412
413 print_cp_state(flag);
414
415 if (!c.fix_on && !c.bug_on) {
416 switch (c.preen_mode) {
417 case PREEN_MODE_1:
418 if (fsck_chk_meta(sbi)) {
419 MSG(0, "[FSCK] F2FS metadata [Fail]");
420 MSG(0, "\tError: meta does not match, "
421 "force check all\n");
422 } else {
423 MSG(0, "[FSCK] F2FS metadata [Ok..]");
424 fsck_free(sbi);
425 return;
426 }
427
428 if (!c.ro)
429 c.fix_on = 1;
430 break;
431 }
432 } else {
433 /*
434 * we can hit this in 3 situations:
435 * 1. fsck -f, fix_on has already been set to 1 when
436 * parsing options;
437 * 2. fsck -a && CP_FSCK_FLAG is set, fix_on has already
438 * been set to 1 when checking CP_FSCK_FLAG;
439 * 3. fsck -p 1 && error is detected, then bug_on is set,
440 * we set fix_on = 1 here, so that fsck can fix errors
441 * automatically
442 */
443 c.fix_on = 1;
444 }
445
446 fsck_chk_orphan_node(sbi);
447
448 /* Traverse all block recursively from root inode */
449 blk_cnt = 1;
450 fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
451 F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
452 fsck_verify(sbi);
453 fsck_free(sbi);
454 }
455
do_dump(struct f2fs_sb_info * sbi)456 static void do_dump(struct f2fs_sb_info *sbi)
457 {
458 struct dump_option *opt = (struct dump_option *)c.private;
459 struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
460 u32 flag = le32_to_cpu(ckpt->ckpt_flags);
461
462 if (opt->end_nat == -1)
463 opt->end_nat = NM_I(sbi)->max_nid;
464 if (opt->end_sit == -1)
465 opt->end_sit = SM_I(sbi)->main_segments;
466 if (opt->end_ssa == -1)
467 opt->end_ssa = SM_I(sbi)->main_segments;
468 if (opt->start_nat != -1)
469 nat_dump(sbi);
470 if (opt->start_sit != -1)
471 sit_dump(sbi, opt->start_sit, opt->end_sit);
472 if (opt->start_ssa != -1)
473 ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
474 if (opt->blk_addr != -1)
475 dump_info_from_blkaddr(sbi, opt->blk_addr);
476 if (opt->nid)
477 dump_node(sbi, opt->nid, 0);
478
479 print_cp_state(flag);
480
481 }
482
do_defrag(struct f2fs_sb_info * sbi)483 static int do_defrag(struct f2fs_sb_info *sbi)
484 {
485 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
486
487 if (c.defrag_start > get_sb(block_count))
488 goto out_range;
489 if (c.defrag_start < SM_I(sbi)->main_blkaddr)
490 c.defrag_start = SM_I(sbi)->main_blkaddr;
491
492 if (c.defrag_len == 0)
493 c.defrag_len = sbi->blocks_per_seg;
494
495 if (c.defrag_start + c.defrag_len > get_sb(block_count))
496 c.defrag_len = get_sb(block_count) - c.defrag_start;
497
498 if (c.defrag_target == 0) {
499 c.defrag_target = c.defrag_start - 1;
500 if (!c.defrag_shrink)
501 c.defrag_target += c.defrag_len + 1;
502 }
503
504 if (c.defrag_target < SM_I(sbi)->main_blkaddr ||
505 c.defrag_target > get_sb(block_count))
506 goto out_range;
507 if (c.defrag_target >= c.defrag_start &&
508 c.defrag_target < c.defrag_start + c.defrag_len)
509 goto out_range;
510
511 if (c.defrag_start > c.defrag_target)
512 MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n",
513 c.defrag_target,
514 c.defrag_start,
515 c.defrag_start + c.defrag_len - 1);
516 else
517 MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n",
518 c.defrag_start,
519 c.defrag_start + c.defrag_len - 1,
520 c.defrag_target);
521
522 return f2fs_defragment(sbi, c.defrag_start, c.defrag_len,
523 c.defrag_target, c.defrag_shrink);
524 out_range:
525 ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"",
526 c.defrag_start,
527 c.defrag_start + c.defrag_len - 1,
528 c.defrag_target);
529 return -1;
530 }
531
do_resize(struct f2fs_sb_info * sbi)532 static int do_resize(struct f2fs_sb_info *sbi)
533 {
534 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
535
536 if (!c.target_sectors)
537 c.target_sectors = c.total_sectors;
538
539 if (c.target_sectors > c.total_sectors) {
540 ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
541 c.target_sectors, c.total_sectors);
542 return -1;
543 }
544
545 /* may different sector size */
546 if ((c.target_sectors * c.sector_size >>
547 get_sb(log_blocksize)) <= get_sb(block_count)) {
548 ASSERT_MSG("Nothing to resize, now only support resize to expand\n");
549 return -1;
550 }
551 return f2fs_resize(sbi);
552 }
553
do_sload(struct f2fs_sb_info * sbi)554 static int do_sload(struct f2fs_sb_info *sbi)
555 {
556 if (!c.from_dir) {
557 MSG(0, "\tError: Need source directory\n");
558 sload_usage();
559 return -1;
560 }
561 if (!c.mount_point)
562 c.mount_point = "/";
563
564 return f2fs_sload(sbi, c.from_dir, c.mount_point, NULL, NULL);
565 }
566
main(int argc,char ** argv)567 int main(int argc, char **argv)
568 {
569 struct f2fs_sb_info *sbi;
570 int ret = 0;
571
572 f2fs_init_configuration();
573
574 f2fs_parse_options(argc, argv);
575
576 if (f2fs_devs_are_umounted() < 0) {
577 if (errno == EBUSY)
578 return -1;
579 if (!c.ro || c.func == DEFRAG) {
580 MSG(0, "\tError: Not available on mounted device!\n");
581 return -1;
582 }
583
584 /* allow ro-mounted partition */
585 MSG(0, "Info: Check FS only due to RO\n");
586 c.fix_on = 0;
587 c.auto_fix = 0;
588 }
589
590 /* Get device */
591 if (f2fs_get_device_info() < 0)
592 return -1;
593 fsck_again:
594 memset(&gfsck, 0, sizeof(gfsck));
595 gfsck.sbi.fsck = &gfsck;
596 sbi = &gfsck.sbi;
597
598 ret = f2fs_do_mount(sbi);
599 if (ret != 0) {
600 if (ret == 1) {
601 MSG(0, "Info: No error was reported\n");
602 ret = 0;
603 }
604 goto out_err;
605 }
606
607 switch (c.func) {
608 case FSCK:
609 do_fsck(sbi);
610 break;
611 case DUMP:
612 do_dump(sbi);
613 break;
614 #ifndef WITH_ANDROID
615 case DEFRAG:
616 ret = do_defrag(sbi);
617 if (ret)
618 goto out_err;
619 break;
620 case RESIZE:
621 if (do_resize(sbi))
622 goto out_err;
623 break;
624 case SLOAD:
625 do_sload(sbi);
626 break;
627 #endif
628 }
629
630 f2fs_do_umount(sbi);
631
632 if (c.func == FSCK && c.bug_on) {
633 if (!c.ro && c.fix_on == 0 && c.auto_fix == 0) {
634 char ans[255] = {0};
635 retry:
636 printf("Do you want to fix this partition? [Y/N] ");
637 ret = scanf("%s", ans);
638 ASSERT(ret >= 0);
639 if (!strcasecmp(ans, "y"))
640 c.fix_on = 1;
641 else if (!strcasecmp(ans, "n"))
642 c.fix_on = 0;
643 else
644 goto retry;
645
646 if (c.fix_on)
647 goto fsck_again;
648 }
649 }
650 f2fs_finalize_device();
651
652 printf("\nDone.\n");
653 return 0;
654
655 out_err:
656 if (sbi->ckpt)
657 free(sbi->ckpt);
658 if (sbi->raw_super)
659 free(sbi->raw_super);
660 return ret;
661 }
662