]>
Commit | Line | Data |
---|---|---|
21c84b71 TT |
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 | ||
521e3685 TT |
194 | /* File sharing blocks with filesystem metadata */ |
195 | { PR_1B_SHARE_METADATA, | |
196 | "\t<filesystem metadata>\n", | |
197 | PROMPT_FIX, PR_MSG_ONLY }, | |
21c84b71 TT |
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 | */ | |
369 | char pr_latch[7]; /* Latch flags register */ | |
370 | char pr_suppress[7]; /* Latch groups which are suppressed */ | |
371 | int latch_question[7] = { | |
372 | PR_1_INODE_BLOCK_LATCH, | |
373 | PR_1_INODE_BBLOCK_LATCH | |
374 | }; | |
375 | ||
376 | static 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 | ||
387 | void reset_problem_latch(int mask) | |
388 | { | |
389 | pr_latch[PR_LATCH(mask)] = 0; | |
390 | pr_suppress[PR_LATCH(mask)] = 0; | |
391 | } | |
392 | ||
393 | void suppress_latch_group(int mask, int value) | |
394 | { | |
395 | pr_suppress[PR_LATCH(mask)] = value; | |
396 | } | |
397 | ||
398 | void 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 | ||
405 | int 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 | } |