]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/phase5.c
xfs_scrub: fix #include ordering to avoid build failure
[thirdparty/xfsprogs-dev.git] / scrub / phase5.c
1 /*
2 * Copyright (C) 2018 Oracle. All Rights Reserved.
3 *
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 #include "xfs.h"
21 #include <stdint.h>
22 #include <dirent.h>
23 #include <sys/types.h>
24 #include <sys/statvfs.h>
25 #ifdef HAVE_LIBATTR
26 # include <attr/attributes.h>
27 #endif
28 #include "handle.h"
29 #include "list.h"
30 #include "path.h"
31 #include "workqueue.h"
32 #include "xfs_scrub.h"
33 #include "common.h"
34 #include "inodes.h"
35 #include "progress.h"
36 #include "scrub.h"
37 #include "unicrash.h"
38
39 /* Phase 5: Check directory connectivity. */
40
41 /*
42 * Warn about problematic bytes in a directory/attribute name. That means
43 * terminal control characters and escape sequences, since that could be used
44 * to do something naughty to the user's computer and/or break scripts. XFS
45 * doesn't consider any byte sequence invalid, so don't flag these as errors.
46 */
47 static bool
48 xfs_scrub_check_name(
49 struct scrub_ctx *ctx,
50 const char *descr,
51 const char *namedescr,
52 const char *name)
53 {
54 const char *p;
55 bool bad = false;
56 char *errname;
57
58 /* Complain about zero length names. */
59 if (*name == '\0' && should_warn_about_name(ctx)) {
60 str_warn(ctx, descr, _("Zero length name found."));
61 return true;
62 }
63
64 /* control characters */
65 for (p = name; *p; p++) {
66 if ((*p >= 1 && *p <= 31) || *p == 127) {
67 bad = true;
68 break;
69 }
70 }
71
72 if (bad && should_warn_about_name(ctx)) {
73 errname = string_escape(name);
74 if (!errname) {
75 str_errno(ctx, descr);
76 return false;
77 }
78 str_info(ctx, descr,
79 _("Control character found in %s name \"%s\"."),
80 namedescr, errname);
81 free(errname);
82 }
83
84 return true;
85 }
86
87 /*
88 * Iterate a directory looking for filenames with problematic
89 * characters.
90 */
91 static bool
92 xfs_scrub_scan_dirents(
93 struct scrub_ctx *ctx,
94 const char *descr,
95 int *fd,
96 struct xfs_bstat *bstat)
97 {
98 struct unicrash *uc = NULL;
99 DIR *dir;
100 struct dirent *dentry;
101 bool moveon = true;
102
103 dir = fdopendir(*fd);
104 if (!dir) {
105 str_errno(ctx, descr);
106 goto out;
107 }
108 *fd = -1; /* closedir will close *fd for us */
109
110 moveon = unicrash_dir_init(&uc, ctx, bstat);
111 if (!moveon)
112 goto out_unicrash;
113
114 dentry = readdir(dir);
115 while (dentry) {
116 moveon = xfs_scrub_check_name(ctx, descr, _("directory"),
117 dentry->d_name);
118 if (!moveon)
119 break;
120 moveon = unicrash_check_dir_name(uc, descr, dentry);
121 if (!moveon)
122 break;
123 dentry = readdir(dir);
124 }
125 unicrash_free(uc);
126
127 out_unicrash:
128 closedir(dir);
129 out:
130 return moveon;
131 }
132
133 #ifdef HAVE_LIBATTR
134 /* Routines to scan all of an inode's xattrs for name problems. */
135 struct xfs_attr_ns {
136 int flags;
137 const char *name;
138 };
139
140 static const struct xfs_attr_ns attr_ns[] = {
141 {0, "user"},
142 {ATTR_ROOT, "system"},
143 {ATTR_SECURE, "secure"},
144 {0, NULL},
145 };
146
147 /*
148 * Check all the xattr names in a particular namespace of a file handle
149 * for Unicode normalization problems or collisions.
150 */
151 static bool
152 xfs_scrub_scan_fhandle_namespace_xattrs(
153 struct scrub_ctx *ctx,
154 const char *descr,
155 struct xfs_handle *handle,
156 struct xfs_bstat *bstat,
157 const struct xfs_attr_ns *attr_ns)
158 {
159 struct attrlist_cursor cur;
160 char attrbuf[XFS_XATTR_LIST_MAX];
161 char keybuf[NAME_MAX + 1];
162 struct attrlist *attrlist = (struct attrlist *)attrbuf;
163 struct attrlist_ent *ent;
164 struct unicrash *uc;
165 bool moveon = true;
166 int i;
167 int error;
168
169 moveon = unicrash_xattr_init(&uc, ctx, bstat);
170 if (!moveon)
171 return false;
172
173 memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
174 memset(&cur, 0, sizeof(cur));
175 memset(keybuf, 0, 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, NAME_MAX, "%s.%s", attr_ns->name,
183 ent->a_name);
184 moveon = xfs_scrub_check_name(ctx, descr,
185 _("extended attribute"), keybuf);
186 if (!moveon)
187 goto out;
188 moveon = unicrash_check_xattr_name(uc, descr, keybuf);
189 if (!moveon)
190 goto out;
191 }
192
193 if (!attrlist->al_more)
194 break;
195 error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
196 XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
197 }
198 if (error && errno != ESTALE)
199 str_errno(ctx, descr);
200 out:
201 unicrash_free(uc);
202 return moveon;
203 }
204
205 /*
206 * Check all the xattr names in all the xattr namespaces for problematic
207 * characters.
208 */
209 static bool
210 xfs_scrub_scan_fhandle_xattrs(
211 struct scrub_ctx *ctx,
212 const char *descr,
213 struct xfs_handle *handle,
214 struct xfs_bstat *bstat)
215 {
216 const struct xfs_attr_ns *ns;
217 bool moveon = true;
218
219 for (ns = attr_ns; ns->name; ns++) {
220 moveon = xfs_scrub_scan_fhandle_namespace_xattrs(ctx, descr,
221 handle, bstat, ns);
222 if (!moveon)
223 break;
224 }
225 return moveon;
226 }
227 #else
228 # define xfs_scrub_scan_fhandle_xattrs(c, d, h, b) (true)
229 #endif /* HAVE_LIBATTR */
230
231 /*
232 * Verify the connectivity of the directory tree.
233 * We know that the kernel's open-by-handle function will try to reconnect
234 * parents of an opened directory, so we'll accept that as sufficient.
235 *
236 * Check for potential Unicode collisions in names.
237 */
238 static int
239 xfs_scrub_connections(
240 struct scrub_ctx *ctx,
241 struct xfs_handle *handle,
242 struct xfs_bstat *bstat,
243 void *arg)
244 {
245 bool *pmoveon = arg;
246 char descr[DESCR_BUFSZ];
247 bool moveon;
248 xfs_agnumber_t agno;
249 xfs_agino_t agino;
250 int fd = -1;
251
252 agno = bstat->bs_ino / (1ULL << (ctx->inopblog + ctx->agblklog));
253 agino = bstat->bs_ino % (1ULL << (ctx->inopblog + ctx->agblklog));
254 snprintf(descr, DESCR_BUFSZ, _("inode %"PRIu64" (%u/%u)"),
255 (uint64_t)bstat->bs_ino, agno, agino);
256 background_sleep();
257
258 /* Warn about naming problems in xattrs. */
259 moveon = xfs_scrub_scan_fhandle_xattrs(ctx, descr, handle, bstat);
260 if (!moveon)
261 goto out;
262
263 /* Open the dir, let the kernel try to reconnect it to the root. */
264 if (S_ISDIR(bstat->bs_mode)) {
265 fd = xfs_open_handle(handle);
266 if (fd < 0) {
267 if (errno == ESTALE)
268 return ESTALE;
269 str_errno(ctx, descr);
270 goto out;
271 }
272 }
273
274 /* Warn about naming problems in the directory entries. */
275 if (fd >= 0 && S_ISDIR(bstat->bs_mode)) {
276 moveon = xfs_scrub_scan_dirents(ctx, descr, &fd, bstat);
277 if (!moveon)
278 goto out;
279 }
280
281 out:
282 progress_add(1);
283 if (fd >= 0)
284 close(fd);
285 if (!moveon)
286 *pmoveon = false;
287 return *pmoveon ? 0 : XFS_ITERATE_INODES_ABORT;
288 }
289
290 /* Check directory connectivity. */
291 bool
292 xfs_scan_connections(
293 struct scrub_ctx *ctx)
294 {
295 bool moveon = true;
296 bool ret;
297
298 if (ctx->errors_found) {
299 str_info(ctx, ctx->mntpoint,
300 _("Filesystem has errors, skipping connectivity checks."));
301 return true;
302 }
303
304 ret = xfs_scan_all_inodes(ctx, xfs_scrub_connections, &moveon);
305 if (!ret)
306 moveon = false;
307 if (!moveon)
308 return false;
309 xfs_scrub_report_preen_triggers(ctx);
310 return true;
311 }