]>
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" |
fa16b376 DW |
11 | #include "path.h" |
12 | #include "workqueue.h" | |
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 | */ | |
28 | static bool | |
29 | xfs_scrub_fd( | |
30 | struct scrub_ctx *ctx, | |
31 | bool (*fn)(struct scrub_ctx *, uint64_t, | |
ee310b0c DW |
32 | uint32_t, int, struct xfs_action_list *), |
33 | struct xfs_bstat *bs, | |
34 | struct xfs_action_list *alist) | |
fa16b376 | 35 | { |
3f9efb2e | 36 | return fn(ctx, bs->bs_ino, bs->bs_gen, ctx->mnt.fd, alist); |
fa16b376 DW |
37 | } |
38 | ||
39 | struct scrub_inode_ctx { | |
40 | struct ptcounter *icount; | |
41 | bool moveon; | |
42 | }; | |
43 | ||
6c05cc5d DW |
44 | /* Report a filesystem error that the vfs fed us on close. */ |
45 | static void | |
46 | xfs_scrub_inode_vfs_error( | |
47 | struct scrub_ctx *ctx, | |
48 | struct xfs_bstat *bstat) | |
49 | { | |
50 | char descr[DESCR_BUFSZ]; | |
51 | xfs_agnumber_t agno; | |
52 | xfs_agino_t agino; | |
53 | int old_errno = errno; | |
54 | ||
55 | agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog)); | |
56 | agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog)); | |
57 | snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"), | |
58 | (uint64_t)bstat->bs_ino, agno, agino); | |
59 | errno = old_errno; | |
60 | str_errno(ctx, descr); | |
61 | } | |
62 | ||
fa16b376 DW |
63 | /* Verify the contents, xattrs, and extent maps of an inode. */ |
64 | static int | |
65 | xfs_scrub_inode( | |
66 | struct scrub_ctx *ctx, | |
67 | struct xfs_handle *handle, | |
68 | struct xfs_bstat *bstat, | |
69 | void *arg) | |
70 | { | |
ee310b0c | 71 | struct xfs_action_list alist; |
fa16b376 DW |
72 | struct scrub_inode_ctx *ictx = arg; |
73 | struct ptcounter *icount = ictx->icount; | |
ee310b0c | 74 | xfs_agnumber_t agno; |
fa16b376 DW |
75 | bool moveon = true; |
76 | int fd = -1; | |
6c05cc5d | 77 | int error; |
fa16b376 | 78 | |
ee310b0c DW |
79 | xfs_action_list_init(&alist); |
80 | agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog)); | |
fa16b376 DW |
81 | background_sleep(); |
82 | ||
83 | /* Try to open the inode to pin it. */ | |
84 | if (S_ISREG(bstat->bs_mode)) { | |
85 | fd = xfs_open_handle(handle); | |
86 | /* Stale inode means we scan the whole cluster again. */ | |
87 | if (fd < 0 && errno == ESTALE) | |
88 | return ESTALE; | |
89 | } | |
90 | ||
91 | /* Scrub the inode. */ | |
ee310b0c DW |
92 | moveon = xfs_scrub_fd(ctx, xfs_scrub_inode_fields, bstat, &alist); |
93 | if (!moveon) | |
94 | goto out; | |
95 | ||
96 | moveon = xfs_action_list_process_or_defer(ctx, agno, &alist); | |
fa16b376 DW |
97 | if (!moveon) |
98 | goto out; | |
99 | ||
100 | /* Scrub all block mappings. */ | |
ee310b0c | 101 | moveon = xfs_scrub_fd(ctx, xfs_scrub_data_fork, bstat, &alist); |
fa16b376 DW |
102 | if (!moveon) |
103 | goto out; | |
ee310b0c | 104 | moveon = xfs_scrub_fd(ctx, xfs_scrub_attr_fork, bstat, &alist); |
fa16b376 DW |
105 | if (!moveon) |
106 | goto out; | |
ee310b0c DW |
107 | moveon = xfs_scrub_fd(ctx, xfs_scrub_cow_fork, bstat, &alist); |
108 | if (!moveon) | |
109 | goto out; | |
110 | ||
111 | moveon = xfs_action_list_process_or_defer(ctx, agno, &alist); | |
fa16b376 DW |
112 | if (!moveon) |
113 | goto out; | |
114 | ||
115 | if (S_ISLNK(bstat->bs_mode)) { | |
116 | /* Check symlink contents. */ | |
117 | moveon = xfs_scrub_symlink(ctx, bstat->bs_ino, | |
3f9efb2e | 118 | bstat->bs_gen, ctx->mnt.fd, &alist); |
fa16b376 DW |
119 | } else if (S_ISDIR(bstat->bs_mode)) { |
120 | /* Check the directory entries. */ | |
ee310b0c | 121 | moveon = xfs_scrub_fd(ctx, xfs_scrub_dir, bstat, &alist); |
fa16b376 DW |
122 | } |
123 | if (!moveon) | |
124 | goto out; | |
125 | ||
126 | /* Check all the extended attributes. */ | |
ee310b0c | 127 | moveon = xfs_scrub_fd(ctx, xfs_scrub_attr, bstat, &alist); |
fa16b376 DW |
128 | if (!moveon) |
129 | goto out; | |
130 | ||
131 | /* Check parent pointers. */ | |
ee310b0c DW |
132 | moveon = xfs_scrub_fd(ctx, xfs_scrub_parent, bstat, &alist); |
133 | if (!moveon) | |
134 | goto out; | |
135 | ||
136 | /* Try to repair the file while it's open. */ | |
137 | moveon = xfs_action_list_process_or_defer(ctx, agno, &alist); | |
fa16b376 DW |
138 | if (!moveon) |
139 | goto out; | |
140 | ||
141 | out: | |
142 | ptcounter_add(icount, 1); | |
ed60d210 | 143 | progress_add(1); |
ee310b0c | 144 | xfs_action_list_defer(ctx, agno, &alist); |
6c05cc5d DW |
145 | if (fd >= 0) { |
146 | error = close(fd); | |
147 | if (error) | |
148 | xfs_scrub_inode_vfs_error(ctx, bstat); | |
149 | } | |
fa16b376 DW |
150 | if (!moveon) |
151 | ictx->moveon = false; | |
152 | return ictx->moveon ? 0 : XFS_ITERATE_INODES_ABORT; | |
153 | } | |
154 | ||
155 | /* Verify all the inodes in a filesystem. */ | |
156 | bool | |
157 | xfs_scan_inodes( | |
158 | struct scrub_ctx *ctx) | |
159 | { | |
160 | struct scrub_inode_ctx ictx; | |
161 | bool ret; | |
162 | ||
163 | ictx.moveon = true; | |
164 | ictx.icount = ptcounter_init(scrub_nproc(ctx)); | |
165 | if (!ictx.icount) { | |
82377bde | 166 | str_info(ctx, ctx->mntpoint, _("Could not create counter.")); |
fa16b376 DW |
167 | return false; |
168 | } | |
169 | ||
170 | ret = xfs_scan_all_inodes(ctx, xfs_scrub_inode, &ictx); | |
171 | if (!ret) | |
172 | ictx.moveon = false; | |
173 | if (!ictx.moveon) | |
174 | goto free; | |
175 | xfs_scrub_report_preen_triggers(ctx); | |
176 | ctx->inodes_checked = ptcounter_value(ictx.icount); | |
177 | ||
178 | free: | |
179 | ptcounter_free(ictx.icount); | |
180 | return ictx.moveon; | |
181 | } | |
ed60d210 DW |
182 | |
183 | /* Estimate how much work we're going to do. */ | |
184 | bool | |
185 | xfs_estimate_inodes_work( | |
186 | struct scrub_ctx *ctx, | |
187 | uint64_t *items, | |
188 | unsigned int *nr_threads, | |
189 | int *rshift) | |
190 | { | |
191 | *items = ctx->mnt_sv.f_files - ctx->mnt_sv.f_ffree; | |
192 | *nr_threads = scrub_nproc(ctx); | |
193 | *rshift = 0; | |
194 | return true; | |
195 | } |