]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/repair.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / scrub / repair.c
CommitLineData
8d318d62 1// SPDX-License-Identifier: GPL-2.0-or-later
ee310b0c 2/*
52520522 3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
8d318d62 4 * Author: Darrick J. Wong <djwong@kernel.org>
ee310b0c
DW
5 */
6#include "xfs.h"
7#include <stdint.h>
8#include <stdlib.h>
9#include <sys/types.h>
10#include <sys/statvfs.h>
11#include "list.h"
42b4c8e8 12#include "libfrog/paths.h"
ee310b0c
DW
13#include "xfs_scrub.h"
14#include "common.h"
15#include "scrub.h"
16#include "progress.h"
17#include "repair.h"
18
19/*
20 * Prioritize action items in order of how long we can wait.
21 * 0 = do it now, 10000 = do it later.
22 *
23 * To minimize the amount of repair work, we want to prioritize metadata
24 * objects by perceived corruptness. If CORRUPT is set, the fields are
25 * just plain bad; try fixing that first. Otherwise if XCORRUPT is set,
26 * the fields could be bad, but the xref data could also be bad; we'll
27 * try fixing that next. Finally, if XFAIL is set, some other metadata
28 * structure failed validation during xref, so we'll recheck this
29 * metadata last since it was probably fine.
30 *
31 * For metadata that lie in the critical path of checking other metadata
32 * (superblock, AG{F,I,FL}, inobt) we scrub and fix those things before
33 * we even get to handling their dependencies, so things should progress
34 * in order.
35 */
36
37/* Sort action items in severity order. */
38static int
39PRIO(
c6b593ee 40 const struct action_item *aitem,
ee310b0c
DW
41 int order)
42{
43 if (aitem->flags & XFS_SCRUB_OFLAG_CORRUPT)
44 return order;
45 else if (aitem->flags & XFS_SCRUB_OFLAG_XCORRUPT)
46 return 100 + order;
47 else if (aitem->flags & XFS_SCRUB_OFLAG_XFAIL)
48 return 200 + order;
49 else if (aitem->flags & XFS_SCRUB_OFLAG_PREEN)
50 return 300 + order;
51 abort();
52}
53
54/* Sort the repair items in dependency order. */
55static int
56xfs_action_item_priority(
c6b593ee 57 const struct action_item *aitem)
ee310b0c
DW
58{
59 switch (aitem->type) {
60 case XFS_SCRUB_TYPE_SB:
61 case XFS_SCRUB_TYPE_AGF:
62 case XFS_SCRUB_TYPE_AGFL:
63 case XFS_SCRUB_TYPE_AGI:
64 case XFS_SCRUB_TYPE_BNOBT:
65 case XFS_SCRUB_TYPE_CNTBT:
66 case XFS_SCRUB_TYPE_INOBT:
67 case XFS_SCRUB_TYPE_FINOBT:
68 case XFS_SCRUB_TYPE_REFCNTBT:
69 case XFS_SCRUB_TYPE_RMAPBT:
70 case XFS_SCRUB_TYPE_INODE:
71 case XFS_SCRUB_TYPE_BMBTD:
72 case XFS_SCRUB_TYPE_BMBTA:
73 case XFS_SCRUB_TYPE_BMBTC:
74 return PRIO(aitem, aitem->type - 1);
75 case XFS_SCRUB_TYPE_DIR:
76 case XFS_SCRUB_TYPE_XATTR:
77 case XFS_SCRUB_TYPE_SYMLINK:
78 case XFS_SCRUB_TYPE_PARENT:
79 return PRIO(aitem, XFS_SCRUB_TYPE_DIR);
80 case XFS_SCRUB_TYPE_RTBITMAP:
81 case XFS_SCRUB_TYPE_RTSUM:
82 return PRIO(aitem, XFS_SCRUB_TYPE_RTBITMAP);
83 case XFS_SCRUB_TYPE_UQUOTA:
84 case XFS_SCRUB_TYPE_GQUOTA:
85 case XFS_SCRUB_TYPE_PQUOTA:
86 return PRIO(aitem, XFS_SCRUB_TYPE_UQUOTA);
cbaf1c9d
DW
87 case XFS_SCRUB_TYPE_FSCOUNTERS:
88 /* This should always go after AG headers no matter what. */
89 return PRIO(aitem, INT_MAX);
ee310b0c
DW
90 }
91 abort();
92}
93
94/* Make sure that btrees get repaired before headers. */
95static int
96xfs_action_item_compare(
97 void *priv,
c6b593ee
DW
98 const struct list_head *a,
99 const struct list_head *b)
ee310b0c 100{
c6b593ee
DW
101 const struct action_item *ra;
102 const struct action_item *rb;
ee310b0c
DW
103
104 ra = container_of(a, struct action_item, list);
105 rb = container_of(b, struct action_item, list);
106
107 return xfs_action_item_priority(ra) - xfs_action_item_priority(rb);
108}
109
110/*
111 * Figure out which AG metadata must be fixed before we can move on
112 * to the inode scan.
113 */
114void
83d2c80b
DW
115action_list_find_mustfix(
116 struct action_list *alist,
117 struct action_list *immediate_alist,
ee310b0c
DW
118 unsigned long long *broken_primaries,
119 unsigned long long *broken_secondaries)
120{
121 struct action_item *n;
122 struct action_item *aitem;
123
124 list_for_each_entry_safe(aitem, n, &alist->list, list) {
125 if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
126 continue;
127 switch (aitem->type) {
128 case XFS_SCRUB_TYPE_RMAPBT:
129 (*broken_secondaries)++;
130 break;
131 case XFS_SCRUB_TYPE_FINOBT:
132 case XFS_SCRUB_TYPE_INOBT:
133 alist->nr--;
134 list_move_tail(&aitem->list, &immediate_alist->list);
135 immediate_alist->nr++;
7c432f77 136 fallthrough;
ee310b0c
DW
137 case XFS_SCRUB_TYPE_BNOBT:
138 case XFS_SCRUB_TYPE_CNTBT:
139 case XFS_SCRUB_TYPE_REFCNTBT:
140 (*broken_primaries)++;
141 break;
142 default:
143 abort();
144 break;
145 }
146 }
147}
148
83d2c80b
DW
149/*
150 * Allocate a certain number of repair lists for the scrub context. Returns
151 * zero or a positive error number.
152 */
153int
154action_lists_alloc(
ee310b0c 155 size_t nr,
83d2c80b 156 struct action_list **listsp)
ee310b0c 157{
83d2c80b 158 struct action_list *lists;
ee310b0c
DW
159 xfs_agnumber_t agno;
160
83d2c80b 161 lists = calloc(nr, sizeof(struct action_list));
ee310b0c 162 if (!lists)
83d2c80b 163 return errno;
ee310b0c
DW
164
165 for (agno = 0; agno < nr; agno++)
83d2c80b 166 action_list_init(&lists[agno]);
ee310b0c
DW
167 *listsp = lists;
168
83d2c80b 169 return 0;
ee310b0c
DW
170}
171
172/* Free the repair lists. */
173void
83d2c80b
DW
174action_lists_free(
175 struct action_list **listsp)
ee310b0c
DW
176{
177 free(*listsp);
178 *listsp = NULL;
179}
180
181/* Initialize repair list */
182void
83d2c80b
DW
183action_list_init(
184 struct action_list *alist)
ee310b0c
DW
185{
186 INIT_LIST_HEAD(&alist->list);
187 alist->nr = 0;
188 alist->sorted = false;
189}
190
191/* Number of repairs in this list. */
0e5dce33 192unsigned long long
83d2c80b
DW
193action_list_length(
194 struct action_list *alist)
ee310b0c
DW
195{
196 return alist->nr;
197};
198
199/* Add to the list of repairs. */
200void
83d2c80b
DW
201action_list_add(
202 struct action_list *alist,
ee310b0c
DW
203 struct action_item *aitem)
204{
205 list_add_tail(&aitem->list, &alist->list);
206 alist->nr++;
207 alist->sorted = false;
208}
209
210/* Splice two repair lists. */
211void
83d2c80b
DW
212action_list_splice(
213 struct action_list *dest,
214 struct action_list *src)
ee310b0c
DW
215{
216 if (src->nr == 0)
217 return;
218
219 list_splice_tail_init(&src->list, &dest->list);
220 dest->nr += src->nr;
221 src->nr = 0;
222 dest->sorted = false;
223}
224
225/* Repair everything on this list. */
83d2c80b
DW
226int
227action_list_process(
ee310b0c
DW
228 struct scrub_ctx *ctx,
229 int fd,
83d2c80b 230 struct action_list *alist,
ee310b0c
DW
231 unsigned int repair_flags)
232{
bb9be147
DW
233 struct xfs_fd xfd;
234 struct xfs_fd *xfdp = &ctx->mnt;
ee310b0c
DW
235 struct action_item *aitem;
236 struct action_item *n;
237 enum check_outcome fix;
238
bb9be147
DW
239 /*
240 * If the caller passed us a file descriptor for a scrub, use it
241 * instead of scrub-by-handle because this enables the kernel to skip
242 * costly inode btree lookups.
243 */
244 if (fd >= 0) {
245 memcpy(&xfd, xfdp, sizeof(xfd));
246 xfd.fd = fd;
247 xfdp = &xfd;
248 }
249
ee310b0c
DW
250 if (!alist->sorted) {
251 list_sort(NULL, &alist->list, xfs_action_item_compare);
252 alist->sorted = true;
253 }
254
255 list_for_each_entry_safe(aitem, n, &alist->list, list) {
bb9be147 256 fix = xfs_repair_metadata(ctx, xfdp, aitem, repair_flags);
ee310b0c
DW
257 switch (fix) {
258 case CHECK_DONE:
259 if (!(repair_flags & ALP_NOPROGRESS))
260 progress_add(1);
261 alist->nr--;
262 list_del(&aitem->list);
263 free(aitem);
264 continue;
265 case CHECK_ABORT:
83d2c80b 266 return ECANCELED;
ee310b0c
DW
267 case CHECK_RETRY:
268 continue;
269 case CHECK_REPAIR:
270 abort();
271 }
272 }
273
273165cc 274 if (scrub_excessive_errors(ctx))
83d2c80b
DW
275 return ECANCELED;
276 return 0;
ee310b0c
DW
277}
278
279/* Defer all the repairs until phase 4. */
280void
83d2c80b 281action_list_defer(
ee310b0c
DW
282 struct scrub_ctx *ctx,
283 xfs_agnumber_t agno,
83d2c80b 284 struct action_list *alist)
ee310b0c 285{
3f9efb2e 286 ASSERT(agno < ctx->mnt.fsgeom.agcount);
ee310b0c 287
83d2c80b 288 action_list_splice(&ctx->action_lists[agno], alist);
ee310b0c
DW
289}
290
291/* Run actions now and defer unfinished items for later. */
83d2c80b
DW
292int
293action_list_process_or_defer(
ee310b0c
DW
294 struct scrub_ctx *ctx,
295 xfs_agnumber_t agno,
83d2c80b 296 struct action_list *alist)
ee310b0c 297{
83d2c80b 298 int ret;
ee310b0c 299
bb9be147 300 ret = action_list_process(ctx, -1, alist,
ee310b0c 301 ALP_REPAIR_ONLY | ALP_NOPROGRESS);
83d2c80b
DW
302 if (ret)
303 return ret;
ee310b0c 304
83d2c80b
DW
305 action_list_defer(ctx, agno, alist);
306 return 0;
ee310b0c 307}