]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/phase5.c
xfs_scrub: explicitly track corruptions, not just errors
[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"
4bbed4ec 24#include "unicrash.h"
c4892e76
DW
25
26/* Phase 5: Check directory connectivity. */
27
396cd022
DW
28/*
29 * Warn about problematic bytes in a directory/attribute name. That means
30 * terminal control characters and escape sequences, since that could be used
31 * to do something naughty to the user's computer and/or break scripts. XFS
32 * doesn't consider any byte sequence invalid, so don't flag these as errors.
33 */
34static bool
35xfs_scrub_check_name(
36 struct scrub_ctx *ctx,
37 const char *descr,
38 const char *namedescr,
39 const char *name)
40{
41 const char *p;
42 bool bad = false;
43 char *errname;
44
45 /* Complain about zero length names. */
46 if (*name == '\0' && should_warn_about_name(ctx)) {
47 str_warn(ctx, descr, _("Zero length name found."));
48 return true;
49 }
50
51 /* control characters */
52 for (p = name; *p; p++) {
53 if ((*p >= 1 && *p <= 31) || *p == 127) {
54 bad = true;
55 break;
56 }
57 }
58
59 if (bad && should_warn_about_name(ctx)) {
60 errname = string_escape(name);
61 if (!errname) {
62 str_errno(ctx, descr);
63 return false;
64 }
65 str_info(ctx, descr,
66_("Control character found in %s name \"%s\"."),
67 namedescr, errname);
68 free(errname);
69 }
70
71 return true;
72}
73
74/*
75 * Iterate a directory looking for filenames with problematic
76 * characters.
77 */
78static bool
79xfs_scrub_scan_dirents(
80 struct scrub_ctx *ctx,
81 const char *descr,
4bbed4ec 82 int *fd,
4cca629d 83 struct xfs_bulkstat *bstat)
396cd022 84{
4bbed4ec 85 struct unicrash *uc = NULL;
396cd022
DW
86 DIR *dir;
87 struct dirent *dentry;
88 bool moveon = true;
89
90 dir = fdopendir(*fd);
91 if (!dir) {
92 str_errno(ctx, descr);
93 goto out;
94 }
95 *fd = -1; /* closedir will close *fd for us */
96
4bbed4ec
DW
97 moveon = unicrash_dir_init(&uc, ctx, bstat);
98 if (!moveon)
99 goto out_unicrash;
100
396cd022
DW
101 dentry = readdir(dir);
102 while (dentry) {
55290cfc
DW
103 if (uc)
104 moveon = unicrash_check_dir_name(uc, descr, dentry);
105 else
106 moveon = xfs_scrub_check_name(ctx, descr,
107 _("directory"), dentry->d_name);
396cd022
DW
108 if (!moveon)
109 break;
110 dentry = readdir(dir);
111 }
4bbed4ec 112 unicrash_free(uc);
396cd022 113
4bbed4ec 114out_unicrash:
396cd022
DW
115 closedir(dir);
116out:
117 return moveon;
118}
119
120#ifdef HAVE_LIBATTR
121/* Routines to scan all of an inode's xattrs for name problems. */
593f2ab8 122struct attrns_decode {
396cd022
DW
123 int flags;
124 const char *name;
125};
126
593f2ab8 127static const struct attrns_decode attr_ns[] = {
396cd022
DW
128 {0, "user"},
129 {ATTR_ROOT, "system"},
130 {ATTR_SECURE, "secure"},
131 {0, NULL},
132};
133
134/*
135 * Check all the xattr names in a particular namespace of a file handle
136 * for Unicode normalization problems or collisions.
137 */
138static bool
139xfs_scrub_scan_fhandle_namespace_xattrs(
140 struct scrub_ctx *ctx,
141 const char *descr,
142 struct xfs_handle *handle,
4cca629d 143 struct xfs_bulkstat *bstat,
593f2ab8 144 const struct attrns_decode *attr_ns)
396cd022
DW
145{
146 struct attrlist_cursor cur;
147 char attrbuf[XFS_XATTR_LIST_MAX];
99ea3018 148 char keybuf[XATTR_NAME_MAX + 1];
396cd022
DW
149 struct attrlist *attrlist = (struct attrlist *)attrbuf;
150 struct attrlist_ent *ent;
55290cfc 151 struct unicrash *uc = NULL;
396cd022
DW
152 bool moveon = true;
153 int i;
154 int error;
155
4bbed4ec
DW
156 moveon = unicrash_xattr_init(&uc, ctx, bstat);
157 if (!moveon)
158 return false;
159
396cd022
DW
160 memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
161 memset(&cur, 0, sizeof(cur));
99ea3018 162 memset(keybuf, 0, XATTR_NAME_MAX + 1);
396cd022
DW
163 error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
164 XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
165 while (!error) {
166 /* Examine the xattrs. */
167 for (i = 0; i < attrlist->al_count; i++) {
168 ent = ATTR_ENTRY(attrlist, i);
99ea3018 169 snprintf(keybuf, XATTR_NAME_MAX, "%s.%s", attr_ns->name,
396cd022 170 ent->a_name);
55290cfc
DW
171 if (uc)
172 moveon = unicrash_check_xattr_name(uc, descr,
173 keybuf);
174 else
175 moveon = xfs_scrub_check_name(ctx, descr,
176 _("extended attribute"),
177 keybuf);
4bbed4ec
DW
178 if (!moveon)
179 goto out;
396cd022
DW
180 }
181
182 if (!attrlist->al_more)
183 break;
184 error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
185 XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
186 }
187 if (error && errno != ESTALE)
188 str_errno(ctx, descr);
189out:
4bbed4ec 190 unicrash_free(uc);
396cd022
DW
191 return moveon;
192}
193
194/*
195 * Check all the xattr names in all the xattr namespaces for problematic
196 * characters.
197 */
198static bool
199xfs_scrub_scan_fhandle_xattrs(
200 struct scrub_ctx *ctx,
201 const char *descr,
4bbed4ec 202 struct xfs_handle *handle,
4cca629d 203 struct xfs_bulkstat *bstat)
396cd022 204{
593f2ab8 205 const struct attrns_decode *ns;
396cd022
DW
206 bool moveon = true;
207
208 for (ns = attr_ns; ns->name; ns++) {
209 moveon = xfs_scrub_scan_fhandle_namespace_xattrs(ctx, descr,
4bbed4ec 210 handle, bstat, ns);
396cd022
DW
211 if (!moveon)
212 break;
213 }
214 return moveon;
215}
216#else
4bbed4ec 217# define xfs_scrub_scan_fhandle_xattrs(c, d, h, b) (true)
396cd022
DW
218#endif /* HAVE_LIBATTR */
219
c4892e76
DW
220/*
221 * Verify the connectivity of the directory tree.
222 * We know that the kernel's open-by-handle function will try to reconnect
223 * parents of an opened directory, so we'll accept that as sufficient.
4bbed4ec
DW
224 *
225 * Check for potential Unicode collisions in names.
c4892e76
DW
226 */
227static int
228xfs_scrub_connections(
229 struct scrub_ctx *ctx,
230 struct xfs_handle *handle,
4cca629d 231 struct xfs_bulkstat *bstat,
c4892e76
DW
232 void *arg)
233{
234 bool *pmoveon = arg;
235 char descr[DESCR_BUFSZ];
6d135e84 236 bool moveon = true;
c4892e76 237 int fd = -1;
6c05cc5d 238 int error;
c4892e76 239
15589f0a
DW
240 scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ, bstat->bs_ino,
241 bstat->bs_gen, NULL);
c4892e76
DW
242 background_sleep();
243
4bbed4ec 244 /* Warn about naming problems in xattrs. */
e24ee6b6
DW
245 if (bstat->bs_xflags & FS_XFLAG_HASATTR) {
246 moveon = xfs_scrub_scan_fhandle_xattrs(ctx, descr, handle,
247 bstat);
248 if (!moveon)
249 goto out;
250 }
396cd022 251
c4892e76
DW
252 /* Open the dir, let the kernel try to reconnect it to the root. */
253 if (S_ISDIR(bstat->bs_mode)) {
254 fd = xfs_open_handle(handle);
255 if (fd < 0) {
256 if (errno == ESTALE)
257 return ESTALE;
258 str_errno(ctx, descr);
259 goto out;
260 }
261 }
262
4bbed4ec
DW
263 /* Warn about naming problems in the directory entries. */
264 if (fd >= 0 && S_ISDIR(bstat->bs_mode)) {
265 moveon = xfs_scrub_scan_dirents(ctx, descr, &fd, bstat);
266 if (!moveon)
267 goto out;
268 }
396cd022 269
c4892e76 270out:
ed60d210 271 progress_add(1);
6c05cc5d
DW
272 if (fd >= 0) {
273 error = close(fd);
274 if (error)
275 str_errno(ctx, descr);
276 }
c4892e76
DW
277 if (!moveon)
278 *pmoveon = false;
279 return *pmoveon ? 0 : XFS_ITERATE_INODES_ABORT;
280}
281
3baa69cd
DW
282#ifndef FS_IOC_GETFSLABEL
283# define FSLABEL_MAX 256
284# define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX])
285#endif /* FS_IOC_GETFSLABEL */
286
287/*
288 * Check the filesystem label for Unicode normalization problems or misleading
289 * sequences.
290 */
291static bool
292xfs_scrub_fs_label(
293 struct scrub_ctx *ctx)
294{
295 char label[FSLABEL_MAX];
296 struct unicrash *uc = NULL;
297 bool moveon = true;
298 int error;
299
300 moveon = unicrash_fs_label_init(&uc, ctx);
301 if (!moveon)
302 return false;
303
304 /* Retrieve label; quietly bail if we don't support that. */
3f9efb2e 305 error = ioctl(ctx->mnt.fd, FS_IOC_GETFSLABEL, &label);
3baa69cd
DW
306 if (error) {
307 if (errno != EOPNOTSUPP && errno != ENOTTY) {
308 moveon = false;
309 perror(ctx->mntpoint);
310 }
311 goto out;
312 }
313
314 /* Ignore empty labels. */
315 if (label[0] == 0)
316 goto out;
317
318 /* Otherwise check for weirdness. */
319 if (uc)
320 moveon = unicrash_check_fs_label(uc, ctx->mntpoint, label);
321 else
322 moveon = xfs_scrub_check_name(ctx, ctx->mntpoint,
323 _("filesystem label"), label);
324 if (!moveon)
325 goto out;
326out:
327 unicrash_free(uc);
328 return moveon;
329}
330
c4892e76
DW
331/* Check directory connectivity. */
332bool
333xfs_scan_connections(
334 struct scrub_ctx *ctx)
335{
336 bool moveon = true;
337 bool ret;
338
abc2e70d 339 if (ctx->corruptions_found) {
c4892e76
DW
340 str_info(ctx, ctx->mntpoint,
341_("Filesystem has errors, skipping connectivity checks."));
342 return true;
343 }
344
3baa69cd
DW
345 moveon = xfs_scrub_fs_label(ctx);
346 if (!moveon)
347 return false;
348
c4892e76
DW
349 ret = xfs_scan_all_inodes(ctx, xfs_scrub_connections, &moveon);
350 if (!ret)
351 moveon = false;
352 if (!moveon)
353 return false;
354 xfs_scrub_report_preen_triggers(ctx);
355 return true;
356}