xref: /aosp_15_r20/external/f2fs-tools/fsck/main.c (revision 59bfda1f02d633cd6b8b69f31eee485d40f6eef6)
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