From patchwork Sun Dec 31 23:18:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508074 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 711BEC15B for ; Sun, 31 Dec 2023 23:18:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r8IetY+4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3DE22C433C8; Sun, 31 Dec 2023 23:18:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064704; bh=AOdWtnVTEGWewYoy1qlqbNzwhMaCpjZUHQ5t66/Y7Tk=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=r8IetY+44Yw1aCvgoeUA8kXHPcCKsfw7wWM9efChm0QZxIqIRlR7EtP41zVoW95uP MPBmRRpMvj9673QDI1m1S03jPvL6VhCm3InTx5raEMkRF7G1Bm2VYNskvM5Zjhmfwd myCj6NEXkx0KEjSg5BXVjFXqgWRJZIR+2+g+8vuGshnaDPN8X7/dnau9O58/KQdL/m +9La+WYbvKYYwz44O61RCqwpkkEbQegbvU2fDFbvW3o5tbcdbEFDEKOAWjlOKOqYQq j5MN1V7s1c5sV0TkWMJNn2m/nGLMHTxTxLUjC9G8/pWGtRoj3Us20Yxyl8cNd5Y+DQ +w0IrOjRaVdJg== Date: Sun, 31 Dec 2023 15:18:23 -0800 Subject: [PATCH 1/6] xfs_db: add link and unlink expert commands From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007446.1805996.6473402596254394783.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Create a pair of commands to create and remove directory entries to support functional testing of directory tree corruption. Signed-off-by: Darrick J. Wong --- db/namei.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++ include/xfs_inode.h | 4 libxfs/libxfs_api_defs.h | 6 + man/man8/xfs_db.8 | 20 ++ 4 files changed, 404 insertions(+) diff --git a/db/namei.c b/db/namei.c index fb7f63fda07..196da7f90e9 100644 --- a/db/namei.c +++ b/db/namei.c @@ -930,6 +930,372 @@ static struct cmdinfo parent_cmd = { .help = parent_help, }; +static void +link_help(void) +{ + dbprintf(_( +"\n" +" Create a directory entry in the current directory that points to the\n" +" specified file.\n" +"\n" +" Options:\n" +" -i -- Point to this specific inode number.\n" +" -p -- Point to the inode given by this path.\n" +" -t -- Set the file type to this value.\n" +" name -- Create this directory entry with this name.\n" + )); +} + +static int +create_child( + struct xfs_mount *mp, + xfs_ino_t parent_ino, + const char *name, + unsigned int ftype, + xfs_ino_t child_ino) +{ + struct xfs_name xname = { + .name = name, + .len = strlen(name), + .type = ftype, + }; + struct xfs_parent_args *ppargs; + struct xfs_trans *tp; + struct xfs_inode *dp, *ip; + unsigned int resblks; + bool isdir; + int error; + + error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp); + if (error) + return error; + + if (!S_ISDIR(VFS_I(dp)->i_mode)) { + error = -ENOTDIR; + goto out_dp; + } + + error = -libxfs_iget(mp, NULL, child_ino, 0, &ip); + if (error) + goto out_dp; + isdir = S_ISDIR(VFS_I(ip)->i_mode); + + if (xname.type == XFS_DIR3_FT_UNKNOWN) + xname.type = libxfs_mode_to_ftype(VFS_I(ip)->i_mode); + + error = -libxfs_parent_start(mp, &ppargs); + if (error) + goto out_ip; + + resblks = libxfs_link_space_res(mp, MAXNAMELEN); + error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_link, resblks, 0, 0, + &tp); + if (error) + goto out_parent; + + libxfs_trans_ijoin(tp, dp, 0); + libxfs_trans_ijoin(tp, ip, 0); + + error = -libxfs_dir_createname(tp, dp, &xname, ip->i_ino, resblks); + if (error) + goto out_trans; + + /* bump dp's link to ip */ + libxfs_bumplink(tp, ip); + + /* bump ip's dotdot link to dp */ + if (isdir) + libxfs_bumplink(tp, dp); + + /* Replace the dotdot entry in the child directory. */ + if (isdir) { + error = -libxfs_dir_replace(tp, ip, &xfs_name_dotdot, + dp->i_ino, resblks); + if (error) + goto out_trans; + } + + error = -libxfs_parent_add(tp, ppargs, dp, &xname, ip); + if (error) + goto out_trans; + + error = -libxfs_trans_commit(tp); + goto out_parent; + +out_trans: + libxfs_trans_cancel(tp); +out_parent: + libxfs_parent_finish(mp, ppargs); +out_ip: + libxfs_irele(ip); +out_dp: + libxfs_irele(dp); + return error; +} + +static const char *ftype_map[] = { + [XFS_DIR3_FT_REG_FILE] = "reg", + [XFS_DIR3_FT_DIR] = "dir", + [XFS_DIR3_FT_CHRDEV] = "cdev", + [XFS_DIR3_FT_BLKDEV] = "bdev", + [XFS_DIR3_FT_FIFO] = "fifo", + [XFS_DIR3_FT_SOCK] = "sock", + [XFS_DIR3_FT_SYMLINK] = "symlink", + [XFS_DIR3_FT_WHT] = "whiteout", +}; + +static int +link_f( + int argc, + char **argv) +{ + xfs_ino_t child_ino = NULLFSINO; + int ftype = XFS_DIR3_FT_UNKNOWN; + unsigned int i; + int c; + int error = 0; + + while ((c = getopt(argc, argv, "i:p:t:")) != -1) { + switch (c) { + case 'i': + errno = 0; + child_ino = strtoull(optarg, NULL, 0); + if (errno == ERANGE) { + printf("%s: unknown inode number\n", optarg); + exitcode = 1; + return 0; + } + break; + case 'p': + push_cur(); + error = path_walk(optarg); + if (error) { + printf("%s: %s\n", optarg, strerror(error)); + exitcode = 1; + return 0; + } else if (iocur_top->typ != &typtab[TYP_INODE]) { + printf("%s: does not point to an inode\n", + optarg); + exitcode = 1; + return 0; + } else { + child_ino = iocur_top->ino; + } + pop_cur(); + break; + case 't': + for (i = 0; i < ARRAY_SIZE(ftype_map); i++) { + if (ftype_map[i] && + !strcmp(ftype_map[i], optarg)) { + ftype = i; + break; + } + } + if (i == ARRAY_SIZE(ftype_map)) { + printf("%s: unknown file type\n", optarg); + exitcode = 1; + return 0; + } + break; + default: + link_help(); + return 0; + } + } + + if (child_ino == NULLFSINO) { + printf("link: need to specify child via -i or -p\n"); + exitcode = 1; + return 0; + } + + if (iocur_top->typ != &typtab[TYP_INODE]) { + printf("io cursor does not point to an inode.\n"); + exitcode = 1; + return 0; + } + + if (optind + 1 != argc) { + printf("link: need directory entry name"); + exitcode = 1; + return 0; + } + + error = create_child(mp, iocur_top->ino, argv[optind], ftype, + child_ino); + if (error) { + printf("link failed: %s\n", strerror(error)); + exitcode = 1; + return 0; + } + + return 0; +} + +static struct cmdinfo link_cmd = { + .name = "link", + .cfunc = link_f, + .argmin = 0, + .argmax = -1, + .canpush = 0, + .args = "[-i ino] [-p path] [-t ftype] name", + .help = link_help, +}; + +static void +unlink_help(void) +{ + dbprintf(_( +"\n" +" Remove a directory entry from the current directory.\n" +"\n" +" Options:\n" +" name -- Remove the directory entry with this name.\n" + )); +} + +static void +droplink( + struct xfs_trans *tp, + struct xfs_inode *ip) +{ + struct inode *inode = VFS_I(ip); + + libxfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); + + if (inode->i_nlink != XFS_NLINK_PINNED) + drop_nlink(VFS_I(ip)); + + libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); +} + +static int +remove_child( + struct xfs_mount *mp, + xfs_ino_t parent_ino, + const char *name) +{ + struct xfs_name xname = { + .name = name, + .len = strlen(name), + }; + struct xfs_parent_args *ppargs; + struct xfs_trans *tp; + struct xfs_inode *dp, *ip; + xfs_ino_t child_ino; + unsigned int resblks; + int error; + + error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp); + if (error) + return error; + + if (!S_ISDIR(VFS_I(dp)->i_mode)) { + error = -ENOTDIR; + goto out_dp; + } + + error = -libxfs_dir_lookup(NULL, dp, &xname, &child_ino, NULL); + if (error) + goto out_dp; + + error = -libxfs_iget(mp, NULL, child_ino, 0, &ip); + if (error) + goto out_dp; + + error = -libxfs_parent_start(mp, &ppargs); + if (error) + goto out_ip; + + resblks = libxfs_remove_space_res(mp, MAXNAMELEN); + error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, resblks, 0, 0, + &tp); + if (error) + goto out_parent; + + libxfs_trans_ijoin(tp, dp, 0); + libxfs_trans_ijoin(tp, ip, 0); + + if (S_ISDIR(VFS_I(ip)->i_mode)) { + /* drop ip's dotdot link to dp */ + droplink(tp, dp); + } else { + libxfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); + } + + /* drop dp's link to ip */ + droplink(tp, ip); + + error = -libxfs_dir_removename(tp, dp, &xname, ip->i_ino, resblks); + if (error) + goto out_trans; + + error = -libxfs_parent_remove(tp, ppargs, dp, &xname, ip); + if (error) + goto out_trans; + + error = -libxfs_trans_commit(tp); + goto out_parent; + +out_trans: + libxfs_trans_cancel(tp); +out_parent: + libxfs_parent_finish(mp, ppargs); +out_ip: + libxfs_irele(ip); +out_dp: + libxfs_irele(dp); + return error; +} + +static int +unlink_f( + int argc, + char **argv) +{ + int c; + int error = 0; + + while ((c = getopt(argc, argv, "")) != -1) { + switch (c) { + default: + unlink_help(); + return 0; + } + } + + if (iocur_top->typ != &typtab[TYP_INODE]) { + printf("io cursor does not point to an inode.\n"); + exitcode = 1; + return 0; + } + + if (optind + 1 != argc) { + printf("unlink: need directory entry name"); + exitcode = 1; + return 0; + } + + error = remove_child(mp, iocur_top->ino, argv[optind]); + if (error) { + printf("unlink failed: %s\n", strerror(error)); + exitcode = 1; + return 0; + } + + return 0; +} + +static struct cmdinfo unlink_cmd = { + .name = "unlink", + .cfunc = unlink_f, + .argmin = 0, + .argmax = -1, + .canpush = 0, + .args = "name", + .help = unlink_help, +}; + void namei_init(void) { @@ -941,4 +1307,12 @@ namei_init(void) parent_cmd.oneline = _("list parent pointers"); add_command(&parent_cmd); + + if (expert_mode) { + link_cmd.oneline = _("create directory link"); + add_command(&link_cmd); + + unlink_cmd.oneline = _("remove directory link"); + add_command(&unlink_cmd); + } } diff --git a/include/xfs_inode.h b/include/xfs_inode.h index 088a6b34f04..496d504747c 100644 --- a/include/xfs_inode.h +++ b/include/xfs_inode.h @@ -315,6 +315,10 @@ static inline void inc_nlink(struct inode *inode) { inode->i_nlink++; } +static inline void drop_nlink(struct inode *inode) +{ + inode->i_nlink--; +} static inline bool xfs_is_reflink_inode(struct xfs_inode *ip) { diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index d5662c42f2b..49372a44029 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -135,12 +135,16 @@ #define xfs_dir_init libxfs_dir_init #define xfs_dir_ino_validate libxfs_dir_ino_validate #define xfs_dir_lookup libxfs_dir_lookup +#define xfs_dir_removename libxfs_dir_removename #define xfs_dir_replace libxfs_dir_replace #define xfs_dqblk_repair libxfs_dqblk_repair #define xfs_dquot_from_disk_ts libxfs_dquot_from_disk_ts #define xfs_dquot_verify libxfs_dquot_verify +#define xfs_bumplink libxfs_bumplink +#define xfs_droplink libxfs_droplink + #define xfs_finobt_calc_reserves libxfs_finobt_calc_reserves #define xfs_free_extent libxfs_free_extent #define xfs_free_extent_later libxfs_free_extent_later @@ -177,6 +181,7 @@ #define xfs_iread_extents libxfs_iread_extents #define xfs_irele libxfs_irele +#define xfs_link_space_res libxfs_link_space_res #define xfs_log_calc_minimum_size libxfs_log_calc_minimum_size #define xfs_log_get_max_trans_res libxfs_log_get_max_trans_res #define xfs_log_sb libxfs_log_sb @@ -187,6 +192,7 @@ #define xfs_parent_irec_from_disk libxfs_parent_irec_from_disk #define xfs_parent_irec_hashname libxfs_parent_irec_hashname #define xfs_parent_lookup libxfs_parent_lookup +#define xfs_parent_remove libxfs_parent_remove #define xfs_parent_set libxfs_parent_set #define xfs_parent_start libxfs_parent_start #define xfs_parent_hashcheck libxfs_parent_hashcheck diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8 index 937b17e79a3..638a8dc9352 100644 --- a/man/man8/xfs_db.8 +++ b/man/man8/xfs_db.8 @@ -884,6 +884,21 @@ will result in truncation and a warning will be issued. If no .I label is given, the current filesystem label is printed. .TP +.BI "link [-i " ino "] [-p " path "] [-t " ftype "] name" +In the current directory, create a directory entry with the given +.I name +pointing to a file. +The file must be specified either as a directory tree path as given by the +.I path +option; or directly as an inode number as given by the +.I ino +option. +The file type in the directory entry will be determined from the mode of the +child file unless the +.I ftype +option is given. +The file being targetted must not be on the iunlink list. +.TP .BI "log [stop | start " filename ] Start logging output to .IR filename , @@ -1035,6 +1050,11 @@ Print the timestamps in the current locale's date and time format instead of raw seconds since the Unix epoch. .RE .TP +.BI "unlink name" +In the current directory, remove a directory entry with the given +.IR name . +The file being targetted will not be put on the iunlink list. +.TP .BI "uuid [" uuid " | " generate " | " rewrite " | " restore ] Set the filesystem universally unique identifier (UUID). The filesystem UUID can be used by From patchwork Sun Dec 31 23:18:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508075 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CDA4C2C5 for ; Sun, 31 Dec 2023 23:18:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="l97yP97y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E363CC433C7; Sun, 31 Dec 2023 23:18:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064719; bh=t8z4aU5BY9zzwEaSSbnEsTYMkLWykRneMq7u0qgtBik=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=l97yP97yyPrhx9Nb29Mz2sQTYxsq2CmSqQ1b5/mSqIenqtJJY05OHkdMtASr8mD/B /mt+CvgU+Dyj67IpCu5rg2FQ8WEg71FvD6NeKzIkxjCB5kCk7q16N6duJvN42KsgT8 g4jMi6QLkTkNCOkgF2qiUhGBpuG61tKmssC7EoEgJG9qWg34HaVHshscH4hpGFl9eY exR84zuqGypA8tLrx0AtvG42CyLni02DyFFaQyKNTZaVIK2CDNnCcUZUQ5BqYLGXV8 oHzHugLrj7HVNpJif2jRNSB4VJkepCpG67Q+JjXFQQeaFg8M1XMLhaM3dGQj2VPaLa einEuzBfStBVg== Date: Sun, 31 Dec 2023 15:18:39 -0800 Subject: [PATCH 2/6] xfs: teach online scrub to find directory tree structure problems From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007459.1805996.13319683096140892679.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Create a new scrubber that detects corruptions within the directory tree structure itself. It can detect directories with multiple parents; loops within the directory tree; and directory loops not accessible from the root. Signed-off-by: Darrick J. Wong --- libfrog/scrub.c | 5 +++++ libxfs/xfs_fs.h | 3 ++- man/man2/ioctl_xfs_scrub_metadata.2 | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libfrog/scrub.c b/libfrog/scrub.c index baaa4b4d940..a2146e228f5 100644 --- a/libfrog/scrub.c +++ b/libfrog/scrub.c @@ -149,6 +149,11 @@ const struct xfrog_scrub_descr xfrog_scrubbers[XFS_SCRUB_TYPE_NR] = { .descr = "retained health records", .group = XFROG_SCRUB_GROUP_NONE, }, + [XFS_SCRUB_TYPE_DIRTREE] = { + .name = "dirtree", + .descr = "directory tree structure", + .group = XFROG_SCRUB_GROUP_INODE, + }, }; #undef DEP diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h index efa68a2d82a..48f38694f12 100644 --- a/libxfs/xfs_fs.h +++ b/libxfs/xfs_fs.h @@ -719,9 +719,10 @@ struct xfs_scrub_metadata { #define XFS_SCRUB_TYPE_QUOTACHECK 25 /* quota counters */ #define XFS_SCRUB_TYPE_NLINKS 26 /* inode link counts */ #define XFS_SCRUB_TYPE_HEALTHY 27 /* everything checked out ok */ +#define XFS_SCRUB_TYPE_DIRTREE 28 /* directory tree structure */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 28 +#define XFS_SCRUB_TYPE_NR 29 /* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1u << 0) diff --git a/man/man2/ioctl_xfs_scrub_metadata.2 b/man/man2/ioctl_xfs_scrub_metadata.2 index 75ae52bb584..44aa139b297 100644 --- a/man/man2/ioctl_xfs_scrub_metadata.2 +++ b/man/man2/ioctl_xfs_scrub_metadata.2 @@ -148,6 +148,20 @@ that points back to the subdirectory. The inode to examine can be specified in the same manner as .BR XFS_SCRUB_TYPE_INODE "." +.TP +.B XFS_SCRUB_TYPE_DIRTREE +This scrubber looks for problems in the directory tree structure such as loops +and directories accessible through more than one path. +Problems are detected by walking parent pointers upwards towards the root. +Loops are detected by comparing the parent directory at each step against the +directories already examined. +Directories with multiple paths are detected by counting the parent pointers +attached to a directory. +Non-directories do not have links pointing away from the directory tree root +and can be skipped. +The directory to examine can be specified in the same manner as +.BR XFS_SCRUB_TYPE_INODE "." + .TP .B XFS_SCRUB_TYPE_SYMLINK Examine the target of a symbolic link for obvious pathname problems. From patchwork Sun Dec 31 23:18:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508076 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BAFEAC2C5 for ; Sun, 31 Dec 2023 23:18:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="n3SRXy6T" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87651C433C8; Sun, 31 Dec 2023 23:18:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064735; bh=2LescKwlE7IzShpoJEPPigqDA5TX2y6109L0//pUMEk=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=n3SRXy6TlaTJ4AHW8H9gYwsZFOPVrQwq382iY9/FsQbyKglUzlvhOBB08rLRLuqB0 5vv9Qr47tH3kHYrLF6W//OY/Th1pJGydRmhsmUXKZ0Z85QCZ0B566WvRLJ3wFi1tso 4FB7UfXjeGaVB3r5f66SiVjuHbh08IK+UObxeSXhf9l3JCxBqIrIfVyJaeken4UUUD RxRRjpZzOIs4M3fMgRtY2p4lctAOkZzA95LIcDl4GQYFh17IGE15Uy2e3L6P2veBxU RLKQ38QD47yjIGGBqTgVHSc1A2ERMqAl4Q4WxsvEw+qzVV7fnyXbmvzZqOoE4z7ao5 IzDsBpetWzilw== Date: Sun, 31 Dec 2023 15:18:55 -0800 Subject: [PATCH 3/6] xfs: report directory tree corruption in the health information From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007473.1805996.14045845817139841397.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Report directories that are the source of corruption in the directory tree. Signed-off-by: Darrick J. Wong --- libxfs/xfs_fs.h | 1 + libxfs/xfs_health.h | 4 +++- man/man2/ioctl_xfs_bulkstat.2 | 3 +++ man/man2/ioctl_xfs_fsbulkstat.2 | 3 +++ spaceman/health.c | 4 ++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h index 48f38694f12..2499a20f5f7 100644 --- a/libxfs/xfs_fs.h +++ b/libxfs/xfs_fs.h @@ -413,6 +413,7 @@ struct xfs_bulkstat { #define XFS_BS_SICK_XATTR (1 << 5) /* extended attributes */ #define XFS_BS_SICK_SYMLINK (1 << 6) /* symbolic link remote target */ #define XFS_BS_SICK_PARENT (1 << 7) /* parent pointers */ +#define XFS_BS_SICK_DIRTREE (1 << 8) /* directory tree structure */ /* * Project quota id helpers (previously projid was 16bit only diff --git a/libxfs/xfs_health.h b/libxfs/xfs_health.h index df07c5877ba..bca1990f71d 100644 --- a/libxfs/xfs_health.h +++ b/libxfs/xfs_health.h @@ -95,6 +95,7 @@ struct xfs_da_args; /* Don't propagate sick status to ag health summary during inactivation */ #define XFS_SICK_INO_FORGET (1 << 12) +#define XFS_SICK_INO_DIRTREE (1 << 13) /* directory tree structure */ /* Primary evidence of health problems in a given group. */ #define XFS_SICK_FS_PRIMARY (XFS_SICK_FS_COUNTERS | \ @@ -125,7 +126,8 @@ struct xfs_da_args; XFS_SICK_INO_DIR | \ XFS_SICK_INO_XATTR | \ XFS_SICK_INO_SYMLINK | \ - XFS_SICK_INO_PARENT) + XFS_SICK_INO_PARENT | \ + XFS_SICK_INO_DIRTREE) #define XFS_SICK_INO_ZAPPED (XFS_SICK_INO_BMBTD_ZAPPED | \ XFS_SICK_INO_BMBTA_ZAPPED | \ diff --git a/man/man2/ioctl_xfs_bulkstat.2 b/man/man2/ioctl_xfs_bulkstat.2 index 3203ca0c5d2..b6d51aa4381 100644 --- a/man/man2/ioctl_xfs_bulkstat.2 +++ b/man/man2/ioctl_xfs_bulkstat.2 @@ -326,6 +326,9 @@ Symbolic link target. .TP .B XFS_BS_SICK_PARENT Parent pointers. +.TP +.B XFS_BS_SICK_DIRTREE +Directory is the source of corruption in the directory tree. .RE .SH ERRORS Error codes can be one of, but are not limited to, the following: diff --git a/man/man2/ioctl_xfs_fsbulkstat.2 b/man/man2/ioctl_xfs_fsbulkstat.2 index 3f059942a21..cd38d2fd6f2 100644 --- a/man/man2/ioctl_xfs_fsbulkstat.2 +++ b/man/man2/ioctl_xfs_fsbulkstat.2 @@ -239,6 +239,9 @@ Symbolic link target. .TP .B XFS_BS_SICK_PARENT Parent pointers. +.TP +.B XFS_BS_SICK_DIRTREE +Directory is the source of corruption in the directory tree. .RE .SH RETURN VALUE On error, \-1 is returned, and diff --git a/spaceman/health.c b/spaceman/health.c index ab5bc074988..8ba78152cb6 100644 --- a/spaceman/health.c +++ b/spaceman/health.c @@ -169,6 +169,10 @@ static const struct flag_map inode_flags[] = { .mask = XFS_BS_SICK_PARENT, .descr = "parent pointers", }, + { + .mask = XFS_BS_SICK_DIRTREE, + .descr = "directory tree structure", + }, {0}, }; From patchwork Sun Dec 31 23:19:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508077 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 500EDC2C0 for ; Sun, 31 Dec 2023 23:19:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="b4uVeRpc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 24963C433C7; Sun, 31 Dec 2023 23:19:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064751; bh=qIpNOiY5JJAWnd0KnHurt3zrgz3OjRPZmNvKbcAIho0=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=b4uVeRpczrcrWXxzgJQw6nzSig9zf8QtV8Gk0/BovVfx6s/TFnFTsC/MM50wMFbEo rydmTW+Vlb50VEtgyk4LdHit6twT0ZKZVg3k2ncz/GQkaBF1hfwkE/jgPa+7W50L7B 3MRGn4nT9d8sNKFXgfsuameE5GOzWuEQCf/sZelLagz3XhDX5aDdewx7+pygi7TfC+ tTrSMQqPHmztSeE301cIwR0IaAKCZV+eF81bCFzyRJAnhu2USmowRft3jS9hc+FbYZ eNRKv1lZr+U8gk9WJnG9u3oCAr9qo/ilFL7WkvuAH8XK874XEy76qczDA67TvXLimt 8Us7uCxu3bDCA== Date: Sun, 31 Dec 2023 15:19:10 -0800 Subject: [PATCH 4/6] xfs_scrub: fix erroring out of check_inode_names From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007486.1805996.10460613956850458046.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong The early exit logic in this function is a bit suboptimal -- we don't need to close the @fd if we haven't even opened it, and since all errors are fatal, we don't need to bump the progress counter. The logic in this function is about to get more involved due to the addition of the directory tree structure checker, so clean up these warts. Signed-off-by: Darrick J. Wong --- scrub/phase5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scrub/phase5.c b/scrub/phase5.c index 0df8c46e9f5..b3719627755 100644 --- a/scrub/phase5.c +++ b/scrub/phase5.c @@ -279,7 +279,7 @@ check_inode_names( if (bstat->bs_xflags & FS_XFLAG_HASATTR) { error = check_xattr_names(ctx, &dsc, handle, bstat); if (error) - goto out; + goto err; } /* @@ -295,16 +295,16 @@ check_inode_names( if (error == ESTALE) return ESTALE; str_errno(ctx, descr_render(&dsc)); - goto out; + goto err; } error = check_dirent_names(ctx, &dsc, &fd, bstat); if (error) - goto out; + goto err_fd; } -out: progress_add(1); +err_fd: if (fd >= 0) { err2 = close(fd); if (err2) @@ -312,7 +312,7 @@ check_inode_names( if (!error && err2) error = err2; } - +err: if (error) *aborted = true; if (!error && *aborted) From patchwork Sun Dec 31 23:19:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508078 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A4FAC2C5 for ; Sun, 31 Dec 2023 23:19:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LrJDYAbE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BFFB0C433C7; Sun, 31 Dec 2023 23:19:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064766; bh=+E1LVwvMboLLgfImNMwpZCESkPJXcPNsu5xm/EBWY2k=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=LrJDYAbEFZfeZ1oZBNIziTAYjlPYrF5z8V1ufXyXkGBQibWpVkvaVR9gvokIAxRp3 BbEPUAYVs+6VVAMcppJNNvMWleqm21GGf/TURxaGOJdoM+BMdwcgXBiGDFSommQTI7 8hZHMO9d4f4nNVwhF1XaMyrvbFGGt48hLnjJxC/bOhAP4lAjqt/hh3gg9FTKfAVvgh 8eqaSmK/6FNXUnG0wBqluaSxBm1I8ya9faOk+sQkXvKn28bVa4GKodBS4YfqEp2R/F 9FdnUx194g2q8cIyRqIsoy4dsf6qrU70QdymJ1F8R9i/sT8QasxG/PYcnEwozovVEe iay3iXLM6De5Q== Date: Sun, 31 Dec 2023 15:19:26 -0800 Subject: [PATCH 5/6] xfs_scrub: detect and repair directory tree corruptions From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007499.1805996.15239156766773873499.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Now that we have online fsck for directory tree structure problems, we need to find a place to call it. The scanner requires that parent pointers are enabled, that directory link counts are correct, and that every directory entry has a corresponding parent pointer. Therefore, we can only run it after phase 4 fixes every file, and phase 5 resets the link counts. In other words, we call it as part of the phase 5 file scan that we do to warn about weird looking file names. This has the added benefit that opening the directory by handle is less likely to fail if there are loops in the directory structure. For now, only plumb in enough to try to fix directory tree problems right away; the next patch will make phase 5 retry the dirloop scanner until the problems are fixed or we stop making forward progress. Signed-off-by: Darrick J. Wong --- scrub/phase5.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/scrub/phase5.c b/scrub/phase5.c index b3719627755..6c8dee66e6e 100644 --- a/scrub/phase5.c +++ b/scrub/phase5.c @@ -252,6 +252,47 @@ render_ino_from_handle( bstat->bs_gen, NULL); } +/* + * Check the directory structure for problems that could cause open_by_handle + * not to work. Returns 0 for no problems; EADDRNOTAVAIL if the there are + * problems that would prevent name checking. + */ +static int +check_dir_connection( + struct scrub_ctx *ctx, + struct descr *dsc, + const struct xfs_bulkstat *bstat) +{ + struct scrub_item sri = { }; + int error; + + /* The dirtree scrubber only works when parent pointers are enabled */ + if (!(ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT)) + return 0; + + scrub_item_init_file(&sri, bstat); + scrub_item_schedule(&sri, XFS_SCRUB_TYPE_DIRTREE); + + error = scrub_item_check_file(ctx, &sri, -1); + if (error) { + str_liberror(ctx, error, _("checking directory loops")); + return error; + } + + error = repair_file_corruption(ctx, &sri, -1); + if (error) { + str_liberror(ctx, error, _("repairing directory loops")); + return error; + } + + /* No directory tree problems? Clear this inode if it was deferred. */ + if (repair_item_count_needsrepair(&sri) == 0) + return 0; + + str_corrupt(ctx, descr_render(dsc), _("directory loop uncorrected!")); + return EADDRNOTAVAIL; +} + /* * Verify the connectivity of the directory tree. * We know that the kernel's open-by-handle function will try to reconnect @@ -275,6 +316,20 @@ check_inode_names( descr_set(&dsc, bstat); background_sleep(); + /* + * Try to fix directory loops before we have problems opening files by + * handle. + */ + if (S_ISDIR(bstat->bs_mode)) { + error = check_dir_connection(ctx, &dsc, bstat); + if (error == EADDRNOTAVAIL) { + error = 0; + goto out; + } + if (error) + goto err; + } + /* Warn about naming problems in xattrs. */ if (bstat->bs_xflags & FS_XFLAG_HASATTR) { error = check_xattr_names(ctx, &dsc, handle, bstat); @@ -315,6 +370,7 @@ check_inode_names( err: if (error) *aborted = true; +out: if (!error && *aborted) error = ECANCELED; From patchwork Sun Dec 31 23:19:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508079 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9DD9FC2CC for ; Sun, 31 Dec 2023 23:19:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="JPEldZTN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 71D08C433C8; Sun, 31 Dec 2023 23:19:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064782; bh=r1KXLIu2NvI+H91zK0uHKgyI1/Uz95foIKhiyepYGz8=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=JPEldZTN+60u2qvVnvNZErnVH9pwToehfYouW9M7+ikCtblZnjYMPHuH97Qy4c6e+ MpiZqCJUrnXWuC3reV7Mg+3I9LSO5jc6YyzTWoUEoBtBxxFIGQL9Yx+oQwd8IdB+iI jr+D3jErN6jcxxw7nm1uqmbLwhKJUrc9isqw/1nrvxTpRePr55c8Z9SlQ71M85FmeS 8qWslRREt1q0Osp6guIujc3Rk/v3+/o51+N21OPPlQlmlCPaGm8OS9VJjhw4aaZw1c 9NE/fkDJz5GJbP66KW4GQrHOIyTZGXlh9qFyL+LE4ubxMhuF8IgVhK+4rOYXDCKyOf jki5kCr0LNI/A== Date: Sun, 31 Dec 2023 15:19:41 -0800 Subject: [PATCH 6/6] xfs_scrub: defer phase5 file scans if dirloop fails From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007512.1805996.11814780916064262740.stgit@frogsfrogsfrogs> In-Reply-To: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> References: <170405007429.1805996.15935827855068032438.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong If we cannot fix dirloop problems during the initial phase 5 inode scan, defer them until later. Signed-off-by: Darrick J. Wong --- scrub/phase5.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- scrub/repair.c | 13 +++ scrub/repair.h | 2 + 3 files changed, 216 insertions(+), 14 deletions(-) diff --git a/scrub/phase5.c b/scrub/phase5.c index 6c8dee66e6e..f6c295c64ad 100644 --- a/scrub/phase5.c +++ b/scrub/phase5.c @@ -18,6 +18,8 @@ #include "libfrog/workqueue.h" #include "libfrog/fsgeom.h" #include "libfrog/scrub.h" +#include "libfrog/bitmap.h" +#include "libfrog/bulkstat.h" #include "xfs_scrub.h" #include "common.h" #include "inodes.h" @@ -29,6 +31,36 @@ /* Phase 5: Full inode scans and check directory connectivity. */ +struct ncheck_state { + struct scrub_ctx *ctx; + + /* Have we aborted this scan? */ + bool aborted; + + /* Is this the last time we're going to process deferred inodes? */ + bool last_call; + + /* Did we fix at least one thing while walking @cur->deferred? */ + bool fixed_something; + + /* Lock for this structure */ + pthread_mutex_t lock; + + /* + * Inodes that are involved with directory tree structure corruptions + * are marked here. This will be NULL until the first corruption is + * noted. + */ + struct bitmap *new_deferred; + + /* + * Inodes that we're reprocessing due to earlier directory tree + * structure corruption problems are marked here. This will be NULL + * during the first (parallel) inode scan. + */ + struct bitmap *cur_deferred; +}; + /* * Warn about problematic bytes in a directory/attribute name. That means * terminal control characters and escape sequences, since that could be used @@ -252,6 +284,26 @@ render_ino_from_handle( bstat->bs_gen, NULL); } +/* Defer this inode until later. */ +static inline int +defer_inode( + struct ncheck_state *ncs, + uint64_t ino) +{ + int error; + + pthread_mutex_lock(&ncs->lock); + if (!ncs->new_deferred) { + error = -bitmap_alloc(&ncs->new_deferred); + if (error) + goto unlock; + } + error = -bitmap_set(ncs->new_deferred, ino, 1); +unlock: + pthread_mutex_unlock(&ncs->lock); + return error; +} + /* * Check the directory structure for problems that could cause open_by_handle * not to work. Returns 0 for no problems; EADDRNOTAVAIL if the there are @@ -260,7 +312,7 @@ render_ino_from_handle( static int check_dir_connection( struct scrub_ctx *ctx, - struct descr *dsc, + struct ncheck_state *ncs, const struct xfs_bulkstat *bstat) { struct scrub_item sri = { }; @@ -279,17 +331,31 @@ check_dir_connection( return error; } - error = repair_file_corruption(ctx, &sri, -1); + if (ncs->last_call) + error = repair_file_corruption_now(ctx, &sri, -1); + else + error = repair_file_corruption(ctx, &sri, -1); if (error) { str_liberror(ctx, error, _("repairing directory loops")); return error; } /* No directory tree problems? Clear this inode if it was deferred. */ - if (repair_item_count_needsrepair(&sri) == 0) + if (repair_item_count_needsrepair(&sri) == 0) { + if (ncs->cur_deferred) + ncs->fixed_something = true; return 0; + } + + /* Don't defer anything during last call. */ + if (ncs->last_call) + return 0; + + /* Directory tree structure problems exist; do not check names yet. */ + error = defer_inode(ncs, bstat->bs_ino); + if (error) + return error; - str_corrupt(ctx, descr_render(dsc), _("directory loop uncorrected!")); return EADDRNOTAVAIL; } @@ -308,7 +374,7 @@ check_inode_names( void *arg) { DEFINE_DESCR(dsc, ctx, render_ino_from_handle); - bool *aborted = arg; + struct ncheck_state *ncs = arg; int fd = -1; int error = 0; int err2; @@ -321,7 +387,7 @@ check_inode_names( * handle. */ if (S_ISDIR(bstat->bs_mode)) { - error = check_dir_connection(ctx, &dsc, bstat); + error = check_dir_connection(ctx, ncs, bstat); if (error == EADDRNOTAVAIL) { error = 0; goto out; @@ -369,14 +435,120 @@ check_inode_names( } err: if (error) - *aborted = true; + ncs->aborted = true; out: - if (!error && *aborted) + if (!error && ncs->aborted) error = ECANCELED; return error; } +/* Try to check_inode_names on a specific inode. */ +static int +retry_deferred_inode( + struct ncheck_state *ncs, + struct xfs_handle *handle, + uint64_t ino) +{ + struct xfs_bulkstat bstat; + struct scrub_ctx *ctx = ncs->ctx; + unsigned int flags = 0; + int error; + + error = -xfrog_bulkstat_single(&ctx->mnt, ino, flags, &bstat); + if (error == ENOENT) { + /* Directory is gone, mark it clear. */ + ncs->fixed_something = true; + return 0; + } + if (error) + return error; + + handle->ha_fid.fid_ino = bstat.bs_ino; + handle->ha_fid.fid_gen = bstat.bs_gen; + + return check_inode_names(ncs->ctx, handle, &bstat, ncs); +} + +/* Try to check_inode_names on a range of inodes from the bitmap. */ +static int +retry_deferred_inode_range( + uint64_t ino, + uint64_t len, + void *arg) +{ + struct xfs_handle handle = { }; + struct ncheck_state *ncs = arg; + struct scrub_ctx *ctx = ncs->ctx; + uint64_t i; + int error; + + memcpy(&handle.ha_fsid, ctx->fshandle, sizeof(handle.ha_fsid)); + handle.ha_fid.fid_len = sizeof(xfs_fid_t) - + sizeof(handle.ha_fid.fid_len); + handle.ha_fid.fid_pad = 0; + + for (i = 0; i < len; i++) { + error = retry_deferred_inode(ncs, &handle, ino + i); + if (error) + return error; + } + + return 0; +} + +/* + * Try to check_inode_names on inodes that were deferred due to directory tree + * problems until we stop making progress. + */ +static int +retry_deferred_inodes( + struct scrub_ctx *ctx, + struct ncheck_state *ncs) +{ + int error; + + if (!ncs->new_deferred) + return 0; + + /* + * Try to repair things until we stop making forward progress or we + * don't observe any new corruptions. During the loop, we do not + * complain about the corruptions that do not get fixed. + */ + do { + ncs->cur_deferred = ncs->new_deferred; + ncs->new_deferred = NULL; + ncs->fixed_something = false; + + error = -bitmap_iterate(ncs->cur_deferred, + retry_deferred_inode_range, ncs); + if (error) + return error; + + bitmap_free(&ncs->cur_deferred); + } while (ncs->fixed_something && ncs->new_deferred); + + /* + * Try one last time to fix things, and complain about any problems + * that remain. + */ + if (!ncs->new_deferred) + return 0; + + ncs->cur_deferred = ncs->new_deferred; + ncs->new_deferred = NULL; + ncs->last_call = true; + + error = -bitmap_iterate(ncs->cur_deferred, + retry_deferred_inode_range, ncs); + if (error) + return error; + + bitmap_free(&ncs->cur_deferred); + return 0; +} + #ifndef FS_IOC_GETFSLABEL # define FSLABEL_MAX 256 # define FS_IOC_GETFSLABEL _IOR(0x94, 49, char[FSLABEL_MAX]) @@ -568,9 +740,10 @@ int phase5_func( struct scrub_ctx *ctx) { - bool aborted = false; + struct ncheck_state ncs = { .ctx = ctx }; int ret; + /* * Check and fix anything that requires a full filesystem scan. We do * this after we've checked all inodes and repaired anything that could @@ -590,14 +763,28 @@ _("Filesystem has errors, skipping connectivity checks.")); if (ret) return ret; - ret = scrub_scan_all_inodes(ctx, check_inode_names, &aborted); + pthread_mutex_init(&ncs.lock, NULL); + + ret = scrub_scan_all_inodes(ctx, check_inode_names, &ncs); if (ret) - return ret; - if (aborted) - return ECANCELED; + goto out_lock; + if (ncs.aborted) { + ret = ECANCELED; + goto out_lock; + } + + ret = retry_deferred_inodes(ctx, &ncs); + if (ret) + goto out_lock; scrub_report_preen_triggers(ctx); - return 0; +out_lock: + pthread_mutex_destroy(&ncs.lock); + if (ncs.new_deferred) + bitmap_free(&ncs.new_deferred); + if (ncs.cur_deferred) + bitmap_free(&ncs.cur_deferred); + return ret; } /* Estimate how much work we're going to do. */ diff --git a/scrub/repair.c b/scrub/repair.c index 0258210722b..4fed86134ed 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -732,6 +732,19 @@ repair_file_corruption( XRM_REPAIR_ONLY | XRM_NOPROGRESS); } +/* Repair all parts of this file or complain if we cannot. */ +int +repair_file_corruption_now( + struct scrub_ctx *ctx, + struct scrub_item *sri, + int override_fd) +{ + repair_item_boost_priorities(sri); + + return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT, + XRM_REPAIR_ONLY | XRM_NOPROGRESS | XRM_FINAL_WARNING); +} + /* * Repair everything in this filesystem object that needs it. This includes * cross-referencing and preening. diff --git a/scrub/repair.h b/scrub/repair.h index 411a379f6fa..ec4aa381a82 100644 --- a/scrub/repair.h +++ b/scrub/repair.h @@ -76,6 +76,8 @@ int action_list_process(struct scrub_ctx *ctx, struct action_list *alist, int repair_item_corruption(struct scrub_ctx *ctx, struct scrub_item *sri); int repair_file_corruption(struct scrub_ctx *ctx, struct scrub_item *sri, int override_fd); +int repair_file_corruption_now(struct scrub_ctx *ctx, struct scrub_item *sri, + int override_fd); int repair_item(struct scrub_ctx *ctx, struct scrub_item *sri, unsigned int repair_flags); int repair_item_to_action_item(struct scrub_ctx *ctx,