]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/repair.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / scrub / repair.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
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"
12 #include "path.h"
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. */
38 static int
39 PRIO(
40 struct action_item *aitem,
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. */
55 static int
56 xfs_action_item_priority(
57 struct action_item *aitem)
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);
87 }
88 abort();
89 }
90
91 /* Make sure that btrees get repaired before headers. */
92 static int
93 xfs_action_item_compare(
94 void *priv,
95 struct list_head *a,
96 struct list_head *b)
97 {
98 struct action_item *ra;
99 struct action_item *rb;
100
101 ra = container_of(a, struct action_item, list);
102 rb = container_of(b, struct action_item, list);
103
104 return xfs_action_item_priority(ra) - xfs_action_item_priority(rb);
105 }
106
107 /*
108 * Figure out which AG metadata must be fixed before we can move on
109 * to the inode scan.
110 */
111 void
112 xfs_action_list_find_mustfix(
113 struct xfs_action_list *alist,
114 struct xfs_action_list *immediate_alist,
115 unsigned long long *broken_primaries,
116 unsigned long long *broken_secondaries)
117 {
118 struct action_item *n;
119 struct action_item *aitem;
120
121 list_for_each_entry_safe(aitem, n, &alist->list, list) {
122 if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT))
123 continue;
124 switch (aitem->type) {
125 case XFS_SCRUB_TYPE_RMAPBT:
126 (*broken_secondaries)++;
127 break;
128 case XFS_SCRUB_TYPE_FINOBT:
129 case XFS_SCRUB_TYPE_INOBT:
130 alist->nr--;
131 list_move_tail(&aitem->list, &immediate_alist->list);
132 immediate_alist->nr++;
133 /* fall through */
134 case XFS_SCRUB_TYPE_BNOBT:
135 case XFS_SCRUB_TYPE_CNTBT:
136 case XFS_SCRUB_TYPE_REFCNTBT:
137 (*broken_primaries)++;
138 break;
139 default:
140 abort();
141 break;
142 }
143 }
144 }
145
146 /* Allocate a certain number of repair lists for the scrub context. */
147 bool
148 xfs_action_lists_alloc(
149 size_t nr,
150 struct xfs_action_list **listsp)
151 {
152 struct xfs_action_list *lists;
153 xfs_agnumber_t agno;
154
155 lists = calloc(nr, sizeof(struct xfs_action_list));
156 if (!lists)
157 return false;
158
159 for (agno = 0; agno < nr; agno++)
160 xfs_action_list_init(&lists[agno]);
161 *listsp = lists;
162
163 return true;
164 }
165
166 /* Free the repair lists. */
167 void
168 xfs_action_lists_free(
169 struct xfs_action_list **listsp)
170 {
171 free(*listsp);
172 *listsp = NULL;
173 }
174
175 /* Initialize repair list */
176 void
177 xfs_action_list_init(
178 struct xfs_action_list *alist)
179 {
180 INIT_LIST_HEAD(&alist->list);
181 alist->nr = 0;
182 alist->sorted = false;
183 }
184
185 /* Number of repairs in this list. */
186 size_t
187 xfs_action_list_length(
188 struct xfs_action_list *alist)
189 {
190 return alist->nr;
191 };
192
193 /* Add to the list of repairs. */
194 void
195 xfs_action_list_add(
196 struct xfs_action_list *alist,
197 struct action_item *aitem)
198 {
199 list_add_tail(&aitem->list, &alist->list);
200 alist->nr++;
201 alist->sorted = false;
202 }
203
204 /* Splice two repair lists. */
205 void
206 xfs_action_list_splice(
207 struct xfs_action_list *dest,
208 struct xfs_action_list *src)
209 {
210 if (src->nr == 0)
211 return;
212
213 list_splice_tail_init(&src->list, &dest->list);
214 dest->nr += src->nr;
215 src->nr = 0;
216 dest->sorted = false;
217 }
218
219 /* Repair everything on this list. */
220 bool
221 xfs_action_list_process(
222 struct scrub_ctx *ctx,
223 int fd,
224 struct xfs_action_list *alist,
225 unsigned int repair_flags)
226 {
227 struct action_item *aitem;
228 struct action_item *n;
229 enum check_outcome fix;
230
231 if (!alist->sorted) {
232 list_sort(NULL, &alist->list, xfs_action_item_compare);
233 alist->sorted = true;
234 }
235
236 list_for_each_entry_safe(aitem, n, &alist->list, list) {
237 fix = xfs_repair_metadata(ctx, fd, aitem, repair_flags);
238 switch (fix) {
239 case CHECK_DONE:
240 if (!(repair_flags & ALP_NOPROGRESS))
241 progress_add(1);
242 alist->nr--;
243 list_del(&aitem->list);
244 free(aitem);
245 continue;
246 case CHECK_ABORT:
247 return false;
248 case CHECK_RETRY:
249 continue;
250 case CHECK_REPAIR:
251 abort();
252 }
253 }
254
255 return !xfs_scrub_excessive_errors(ctx);
256 }
257
258 /* Defer all the repairs until phase 4. */
259 void
260 xfs_action_list_defer(
261 struct scrub_ctx *ctx,
262 xfs_agnumber_t agno,
263 struct xfs_action_list *alist)
264 {
265 ASSERT(agno < ctx->geo.agcount);
266
267 xfs_action_list_splice(&ctx->action_lists[agno], alist);
268 }
269
270 /* Run actions now and defer unfinished items for later. */
271 bool
272 xfs_action_list_process_or_defer(
273 struct scrub_ctx *ctx,
274 xfs_agnumber_t agno,
275 struct xfs_action_list *alist)
276 {
277 bool moveon;
278
279 moveon = xfs_action_list_process(ctx, ctx->mnt_fd, alist,
280 ALP_REPAIR_ONLY | ALP_NOPROGRESS);
281 if (!moveon)
282 return moveon;
283
284 xfs_action_list_defer(ctx, agno, alist);
285 return true;
286 }