tv.tv_nsec = time % 1000000;
nanosleep(&tv, NULL);
}
+
+/*
+ * Return the input string with non-printing bytes escaped.
+ * Caller must free the buffer.
+ */
+char *
+string_escape(
+ const char *in)
+{
+ char *str;
+ const char *p;
+ char *q;
+ int x;
+
+ str = malloc(strlen(in) * 4);
+ if (!str)
+ return NULL;
+ for (p = in, q = str; *p != '\0'; p++) {
+ if (isprint(*p)) {
+ *q = *p;
+ q++;
+ } else {
+ x = sprintf(q, "\\x%02x", *p);
+ q += x;
+ }
+ }
+ *q = '\0';
+ return str;
+}
+
+/*
+ * Record another naming warning, and decide if it's worth
+ * complaining about.
+ */
+bool
+should_warn_about_name(
+ struct scrub_ctx *ctx)
+{
+ bool whine;
+ bool res;
+
+ pthread_mutex_lock(&ctx->lock);
+ ctx->naming_warnings++;
+ whine = ctx->naming_warnings == TOO_MANY_NAME_WARNINGS;
+ res = ctx->naming_warnings < TOO_MANY_NAME_WARNINGS;
+ pthread_mutex_unlock(&ctx->lock);
+
+ if (whine && !(debug || verbose))
+ str_info(ctx, ctx->mntpoint,
+_("More than %u naming warnings, shutting up."),
+ TOO_MANY_NAME_WARNINGS);
+
+ return debug || verbose || res;
+}
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
+#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#ifdef HAVE_LIBATTR
+# include <attr/attributes.h>
+#endif
#include "xfs.h"
+#include "handle.h"
#include "path.h"
#include "workqueue.h"
#include "xfs_scrub.h"
/* Phase 5: Check directory connectivity. */
+/*
+ * Warn about problematic bytes in a directory/attribute name. That means
+ * terminal control characters and escape sequences, since that could be used
+ * to do something naughty to the user's computer and/or break scripts. XFS
+ * doesn't consider any byte sequence invalid, so don't flag these as errors.
+ */
+static bool
+xfs_scrub_check_name(
+ struct scrub_ctx *ctx,
+ const char *descr,
+ const char *namedescr,
+ const char *name)
+{
+ const char *p;
+ bool bad = false;
+ char *errname;
+
+ /* Complain about zero length names. */
+ if (*name == '\0' && should_warn_about_name(ctx)) {
+ str_warn(ctx, descr, _("Zero length name found."));
+ return true;
+ }
+
+ /* control characters */
+ for (p = name; *p; p++) {
+ if ((*p >= 1 && *p <= 31) || *p == 127) {
+ bad = true;
+ break;
+ }
+ }
+
+ if (bad && should_warn_about_name(ctx)) {
+ errname = string_escape(name);
+ if (!errname) {
+ str_errno(ctx, descr);
+ return false;
+ }
+ str_info(ctx, descr,
+_("Control character found in %s name \"%s\"."),
+ namedescr, errname);
+ free(errname);
+ }
+
+ return true;
+}
+
+/*
+ * Iterate a directory looking for filenames with problematic
+ * characters.
+ */
+static bool
+xfs_scrub_scan_dirents(
+ struct scrub_ctx *ctx,
+ const char *descr,
+ int *fd)
+{
+ DIR *dir;
+ struct dirent *dentry;
+ bool moveon = true;
+
+ dir = fdopendir(*fd);
+ if (!dir) {
+ str_errno(ctx, descr);
+ goto out;
+ }
+ *fd = -1; /* closedir will close *fd for us */
+
+ dentry = readdir(dir);
+ while (dentry) {
+ moveon = xfs_scrub_check_name(ctx, descr, _("directory"),
+ dentry->d_name);
+ if (!moveon)
+ break;
+ dentry = readdir(dir);
+ }
+
+ closedir(dir);
+out:
+ return moveon;
+}
+
+#ifdef HAVE_LIBATTR
+/* Routines to scan all of an inode's xattrs for name problems. */
+struct xfs_attr_ns {
+ int flags;
+ const char *name;
+};
+
+static const struct xfs_attr_ns attr_ns[] = {
+ {0, "user"},
+ {ATTR_ROOT, "system"},
+ {ATTR_SECURE, "secure"},
+ {0, NULL},
+};
+
+/*
+ * Check all the xattr names in a particular namespace of a file handle
+ * for Unicode normalization problems or collisions.
+ */
+static bool
+xfs_scrub_scan_fhandle_namespace_xattrs(
+ struct scrub_ctx *ctx,
+ const char *descr,
+ struct xfs_handle *handle,
+ const struct xfs_attr_ns *attr_ns)
+{
+ struct attrlist_cursor cur;
+ char attrbuf[XFS_XATTR_LIST_MAX];
+ char keybuf[NAME_MAX + 1];
+ struct attrlist *attrlist = (struct attrlist *)attrbuf;
+ struct attrlist_ent *ent;
+ bool moveon = true;
+ int i;
+ int error;
+
+ memset(attrbuf, 0, XFS_XATTR_LIST_MAX);
+ memset(&cur, 0, sizeof(cur));
+ memset(keybuf, 0, NAME_MAX + 1);
+ error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
+ XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
+ while (!error) {
+ /* Examine the xattrs. */
+ for (i = 0; i < attrlist->al_count; i++) {
+ ent = ATTR_ENTRY(attrlist, i);
+ snprintf(keybuf, NAME_MAX, "%s.%s", attr_ns->name,
+ ent->a_name);
+ moveon = xfs_scrub_check_name(ctx, descr,
+ _("extended attribute"), keybuf);
+ if (!moveon)
+ goto out;
+ }
+
+ if (!attrlist->al_more)
+ break;
+ error = attr_list_by_handle(handle, sizeof(*handle), attrbuf,
+ XFS_XATTR_LIST_MAX, attr_ns->flags, &cur);
+ }
+ if (error && errno != ESTALE)
+ str_errno(ctx, descr);
+out:
+ return moveon;
+}
+
+/*
+ * Check all the xattr names in all the xattr namespaces for problematic
+ * characters.
+ */
+static bool
+xfs_scrub_scan_fhandle_xattrs(
+ struct scrub_ctx *ctx,
+ const char *descr,
+ struct xfs_handle *handle)
+{
+ const struct xfs_attr_ns *ns;
+ bool moveon = true;
+
+ for (ns = attr_ns; ns->name; ns++) {
+ moveon = xfs_scrub_scan_fhandle_namespace_xattrs(ctx, descr,
+ handle, ns);
+ if (!moveon)
+ break;
+ }
+ return moveon;
+}
+#else
+static inline bool
+xfs_scrub_scan_fhandle_xattrs(
+ struct scrub_ctx *ctx,
+ const char *descr,
+ struct xfs_handle *handle)
+{
+ return true;
+}
+#endif /* HAVE_LIBATTR */
+
/*
* Verify the connectivity of the directory tree.
* We know that the kernel's open-by-handle function will try to reconnect
(uint64_t)bstat->bs_ino, agno, agino);
background_sleep();
+ /* Warn about naming problems in xattrs. */
+ moveon = xfs_scrub_scan_fhandle_xattrs(ctx, descr, handle);
+ if (!moveon)
+ goto out;
+
/* Open the dir, let the kernel try to reconnect it to the root. */
if (S_ISDIR(bstat->bs_mode)) {
fd = xfs_open_handle(handle);
}
}
+ /* Warn about naming problems in the directory entries. */
+ if (fd >= 0 && S_ISDIR(bstat->bs_mode)) {
+ moveon = xfs_scrub_scan_dirents(ctx, descr, &fd);
+ if (!moveon)
+ goto out;
+ }
+
out:
if (fd >= 0)
close(fd);