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 22struct f2fs_fsck gfsck; 23 24void 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 36void 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 50void 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 62void 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 71void 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 81static 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 91static 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 107void 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 405static 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 456static 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 483static 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); 524out_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 532static 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 554static 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 567int 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; 593fsck_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}; 635retry: 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 655out_err: 656 if (sbi->ckpt) 657 free(sbi->ckpt); 658 if (sbi->raw_super) 659 free(sbi->raw_super); 660 return ret; 661} 662