blob: 573b840f266c8fbbf8266a156f4fcb3013e79ea7 [file] [log] [blame]
Theodore Ts'o21c84b71997-04-29 16:15:03 +00001/*
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,
Theodore Ts'oda2e97f1997-06-12 04:28:07 +0000118 "@g %g's @b @B at %b @C.\n",
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000119 PROMPT_RELOCATE, 0 },
120
121 /* Inode bitmap conflicts with some other fs block */
122 { PR_1_IB_CONFLICT,
Theodore Ts'oda2e97f1997-06-12 04:28:07 +0000123 "@g %g's @i @B at %b @C.\n",
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000124 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
Theodore Ts'o521e3681997-04-29 17:48:10 +0000194 /* File sharing blocks with filesystem metadata */
195 { PR_1B_SHARE_METADATA,
196 "\t<filesystem metadata>\n",
197 PROMPT_FIX, PR_MSG_ONLY },
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000198
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}