problem.c revision 521e36857227b21e7ab47b0a97f788d2af9f9717
1/*
2 * problem.c --- report filesystem problems to the user
3 *
4 * Copyright 1996, 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdlib.h>
13#include <unistd.h>
14#include <string.h>
15#include <ctype.h>
16#include <termios.h>
17
18#include "e2fsck.h"
19
20#include "problem.h"
21
22#define PROMPT_FIX	0
23#define PROMPT_CLEAR	1
24#define PROMPT_RELOCATE	2
25#define PROMPT_ALLOCATE 3
26#define PROMPT_EXPAND	4
27#define PROMPT_CONNECT 	5
28#define PROMPT_CREATE	6
29#define PROMPT_SALVAGE	7
30#define PROMPT_TRUNCATE	8
31#define PROMPT_CLEAR_INODE 9
32
33/*
34 * These are the prompts which are used to ask the user if they want
35 * to fix a problem.
36 */
37static const char *prompt[] = {
38	"Fix",			/* 0 */
39	"Clear",		/* 1 */
40	"Relocate",		/* 2 */
41	"Allocate",		/* 3 */
42	"Expand",		/* 4 */
43	"Connect to /lost+found", /* 5 */
44	"Create",		/* 6 */
45	"Salvage",		/* 7 */
46	"Truncate",		/* 8 */
47	"Clear inode"		/* 9 */
48	};
49
50/*
51 * These messages are printed when we are preen mode and we will be
52 * automatically fixing the problem.
53 */
54static const char *preen_msg[] = {
55	"FIXED",		/* 0 */
56	"CLEARED",		/* 1 */
57	"RELOCATED",		/* 2 */
58	"ALLOCATED",		/* 3 */
59	"EXPANDED",		/* 4 */
60	"RECONNECTED",		/* 5 */
61	"CREATED",		/* 6 */
62	"SALVAGED",		/* 7 */
63	"TRUNCATED",		/* 8 */
64	"INODE CLEARED"		/* 9 */
65};
66
67static struct e2fsck_problem problem_table[] = {
68
69	/* Pre-Pass 1 errors */
70
71	/* Block bitmap not in group */
72	{ PR_0_BB_NOT_GROUP, "@b @B for @g %g is not in @g.  (@b %b)\n",
73	  PROMPT_RELOCATE, 0 },
74
75	/* Inode bitmap not in group */
76	{ PR_0_IB_NOT_GROUP, "@i @B for @g %g is not in @g.  (@b %b)\n",
77	  PROMPT_RELOCATE, 0 },
78
79	/* Inode table not in group */
80	{ PR_0_ITABLE_NOT_GROUP,
81	  "@i table for @g %g is not in @g.  (@b %b)\n"
82	  "WARNING: SEVERE DATA LOSS POSSIBLE.\n",
83	  PROMPT_RELOCATE, 0 },
84
85	/* Pass 1 errors */
86
87	/* Root directory is not an inode */
88	{ PR_1_ROOT_NO_DIR, "@r is not a @d.  ",
89	  PROMPT_CLEAR, 0 },
90
91	/* Root directory has dtime set */
92	{ PR_1_ROOT_DTIME,
93	  "@r has dtime set (probably due to old mke2fs).  ",
94	  PROMPT_FIX, PR_PREEN_OK },
95
96	/* Reserved inode has bad mode */
97	{ PR_1_RESERVED_BAD_MODE,
98	  "Reserved @i %i has bad mode.  ",
99	  PROMPT_CLEAR, PR_PREEN_OK },
100
101	/* Deleted inode has zero dtime */
102	{ PR_1_ZERO_DTIME,
103	  "@D @i %i has zero dtime.  ",
104	  PROMPT_FIX, PR_PREEN_OK },
105
106	/* Inode in use, but dtime set */
107	{ PR_1_SET_DTIME,
108	  "@i %i is in use, but has dtime set.  ",
109	  PROMPT_FIX, PR_PREEN_OK },
110
111	/* Zero-length directory */
112	{ PR_1_ZERO_LENGTH_DIR,
113	  "@i %i is a @z @d.  ",
114	  PROMPT_CLEAR, PR_PREEN_OK },
115
116	/* Block bitmap conflicts with some other fs block */
117	{ PR_1_BB_CONFLICT,
118	  "@g %N's @b @B at %b @C.\n",
119	  PROMPT_RELOCATE, 0 },
120
121	/* Inode bitmap conflicts with some other fs block */
122	{ PR_1_IB_CONFLICT,
123	  "@g %N's @i @B at %b @C.\n",
124	  PROMPT_RELOCATE, 0 },
125
126	/* Inode table conflicts with some other fs block */
127	{ PR_1_ITABLE_CONFLICT,
128	  "@g %g's @i table at %b @C.\n",
129	  PROMPT_RELOCATE, 0 },
130
131	/* Block bitmap is on a bad block */
132	{ PR_1_BB_BAD_BLOCK,
133	  "@g %g's @b @B (%b) is bad.  ",
134	  PROMPT_RELOCATE, 0 },
135
136	/* Inode bitmap is on a bad block */
137	{ PR_1_IB_BAD_BLOCK,
138	  "@g %g's @i @B (%b) is bad.  ",
139	  PROMPT_RELOCATE, 0 },
140
141	/* Inode has incorrect i_size */
142	{ PR_1_BAD_I_SIZE,
143	  "@i %i, i_size is %Is, @s %N.  ",
144		  PROMPT_FIX, PR_PREEN_OK },
145
146	/* Inode has incorrect i_blocks */
147	{ PR_1_BAD_I_BLOCKS,
148	  "@i %i, i_blocks is %Ib, @s %N.  ",
149		  PROMPT_FIX, PR_PREEN_OK },
150
151	/* Illegal block number in inode */
152	{ PR_1_ILLEGAL_BLOCK_NUM,
153	  "Illegal @b #%B (%b) in @i %i.  ",
154	  PROMPT_CLEAR, PR_LATCH_BLOCK },
155
156	/* Block number overlaps fs metadata */
157	{ PR_1_BLOCK_OVERLAPS_METADATA,
158	  "@b #%B (%b) overlaps filesystem metadata in @i %i.  ",
159	  PROMPT_CLEAR, PR_LATCH_BLOCK },
160
161	/* Inode has illegal blocks (latch question) */
162	{ PR_1_INODE_BLOCK_LATCH,
163	  "@i %i has illegal @b(s).  ",
164	  PROMPT_CLEAR, 0 },
165
166	/* Too many bad blocks in inode */
167	{ PR_1_TOO_MANY_BAD_BLOCKS,
168	  "Too many illegal @bs in @i %i.\n",
169	  PROMPT_CLEAR_INODE, PR_NO_OK },
170
171	/* Illegal block number in bad block inode */
172	{ PR_1_BB_ILLEGAL_BLOCK_NUM,
173	  "Illegal @b #%B (%b) in bad @b @i.  ",
174	  PROMPT_CLEAR, PR_LATCH_BBLOCK },
175
176	/* Bad block inode has illegal blocks (latch question) */
177	{ PR_1_INODE_BBLOCK_LATCH,
178	  "Bad @b @i has illegal @b(s).  ",
179	  PROMPT_CLEAR, 0 },
180
181	/* Pass 1b errors */
182
183	/* File has duplicate blocks */
184	{ PR_1B_DUP_FILE,
185	  "File %Q (@i #%i, mod time %IM) \n"
186	  "  has %B duplicate @b(s), shared with %N file(s):\n",
187	  PROMPT_FIX, PR_MSG_ONLY },
188
189	/* List of files sharing duplicate blocks */
190	{ PR_1B_DUP_FILE_LIST,
191	  "\t%Q (@i #%i, mod time %IM)\n",
192	  PROMPT_FIX, PR_MSG_ONLY },
193
194	/* File sharing blocks with filesystem metadata  */
195	{ PR_1B_SHARE_METADATA,
196	  "\t<filesystem metadata>\n",
197	  PROMPT_FIX, PR_MSG_ONLY },
198
199	/* Pass 2 errors */
200
201	/* Bad inode number for '.' */
202	{ PR_2_BAD_INODE_DOT,
203	  "Bad @i number for '.' in @d @i %i.\n",
204	  PROMPT_FIX, 0 },
205
206	/* Directory entry has bad inode number */
207	{ PR_2_BAD_INO,
208	  "@E has bad @i #: %Di.\n",
209	  PROMPT_CLEAR, 0 },
210
211	/* Directory entry has deleted or unused inode */
212	{ PR_2_UNUSED_INODE,
213	  "@E has @D/unused @i %Di.  ",
214	  PROMPT_CLEAR, PR_PREEN_OK },
215
216	/* Directry entry is link to '.' */
217	{ PR_2_LINK_DOT,
218	  "@E @L to '.'  ",
219	  PROMPT_CLEAR, 0 },
220
221	/* Directory entry points to inode now located in a bad block */
222	{ PR_2_BB_INODE,
223	  "@E points to @i (%Di) located in a bad @b.\n",
224	  PROMPT_CLEAR, 0 },
225
226	/* Directory entry contains a link to a directory */
227	{ PR_2_LINK_DIR,
228	  "@E @L to @d %P (%Di).\n",
229	  PROMPT_CLEAR, 0 },
230
231	/* Directory entry contains a link to the root directry */
232	{ PR_2_LINK_ROOT,
233	  "@E @L to the @r.\n",
234	  PROMPT_CLEAR, 0 },
235
236	/* Directory entry has illegal characters in its name */
237	{ PR_2_BAD_NAME,
238	  "@E has illegal characters in its name.\n",
239	  PROMPT_FIX, 0 },
240
241	/* Missing '.' in directory inode */
242	{ PR_2_MISSING_DOT,
243	  "Missing '.' in @d @i %i.\n",
244	  PROMPT_FIX, 0 },
245
246	/* Missing '..' in directory inode */
247	{ PR_2_MISSING_DOT_DOT,
248	  "Missing '..' in @d @i %i.\n",
249	  PROMPT_FIX, 0 },
250
251	/* First entry in directory inode doesn't contain '.' */
252	{ PR_2_1ST_NOT_DOT,
253	  "First @e '%Dn' (inode=%Di) in @d @i %i (%p) @s '.'\n",
254	  PROMPT_FIX, 0 },
255
256	/* Second entry in directory inode doesn't contain '..' */
257	{ PR_2_2ND_NOT_DOT_DOT,
258	  "Second @e '%Dn' (inode=%Di) in @d @i %i @s '..'\n",
259	  PROMPT_FIX, 0 },
260
261	/* i_faddr should be zero */
262	{ PR_2_FADDR_ZERO,
263	  "i_faddr @F %IF, @s zero.\n",
264	  PROMPT_CLEAR, 0 },
265
266  	/* i_file_acl should be zero */
267	{ PR_2_FILE_ACL_ZERO,
268	  "i_file_acl @F %If, @s zero.\n",
269	  PROMPT_CLEAR, 0 },
270
271  	/* i_dir_acl should be zero */
272	{ PR_2_DIR_ACL_ZERO,
273	  "i_dir_acl @F %Id, @s zero.\n",
274	  PROMPT_CLEAR, 0 },
275
276  	/* i_frag should be zero */
277	{ PR_2_FRAG_ZERO,
278	  "i_frag @F %N, @s zero.\n",
279	  PROMPT_CLEAR, 0 },
280
281  	/* i_fsize should be zero */
282	{ PR_2_FSIZE_ZERO,
283	  "i_fsize @F %N, @s zero.\n",
284	  PROMPT_CLEAR, 0 },
285
286	/* inode has bad mode */
287	{ PR_2_BAD_MODE,
288	  "@i %i (%Q) has a bad mode (%Im).\n",
289	  PROMPT_CLEAR, 0 },
290
291	/* directory corrupted */
292	{ PR_2_DIR_CORRUPTED,
293	  "@d @i %i, @b %B, offset %N: @d corrupted\n",
294	  PROMPT_SALVAGE, 0 },
295
296	/* filename too long */
297	{ PR_2_FILENAME_LONG,
298	  "@d @i %i, @b %B, offset %N: filename too long\n",
299	  PROMPT_TRUNCATE, 0 },
300
301	/* Directory inode has a missing block (hole) */
302	{ PR_2_DIRECTORY_HOLE,
303	  "@d @i %i has an unallocated @b #%B.  ",
304	  PROMPT_ALLOCATE, 0 },
305
306	/* '.' is not NULL terminated */
307	{ PR_2_DOT_NULL_TERM,
308	  "'.' directory entry in @d @i %i is not NULL terminated\n",
309	  PROMPT_FIX, 0 },
310
311	/* '..' is not NULL terminated */
312	{ PR_2_DOT_DOT_NULL_TERM,
313	  "'..' directory entry in @d @i %i is not NULL terminated\n",
314	  PROMPT_FIX, 0 },
315
316	  /* Pass 3 errors */
317
318	/* Root inode not allocated */
319	{ PR_3_NO_ROOT_INODE,
320	  "@r not allocated.  ",
321	  PROMPT_ALLOCATE, 0 },
322
323	/* No room in lost+found */
324	{ PR_3_EXPAND_LF_DIR,
325	  "No room in @l @d.  ",
326	  PROMPT_EXPAND, 0 },
327
328	/* Unconnected directory inode */
329	{ PR_3_UNCONNECTED_DIR,
330	  "Unconnected @d @i %i (%p)\n",
331	  PROMPT_CONNECT, 0 },
332
333	/* /lost+found not found */
334	{ PR_3_NO_LF_DIR,
335	  "/@l not found.  ",
336	  PROMPT_CREATE, 0 },
337
338	/* .. entry is incorrect */
339	{ PR_3_BAD_DOT_DOT,
340	  "'..' in %Q (%i) is %P (%j), @s %q (%d).\n",
341	  PROMPT_FIX, 0 },
342
343	/* Pass 4 errors */
344
345	/* Unattached zero-length inode */
346	{ PR_4_ZERO_LEN_INODE,
347	  "@u @z @i %i.  ",
348	  PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
349
350	/* Unattached inode */
351	{ PR_4_UNATTACHED_INODE,
352	  "@u @i %i\n",
353	  PROMPT_CONNECT, 0 },
354
355	/* Inode ref count wrong */
356	{ PR_4_BAD_REF_COUNT,
357	  "@i %i ref count is %Il, @s %N.  ",
358	  PROMPT_FIX, PR_PREEN_OK },
359
360	{ 0 }
361};
362
363/*
364 * This is the latch flags register.  It allows several problems to be
365 * "latched" together.  This means that the user has to answer but one
366 * question for the set of problems, and all of the associated
367 * problems will be either fixed or not fixed.
368 */
369char pr_latch[7];		/* Latch flags register */
370char pr_suppress[7];		/* Latch groups which are suppressed */
371int latch_question[7] = {
372	PR_1_INODE_BLOCK_LATCH,
373	PR_1_INODE_BBLOCK_LATCH
374};
375
376static struct e2fsck_problem *find_problem(int code)
377{
378	int 	i;
379
380	for (i=0; problem_table[i].e2p_code; i++) {
381		if (problem_table[i].e2p_code == code)
382			return &problem_table[i];
383	}
384	return 0;
385}
386
387void reset_problem_latch(int mask)
388{
389	pr_latch[PR_LATCH(mask)] = 0;
390	pr_suppress[PR_LATCH(mask)] = 0;
391}
392
393void suppress_latch_group(int mask, int value)
394{
395	pr_suppress[PR_LATCH(mask)] = value;
396}
397
398void clear_problem_context(struct problem_context *ctx)
399{
400	memset(ctx, 0, sizeof(struct problem_context));
401	ctx->blkcount = -1;
402	ctx->group = -1;
403}
404
405int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx)
406{
407	struct e2fsck_problem *ptr;
408	int 		def_yn, answer;
409	int		latch;
410	int		print_answer = 0;
411	int		suppress = 0;
412
413	ptr = find_problem(code);
414	if (!ptr) {
415		printf("Unhandled error code (%d)!\n", code);
416		return 0;
417	}
418	def_yn = (ptr->flags & PR_NO_DEFAULT) ? 0 : 1;
419
420	/*
421	 * Do special latch processing.  This is where we ask the
422	 * latch question, if it exists
423	 */
424	if (ptr->flags & PR_LATCH_MASK) {
425		latch = PR_LATCH(ptr->flags);
426		if (latch_question[latch] && !pr_latch[latch])
427			pr_latch[latch] = fix_problem(fs,
428						      latch_question[latch],
429						      ctx) + 1;
430		if (pr_suppress[latch])
431			suppress++;
432	}
433
434	if (!suppress) {
435		if (preen)
436			printf("%s: ", device_name);
437		print_e2fsck_message(fs, ptr->e2p_description, ctx, 1);
438	}
439	if (!(ptr->flags & PR_PREEN_OK))
440		preenhalt(fs);
441
442	if (ptr->flags & PR_MSG_ONLY)
443		return 1;
444
445	if (preen) {
446		answer = def_yn;
447		print_answer = 1;
448	} else if (ptr->flags & PR_LATCH_MASK) {
449		latch = PR_LATCH(ptr->flags);
450		if (!pr_latch[latch])
451			pr_latch[latch] =
452				ask(prompt[(int) ptr->prompt], def_yn) + 1;
453		else
454			print_answer = 1;
455		answer = pr_latch[latch] - 1;
456	} else
457		answer = ask(prompt[(int) ptr->prompt], def_yn);
458	if (!answer && !(ptr->flags & PR_NO_OK))
459		ext2fs_unmark_valid(fs);
460
461	if (print_answer)
462		printf("%s.\n",
463		       answer ? preen_msg[(int) ptr->prompt] : "IGNORED");
464
465	return answer;
466}
467