aboutsummaryrefslogtreecommitdiff
path: root/ubifs-utils/fsck.ubifs/problem.c
blob: f987e480c9a83a29d459149c7ecb5a603984b7e2 (plain)
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// 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 "key.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
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no inode"},	// FILE_HAS_NO_INODE
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has zero-nlink inode"},	// FILE_HAS_0_NLINK_INODE
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has inconsistent type"},	// FILE_HAS_INCONSIST_TYPE
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has too many dentries"},	// FILE_HAS_TOO_MANY_DENT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File should not have data"},	// FILE_SHOULDNT_HAVE_DATA
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "File has no dentries"},	// FILE_HAS_NO_DENT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has no host"},	// XATTR_HAS_NO_HOST
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Xattr file has wrong host"},	// XATTR_HAS_WRONG_HOST
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Encrypted file has no encryption information"},	// FILE_HAS_NO_ENCRYPT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is disconnected(regular file without dentries)"},	// FILE_IS_DISCONNECTED
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Root dir should not have a dentry"},	// FILE_ROOT_HAS_DENT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA, "Dentry is unreachable"},	// DENTRY_IS_UNREACHABLE
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "File is inconsistent"},	// FILE_IS_INCONSISTENT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX | PROBLEM_DROP_DATA | PROBLEM_NEED_REBUILD, "TNC is empty"},	// EMPTY_TNC
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Corrupted pnode/nnode"},	// LPT_CORRUPTED
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for nnode"},	// NNODE_INCORRECT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for pnode"},	// PNODE_INCORRECT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for LEB"},	// LP_INCORRECT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Incorrect space statistics"},	// SPACE_STAT_INCORRECT
	{PROBLEM_FIXABLE | PROBLEM_MUST_FIX, "Inconsistent properties for lprops table"},	// LTAB_INCORRECT
};

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?";
	case FILE_HAS_NO_INODE:
	case FILE_HAS_0_NLINK_INODE:
	case FILE_HAS_NO_DENT:
	case XATTR_HAS_NO_HOST:
	case XATTR_HAS_WRONG_HOST:
	case FILE_HAS_NO_ENCRYPT:
	case FILE_ROOT_HAS_DENT:
	case DENTRY_IS_UNREACHABLE:
		return "Delete it?";
	case FILE_HAS_INCONSIST_TYPE:
	case FILE_HAS_TOO_MANY_DENT:
		return "Remove dentry?";
	case FILE_SHOULDNT_HAVE_DATA:
		return "Remove data block?";
	case FILE_IS_DISCONNECTED:
		return "Put it into disconnected list?";
	case LPT_CORRUPTED:
		return "Rebuild LPT?";
	}

	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;
	}
	case FILE_HAS_NO_INODE:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;

		log_out(c, "problem: %s, ino %lu", problem->desc, ifp->file->inum);
		break;
	}
	case FILE_HAS_INCONSIST_TYPE:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;

		log_out(c, "problem: %s, ino %lu, inode type %s%s, dentry %s has type %s%s",
			problem->desc, ifp->file->inum,
			ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
			ifp->file->ino.is_xattr ? "(xattr)" : "",
			c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
			ubifs_get_type_name(dent_node->type),
			key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
		break;
	}
	case FILE_HAS_TOO_MANY_DENT:
	case FILE_ROOT_HAS_DENT:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;

		log_out(c, "problem: %s, ino %lu, type %s%s, dentry %s",
			problem->desc, ifp->file->inum,
			ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
			ifp->file->ino.is_xattr ? "(xattr)" : "",
			c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name);
		break;
	}
	case FILE_SHOULDNT_HAVE_DATA:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_data_node *data_node = (const struct scanned_data_node *)ifp->priv;

		log_out(c, "problem: %s, ino %lu, type %s%s, data block %u",
			problem->desc, ifp->file->inum,
			ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
			ifp->file->ino.is_xattr ? "(xattr)" : "",
			key_block(c, &data_node->key));
		break;
	}
	case FILE_HAS_0_NLINK_INODE:
	case FILE_HAS_NO_DENT:
	case XATTR_HAS_NO_HOST:
	case FILE_HAS_NO_ENCRYPT:
	case FILE_IS_DISCONNECTED:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;

		log_out(c, "problem: %s, ino %lu type %s%s", problem->desc,
			ifp->file->inum,
			ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
			ifp->file->ino.is_xattr ? "(xattr)" : "");
		break;
	}
	case XATTR_HAS_WRONG_HOST:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_file *host = (const struct scanned_file *)ifp->priv;

		log_out(c, "problem: %s, ino %lu type %s%s, host ino %lu type %s%s",
			problem->desc, ifp->file->inum,
			ubifs_get_type_name(ubifs_get_dent_type(ifp->file->ino.mode)),
			ifp->file->ino.is_xattr ? "(xattr)" : "", host->inum,
			ubifs_get_type_name(ubifs_get_dent_type(host->ino.mode)),
			host->ino.is_xattr ? "(xattr)" : "");
		break;
	}
	case DENTRY_IS_UNREACHABLE:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_dent_node *dent_node = (const struct scanned_dent_node *)ifp->priv;

		log_out(c, "problem: %s, ino %lu, unreachable dentry %s, type %s%s",
			problem->desc, ifp->file->inum,
			c->encrypted && !ifp->file->ino.is_xattr ? "<encrypted>" : dent_node->name,
			ubifs_get_type_name(dent_node->type),
			key_type(c, &dent_node->key) == UBIFS_XENT_KEY ? "(xattr)" : "");
		break;
	}
	case FILE_IS_INCONSISTENT:
	{
		const struct invalid_file_problem *ifp = (const struct invalid_file_problem *)priv;
		const struct scanned_file *file = ifp->file;

		log_out(c, "problem: %s, ino %lu type %s, nlink %u xcnt %u xsz %u xnms %u size %llu, "
			"should be nlink %u xcnt %u xsz %u xnms %u size %llu",
			problem->desc, file->inum,
			file->ino.is_xattr ? "xattr" : ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)),
			file->ino.nlink, file->ino.xcnt, file->ino.xsz,
			file->ino.xnms, file->ino.size,
			file->calc_nlink, file->calc_xcnt, file->calc_xsz,
			file->calc_xnms, file->calc_size);
		break;
	}
	case NNODE_INCORRECT:
	{
		const struct nnode_problem *nnp = (const struct nnode_problem *)priv;

		log_out(c, "problem: %s, nnode num %d expected %d parent num %d iip %d",
			problem->desc, nnp->nnode->num, nnp->num,
			nnp->parent_nnode ? nnp->parent_nnode->num : 0,
			nnp->nnode->iip);
		break;
	}
	case PNODE_INCORRECT:
	{
		const struct pnode_problem *pnp = (const struct pnode_problem *)priv;

		log_out(c, "problem: %s, pnode num %d expected %d parent num %d iip %d",
			problem->desc, pnp->pnode->num, pnp->num,
			pnp->pnode->parent->num, pnp->pnode->iip);
		break;
	}
	case LP_INCORRECT:
	{
		const struct lp_problem *lpp = (const struct lp_problem *)priv;

		log_out(c, "problem: %s %d, free %d dirty %d is_idx %d, should be lnum %d free %d dirty %d is_idx %d",
			problem->desc, lpp->lp->lnum, lpp->lp->free,
			lpp->lp->dirty, lpp->lp->flags & LPROPS_INDEX ? 1 : 0,
			lpp->lnum, lpp->free, lpp->dirty, lpp->is_idx);
		break;
	}
	case SPACE_STAT_INCORRECT:
	{
		const struct space_stat_problem *ssp = (const struct space_stat_problem *)priv;

		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",
			problem->desc, ssp->lst->empty_lebs, ssp->lst->idx_lebs,
			ssp->lst->total_free, ssp->lst->total_dirty,
			ssp->lst->total_used, ssp->lst->total_dead,
			ssp->lst->total_dark, ssp->calc_lst->empty_lebs,
			ssp->calc_lst->idx_lebs, ssp->calc_lst->total_free,
			ssp->calc_lst->total_dirty, ssp->calc_lst->total_used,
			ssp->calc_lst->total_dead, ssp->calc_lst->total_dark);
		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;
}