1/**
2 * main.c
3 *
4 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5 *             http://www.samsung.com/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include "fsck.h"
12#include <libgen.h>
13
14struct f2fs_fsck gfsck;
15
16void fsck_usage()
17{
18	MSG(0, "\nUsage: fsck.f2fs [options] device\n");
19	MSG(0, "[options]:\n");
20	MSG(0, "  -a check/fix potential corruption, reported by f2fs\n");
21	MSG(0, "  -d debug level [default:0]\n");
22	MSG(0, "  -f check/fix entire partition\n");
23	MSG(0, "  -t show directory tree [-d -1]\n");
24	exit(1);
25}
26
27void dump_usage()
28{
29	MSG(0, "\nUsage: dump.f2fs [options] device\n");
30	MSG(0, "[options]:\n");
31	MSG(0, "  -d debug level [default:0]\n");
32	MSG(0, "  -i inode no (hex)\n");
33	MSG(0, "  -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n");
34	MSG(0, "  -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n");
35	MSG(0, "  -b blk_addr (in 4KB)\n");
36
37	exit(1);
38}
39
40void f2fs_parse_options(int argc, char *argv[])
41{
42	int option = 0;
43	char *prog = basename(argv[0]);
44
45	if (!strcmp("fsck.f2fs", prog)) {
46		const char *option_string = "ad:ft";
47
48		config.func = FSCK;
49		while ((option = getopt(argc, argv, option_string)) != EOF) {
50			switch (option) {
51			case 'a':
52				config.auto_fix = 1;
53				MSG(0, "Info: Fix the reported corruption.\n");
54				break;
55			case 'd':
56				config.dbg_lv = atoi(optarg);
57				MSG(0, "Info: Debug level = %d\n",
58							config.dbg_lv);
59				break;
60			case 'f':
61				config.fix_on = 1;
62				MSG(0, "Info: Force to fix corruption\n");
63				break;
64			case 't':
65				config.dbg_lv = -1;
66				break;
67			default:
68				MSG(0, "\tError: Unknown option %c\n", option);
69				fsck_usage();
70				break;
71			}
72		}
73	} else if (!strcmp("dump.f2fs", prog)) {
74		const char *option_string = "d:i:s:a:b:";
75		static struct dump_option dump_opt = {
76			.nid = 3,	/* default root ino */
77			.start_sit = -1,
78			.end_sit = -1,
79			.start_ssa = -1,
80			.end_ssa = -1,
81			.blk_addr = -1,
82		};
83
84		config.func = DUMP;
85		while ((option = getopt(argc, argv, option_string)) != EOF) {
86			int ret = 0;
87
88			switch (option) {
89			case 'd':
90				config.dbg_lv = atoi(optarg);
91				MSG(0, "Info: Debug level = %d\n",
92							config.dbg_lv);
93				break;
94			case 'i':
95				if (strncmp(optarg, "0x", 2))
96					ret = sscanf(optarg, "%d",
97							&dump_opt.nid);
98				else
99					ret = sscanf(optarg, "%x",
100							&dump_opt.nid);
101				break;
102			case 's':
103				ret = sscanf(optarg, "%d~%d",
104							&dump_opt.start_sit,
105							&dump_opt.end_sit);
106				break;
107			case 'a':
108				ret = sscanf(optarg, "%d~%d",
109							&dump_opt.start_ssa,
110							&dump_opt.end_ssa);
111				break;
112			case 'b':
113				if (strncmp(optarg, "0x", 2))
114					ret = sscanf(optarg, "%d",
115							&dump_opt.blk_addr);
116				else
117					ret = sscanf(optarg, "%x",
118							&dump_opt.blk_addr);
119				break;
120			default:
121				MSG(0, "\tError: Unknown option %c\n", option);
122				dump_usage();
123				break;
124			}
125			ASSERT(ret >= 0);
126		}
127
128		config.private = &dump_opt;
129	}
130
131	if ((optind + 1) != argc) {
132		MSG(0, "\tError: Device not specified\n");
133		if (config.func == FSCK)
134			fsck_usage();
135		else if (config.func == DUMP)
136			dump_usage();
137	}
138	config.device_name = argv[optind];
139}
140
141static void do_fsck(struct f2fs_sb_info *sbi)
142{
143	u32 blk_cnt;
144
145	fsck_init(sbi);
146
147	fsck_chk_orphan_node(sbi);
148
149	/* Traverse all block recursively from root inode */
150	blk_cnt = 1;
151	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num,
152			F2FS_FT_DIR, TYPE_INODE, &blk_cnt);
153	fsck_verify(sbi);
154	fsck_free(sbi);
155}
156
157static void do_dump(struct f2fs_sb_info *sbi)
158{
159	struct dump_option *opt = (struct dump_option *)config.private;
160	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
161	u32 flag = le32_to_cpu(ckpt->ckpt_flags);
162
163	fsck_init(sbi);
164
165	if (opt->end_sit == -1)
166		opt->end_sit = SM_I(sbi)->main_segments;
167	if (opt->end_ssa == -1)
168		opt->end_ssa = SM_I(sbi)->main_segments;
169	if (opt->start_sit != -1)
170		sit_dump(sbi, opt->start_sit, opt->end_sit);
171	if (opt->start_ssa != -1)
172		ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
173	if (opt->blk_addr != -1) {
174		dump_info_from_blkaddr(sbi, opt->blk_addr);
175		goto cleanup;
176	}
177
178	MSG(0, "Info: checkpoint state = %x : ", flag);
179	if (flag & CP_FSCK_FLAG)
180		MSG(0, "%s", " fsck");
181	if (flag & CP_ERROR_FLAG)
182		MSG(0, "%s", " error");
183	if (flag & CP_COMPACT_SUM_FLAG)
184		MSG(0, "%s", " compacted_summary");
185	if (flag & CP_ORPHAN_PRESENT_FLAG)
186		MSG(0, "%s", " orphan_inodes");
187	if (flag & CP_FASTBOOT_FLAG)
188		MSG(0, "%s", " fastboot");
189	if (flag & CP_UMOUNT_FLAG)
190		MSG(0, "%s", " unmount");
191	else
192		MSG(0, "%s", " sudden-power-off");
193	MSG(0, "\n");
194
195	dump_node(sbi, opt->nid);
196cleanup:
197	fsck_free(sbi);
198}
199
200int main(int argc, char **argv)
201{
202	struct f2fs_sb_info *sbi;
203	int ret = 0;
204
205	f2fs_init_configuration(&config);
206
207	f2fs_parse_options(argc, argv);
208
209	if (f2fs_dev_is_umounted(&config) < 0)
210		return -1;
211
212	/* Get device */
213	if (f2fs_get_device_info(&config) < 0)
214		return -1;
215fsck_again:
216	memset(&gfsck, 0, sizeof(gfsck));
217	gfsck.sbi.fsck = &gfsck;
218	sbi = &gfsck.sbi;
219
220	ret = f2fs_do_mount(sbi);
221	if (ret == 1) {
222		free(sbi->ckpt);
223		free(sbi->raw_super);
224		goto out;
225	} else if (ret < 0)
226		return -1;
227
228	switch (config.func) {
229	case FSCK:
230		do_fsck(sbi);
231		break;
232	case DUMP:
233		do_dump(sbi);
234		break;
235	}
236
237	f2fs_do_umount(sbi);
238out:
239	if (config.func == FSCK && config.bug_on) {
240		if (config.fix_on == 0 && config.auto_fix == 0) {
241			char ans[255] = {0};
242retry:
243			printf("Do you want to fix this partition? [Y/N] ");
244			ret = scanf("%s", ans);
245			ASSERT(ret >= 0);
246			if (!strcasecmp(ans, "y"))
247				config.fix_on = 1;
248			else if (!strcasecmp(ans, "n"))
249				config.fix_on = 0;
250			else
251				goto retry;
252
253			if (config.fix_on)
254				goto fsck_again;
255		}
256	}
257	f2fs_finalize_device(&config);
258
259	printf("\nDone.\n");
260	return 0;
261}
262