Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * Copyright (C) 2024, Huawei Technologies Co, Ltd.
4 : *
5 : * Authors: Zhihao Cheng <chengzhihao1@huawei.com>
6 : */
7 :
8 : #include <stdio.h>
9 : #include <stdlib.h>
10 : #include <getopt.h>
11 :
12 : #include "ubifs.h"
13 : #include "defs.h"
14 : #include "debug.h"
15 : #include "key.h"
16 : #include "fsck.ubifs.h"
17 :
18 : /*
19 : * problem flags.
20 : *
21 : * PROBLEM_FIXABLE: problem is fixable, unsolvable problem such as corrupted
22 : * super block will abort the fsck program
23 : * PROBLEM_MUST_FIX: problem must be fixed because it will affect the subsequent
24 : * fsck process, otherwise aborting the fsck program
25 : * PROBLEM_DROP_DATA: user data could be dropped after fixing the problem
26 : * PROBLEM_NEED_REBUILD: rebuilding filesystem is needed to fix the problem
27 : */
28 : #define PROBLEM_FIXABLE (1<<0)
29 : #define PROBLEM_MUST_FIX (1<<1)
30 : #define PROBLEM_DROP_DATA (1<<2)
31 : #define PROBLEM_NEED_REBUILD (1<<3)
32 :
33 : struct fsck_problem {
34 : unsigned int flags;
35 : const char *desc;
36 : };
37 :
38 : static const struct fsck_problem problem_table[] = {
39 : {0, "Corrupted superblock"}, // SB_CORRUPTED
40 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted master node"}, // MST_CORRUPTED
41 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted log area"}, // LOG_CORRUPTED
42 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted bud LEB"}, // BUD_CORRUPTED
43 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "Corrupted index node"}, // TNC_CORRUPTED
44 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data searched from TNC"}, // TNC_DATA_CORRUPTED
45 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted orphan LEB"}, // ORPHAN_CORRUPTED
46 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid inode node"}, // INVALID_INO_NODE
47 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid dentry node"}, // INVALID_DENT_NODE
48 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Invalid data node"}, // INVALID_DATA_NODE
49 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Corrupted data is scanned"}, // SCAN_CORRUPTED
50 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no inode"}, // FILE_HAS_NO_INODE
51 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has zero-nlink inode"}, // FILE_HAS_0_NLINK_INODE
52 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has inconsistent type"}, // FILE_HAS_INCONSIST_TYPE
53 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has too many dentries"}, // FILE_HAS_TOO_MANY_DENT
54 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File should not have data"}, // FILE_SHOULDNT_HAVE_DATA
55 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no dentries"}, // FILE_HAS_NO_DENT
56 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has no host"}, // XATTR_HAS_NO_HOST
57 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has wrong host"}, // XATTR_HAS_WRONG_HOST
58 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Encrypted file has no encryption information"}, // FILE_HAS_NO_ENCRYPT
59 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"}, // FILE_IS_DISCONNECTED
60 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"}, // FILE_ROOT_HAS_DENT
61 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"}, // DENTRY_IS_UNREACHABLE
62 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"}, // FILE_IS_INCONSISTENT
63 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "TNC is empty"}, // EMPTY_TNC
64 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Corrupted pnode/nnode"}, // LPT_CORRUPTED
65 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for nnode"}, // NNODE_INCORRECT
66 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for pnode"}, // PNODE_INCORRECT
67 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for LEB"}, // LP_INCORRECT
68 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"}, // SPACE_STAT_INCORRECT
69 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"}, // LTAB_INCORRECT
70 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect index size"}, // INCORRECT_IDX_SZ
71 : {PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Root dir is lost"}, // ROOT_DIR_NOT_FOUND
72 : {PROBLEM_FIXABLE | PROBLEM_DROP_DATA, "Disconnected file cannot be recovered"}, // DISCONNECTED_FILE_CANNOT_BE_RECOVERED
73 : };
74 :
75 : static const char *get_question(const struct fsck_problem *problem,
76 : int problem_type)
77 : {
78 328296 : if (problem->flags & PROBLEM_NEED_REBUILD)
79 : return "Rebuild filesystem?";
80 :
81 327752 : switch (problem_type) {
82 : case BUD_CORRUPTED:
83 : return "Drop bud?";
84 : case TNC_DATA_CORRUPTED:
85 : case INVALID_INO_NODE:
86 : case INVALID_DENT_NODE:
87 : case INVALID_DATA_NODE:
88 : case SCAN_CORRUPTED:
89 : return "Drop it?";
90 : case ORPHAN_CORRUPTED:
91 : return "Drop orphans on the LEB?";
92 : case FILE_HAS_NO_INODE:
93 : case FILE_HAS_0_NLINK_INODE:
94 : case FILE_HAS_NO_DENT:
95 : case XATTR_HAS_NO_HOST:
96 : case XATTR_HAS_WRONG_HOST:
97 : case FILE_HAS_NO_ENCRYPT:
98 : case FILE_ROOT_HAS_DENT:
99 : case DENTRY_IS_UNREACHABLE:
100 : case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
101 : return "Delete it?";
102 : case FILE_HAS_INCONSIST_TYPE:
103 : case FILE_HAS_TOO_MANY_DENT:
104 : return "Remove dentry?";
105 : case FILE_SHOULDNT_HAVE_DATA:
106 : return "Remove data block?";
107 : case FILE_IS_DISCONNECTED:
108 : return "Put it into disconnected list?";
109 : case LPT_CORRUPTED:
110 : return "Rebuild LPT?";
111 : case ROOT_DIR_NOT_FOUND:
112 : return "Create a new one?";
113 : }
114 :
115 : return "Fix it?";
116 : }
117 :
118 328248 : static void print_problem(const struct ubifs_info *c,
119 : const struct fsck_problem *problem, int problem_type,
120 : const void *priv)
121 : {
122 328248 : switch (problem_type) {
123 22 : case BUD_CORRUPTED:
124 : {
125 22 : const struct ubifs_bud *bud = (const struct ubifs_bud *)priv;
126 :
127 44 : log_out(c, "problem: %s %d:%d %s", problem->desc, bud->lnum,
128 : bud->start, dbg_jhead(bud->jhead));
129 : break;
130 : }
131 6 : case ORPHAN_CORRUPTED:
132 : {
133 6 : const int *lnum = (const int *)priv;
134 :
135 12 : log_out(c, "problem: %s %d", problem->desc, *lnum);
136 : break;
137 : }
138 12625 : case SCAN_CORRUPTED:
139 : {
140 12625 : const struct ubifs_zbranch *zbr = (const struct ubifs_zbranch *)priv;
141 :
142 25250 : log_out(c, "problem: %s in LEB %d, node in %d:%d becomes invalid",
143 : problem->desc, zbr->lnum, zbr->lnum, zbr->offs);
144 : break;
145 : }
146 1255 : case FILE_HAS_NO_INODE:
147 : {
148 1255 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
149 :
150 2510 : log_out(c, "problem: %s, ino %lu", problem->desc, ifp->file->inum);
151 : break;
152 : }
153 15 : case FILE_HAS_INCONSIST_TYPE:
154 : {
155 15 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
156 15 : const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
157 :
158 45 : log_out(c, "problem: %s, ino %lu, inode type %s%s, dentry %s has type %s%s",
159 : problem->desc, ifp->file->inum,
160 : ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
161 : ifp->file->ino.is_xattr ? "(xattr)" : "",
162 : c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
163 : ubifs_get_type_name(dent_node->type),
164 : key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
165 : break;
166 : }
167 6 : case FILE_HAS_TOO_MANY_DENT:
168 : case FILE_ROOT_HAS_DENT:
169 : {
170 6 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
171 6 : const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
172 :
173 12 : log_out(c, "problem: %s, ino %lu, type %s%s, dentry %s",
174 : problem->desc, ifp->file->inum,
175 : ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
176 : ifp->file->ino.is_xattr ? "(xattr)" : "",
177 : c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name);
178 : break;
179 : }
180 6 : case FILE_SHOULDNT_HAVE_DATA:
181 : {
182 6 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
183 6 : const struct scanned_data_node *data_node = (const struct scanned_data_node *)ifp->priv;
184 :
185 18 : log_out(c, "problem: %s, ino %lu, type %s%s, data block %u",
186 : problem->desc, ifp->file->inum,
187 : ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
188 : ifp->file->ino.is_xattr ? "(xattr)" : "",
189 : key_block(c, &data_node->key));
190 : break;
191 : }
192 64413 : case FILE_HAS_0_NLINK_INODE:
193 : case FILE_HAS_NO_DENT:
194 : case XATTR_HAS_NO_HOST:
195 : case FILE_HAS_NO_ENCRYPT:
196 : case FILE_IS_DISCONNECTED:
197 : {
198 64413 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
199 :
200 128826 : log_out(c, "problem: %s, ino %lu type %s%s", problem->desc,
201 : ifp->file->inum,
202 : ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
203 : ifp->file->ino.is_xattr ? "(xattr)" : "");
204 : break;
205 : }
206 6 : case XATTR_HAS_WRONG_HOST:
207 : {
208 6 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
209 6 : const struct scanned_file *host = (const struct scanned_file *)ifp->priv;
210 :
211 12 : log_out(c, "problem: %s, ino %lu type %s%s, host ino %lu type %s%s",
212 : problem->desc, ifp->file->inum,
213 : ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
214 : ifp->file->ino.is_xattr ? "(xattr)" : "", host->inum,
215 : ubifs_get_type_name(ubifs_get_dent_type(host->ino.mode)),
216 : host->ino.is_xattr ? "(xattr)" : "");
217 : break;
218 : }
219 62053 : case DENTRY_IS_UNREACHABLE:
220 : {
221 62053 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
222 62053 : const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;
223 :
224 186159 : log_out(c, "problem: %s, ino %lu, unreachable dentry %s, type %s%s",
225 : problem->desc, ifp->file->inum,
226 : c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
227 : ubifs_get_type_name(dent_node->type),
228 : key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
229 : break;
230 : }
231 653 : case FILE_IS_INCONSISTENT:
232 : {
233 653 : const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
234 653 : const struct scanned_file *file = ifp->file;
235 :
236 1306 : log_out(c, "problem: %s, ino %lu type %s, nlink %u xcnt %u xsz %u xnms %u size %llu, "
237 : "should be nlink %u xcnt %u xsz %u xnms %u size %llu",
238 : problem->desc, file->inum,
239 : file->ino.is_xattr ? "xattr" : ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
240 : file->ino.nlink, file->ino.xcnt, file->ino.xsz,
241 : file->ino.xnms, file->ino.size,
242 : file->calc_nlink, file->calc_xcnt, file->calc_xsz,
243 : file->calc_xnms, file->calc_size);
244 : break;
245 : }
246 0 : case NNODE_INCORRECT:
247 : {
248 0 : const struct nnode_problem *nnp = (const struct nnode_problem *)priv;
249 :
250 0 : log_out(c, "problem: %s, nnode num %d expected %d parent num %d iip %d",
251 : problem->desc, nnp->nnode->num, nnp->num,
252 : nnp->parent_nnode ? nnp->parent_nnode->num : 0,
253 : nnp->nnode->iip);
254 : break;
255 : }
256 0 : case PNODE_INCORRECT:
257 : {
258 0 : const struct pnode_problem *pnp = (const struct pnode_problem *)priv;
259 :
260 0 : log_out(c, "problem: %s, pnode num %d expected %d parent num %d iip %d",
261 : problem->desc, pnp->pnode->num, pnp->num,
262 : pnp->pnode->parent->num, pnp->pnode->iip);
263 : break;
264 : }
265 766 : case LP_INCORRECT:
266 : {
267 766 : const struct lp_problem *lpp = (const struct lp_problem *)priv;
268 :
269 1532 : log_out(c, "problem: %s %d, free %d dirty %d is_idx %d, should be lnum %d free %d dirty %d is_idx %d",
270 : problem->desc, lpp->lp->lnum, lpp->lp->free,
271 : lpp->lp->dirty, lpp->lp->flags & LPROPS_INDEX ? 1 : 0,
272 : lpp->lnum, lpp->free, lpp->dirty, lpp->is_idx);
273 : break;
274 : }
275 77 : case SPACE_STAT_INCORRECT:
276 : {
277 77 : const struct space_stat_problem *ssp = (const struct space_stat_problem *)priv;
278 :
279 154 : log_out(c, "problem: %s, empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld, should be empty_lebs %d idx_lebs %d total_free %lld total_dirty %lld total_used %lld total_dead %lld total_dark %lld",
280 : problem->desc, ssp->lst->empty_lebs, ssp->lst->idx_lebs,
281 : ssp->lst->total_free, ssp->lst->total_dirty,
282 : ssp->lst->total_used, ssp->lst->total_dead,
283 : ssp->lst->total_dark, ssp->calc_lst->empty_lebs,
284 : ssp->calc_lst->idx_lebs, ssp->calc_lst->total_free,
285 : ssp->calc_lst->total_dirty, ssp->calc_lst->total_used,
286 : ssp->calc_lst->total_dead, ssp->calc_lst->total_dark);
287 : break;
288 : }
289 3 : case INCORRECT_IDX_SZ:
290 : {
291 3 : const unsigned long long *calc_sz = (const unsigned long long *)priv;
292 :
293 6 : log_out(c, "problem: %s, index size is %llu, should be %llu",
294 : problem->desc, c->calc_idx_sz, *calc_sz);
295 : break;
296 : }
297 628 : case DISCONNECTED_FILE_CANNOT_BE_RECOVERED:
298 : {
299 628 : const struct scanned_file *file = (const struct scanned_file *)priv;
300 :
301 1256 : log_out(c, "problem: %s, ino %lu, size %llu", problem->desc,
302 : file->inum, file->ino.size);
303 : break;
304 : }
305 185714 : default:
306 371428 : log_out(c, "problem: %s", problem->desc);
307 : break;
308 : }
309 328248 : }
310 :
311 350 : static void fatal_error(const struct ubifs_info *c,
312 : const struct fsck_problem *problem)
313 : {
314 350 : if (!(problem->flags & PROBLEM_FIXABLE))
315 48 : log_out(c, "inconsistent problem cannot be fixed");
316 : else
317 302 : log_out(c, "inconsistent problem must be fixed");
318 350 : exit(exit_code);
319 : }
320 :
321 : /**
322 : * fix_problem - whether fixing the inconsistent problem
323 : * @c: UBIFS file-system description object
324 : * @problem_type: the type of inconsistent problem
325 : * @priv: private data for problem instance
326 : *
327 : * This function decides to fix/skip the inconsistent problem or abort the
328 : * program according to @problem_type, returns %true if the problem should
329 : * be fixed, returns %false if the problem will be skipped.
330 : */
331 328296 : bool fix_problem(const struct ubifs_info *c, int problem_type, const void *priv)
332 : {
333 328296 : bool ans, ask = true, def_y = true;
334 328296 : const struct fsck_problem *problem = &problem_table[problem_type];
335 656592 : const char *question = get_question(problem, problem_type);
336 :
337 328296 : ubifs_assert(c, FSCK(c)->mode != REBUILD_MODE);
338 :
339 328296 : if (!(problem->flags & PROBLEM_FIXABLE)) {
340 48 : exit_code |= FSCK_UNCORRECTED;
341 48 : fatal_error(c, problem);
342 : }
343 :
344 656384 : if (FSCK(c)->mode == CHECK_MODE ||
345 635170 : ((problem->flags & PROBLEM_DROP_DATA) && FSCK(c)->mode == SAFE_MODE) ||
346 471 : ((problem->flags & PROBLEM_NEED_REBUILD) &&
347 471 : (FSCK(c)->mode == SAFE_MODE || FSCK(c)->mode == DANGER_MODE0)))
348 302 : def_y = false;
349 :
350 328792 : if ((problem->flags & PROBLEM_NEED_REBUILD) &&
351 544 : (FSCK(c)->mode == DANGER_MODE0 || FSCK(c)->mode == DANGER_MODE1))
352 471 : ask = false;
353 :
354 328248 : print_problem(c, problem, problem_type, priv);
355 328248 : ans = def_y;
356 328248 : if (FSCK(c)->mode == NORMAL_MODE) {
357 0 : printf("%s[%d] (%s%s)", c->program_name, getpid(),
358 0 : c->dev_name ? : "-", mode_name(c));
359 0 : if (prompt(question, def_y))
360 : ans = true;
361 : else
362 : ans = false;
363 : } else {
364 328248 : if (ask)
365 327777 : log_out(c, "%s %c\n", question, def_y ? 'y' : 'n');
366 : }
367 :
368 328248 : if (!ans) {
369 302 : exit_code |= FSCK_UNCORRECTED;
370 302 : if (problem->flags & PROBLEM_MUST_FIX)
371 302 : fatal_error(c, problem);
372 : } else {
373 327946 : exit_code |= FSCK_NONDESTRUCT;
374 : }
375 :
376 327946 : return ans;
377 : }
|