Whamcloud - gitweb
Many files:
[tools/e2fsprogs.git] / e2fsck / problem.c
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  */
37 static 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  */
54 static 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
67 static 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
195         /* Pass 2 errors */
196
197         /* Bad inode number for '.' */
198         { PR_2_BAD_INODE_DOT,
199           "Bad @i number for '.' in @d @i %i.\n",
200           PROMPT_FIX, 0 },
201
202         /* Directory entry has bad inode number */
203         { PR_2_BAD_INO, 
204           "@E has bad @i #: %Di.\n",
205           PROMPT_CLEAR, 0 },
206
207         /* Directory entry has deleted or unused inode */
208         { PR_2_UNUSED_INODE, 
209           "@E has @D/unused @i %Di.  ",
210           PROMPT_CLEAR, PR_PREEN_OK },
211
212         /* Directry entry is link to '.' */
213         { PR_2_LINK_DOT, 
214           "@E @L to '.'  ",
215           PROMPT_CLEAR, 0 },
216
217         /* Directory entry points to inode now located in a bad block */
218         { PR_2_BB_INODE,
219           "@E points to @i (%Di) located in a bad @b.\n",
220           PROMPT_CLEAR, 0 },
221
222         /* Directory entry contains a link to a directory */
223         { PR_2_LINK_DIR, 
224           "@E @L to @d %P (%Di).\n",
225           PROMPT_CLEAR, 0 },
226
227         /* Directory entry contains a link to the root directry */
228         { PR_2_LINK_ROOT, 
229           "@E @L to the @r.\n",
230           PROMPT_CLEAR, 0 },
231
232         /* Directory entry has illegal characters in its name */
233         { PR_2_BAD_NAME, 
234           "@E has illegal characters in its name.\n",
235           PROMPT_FIX, 0 },
236
237         /* Missing '.' in directory inode */      
238         { PR_2_MISSING_DOT,
239           "Missing '.' in @d @i %i.\n",
240           PROMPT_FIX, 0 },
241
242         /* Missing '..' in directory inode */     
243         { PR_2_MISSING_DOT_DOT,
244           "Missing '..' in @d @i %i.\n",
245           PROMPT_FIX, 0 },
246
247         /* First entry in directory inode doesn't contain '.' */
248         { PR_2_1ST_NOT_DOT,
249           "First @e '%Dn' (inode=%Di) in @d @i %i (%p) @s '.'\n",
250           PROMPT_FIX, 0 },
251
252         /* Second entry in directory inode doesn't contain '..' */
253         { PR_2_2ND_NOT_DOT_DOT,
254           "Second @e '%Dn' (inode=%Di) in @d @i %i @s '..'\n",
255           PROMPT_FIX, 0 },
256                   
257         /* i_faddr should be zero */
258         { PR_2_FADDR_ZERO,
259           "i_faddr @F %IF, @s zero.\n",
260           PROMPT_CLEAR, 0 },
261
262         /* i_file_acl should be zero */
263         { PR_2_FILE_ACL_ZERO,
264           "i_file_acl @F %If, @s zero.\n",
265           PROMPT_CLEAR, 0 },
266
267         /* i_dir_acl should be zero */
268         { PR_2_DIR_ACL_ZERO,
269           "i_dir_acl @F %Id, @s zero.\n",
270           PROMPT_CLEAR, 0 },
271
272         /* i_frag should be zero */
273         { PR_2_FRAG_ZERO,
274           "i_frag @F %N, @s zero.\n",
275           PROMPT_CLEAR, 0 },
276
277         /* i_fsize should be zero */
278         { PR_2_FSIZE_ZERO,
279           "i_fsize @F %N, @s zero.\n",
280           PROMPT_CLEAR, 0 },
281
282         /* inode has bad mode */
283         { PR_2_BAD_MODE,
284           "@i %i (%Q) has a bad mode (%Im).\n",
285           PROMPT_CLEAR, 0 },
286
287         /* directory corrupted */
288         { PR_2_DIR_CORRUPTED,     
289           "@d @i %i, @b %B, offset %N: @d corrupted\n",
290           PROMPT_SALVAGE, 0 },
291                   
292         /* filename too long */
293         { PR_2_FILENAME_LONG,     
294           "@d @i %i, @b %B, offset %N: filename too long\n",
295           PROMPT_TRUNCATE, 0 },
296
297         /* Directory inode has a missing block (hole) */
298         { PR_2_DIRECTORY_HOLE,    
299           "@d @i %i has an unallocated @b #%B.  ",
300           PROMPT_ALLOCATE, 0 },
301
302         /* '.' is not NULL terminated */
303         { PR_2_DOT_NULL_TERM,
304           "'.' directory entry in @d @i %i is not NULL terminated\n",
305           PROMPT_FIX, 0 },
306
307         /* '..' is not NULL terminated */
308         { PR_2_DOT_DOT_NULL_TERM,
309           "'..' directory entry in @d @i %i is not NULL terminated\n",
310           PROMPT_FIX, 0 },
311
312           /* Pass 3 errors */
313
314         /* Root inode not allocated */
315         { PR_3_NO_ROOT_INODE,
316           "@r not allocated.  ",
317           PROMPT_ALLOCATE, 0 }, 
318                   
319         /* No room in lost+found */
320         { PR_3_EXPAND_LF_DIR,
321           "No room in @l @d.  ",
322           PROMPT_EXPAND, 0 },
323
324         /* Unconnected directory inode */
325         { PR_3_UNCONNECTED_DIR,
326           "Unconnected @d @i %i (%p)\n",
327           PROMPT_CONNECT, 0 },
328
329         /* /lost+found not found */
330         { PR_3_NO_LF_DIR,
331           "/@l not found.  ",
332           PROMPT_CREATE, 0 },
333
334         /* .. entry is incorrect */
335         { PR_3_BAD_DOT_DOT,
336           "'..' in %Q (%i) is %P (%j), @s %q (%d).\n",
337           PROMPT_FIX, 0 },
338
339         /* Pass 4 errors */
340         
341         /* Unattached zero-length inode */
342         { PR_4_ZERO_LEN_INODE,
343           "@u @z @i %i.  ",
344           PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
345
346         /* Unattached inode */
347         { PR_4_UNATTACHED_INODE,
348           "@u @i %i\n",
349           PROMPT_CONNECT, 0 },
350
351         /* Inode ref count wrong */
352         { PR_4_BAD_REF_COUNT,
353           "@i %i ref count is %Il, @s %N.  ",
354           PROMPT_FIX, PR_PREEN_OK },
355         
356         { 0 }
357 };
358
359 /*
360  * This is the latch flags register.  It allows several problems to be
361  * "latched" together.  This means that the user has to answer but one
362  * question for the set of problems, and all of the associated
363  * problems will be either fixed or not fixed.
364  */
365 char pr_latch[7];               /* Latch flags register */
366 char pr_suppress[7];            /* Latch groups which are suppressed */
367 int latch_question[7] = {
368         PR_1_INODE_BLOCK_LATCH,
369         PR_1_INODE_BBLOCK_LATCH 
370 };
371
372 static struct e2fsck_problem *find_problem(int code)
373 {
374         int     i;
375
376         for (i=0; problem_table[i].e2p_code; i++) {
377                 if (problem_table[i].e2p_code == code)
378                         return &problem_table[i];
379         }
380         return 0;
381 }
382
383 void reset_problem_latch(int mask)
384 {
385         pr_latch[PR_LATCH(mask)] = 0;
386         pr_suppress[PR_LATCH(mask)] = 0;
387 }
388
389 void suppress_latch_group(int mask, int value)
390 {
391         pr_suppress[PR_LATCH(mask)] = value;
392 }
393
394 void clear_problem_context(struct problem_context *ctx)
395 {
396         memset(ctx, 0, sizeof(struct problem_context));
397         ctx->blkcount = -1;
398         ctx->group = -1;
399 }
400
401 int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx)
402 {
403         struct e2fsck_problem *ptr;
404         int             def_yn, answer;
405         int             latch;
406         int             print_answer = 0;
407         int             suppress = 0;
408
409         ptr = find_problem(code);
410         if (!ptr) {
411                 printf("Unhandled error code (%d)!\n", code);
412                 return 0;
413         }
414         def_yn = (ptr->flags & PR_NO_DEFAULT) ? 0 : 1;
415
416         /*
417          * Do special latch processing.  This is where we ask the
418          * latch question, if it exists
419          */
420         if (ptr->flags & PR_LATCH_MASK) {
421                 latch = PR_LATCH(ptr->flags);
422                 if (latch_question[latch] && !pr_latch[latch])
423                         pr_latch[latch] = fix_problem(fs,
424                                                       latch_question[latch],
425                                                       ctx) + 1;
426                 if (pr_suppress[latch])
427                         suppress++;
428         }
429
430         if (!suppress) {
431                 if (preen)
432                         printf("%s: ", device_name);
433                 print_e2fsck_message(fs, ptr->e2p_description, ctx, 1);
434         }
435         if (!(ptr->flags & PR_PREEN_OK))
436                 preenhalt(fs);
437
438         if (ptr->flags & PR_MSG_ONLY)
439                 return 1;
440         
441         if (preen) {
442                 answer = def_yn;
443                 print_answer = 1;
444         } else if (ptr->flags & PR_LATCH_MASK) {
445                 latch = PR_LATCH(ptr->flags);
446                 if (!pr_latch[latch])
447                         pr_latch[latch] =
448                                 ask(prompt[(int) ptr->prompt], def_yn) + 1;
449                 else
450                         print_answer = 1;
451                 answer = pr_latch[latch] - 1;
452         } else
453                 answer = ask(prompt[(int) ptr->prompt], def_yn);
454         if (!answer && !(ptr->flags & PR_NO_OK))
455                 ext2fs_unmark_valid(fs);
456         
457         if (print_answer)
458                 printf("%s.\n",
459                        answer ? preen_msg[(int) ptr->prompt] : "IGNORED");
460         
461         return answer;
462 }