]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/phase5.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
10 #include <sys/statvfs.h>
12 # include <attr/attributes.h>
17 #include "libfrog/paths.h"
18 #include "libfrog/workqueue.h"
19 #include "xfs_scrub.h"
27 /* Phase 5: Check directory connectivity. */
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.
35 * Returns 0 for success or -1 for error. This function logs errors.
39 struct scrub_ctx
*ctx
,
41 const char *namedescr
,
48 /* Complain about zero length names. */
49 if (*name
== '\0' && should_warn_about_name(ctx
)) {
50 str_warn(ctx
, descr_render(dsc
), _("Zero length name found."));
54 /* control characters */
55 for (p
= name
; *p
; p
++) {
56 if ((*p
>= 1 && *p
<= 31) || *p
== 127) {
62 if (bad
&& should_warn_about_name(ctx
)) {
63 errname
= string_escape(name
);
65 str_errno(ctx
, descr_render(dsc
));
68 str_info(ctx
, descr_render(dsc
),
69 _("Control character found in %s name \"%s\"."),
78 * Iterate a directory looking for filenames with problematic
83 struct scrub_ctx
*ctx
,
86 struct xfs_bulkstat
*bstat
)
88 struct unicrash
*uc
= NULL
;
90 struct dirent
*dentry
;
95 str_errno(ctx
, descr_render(dsc
));
98 *fd
= -1; /* closedir will close *fd for us */
100 ret
= unicrash_dir_init(&uc
, ctx
, bstat
);
102 str_liberror(ctx
, ret
, descr_render(dsc
));
107 dentry
= readdir(dir
);
110 ret
= unicrash_check_dir_name(uc
, dsc
, dentry
);
112 ret
= simple_check_name(ctx
, dsc
, _("directory"),
115 str_liberror(ctx
, ret
, descr_render(dsc
));
119 dentry
= readdir(dir
);
123 str_liberror(ctx
, ret
, descr_render(dsc
));
133 /* Routines to scan all of an inode's xattrs for name problems. */
134 struct attrns_decode
{
139 static const struct attrns_decode attr_ns
[] = {
141 {ATTR_ROOT
, "system"},
142 {ATTR_SECURE
, "secure"},
147 * Check all the xattr names in a particular namespace of a file handle
148 * for Unicode normalization problems or collisions.
151 check_xattr_ns_names(
152 struct scrub_ctx
*ctx
,
154 struct xfs_handle
*handle
,
155 struct xfs_bulkstat
*bstat
,
156 const struct attrns_decode
*attr_ns
)
158 struct attrlist_cursor cur
;
159 char attrbuf
[XFS_XATTR_LIST_MAX
];
160 char keybuf
[XATTR_NAME_MAX
+ 1];
161 struct attrlist
*attrlist
= (struct attrlist
*)attrbuf
;
162 struct attrlist_ent
*ent
;
163 struct unicrash
*uc
= NULL
;
167 error
= unicrash_xattr_init(&uc
, ctx
, bstat
);
169 str_liberror(ctx
, error
, descr_render(dsc
));
173 memset(attrbuf
, 0, XFS_XATTR_LIST_MAX
);
174 memset(&cur
, 0, sizeof(cur
));
175 memset(keybuf
, 0, XATTR_NAME_MAX
+ 1);
176 error
= attr_list_by_handle(handle
, sizeof(*handle
), attrbuf
,
177 XFS_XATTR_LIST_MAX
, attr_ns
->flags
, &cur
);
179 /* Examine the xattrs. */
180 for (i
= 0; i
< attrlist
->al_count
; i
++) {
181 ent
= ATTR_ENTRY(attrlist
, i
);
182 snprintf(keybuf
, XATTR_NAME_MAX
, "%s.%s", attr_ns
->name
,
185 error
= unicrash_check_xattr_name(uc
, dsc
,
188 error
= simple_check_name(ctx
, dsc
,
189 _("extended attribute"),
192 str_liberror(ctx
, error
, descr_render(dsc
));
197 if (!attrlist
->al_more
)
199 error
= attr_list_by_handle(handle
, sizeof(*handle
), attrbuf
,
200 XFS_XATTR_LIST_MAX
, attr_ns
->flags
, &cur
);
207 str_errno(ctx
, descr_render(dsc
));
215 * Check all the xattr names in all the xattr namespaces for problematic
220 struct scrub_ctx
*ctx
,
222 struct xfs_handle
*handle
,
223 struct xfs_bulkstat
*bstat
)
225 const struct attrns_decode
*ns
;
228 for (ns
= attr_ns
; ns
->name
; ns
++) {
229 ret
= check_xattr_ns_names(ctx
, dsc
, handle
, bstat
, ns
);
236 # define check_xattr_names(c, d, h, b) (0)
237 #endif /* HAVE_LIBATTR */
240 render_ino_from_handle(
241 struct scrub_ctx
*ctx
,
246 struct xfs_bulkstat
*bstat
= data
;
248 return scrub_render_ino_descr(ctx
, buf
, buflen
, bstat
->bs_ino
,
249 bstat
->bs_gen
, NULL
);
253 * Verify the connectivity of the directory tree.
254 * We know that the kernel's open-by-handle function will try to reconnect
255 * parents of an opened directory, so we'll accept that as sufficient.
257 * Check for potential Unicode collisions in names.
261 struct scrub_ctx
*ctx
,
262 struct xfs_handle
*handle
,
263 struct xfs_bulkstat
*bstat
,
266 DEFINE_DESCR(dsc
, ctx
, render_ino_from_handle
);
272 descr_set(&dsc
, bstat
);
275 /* Warn about naming problems in xattrs. */
276 if (bstat
->bs_xflags
& FS_XFLAG_HASATTR
) {
277 error
= check_xattr_names(ctx
, &dsc
, handle
, bstat
);
283 * Warn about naming problems in the directory entries. Opening the
284 * dir by handle means the kernel will try to reconnect it to the root.
285 * If the reconnection fails due to corruption in the parents we get
286 * ESTALE, which is why we skip phase 5 if we found corruption.
288 if (S_ISDIR(bstat
->bs_mode
)) {
289 fd
= scrub_open_handle(handle
);
294 str_errno(ctx
, descr_render(&dsc
));
298 error
= check_dirent_names(ctx
, &dsc
, &fd
, bstat
);
308 str_errno(ctx
, descr_render(&dsc
));
315 if (!error
&& *aborted
)
321 #ifndef FS_IOC_GETFSLABEL
322 # define FSLABEL_MAX 256
323 # define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX])
324 #endif /* FS_IOC_GETFSLABEL */
327 scrub_render_mountpoint(
328 struct scrub_ctx
*ctx
,
333 return snprintf(buf
, buflen
, _("%s"), ctx
->mntpoint
);
337 * Check the filesystem label for Unicode normalization problems or misleading
342 struct scrub_ctx
*ctx
)
344 DEFINE_DESCR(dsc
, ctx
, scrub_render_mountpoint
);
345 char label
[FSLABEL_MAX
];
346 struct unicrash
*uc
= NULL
;
349 error
= unicrash_fs_label_init(&uc
, ctx
);
351 str_liberror(ctx
, error
, descr_render(&dsc
));
355 descr_set(&dsc
, NULL
);
357 /* Retrieve label; quietly bail if we don't support that. */
358 error
= ioctl(ctx
->mnt
.fd
, FS_IOC_GETFSLABEL
, &label
);
360 if (errno
!= EOPNOTSUPP
&& errno
!= ENOTTY
) {
362 perror(ctx
->mntpoint
);
367 /* Ignore empty labels. */
371 /* Otherwise check for weirdness. */
373 error
= unicrash_check_fs_label(uc
, &dsc
, label
);
375 error
= simple_check_name(ctx
, &dsc
, _("filesystem label"),
378 str_liberror(ctx
, error
, descr_render(&dsc
));
384 /* Check directory connectivity. */
387 struct scrub_ctx
*ctx
)
389 bool aborted
= false;
392 if (ctx
->corruptions_found
|| ctx
->unfixable_errors
) {
393 str_info(ctx
, ctx
->mntpoint
,
394 _("Filesystem has errors, skipping connectivity checks."));
398 ret
= check_fs_label(ctx
);
402 ret
= scrub_scan_all_inodes(ctx
, check_inode_names
, &aborted
);
408 scrub_report_preen_triggers(ctx
);
412 /* Estimate how much work we're going to do. */
415 struct scrub_ctx
*ctx
,
417 unsigned int *nr_threads
,
420 *items
= ctx
->mnt_sv
.f_files
- ctx
->mnt_sv
.f_ffree
;
421 *nr_threads
= scrub_nproc(ctx
);