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