mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2025-01-10 10:33:32 +08:00
da2e97f7cf
pass1.c (mark_table_blocks): Mark the superblock and group descriptor blocks first, so that any conflicts between these and the bitmap or inode table blocks is noticed. problem.c: Fix message printed out when a block or inode bitmap conflicts with other fs data, has the correct group number in it.
467 lines
11 KiB
C
467 lines
11 KiB
C
/*
|
|
* problem.c --- report filesystem problems to the user
|
|
*
|
|
* Copyright 1996, 1997 by Theodore Ts'o
|
|
*
|
|
* %Begin-Header%
|
|
* This file may be redistributed under the terms of the GNU Public
|
|
* License.
|
|
* %End-Header%
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <termios.h>
|
|
|
|
#include "e2fsck.h"
|
|
|
|
#include "problem.h"
|
|
|
|
#define PROMPT_FIX 0
|
|
#define PROMPT_CLEAR 1
|
|
#define PROMPT_RELOCATE 2
|
|
#define PROMPT_ALLOCATE 3
|
|
#define PROMPT_EXPAND 4
|
|
#define PROMPT_CONNECT 5
|
|
#define PROMPT_CREATE 6
|
|
#define PROMPT_SALVAGE 7
|
|
#define PROMPT_TRUNCATE 8
|
|
#define PROMPT_CLEAR_INODE 9
|
|
|
|
/*
|
|
* These are the prompts which are used to ask the user if they want
|
|
* to fix a problem.
|
|
*/
|
|
static const char *prompt[] = {
|
|
"Fix", /* 0 */
|
|
"Clear", /* 1 */
|
|
"Relocate", /* 2 */
|
|
"Allocate", /* 3 */
|
|
"Expand", /* 4 */
|
|
"Connect to /lost+found", /* 5 */
|
|
"Create", /* 6 */
|
|
"Salvage", /* 7 */
|
|
"Truncate", /* 8 */
|
|
"Clear inode" /* 9 */
|
|
};
|
|
|
|
/*
|
|
* These messages are printed when we are preen mode and we will be
|
|
* automatically fixing the problem.
|
|
*/
|
|
static const char *preen_msg[] = {
|
|
"FIXED", /* 0 */
|
|
"CLEARED", /* 1 */
|
|
"RELOCATED", /* 2 */
|
|
"ALLOCATED", /* 3 */
|
|
"EXPANDED", /* 4 */
|
|
"RECONNECTED", /* 5 */
|
|
"CREATED", /* 6 */
|
|
"SALVAGED", /* 7 */
|
|
"TRUNCATED", /* 8 */
|
|
"INODE CLEARED" /* 9 */
|
|
};
|
|
|
|
static struct e2fsck_problem problem_table[] = {
|
|
|
|
/* Pre-Pass 1 errors */
|
|
|
|
/* Block bitmap not in group */
|
|
{ PR_0_BB_NOT_GROUP, "@b @B for @g %g is not in @g. (@b %b)\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode bitmap not in group */
|
|
{ PR_0_IB_NOT_GROUP, "@i @B for @g %g is not in @g. (@b %b)\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode table not in group */
|
|
{ PR_0_ITABLE_NOT_GROUP,
|
|
"@i table for @g %g is not in @g. (@b %b)\n"
|
|
"WARNING: SEVERE DATA LOSS POSSIBLE.\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Pass 1 errors */
|
|
|
|
/* Root directory is not an inode */
|
|
{ PR_1_ROOT_NO_DIR, "@r is not a @d. ",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Root directory has dtime set */
|
|
{ PR_1_ROOT_DTIME,
|
|
"@r has dtime set (probably due to old mke2fs). ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
/* Reserved inode has bad mode */
|
|
{ PR_1_RESERVED_BAD_MODE,
|
|
"Reserved @i %i has bad mode. ",
|
|
PROMPT_CLEAR, PR_PREEN_OK },
|
|
|
|
/* Deleted inode has zero dtime */
|
|
{ PR_1_ZERO_DTIME,
|
|
"@D @i %i has zero dtime. ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
/* Inode in use, but dtime set */
|
|
{ PR_1_SET_DTIME,
|
|
"@i %i is in use, but has dtime set. ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
/* Zero-length directory */
|
|
{ PR_1_ZERO_LENGTH_DIR,
|
|
"@i %i is a @z @d. ",
|
|
PROMPT_CLEAR, PR_PREEN_OK },
|
|
|
|
/* Block bitmap conflicts with some other fs block */
|
|
{ PR_1_BB_CONFLICT,
|
|
"@g %g's @b @B at %b @C.\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode bitmap conflicts with some other fs block */
|
|
{ PR_1_IB_CONFLICT,
|
|
"@g %g's @i @B at %b @C.\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode table conflicts with some other fs block */
|
|
{ PR_1_ITABLE_CONFLICT,
|
|
"@g %g's @i table at %b @C.\n",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Block bitmap is on a bad block */
|
|
{ PR_1_BB_BAD_BLOCK,
|
|
"@g %g's @b @B (%b) is bad. ",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode bitmap is on a bad block */
|
|
{ PR_1_IB_BAD_BLOCK,
|
|
"@g %g's @i @B (%b) is bad. ",
|
|
PROMPT_RELOCATE, 0 },
|
|
|
|
/* Inode has incorrect i_size */
|
|
{ PR_1_BAD_I_SIZE,
|
|
"@i %i, i_size is %Is, @s %N. ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
/* Inode has incorrect i_blocks */
|
|
{ PR_1_BAD_I_BLOCKS,
|
|
"@i %i, i_blocks is %Ib, @s %N. ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
/* Illegal block number in inode */
|
|
{ PR_1_ILLEGAL_BLOCK_NUM,
|
|
"Illegal @b #%B (%b) in @i %i. ",
|
|
PROMPT_CLEAR, PR_LATCH_BLOCK },
|
|
|
|
/* Block number overlaps fs metadata */
|
|
{ PR_1_BLOCK_OVERLAPS_METADATA,
|
|
"@b #%B (%b) overlaps filesystem metadata in @i %i. ",
|
|
PROMPT_CLEAR, PR_LATCH_BLOCK },
|
|
|
|
/* Inode has illegal blocks (latch question) */
|
|
{ PR_1_INODE_BLOCK_LATCH,
|
|
"@i %i has illegal @b(s). ",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Too many bad blocks in inode */
|
|
{ PR_1_TOO_MANY_BAD_BLOCKS,
|
|
"Too many illegal @bs in @i %i.\n",
|
|
PROMPT_CLEAR_INODE, PR_NO_OK },
|
|
|
|
/* Illegal block number in bad block inode */
|
|
{ PR_1_BB_ILLEGAL_BLOCK_NUM,
|
|
"Illegal @b #%B (%b) in bad @b @i. ",
|
|
PROMPT_CLEAR, PR_LATCH_BBLOCK },
|
|
|
|
/* Bad block inode has illegal blocks (latch question) */
|
|
{ PR_1_INODE_BBLOCK_LATCH,
|
|
"Bad @b @i has illegal @b(s). ",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Pass 1b errors */
|
|
|
|
/* File has duplicate blocks */
|
|
{ PR_1B_DUP_FILE,
|
|
"File %Q (@i #%i, mod time %IM) \n"
|
|
" has %B duplicate @b(s), shared with %N file(s):\n",
|
|
PROMPT_FIX, PR_MSG_ONLY },
|
|
|
|
/* List of files sharing duplicate blocks */
|
|
{ PR_1B_DUP_FILE_LIST,
|
|
"\t%Q (@i #%i, mod time %IM)\n",
|
|
PROMPT_FIX, PR_MSG_ONLY },
|
|
|
|
/* File sharing blocks with filesystem metadata */
|
|
{ PR_1B_SHARE_METADATA,
|
|
"\t<filesystem metadata>\n",
|
|
PROMPT_FIX, PR_MSG_ONLY },
|
|
|
|
/* Pass 2 errors */
|
|
|
|
/* Bad inode number for '.' */
|
|
{ PR_2_BAD_INODE_DOT,
|
|
"Bad @i number for '.' in @d @i %i.\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Directory entry has bad inode number */
|
|
{ PR_2_BAD_INO,
|
|
"@E has bad @i #: %Di.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Directory entry has deleted or unused inode */
|
|
{ PR_2_UNUSED_INODE,
|
|
"@E has @D/unused @i %Di. ",
|
|
PROMPT_CLEAR, PR_PREEN_OK },
|
|
|
|
/* Directry entry is link to '.' */
|
|
{ PR_2_LINK_DOT,
|
|
"@E @L to '.' ",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Directory entry points to inode now located in a bad block */
|
|
{ PR_2_BB_INODE,
|
|
"@E points to @i (%Di) located in a bad @b.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Directory entry contains a link to a directory */
|
|
{ PR_2_LINK_DIR,
|
|
"@E @L to @d %P (%Di).\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Directory entry contains a link to the root directry */
|
|
{ PR_2_LINK_ROOT,
|
|
"@E @L to the @r.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* Directory entry has illegal characters in its name */
|
|
{ PR_2_BAD_NAME,
|
|
"@E has illegal characters in its name.\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Missing '.' in directory inode */
|
|
{ PR_2_MISSING_DOT,
|
|
"Missing '.' in @d @i %i.\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Missing '..' in directory inode */
|
|
{ PR_2_MISSING_DOT_DOT,
|
|
"Missing '..' in @d @i %i.\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* First entry in directory inode doesn't contain '.' */
|
|
{ PR_2_1ST_NOT_DOT,
|
|
"First @e '%Dn' (inode=%Di) in @d @i %i (%p) @s '.'\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Second entry in directory inode doesn't contain '..' */
|
|
{ PR_2_2ND_NOT_DOT_DOT,
|
|
"Second @e '%Dn' (inode=%Di) in @d @i %i @s '..'\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* i_faddr should be zero */
|
|
{ PR_2_FADDR_ZERO,
|
|
"i_faddr @F %IF, @s zero.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* i_file_acl should be zero */
|
|
{ PR_2_FILE_ACL_ZERO,
|
|
"i_file_acl @F %If, @s zero.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* i_dir_acl should be zero */
|
|
{ PR_2_DIR_ACL_ZERO,
|
|
"i_dir_acl @F %Id, @s zero.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* i_frag should be zero */
|
|
{ PR_2_FRAG_ZERO,
|
|
"i_frag @F %N, @s zero.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* i_fsize should be zero */
|
|
{ PR_2_FSIZE_ZERO,
|
|
"i_fsize @F %N, @s zero.\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* inode has bad mode */
|
|
{ PR_2_BAD_MODE,
|
|
"@i %i (%Q) has a bad mode (%Im).\n",
|
|
PROMPT_CLEAR, 0 },
|
|
|
|
/* directory corrupted */
|
|
{ PR_2_DIR_CORRUPTED,
|
|
"@d @i %i, @b %B, offset %N: @d corrupted\n",
|
|
PROMPT_SALVAGE, 0 },
|
|
|
|
/* filename too long */
|
|
{ PR_2_FILENAME_LONG,
|
|
"@d @i %i, @b %B, offset %N: filename too long\n",
|
|
PROMPT_TRUNCATE, 0 },
|
|
|
|
/* Directory inode has a missing block (hole) */
|
|
{ PR_2_DIRECTORY_HOLE,
|
|
"@d @i %i has an unallocated @b #%B. ",
|
|
PROMPT_ALLOCATE, 0 },
|
|
|
|
/* '.' is not NULL terminated */
|
|
{ PR_2_DOT_NULL_TERM,
|
|
"'.' directory entry in @d @i %i is not NULL terminated\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* '..' is not NULL terminated */
|
|
{ PR_2_DOT_DOT_NULL_TERM,
|
|
"'..' directory entry in @d @i %i is not NULL terminated\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Pass 3 errors */
|
|
|
|
/* Root inode not allocated */
|
|
{ PR_3_NO_ROOT_INODE,
|
|
"@r not allocated. ",
|
|
PROMPT_ALLOCATE, 0 },
|
|
|
|
/* No room in lost+found */
|
|
{ PR_3_EXPAND_LF_DIR,
|
|
"No room in @l @d. ",
|
|
PROMPT_EXPAND, 0 },
|
|
|
|
/* Unconnected directory inode */
|
|
{ PR_3_UNCONNECTED_DIR,
|
|
"Unconnected @d @i %i (%p)\n",
|
|
PROMPT_CONNECT, 0 },
|
|
|
|
/* /lost+found not found */
|
|
{ PR_3_NO_LF_DIR,
|
|
"/@l not found. ",
|
|
PROMPT_CREATE, 0 },
|
|
|
|
/* .. entry is incorrect */
|
|
{ PR_3_BAD_DOT_DOT,
|
|
"'..' in %Q (%i) is %P (%j), @s %q (%d).\n",
|
|
PROMPT_FIX, 0 },
|
|
|
|
/* Pass 4 errors */
|
|
|
|
/* Unattached zero-length inode */
|
|
{ PR_4_ZERO_LEN_INODE,
|
|
"@u @z @i %i. ",
|
|
PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
|
|
|
|
/* Unattached inode */
|
|
{ PR_4_UNATTACHED_INODE,
|
|
"@u @i %i\n",
|
|
PROMPT_CONNECT, 0 },
|
|
|
|
/* Inode ref count wrong */
|
|
{ PR_4_BAD_REF_COUNT,
|
|
"@i %i ref count is %Il, @s %N. ",
|
|
PROMPT_FIX, PR_PREEN_OK },
|
|
|
|
{ 0 }
|
|
};
|
|
|
|
/*
|
|
* This is the latch flags register. It allows several problems to be
|
|
* "latched" together. This means that the user has to answer but one
|
|
* question for the set of problems, and all of the associated
|
|
* problems will be either fixed or not fixed.
|
|
*/
|
|
char pr_latch[7]; /* Latch flags register */
|
|
char pr_suppress[7]; /* Latch groups which are suppressed */
|
|
int latch_question[7] = {
|
|
PR_1_INODE_BLOCK_LATCH,
|
|
PR_1_INODE_BBLOCK_LATCH
|
|
};
|
|
|
|
static struct e2fsck_problem *find_problem(int code)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; problem_table[i].e2p_code; i++) {
|
|
if (problem_table[i].e2p_code == code)
|
|
return &problem_table[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void reset_problem_latch(int mask)
|
|
{
|
|
pr_latch[PR_LATCH(mask)] = 0;
|
|
pr_suppress[PR_LATCH(mask)] = 0;
|
|
}
|
|
|
|
void suppress_latch_group(int mask, int value)
|
|
{
|
|
pr_suppress[PR_LATCH(mask)] = value;
|
|
}
|
|
|
|
void clear_problem_context(struct problem_context *ctx)
|
|
{
|
|
memset(ctx, 0, sizeof(struct problem_context));
|
|
ctx->blkcount = -1;
|
|
ctx->group = -1;
|
|
}
|
|
|
|
int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx)
|
|
{
|
|
struct e2fsck_problem *ptr;
|
|
int def_yn, answer;
|
|
int latch;
|
|
int print_answer = 0;
|
|
int suppress = 0;
|
|
|
|
ptr = find_problem(code);
|
|
if (!ptr) {
|
|
printf("Unhandled error code (%d)!\n", code);
|
|
return 0;
|
|
}
|
|
def_yn = (ptr->flags & PR_NO_DEFAULT) ? 0 : 1;
|
|
|
|
/*
|
|
* Do special latch processing. This is where we ask the
|
|
* latch question, if it exists
|
|
*/
|
|
if (ptr->flags & PR_LATCH_MASK) {
|
|
latch = PR_LATCH(ptr->flags);
|
|
if (latch_question[latch] && !pr_latch[latch])
|
|
pr_latch[latch] = fix_problem(fs,
|
|
latch_question[latch],
|
|
ctx) + 1;
|
|
if (pr_suppress[latch])
|
|
suppress++;
|
|
}
|
|
|
|
if (!suppress) {
|
|
if (preen)
|
|
printf("%s: ", device_name);
|
|
print_e2fsck_message(fs, ptr->e2p_description, ctx, 1);
|
|
}
|
|
if (!(ptr->flags & PR_PREEN_OK))
|
|
preenhalt(fs);
|
|
|
|
if (ptr->flags & PR_MSG_ONLY)
|
|
return 1;
|
|
|
|
if (preen) {
|
|
answer = def_yn;
|
|
print_answer = 1;
|
|
} else if (ptr->flags & PR_LATCH_MASK) {
|
|
latch = PR_LATCH(ptr->flags);
|
|
if (!pr_latch[latch])
|
|
pr_latch[latch] =
|
|
ask(prompt[(int) ptr->prompt], def_yn) + 1;
|
|
else
|
|
print_answer = 1;
|
|
answer = pr_latch[latch] - 1;
|
|
} else
|
|
answer = ask(prompt[(int) ptr->prompt], def_yn);
|
|
if (!answer && !(ptr->flags & PR_NO_OK))
|
|
ext2fs_unmark_valid(fs);
|
|
|
|
if (print_answer)
|
|
printf("%s.\n",
|
|
answer ? preen_msg[(int) ptr->prompt] : "IGNORED");
|
|
|
|
return answer;
|
|
}
|