]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
fa16b376 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
fa16b376 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
fa16b376 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
fa16b376 | 7 | #include <stdint.h> |
fa16b376 | 8 | #include <sys/types.h> |
fa16b376 | 9 | #include <sys/statvfs.h> |
19852474 | 10 | #include "list.h" |
42b4c8e8 | 11 | #include "libfrog/paths.h" |
56598728 | 12 | #include "libfrog/workqueue.h" |
fa16b376 DW |
13 | #include "xfs_scrub.h" |
14 | #include "common.h" | |
15 | #include "counter.h" | |
16 | #include "inodes.h" | |
ed60d210 | 17 | #include "progress.h" |
fa16b376 | 18 | #include "scrub.h" |
ee310b0c | 19 | #include "repair.h" |
fa16b376 DW |
20 | |
21 | /* Phase 3: Scan all inodes. */ | |
22 | ||
23 | /* | |
24 | * Run a per-file metadata scanner. We use the ino/gen interface to | |
25 | * ensure that the inode we're checking matches what the inode scan | |
26 | * told us to look at. | |
27 | */ | |
d22f2471 DW |
28 | static int |
29 | scrub_fd( | |
fa16b376 | 30 | struct scrub_ctx *ctx, |
d22f2471 | 31 | int (*fn)(struct scrub_ctx *ctx, uint64_t ino, |
83d2c80b | 32 | uint32_t gen, struct action_list *a), |
4cca629d | 33 | struct xfs_bulkstat *bs, |
83d2c80b | 34 | struct action_list *alist) |
fa16b376 | 35 | { |
991e5a84 | 36 | return fn(ctx, bs->bs_ino, bs->bs_gen, alist); |
fa16b376 DW |
37 | } |
38 | ||
39 | struct scrub_inode_ctx { | |
40 | struct ptcounter *icount; | |
df024103 | 41 | bool aborted; |
fa16b376 DW |
42 | }; |
43 | ||
6c05cc5d DW |
44 | /* Report a filesystem error that the vfs fed us on close. */ |
45 | static void | |
df024103 | 46 | report_close_error( |
6c05cc5d | 47 | struct scrub_ctx *ctx, |
4cca629d | 48 | struct xfs_bulkstat *bstat) |
6c05cc5d DW |
49 | { |
50 | char descr[DESCR_BUFSZ]; | |
6c05cc5d DW |
51 | int old_errno = errno; |
52 | ||
15589f0a DW |
53 | scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ, bstat->bs_ino, |
54 | bstat->bs_gen, NULL); | |
6c05cc5d DW |
55 | errno = old_errno; |
56 | str_errno(ctx, descr); | |
57 | } | |
58 | ||
fa16b376 DW |
59 | /* Verify the contents, xattrs, and extent maps of an inode. */ |
60 | static int | |
df024103 | 61 | scrub_inode( |
fa16b376 DW |
62 | struct scrub_ctx *ctx, |
63 | struct xfs_handle *handle, | |
4cca629d | 64 | struct xfs_bulkstat *bstat, |
fa16b376 DW |
65 | void *arg) |
66 | { | |
83d2c80b | 67 | struct action_list alist; |
fa16b376 DW |
68 | struct scrub_inode_ctx *ictx = arg; |
69 | struct ptcounter *icount = ictx->icount; | |
ee310b0c | 70 | xfs_agnumber_t agno; |
fa16b376 | 71 | int fd = -1; |
6c05cc5d | 72 | int error; |
fa16b376 | 73 | |
83d2c80b | 74 | action_list_init(&alist); |
a749451c | 75 | agno = cvt_ino_to_agno(&ctx->mnt, bstat->bs_ino); |
fa16b376 DW |
76 | background_sleep(); |
77 | ||
78 | /* Try to open the inode to pin it. */ | |
79 | if (S_ISREG(bstat->bs_mode)) { | |
59f79e0a | 80 | fd = scrub_open_handle(handle); |
fa16b376 DW |
81 | /* Stale inode means we scan the whole cluster again. */ |
82 | if (fd < 0 && errno == ESTALE) | |
83 | return ESTALE; | |
84 | } | |
85 | ||
86 | /* Scrub the inode. */ | |
d22f2471 DW |
87 | error = scrub_fd(ctx, xfs_scrub_inode_fields, bstat, &alist); |
88 | if (error) | |
ee310b0c DW |
89 | goto out; |
90 | ||
83d2c80b DW |
91 | error = action_list_process_or_defer(ctx, agno, &alist); |
92 | if (error) | |
fa16b376 DW |
93 | goto out; |
94 | ||
95 | /* Scrub all block mappings. */ | |
d22f2471 DW |
96 | error = scrub_fd(ctx, xfs_scrub_data_fork, bstat, &alist); |
97 | if (error) | |
fa16b376 | 98 | goto out; |
d22f2471 DW |
99 | error = scrub_fd(ctx, xfs_scrub_attr_fork, bstat, &alist); |
100 | if (error) | |
fa16b376 | 101 | goto out; |
d22f2471 DW |
102 | error = scrub_fd(ctx, xfs_scrub_cow_fork, bstat, &alist); |
103 | if (error) | |
ee310b0c DW |
104 | goto out; |
105 | ||
83d2c80b DW |
106 | error = action_list_process_or_defer(ctx, agno, &alist); |
107 | if (error) | |
fa16b376 DW |
108 | goto out; |
109 | ||
110 | if (S_ISLNK(bstat->bs_mode)) { | |
111 | /* Check symlink contents. */ | |
d22f2471 | 112 | error = xfs_scrub_symlink(ctx, bstat->bs_ino, bstat->bs_gen, |
991e5a84 | 113 | &alist); |
fa16b376 DW |
114 | } else if (S_ISDIR(bstat->bs_mode)) { |
115 | /* Check the directory entries. */ | |
d22f2471 | 116 | error = scrub_fd(ctx, xfs_scrub_dir, bstat, &alist); |
fa16b376 | 117 | } |
d22f2471 | 118 | if (error) |
fa16b376 DW |
119 | goto out; |
120 | ||
121 | /* Check all the extended attributes. */ | |
d22f2471 DW |
122 | error = scrub_fd(ctx, xfs_scrub_attr, bstat, &alist); |
123 | if (error) | |
fa16b376 DW |
124 | goto out; |
125 | ||
126 | /* Check parent pointers. */ | |
d22f2471 DW |
127 | error = scrub_fd(ctx, xfs_scrub_parent, bstat, &alist); |
128 | if (error) | |
ee310b0c DW |
129 | goto out; |
130 | ||
131 | /* Try to repair the file while it's open. */ | |
83d2c80b DW |
132 | error = action_list_process_or_defer(ctx, agno, &alist); |
133 | if (error) | |
fa16b376 DW |
134 | goto out; |
135 | ||
136 | out: | |
d22f2471 | 137 | if (error) |
df024103 DW |
138 | ictx->aborted = true; |
139 | ||
da3dd6c0 DW |
140 | error = ptcounter_add(icount, 1); |
141 | if (error) { | |
142 | str_liberror(ctx, error, | |
143 | _("incrementing scanned inode counter")); | |
df024103 | 144 | ictx->aborted = true; |
da3dd6c0 | 145 | } |
ed60d210 | 146 | progress_add(1); |
83d2c80b | 147 | action_list_defer(ctx, agno, &alist); |
6c05cc5d | 148 | if (fd >= 0) { |
df024103 DW |
149 | int err2; |
150 | ||
151 | err2 = close(fd); | |
152 | if (err2) { | |
153 | report_close_error(ctx, bstat); | |
154 | ictx->aborted = true; | |
155 | } | |
6c05cc5d | 156 | } |
df024103 DW |
157 | |
158 | if (!error && ictx->aborted) | |
159 | error = ECANCELED; | |
160 | return error; | |
fa16b376 DW |
161 | } |
162 | ||
163 | /* Verify all the inodes in a filesystem. */ | |
df024103 DW |
164 | int |
165 | phase3_func( | |
fa16b376 DW |
166 | struct scrub_ctx *ctx) |
167 | { | |
df024103 | 168 | struct scrub_inode_ctx ictx = { NULL }; |
da3dd6c0 DW |
169 | uint64_t val; |
170 | int err; | |
fa16b376 | 171 | |
da3dd6c0 DW |
172 | err = ptcounter_alloc(scrub_nproc(ctx), &ictx.icount); |
173 | if (err) { | |
174 | str_liberror(ctx, err, _("creating scanned inode counter")); | |
df024103 | 175 | return err; |
fa16b376 DW |
176 | } |
177 | ||
df024103 DW |
178 | err = scrub_scan_all_inodes(ctx, scrub_inode, &ictx); |
179 | if (!err && ictx.aborted) | |
180 | err = ECANCELED; | |
59f79e0a | 181 | if (err) |
fa16b376 | 182 | goto free; |
df024103 | 183 | |
fa16b376 | 184 | xfs_scrub_report_preen_triggers(ctx); |
da3dd6c0 DW |
185 | err = ptcounter_value(ictx.icount, &val); |
186 | if (err) { | |
187 | str_liberror(ctx, err, _("summing scanned inode counter")); | |
df024103 | 188 | return err; |
da3dd6c0 | 189 | } |
df024103 | 190 | |
da3dd6c0 | 191 | ctx->inodes_checked = val; |
fa16b376 DW |
192 | free: |
193 | ptcounter_free(ictx.icount); | |
df024103 | 194 | return err; |
fa16b376 | 195 | } |
ed60d210 | 196 | |
df024103 DW |
197 | /* Estimate how much work we're going to do. */ |
198 | int | |
199 | phase3_estimate( | |
ed60d210 DW |
200 | struct scrub_ctx *ctx, |
201 | uint64_t *items, | |
202 | unsigned int *nr_threads, | |
203 | int *rshift) | |
204 | { | |
205 | *items = ctx->mnt_sv.f_files - ctx->mnt_sv.f_ffree; | |
206 | *nr_threads = scrub_nproc(ctx); | |
207 | *rshift = 0; | |
df024103 DW |
208 | return 0; |
209 | } |