]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/phase5.c
e0c4a3dfafad4cc0defcb7500a35914ff6f89a17
[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 "unicrash.h"
25
26 /* Phase 5: Check directory connectivity. */
27
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 */
34 static bool
35 xfs_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 */
78 static bool
79 xfs_scrub_scan_dirents(
80 struct scrub_ctx *ctx,
81 const char *descr,
82 int *fd,
83 struct xfs_bulkstat *bstat)
84 {
85 struct unicrash *uc = NULL;
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
97 moveon = unicrash_dir_init(&uc, ctx, bstat);
98 if (!moveon)
99 goto out_unicrash;
100
101 dentry = readdir(dir);
102 while (dentry) {
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);
108 if (!moveon)
109 break;
110 dentry = readdir(dir);
111 }
112 unicrash_free(uc);
113
114 out_unicrash:
115 closedir(dir);
116 out:
117 return moveon;
118 }
119
120 #ifdef HAVE_LIBATTR
121 /* Routines to scan all of an inode's xattrs for name problems. */
122 struct attrns_decode {
123 int flags;
124 const char *name;
125 };
126
127 static const struct attrns_decode attr_ns[] = {
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 */
138 static bool
139 xfs_scrub_scan_fhandle_namespace_xattrs(
140 struct scrub_ctx *ctx,
141 const char *descr,
142 struct xfs_handle *handle,
143 struct xfs_bulkstat *bstat,
144 const struct attrns_decode *attr_ns)
145 {
146 struct attrlist_cursor cur;
147 char attrbuf[XFS_XATTR_LIST_MAX];
148 char keybuf[XATTR_NAME_MAX + 1];
149 struct attrlist *attrlist = (struct attrlist *)attrbuf;
150 struct attrlist_ent *ent;
151 struct unicrash *uc = NULL;
152 bool moveon = true;
153 int i;
154 int error;
155
156 moveon = unicrash_xattr_init(&uc, ctx, bstat);
157 if (!moveon)
158 return false;
159
160 memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
161 memset(&cur, 0, sizeof(cur));
162 memset(keybuf, 0, XATTR_NAME_MAX + 1);
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);
169 snprintf(keybuf, XATTR_NAME_MAX, "%s.%s", attr_ns->name,
170 ent->a_name);
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);
178 if (!moveon)
179 goto out;
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);
189 out:
190 unicrash_free(uc);
191 return moveon;
192 }
193
194 /*
195 * Check all the xattr names in all the xattr namespaces for problematic
196 * characters.
197 */
198 static bool
199 xfs_scrub_scan_fhandle_xattrs(
200 struct scrub_ctx *ctx,
201 const char *descr,
202 struct xfs_handle *handle,
203 struct xfs_bulkstat *bstat)
204 {
205 const struct attrns_decode *ns;
206 bool moveon = true;
207
208 for (ns = attr_ns; ns->name; ns++) {
209 moveon = xfs_scrub_scan_fhandle_namespace_xattrs(ctx, descr,
210 handle, bstat, ns);
211 if (!moveon)
212 break;
213 }
214 return moveon;
215 }
216 #else
217 # define xfs_scrub_scan_fhandle_xattrs(c, d, h, b) (true)
218 #endif /* HAVE_LIBATTR */
219
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.
224 *
225 * Check for potential Unicode collisions in names.
226 */
227 static int
228 xfs_scrub_connections(
229 struct scrub_ctx *ctx,
230 struct xfs_handle *handle,
231 struct xfs_bulkstat *bstat,
232 void *arg)
233 {
234 bool *pmoveon = arg;
235 char descr[DESCR_BUFSZ];
236 bool moveon = true;
237 int fd = -1;
238 int error;
239
240 scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ, bstat->bs_ino,
241 bstat->bs_gen, NULL);
242 background_sleep();
243
244 /* Warn about naming problems in xattrs. */
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 }
251
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
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 }
269
270 out:
271 progress_add(1);
272 if (fd >= 0) {
273 error = close(fd);
274 if (error)
275 str_errno(ctx, descr);
276 }
277 if (!moveon)
278 *pmoveon = false;
279 return *pmoveon ? 0 : XFS_ITERATE_INODES_ABORT;
280 }
281
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 */
291 static bool
292 xfs_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. */
305 error = ioctl(ctx->mnt.fd, FS_IOC_GETFSLABEL, &label);
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;
326 out:
327 unicrash_free(uc);
328 return moveon;
329 }
330
331 /* Check directory connectivity. */
332 bool
333 xfs_scan_connections(
334 struct scrub_ctx *ctx)
335 {
336 bool moveon = true;
337 bool ret;
338
339 if (ctx->corruptions_found || ctx->unfixable_errors) {
340 str_info(ctx, ctx->mntpoint,
341 _("Filesystem has errors, skipping connectivity checks."));
342 return true;
343 }
344
345 moveon = xfs_scrub_fs_label(ctx);
346 if (!moveon)
347 return false;
348
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 }