1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024, Huawei Technologies Co, Ltd.
*
* Authors: Zhihao Cheng <chengzhihao1@huawei.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "ubifs.h"
#include "defs.h"
#include "debug.h"
#include "fsck.ubifs.h"
/*
* problem flags.
*
* PROBLEM_FIXABLE: problem is fixable, unsolvable problem such as corrupted
* super block will abort the fsck program
* PROBLEM_MUST_FIX: problem must be fixed because it will affect the subsequent
* fsck process, otherwise aborting the fsck program
* PROBLEM_DROP_DATA: user data could be dropped after fixing the problem
* PROBLEM_NEED_REBUILD: rebuilding filesystem is needed to fix the problem
*/
#define PROBLEM_FIXABLE (1<<0)
#define PROBLEM_MUST_FIX (1<<1)
#define PROBLEM_DROP_DATA (1<<2)
#define PROBLEM_NEED_REBUILD (1<<3)
struct fsck_problem {
unsigned int flags;
const char *desc;
};
static const struct fsck_problem problem_table[] = {
{0, "Corrupted superblock"}, // SB_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted master node"}, // MST_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted log area"}, // LOG_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted bud LEB"}, // BUD_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted index node"}, // TNC_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data searched from TNC"}, // TNC_DATA_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted orphan LEB"}, // ORPHAN_CORRUPTED
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid inode node"}, // INVALID_INO_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid dentry node"}, // INVALID_DENT_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid data node"}, // INVALID_DATA_NODE
{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data is scanned"}, // SCAN_CORRUPTED
};
static const char *get_question(const struct fsck_problem *problem,
int problem_type)
{
if (problem->flags & PROBLEM_NEED_REBUILD)
return "Rebuild filesystem?";
switch (problem_type) {
case BUD_CORRUPTED:
return "Drop bud?";
case TNC_DATA_CORRUPTED:
case INVALID_INO_NODE:
case INVALID_DENT_NODE:
case INVALID_DATA_NODE:
case SCAN_CORRUPTED:
return "Drop it?";
case ORPHAN_CORRUPTED:
return "Drop orphans on the LEB?";
}
return "Fix it?";
}
static void print_problem(const struct ubifs_info *c,
const struct fsck_problem *problem, int problem_type,
const void *priv)
{
switch (problem_type) {
case BUD_CORRUPTED:
{
const struct ubifs_bud *bud = (const struct ubifs_bud *)priv;
log_out(c, "problem: %s %d:%d %s", problem->desc, bud->lnum,
bud->start, dbg_jhead(bud->jhead));
break;
}
case ORPHAN_CORRUPTED:
{
const int *lnum = (const int *)priv;
log_out(c, "problem: %s %d", problem->desc, *lnum);
break;
}
case SCAN_CORRUPTED:
{
const struct ubifs_zbranch *zbr = (const struct ubifs_zbranch *)priv;
log_out(c, "problem: %s in LEB %d, node in %d:%d becomes invalid",
problem->desc, zbr->lnum, zbr->lnum, zbr->offs);
break;
}
default:
log_out(c, "problem: %s", problem->desc);
break;
}
}
static void fatal_error(const struct ubifs_info *c,
const struct fsck_problem *problem)
{
if (!(problem->flags & PROBLEM_FIXABLE))
log_out(c, "inconsistent problem cannot be fixed");
else
log_out(c, "inconsistent problem must be fixed");
exit(exit_code);
}
/**
* fix_problem - whether fixing the inconsistent problem
* @c: UBIFS file-system description object
* @problem_type: the type of inconsistent problem
* @priv: private data for problem instance
*
* This function decides to fix/skip the inconsistent problem or abort the
* program according to @problem_type, returns %true if the problem should
* be fixed, returns %false if the problem will be skipped.
*/
bool fix_problem(const struct ubifs_info *c, int problem_type, const void *priv)
{
bool ans, ask = true, def_y = true;
const struct fsck_problem *problem = &problem_table[problem_type];
const char *question = get_question(problem, problem_type);
ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
if (!(problem->flags & PROBLEM_FIXABLE)) {
exit_code |= FSCK_UNCORRECTED;
fatal_error(c, problem);
}
if (FSCK(c)->mode == CHECK_MODE ||
((problem->flags & PROBLEM_DROP_DATA) && FSCK(c)->mode == SAFE_MODE) ||
((problem->flags & PROBLEM_NEED_REBUILD) &&
(FSCK(c)->mode == SAFE_MODE || FSCK(c)->mode == DANGER_MODE0)))
def_y = false;
if ((problem->flags & PROBLEM_NEED_REBUILD) &&
(FSCK(c)->mode == DANGER_MODE0 || FSCK(c)->mode == DANGER_MODE1))
ask = false;
print_problem(c, problem, problem_type, priv);
ans = def_y;
if (FSCK(c)->mode == NORMAL_MODE) {
printf("%s[%d] (%s%s)", c->program_name, getpid(),
c->dev_name ? : "-", mode_name(c));
if (prompt(question, def_y))
ans = true;
else
ans = false;
} else {
if (ask)
log_out(c, "%s %c\n", question, def_y ? 'y' : 'n');
}
if (!ans) {
exit_code |= FSCK_UNCORRECTED;
if (problem->flags & PROBLEM_MUST_FIX)
fatal_error(c, problem);
} else {
exit_code |= FSCK_NONDESTRUCT;
}
return ans;
}
|