]>
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> |
50a573a7 DW |
16 | #include "libfrog.h" |
17 | #include "workqueue.h" | |
18 | #include "input.h" | |
19 | #include "path.h" | |
20 | #include "handle.h" | |
21 | #include "bitops.h" | |
50a573a7 DW |
22 | #include "avl64.h" |
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" |
50a573a7 DW |
29 | |
30 | /* Phase 1: Find filesystem geometry (and clean up after) */ | |
31 | ||
32 | /* Shut down the filesystem. */ | |
33 | void | |
34 | xfs_shutdown_fs( | |
35 | struct scrub_ctx *ctx) | |
36 | { | |
37 | int flag; | |
38 | ||
39 | flag = XFS_FSOP_GOING_FLAGS_LOGFLUSH; | |
40 | str_info(ctx, ctx->mntpoint, _("Shutting down filesystem!")); | |
41 | if (ioctl(ctx->mnt_fd, XFS_IOC_GOINGDOWN, &flag)) | |
42 | str_errno(ctx, ctx->mntpoint); | |
43 | } | |
44 | ||
45 | /* Clean up the XFS-specific state data. */ | |
46 | bool | |
47 | xfs_cleanup_fs( | |
48 | struct scrub_ctx *ctx) | |
49 | { | |
6c05cc5d DW |
50 | int error; |
51 | ||
ee310b0c | 52 | xfs_action_lists_free(&ctx->action_lists); |
50a573a7 DW |
53 | if (ctx->fshandle) |
54 | free_handle(ctx->fshandle, ctx->fshandle_len); | |
55 | if (ctx->rtdev) | |
56 | disk_close(ctx->rtdev); | |
57 | if (ctx->logdev) | |
58 | disk_close(ctx->logdev); | |
59 | if (ctx->datadev) | |
60 | disk_close(ctx->datadev); | |
61 | fshandle_destroy(); | |
6c05cc5d DW |
62 | error = close(ctx->mnt_fd); |
63 | if (error) | |
64 | str_errno(ctx, _("closing mountpoint fd")); | |
50a573a7 DW |
65 | fs_table_destroy(); |
66 | ||
67 | return true; | |
68 | } | |
69 | ||
70 | /* | |
71 | * Bind to the mountpoint, read the XFS geometry, bind to the block devices. | |
72 | * Anything we've already built will be cleaned up by xfs_cleanup_fs. | |
73 | */ | |
74 | bool | |
75 | xfs_setup_fs( | |
76 | struct scrub_ctx *ctx) | |
77 | { | |
50a573a7 DW |
78 | int error; |
79 | ||
80 | /* | |
81 | * Open the directory with O_NOATIME. For mountpoints owned | |
82 | * by root, this should be sufficient to ensure that we have | |
83 | * CAP_SYS_ADMIN, which we probably need to do anything fancy | |
84 | * with the (XFS driver) kernel. | |
85 | */ | |
86 | ctx->mnt_fd = open(ctx->mntpoint, O_RDONLY | O_NOATIME | O_DIRECTORY); | |
87 | if (ctx->mnt_fd < 0) { | |
88 | if (errno == EPERM) | |
89 | str_info(ctx, ctx->mntpoint, | |
90 | _("Must be root to run scrub.")); | |
91 | else | |
92 | str_errno(ctx, ctx->mntpoint); | |
93 | return false; | |
94 | } | |
95 | ||
96 | error = fstat(ctx->mnt_fd, &ctx->mnt_sb); | |
97 | if (error) { | |
98 | str_errno(ctx, ctx->mntpoint); | |
99 | return false; | |
100 | } | |
101 | error = fstatvfs(ctx->mnt_fd, &ctx->mnt_sv); | |
102 | if (error) { | |
103 | str_errno(ctx, ctx->mntpoint); | |
104 | return false; | |
105 | } | |
106 | error = fstatfs(ctx->mnt_fd, &ctx->mnt_sf); | |
107 | if (error) { | |
108 | str_errno(ctx, ctx->mntpoint); | |
109 | return false; | |
110 | } | |
111 | ||
112 | ctx->nr_io_threads = nproc; | |
113 | if (verbose) { | |
114 | fprintf(stdout, _("%s: using %d threads to scrub.\n"), | |
115 | ctx->mntpoint, scrub_nproc(ctx)); | |
116 | fflush(stdout); | |
117 | } | |
118 | ||
119 | if (!platform_test_xfs_fd(ctx->mnt_fd)) { | |
82377bde | 120 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
121 | _("Does not appear to be an XFS filesystem!")); |
122 | return false; | |
123 | } | |
124 | ||
125 | /* | |
126 | * Flush everything out to disk before we start checking. | |
127 | * This seems to reduce the incidence of stale file handle | |
128 | * errors when we open things by handle. | |
129 | */ | |
130 | error = syncfs(ctx->mnt_fd); | |
131 | if (error) { | |
132 | str_errno(ctx, ctx->mntpoint); | |
133 | return false; | |
134 | } | |
135 | ||
136 | /* Retrieve XFS geometry. */ | |
137 | error = ioctl(ctx->mnt_fd, XFS_IOC_FSGEOMETRY, &ctx->geo); | |
138 | if (error) { | |
139 | str_errno(ctx, ctx->mntpoint); | |
140 | return false; | |
141 | } | |
142 | ||
ee310b0c DW |
143 | if (!xfs_action_lists_alloc(ctx->geo.agcount, &ctx->action_lists)) { |
144 | str_error(ctx, ctx->mntpoint, _("Not enough memory.")); | |
145 | return false; | |
146 | } | |
147 | ||
50a573a7 DW |
148 | ctx->agblklog = log2_roundup(ctx->geo.agblocks); |
149 | ctx->blocklog = highbit32(ctx->geo.blocksize); | |
150 | ctx->inodelog = highbit32(ctx->geo.inodesize); | |
151 | ctx->inopblog = ctx->blocklog - ctx->inodelog; | |
152 | ||
153 | error = path_to_fshandle(ctx->mntpoint, &ctx->fshandle, | |
154 | &ctx->fshandle_len); | |
155 | if (error) { | |
156 | str_errno(ctx, _("getting fshandle")); | |
157 | return false; | |
158 | } | |
159 | ||
fd7d73c0 DW |
160 | /* Do we have kernel-assisted metadata scrubbing? */ |
161 | if (!xfs_can_scrub_fs_metadata(ctx) || !xfs_can_scrub_inode(ctx) || | |
162 | !xfs_can_scrub_bmap(ctx) || !xfs_can_scrub_dir(ctx) || | |
163 | !xfs_can_scrub_attr(ctx) || !xfs_can_scrub_symlink(ctx) || | |
164 | !xfs_can_scrub_parent(ctx)) { | |
1df93c8d | 165 | str_info(ctx, ctx->mntpoint, |
ffdd2726 | 166 | _("Kernel metadata scrubbing facility is not available.")); |
fd7d73c0 DW |
167 | return false; |
168 | } | |
169 | ||
19852474 DW |
170 | /* Do we need kernel-assisted metadata repair? */ |
171 | if (ctx->mode != SCRUB_MODE_DRY_RUN && !xfs_can_repair(ctx)) { | |
1df93c8d | 172 | str_info(ctx, ctx->mntpoint, |
ffdd2726 | 173 | _("Kernel metadata repair facility is not available. Use -n to scrub.")); |
19852474 DW |
174 | return false; |
175 | } | |
176 | ||
50a573a7 DW |
177 | /* Did we find the log and rt devices, if they're present? */ |
178 | if (ctx->geo.logstart == 0 && ctx->fsinfo.fs_log == NULL) { | |
82377bde | 179 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
180 | _("Unable to find log device path.")); |
181 | return false; | |
182 | } | |
183 | if (ctx->geo.rtblocks && ctx->fsinfo.fs_rt == NULL) { | |
82377bde | 184 | str_info(ctx, ctx->mntpoint, |
50a573a7 DW |
185 | _("Unable to find realtime device path.")); |
186 | return false; | |
187 | } | |
188 | ||
189 | /* Open the raw devices. */ | |
190 | ctx->datadev = disk_open(ctx->fsinfo.fs_name); | |
191 | if (error) { | |
192 | str_errno(ctx, ctx->fsinfo.fs_name); | |
193 | return false; | |
194 | } | |
195 | ||
196 | if (ctx->fsinfo.fs_log) { | |
197 | ctx->logdev = disk_open(ctx->fsinfo.fs_log); | |
198 | if (error) { | |
199 | str_errno(ctx, ctx->fsinfo.fs_name); | |
200 | return false; | |
201 | } | |
202 | } | |
203 | if (ctx->fsinfo.fs_rt) { | |
204 | ctx->rtdev = disk_open(ctx->fsinfo.fs_rt); | |
205 | if (error) { | |
206 | str_errno(ctx, ctx->fsinfo.fs_name); | |
207 | return false; | |
208 | } | |
209 | } | |
210 | ||
211 | /* | |
212 | * Everything's set up, which means any failures recorded after | |
213 | * this point are most probably corruption errors (as opposed to | |
214 | * purely setup errors). | |
215 | */ | |
7c309151 | 216 | log_info(ctx, _("Invoking online scrub."), ctx); |
c767c5ae | 217 | ctx->scrub_setup_succeeded = true; |
50a573a7 DW |
218 | return true; |
219 | } |