]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
50a573a7 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
50a573a7 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
50a573a7 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
50a573a7 DW |
7 | #include <unistd.h> |
8 | #include <sys/types.h> | |
50a573a7 DW |
9 | #include <sys/time.h> |
10 | #include <sys/resource.h> | |
11 | #include <sys/statvfs.h> | |
50a573a7 DW |
12 | #include <fcntl.h> |
13 | #include <dirent.h> | |
14 | #include <stdint.h> | |
50a573a7 | 15 | #include <pthread.h> |
660b5d96 | 16 | #include "libfrog/util.h" |
56598728 | 17 | #include "libfrog/workqueue.h" |
50a573a7 | 18 | #include "input.h" |
42b4c8e8 | 19 | #include "libfrog/paths.h" |
50a573a7 DW |
20 | #include "handle.h" |
21 | #include "bitops.h" | |
b4a09f89 | 22 | #include "libfrog/avl64.h" |
50a573a7 DW |
23 | #include "list.h" |
24 | #include "xfs_scrub.h" | |
25 | #include "common.h" | |
26 | #include "disk.h" | |
fd7d73c0 | 27 | #include "scrub.h" |
ee310b0c | 28 | #include "repair.h" |
fee68490 | 29 | #include "libfrog/fsgeom.h" |
50a573a7 DW |
30 | |
31 | /* Phase 1: Find filesystem geometry (and clean up after) */ | |
32 | ||
33 | /* Shut down the filesystem. */ | |
34 | void | |
35 | xfs_shutdown_fs( | |
36 | struct scrub_ctx *ctx) | |
37 | { | |
38 | int flag; | |
39 | ||
40 | flag = XFS_FSOP_GOING_FLAGS_LOGFLUSH; | |
41 | str_info(ctx, ctx->mntpoint, _("Shutting down filesystem!")); | |
3f9efb2e | 42 | if (ioctl(ctx->mnt.fd, XFS_IOC_GOINGDOWN, &flag)) |
50a573a7 DW |
43 | str_errno(ctx, ctx->mntpoint); |
44 | } | |
45 | ||
46 | /* Clean up the XFS-specific state data. */ | |
35b65bcf DW |
47 | int |
48 | scrub_cleanup( | |
50a573a7 DW |
49 | struct scrub_ctx *ctx) |
50 | { | |
6c05cc5d DW |
51 | int error; |
52 | ||
83d2c80b | 53 | action_lists_free(&ctx->action_lists); |
50a573a7 DW |
54 | if (ctx->fshandle) |
55 | free_handle(ctx->fshandle, ctx->fshandle_len); | |
56 | if (ctx->rtdev) | |
57 | disk_close(ctx->rtdev); | |
58 | if (ctx->logdev) | |
59 | disk_close(ctx->logdev); | |
60 | if (ctx->datadev) | |
61 | disk_close(ctx->datadev); | |
62 | fshandle_destroy(); | |
3f9efb2e DW |
63 | error = xfd_close(&ctx->mnt); |
64 | if (error) | |
65 | str_liberror(ctx, error, _("closing mountpoint fd")); | |
50a573a7 DW |
66 | fs_table_destroy(); |
67 | ||
35b65bcf | 68 | return error; |
50a573a7 DW |
69 | } |
70 | ||
71 | /* | |
72 | * Bind to the mountpoint, read the XFS geometry, bind to the block devices. | |
35b65bcf | 73 | * Anything we've already built will be cleaned up by scrub_cleanup. |
50a573a7 | 74 | */ |
35b65bcf DW |
75 | int |
76 | phase1_func( | |
50a573a7 DW |
77 | struct scrub_ctx *ctx) |
78 | { | |
50a573a7 DW |
79 | int error; |
80 | ||
81 | /* | |
82 | * Open the directory with O_NOATIME. For mountpoints owned | |
83 | * by root, this should be sufficient to ensure that we have | |
84 | * CAP_SYS_ADMIN, which we probably need to do anything fancy | |
85 | * with the (XFS driver) kernel. | |
86 | */ | |
248af7cb DW |
87 | error = xfd_open(&ctx->mnt, ctx->mntpoint, |
88 | O_RDONLY | O_NOATIME | O_DIRECTORY); | |
89 | if (error) { | |
90 | if (error == EPERM) | |
e458f3f1 | 91 | str_error(ctx, ctx->mntpoint, |
50a573a7 | 92 | _("Must be root to run scrub.")); |
248af7cb DW |
93 | else if (error == ENOTTY) |
94 | str_error(ctx, ctx->mntpoint, | |
95 | _("Not an XFS filesystem.")); | |
50a573a7 | 96 | else |
248af7cb | 97 | str_liberror(ctx, error, ctx->mntpoint); |
35b65bcf | 98 | return error; |
50a573a7 DW |
99 | } |
100 | ||
3f9efb2e | 101 | error = fstat(ctx->mnt.fd, &ctx->mnt_sb); |
50a573a7 DW |
102 | if (error) { |
103 | str_errno(ctx, ctx->mntpoint); | |
35b65bcf | 104 | return error; |
50a573a7 | 105 | } |
3f9efb2e | 106 | error = fstatvfs(ctx->mnt.fd, &ctx->mnt_sv); |
50a573a7 DW |
107 | if (error) { |
108 | str_errno(ctx, ctx->mntpoint); | |
35b65bcf | 109 | return error; |
50a573a7 | 110 | } |
3f9efb2e | 111 | error = fstatfs(ctx->mnt.fd, &ctx->mnt_sf); |
50a573a7 DW |
112 | if (error) { |
113 | str_errno(ctx, ctx->mntpoint); | |
35b65bcf | 114 | return error; |
50a573a7 DW |
115 | } |
116 | ||
50a573a7 DW |
117 | /* |
118 | * Flush everything out to disk before we start checking. | |
119 | * This seems to reduce the incidence of stale file handle | |
120 | * errors when we open things by handle. | |
121 | */ | |
3f9efb2e | 122 | error = syncfs(ctx->mnt.fd); |
50a573a7 DW |
123 | if (error) { |
124 | str_errno(ctx, ctx->mntpoint); | |
35b65bcf | 125 | return error; |
50a573a7 DW |
126 | } |
127 | ||
83d2c80b DW |
128 | error = action_lists_alloc(ctx->mnt.fsgeom.agcount, |
129 | &ctx->action_lists); | |
130 | if (error) { | |
131 | str_liberror(ctx, error, _("allocating action lists")); | |
35b65bcf | 132 | return error; |
ee310b0c DW |
133 | } |
134 | ||
50a573a7 DW |
135 | error = path_to_fshandle(ctx->mntpoint, &ctx->fshandle, |
136 | &ctx->fshandle_len); | |
137 | if (error) { | |
138 | str_errno(ctx, _("getting fshandle")); | |
35b65bcf | 139 | return error; |
50a573a7 DW |
140 | } |
141 | ||
fd7d73c0 DW |
142 | /* Do we have kernel-assisted metadata scrubbing? */ |
143 | if (!xfs_can_scrub_fs_metadata(ctx) || !xfs_can_scrub_inode(ctx) || | |
144 | !xfs_can_scrub_bmap(ctx) || !xfs_can_scrub_dir(ctx) || | |
145 | !xfs_can_scrub_attr(ctx) || !xfs_can_scrub_symlink(ctx) || | |
146 | !xfs_can_scrub_parent(ctx)) { | |
e458f3f1 | 147 | str_error(ctx, ctx->mntpoint, |
ffdd2726 | 148 | _("Kernel metadata scrubbing facility is not available.")); |
35b65bcf | 149 | return ECANCELED; |
fd7d73c0 DW |
150 | } |
151 | ||
19852474 DW |
152 | /* Do we need kernel-assisted metadata repair? */ |
153 | if (ctx->mode != SCRUB_MODE_DRY_RUN && !xfs_can_repair(ctx)) { | |
e458f3f1 | 154 | str_error(ctx, ctx->mntpoint, |
ffdd2726 | 155 | _("Kernel metadata repair facility is not available. Use -n to scrub.")); |
35b65bcf | 156 | return ECANCELED; |
19852474 DW |
157 | } |
158 | ||
50a573a7 | 159 | /* Did we find the log and rt devices, if they're present? */ |
3f9efb2e | 160 | if (ctx->mnt.fsgeom.logstart == 0 && ctx->fsinfo.fs_log == NULL) { |
e458f3f1 | 161 | str_error(ctx, ctx->mntpoint, |
50a573a7 | 162 | _("Unable to find log device path.")); |
35b65bcf | 163 | return ECANCELED; |
50a573a7 | 164 | } |
3f9efb2e | 165 | if (ctx->mnt.fsgeom.rtblocks && ctx->fsinfo.fs_rt == NULL) { |
e458f3f1 | 166 | str_error(ctx, ctx->mntpoint, |
50a573a7 | 167 | _("Unable to find realtime device path.")); |
35b65bcf | 168 | return ECANCELED; |
50a573a7 DW |
169 | } |
170 | ||
171 | /* Open the raw devices. */ | |
172 | ctx->datadev = disk_open(ctx->fsinfo.fs_name); | |
173 | if (error) { | |
174 | str_errno(ctx, ctx->fsinfo.fs_name); | |
35b65bcf | 175 | return error; |
50a573a7 DW |
176 | } |
177 | ||
96626446 DW |
178 | ctx->nr_io_threads = disk_heads(ctx->datadev); |
179 | if (verbose) { | |
180 | fprintf(stdout, _("%s: using %d threads to scrub.\n"), | |
181 | ctx->mntpoint, scrub_nproc(ctx)); | |
182 | fflush(stdout); | |
183 | } | |
184 | ||
50a573a7 DW |
185 | if (ctx->fsinfo.fs_log) { |
186 | ctx->logdev = disk_open(ctx->fsinfo.fs_log); | |
187 | if (error) { | |
188 | str_errno(ctx, ctx->fsinfo.fs_name); | |
35b65bcf | 189 | return error; |
50a573a7 DW |
190 | } |
191 | } | |
192 | if (ctx->fsinfo.fs_rt) { | |
193 | ctx->rtdev = disk_open(ctx->fsinfo.fs_rt); | |
194 | if (error) { | |
195 | str_errno(ctx, ctx->fsinfo.fs_name); | |
35b65bcf | 196 | return error; |
50a573a7 DW |
197 | } |
198 | } | |
199 | ||
200 | /* | |
201 | * Everything's set up, which means any failures recorded after | |
202 | * this point are most probably corruption errors (as opposed to | |
203 | * purely setup errors). | |
204 | */ | |
7c309151 | 205 | log_info(ctx, _("Invoking online scrub."), ctx); |
c767c5ae | 206 | ctx->scrub_setup_succeeded = true; |
35b65bcf DW |
207 | return 0; |
208 | } |