1 /**
2 * main.c
3 *
4 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com/
6 * Copyright (c) 2015 Jaegeuk Kim <[email protected]>
7 * : implement defrag.f2fs
8 * Copyright (C) 2015 Huawei Ltd.
9 * Hou Pengyang <[email protected]>
10 * Liu Shuoran <[email protected]>
11 * Jaegeuk Kim <[email protected]>
12 * : add sload.f2fs
13 * Copyright (c) 2019 Google Inc.
14 * Robin Hsu <[email protected]>
15 * : add cache layer
16 * Copyright (c) 2020 Google Inc.
17 * Robin Hsu <[email protected]>
18 * : add sload compression support
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License version 2 as
22 * published by the Free Software Foundation.
23 */
24 #include "fsck.h"
25 #include <libgen.h>
26 #include <ctype.h>
27 #include <time.h>
28 #include <getopt.h>
29 #include <stdbool.h>
30 #include "quotaio.h"
31 #include "compress.h"
32 #ifdef WITH_INJECT
33 #include "inject.h"
34 #else
inject_usage(void)35 static void inject_usage(void)
36 {
37 MSG(0, "\ninject.f2fs not supported\n");
38 exit(1);
39 }
40 #endif
41
42 struct f2fs_fsck gfsck;
43
44 INIT_FEATURE_TABLE;
45
46 #if defined(WITH_SLOAD) || defined(WITH_DUMP)
absolute_path(const char * file)47 static char *absolute_path(const char *file)
48 {
49 char *ret;
50 char cwd[PATH_MAX];
51
52 if (file[0] != '/') {
53 if (getcwd(cwd, PATH_MAX) == NULL) {
54 fprintf(stderr, "Failed to getcwd\n");
55 exit(EXIT_FAILURE);
56 }
57 ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
58 if (ret)
59 sprintf(ret, "%s/%s", cwd, file);
60 } else
61 ret = strdup(file);
62 return ret;
63 }
64 #endif
65
fsck_usage()66 void fsck_usage()
67 {
68 MSG(0, "\nUsage: fsck.f2fs [options] device\n");
69 MSG(0, "[options]:\n");
70 MSG(0, " -a check/fix potential corruption, reported by f2fs\n");
71 MSG(0, " -c <num-cache-entry> set number of cache entries"
72 " (default 0)\n");
73 MSG(0, " -m <max-hash-collision> set max cache hash collision"
74 " (default 16)\n");
75 MSG(0, " -C encoding[:flag1,flag2] Set options for enabling"
76 " casefolding\n");
77 MSG(0, " -d debug level [default:0]\n");
78 MSG(0, " -f check/fix entire partition\n");
79 MSG(0, " -g add default options\n");
80 MSG(0, " -H support write hint\n");
81 MSG(0, " -l show superblock/checkpoint\n");
82 MSG(0, " -M show a file map\n");
83 MSG(0, " -O feature1[feature2,feature3,...] e.g. \"encrypt\"\n");
84 MSG(0, " -p preen mode [default:0 the same as -a [0|1|2]]\n");
85 MSG(0, " -S sparse_mode\n");
86 MSG(0, " -t show directory tree\n");
87 MSG(0, " -q preserve quota limits\n");
88 MSG(0, " -y fix all the time\n");
89 MSG(0, " -V print the version number and exit\n");
90 MSG(0, " --dry-run do not really fix corruptions\n");
91 MSG(0, " --no-kernel-check skips detecting kernel change\n");
92 MSG(0, " --kernel-check checks kernel change\n");
93 MSG(0, " --debug-cache to debug cache when -c is used\n");
94 exit(1);
95 }
96
dump_usage()97 void dump_usage()
98 {
99 MSG(0, "\nUsage: dump.f2fs [options] device\n");
100 MSG(0, "[options]:\n");
101 MSG(0, " -d debug level [default:0]\n");
102 MSG(0, " -i inode no (hex)\n");
103 MSG(0, " -I inode no (hex) scan full disk\n");
104 MSG(0, " -n [NAT dump nid from #1~#2 (decimal), for all 0~-1]\n");
105 MSG(0, " -M show a block map\n");
106 MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
107 MSG(0, " -S sparse_mode\n");
108 MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
109 MSG(0, " -b blk_addr (in 4KB)\n");
110 MSG(0, " -r dump out from the root inode\n");
111 MSG(0, " -f do not prompt before dumping\n");
112 MSG(0, " -H support write hint\n");
113 MSG(0, " -y alias for -f\n");
114 MSG(0, " -o dump inodes to the given path\n");
115 MSG(0, " -P preserve mode/owner/group for dumped inode\n");
116 MSG(0, " -L Preserves symlinks. Otherwise symlinks are dumped as regular files.\n");
117 MSG(0, " -V print the version number and exit\n");
118
119 exit(1);
120 }
121
defrag_usage()122 void defrag_usage()
123 {
124 MSG(0, "\nUsage: defrag.f2fs [options] device\n");
125 MSG(0, "[options]:\n");
126 MSG(0, " -d debug level [default:0]\n");
127 MSG(0, " -H support write hint\n");
128 MSG(0, " -s start block address [default: main_blkaddr]\n");
129 MSG(0, " -S sparse_mode\n");
130 MSG(0, " -l length [default:512 (2MB)]\n");
131 MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n");
132 MSG(0, " -i set direction as shrink [default: expand]\n");
133 MSG(0, " -V print the version number and exit\n");
134 exit(1);
135 }
136
resize_usage()137 void resize_usage()
138 {
139 MSG(0, "\nUsage: resize.f2fs [options] device\n");
140 MSG(0, "[options]:\n");
141 MSG(0, " -d debug level [default:0]\n");
142 MSG(0, " -H support write hint\n");
143 MSG(0, " -i extended node bitmap, node ratio is 20%% by default\n");
144 MSG(0, " -o overprovision percentage [default:auto]\n");
145 MSG(0, " -s safe resize (Does not resize metadata)\n");
146 MSG(0, " -t target sectors [default: device size]\n");
147 MSG(0, " -V print the version number and exit\n");
148 exit(1);
149 }
150
sload_usage()151 void sload_usage()
152 {
153 MSG(0, "\nUsage: sload.f2fs [options] device\n");
154 MSG(0, "[options]:\n");
155 MSG(0, " -C fs_config\n");
156 MSG(0, " -f source directory [path of the source directory]\n");
157 MSG(0, " -p product out directory\n");
158 MSG(0, " -s file_contexts\n");
159 MSG(0, " -S sparse_mode\n");
160 MSG(0, " -t mount point [prefix of target fs path, default:/]\n");
161 MSG(0, " -T timestamp\n");
162 MSG(0, " -P preserve owner: user and group\n");
163 MSG(0, " -c enable compression (default allow policy)\n");
164 MSG(0, " ------------ Compression sub-options -----------------\n");
165 MSG(0, " -L <log-of-blocks-per-cluster>, default 2\n");
166 MSG(0, " -a <algorithm> compression algorithm, default LZ4\n");
167 MSG(0, " -x <ext> compress files except for these extensions.\n");
168 MSG(0, " -i <ext> compress files with these extensions only.\n");
169 MSG(0, " * -i or -x: use it many times for multiple extensions.\n");
170 MSG(0, " * -i and -x cannot be used together..\n");
171 MSG(0, " -m <num> min compressed blocks per cluster\n");
172 MSG(0, " -r read only (to release unused blocks) for compressed "
173 "files\n");
174 MSG(0, " ------------------------------------------------------\n");
175 MSG(0, " -d debug level [default:0]\n");
176 MSG(0, " -V print the version number and exit\n");
177 exit(1);
178 }
179
label_usage()180 void label_usage()
181 {
182 MSG(0, "\nUsage: f2fslabel [options] device [volume-label]\n");
183 MSG(0, "[options]:\n");
184 MSG(0, " -V print the version number and exit\n");
185 exit(1);
186 }
187
is_digits(char * optarg)188 int is_digits(char *optarg)
189 {
190 unsigned int i;
191
192 for (i = 0; i < strlen(optarg); i++)
193 if (!isdigit(optarg[i]))
194 break;
195 return i == strlen(optarg);
196 }
197
error_out(char * prog)198 static void error_out(char *prog)
199 {
200 if (!strcmp("fsck.f2fs", prog))
201 fsck_usage();
202 else if (!strcmp("dump.f2fs", prog))
203 dump_usage();
204 else if (!strcmp("defrag.f2fs", prog))
205 defrag_usage();
206 else if (!strcmp("resize.f2fs", prog))
207 resize_usage();
208 else if (!strcmp("sload.f2fs", prog))
209 sload_usage();
210 else if (!strcmp("f2fslabel", prog))
211 label_usage();
212 else if (!strcmp("inject.f2fs", prog))
213 inject_usage();
214 else
215 MSG(0, "\nWrong program.\n");
216 }
217
__add_fsck_options(void)218 static void __add_fsck_options(void)
219 {
220 /* -a */
221 c.auto_fix = 1;
222 }
223
add_default_options(void)224 static void add_default_options(void)
225 {
226 switch (c.defset) {
227 case CONF_ANDROID:
228 __add_fsck_options();
229 }
230 c.quota_fix = 1;
231 }
232
f2fs_parse_options(int argc,char * argv[])233 void f2fs_parse_options(int argc, char *argv[])
234 {
235 int option = 0;
236 char *prog = basename(argv[0]);
237 int err = NOERROR;
238 #ifdef WITH_ANDROID
239 int i;
240
241 /* Allow prog names (e.g, sload_f2fs, fsck_f2fs, etc) */
242 for (i = 0; i < strlen(prog); i++) {
243 if (prog[i] == '_')
244 prog[i] = '.';
245 }
246 #endif
247 if (argc < 2) {
248 MSG(0, "\tError: Device not specified\n");
249 error_out(prog);
250 }
251
252 if (!strcmp("fsck.f2fs", prog)) {
253 const char *option_string = ":aC:c:m:Md:fg:HlO:p:q:StyV";
254 int opt = 0, val;
255 char *token;
256 struct option long_opt[] = {
257 {"dry-run", no_argument, 0, 1},
258 {"no-kernel-check", no_argument, 0, 2},
259 {"kernel-check", no_argument, 0, 3},
260 {"debug-cache", no_argument, 0, 4},
261 {0, 0, 0, 0}
262 };
263
264 c.func = FSCK;
265 c.cache_config.max_hash_collision = 16;
266 c.cache_config.dbg_en = false;
267 while ((option = getopt_long(argc, argv, option_string,
268 long_opt, &opt)) != EOF) {
269 switch (option) {
270 case 1:
271 c.dry_run = 1;
272 MSG(0, "Info: Dry run\n");
273 break;
274 case 2:
275 c.no_kernel_check = 1;
276 MSG(0, "Info: No Kernel Check\n");
277 break;
278 case 3:
279 c.no_kernel_check = 0;
280 MSG(0, "Info: Do Kernel Check\n");
281 break;
282 case 4:
283 c.cache_config.dbg_en = true;
284 break;
285 case 'a':
286 c.auto_fix = 1;
287 MSG(0, "Info: Automatic fix mode enabled.\n");
288 break;
289 case 'c':
290 c.cache_config.num_cache_entry = atoi(optarg);
291 break;
292 case 'm':
293 c.cache_config.max_hash_collision =
294 atoi(optarg);
295 break;
296 case 'g':
297 if (!strcmp(optarg, "android")) {
298 c.defset = CONF_ANDROID;
299 MSG(0, "Info: Set conf for android\n");
300 }
301 break;
302 case 'H':
303 c.need_whint = true;
304 c.whint = WRITE_LIFE_NOT_SET;
305 break;
306 case 'l':
307 c.layout = 1;
308 break;
309 case 'M':
310 c.show_file_map = 1;
311 break;
312 case 'O':
313 if (parse_feature(feature_table, optarg))
314 fsck_usage();
315 break;
316 case 'p':
317 /* preen mode has different levels:
318 * 0: default level, the same as -a
319 * 1: check meta
320 * 2: same as 0, but will skip some
321 * check for old kernel
322 */
323 if (optarg[0] == '-' || !is_digits(optarg) ||
324 optind == argc) {
325 MSG(0, "Info: Use default preen mode\n");
326 c.preen_mode = PREEN_MODE_0;
327 c.auto_fix = 1;
328 optind--;
329 break;
330 }
331 c.preen_mode = atoi(optarg);
332 if (c.preen_mode < 0)
333 c.preen_mode = PREEN_MODE_0;
334 else if (c.preen_mode >= PREEN_MODE_MAX)
335 c.preen_mode = PREEN_MODE_MAX - 1;
336 if (c.preen_mode == PREEN_MODE_0 ||
337 c.preen_mode == PREEN_MODE_2)
338 c.auto_fix = 1;
339 MSG(0, "Info: Fix the reported corruption in "
340 "preen mode %d\n", c.preen_mode);
341 break;
342 case 'd':
343 if (optarg[0] == '-') {
344 err = ENEED_ARG;
345 break;
346 } else if (!is_digits(optarg)) {
347 err = EWRONG_OPT;
348 break;
349 }
350 c.dbg_lv = atoi(optarg);
351 MSG(0, "Info: Debug level = %d\n", c.dbg_lv);
352 break;
353 case 'f':
354 case 'y':
355 c.fix_on = 1;
356 c.force = 1;
357 MSG(0, "Info: Force to fix corruption\n");
358 break;
359 case 'q':
360 c.preserve_limits = atoi(optarg);
361 MSG(0, "Info: Preserve quota limits = %d\n",
362 c.preserve_limits);
363 break;
364 case 'S':
365 c.sparse_mode = 1;
366 break;
367 case 't':
368 c.show_dentry = 1;
369 break;
370 case ':':
371 if (optopt == 'p') {
372 MSG(0, "Info: Use default preen mode\n");
373 c.preen_mode = PREEN_MODE_0;
374 c.auto_fix = 1;
375 } else {
376 option = optopt;
377 err = ENEED_ARG;
378 break;
379 }
380 break;
381 case 'C':
382 token = strtok(optarg, ":");
383 val = f2fs_str2encoding(token);
384 if (val < 0) {
385 MSG(0, "\tError: Unknown encoding %s\n", token);
386 fsck_usage();
387 }
388 c.s_encoding = val;
389 token = strtok(NULL, "");
390 val = f2fs_str2encoding_flags(&token, &c.s_encoding_flags);
391 if (val) {
392 MSG(0, "\tError: Unknown flag %s\n", token);
393 fsck_usage();
394 }
395 c.feature |= F2FS_FEATURE_CASEFOLD;
396 break;
397 case 'V':
398 show_version(prog);
399 exit(0);
400 case '?':
401 option = optopt;
402 fallthrough;
403 default:
404 err = EUNKNOWN_OPT;
405 break;
406 }
407 if (err != NOERROR)
408 break;
409 }
410 } else if (!strcmp("dump.f2fs", prog)) {
411 #ifdef WITH_DUMP
412 const char *option_string = "d:fi:I:n:LMo:Prs:Sa:b:Vy";
413 static struct dump_option dump_opt = {
414 .nid = 0, /* default root ino */
415 .start_nat = -1,
416 .end_nat = -1,
417 .start_sit = -1,
418 .end_sit = -1,
419 .start_ssa = -1,
420 .end_ssa = -1,
421 .blk_addr = -1,
422 .scan_nid = 0,
423 .use_root_nid = 0,
424 .base_path = NULL,
425 };
426
427 c.func = DUMP;
428 while ((option = getopt(argc, argv, option_string)) != EOF) {
429 int ret = 0;
430
431 switch (option) {
432 case 'd':
433 if (!is_digits(optarg)) {
434 err = EWRONG_OPT;
435 break;
436 }
437 c.dbg_lv = atoi(optarg);
438 MSG(0, "Info: Debug level = %d\n",
439 c.dbg_lv);
440 break;
441 case 'i':
442 if (strncmp(optarg, "0x", 2))
443 ret = sscanf(optarg, "%d",
444 &dump_opt.nid);
445 else
446 ret = sscanf(optarg, "%x",
447 &dump_opt.nid);
448 break;
449 case 'I':
450 if (strncmp(optarg, "0x", 2))
451 ret = sscanf(optarg, "%d",
452 &dump_opt.scan_nid);
453 else
454 ret = sscanf(optarg, "%x",
455 &dump_opt.scan_nid);
456 break;
457 case 'n':
458 ret = sscanf(optarg, "%d~%d",
459 &dump_opt.start_nat,
460 &dump_opt.end_nat);
461 break;
462 case 'M':
463 c.show_file_map = 1;
464 break;
465 case 's':
466 ret = sscanf(optarg, "%d~%d",
467 &dump_opt.start_sit,
468 &dump_opt.end_sit);
469 break;
470 case 'S':
471 c.sparse_mode = 1;
472 break;
473 case 'a':
474 ret = sscanf(optarg, "%d~%d",
475 &dump_opt.start_ssa,
476 &dump_opt.end_ssa);
477 break;
478 case 'b':
479 if (strncmp(optarg, "0x", 2))
480 ret = sscanf(optarg, "%d",
481 &dump_opt.blk_addr);
482 else
483 ret = sscanf(optarg, "%x",
484 &dump_opt.blk_addr);
485 break;
486 case 'y':
487 case 'f':
488 c.force = 1;
489 break;
490 case 'r':
491 dump_opt.use_root_nid = 1;
492 break;
493 case 'o':
494 dump_opt.base_path = absolute_path(optarg);
495 break;
496 case 'P':
497 #if defined(__MINGW32__)
498 MSG(0, "-P not supported for Windows\n");
499 err = EWRONG_OPT;
500 #else
501 c.preserve_perms = 1;
502 #endif
503 break;
504 case 'L':
505 #if defined(__MINGW32__)
506 MSG(0, "-L not supported for Windows\n");
507 err = EWRONG_OPT;
508 #else
509 c.preserve_symlinks = 1;
510 #endif
511 break;
512 case 'V':
513 show_version(prog);
514 exit(0);
515 default:
516 err = EUNKNOWN_OPT;
517 break;
518 }
519 ASSERT(ret >= 0);
520 if (err != NOERROR)
521 break;
522 }
523
524 c.private = &dump_opt;
525 #endif
526 } else if (!strcmp("defrag.f2fs", prog)) {
527 #ifdef WITH_DEFRAG
528 const char *option_string = "d:Hs:Sl:t:iV";
529
530 c.func = DEFRAG;
531 while ((option = getopt(argc, argv, option_string)) != EOF) {
532 int ret = 0;
533
534 switch (option) {
535 case 'd':
536 if (!is_digits(optarg)) {
537 err = EWRONG_OPT;
538 break;
539 }
540 c.dbg_lv = atoi(optarg);
541 MSG(0, "Info: Debug level = %d\n",
542 c.dbg_lv);
543 break;
544 case 'H':
545 c.need_whint = true;
546 c.whint = WRITE_LIFE_NOT_SET;
547 break;
548 case 's':
549 if (strncmp(optarg, "0x", 2))
550 ret = sscanf(optarg, "%"PRIu64"",
551 &c.defrag_start);
552 else
553 ret = sscanf(optarg, "%"PRIx64"",
554 &c.defrag_start);
555 break;
556 case 'S':
557 c.sparse_mode = 1;
558 break;
559 case 'l':
560 if (strncmp(optarg, "0x", 2))
561 ret = sscanf(optarg, "%"PRIu64"",
562 &c.defrag_len);
563 else
564 ret = sscanf(optarg, "%"PRIx64"",
565 &c.defrag_len);
566 break;
567 case 't':
568 if (strncmp(optarg, "0x", 2))
569 ret = sscanf(optarg, "%"PRIu64"",
570 &c.defrag_target);
571 else
572 ret = sscanf(optarg, "%"PRIx64"",
573 &c.defrag_target);
574 break;
575 case 'i':
576 c.defrag_shrink = 1;
577 break;
578 case 'V':
579 show_version(prog);
580 exit(0);
581 default:
582 err = EUNKNOWN_OPT;
583 break;
584 }
585 ASSERT(ret >= 0);
586 if (err != NOERROR)
587 break;
588 }
589 #endif
590 } else if (!strcmp("resize.f2fs", prog)) {
591 #ifdef WITH_RESIZE
592 const char *option_string = "d:fHst:io:V";
593
594 c.func = RESIZE;
595 while ((option = getopt(argc, argv, option_string)) != EOF) {
596 int ret = 0;
597
598 switch (option) {
599 case 'd':
600 if (!is_digits(optarg)) {
601 err = EWRONG_OPT;
602 break;
603 }
604 c.dbg_lv = atoi(optarg);
605 MSG(0, "Info: Debug level = %d\n",
606 c.dbg_lv);
607 break;
608 case 'f':
609 c.force = 1;
610 MSG(0, "Info: Force to resize\n");
611 break;
612 case 'H':
613 c.need_whint = true;
614 c.whint = WRITE_LIFE_NOT_SET;
615 break;
616 case 's':
617 c.safe_resize = 1;
618 break;
619 case 't':
620 if (strncmp(optarg, "0x", 2))
621 ret = sscanf(optarg, "%"PRIu64"",
622 &c.target_sectors);
623 else
624 ret = sscanf(optarg, "%"PRIx64"",
625 &c.target_sectors);
626 break;
627 case 'i':
628 c.large_nat_bitmap = 1;
629 break;
630 case 'o':
631 c.new_overprovision = atof(optarg);
632 break;
633 case 'V':
634 show_version(prog);
635 exit(0);
636 default:
637 err = EUNKNOWN_OPT;
638 break;
639 }
640 ASSERT(ret >= 0);
641 if (err != NOERROR)
642 break;
643 }
644 #endif
645 } else if (!strcmp("sload.f2fs", prog)) {
646 #ifdef WITH_SLOAD
647 const char *option_string = "cL:a:i:x:m:rC:d:f:p:s:St:T:VP";
648 #ifdef HAVE_LIBSELINUX
649 int max_nr_opt = (int)sizeof(c.seopt_file) /
650 sizeof(c.seopt_file[0]);
651 char *token;
652 #endif
653 char *p;
654
655 c.func = SLOAD;
656 c.compress.cc.log_cluster_size = 2;
657 c.compress.alg = COMPR_LZ4;
658 c.compress.min_blocks = 1;
659 c.compress.filter_ops = &ext_filter;
660 while ((option = getopt(argc, argv, option_string)) != EOF) {
661 unsigned int i;
662 int val;
663
664 switch (option) {
665 case 'c': /* compression support */
666 c.compress.enabled = true;
667 break;
668 case 'L': /* compression: log of blocks-per-cluster */
669 c.compress.required = true;
670 val = atoi(optarg);
671 if (val < MIN_COMPRESS_LOG_SIZE ||
672 val > MAX_COMPRESS_LOG_SIZE) {
673 MSG(0, "\tError: log of blocks per"
674 " cluster must be in the range"
675 " of %d .. %d.\n",
676 MIN_COMPRESS_LOG_SIZE,
677 MAX_COMPRESS_LOG_SIZE);
678 error_out(prog);
679 }
680 c.compress.cc.log_cluster_size = val;
681 break;
682 case 'a': /* compression: choose algorithm */
683 c.compress.required = true;
684 c.compress.alg = MAX_COMPRESS_ALGS;
685 for (i = 0; i < MAX_COMPRESS_ALGS; i++) {
686 if (!strcmp(supported_comp_names[i],
687 optarg)) {
688 c.compress.alg = i;
689 break;
690 }
691 }
692 if (c.compress.alg == MAX_COMPRESS_ALGS) {
693 MSG(0, "\tError: Unknown compression"
694 " algorithm %s\n", optarg);
695 error_out(prog);
696 }
697 break;
698 case 'i': /* compress only these extensions */
699 c.compress.required = true;
700 if (c.compress.filter == COMPR_FILTER_ALLOW) {
701 MSG(0, "\tError: could not mix option"
702 " -i and -x\n");
703 error_out(prog);
704 }
705 c.compress.filter = COMPR_FILTER_DENY;
706 c.compress.filter_ops->add(optarg);
707 break;
708 case 'x': /* compress except for these extensions */
709 c.compress.required = true;
710 if (c.compress.filter == COMPR_FILTER_DENY) {
711 MSG(0, "\tError: could not mix option"
712 " -i and -x\n");
713 error_out(prog);
714 }
715 c.compress.filter = COMPR_FILTER_ALLOW;
716 c.compress.filter_ops->add(optarg);
717 break;
718 case 'm': /* minimum compressed blocks per cluster */
719 c.compress.required = true;
720 val = atoi(optarg);
721 if (val <= 0) {
722 MSG(0, "\tError: minimum compressed"
723 " blocks per cluster must be"
724 " positive.\n");
725 error_out(prog);
726 }
727 c.compress.min_blocks = val;
728 break;
729 case 'r': /* for setting FI_COMPRESS_RELEASED */
730 c.compress.required = true;
731 c.compress.readonly = true;
732 break;
733 case 'C':
734 c.fs_config_file = absolute_path(optarg);
735 break;
736 case 'd':
737 if (!is_digits(optarg)) {
738 err = EWRONG_OPT;
739 break;
740 }
741 c.dbg_lv = atoi(optarg);
742 MSG(0, "Info: Debug level = %d\n",
743 c.dbg_lv);
744 break;
745 case 'f':
746 c.from_dir = absolute_path(optarg);
747 break;
748 case 'p':
749 c.target_out_dir = absolute_path(optarg);
750 break;
751 case 's':
752 #ifdef HAVE_LIBSELINUX
753 token = strtok(optarg, ",");
754 while (token) {
755 if (c.nr_opt == max_nr_opt) {
756 MSG(0, "\tError: Expected at most %d selinux opts\n",
757 max_nr_opt);
758 error_out(prog);
759 }
760 c.seopt_file[c.nr_opt].type =
761 SELABEL_OPT_PATH;
762 c.seopt_file[c.nr_opt].value =
763 absolute_path(token);
764 c.nr_opt++;
765 token = strtok(NULL, ",");
766 }
767 #else
768 MSG(0, "Info: Not support selinux opts\n");
769 #endif
770 break;
771 case 'S':
772 c.sparse_mode = 1;
773 break;
774 case 't':
775 c.mount_point = (char *)optarg;
776 break;
777 case 'T':
778 c.fixed_time = strtoul(optarg, &p, 0);
779 break;
780 case 'V':
781 show_version(prog);
782 exit(0);
783 case 'P':
784 c.preserve_perms = 1;
785 break;
786 default:
787 err = EUNKNOWN_OPT;
788 break;
789 }
790 if (err != NOERROR)
791 break;
792 }
793 if (c.compress.required && !c.compress.enabled) {
794 MSG(0, "\tError: compression sub-options are used"
795 " without the compression enable (-c) option\n"
796 );
797 error_out(prog);
798 }
799 if (err == NOERROR && c.compress.enabled) {
800 c.compress.cc.cluster_size = 1
801 << c.compress.cc.log_cluster_size;
802 if (c.compress.filter == COMPR_FILTER_UNASSIGNED)
803 c.compress.filter = COMPR_FILTER_ALLOW;
804 if (c.compress.min_blocks >=
805 c.compress.cc.cluster_size) {
806 MSG(0, "\tError: minimum reduced blocks by"
807 " compression per cluster must be at"
808 " most one less than blocks per"
809 " cluster, i.e. %d\n",
810 c.compress.cc.cluster_size - 1);
811 error_out(prog);
812 }
813 }
814 #endif /* WITH_SLOAD */
815 } else if (!strcmp("f2fslabel", prog)) {
816 #ifdef WITH_LABEL
817 const char *option_string = "V";
818
819 c.func = LABEL;
820 while ((option = getopt(argc, argv, option_string)) != EOF) {
821 switch (option) {
822 case 'V':
823 show_version(prog);
824 exit(0);
825 default:
826 err = EUNKNOWN_OPT;
827 break;
828 }
829 if (err != NOERROR)
830 break;
831 }
832
833 if (argc > (optind + 2)) { /* unknown argument(s) is(are) passed */
834 optind += 2;
835 err = EUNKNOWN_ARG;
836 } else if (argc == (optind + 2)) { /* change label */
837 c.vol_label = argv[optind + 1];
838 argc--;
839 } else { /* print label */
840 /*
841 * Since vol_label was initialized as "", in order to
842 * distinguish between clear label and print, set
843 * vol_label as NULL for print case
844 */
845 c.vol_label = NULL;
846 }
847 #endif /* WITH_LABEL */
848 } else if (!strcmp("inject.f2fs", prog)) {
849 #ifdef WITH_INJECT
850 static struct inject_option inject_opt = {
851 .sb = -1,
852 .cp = -1,
853 .nat = -1,
854 .sit = -1,
855 .idx = -1,
856 .nid = -1,
857 };
858
859 err = inject_parse_options(argc, argv, &inject_opt);
860 if (err < 0) {
861 err = EWRONG_OPT;
862 }
863
864 c.func = INJECT;
865 c.private = &inject_opt;
866 #endif /* WITH_INJECT */
867 }
868
869 #if defined(__MINGW32__)
870 if (c.need_whint) {
871 MSG(0, "-H not supported for Windows\n");
872 err = EWRONG_OPT;
873 }
874 #endif
875 if (err == NOERROR) {
876 add_default_options();
877
878 if (optind >= argc) {
879 MSG(0, "\tError: Device not specified\n");
880 error_out(prog);
881 }
882
883 c.devices[0].path = strdup(argv[optind]);
884 if (argc > (optind + 1)) {
885 c.dbg_lv = 0;
886 err = EUNKNOWN_ARG;
887 }
888 if (err == NOERROR)
889 return;
890 }
891
892 check_block_struct_sizes();
893 /* print out error */
894 switch (err) {
895 case EWRONG_OPT:
896 MSG(0, "\tError: Wrong option -%c %s\n", option, optarg);
897 break;
898 case ENEED_ARG:
899 MSG(0, "\tError: Need argument for -%c\n", option);
900 break;
901 case EUNKNOWN_OPT:
902 MSG(0, "\tError: Unknown option %c\n", option);
903 break;
904 case EUNKNOWN_ARG:
905 MSG(0, "\tError: Unknown argument %s\n", argv[optind]);
906 break;
907 }
908 error_out(prog);
909 }
910
do_fsck(struct f2fs_sb_info * sbi)911 static int do_fsck(struct f2fs_sb_info *sbi)
912 {
913 struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
914 u32 flag = le32_to_cpu(ckpt->ckpt_flags);
915 u32 blk_cnt;
916 struct f2fs_compr_blk_cnt cbc;
917 struct child_info child = { 0 };
918 errcode_t ret;
919
920 fsck_init(sbi);
921
922 print_cp_state(flag);
923
924 if (c.roll_forward && c.zoned_model == F2FS_ZONED_HM)
925 save_curseg_warm_node_info(sbi);
926
927 fsck_chk_and_fix_write_pointers(sbi);
928
929 fsck_chk_curseg_info(sbi);
930
931 if (!c.fix_on && !c.bug_on) {
932 switch (c.preen_mode) {
933 case PREEN_MODE_1:
934 if (fsck_chk_meta(sbi)) {
935 MSG(0, "[FSCK] F2FS metadata [Fail]");
936 MSG(0, "\tError: meta does not match, "
937 "force check all\n");
938 } else {
939 MSG(0, "[FSCK] F2FS metadata [Ok..]");
940 fsck_free(sbi);
941 return FSCK_SUCCESS;
942 }
943
944 if (!c.ro)
945 c.fix_on = 1;
946 break;
947 }
948 } else if (c.preen_mode) {
949 /*
950 * we can hit this in 3 situations:
951 * 1. fsck -f, fix_on has already been set to 1 when
952 * parsing options;
953 * 2. fsck -a && CP_FSCK_FLAG is set, fix_on has already
954 * been set to 1 when checking CP_FSCK_FLAG;
955 * 3. fsck -p 1 && error is detected, then bug_on is set,
956 * we set fix_on = 1 here, so that fsck can fix errors
957 * automatically
958 */
959 c.fix_on = 1;
960 }
961
962 fsck_chk_checkpoint(sbi);
963
964 fsck_chk_quota_node(sbi);
965
966 /* Traverse all block recursively from root inode */
967 blk_cnt = 1;
968 cbc.cnt = 0;
969 cbc.cheader_pgofs = CHEADER_PGOFS_NONE;
970
971 if (c.feature & F2FS_FEATURE_QUOTA_INO) {
972 ret = quota_init_context(sbi);
973 if (ret) {
974 ASSERT_MSG("quota_init_context failure: %d", ret);
975 return FSCK_OPERATIONAL_ERROR;
976 }
977 }
978 fsck_chk_orphan_node(sbi);
979
980 if (fsck_sanity_check_nat(sbi, sbi->root_ino_num))
981 fsck_chk_root_inode(sbi);
982
983 child.p_ino = sbi->root_ino_num;
984 fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
985 F2FS_FT_DIR, TYPE_INODE, &blk_cnt, &cbc, &child);
986 fsck_chk_quota_files(sbi);
987
988 ret = fsck_verify(sbi);
989 fsck_free(sbi);
990
991 if (!c.bug_on)
992 return FSCK_SUCCESS;
993 if (!ret)
994 return FSCK_ERROR_CORRECTED;
995 return FSCK_ERRORS_LEFT_UNCORRECTED;
996 }
997
998 #ifdef WITH_DUMP
do_dump(struct f2fs_sb_info * sbi)999 static void do_dump(struct f2fs_sb_info *sbi)
1000 {
1001 struct dump_option *opt = (struct dump_option *)c.private;
1002 struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
1003 u32 flag = le32_to_cpu(ckpt->ckpt_flags);
1004
1005 if (opt->use_root_nid)
1006 opt->nid = sbi->root_ino_num;
1007
1008 if (opt->end_nat == -1)
1009 opt->end_nat = NM_I(sbi)->max_nid;
1010 if (opt->end_sit == -1)
1011 opt->end_sit = SM_I(sbi)->main_segments;
1012 if (opt->end_ssa == -1)
1013 opt->end_ssa = SM_I(sbi)->main_segments;
1014 if (opt->start_nat != -1)
1015 nat_dump(sbi, opt->start_nat, opt->end_nat);
1016 if (opt->start_sit != -1)
1017 sit_dump(sbi, opt->start_sit, opt->end_sit);
1018 if (opt->start_ssa != -1)
1019 ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
1020 if (opt->blk_addr != -1)
1021 dump_info_from_blkaddr(sbi, opt->blk_addr);
1022 if (opt->nid)
1023 dump_node(sbi, opt->nid, c.force, opt->base_path, 1, 1, NULL);
1024 if (opt->scan_nid)
1025 dump_node_scan_disk(sbi, opt->scan_nid);
1026
1027 print_cp_state(flag);
1028
1029 }
1030 #endif
1031
1032 #ifdef WITH_DEFRAG
do_defrag(struct f2fs_sb_info * sbi)1033 static int do_defrag(struct f2fs_sb_info *sbi)
1034 {
1035 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
1036
1037 if (get_sb(feature) & F2FS_FEATURE_RO) {
1038 MSG(0, "Not support on readonly image.\n");
1039 return -1;
1040 }
1041
1042 if (get_sb(feature) & F2FS_FEATURE_DEVICE_ALIAS) {
1043 MSG(0, "Not support on image with device aliasing feature.\n");
1044 return -1;
1045 }
1046
1047 if (c.defrag_start > get_sb(block_count))
1048 goto out_range;
1049 if (c.defrag_start < SM_I(sbi)->main_blkaddr)
1050 c.defrag_start = SM_I(sbi)->main_blkaddr;
1051
1052 if (c.defrag_len == 0)
1053 c.defrag_len = sbi->blocks_per_seg;
1054
1055 if (c.defrag_start + c.defrag_len > get_sb(block_count))
1056 c.defrag_len = get_sb(block_count) - c.defrag_start;
1057
1058 if (c.defrag_target == 0) {
1059 c.defrag_target = c.defrag_start - 1;
1060 if (!c.defrag_shrink)
1061 c.defrag_target += c.defrag_len + 1;
1062 }
1063
1064 if (c.defrag_target < SM_I(sbi)->main_blkaddr ||
1065 c.defrag_target > get_sb(block_count))
1066 goto out_range;
1067 if (c.defrag_target >= c.defrag_start &&
1068 c.defrag_target < c.defrag_start + c.defrag_len)
1069 goto out_range;
1070
1071 if (c.defrag_start > c.defrag_target)
1072 MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n",
1073 c.defrag_target,
1074 c.defrag_start,
1075 c.defrag_start + c.defrag_len - 1);
1076 else
1077 MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n",
1078 c.defrag_start,
1079 c.defrag_start + c.defrag_len - 1,
1080 c.defrag_target);
1081
1082 return f2fs_defragment(sbi, c.defrag_start, c.defrag_len,
1083 c.defrag_target, c.defrag_shrink);
1084 out_range:
1085 ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"",
1086 c.defrag_start,
1087 c.defrag_start + c.defrag_len - 1,
1088 c.defrag_target);
1089 return -1;
1090 }
1091 #endif
1092
1093 #ifdef WITH_RESIZE
do_resize(struct f2fs_sb_info * sbi)1094 static int do_resize(struct f2fs_sb_info *sbi)
1095 {
1096 if (!c.target_sectors)
1097 c.target_sectors = c.total_sectors;
1098
1099 if (c.target_sectors > c.total_sectors) {
1100 ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
1101 c.target_sectors, c.total_sectors);
1102 return -1;
1103 }
1104
1105 return f2fs_resize(sbi);
1106 }
1107 #endif
1108
1109 #ifdef WITH_SLOAD
init_compr(struct f2fs_sb_info * sbi)1110 static int init_compr(struct f2fs_sb_info *sbi)
1111 {
1112 if (!c.compress.enabled)
1113 return 0;
1114
1115 if (!(sbi->raw_super->feature
1116 & cpu_to_le32(F2FS_FEATURE_COMPRESSION))) {
1117 MSG(0, "Error: Compression (-c) was requested "
1118 "but the file system is not created "
1119 "with such feature.\n");
1120 return -1;
1121 }
1122 if (!supported_comp_ops[c.compress.alg].init) {
1123 MSG(0, "Error: The selected compression algorithm is not"
1124 " supported\n");
1125 return -1;
1126 }
1127 c.compress.ops = supported_comp_ops + c.compress.alg;
1128 c.compress.ops->init(&c.compress.cc);
1129 c.compress.ops->reset(&c.compress.cc);
1130 c.compress.cc.rlen = c.compress.cc.cluster_size * F2FS_BLKSIZE;
1131 return 0;
1132 }
1133
do_sload(struct f2fs_sb_info * sbi)1134 static int do_sload(struct f2fs_sb_info *sbi)
1135 {
1136 if (!c.from_dir) {
1137 MSG(0, "Info: No source directory, but it's okay.\n");
1138 return 0;
1139 }
1140 if (!c.mount_point)
1141 c.mount_point = "/";
1142
1143 if (init_compr(sbi))
1144 return -1;
1145
1146 return f2fs_sload(sbi);
1147 }
1148 #endif
1149
1150 #ifdef WITH_LABEL
do_label(struct f2fs_sb_info * sbi)1151 static int do_label(struct f2fs_sb_info *sbi)
1152 {
1153 struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
1154
1155 if (!c.vol_label) {
1156 char label[MAX_VOLUME_NAME];
1157
1158 utf16_to_utf8(label, (const char *)sb->volume_name,
1159 MAX_VOLUME_NAME, MAX_VOLUME_NAME);
1160 MSG(0, "Info: volume label = %s\n", label);
1161 return 0;
1162 }
1163
1164 if (strlen(c.vol_label) > MAX_VOLUME_NAME) {
1165 ERR_MSG("Label should not exceed %d characters\n", MAX_VOLUME_NAME);
1166 return -1;
1167 }
1168
1169 utf8_to_utf16((char *)sb->volume_name, (const char *)c.vol_label,
1170 MAX_VOLUME_NAME, strlen(c.vol_label));
1171
1172 update_superblock(sb, SB_MASK_ALL);
1173
1174 MSG(0, "Info: volume label is changed to %s\n", c.vol_label);
1175
1176 return 0;
1177 }
1178 #endif
1179
1180 #ifdef HAVE_MACH_TIME_H
get_boottime_ns()1181 static u64 get_boottime_ns()
1182 {
1183 return mach_absolute_time();
1184 }
1185 #elif defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_BOOTTIME)
get_boottime_ns()1186 static u64 get_boottime_ns()
1187 {
1188 struct timespec t;
1189 t.tv_sec = t.tv_nsec = 0;
1190 clock_gettime(CLOCK_BOOTTIME, &t);
1191 return (u64)t.tv_sec * 1000000000LL + t.tv_nsec;
1192 }
1193 #else
get_boottime_ns()1194 static u64 get_boottime_ns()
1195 {
1196 return 0;
1197 }
1198 #endif
1199
main(int argc,char ** argv)1200 int main(int argc, char **argv)
1201 {
1202 struct f2fs_sb_info *sbi;
1203 int ret = 0, ret2;
1204 u64 start = get_boottime_ns();
1205
1206 f2fs_init_configuration();
1207
1208 f2fs_parse_options(argc, argv);
1209
1210 if (c.func != DUMP && (ret = f2fs_devs_are_umounted()) < 0) {
1211 if (ret == -EBUSY) {
1212 ret = -1;
1213 if (c.func == FSCK)
1214 ret = FSCK_OPERATIONAL_ERROR;
1215 goto quick_err;
1216 }
1217 if (!c.ro || c.func == DEFRAG) {
1218 MSG(0, "\tError: Not available on mounted device!\n");
1219 ret = -1;
1220 if (c.func == FSCK)
1221 ret = FSCK_OPERATIONAL_ERROR;
1222 goto quick_err;
1223 }
1224
1225 /* allow ro-mounted partition */
1226 if (c.force) {
1227 MSG(0, "Info: Force to check/repair FS on RO mounted device\n");
1228 } else {
1229 MSG(0, "Info: Check FS only on RO mounted device\n");
1230 c.fix_on = 0;
1231 c.auto_fix = 0;
1232 }
1233 }
1234
1235 /* Get device */
1236 if (f2fs_get_device_info() < 0 || f2fs_get_f2fs_info() < 0) {
1237 ret = -1;
1238 if (c.func == FSCK)
1239 ret = FSCK_OPERATIONAL_ERROR;
1240 goto quick_err;
1241 }
1242
1243 fsck_again:
1244 memset(&gfsck, 0, sizeof(gfsck));
1245 gfsck.sbi.fsck = &gfsck;
1246 sbi = &gfsck.sbi;
1247
1248 ret = f2fs_do_mount(sbi);
1249 if (ret != 0) {
1250 if (ret == 1) {
1251 MSG(0, "Info: No error was reported\n");
1252 ret = 0;
1253 }
1254 goto out_err;
1255 }
1256
1257 switch (c.func) {
1258 case FSCK:
1259 ret = do_fsck(sbi);
1260 break;
1261 #ifdef WITH_DUMP
1262 case DUMP:
1263 do_dump(sbi);
1264 break;
1265 #endif
1266 #ifdef WITH_DEFRAG
1267 case DEFRAG:
1268 ret = do_defrag(sbi);
1269 if (ret)
1270 goto out_err;
1271 break;
1272 #endif
1273 #ifdef WITH_RESIZE
1274 case RESIZE:
1275 if (do_resize(sbi))
1276 goto out_err;
1277 break;
1278 #endif
1279 #ifdef WITH_SLOAD
1280 case SLOAD:
1281 if (do_sload(sbi))
1282 goto out_err;
1283
1284 ret = f2fs_sparse_initialize_meta(sbi);
1285 if (ret < 0)
1286 goto out_err;
1287
1288 f2fs_do_umount(sbi);
1289
1290 /* fsck to fix missing quota */
1291 c.func = FSCK;
1292 c.fix_on = 1;
1293 goto fsck_again;
1294 #endif
1295 #ifdef WITH_LABEL
1296 case LABEL:
1297 if (do_label(sbi))
1298 goto out_err;
1299 break;
1300 #endif
1301 #ifdef WITH_INJECT
1302 case INJECT:
1303 if (do_inject(sbi))
1304 goto out_err;
1305 break;
1306 #endif
1307 default:
1308 ERR_MSG("Wrong program name\n");
1309 ASSERT(0);
1310 }
1311
1312 f2fs_do_umount(sbi);
1313
1314 if (c.func == FSCK && c.bug_on) {
1315 if (!c.ro && c.fix_on == 0 && c.auto_fix == 0 && !c.dry_run) {
1316 char ans[255] = {0};
1317 retry:
1318 printf("Do you want to fix this partition? [Y/N] ");
1319 ret2 = scanf("%s", ans);
1320 ASSERT(ret2 >= 0);
1321 if (!strcasecmp(ans, "y"))
1322 c.fix_on = 1;
1323 else if (!strcasecmp(ans, "n"))
1324 c.fix_on = 0;
1325 else
1326 goto retry;
1327
1328 if (c.fix_on)
1329 goto fsck_again;
1330 }
1331 }
1332 ret2 = f2fs_finalize_device();
1333 if (ret2) {
1334 if (c.func == FSCK)
1335 return FSCK_OPERATIONAL_ERROR;
1336 return ret2;
1337 }
1338
1339 if (c.func == SLOAD)
1340 c.compress.filter_ops->destroy();
1341
1342 if (!c.show_file_map)
1343 printf("\nDone: %lf secs\n", (get_boottime_ns() - start) / 1000000000.0);
1344 return ret;
1345
1346 out_err:
1347 if (sbi->ckpt)
1348 free(sbi->ckpt);
1349 if (sbi->raw_super)
1350 free(sbi->raw_super);
1351 quick_err:
1352 f2fs_release_sparse_resource();
1353 return ret;
1354 }
1355