• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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