From patchwork Thu Feb 16 20:48:03 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: 13143832 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49838C61DA4 for ; Thu, 16 Feb 2023 20:48:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229547AbjBPUsG (ORCPT ); Thu, 16 Feb 2023 15:48:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58206 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229738AbjBPUsG (ORCPT ); Thu, 16 Feb 2023 15:48:06 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8D2ABEC for ; Thu, 16 Feb 2023 12:48:04 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 2966660C1A for ; Thu, 16 Feb 2023 20:48:04 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 88B38C433D2; Thu, 16 Feb 2023 20:48:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580483; bh=16ARWnkDXfjc7l6xgvmPkKzT+e7sXnVKcxOQM8flfQo=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=GiMIAiIHM94HcgXhb9WituaMcpIzyMRR0KD9/G0wKCtrhS9LHZpRgJW6nhn5OcfXD d2njgHykaqqqedO/Wx8oCfk6Nj741xjEL1SCcYXvZ3CDc8IrqMz7FI2amnrrDGItyi o1UV9Rntzfu9EOdQYil5aYuE4oYD3wJXalzj1vRCXCvFRGSSk7dEA0G+jklE0UuiD8 0JYbiIkVsDPzRnp8r1eNhIMftLQzH5vXkNnsvHwxgIJKFbPldjSZiZSOyUAkIyF4oq AjlgbYwdusOXt08VNnuXhLw1Qnt173MXxi+hU978OjPCJl9e5QWFRyI3Ssy+Z9TnyD 4obgAqhf7cLsw== Date: Thu, 16 Feb 2023 12:48:03 -0800 Subject: [PATCH 1/7] xfs: pass directory offsets as part of the dirent hook data From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874482.3474898.1437280787820551301.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong When we're calling the dirent hooks about a directory entry update, be sure to pass the diroffset associated with the change. We're going to need this in the next patch. Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 40 +++++++++++++++++++++++++--------------- fs/xfs/xfs_inode.h | 5 +++-- fs/xfs/xfs_symlink.c | 2 +- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 8ad646beee75..ce1f6d03c3a9 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1042,7 +1042,8 @@ xfs_dirent_child_delta( struct xfs_inode *dp, struct xfs_inode *ip, int delta, - struct xfs_name *name) + struct xfs_name *name, + unsigned int diroffset) { if (xfs_hooks_switched_on(&xfs_dirents_hooks_switch)) { struct xfs_dirent_update_params p = { @@ -1050,6 +1051,7 @@ xfs_dirent_child_delta( .ip = ip, .delta = delta, .name = name, + .diroffset = diroffset, }; struct xfs_mount *mp = ip->i_mount; @@ -1210,7 +1212,7 @@ xfs_create( * Create ip with a reference from dp, and add '.' and '..' references * if it's a directory. */ - xfs_dirent_child_delta(dp, ip, 1, name); + xfs_dirent_child_delta(dp, ip, 1, name, diroffset); if (is_dir) { xfs_dirent_self_delta(ip, 1); xfs_dirent_backref_delta(dp, ip, 1); @@ -1481,7 +1483,7 @@ xfs_link( goto error_return; } - xfs_dirent_child_delta(tdp, sip, 1, target_name); + xfs_dirent_child_delta(tdp, sip, 1, target_name, diroffset); /* * If this is a synchronous mount, make sure that the @@ -2757,7 +2759,7 @@ xfs_remove( * Drop the link from dp to ip, and if ip was a directory, remove the * '.' and '..' references since we freed the directory. */ - xfs_dirent_child_delta(dp, ip, -1, name); + xfs_dirent_child_delta(dp, ip, -1, name, dir_offset); if (S_ISDIR(VFS_I(ip)->i_mode)) { xfs_dirent_backref_delta(dp, ip, -1); xfs_dirent_self_delta(ip, -1); @@ -2873,18 +2875,22 @@ static inline void xfs_exchange_call_nlink_hooks( struct xfs_inode *src_dp, struct xfs_name *src_name, + xfs_dir2_dataptr_t src_diroffset, struct xfs_inode *src_ip, struct xfs_inode *target_dp, struct xfs_name *target_name, + xfs_dir2_dataptr_t target_diroffset, struct xfs_inode *target_ip) { /* Exchange files in the source directory. */ - xfs_dirent_child_delta(src_dp, src_ip, -1, src_name); - xfs_dirent_child_delta(src_dp, target_ip, 1, src_name); + xfs_dirent_child_delta(src_dp, src_ip, -1, src_name, src_diroffset); + xfs_dirent_child_delta(src_dp, target_ip, 1, src_name, src_diroffset); /* Exchange files in the target directory. */ - xfs_dirent_child_delta(target_dp, target_ip, -1, target_name); - xfs_dirent_child_delta(target_dp, src_ip, 1, target_name); + xfs_dirent_child_delta(target_dp, target_ip, -1, target_name, + target_diroffset); + xfs_dirent_child_delta(target_dp, src_ip, 1, target_name, + target_diroffset); /* If the source file is a dir, update its dotdot entry. */ if (S_ISDIR(VFS_I(src_ip)->i_mode)) { @@ -2903,9 +2909,11 @@ static inline void xfs_rename_call_nlink_hooks( struct xfs_inode *src_dp, struct xfs_name *src_name, + xfs_dir2_dataptr_t src_diroffset, struct xfs_inode *src_ip, struct xfs_inode *target_dp, struct xfs_name *target_name, + xfs_dir2_dataptr_t target_diroffset, struct xfs_inode *target_ip, struct xfs_inode *wip) { @@ -2914,16 +2922,16 @@ xfs_rename_call_nlink_hooks( * move the source file to the target directory. */ if (target_ip) - xfs_dirent_child_delta(target_dp, target_ip, -1, target_name); - xfs_dirent_child_delta(target_dp, src_ip, 1, target_name); + xfs_dirent_child_delta(target_dp, target_ip, -1, target_name, target_diroffset); + xfs_dirent_child_delta(target_dp, src_ip, 1, target_name, target_diroffset); /* * Remove the source file from the source directory, and possibly move * the whiteout file into its place. */ - xfs_dirent_child_delta(src_dp, src_ip, -1, src_name); + xfs_dirent_child_delta(src_dp, src_ip, -1, src_name, src_diroffset); if (wip) - xfs_dirent_child_delta(src_dp, wip, 1, src_name); + xfs_dirent_child_delta(src_dp, wip, 1, src_name, src_diroffset); /* If the source file is a dir, update its dotdot entry. */ if (S_ISDIR(VFS_I(src_ip)->i_mode)) { @@ -3080,7 +3088,8 @@ xfs_cross_rename( xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); if (xfs_hooks_switched_on(&xfs_dirents_hooks_switch)) - xfs_exchange_call_nlink_hooks(dp1, name1, ip1, dp2, name2, ip2); + xfs_exchange_call_nlink_hooks(dp1, name1, old_diroffset, ip1, + dp2, name2, new_diroffset, ip2); return xfs_finish_rename(tp); @@ -3560,8 +3569,9 @@ xfs_rename( xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); if (xfs_hooks_switched_on(&xfs_dirents_hooks_switch)) - xfs_rename_call_nlink_hooks(src_dp, src_name, src_ip, - target_dp, target_name, target_ip, wip); + xfs_rename_call_nlink_hooks(src_dp, src_name, old_diroffset, + src_ip, target_dp, target_name, new_diroffset, + target_ip, wip); error = xfs_finish_rename(tp); diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 94a1490fb7b0..403b0f4cb5c0 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -592,12 +592,13 @@ struct xfs_dirent_update_params { const struct xfs_inode *dp; const struct xfs_inode *ip; const struct xfs_name *name; + unsigned int diroffset; int delta; }; #ifdef CONFIG_XFS_LIVE_HOOKS void xfs_dirent_child_delta(struct xfs_inode *dp, struct xfs_inode *ip, - int delta, struct xfs_name *name); + int delta, struct xfs_name *name, unsigned int diroffset); struct xfs_dirent_hook { struct xfs_hook delta_hook; @@ -610,7 +611,7 @@ int xfs_dirent_hook_add(struct xfs_mount *mp, struct xfs_dirent_hook *hook); void xfs_dirent_hook_del(struct xfs_mount *mp, struct xfs_dirent_hook *hook); #else -# define xfs_dirent_child_delta(dp, ip, delta, name) ((void)0) +# define xfs_dirent_child_delta(dp, ip, delta, name, doff) ((void)0) #endif /* CONFIG_XFS_LIVE_HOOKS */ #endif /* __XFS_INODE_H__ */ diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 77427a50a760..fdfaab466f5d 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -354,7 +354,7 @@ xfs_symlink( goto out_trans_cancel; } - xfs_dirent_child_delta(dp, ip, 1, link_name); + xfs_dirent_child_delta(dp, ip, 1, link_name, diroffset); /* * If this is a synchronous mount, make sure that the From patchwork Thu Feb 16 20:48:18 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: 13143833 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 03546C636CC for ; Thu, 16 Feb 2023 20:48:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229739AbjBPUsZ (ORCPT ); Thu, 16 Feb 2023 15:48:25 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58276 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229738AbjBPUsY (ORCPT ); Thu, 16 Feb 2023 15:48:24 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D6F8F4C3C5 for ; Thu, 16 Feb 2023 12:48:23 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 80CAEB82962 for ; Thu, 16 Feb 2023 20:48:20 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3DC17C433D2; Thu, 16 Feb 2023 20:48:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580499; bh=MrrrzlwG88p1IOR3pvSvT9LzzpyZ+rhUWCqizw4i1rU=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=D+M7gV/2MAH1/RywJSRYrE29I3OgXMRDhrrgPpyxjJod8c3qAWH+2eLvdickbHXHh lZeTsnaM0/pL0XrCMu5h61kWXJrxl5Nv5xixzgRPEK/qdD+PTWACItkZAV+aDDsUZH +X6UgJx0ovlDwF+rk8AqIlFh6vrHXzid+IRF+iN5ua+YQjzGLtTzRFWWf8SFxE5TTS vnJ0GtO0Zc1rwJZjwV2lTTPdV/KokUMxujWMeAsLjPI0xiPAJcDjd93Ip8AhMZCj6+ E/ekRC/NkfwkUjSGIhpbHRsxiR9VkOSFVf1Vw3uD7zeddTwZds7vT1LA3T1swoVl4n YIBp7E8S3uHzA== Date: Thu, 16 Feb 2023 12:48:18 -0800 Subject: [PATCH 2/7] xfs: pass diroffset back from xchk_dir_lookup From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874496.3474898.6926525132054977375.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong Pass directory offsets back from xchk_dir_lookup so that we can compare things in scrub. Signed-off-by: Darrick J. Wong --- fs/xfs/scrub/dir.c | 2 +- fs/xfs/scrub/readdir.c | 12 ++++++++++-- fs/xfs/scrub/readdir.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index 46080134b408..06783e4b95ad 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -105,7 +105,7 @@ xchk_dir_actor( } /* Verify that we can look up this name by hash. */ - error = xchk_dir_lookup(sc, dp, name, &lookup_ino); + error = xchk_dir_lookup(sc, dp, name, &lookup_ino, NULL); /* ENOENT means the hash lookup failed and the dir is corrupt */ if (error == -ENOENT) error = -EFSCORRUPTED; diff --git a/fs/xfs/scrub/readdir.c b/fs/xfs/scrub/readdir.c index 7d1695e98cc6..0a53438975c3 100644 --- a/fs/xfs/scrub/readdir.c +++ b/fs/xfs/scrub/readdir.c @@ -314,7 +314,8 @@ xchk_dir_lookup( struct xfs_scrub *sc, struct xfs_inode *dp, const struct xfs_name *name, - xfs_ino_t *ino) + xfs_ino_t *ino, + xfs_dir2_dataptr_t *diroffsetp) { struct xfs_da_args args = { .dp = dp, @@ -326,10 +327,14 @@ xchk_dir_lookup( .hashval = xfs_dir2_hashname(dp->i_mount, name), .whichfork = XFS_DATA_FORK, .op_flags = XFS_DA_OP_OKNOENT, + .offset = XFS_DIR2_NULL_DATAPTR, }; bool isblock, isleaf; int error; + if (diroffsetp) + *diroffsetp = XFS_DIR2_NULL_DATAPTR; + if (xfs_is_shutdown(dp->i_mount)) return -EIO; @@ -369,7 +374,10 @@ xchk_dir_lookup( out_check_rval: if (error == -EEXIST) error = 0; - if (!error) + if (!error) { *ino = args.inumber; + if (diroffsetp) + *diroffsetp = args.offset; + } return error; } diff --git a/fs/xfs/scrub/readdir.h b/fs/xfs/scrub/readdir.h index 7272f3bd28b4..1a18bb59adb2 100644 --- a/fs/xfs/scrub/readdir.h +++ b/fs/xfs/scrub/readdir.h @@ -14,6 +14,7 @@ int xchk_dir_walk(struct xfs_scrub *sc, struct xfs_inode *dp, xchk_dirent_fn dirent_fn, void *priv); int xchk_dir_lookup(struct xfs_scrub *sc, struct xfs_inode *dp, - const struct xfs_name *name, xfs_ino_t *ino); + const struct xfs_name *name, xfs_ino_t *ino, + xfs_dir2_dataptr_t *diroffsetp); #endif /* __XFS_SCRUB_READDIR_H__ */ From patchwork Thu Feb 16 20:48:34 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: 13143834 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3B277C636CC for ; Thu, 16 Feb 2023 20:48:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229738AbjBPUsj (ORCPT ); Thu, 16 Feb 2023 15:48:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58320 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229606AbjBPUsi (ORCPT ); Thu, 16 Feb 2023 15:48:38 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8D4F14BEB7 for ; Thu, 16 Feb 2023 12:48:37 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 33E56B82958 for ; Thu, 16 Feb 2023 20:48:36 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id CAFA5C4339B; Thu, 16 Feb 2023 20:48:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580514; bh=fFk7AFjlFMGkKt8LG1Sl8kdJ073CtX6H+4NMllyuOjY=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=sYeBFHVNRwtNU+LpZlxNkCkoAeHPF+5qOu90aoPH3gaSm1/8jeSgAqImS3r2Kzvji ksqrUGUzdk6HqoALsHI+tJ3FWmfa030Z9gTWch9t2yEKEidAfCT+pUI11V22KgwFbb qr0TbxU5NRckcPPr4yyHoTw4pZmxezMiHLIddfYIl7917nuXtXcsqZW4RZEPUZfY7/ 87FqMl70CxG7h+utyhKZZWZOpWYksm0ZoEyw8CnVQrp1oAv0yTUqAB6RnsbthmiD7D B2ov/q6XSxVcYCWIDsbCxNAveCbLzlivbyyFNE+teEEYsRAkTY6wYkZp7pIY27evJp oLT942LmlRCqA== Date: Thu, 16 Feb 2023 12:48:34 -0800 Subject: [PATCH 3/7] xfs: shorten parent pointer function names From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874511.3474898.14887574406202874361.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong Shorten the function names and add brief comments to each, outlining what they're supposed to be doing. Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_parent.c | 18 ++++++++++++------ fs/xfs/libxfs/xfs_parent.h | 24 ++++++++++++------------ fs/xfs/xfs_inode.c | 16 ++++++++-------- fs/xfs/xfs_symlink.c | 2 +- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c index 179b9bebaf25..ec2bff195773 100644 --- a/fs/xfs/libxfs/xfs_parent.c +++ b/fs/xfs/libxfs/xfs_parent.c @@ -135,6 +135,10 @@ xfs_parent_irec_from_disk( memset(&irec->p_name[valuelen], 0, sizeof(irec->p_name) - valuelen); } +/* + * Allocate memory to control a logged parent pointer update as part of a + * dirent operation. + */ int __xfs_parent_init( struct xfs_mount *mp, @@ -170,12 +174,13 @@ __xfs_parent_init( return 0; } +/* Add a parent pointer to reflect a dirent addition. */ int -xfs_parent_defer_add( +xfs_parent_add( struct xfs_trans *tp, struct xfs_parent_defer *parent, struct xfs_inode *dp, - struct xfs_name *parent_name, + const struct xfs_name *parent_name, xfs_dir2_dataptr_t diroffset, struct xfs_inode *child) { @@ -194,8 +199,9 @@ xfs_parent_defer_add( return xfs_attr_defer_add(args); } +/* Remove a parent pointer to reflect a dirent removal. */ int -xfs_parent_defer_remove( +xfs_parent_remove( struct xfs_trans *tp, struct xfs_inode *dp, struct xfs_parent_defer *parent, @@ -211,14 +217,14 @@ xfs_parent_defer_remove( return xfs_attr_defer_remove(args); } - +/* Replace one parent pointer with another to reflect a rename. */ int -xfs_parent_defer_replace( +xfs_parent_replace( struct xfs_trans *tp, struct xfs_parent_defer *new_parent, struct xfs_inode *old_dp, xfs_dir2_dataptr_t old_diroffset, - struct xfs_name *parent_name, + const struct xfs_name *parent_name, struct xfs_inode *new_dp, xfs_dir2_dataptr_t new_diroffset, struct xfs_inode *child) diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h index f4f5887d1133..35854e968f1d 100644 --- a/fs/xfs/libxfs/xfs_parent.h +++ b/fs/xfs/libxfs/xfs_parent.h @@ -49,8 +49,9 @@ struct xfs_parent_defer { * Parent pointer attribute prototypes */ void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec, - struct xfs_inode *ip, - uint32_t p_diroffset); + struct xfs_inode *ip, uint32_t p_diroffset); +void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec, + struct xfs_parent_name_rec *rec); int __xfs_parent_init(struct xfs_mount *mp, bool grab_log, struct xfs_parent_defer **parentp); @@ -78,18 +79,17 @@ xfs_parent_start_locked( return 0; } -int xfs_parent_defer_add(struct xfs_trans *tp, struct xfs_parent_defer *parent, - struct xfs_inode *dp, struct xfs_name *parent_name, - xfs_dir2_dataptr_t diroffset, struct xfs_inode *child); -int xfs_parent_defer_replace(struct xfs_trans *tp, +int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent, + struct xfs_inode *dp, const struct xfs_name *parent_name, + xfs_dir2_dataptr_t diroffset, struct xfs_inode *child); +int xfs_parent_replace(struct xfs_trans *tp, struct xfs_parent_defer *new_parent, struct xfs_inode *old_dp, - xfs_dir2_dataptr_t old_diroffset, struct xfs_name *parent_name, - struct xfs_inode *new_ip, xfs_dir2_dataptr_t new_diroffset, + xfs_dir2_dataptr_t old_diroffset, + const struct xfs_name *parent_name, struct xfs_inode *new_ip, + xfs_dir2_dataptr_t new_diroffset, struct xfs_inode *child); +int xfs_parent_remove(struct xfs_trans *tp, struct xfs_inode *dp, + struct xfs_parent_defer *parent, xfs_dir2_dataptr_t diroffset, struct xfs_inode *child); -int xfs_parent_defer_remove(struct xfs_trans *tp, struct xfs_inode *dp, - struct xfs_parent_defer *parent, - xfs_dir2_dataptr_t diroffset, - struct xfs_inode *child); void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index ce1f6d03c3a9..09b0ac6b99cb 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1202,7 +1202,7 @@ xfs_create( * the parent information now. */ if (parent) { - error = xfs_parent_defer_add(tp, parent, dp, name, diroffset, + error = xfs_parent_add(tp, parent, dp, name, diroffset, ip); if (error) goto out_trans_cancel; @@ -1477,7 +1477,7 @@ xfs_link( * the parent to the inode. */ if (parent) { - error = xfs_parent_defer_add(tp, parent, tdp, target_name, + error = xfs_parent_add(tp, parent, tdp, target_name, diroffset, sip); if (error) goto error_return; @@ -2750,7 +2750,7 @@ xfs_remove( } if (parent) { - error = xfs_parent_defer_remove(tp, dp, parent, dir_offset, ip); + error = xfs_parent_remove(tp, dp, parent, dir_offset, ip); if (error) goto out_trans_cancel; } @@ -3061,12 +3061,12 @@ xfs_cross_rename( } if (xfs_has_parent(mp)) { - error = xfs_parent_defer_replace(tp, ip1_pptr, dp1, + error = xfs_parent_replace(tp, ip1_pptr, dp1, old_diroffset, name2, dp2, new_diroffset, ip1); if (error) goto out_trans_abort; - error = xfs_parent_defer_replace(tp, ip2_pptr, dp2, + error = xfs_parent_replace(tp, ip2_pptr, dp2, new_diroffset, name1, dp1, old_diroffset, ip2); if (error) goto out_trans_abort; @@ -3540,7 +3540,7 @@ xfs_rename( goto out_trans_cancel; if (wip_pptr) { - error = xfs_parent_defer_add(tp, wip_pptr, + error = xfs_parent_add(tp, wip_pptr, src_dp, src_name, old_diroffset, wip); if (error) @@ -3548,7 +3548,7 @@ xfs_rename( } if (src_ip_pptr) { - error = xfs_parent_defer_replace(tp, src_ip_pptr, src_dp, + error = xfs_parent_replace(tp, src_ip_pptr, src_dp, old_diroffset, target_name, target_dp, new_diroffset, src_ip); if (error) @@ -3556,7 +3556,7 @@ xfs_rename( } if (tgt_ip_pptr) { - error = xfs_parent_defer_remove(tp, target_dp, + error = xfs_parent_remove(tp, target_dp, tgt_ip_pptr, new_diroffset, target_ip); if (error) diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index fdfaab466f5d..63e68e832551 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -348,7 +348,7 @@ xfs_symlink( xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); if (parent) { - error = xfs_parent_defer_add(tp, parent, dp, link_name, + error = xfs_parent_add(tp, parent, dp, link_name, diroffset, ip); if (error) goto out_trans_cancel; From patchwork Thu Feb 16 20:48:50 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: 13143835 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E12ECC636CC for ; Thu, 16 Feb 2023 20:48:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229606AbjBPUsw (ORCPT ); Thu, 16 Feb 2023 15:48:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229532AbjBPUsw (ORCPT ); Thu, 16 Feb 2023 15:48:52 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 77AD74BEA8 for ; Thu, 16 Feb 2023 12:48:51 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 1260360AB9 for ; Thu, 16 Feb 2023 20:48:51 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6E9F1C433EF; Thu, 16 Feb 2023 20:48:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580530; bh=fwUeCcHM7OWq2avE8NlWhTsGQ/a04yZKa6M2Ar/34H4=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=BxUPFNEw2usiFfGsFIQQCTxaAwZHHNSYbe3/7Qb/M9uC1Sjjw9+yNakCWuJePYr0t tf88TB8aNGXcPNsflkzMXMPmPWeDPnc1wNBD1bLU55YCC8KyjtQyVL3kpJ2LQ5kfn9 su8JOpKBO3a8JE+eKujB1x/5jVPZbR5Y+iWHFDz0FJoElZI/u+Fdwyqyck4pVMIF7E P5rDcWM8BWD5m0EXKQ3+2E7E5wFoABD103cdafx0+z47otj1htmyYrS74kj5faqi9p WKSKZqoc62Vrn0VtSppkrTjoT0prq4/yAclyAoN/iF3sRRPDMMQEcCZzwaR4ITVrkN 19gnEUswZOKcw== Date: Thu, 16 Feb 2023 12:48:50 -0800 Subject: [PATCH 4/7] xfs: rearrange bits of the parent pointer apis for fsck From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874525.3474898.4454796413862072546.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong Rearrange parts of this thing in preparation for fsck code. Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_da_format.h | 11 +++++++++++ fs/xfs/libxfs/xfs_parent.c | 29 ++++++++++++++++++++++++++++- fs/xfs/libxfs/xfs_parent.h | 6 ++---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h index 2db1cf97b2c8..c07b8166e8ff 100644 --- a/fs/xfs/libxfs/xfs_da_format.h +++ b/fs/xfs/libxfs/xfs_da_format.h @@ -159,6 +159,17 @@ struct xfs_da3_intnode { #define XFS_DIR3_FT_MAX 9 +#define XFS_DIR3_FTYPE_STR \ + { XFS_DIR3_FT_UNKNOWN, "unknown" }, \ + { XFS_DIR3_FT_REG_FILE, "file" }, \ + { XFS_DIR3_FT_DIR, "directory" }, \ + { XFS_DIR3_FT_CHRDEV, "char" }, \ + { XFS_DIR3_FT_BLKDEV, "block" }, \ + { XFS_DIR3_FT_FIFO, "fifo" }, \ + { XFS_DIR3_FT_SOCK, "sock" }, \ + { XFS_DIR3_FT_SYMLINK, "symlink" }, \ + { XFS_DIR3_FT_WHT, "whiteout" } + /* * Byte offset in data block and shortform entry. */ diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c index ec2bff195773..fe6d4d1a7d57 100644 --- a/fs/xfs/libxfs/xfs_parent.c +++ b/fs/xfs/libxfs/xfs_parent.c @@ -91,7 +91,7 @@ xfs_parent_valuecheck( } /* Initializes a xfs_parent_name_rec to be stored as an attribute name */ -void +static inline void xfs_init_parent_name_rec( struct xfs_parent_name_rec *rec, struct xfs_inode *ip, @@ -135,6 +135,33 @@ xfs_parent_irec_from_disk( memset(&irec->p_name[valuelen], 0, sizeof(irec->p_name) - valuelen); } +/* + * Convert an incore parent_name record to its ondisk format. If @value or + * @valuelen are NULL, they will not be written to. + */ +void +xfs_parent_irec_to_disk( + struct xfs_parent_name_rec *rec, + void *value, + int *valuelen, + const struct xfs_parent_name_irec *irec) +{ + rec->p_ino = cpu_to_be64(irec->p_ino); + rec->p_gen = cpu_to_be32(irec->p_gen); + rec->p_diroffset = cpu_to_be32(irec->p_diroffset); + + if (valuelen) { + ASSERT(*valuelen > 0); + ASSERT(*valuelen >= irec->p_namelen); + ASSERT(*valuelen < MAXNAMELEN); + + *valuelen = irec->p_namelen; + } + + if (value) + memcpy(value, irec->p_name, irec->p_namelen); +} + /* * Allocate memory to control a logged parent pointer update as part of a * dirent operation. diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h index 35854e968f1d..4eb92fb4b11b 100644 --- a/fs/xfs/libxfs/xfs_parent.h +++ b/fs/xfs/libxfs/xfs_parent.h @@ -33,6 +33,8 @@ struct xfs_parent_name_irec { void xfs_parent_irec_from_disk(struct xfs_parent_name_irec *irec, const struct xfs_parent_name_rec *rec, const void *value, int valuelen); +void xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec, void *value, + int *valuelen, const struct xfs_parent_name_irec *irec); /* * Dynamically allocd structure used to wrap the needed data to pass around @@ -48,10 +50,6 @@ struct xfs_parent_defer { /* * Parent pointer attribute prototypes */ -void xfs_init_parent_name_rec(struct xfs_parent_name_rec *rec, - struct xfs_inode *ip, uint32_t p_diroffset); -void xfs_init_parent_name_irec(struct xfs_parent_name_irec *irec, - struct xfs_parent_name_rec *rec); int __xfs_parent_init(struct xfs_mount *mp, bool grab_log, struct xfs_parent_defer **parentp); From patchwork Thu Feb 16 20:49:05 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: 13143841 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8B3E5C636CC for ; Thu, 16 Feb 2023 20:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229660AbjBPUtN (ORCPT ); Thu, 16 Feb 2023 15:49:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58458 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229532AbjBPUtM (ORCPT ); Thu, 16 Feb 2023 15:49:12 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [IPv6:2604:1380:4601:e00::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07A0E4BEA8 for ; Thu, 16 Feb 2023 12:49:09 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 85FA1B829AB for ; Thu, 16 Feb 2023 20:49:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 198E2C433D2; Thu, 16 Feb 2023 20:49:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580546; bh=X9PXrtj8hR+byid9VmjGsqDgjdirDc7tNfm/3pOXrQM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=dxkiU/I3wU2Dz5WKUhA6DNW9prxnAhqObVDKH+jZqsYxLjtbD2pg0qLlRsAA3uH0x NRERLsDFrlXJIjUUOXE2+MyubOPbkGEniYVDJpOfw9AZ2+9V7wQ3d2kgjU/NqZLLB5 S5L2/NDAPopsb+tr3lItb0jyOqPqmXTnY2nDgLf1rqUad4xU2w1PQJW0ALKUMcm/pM cBgCZnkBpingwGPt82/yXTzdESpppj+FGJKKlBH7XHQvjpbFzPDIOfTSGIpRZfphgD BgL1yEXLE5DKh7UmNfAKfF+qhrxumbcuZDdDYmS6f/NQobk520kYQWOPJGdbNNoy7n ZzhGxxCHjA6dw== Date: Thu, 16 Feb 2023 12:49:05 -0800 Subject: [PATCH 5/7] xfs: reconstruct directories from parent pointers From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874540.3474898.6037173918747447444.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong Use the filesystem scanning infrastructure to walk the filesystem looking for parent pointers and child dirents that reference the directory that we're rebuilding. Signed-off-by: Darrick J. Wong --- fs/xfs/Makefile | 1 fs/xfs/scrub/common.c | 15 + fs/xfs/scrub/common.h | 28 + fs/xfs/scrub/dir.c | 9 fs/xfs/scrub/dir_repair.c | 940 +++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 16 + fs/xfs/scrub/scrub.c | 2 fs/xfs/scrub/tempfile.c | 42 ++ fs/xfs/scrub/tempfile.h | 2 fs/xfs/scrub/trace.c | 1 fs/xfs/scrub/trace.h | 67 +++ 11 files changed, 1122 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/dir_repair.c diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index 6a30b145491d..a32f6da27a86 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -178,6 +178,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ agheader_repair.o \ + dir_repair.o \ repair.o \ tempfile.o \ xfblob.o \ diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 2874da088e8d..17a9bc610a76 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -551,6 +551,21 @@ xchk_ag_init( /* Per-scrubber setup functions */ +void +xchk_trans_cancel( + struct xfs_scrub *sc) +{ + xfs_trans_cancel(sc->tp); + sc->tp = NULL; +} + +int +xchk_trans_alloc_empty( + struct xfs_scrub *sc) +{ + return xfs_trans_alloc_empty(sc->mp, &sc->tp); +} + /* * Grab an empty transaction so that we can re-grab locked buffers if * one of our btrees turns out to be cyclic. diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index 423a98c39fb6..7720982adfc6 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -31,6 +31,9 @@ xchk_should_terminate( return false; } +void xchk_trans_cancel(struct xfs_scrub *sc); +int xchk_trans_alloc_empty(struct xfs_scrub *sc); + int xchk_trans_alloc(struct xfs_scrub *sc, uint resblks); bool xchk_process_error(struct xfs_scrub *sc, xfs_agnumber_t agno, xfs_agblock_t bno, int *error); @@ -159,4 +162,29 @@ void xchk_start_reaping(struct xfs_scrub *sc); void xchk_fshooks_enable(struct xfs_scrub *sc, unsigned int scrub_fshooks); +#ifdef CONFIG_XFS_ONLINE_REPAIR +/* Decide if a repair is required. */ +static inline bool xchk_needs_repair(const struct xfs_scrub_metadata *sm) +{ + return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | + XFS_SCRUB_OFLAG_XCORRUPT | + XFS_SCRUB_OFLAG_PREEN); +} + +/* + * "Should we prepare for a repair?" + * + * Return true if the caller permits us to repair metadata and we're not + * setting up for a post-repair evaluation. + */ +static inline bool xchk_could_repair(const struct xfs_scrub *sc) +{ + return (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) && + !(sc->flags & XREP_ALREADY_FIXED); +} +#else +# define xchk_needs_repair(sc) (false) +# define xchk_could_repair(sc) (false) +#endif /* CONFIG_XFS_ONLINE_REPAIR */ + #endif /* __XFS_SCRUB_COMMON_H__ */ diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index 06783e4b95ad..d720f1e143dd 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -19,12 +19,21 @@ #include "scrub/common.h" #include "scrub/dabtree.h" #include "scrub/readdir.h" +#include "scrub/repair.h" /* Set us up to scrub directories. */ int xchk_setup_directory( struct xfs_scrub *sc) { + int error; + + if (xchk_could_repair(sc)) { + error = xrep_setup_directory(sc); + if (error) + return error; + } + return xchk_setup_inode_contents(sc, 0); } diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c new file mode 100644 index 000000000000..a6576a29e784 --- /dev/null +++ b/fs/xfs/scrub/dir_repair.c @@ -0,0 +1,940 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_defer.h" +#include "xfs_bit.h" +#include "xfs_log_format.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_da_format.h" +#include "xfs_da_btree.h" +#include "xfs_dir2.h" +#include "xfs_dir2_priv.h" +#include "xfs_bmap.h" +#include "xfs_quota.h" +#include "xfs_bmap_btree.h" +#include "xfs_trans_space.h" +#include "xfs_bmap_util.h" +#include "xfs_attr.h" +#include "xfs_parent.h" +#include "scrub/xfs_scrub.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/trace.h" +#include "scrub/repair.h" +#include "scrub/tempfile.h" +#include "scrub/iscan.h" +#include "scrub/readdir.h" +#include "scrub/listxattr.h" +#include "scrub/xfile.h" +#include "scrub/xfarray.h" +#include "scrub/xfblob.h" + +/* + * Directory Repairs + * ================= + * + * Reconstruct a directory by visiting each parent pointer of each file in the + * filesystem and translating the relevant pptrs into dirents. Translation + * occurs by adding new dirents to a temporary directory, which formats the + * ondisk directory blocks. In the final version of this code, we'll use the + * atomic extent swap code to exchange the entire directory structure of the + * file being repaired and the temporary, but for this PoC we omit the commit + * to reduce the amount of code that has to be ported. + * + * Because we have to scan the entire filesystem, the next patch introduces the + * inode scan and live update hooks so that the rebuilder can be kept aware of + * filesystem updates being made to this directory by other threads. Directory + * entry translation therefore requires two steps to avoid problems with lock + * contention and to keep ondisk tempdir updates out of the hook path. + * + * Every time the filesystem scanner or the live update hook code encounter a + * directory operation relevant to this rebuilder, they will write a record of + * the createname/removename operation to an xfarray. Dirent names are stored + * in an xfblob structure. At opportune times, these stashed updates will be + * read from the xfarray and committed (individually) to the temporary + * directory. + * + * When the filesystem scan is complete, we relock both the directory and the + * tempdir, and finish any stashed operations. At that point, we are + * theoretically ready to exchange the directory data fork mappings. This + * cannot happen until two patchsets get merged: the first allows callers to + * specify the owning inode number explicitly; and the second is the atomic + * extent swap series. + * + * For now we'll simply compare the two directories and complain about + * discrepancies. + */ + +/* Maximum memory usage for the tempdir log, in bytes. */ +#define MAX_DIRENT_STASH_SIZE (32ULL << 10) + +/* Create a dirent in the tempdir. */ +#define XREP_DIRENT_ADD (1) + +/* Remove a dirent from the tempdir. */ +#define XREP_DIRENT_REMOVE (2) + +/* A stashed dirent update. */ +struct xrep_dirent { + /* Cookie for retrieval of the dirent name. */ + xfblob_cookie name_cookie; + + /* Child inode number. */ + xfs_ino_t ino; + + /* Directory offset that we want. We're not going to get it. */ + xfs_dir2_dataptr_t diroffset; + + /* Length of the dirent name. */ + uint8_t namelen; + + /* File type of the dirent. */ + uint8_t ftype; + + /* XREP_DIRENT_{ADD,REMOVE} */ + uint8_t action; +}; + +struct xrep_dir { + struct xfs_scrub *sc; + + /* Inode scan cursor. */ + struct xchk_iscan iscan; + + /* Preallocated args struct for performing dir operations */ + struct xfs_da_args args; + + /* Stashed directory entry updates. */ + struct xfarray *dir_entries; + + /* Directory entry names. */ + struct xfblob *dir_names; + + /* Mutex protecting dir_entries, dir_names, and parent_ino. */ + struct mutex lock; + + /* + * This is the dotdot inumber that we're going to set on the + * reconstructed directory. + */ + xfs_ino_t parent_ino; + + /* Scratch buffer for scanning pptr xattrs */ + struct xfs_parent_name_irec pptr; +}; + +/* Tear down all the incore stuff we created. */ +static void +xrep_dir_teardown( + struct xfs_scrub *sc) +{ + struct xrep_dir *rd = sc->buf; + + xchk_iscan_finish(&rd->iscan); + mutex_destroy(&rd->lock); + xfblob_destroy(rd->dir_names); + xfarray_destroy(rd->dir_entries); +} + +/* Set up for a directory repair. */ +int +xrep_setup_directory( + struct xfs_scrub *sc) +{ + struct xrep_dir *rd; + int error; + + error = xrep_tempfile_create(sc, S_IFDIR); + if (error) + return error; + + rd = kvzalloc(sizeof(struct xrep_dir), XCHK_GFP_FLAGS); + if (!rd) + return -ENOMEM; + + sc->buf = rd; + rd->sc = sc; + rd->parent_ino = NULLFSINO; + return 0; +} + +/* Are these two directory names the same? */ +static inline bool +xrep_dir_samename( + const struct xfs_name *n1, + const struct xfs_name *n2) +{ + return n1->len == n2->len && !memcmp(n1->name, n2->name, n1->len); +} + +/* + * Look up the inode number for an exact name in a directory. + * + * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not + * checked for correctness. This initializes rd->args. + */ +STATIC int +xrep_dir_lookup( + struct xrep_dir *rd, + struct xfs_inode *dp, + const struct xfs_name *name, + xfs_ino_t *ino) +{ + struct xfs_scrub *sc = rd->sc; + bool isblock, isleaf; + int error; + + if (xfs_is_shutdown(dp->i_mount)) + return -EIO; + + ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); + ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); + + memset(&rd->args, 0, sizeof(struct xfs_da_args)); + rd->args.dp = dp; + rd->args.geo = sc->mp->m_dir_geo; + rd->args.hashval = xfs_dir2_hashname(dp->i_mount, name); + rd->args.namelen = name->len; + rd->args.name = name->name; + rd->args.op_flags = XFS_DA_OP_OKNOENT; + rd->args.trans = sc->tp; + rd->args.whichfork = XFS_DATA_FORK; + + if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { + error = xfs_dir2_sf_lookup(&rd->args); + goto out_check_rval; + } + + /* dir2 functions require that the data fork is loaded */ + error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK); + if (error) + return error; + + error = xfs_dir2_isblock(&rd->args, &isblock); + if (error) + return error; + + if (isblock) { + error = xfs_dir2_block_lookup(&rd->args); + goto out_check_rval; + } + + error = xfs_dir2_isleaf(&rd->args, &isleaf); + if (error) + return error; + + if (isleaf) { + error = xfs_dir2_leaf_lookup(&rd->args); + goto out_check_rval; + } + + error = xfs_dir2_node_lookup(&rd->args); + +out_check_rval: + if (error == -EEXIST) + error = 0; + if (!error) + *ino = rd->args.inumber; + return error; +} + +/* Create a directory entry, having filled out most of rd->args via lookup. */ +STATIC int +xrep_dir_createname( + struct xrep_dir *rd, + const struct xfs_name *name, + xfs_ino_t inum, + xfs_extlen_t total, + xfs_dir2_dataptr_t diroffset) +{ + struct xfs_scrub *sc = rd->sc; + struct xfs_inode *dp = rd->args.dp; + bool is_block, is_leaf; + int error; + + ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); + + error = xfs_dir_ino_validate(sc->mp, inum); + if (error) + return error; + + trace_xrep_dir_createname(dp, name, inum, diroffset); + + /* reset cmpresult as if we haven't done a lookup */ + rd->args.cmpresult = XFS_CMP_DIFFERENT; + rd->args.filetype = name->type; + rd->args.inumber = inum; + rd->args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT; + rd->args.total = total; + + if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) + return xfs_dir2_sf_addname(&rd->args); + + error = xfs_dir2_isblock(&rd->args, &is_block); + if (error) + return error; + if (is_block) + return xfs_dir2_block_addname(&rd->args); + + error = xfs_dir2_isleaf(&rd->args, &is_leaf); + if (error) + return error; + if (is_leaf) + return xfs_dir2_leaf_addname(&rd->args); + + return xfs_dir2_node_addname(&rd->args); +} + +/* Remove a directory entry, having filled out rd->args via lookup. */ +STATIC int +xrep_dir_removename( + struct xrep_dir *rd, + const struct xfs_name *name, + xfs_extlen_t total, + xfs_dir2_dataptr_t diroffset) +{ + struct xfs_inode *dp = rd->args.dp; + bool is_block, is_leaf; + int error; + + ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); + + /* reset cmpresult as if we haven't done a lookup */ + rd->args.cmpresult = XFS_CMP_DIFFERENT; + rd->args.op_flags = 0; + rd->args.total = total; + + trace_xrep_dir_removename(dp, name, rd->args.inumber, diroffset); + + if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) + return xfs_dir2_sf_removename(&rd->args); + + error = xfs_dir2_isblock(&rd->args, &is_block); + if (error) + return error; + if (is_block) + return xfs_dir2_block_removename(&rd->args); + + error = xfs_dir2_isleaf(&rd->args, &is_leaf); + if (error) + return error; + if (is_leaf) + return xfs_dir2_leaf_removename(&rd->args); + + return xfs_dir2_node_removename(&rd->args); +} + +/* Update the temporary directory with a stashed update. */ +STATIC int +xrep_dir_replay_update( + struct xrep_dir *rd, + const struct xrep_dirent *dirent) +{ + struct xfs_name xname = { + .len = dirent->namelen, + .type = dirent->ftype, + .name = rd->pptr.p_name, + }; + struct xfs_scrub *sc = rd->sc; + struct xfs_mount *mp = sc->mp; + xfs_ino_t child_ino; + uint resblks; + int error; + + if (dirent->action == XREP_DIRENT_REMOVE) + resblks = XFS_DIRREMOVE_SPACE_RES(mp); + else + resblks = XFS_DIRENTER_SPACE_RES(mp, dirent->namelen); + + error = xchk_trans_alloc(sc, resblks); + if (error) + return error; + + error = xrep_tempfile_ilock_polled(sc); + if (error) { + xchk_trans_cancel(rd->sc); + return error; + } + + xfs_trans_ijoin(sc->tp, sc->tempip, 0); + + error = xrep_dir_lookup(rd, sc->tempip, &xname, &child_ino); + if (dirent->action == XREP_DIRENT_REMOVE) { + /* Remove this dirent. The lookup must succeed. */ + if (error) + goto out_cancel; + if (child_ino != dirent->ino) { + error = -ENOENT; + goto out_cancel; + } + + error = xrep_dir_removename(rd, &xname, resblks, + dirent->diroffset); + } else { + /* Add this dirent. The lookup must not succeed. */ + if (error == 0) + error = -EEXIST; + if (error != -ENOENT) + goto out_cancel; + + error = xrep_dir_createname(rd, &xname, dirent->ino, resblks, + dirent->diroffset); + } + if (error) + goto out_cancel; + + error = xrep_trans_commit(sc); + goto out_ilock; + +out_cancel: + xchk_trans_cancel(rd->sc); +out_ilock: + xrep_tempfile_iunlock(rd->sc); + return error; +} + +/* + * Flush stashed dirent updates that have been recorded by the scanner. This + * is done to reduce the memory requirements of the directory rebuild, since + * directories can contain up to 32GB of directory data. + * + * Caller must not hold transactions or ILOCKs. Caller must hold the tempdir + * IOLOCK. + */ +STATIC int +xrep_dir_replay_updates( + struct xrep_dir *rd) +{ + xfarray_idx_t array_cur; + int error; + + mutex_lock(&rd->lock); + foreach_xfarray_idx(rd->dir_entries, array_cur) { + struct xrep_dirent dirent; + + error = xfarray_load(rd->dir_entries, array_cur, &dirent); + if (error) + goto out_unlock; + + error = xfblob_load(rd->dir_names, dirent.name_cookie, + rd->pptr.p_name, dirent.namelen); + if (error) + goto out_unlock; + rd->pptr.p_name[MAXNAMELEN - 1] = 0; + mutex_unlock(&rd->lock); + + error = xrep_dir_replay_update(rd, &dirent); + if (error) + return error; + + mutex_lock(&rd->lock); + } + + /* Empty out both arrays now that we've added the entries. */ + xfarray_truncate(rd->dir_entries); + xfblob_truncate(rd->dir_names); + mutex_unlock(&rd->lock); + return 0; +out_unlock: + mutex_unlock(&rd->lock); + return error; +} + +/* + * Remember that we want to create a dirent in the tempdir. These stashed + * actions will be replayed later. + */ +STATIC int +xrep_dir_add_dirent( + struct xrep_dir *rd, + const struct xfs_name *name, + xfs_ino_t ino, + xfs_dir2_dataptr_t diroffset) +{ + struct xrep_dirent dirent = { + .action = XREP_DIRENT_ADD, + .ino = ino, + .namelen = name->len, + .ftype = name->type, + .diroffset = diroffset, + }; + int error; + + trace_xrep_dir_add_dirent(rd->sc->tempip, name, ino, diroffset); + + error = xfblob_store(rd->dir_names, &dirent.name_cookie, name->name, + name->len); + if (error) + return error; + + return xfarray_append(rd->dir_entries, &dirent); +} + +/* + * Remember that we want to remove a dirent from the tempdir. These stashed + * actions will be replayed later. + */ +STATIC int +xrep_dir_remove_dirent( + struct xrep_dir *rd, + const struct xfs_name *name, + xfs_ino_t ino, + xfs_dir2_dataptr_t diroffset) +{ + struct xrep_dirent dirent = { + .action = XREP_DIRENT_REMOVE, + .ino = ino, + .namelen = name->len, + .ftype = name->type, + .diroffset = diroffset, + }; + int error; + + trace_xrep_dir_remove_dirent(rd->sc->tempip, name, ino, diroffset); + + error = xfblob_store(rd->dir_names, &dirent.name_cookie, name->name, + name->len); + if (error) + return error; + + return xfarray_append(rd->dir_entries, &dirent); +} + +/* + * Examine an xattr of a file. If this xattr is a parent pointer that leads us + * back to the directory that we're rebuilding, add a dirent to the temporary + * directory. + */ +STATIC int +xrep_dir_scan_parent_pointer( + struct xfs_scrub *sc, + struct xfs_inode *ip, + unsigned int attr_flags, + const unsigned char *name, + unsigned int namelen, + const void *value, + unsigned int valuelen, + void *priv) +{ + struct xfs_name xname; + struct xrep_dir *rd = priv; + const struct xfs_parent_name_rec *rec = (const void *)name; + int error; + + /* Ignore incomplete xattrs */ + if (attr_flags & XFS_ATTR_INCOMPLETE) + return 0; + + /* Ignore anything that isn't a parent pointer. */ + if (!(attr_flags & XFS_ATTR_PARENT)) + return 0; + + /* Does the ondisk parent pointer structure make sense? */ + if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags) || + !xfs_parent_valuecheck(sc->mp, value, valuelen)) + return -EFSCORRUPTED; + + xfs_parent_irec_from_disk(&rd->pptr, rec, value, valuelen); + + /* Ignore parent pointers that point back to a different dir. */ + if (rd->pptr.p_ino != sc->ip->i_ino || + rd->pptr.p_gen != VFS_I(sc->ip)->i_generation) + return 0; + + /* + * Transform this parent pointer into a dirent and queue it for later + * addition to the temporary directory. + */ + xname.name = rd->pptr.p_name; + xname.len = rd->pptr.p_namelen; + xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode); + + mutex_lock(&rd->lock); + error = xrep_dir_add_dirent(rd, &xname, ip->i_ino, + rd->pptr.p_diroffset); + mutex_unlock(&rd->lock); + return error; +} + +/* + * If this child dirent points to the directory being repaired, remember that + * fact so that we can reset the dotdot entry if necessary. + */ +STATIC int +xrep_dir_scan_dirent( + struct xfs_scrub *sc, + struct xfs_inode *dp, + xfs_dir2_dataptr_t dapos, + const struct xfs_name *name, + xfs_ino_t ino, + void *priv) +{ + struct xrep_dir *rd = priv; + + /* Dirent doesn't point to this directory. */ + if (ino != rd->sc->ip->i_ino) + return 0; + + /* Ignore garbage inum. */ + if (!xfs_verify_dir_ino(rd->sc->mp, ino)) + return 0; + + /* No weird looking names. */ + if (name->len >= MAXNAMELEN || name->len <= 0) + return 0; + + /* Don't pick up dot or dotdot entries; we only want child dirents. */ + if (xrep_dir_samename(name, &xfs_name_dotdot) || + xrep_dir_samename(name, &xfs_name_dot)) + return 0; + + trace_xrep_dir_replacename(sc->tempip, &xfs_name_dotdot, dp->i_ino, 0); + + mutex_lock(&rd->lock); + rd->parent_ino = dp->i_ino; + mutex_unlock(&rd->lock); + return 0; +} + +/* + * Decide if we want to look for child dirents or parent pointers in this file. + * Skip the dir being repaired and any files being used to stage repairs. + */ +static inline bool +xrep_dir_want_scan( + struct xrep_dir *rd, + const struct xfs_inode *ip) +{ + return ip != rd->sc->ip && !xrep_is_tempfile(ip); +} + +/* + * Take ILOCK on a file that we want to scan. + * + * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt or + * has an unloaded attr bmbt. Otherwise, take ILOCK_SHARED. + */ +static inline unsigned int +xrep_dir_scan_ilock( + struct xrep_dir *rd, + struct xfs_inode *ip) +{ + uint lock_mode = XFS_ILOCK_SHARED; + + /* Need to take the shared ILOCK to advance the iscan cursor. */ + if (!xrep_dir_want_scan(rd, ip)) + goto lock; + + if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) { + lock_mode = XFS_ILOCK_EXCL; + goto lock; + } + + if (xfs_inode_has_attr_fork(ip) && xfs_need_iread_extents(&ip->i_af)) + lock_mode = XFS_ILOCK_EXCL; + +lock: + xfs_ilock(ip, lock_mode); + return lock_mode; +} + +/* + * Scan this file for relevant child dirents or parent pointers that point to + * the directory we're rebuilding. + */ +STATIC int +xrep_dir_scan_file( + struct xrep_dir *rd, + struct xfs_inode *ip) +{ + unsigned int lock_mode; + int error = 0; + + lock_mode = xrep_dir_scan_ilock(rd, ip); + + if (!xrep_dir_want_scan(rd, ip)) + goto scan_done; + + error = xchk_xattr_walk(rd->sc, ip, xrep_dir_scan_parent_pointer, rd); + if (error) + goto scan_done; + + if (S_ISDIR(VFS_I(ip)->i_mode)) { + error = xchk_dir_walk(rd->sc, ip, xrep_dir_scan_dirent, rd); + if (error) + goto scan_done; + } + +scan_done: + xchk_iscan_mark_visited(&rd->iscan, ip); + xfs_iunlock(ip, lock_mode); + return error; +} + +/* Scan all files in the filesystem for dirents. */ +STATIC int +xrep_dir_scan_dirtree( + struct xrep_dir *rd) +{ + struct xfs_scrub *sc = rd->sc; + struct xfs_inode *ip; + int error; + + /* + * Filesystem scans are time consuming. Drop the directory ILOCK and + * all other resources for the duration of the scan and hope for the + * best. + */ + xchk_trans_cancel(sc); + if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) + xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED | + XFS_ILOCK_EXCL)); + error = xchk_trans_alloc_empty(sc); + if (error) + return error; + + while ((error = xchk_iscan_iter(sc, &rd->iscan, &ip)) == 1) { + uint64_t mem_usage; + + error = xrep_dir_scan_file(rd, ip); + xchk_irele(sc, ip); + if (error) + break; + + /* Flush stashed dirent updates to constrain memory usage. */ + mutex_lock(&rd->lock); + mem_usage = xfarray_bytes(rd->dir_entries) + + xfblob_bytes(rd->dir_names); + mutex_unlock(&rd->lock); + if (mem_usage >= MAX_DIRENT_STASH_SIZE) { + xchk_trans_cancel(sc); + + error = xrep_tempfile_iolock_polled(sc); + if (error) + break; + + error = xrep_dir_replay_updates(rd); + xrep_tempfile_iounlock(sc); + if (error) + break; + + error = xchk_trans_alloc_empty(sc); + if (error) + break; + } + + if (xchk_should_terminate(sc, &error)) + break; + } + if (error) { + /* + * If we couldn't grab an inode that was busy with a state + * change, change the error code so that we exit to userspace + * as quickly as possible. + */ + if (error == -EBUSY) + return -ECANCELED; + return error; + } + + return 0; +} + +/* Dump a dirent from the temporary dir. */ +STATIC int +xrep_dir_dump_tempdir( + struct xfs_scrub *sc, + struct xfs_inode *dp, + xfs_dir2_dataptr_t dapos, + const struct xfs_name *name, + xfs_ino_t ino, + void *priv) +{ + struct xrep_dir *rd = priv; + bool child = true; + int error; + + /* + * The tempdir was created with a dotdot entry pointing to the root + * directory. Substitute whatever inode number we found during the + * filesystem scan. + * + * The tempdir was also created with a dot entry pointing to itself. + * Substitute the inode number of the directory being repaired. A + * prerequisite for the real repair code is a patchset to allow dir + * callers to set the owner (and dot entry in the case of sf -> block + * conversion) explicitly. + * + * I've chosen not to port the owner setting patchset or the swapext + * patchset for this PoC, which is why we build the tempdir, compare + * the contents, and drop the tempdir. + */ + if (xrep_dir_samename(name, &xfs_name_dotdot)) { + child = false; + ino = rd->parent_ino; + } + if (xrep_dir_samename(name, &xfs_name_dot)) { + child = false; + ino = sc->ip->i_ino; + } + + trace_xrep_dir_dumpname(sc->tempip, name, ino, dapos); + + if (!child) + return 0; + + /* + * Set ourselves up to free every dirent in the tempdir because + * directory inactivation won't do it for us. The rest of the online + * fsck patchset provides us a means to swap the directory structure + * and reap it responsibly, but I didn't feel like porting all that. + */ + mutex_lock(&rd->lock); + error = xrep_dir_remove_dirent(rd, name, ino, dapos); + mutex_unlock(&rd->lock); + return error; +} + +/* + * "Commit" the new directory structure to the file that we're repairing. + * + * In the final version, we'd swap the new directory contents (which we created + * in the tempfile) into the directory being repaired. For now we just lock + * the temporary dir and dump what we found. + */ +STATIC int +xrep_dir_rebuild_tree( + struct xrep_dir *rd) +{ + struct xfs_scrub *sc = rd->sc; + int error = 0; + + /* + * Replay the last of the stashed dirent updates. We still hold the + * IOLOCK_EXCL of the directory that we're repairing and the temporary + * directory. + */ + xchk_trans_cancel(sc); + + ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL); + error = xrep_tempfile_iolock_polled(sc); + if (error) + return error; + + error = xrep_dir_replay_updates(rd); + if (error) + return error; + + if (sc->ip == sc->mp->m_rootip) { + /* Should not have found any parent of the root directory. */ + ASSERT(rd->parent_ino == NULLFSINO); + rd->parent_ino = sc->mp->m_rootip->i_ino; + } else if (rd->parent_ino == NULLFSINO) { + /* + * Should have found a parent somewhere unless this is an + * unlinked directory. + */ + ASSERT(VFS_I(sc->ip)->i_nlink == 0); + rd->parent_ino = rd->sc->mp->m_sb.sb_rootino; + } + + /* + * At this point, we've quiesced both directories and should be ready + * to commit the new contents. + * + * We don't have atomic swapext here, so all we do is dump the dirents + * that we found to the ftrace buffer and {ab,re}use the dirent update + * stashing mechanism to schedule deletion of every dirent in the + * temporary directory to avoid leaking directory blocks. + */ + error = xchk_trans_alloc_empty(sc); + if (error) + return error; + + trace_xrep_dir_rebuild_tree(sc->ip, rd->parent_ino); + + xrep_tempfile_ilock(sc); + error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd); + if (error) + return error; + + xrep_tempfile_iunlock(sc); + xchk_trans_cancel(sc); + + return xrep_dir_replay_updates(rd); +} + +/* Set up the filesystem scan so we can regenerate directory entries. */ +STATIC int +xrep_dir_setup_scan( + struct xrep_dir *rd) +{ + struct xfs_scrub *sc = rd->sc; + int error; + + error = xfarray_create(sc->mp, "directory entries", 0, + sizeof(struct xrep_dirent), &rd->dir_entries); + if (error) + return error; + + error = xfblob_create(sc->mp, "dirent names", &rd->dir_names); + if (error) + goto out_entries; + + mutex_init(&rd->lock); + + /* Retry iget every tenth of a second for up to 30 seconds. */ + xchk_iscan_start(&rd->iscan, 30000, 100); + + return 0; + +out_entries: + xfarray_destroy(rd->dir_entries); + return error; +} + +/* + * Repair the directory metadata. + * + * XXX: Is it necessary to check the dcache for this directory to make sure + * that we always recreate every cached entry? + */ +int +xrep_directory( + struct xfs_scrub *sc) +{ + struct xrep_dir *rd = sc->buf; + int error = 0; + + /* We require directory parent pointers to rebuild anything. */ + if (!xfs_has_parent(sc->mp)) + return -EOPNOTSUPP; + + error = xrep_dir_setup_scan(rd); + if (error) + goto out; + + error = xrep_dir_scan_dirtree(rd); + if (error) + goto out_finish_scan; + + error = xrep_dir_rebuild_tree(rd); + if (error) + goto out_finish_scan; + +out_finish_scan: + xrep_dir_teardown(sc); +out: + return error; +} diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index 840f74ec431c..ff254ff9b86d 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -30,6 +30,16 @@ int xrep_init_btblock(struct xfs_scrub *sc, xfs_fsblock_t fsb, struct xfs_buf **bpp, xfs_btnum_t btnum, const struct xfs_buf_ops *ops); +static inline int +xrep_trans_commit( + struct xfs_scrub *sc) +{ + int error = xfs_trans_commit(sc->tp); + + sc->tp = NULL; + return error; +} + struct xbitmap; int xrep_fix_freelist(struct xfs_scrub *sc, bool can_shrink); @@ -57,6 +67,8 @@ int xrep_find_ag_btree_roots(struct xfs_scrub *sc, struct xfs_buf *agf_bp, void xrep_force_quotacheck(struct xfs_scrub *sc, xfs_dqtype_t type); int xrep_ino_dqattach(struct xfs_scrub *sc); +int xrep_setup_directory(struct xfs_scrub *sc); + /* Metadata repairers */ int xrep_probe(struct xfs_scrub *sc); @@ -64,6 +76,7 @@ int xrep_superblock(struct xfs_scrub *sc); int xrep_agf(struct xfs_scrub *sc); int xrep_agfl(struct xfs_scrub *sc); int xrep_agi(struct xfs_scrub *sc); +int xrep_directory(struct xfs_scrub *sc); #else @@ -83,11 +96,14 @@ xrep_calc_ag_resblks( return 0; } +#define xrep_setup_directory(sc) (0) + #define xrep_probe xrep_notsupported #define xrep_superblock xrep_notsupported #define xrep_agf xrep_notsupported #define xrep_agfl xrep_notsupported #define xrep_agi xrep_notsupported +#define xrep_directory xrep_notsupported #endif /* CONFIG_XFS_ONLINE_REPAIR */ diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index a19ea7fdd510..b2a8de449d11 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -299,7 +299,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { .type = ST_INODE, .setup = xchk_setup_directory, .scrub = xchk_directory, - .repair = xrep_notsupported, + .repair = xrep_directory, }, [XFS_SCRUB_TYPE_XATTR] = { /* extended attributes */ .type = ST_INODE, diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index 91875d4bb67f..fa47e3423763 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -136,6 +136,7 @@ xrep_tempfile_create( xfs_setup_iops(sc->tempip); xfs_finish_inode_setup(sc->tempip); + xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL); sc->temp_ilock_flags = 0; return error; @@ -149,6 +150,7 @@ xrep_tempfile_create( */ if (sc->tempip) { xfs_finish_inode_setup(sc->tempip); + xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL); xchk_irele(sc, sc->tempip); } out_release_dquots: @@ -172,6 +174,26 @@ xrep_tempfile_iolock_nowait( return false; } +/* + * Take the temporary file's IOLOCK while holding a different inode's IOLOCK. + * In theory nobody else should hold the tempfile's IOLOCK, but we use trylock + * to avoid deadlocks and lockdep. + */ +int +xrep_tempfile_iolock_polled( + struct xfs_scrub *sc) +{ + int error = 0; + + while (!xrep_tempfile_iolock_nowait(sc)) { + if (xchk_should_terminate(sc, &error)) + return error; + delay(1); + } + + return 0; +} + /* Release IOLOCK_EXCL on the temporary file. */ void xrep_tempfile_iounlock( @@ -203,6 +225,26 @@ xrep_tempfile_ilock_nowait( return false; } +/* + * Take the temporary file's ILOCK while holding a different inode's ILOCK. In + * theory nobody else should hold the tempfile's ILOCK, but we use trylock to + * avoid deadlocks and lockdep. + */ +int +xrep_tempfile_ilock_polled( + struct xfs_scrub *sc) +{ + int error = 0; + + while (!xrep_tempfile_ilock_nowait(sc)) { + if (xchk_should_terminate(sc, &error)) + return error; + delay(1); + } + + return 0; +} + /* Unlock ILOCK_EXCL on the temporary file after an update. */ void xrep_tempfile_iunlock( diff --git a/fs/xfs/scrub/tempfile.h b/fs/xfs/scrub/tempfile.h index e2f493b5d3d9..1e61d8e1ddce 100644 --- a/fs/xfs/scrub/tempfile.h +++ b/fs/xfs/scrub/tempfile.h @@ -11,10 +11,12 @@ int xrep_tempfile_create(struct xfs_scrub *sc, uint16_t mode); void xrep_tempfile_rele(struct xfs_scrub *sc); bool xrep_tempfile_iolock_nowait(struct xfs_scrub *sc); +int xrep_tempfile_iolock_polled(struct xfs_scrub *sc); void xrep_tempfile_iounlock(struct xfs_scrub *sc); void xrep_tempfile_ilock(struct xfs_scrub *sc); bool xrep_tempfile_ilock_nowait(struct xfs_scrub *sc); +int xrep_tempfile_ilock_polled(struct xfs_scrub *sc); void xrep_tempfile_iunlock(struct xfs_scrub *sc); bool xrep_is_tempfile(const struct xfs_inode *ip); #else diff --git a/fs/xfs/scrub/trace.c b/fs/xfs/scrub/trace.c index 83e8a64c95d4..61b51617fbb4 100644 --- a/fs/xfs/scrub/trace.c +++ b/fs/xfs/scrub/trace.c @@ -17,6 +17,7 @@ #include "scrub/xfile.h" #include "scrub/xfarray.h" #include "scrub/iscan.h" +#include "xfs_da_format.h" /* Figure out which block the btree cursor was pointing to. */ static inline xfs_fsblock_t diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 0c27eb197f83..cbf914bce6db 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1182,6 +1182,73 @@ TRACE_EVENT(xrep_tempfile_create, __entry->temp_inum) ); +DECLARE_EVENT_CLASS(xrep_dirent_class, + TP_PROTO(struct xfs_inode *dp, const struct xfs_name *name, + xfs_ino_t ino, unsigned int diroffset), + TP_ARGS(dp, name, ino, diroffset), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, dir_ino) + __field(unsigned int, namelen) + __dynamic_array(char, name, name->len) + __field(xfs_ino_t, ino) + __field(uint8_t, ftype) + __field(unsigned int, diroffset) + ), + TP_fast_assign( + __entry->dev = dp->i_mount->m_super->s_dev; + __entry->dir_ino = dp->i_ino; + __entry->namelen = name->len; + memcpy(__get_str(name), name->name, name->len); + __entry->ino = ino; + __entry->ftype = name->type; + __entry->diroffset = diroffset; + ), + TP_printk("dev %d:%d dir 0x%llx dapos 0x%x ftype %s name '%.*s' ino 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->dir_ino, + __entry->diroffset, + __print_symbolic(__entry->ftype, XFS_DIR3_FTYPE_STR), + __entry->namelen, + __get_str(name), + __entry->ino) +) +#define DEFINE_XREP_DIRENT_CLASS(name) \ +DEFINE_EVENT(xrep_dirent_class, name, \ + TP_PROTO(struct xfs_inode *dp, const struct xfs_name *name, \ + xfs_ino_t ino, unsigned int diroffset), \ + TP_ARGS(dp, name, ino, diroffset)) +DEFINE_XREP_DIRENT_CLASS(xrep_dir_add_dirent); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_remove_dirent); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_createname); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_removename); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_replacename); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_dumpname); + +DECLARE_EVENT_CLASS(xrep_dir_class, + TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino), + TP_ARGS(dp, parent_ino), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, dir_ino) + __field(xfs_ino_t, parent_ino) + ), + TP_fast_assign( + __entry->dev = dp->i_mount->m_super->s_dev; + __entry->dir_ino = dp->i_ino; + __entry->parent_ino = parent_ino; + ), + TP_printk("dev %d:%d dir 0x%llx parent 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->dir_ino, + __entry->parent_ino) +) +#define DEFINE_XREP_DIR_CLASS(name) \ +DEFINE_EVENT(xrep_dir_class, name, \ + TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino), \ + TP_ARGS(dp, parent_ino)) +DEFINE_XREP_DIR_CLASS(xrep_dir_rebuild_tree); + #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */ From patchwork Thu Feb 16 20:49:21 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: 13143842 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B4203C61DA4 for ; Thu, 16 Feb 2023 20:49:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229532AbjBPUt1 (ORCPT ); Thu, 16 Feb 2023 15:49:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58488 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229741AbjBPUt0 (ORCPT ); Thu, 16 Feb 2023 15:49:26 -0500 Received: from sin.source.kernel.org (sin.source.kernel.org [IPv6:2604:1380:40e1:4800::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3BB154BEB7 for ; Thu, 16 Feb 2023 12:49:25 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sin.source.kernel.org (Postfix) with ESMTPS id 85AA1CE2D74 for ; Thu, 16 Feb 2023 20:49:23 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id AE3FDC433EF; Thu, 16 Feb 2023 20:49:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580561; bh=TN3kl8Ja2i+7Qu2b/WcQO5Tym3PdYBOpDnxhjJFWgbI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=SWnyeazEr25LFSsXknLH9BoSd7+TD5CnKtjNBCaOzakekgomP6qBNwHYtrx09APXp B45z26jiaAEUP2vzuGFn0U9D3OJ0n5XVMUJJV1ZyMmj2UxOImXGEYELCe3PRnZ/u6u QCHH/JyQiPtpgwNpNUkSzXPnTixTVDBIyZYMpHT771VV7i/jfQFzWZJK5f+idNyBRP aJ5AIfXBCxDbfQw9fF1urY0jbss6PWRPMo5O6ylaLpV8eK9Z7kfWjgdbYqnVC2EqRn e2cRENUIAQd3TzdtpjzOmw6xJVZrzemmeBUSWWOpm6gZ1AnWrhime4iA3DWINSmpEq trkFT0EYeT7CA== Date: Thu, 16 Feb 2023 12:49:21 -0800 Subject: [PATCH 6/7] xfs: add hooks to do directory updates From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874554.3474898.13161459001017692488.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong While we're scanning the filesystem, we still need to keep the tempdir up to date with whatever changes get made to the you know what. Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_dir2.c | 2 - fs/xfs/libxfs/xfs_dir2.h | 2 - fs/xfs/scrub/dir_repair.c | 94 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index 27e408d20d18..8bed71a5e9cc 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c @@ -440,7 +440,7 @@ int xfs_dir_removename( struct xfs_trans *tp, struct xfs_inode *dp, - struct xfs_name *name, + const struct xfs_name *name, xfs_ino_t ino, xfs_extlen_t total, /* bmap's total block count */ xfs_dir2_dataptr_t *offset) /* OUT: offset in directory */ diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h index ac360c0b2fe7..6ed86b7bd13c 100644 --- a/fs/xfs/libxfs/xfs_dir2.h +++ b/fs/xfs/libxfs/xfs_dir2.h @@ -46,7 +46,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp, const struct xfs_name *name, xfs_ino_t *inum, struct xfs_name *ci_name); extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp, - struct xfs_name *name, xfs_ino_t ino, + const struct xfs_name *name, xfs_ino_t ino, xfs_extlen_t tot, xfs_dir2_dataptr_t *offset); extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp, diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index a6576a29e784..25af002df1da 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -124,6 +124,9 @@ struct xrep_dir { /* Mutex protecting dir_entries, dir_names, and parent_ino. */ struct mutex lock; + /* Hook to capture directory entry updates. */ + struct xfs_dirent_hook hooks; + /* * This is the dotdot inumber that we're going to set on the * reconstructed directory. @@ -141,6 +144,7 @@ xrep_dir_teardown( { struct xrep_dir *rd = sc->buf; + xfs_dirent_hook_del(sc->mp, &rd->hooks); xchk_iscan_finish(&rd->iscan); mutex_destroy(&rd->lock); xfblob_destroy(rd->dir_names); @@ -155,6 +159,8 @@ xrep_setup_directory( struct xrep_dir *rd; int error; + xchk_fshooks_enable(sc, XCHK_FSHOOKS_DIRENTS); + error = xrep_tempfile_create(sc, S_IFDIR); if (error) return error; @@ -832,6 +838,12 @@ xrep_dir_rebuild_tree( if (error) return error; + /* + * Abort the inode scan so that the live hooks won't stash any more + * directory updates. + */ + xchk_iscan_abort(&rd->iscan); + error = xrep_dir_replay_updates(rd); if (error) return error; @@ -875,6 +887,72 @@ xrep_dir_rebuild_tree( return xrep_dir_replay_updates(rd); } +/* + * Capture dirent updates being made by other threads which are relevant to the + * directory being repaired. + */ +STATIC int +xrep_dir_live_update( + struct notifier_block *nb, + unsigned long action, + void *data) +{ + struct xfs_dirent_update_params *p = data; + struct xrep_dir *rd; + struct xfs_scrub *sc; + int error = 0; + + rd = container_of(nb, struct xrep_dir, hooks.delta_hook.nb); + sc = rd->sc; + + if (action != XFS_DIRENT_CHILD_DELTA) + return NOTIFY_DONE; + + /* + * This thread updated a dirent in the directory that we're rebuilding, + * so stash the update for replay against the temporary directory. + */ + if (p->dp->i_ino == sc->ip->i_ino && + xchk_iscan_want_live_update(&rd->iscan, p->ip->i_ino)) { + mutex_lock(&rd->lock); + if (p->delta > 0) + error = xrep_dir_add_dirent(rd, p->name, p->ip->i_ino, + p->diroffset); + else + error = xrep_dir_remove_dirent(rd, p->name, + p->ip->i_ino, p->diroffset); + mutex_unlock(&rd->lock); + if (error) + goto out_abort; + } + + /* + * This thread updated a dirent that points to the directory that we're + * rebuilding, so remember the new dotdot target. + */ + if (p->ip->i_ino == sc->ip->i_ino && + xchk_iscan_want_live_update(&rd->iscan, p->dp->i_ino)) { + mutex_lock(&rd->lock); + if (p->delta > 0) { + trace_xrep_dir_add_dirent(sc->tempip, &xfs_name_dotdot, + p->dp->i_ino, 0); + + rd->parent_ino = p->dp->i_ino; + } else { + trace_xrep_dir_remove_dirent(sc->tempip, + &xfs_name_dotdot, NULLFSINO, 0); + + rd->parent_ino = NULLFSINO; + } + mutex_unlock(&rd->lock); + } + + return NOTIFY_DONE; +out_abort: + xchk_iscan_abort(&rd->iscan); + return NOTIFY_DONE; +} + /* Set up the filesystem scan so we can regenerate directory entries. */ STATIC int xrep_dir_setup_scan( @@ -897,8 +975,24 @@ xrep_dir_setup_scan( /* Retry iget every tenth of a second for up to 30 seconds. */ xchk_iscan_start(&rd->iscan, 30000, 100); + /* + * Hook into the dirent update code. The hook only operates on inodes + * that were already scanned, and the scanner thread takes each inode's + * ILOCK, which means that any in-progress inode updates will finish + * before we can scan the inode. + */ + ASSERT(sc->flags & XCHK_FSHOOKS_DIRENTS); + xfs_hook_setup(&rd->hooks.delta_hook, xrep_dir_live_update); + error = xfs_dirent_hook_add(sc->mp, &rd->hooks); + if (error) + goto out_scan; + return 0; +out_scan: + xchk_iscan_finish(&rd->iscan); + mutex_destroy(&rd->lock); + xfblob_destroy(rd->dir_names); out_entries: xfarray_destroy(rd->dir_entries); return error; From patchwork Thu Feb 16 20:49:36 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: 13143843 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E1BDFC636CC for ; Thu, 16 Feb 2023 20:49:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229741AbjBPUtl (ORCPT ); Thu, 16 Feb 2023 15:49:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58550 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229744AbjBPUtk (ORCPT ); Thu, 16 Feb 2023 15:49:40 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DFBB64BEA8 for ; Thu, 16 Feb 2023 12:49:39 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 90ECCB829BB for ; Thu, 16 Feb 2023 20:49:38 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3FF6BC4339C; Thu, 16 Feb 2023 20:49:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1676580577; bh=MQH28wg0NxOaYGhe+UI/TrNBcnxpqhrure5CnSLZb98=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=r7GLGspnigd3AI65qvX26xBgtaP4c0GbeeZrDlhBaWB90x/9ZbWP154G2Xgg92miY +eeXLSj7Xgm6aQzKVQPYPZtl5ASYECi6ZEO2YJJjrH1wCEs+6pLHKgd8z+0wqwnrat TsJ5RtEWBMLK9PUdMx1kQgIapyvpUZzpj3Mi0I2Nv/Nxpd8/yWQ/XpUx7uoE2f8INP 52CZFKMSwAxPLa1tYFToAoo1DqHd6yDVcfQmoHJKp6xS2gPU6bLWREgjnHb94N5o3w /qB1Xdu5zWSWk108EGurPVS6eVphvOeGgibaBQQo+swUsFZxJwa9mQ3VLXW3c/vSdy PdU220aM+ljrA== Date: Thu, 16 Feb 2023 12:49:36 -0800 Subject: [PATCH 7/7] xfs: compare generated and existing dirents From: "Darrick J. Wong" To: djwong@kernel.org Cc: allison.henderson@oracle.com, linux-xfs@vger.kernel.org Message-ID: <167657874569.3474898.11791594095848245581.stgit@magnolia> In-Reply-To: <167657874461.3474898.12919390014293805981.stgit@magnolia> References: <167657874461.3474898.12919390014293805981.stgit@magnolia> User-Agent: StGit/0.19 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-xfs@vger.kernel.org From: Darrick J. Wong Check our work to make sure we found all the dirents that the original directory had. Signed-off-by: Darrick J. Wong --- fs/xfs/scrub/dir_repair.c | 101 ++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/scrub/trace.h | 2 + 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index 25af002df1da..ec48b3268809 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -757,7 +757,10 @@ xrep_dir_scan_dirtree( return 0; } -/* Dump a dirent from the temporary dir. */ +/* + * Dump a dirent from the temporary dir and check it against the dir we're + * rebuilding. We are not committing any of this. + */ STATIC int xrep_dir_dump_tempdir( struct xfs_scrub *sc, @@ -768,7 +771,9 @@ xrep_dir_dump_tempdir( void *priv) { struct xrep_dir *rd = priv; + xfs_ino_t child_ino; bool child = true; + xfs_dir2_dataptr_t child_diroffset = XFS_DIR2_NULL_DATAPTR; int error; /* @@ -809,7 +814,88 @@ xrep_dir_dump_tempdir( mutex_lock(&rd->lock); error = xrep_dir_remove_dirent(rd, name, ino, dapos); mutex_unlock(&rd->lock); - return error; + if (error) + return error; + + /* Check that the dir being repaired has the same entry. */ + error = xchk_dir_lookup(sc, sc->ip, name, &child_ino, + &child_diroffset); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->ip, name, NULLFSINO, + XFS_DIR2_NULL_DATAPTR); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->ip, name, child_ino, + child_diroffset); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + + if (dapos != child_diroffset) { + trace_xrep_dir_badposname(sc->ip, name, child_ino, + child_diroffset); + /* We have no way to update this, so we just leave it. */ + } + + return 0; +} + +/* + * Dump a dirent from the dir we're rebuilding and check it against the + * temporary dir. This assumes that the directory wasn't really corrupt to + * begin with. + */ +STATIC int +xrep_dir_dump_baddir( + struct xfs_scrub *sc, + struct xfs_inode *dp, + xfs_dir2_dataptr_t dapos, + const struct xfs_name *name, + xfs_ino_t ino, + void *priv) +{ + xfs_ino_t child_ino; + xfs_dir2_dataptr_t child_diroffset = XFS_DIR2_NULL_DATAPTR; + int error; + + /* Ignore the directory's dot and dotdot entries. */ + if (xrep_dir_samename(name, &xfs_name_dotdot) || + xrep_dir_samename(name, &xfs_name_dot)) + return 0; + + trace_xrep_dir_dumpname(sc->ip, name, ino, dapos); + + /* Check that the tempdir has the same entry. */ + error = xchk_dir_lookup(sc, sc->tempip, name, &child_ino, + &child_diroffset); + if (error == -ENOENT) { + trace_xrep_dir_checkname(sc->tempip, name, NULLFSINO, + XFS_DIR2_NULL_DATAPTR); + ASSERT(error != -ENOENT); + return -EFSCORRUPTED; + } + if (error) + return error; + + if (ino != child_ino) { + trace_xrep_dir_checkname(sc->tempip, name, child_ino, + child_diroffset); + ASSERT(ino == child_ino); + return -EFSCORRUPTED; + } + + if (dapos != child_diroffset) { + trace_xrep_dir_badposname(sc->ip, name, child_ino, + child_diroffset); + /* We have no way to update this, so we just leave it. */ + } + + return 0; } /* @@ -876,12 +962,21 @@ xrep_dir_rebuild_tree( trace_xrep_dir_rebuild_tree(sc->ip, rd->parent_ino); - xrep_tempfile_ilock(sc); + xchk_ilock(sc, XFS_ILOCK_EXCL); + error = xrep_tempfile_ilock_polled(sc); + if (error) + return error; + error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd); if (error) return error; + error = xchk_dir_walk(sc, sc->ip, xrep_dir_dump_baddir, rd); + if (error) + return error; + xrep_tempfile_iunlock(sc); + xchk_iunlock(sc, XFS_ILOCK_EXCL); xchk_trans_cancel(sc); return xrep_dir_replay_updates(rd); diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index cbf914bce6db..81d26be0ef3b 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1224,6 +1224,8 @@ DEFINE_XREP_DIRENT_CLASS(xrep_dir_createname); DEFINE_XREP_DIRENT_CLASS(xrep_dir_removename); DEFINE_XREP_DIRENT_CLASS(xrep_dir_replacename); DEFINE_XREP_DIRENT_CLASS(xrep_dir_dumpname); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_checkname); +DEFINE_XREP_DIRENT_CLASS(xrep_dir_badposname); DECLARE_EVENT_CLASS(xrep_dir_class, TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino),