]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/phase5.c
xfs_scrub: remove moveon from main program
[thirdparty/xfsprogs-dev.git] / scrub / phase5.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
c4892e76
DW
2/*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
c4892e76 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
c4892e76 5 */
a440f877 6#include "xfs.h"
c4892e76 7#include <stdint.h>
396cd022 8#include <dirent.h>
c4892e76 9#include <sys/types.h>
c4892e76 10#include <sys/statvfs.h>
396cd022
DW
11#ifdef HAVE_LIBATTR
12# include <attr/attributes.h>
13#endif
3baa69cd 14#include <linux/fs.h>
396cd022 15#include "handle.h"
19852474 16#include "list.h"
42b4c8e8 17#include "libfrog/paths.h"
56598728 18#include "libfrog/workqueue.h"
c4892e76
DW
19#include "xfs_scrub.h"
20#include "common.h"
21#include "inodes.h"
ed60d210 22#include "progress.h"
c4892e76 23#include "scrub.h"
a3158a75 24#include "descr.h"
4bbed4ec 25#include "unicrash.h"
c4892e76
DW
26
27/* Phase 5: Check directory connectivity. */
28
396cd022
DW
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.
8142c597
DW
34 *
35 * Returns 0 for success or -1 for error. This function logs errors.
396cd022 36 */
8142c597
DW
37static int
38simple_check_name(
396cd022 39 struct scrub_ctx *ctx,
a3158a75 40 struct descr *dsc,
396cd022
DW
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)) {
a3158a75 50 str_warn(ctx, descr_render(dsc), _("Zero length name found."));
8142c597 51 return 0;
396cd022
DW
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) {
a3158a75 65 str_errno(ctx, descr_render(dsc));
8142c597 66 return -1;
396cd022 67 }
a3158a75 68 str_info(ctx, descr_render(dsc),
396cd022
DW
69_("Control character found in %s name \"%s\"."),
70 namedescr, errname);
71 free(errname);
72 }
73
8142c597 74 return 0;
396cd022
DW
75}
76
77/*
78 * Iterate a directory looking for filenames with problematic
79 * characters.
80 */
8142c597
DW
81static int
82check_dirent_names(
396cd022 83 struct scrub_ctx *ctx,
a3158a75 84 struct descr *dsc,
4bbed4ec 85 int *fd,
4cca629d 86 struct xfs_bulkstat *bstat)
396cd022 87{
4bbed4ec 88 struct unicrash *uc = NULL;
396cd022
DW
89 DIR *dir;
90 struct dirent *dentry;
ac1c1f8e 91 int ret;
396cd022
DW
92
93 dir = fdopendir(*fd);
94 if (!dir) {
a3158a75 95 str_errno(ctx, descr_render(dsc));
8142c597 96 return errno;
396cd022
DW
97 }
98 *fd = -1; /* closedir will close *fd for us */
99
ac1c1f8e
DW
100 ret = unicrash_dir_init(&uc, ctx, bstat);
101 if (ret) {
102 str_liberror(ctx, ret, descr_render(dsc));
4bbed4ec 103 goto out_unicrash;
ac1c1f8e 104 }
4bbed4ec 105
8142c597 106 errno = 0;
396cd022
DW
107 dentry = readdir(dir);
108 while (dentry) {
8142c597 109 if (uc)
ac1c1f8e 110 ret = unicrash_check_dir_name(uc, dsc, dentry);
8142c597
DW
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));
396cd022 116 break;
8142c597
DW
117 }
118 errno = 0;
396cd022
DW
119 dentry = readdir(dir);
120 }
8142c597
DW
121 if (errno) {
122 ret = errno;
123 str_liberror(ctx, ret, descr_render(dsc));
124 }
4bbed4ec 125 unicrash_free(uc);
396cd022 126
4bbed4ec 127out_unicrash:
396cd022 128 closedir(dir);
8142c597 129 return ret;
396cd022
DW
130}
131
132#ifdef HAVE_LIBATTR
133/* Routines to scan all of an inode's xattrs for name problems. */
593f2ab8 134struct attrns_decode {
396cd022
DW
135 int flags;
136 const char *name;
137};
138
593f2ab8 139static const struct attrns_decode attr_ns[] = {
396cd022
DW
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 */
8142c597
DW
150static int
151check_xattr_ns_names(
396cd022 152 struct scrub_ctx *ctx,
a3158a75 153 struct descr *dsc,
396cd022 154 struct xfs_handle *handle,
4cca629d 155 struct xfs_bulkstat *bstat,
593f2ab8 156 const struct attrns_decode *attr_ns)
396cd022
DW
157{
158 struct attrlist_cursor cur;
159 char attrbuf[XFS_XATTR_LIST_MAX];
99ea3018 160 char keybuf[XATTR_NAME_MAX + 1];
396cd022
DW
161 struct attrlist *attrlist = (struct attrlist *)attrbuf;
162 struct attrlist_ent *ent;
55290cfc 163 struct unicrash *uc = NULL;
396cd022
DW
164 int i;
165 int error;
166
ac1c1f8e
DW
167 error = unicrash_xattr_init(&uc, ctx, bstat);
168 if (error) {
169 str_liberror(ctx, error, descr_render(dsc));
8142c597 170 return error;
ac1c1f8e 171 }
4bbed4ec 172
396cd022
DW
173 memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
174 memset(&cur, 0, sizeof(cur));
99ea3018 175 memset(keybuf, 0, XATTR_NAME_MAX + 1);
396cd022
DW
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);
99ea3018 182 snprintf(keybuf, XATTR_NAME_MAX, "%s.%s", attr_ns->name,
396cd022 183 ent->a_name);
8142c597 184 if (uc)
ac1c1f8e 185 error = unicrash_check_xattr_name(uc, dsc,
55290cfc 186 keybuf);
8142c597
DW
187 else
188 error = simple_check_name(ctx, dsc,
55290cfc
DW
189 _("extended attribute"),
190 keybuf);
8142c597
DW
191 if (error) {
192 str_liberror(ctx, error, descr_render(dsc));
4bbed4ec 193 goto out;
8142c597 194 }
396cd022
DW
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 }
8142c597
DW
202 if (error) {
203 if (errno == ESTALE)
204 errno = 0;
205 if (errno)
206 str_errno(ctx, descr_render(dsc));
207 }
396cd022 208out:
4bbed4ec 209 unicrash_free(uc);
8142c597 210 return error;
396cd022
DW
211}
212
213/*
214 * Check all the xattr names in all the xattr namespaces for problematic
215 * characters.
216 */
8142c597
DW
217static int
218check_xattr_names(
396cd022 219 struct scrub_ctx *ctx,
a3158a75 220 struct descr *dsc,
4bbed4ec 221 struct xfs_handle *handle,
4cca629d 222 struct xfs_bulkstat *bstat)
396cd022 223{
593f2ab8 224 const struct attrns_decode *ns;
8142c597 225 int ret;
396cd022
DW
226
227 for (ns = attr_ns; ns->name; ns++) {
8142c597
DW
228 ret = check_xattr_ns_names(ctx, dsc, handle, bstat, ns);
229 if (ret)
396cd022
DW
230 break;
231 }
8142c597 232 return ret;
396cd022
DW
233}
234#else
8142c597 235# define check_xattr_names(c, d, h, b) (0)
396cd022
DW
236#endif /* HAVE_LIBATTR */
237
a3158a75
DW
238static int
239render_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
c4892e76
DW
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.
4bbed4ec
DW
255 *
256 * Check for potential Unicode collisions in names.
c4892e76
DW
257 */
258static int
8142c597 259check_inode_names(
c4892e76
DW
260 struct scrub_ctx *ctx,
261 struct xfs_handle *handle,
4cca629d 262 struct xfs_bulkstat *bstat,
c4892e76
DW
263 void *arg)
264{
a3158a75 265 DEFINE_DESCR(dsc, ctx, render_ino_from_handle);
8142c597 266 bool *aborted = arg;
c4892e76 267 int fd = -1;
8142c597
DW
268 int error = 0;
269 int err2;
c4892e76 270
a3158a75 271 descr_set(&dsc, bstat);
c4892e76
DW
272 background_sleep();
273
4bbed4ec 274 /* Warn about naming problems in xattrs. */
e24ee6b6 275 if (bstat->bs_xflags & FS_XFLAG_HASATTR) {
8142c597
DW
276 error = check_xattr_names(ctx, &dsc, handle, bstat);
277 if (error)
e24ee6b6
DW
278 goto out;
279 }
396cd022 280
c4892e76
DW
281 /* Open the dir, let the kernel try to reconnect it to the root. */
282 if (S_ISDIR(bstat->bs_mode)) {
59f79e0a 283 fd = scrub_open_handle(handle);
c4892e76 284 if (fd < 0) {
8142c597
DW
285 error = errno;
286 if (error == ESTALE)
c4892e76 287 return ESTALE;
a3158a75 288 str_errno(ctx, descr_render(&dsc));
c4892e76
DW
289 goto out;
290 }
291 }
292
4bbed4ec
DW
293 /* Warn about naming problems in the directory entries. */
294 if (fd >= 0 && S_ISDIR(bstat->bs_mode)) {
8142c597
DW
295 error = check_dirent_names(ctx, &dsc, &fd, bstat);
296 if (error)
4bbed4ec
DW
297 goto out;
298 }
396cd022 299
c4892e76 300out:
ed60d210 301 progress_add(1);
6c05cc5d 302 if (fd >= 0) {
8142c597
DW
303 err2 = close(fd);
304 if (err2)
a3158a75 305 str_errno(ctx, descr_render(&dsc));
8142c597
DW
306 if (!error && err2)
307 error = err2;
6c05cc5d 308 }
8142c597
DW
309
310 if (error)
311 *aborted = true;
312 if (!error && *aborted)
313 error = ECANCELED;
314
315 return error;
c4892e76
DW
316}
317
3baa69cd
DW
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
a3158a75
DW
323static int
324scrub_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
3baa69cd
DW
333/*
334 * Check the filesystem label for Unicode normalization problems or misleading
335 * sequences.
336 */
8142c597
DW
337static int
338check_fs_label(
3baa69cd
DW
339 struct scrub_ctx *ctx)
340{
a3158a75 341 DEFINE_DESCR(dsc, ctx, scrub_render_mountpoint);
3baa69cd
DW
342 char label[FSLABEL_MAX];
343 struct unicrash *uc = NULL;
3baa69cd
DW
344 int error;
345
ac1c1f8e
DW
346 error = unicrash_fs_label_init(&uc, ctx);
347 if (error) {
348 str_liberror(ctx, error, descr_render(&dsc));
8142c597 349 return error;
ac1c1f8e 350 }
3baa69cd 351
a3158a75
DW
352 descr_set(&dsc, NULL);
353
3baa69cd 354 /* Retrieve label; quietly bail if we don't support that. */
3f9efb2e 355 error = ioctl(ctx->mnt.fd, FS_IOC_GETFSLABEL, &label);
3baa69cd
DW
356 if (error) {
357 if (errno != EOPNOTSUPP && errno != ENOTTY) {
8142c597 358 error = errno;
3baa69cd
DW
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. */
8142c597 369 if (uc)
ac1c1f8e 370 error = unicrash_check_fs_label(uc, &dsc, label);
8142c597
DW
371 else
372 error = simple_check_name(ctx, &dsc, _("filesystem label"),
a3158a75 373 label);
8142c597
DW
374 if (error)
375 str_liberror(ctx, error, descr_render(&dsc));
3baa69cd
DW
376out:
377 unicrash_free(uc);
8142c597 378 return error;
3baa69cd
DW
379}
380
c4892e76 381/* Check directory connectivity. */
8142c597
DW
382int
383phase5_func(
c4892e76
DW
384 struct scrub_ctx *ctx)
385{
8142c597 386 bool aborted = false;
59f79e0a 387 int ret;
c4892e76 388
49e05cb0 389 if (ctx->corruptions_found || ctx->unfixable_errors) {
c4892e76
DW
390 str_info(ctx, ctx->mntpoint,
391_("Filesystem has errors, skipping connectivity checks."));
8142c597 392 return 0;
c4892e76
DW
393 }
394
8142c597 395 ret = check_fs_label(ctx);
d22f2471 396 if (ret)
8142c597 397 return ret;
3baa69cd 398
8142c597 399 ret = scrub_scan_all_inodes(ctx, check_inode_names, &aborted);
59f79e0a 400 if (ret)
8142c597
DW
401 return ret;
402 if (aborted)
403 return ECANCELED;
404
c4892e76 405 xfs_scrub_report_preen_triggers(ctx);
8142c597
DW
406 return 0;
407}
408
64dabc9f
DW
409/* Estimate how much work we're going to do. */
410int
411phase5_estimate(
412 struct scrub_ctx *ctx,
413 uint64_t *items,
414 unsigned int *nr_threads,
415 int *rshift)
8142c597 416{
64dabc9f
DW
417 *items = ctx->mnt_sv.f_files - ctx->mnt_sv.f_ffree;
418 *nr_threads = scrub_nproc(ctx);
419 *rshift = 0;
420 return 0;
c4892e76 421}