]>
Commit | Line | Data |
---|---|---|
50a573a7 DW |
1 | /* |
2 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
3 | * | |
4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it would be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write the Free Software Foundation, | |
18 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | */ | |
20 | #include <stdio.h> | |
21 | #include <mntent.h> | |
22 | #include <unistd.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/stat.h> | |
25 | #include <sys/time.h> | |
26 | #include <sys/resource.h> | |
27 | #include <sys/statvfs.h> | |
28 | #include <sys/vfs.h> | |
29 | #include <fcntl.h> | |
30 | #include <dirent.h> | |
31 | #include <stdint.h> | |
32 | #include <stdbool.h> | |
33 | #include <pthread.h> | |
34 | #include <errno.h> | |
35 | #include <linux/fs.h> | |
36 | #include "libfrog.h" | |
37 | #include "workqueue.h" | |
38 | #include "input.h" | |
39 | #include "path.h" | |
40 | #include "handle.h" | |
41 | #include "bitops.h" | |
42 | #include "xfs_arch.h" | |
43 | #include "xfs_format.h" | |
44 | #include "avl64.h" | |
45 | #include "list.h" | |
46 | #include "xfs_scrub.h" | |
47 | #include "common.h" | |
48 | #include "disk.h" | |
fd7d73c0 | 49 | #include "scrub.h" |
50a573a7 DW |
50 | |
51 | /* Phase 1: Find filesystem geometry (and clean up after) */ | |
52 | ||
53 | /* Shut down the filesystem. */ | |
54 | void | |
55 | xfs_shutdown_fs( | |
56 | struct scrub_ctx *ctx) | |
57 | { | |
58 | int flag; | |
59 | ||
60 | flag = XFS_FSOP_GOING_FLAGS_LOGFLUSH; | |
61 | str_info(ctx, ctx->mntpoint, _("Shutting down filesystem!")); | |
62 | if (ioctl(ctx->mnt_fd, XFS_IOC_GOINGDOWN, &flag)) | |
63 | str_errno(ctx, ctx->mntpoint); | |
64 | } | |
65 | ||
66 | /* Clean up the XFS-specific state data. */ | |
67 | bool | |
68 | xfs_cleanup_fs( | |
69 | struct scrub_ctx *ctx) | |
70 | { | |
71 | if (ctx->fshandle) | |
72 | free_handle(ctx->fshandle, ctx->fshandle_len); | |
73 | if (ctx->rtdev) | |
74 | disk_close(ctx->rtdev); | |
75 | if (ctx->logdev) | |
76 | disk_close(ctx->logdev); | |
77 | if (ctx->datadev) | |
78 | disk_close(ctx->datadev); | |
79 | fshandle_destroy(); | |
80 | close(ctx->mnt_fd); | |
81 | fs_table_destroy(); | |
82 | ||
83 | return true; | |
84 | } | |
85 | ||
86 | /* | |
87 | * Bind to the mountpoint, read the XFS geometry, bind to the block devices. | |
88 | * Anything we've already built will be cleaned up by xfs_cleanup_fs. | |
89 | */ | |
90 | bool | |
91 | xfs_setup_fs( | |
92 | struct scrub_ctx *ctx) | |
93 | { | |
94 | struct fs_path *fsp; | |
95 | int error; | |
96 | ||
97 | /* | |
98 | * Open the directory with O_NOATIME. For mountpoints owned | |
99 | * by root, this should be sufficient to ensure that we have | |
100 | * CAP_SYS_ADMIN, which we probably need to do anything fancy | |
101 | * with the (XFS driver) kernel. | |
102 | */ | |
103 | ctx->mnt_fd = open(ctx->mntpoint, O_RDONLY | O_NOATIME | O_DIRECTORY); | |
104 | if (ctx->mnt_fd < 0) { | |
105 | if (errno == EPERM) | |
106 | str_info(ctx, ctx->mntpoint, | |
107 | _("Must be root to run scrub.")); | |
108 | else | |
109 | str_errno(ctx, ctx->mntpoint); | |
110 | return false; | |
111 | } | |
112 | ||
113 | error = fstat(ctx->mnt_fd, &ctx->mnt_sb); | |
114 | if (error) { | |
115 | str_errno(ctx, ctx->mntpoint); | |
116 | return false; | |
117 | } | |
118 | error = fstatvfs(ctx->mnt_fd, &ctx->mnt_sv); | |
119 | if (error) { | |
120 | str_errno(ctx, ctx->mntpoint); | |
121 | return false; | |
122 | } | |
123 | error = fstatfs(ctx->mnt_fd, &ctx->mnt_sf); | |
124 | if (error) { | |
125 | str_errno(ctx, ctx->mntpoint); | |
126 | return false; | |
127 | } | |
128 | ||
129 | ctx->nr_io_threads = nproc; | |
130 | if (verbose) { | |
131 | fprintf(stdout, _("%s: using %d threads to scrub.\n"), | |
132 | ctx->mntpoint, scrub_nproc(ctx)); | |
133 | fflush(stdout); | |
134 | } | |
135 | ||
136 | if (!platform_test_xfs_fd(ctx->mnt_fd)) { | |
82377bde | 137 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
138 | _("Does not appear to be an XFS filesystem!")); |
139 | return false; | |
140 | } | |
141 | ||
142 | /* | |
143 | * Flush everything out to disk before we start checking. | |
144 | * This seems to reduce the incidence of stale file handle | |
145 | * errors when we open things by handle. | |
146 | */ | |
147 | error = syncfs(ctx->mnt_fd); | |
148 | if (error) { | |
149 | str_errno(ctx, ctx->mntpoint); | |
150 | return false; | |
151 | } | |
152 | ||
153 | /* Retrieve XFS geometry. */ | |
154 | error = ioctl(ctx->mnt_fd, XFS_IOC_FSGEOMETRY, &ctx->geo); | |
155 | if (error) { | |
156 | str_errno(ctx, ctx->mntpoint); | |
157 | return false; | |
158 | } | |
159 | ||
160 | ctx->agblklog = log2_roundup(ctx->geo.agblocks); | |
161 | ctx->blocklog = highbit32(ctx->geo.blocksize); | |
162 | ctx->inodelog = highbit32(ctx->geo.inodesize); | |
163 | ctx->inopblog = ctx->blocklog - ctx->inodelog; | |
164 | ||
165 | error = path_to_fshandle(ctx->mntpoint, &ctx->fshandle, | |
166 | &ctx->fshandle_len); | |
167 | if (error) { | |
168 | str_errno(ctx, _("getting fshandle")); | |
169 | return false; | |
170 | } | |
171 | ||
fd7d73c0 DW |
172 | /* Do we have kernel-assisted metadata scrubbing? */ |
173 | if (!xfs_can_scrub_fs_metadata(ctx) || !xfs_can_scrub_inode(ctx) || | |
174 | !xfs_can_scrub_bmap(ctx) || !xfs_can_scrub_dir(ctx) || | |
175 | !xfs_can_scrub_attr(ctx) || !xfs_can_scrub_symlink(ctx) || | |
176 | !xfs_can_scrub_parent(ctx)) { | |
1df93c8d | 177 | str_info(ctx, ctx->mntpoint, |
ffdd2726 | 178 | _("Kernel metadata scrubbing facility is not available.")); |
fd7d73c0 DW |
179 | return false; |
180 | } | |
181 | ||
19852474 DW |
182 | /* Do we need kernel-assisted metadata repair? */ |
183 | if (ctx->mode != SCRUB_MODE_DRY_RUN && !xfs_can_repair(ctx)) { | |
1df93c8d | 184 | str_info(ctx, ctx->mntpoint, |
ffdd2726 | 185 | _("Kernel metadata repair facility is not available. Use -n to scrub.")); |
19852474 DW |
186 | return false; |
187 | } | |
188 | ||
50a573a7 DW |
189 | /* Go find the XFS devices if we have a usable fsmap. */ |
190 | fs_table_initialise(0, NULL, 0, NULL); | |
191 | errno = 0; | |
192 | fsp = fs_table_lookup(ctx->mntpoint, FS_MOUNT_POINT); | |
193 | if (!fsp) { | |
82377bde | 194 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
195 | _("Unable to find XFS information.")); |
196 | return false; | |
197 | } | |
198 | memcpy(&ctx->fsinfo, fsp, sizeof(struct fs_path)); | |
199 | ||
200 | /* Did we find the log and rt devices, if they're present? */ | |
201 | if (ctx->geo.logstart == 0 && ctx->fsinfo.fs_log == NULL) { | |
82377bde | 202 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
203 | _("Unable to find log device path.")); |
204 | return false; | |
205 | } | |
206 | if (ctx->geo.rtblocks && ctx->fsinfo.fs_rt == NULL) { | |
82377bde | 207 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
208 | _("Unable to find realtime device path.")); |
209 | return false; | |
210 | } | |
211 | ||
212 | /* Open the raw devices. */ | |
213 | ctx->datadev = disk_open(ctx->fsinfo.fs_name); | |
214 | if (error) { | |
215 | str_errno(ctx, ctx->fsinfo.fs_name); | |
216 | return false; | |
217 | } | |
218 | ||
219 | if (ctx->fsinfo.fs_log) { | |
220 | ctx->logdev = disk_open(ctx->fsinfo.fs_log); | |
221 | if (error) { | |
222 | str_errno(ctx, ctx->fsinfo.fs_name); | |
223 | return false; | |
224 | } | |
225 | } | |
226 | if (ctx->fsinfo.fs_rt) { | |
227 | ctx->rtdev = disk_open(ctx->fsinfo.fs_rt); | |
228 | if (error) { | |
229 | str_errno(ctx, ctx->fsinfo.fs_name); | |
230 | return false; | |
231 | } | |
232 | } | |
233 | ||
234 | /* | |
235 | * Everything's set up, which means any failures recorded after | |
236 | * this point are most probably corruption errors (as opposed to | |
237 | * purely setup errors). | |
238 | */ | |
239 | ctx->need_repair = true; | |
240 | return true; | |
241 | } |