]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/phase5.c
xfs_scrub: remove moveon from main program
[thirdparty/xfsprogs-dev.git] / scrub / phase5.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "xfs.h"
7 #include <stdint.h>
8 #include <dirent.h>
9 #include <sys/types.h>
10 #include <sys/statvfs.h>
11 #ifdef HAVE_LIBATTR
12 # include <attr/attributes.h>
13 #endif
14 #include <linux/fs.h>
15 #include "handle.h"
16 #include "list.h"
17 #include "libfrog/paths.h"
18 #include "libfrog/workqueue.h"
19 #include "xfs_scrub.h"
20 #include "common.h"
21 #include "inodes.h"
22 #include "progress.h"
23 #include "scrub.h"
24 #include "descr.h"
25 #include "unicrash.h"
26
27 /* Phase 5: Check directory connectivity. */
28
29 /*
30 * Warn about problematic bytes in a directory/attribute name. That means
31 * terminal control characters and escape sequences, since that could be used
32 * to do something naughty to the user's computer and/or break scripts. XFS
33 * doesn't consider any byte sequence invalid, so don't flag these as errors.
34 *
35 * Returns 0 for success or -1 for error. This function logs errors.
36 */
37 static int
38 simple_check_name(
39 struct scrub_ctx *ctx,
40 struct descr *dsc,
41 const char *namedescr,
42 const char *name)
43 {
44 const char *p;
45 bool bad = false;
46 char *errname;
47
48 /* Complain about zero length names. */
49 if (*name == '\0' && should_warn_about_name(ctx)) {
50 str_warn(ctx, descr_render(dsc), _("Zero length name found."));
51 return 0;
52 }
53
54 /* control characters */
55 for (p = name; *p; p++) {
56 if ((*p >= 1 && *p <= 31) || *p == 127) {
57 bad = true;
58 break;
59 }
60 }
61
62 if (bad && should_warn_about_name(ctx)) {
63 errname = string_escape(name);
64 if (!errname) {
65 str_errno(ctx, descr_render(dsc));
66 return -1;
67 }
68 str_info(ctx, descr_render(dsc),
69 _("Control character found in %s name \"%s\"."),
70 namedescr, errname);
71 free(errname);
72 }
73
74 return 0;
75 }
76
77 /*
78 * Iterate a directory looking for filenames with problematic
79 * characters.
80 */
81 static int
82 check_dirent_names(
83 struct scrub_ctx *ctx,
84 struct descr *dsc,
85 int *fd,
86 struct xfs_bulkstat *bstat)
87 {
88 struct unicrash *uc = NULL;
89 DIR *dir;
90 struct dirent *dentry;
91 int ret;
92
93 dir = fdopendir(*fd);
94 if (!dir) {
95 str_errno(ctx, descr_render(dsc));
96 return errno;
97 }
98 *fd = -1; /* closedir will close *fd for us */
99
100 ret = unicrash_dir_init(&uc, ctx, bstat);
101 if (ret) {
102 str_liberror(ctx, ret, descr_render(dsc));
103 goto out_unicrash;
104 }
105
106 errno = 0;
107 dentry = readdir(dir);
108 while (dentry) {
109 if (uc)
110 ret = unicrash_check_dir_name(uc, dsc, dentry);
111 else
112 ret = simple_check_name(ctx, dsc, _("directory"),
113 dentry->d_name);
114 if (ret) {
115 str_liberror(ctx, ret, descr_render(dsc));
116 break;
117 }
118 errno = 0;
119 dentry = readdir(dir);
120 }
121 if (errno) {
122 ret = errno;
123 str_liberror(ctx, ret, descr_render(dsc));
124 }
125 unicrash_free(uc);
126
127 out_unicrash:
128 closedir(dir);
129 return ret;
130 }
131
132 #ifdef HAVE_LIBATTR
133 /* Routines to scan all of an inode's xattrs for name problems. */
134 struct attrns_decode {
135 int flags;
136 const char *name;
137 };
138
139 static const struct attrns_decode attr_ns[] = {
140 {0, "user"},
141 {ATTR_ROOT, "system"},
142 {ATTR_SECURE, "secure"},
143 {0, NULL},
144 };
145
146 /*
147 * Check all the xattr names in a particular namespace of a file handle
148 * for Unicode normalization problems or collisions.
149 */
150 static int
151 check_xattr_ns_names(
152 struct scrub_ctx *ctx,
153 struct descr *dsc,
154 struct xfs_handle *handle,
155 struct xfs_bulkstat *bstat,
156 const struct attrns_decode *attr_ns)
157 {
158 struct attrlist_cursor cur;
159 char attrbuf[XFS_XATTR_LIST_MAX];
160 char keybuf[XATTR_NAME_MAX + 1];
161 struct attrlist *attrlist = (struct attrlist *)attrbuf;
162 struct attrlist_ent *ent;
163 struct unicrash *uc = NULL;
164 int i;
165 int error;
166
167 error = unicrash_xattr_init(&uc, ctx, bstat);
168 if (error) {
169 str_liberror(ctx, error, descr_render(dsc));
170 return error;
171 }
172
173 memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
174 memset(&cur, 0, sizeof(cur));
175 memset(keybuf, 0, XATTR_NAME_MAX + 1);
176 error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
177 XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
178 while (!error) {
179 /* Examine the xattrs. */
180 for (i = 0; i < attrlist->al_count; i++) {
181 ent = ATTR_ENTRY(attrlist, i);
182 snprintf(keybuf, XATTR_NAME_MAX, "%s.%s", attr_ns->name,
183 ent->a_name);
184 if (uc)
185 error = unicrash_check_xattr_name(uc, dsc,
186 keybuf);
187 else
188 error = simple_check_name(ctx, dsc,
189 _("extended attribute"),
190 keybuf);
191 if (error) {
192 str_liberror(ctx, error, descr_render(dsc));
193 goto out;
194 }
195 }
196
197 if (!attrlist->al_more)
198 break;
199 error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
200 XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
201 }
202 if (error) {
203 if (errno == ESTALE)
204 errno = 0;
205 if (errno)
206 str_errno(ctx, descr_render(dsc));
207 }
208 out:
209 unicrash_free(uc);
210 return error;
211 }
212
213 /*
214 * Check all the xattr names in all the xattr namespaces for problematic
215 * characters.
216 */
217 static int
218 check_xattr_names(
219 struct scrub_ctx *ctx,
220 struct descr *dsc,
221 struct xfs_handle *handle,
222 struct xfs_bulkstat *bstat)
223 {
224 const struct attrns_decode *ns;
225 int ret;
226
227 for (ns = attr_ns; ns->name; ns++) {
228 ret = check_xattr_ns_names(ctx, dsc, handle, bstat, ns);
229 if (ret)
230 break;
231 }
232 return ret;
233 }
234 #else
235 # define check_xattr_names(c, d, h, b) (0)
236 #endif /* HAVE_LIBATTR */
237
238 static int
239 render_ino_from_handle(
240 struct scrub_ctx *ctx,
241 char *buf,
242 size_t buflen,
243 void *data)
244 {
245 struct xfs_bstat *bstat = data;
246
247 return scrub_render_ino_descr(ctx, buf, buflen, bstat->bs_ino,
248 bstat->bs_gen, NULL);
249 }
250
251 /*
252 * Verify the connectivity of the directory tree.
253 * We know that the kernel's open-by-handle function will try to reconnect
254 * parents of an opened directory, so we'll accept that as sufficient.
255 *
256 * Check for potential Unicode collisions in names.
257 */
258 static int
259 check_inode_names(
260 struct scrub_ctx *ctx,
261 struct xfs_handle *handle,
262 struct xfs_bulkstat *bstat,
263 void *arg)
264 {
265 DEFINE_DESCR(dsc, ctx, render_ino_from_handle);
266 bool *aborted = arg;
267 int fd = -1;
268 int error = 0;
269 int err2;
270
271 descr_set(&dsc, bstat);
272 background_sleep();
273
274 /* Warn about naming problems in xattrs. */
275 if (bstat->bs_xflags & FS_XFLAG_HASATTR) {
276 error = check_xattr_names(ctx, &dsc, handle, bstat);
277 if (error)
278 goto out;
279 }
280
281 /* Open the dir, let the kernel try to reconnect it to the root. */
282 if (S_ISDIR(bstat->bs_mode)) {
283 fd = scrub_open_handle(handle);
284 if (fd < 0) {
285 error = errno;
286 if (error == ESTALE)
287 return ESTALE;
288 str_errno(ctx, descr_render(&dsc));
289 goto out;
290 }
291 }
292
293 /* Warn about naming problems in the directory entries. */
294 if (fd >= 0 && S_ISDIR(bstat->bs_mode)) {
295 error = check_dirent_names(ctx, &dsc, &fd, bstat);
296 if (error)
297 goto out;
298 }
299
300 out:
301 progress_add(1);
302 if (fd >= 0) {
303 err2 = close(fd);
304 if (err2)
305 str_errno(ctx, descr_render(&dsc));
306 if (!error && err2)
307 error = err2;
308 }
309
310 if (error)
311 *aborted = true;
312 if (!error && *aborted)
313 error = ECANCELED;
314
315 return error;
316 }
317
318 #ifndef FS_IOC_GETFSLABEL
319 # define FSLABEL_MAX 256
320 # define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX])
321 #endif /* FS_IOC_GETFSLABEL */
322
323 static int
324 scrub_render_mountpoint(
325 struct scrub_ctx *ctx,
326 char *buf,
327 size_t buflen,
328 void *data)
329 {
330 return snprintf(buf, buflen, _("%s"), ctx->mntpoint);
331 }
332
333 /*
334 * Check the filesystem label for Unicode normalization problems or misleading
335 * sequences.
336 */
337 static int
338 check_fs_label(
339 struct scrub_ctx *ctx)
340 {
341 DEFINE_DESCR(dsc, ctx, scrub_render_mountpoint);
342 char label[FSLABEL_MAX];
343 struct unicrash *uc = NULL;
344 int error;
345
346 error = unicrash_fs_label_init(&uc, ctx);
347 if (error) {
348 str_liberror(ctx, error, descr_render(&dsc));
349 return error;
350 }
351
352 descr_set(&dsc, NULL);
353
354 /* Retrieve label; quietly bail if we don't support that. */
355 error = ioctl(ctx->mnt.fd, FS_IOC_GETFSLABEL, &label);
356 if (error) {
357 if (errno != EOPNOTSUPP && errno != ENOTTY) {
358 error = errno;
359 perror(ctx->mntpoint);
360 }
361 goto out;
362 }
363
364 /* Ignore empty labels. */
365 if (label[0] == 0)
366 goto out;
367
368 /* Otherwise check for weirdness. */
369 if (uc)
370 error = unicrash_check_fs_label(uc, &dsc, label);
371 else
372 error = simple_check_name(ctx, &dsc, _("filesystem label"),
373 label);
374 if (error)
375 str_liberror(ctx, error, descr_render(&dsc));
376 out:
377 unicrash_free(uc);
378 return error;
379 }
380
381 /* Check directory connectivity. */
382 int
383 phase5_func(
384 struct scrub_ctx *ctx)
385 {
386 bool aborted = false;
387 int ret;
388
389 if (ctx->corruptions_found || ctx->unfixable_errors) {
390 str_info(ctx, ctx->mntpoint,
391 _("Filesystem has errors, skipping connectivity checks."));
392 return 0;
393 }
394
395 ret = check_fs_label(ctx);
396 if (ret)
397 return ret;
398
399 ret = scrub_scan_all_inodes(ctx, check_inode_names, &aborted);
400 if (ret)
401 return ret;
402 if (aborted)
403 return ECANCELED;
404
405 xfs_scrub_report_preen_triggers(ctx);
406 return 0;
407 }
408
409 /* Estimate how much work we're going to do. */
410 int
411 phase5_estimate(
412 struct scrub_ctx *ctx,
413 uint64_t *items,
414 unsigned int *nr_threads,
415 int *rshift)
416 {
417 *items = ctx->mnt_sv.f_files - ctx->mnt_sv.f_ffree;
418 *nr_threads = scrub_nproc(ctx);
419 *rshift = 0;
420 return 0;
421 }