From patchwork Tue Jul 30 01:25:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746194 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 BE0EB79CC for ; Tue, 30 Jul 2024 01:25:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302754; cv=none; b=tnSR6izKtn7KfRy22LQxFcSE/WreNG1enB80UZ+g/Cmg893s5kExQZ/OlLELmh5TeX5qrG+Koc7s8uDDDcstcVrqnnI9W5B6/4APC9Q6WrVeuUHekLQf6Q5B6KiwmYsW8D3yXKIL61ELv4vEeRIJmJxZWYp7KuQWDNV2xvoTNzw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302754; c=relaxed/simple; bh=pRU4qNmG0lZqrnE1uUv46DIJr8UAHdMaRmv0k4RMKso=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iKg16uppXV/Jl5JBLDUh0cSZHfsUDooQPUrg+wEq0v0i0FDK8d0e4y0Zx88nQeNcwtE0VoA04wNuR0ZokrbupkQHx+A0rZxavvhYS4RlQfGmxZnVNX7zyCkWOq/Foo0yN/4gPfiLxzThSGYtIQGuDqaA85RugTo7O04i0kUp+Jk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=flPcCIDb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="flPcCIDb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3E50FC4AF0A; Tue, 30 Jul 2024 01:25:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302754; bh=pRU4qNmG0lZqrnE1uUv46DIJr8UAHdMaRmv0k4RMKso=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=flPcCIDbJyEcozDvmQk0+lSNKgJbSCvnTL00V527mIWWS4RuOJYkfjiSzcLU6u5AI RoXEVp1qj7HV2p5mxRBONs3SNXVyBBqh719Azq6Ujj+993Wy69iP4SQLkXzbNGSOO/ WrYGK3l6n3jEBBCuNWzB/DYrHyyB4Z6K/GvIMGEoqQOCjqLbmLzp9Ua2ufUjS0tJja nQF67ykmnWaHAYWoIgLOYdUwEfyEvOSWFlKAiAiX0EQAXYIEvN15LXSN3HOnC6K7ON Djlxl2Poaj7xpj/uqM4uSRZzETUr6B9rgUhAdSGaQfL+XUw5myRtzmjSjgeEtxW9fF cvFEOKF3y3Y3A== Date: Mon, 29 Jul 2024 18:25:53 -0700 Subject: [PATCH 01/12] xfs_db: remove some boilerplate from xfs_attr_set From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851502.1352527.1905635215078738882.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 In preparation for online/offline repair wanting to use xfs_attr_set, move some of the boilerplate out of this function into the callers. Repair can initialize the da_args completely, and the userspace flag handling/twisting goes away once we move it to xfs_attr_change. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- db/attrset.c | 18 ++++++++++++++++-- libxfs/libxfs_api_defs.h | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/db/attrset.c b/db/attrset.c index d9ab79fa7..008662571 100644 --- a/db/attrset.c +++ b/db/attrset.c @@ -111,7 +111,11 @@ attr_set_f( int argc, char **argv) { - struct xfs_da_args args = { }; + struct xfs_da_args args = { + .geo = mp->m_attr_geo, + .whichfork = XFS_ATTR_FORK, + .op_flags = XFS_DA_OP_OKNOENT, + }; char *sp; char *name_from_file = NULL; char *value_from_file = NULL; @@ -253,6 +257,9 @@ attr_set_f( goto out; } + args.owner = iocur_top->ino; + libxfs_attr_sethash(&args); + if (libxfs_attr_set(&args, op, false)) { dbprintf(_("failed to set attr %s on inode %llu\n"), args.name, (unsigned long long)iocur_top->ino); @@ -277,7 +284,11 @@ attr_remove_f( int argc, char **argv) { - struct xfs_da_args args = { }; + struct xfs_da_args args = { + .geo = mp->m_attr_geo, + .whichfork = XFS_ATTR_FORK, + .op_flags = XFS_DA_OP_OKNOENT, + }; char *name_from_file = NULL; int c; @@ -365,6 +376,9 @@ attr_remove_f( goto out; } + args.owner = iocur_top->ino; + libxfs_attr_sethash(&args); + if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false)) { dbprintf(_("failed to remove attr %s from inode %llu\n"), (unsigned char *)args.name, diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index df83aabdc..bf1d3c9d3 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -47,6 +47,7 @@ #define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize #define xfs_attr_namecheck libxfs_attr_namecheck #define xfs_attr_set libxfs_attr_set +#define xfs_attr_sethash libxfs_attr_sethash #define xfs_attr_sf_firstentry libxfs_attr_sf_firstentry #define xfs_attr_shortform_verify libxfs_attr_shortform_verify From patchwork Tue Jul 30 01:26:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746195 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 5353D79E1 for ; Tue, 30 Jul 2024 01:26:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302770; cv=none; b=GuZR4mRXQAjYIQJ/5SGKLonean8kRElPEaIBxbW+rMRH0yHTHQCqb6Mf9HcrlORUEWVfxENzkIysuZfpwKJhLnvY6FWFSgX+r64cMSGaZ0HFkd4HFco54z37bh11Fz4GAWyGqpqNnIA2DLCfEtyyxle+wUSkJuJt3ea8ueO0XdI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302770; c=relaxed/simple; bh=Ubvl4vhVTdBbVONyxhpSPe1x77zhfZznbF9Jx1uusxE=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=s3Mj2YvVSUv5mZX4QAs9kYQoDxcX9yd0oxFV0VgpwAURPloUxg3y+yi7lolMHuxTE+HA3VzakfmC8/okro/aepmkRglV2UFsTN+bjmfCZ7voHrVCD3iO1iSQgfgQo9caZ0rFXHHeN1IdCKJwZYj4SFoL6xJLAuytui3hVZnxa9M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=g0AQg/3F; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="g0AQg/3F" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DBC3EC4AF07; Tue, 30 Jul 2024 01:26:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302769; bh=Ubvl4vhVTdBbVONyxhpSPe1x77zhfZznbF9Jx1uusxE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=g0AQg/3F5BgzaY/E3XPnRV58mxZefvYsBEo/9sikHt9ldEtcDc9oBV40VHOzHQF79 lCI/yyFS8ovNvEw3QC8LkW6CH62wCIMDqjc4Ci8CuY1r3pIVo/9TeLEo5dHBHeR5Yp B398Nq4OcIDOi8r7vZRRQQmZfYo0xLwiGNE3zinei7vrmiCC4a3Fk7bXbKkUettH/f CibbykCfZMavKGsFJDfKJOEEg7iL0q8Nj3PYQbYi/R/B8CtZvsXf+93Dy428lbtC3V ck9sKljQYmldhgQzKDLIhBgTNf8MGe8Ft34P509DaJjH7yKL3zTIYaPf09sVS1yDaC PEco6cLzSLtfw== Date: Mon, 29 Jul 2024 18:26:09 -0700 Subject: [PATCH 02/12] xfs_db: actually report errors from libxfs_attr_set From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851515.1352527.17325767071539808212.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Actually tell the user what went wrong when setting or removing xattrs. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- db/attrset.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/db/attrset.c b/db/attrset.c index 008662571..81d530055 100644 --- a/db/attrset.c +++ b/db/attrset.c @@ -121,6 +121,7 @@ attr_set_f( char *value_from_file = NULL; enum xfs_attr_update op = XFS_ATTRUPDATE_UPSERT; int c; + int error; if (cur_typ == NULL) { dbprintf(_("no current type\n")); @@ -260,9 +261,11 @@ attr_set_f( args.owner = iocur_top->ino; libxfs_attr_sethash(&args); - if (libxfs_attr_set(&args, op, false)) { - dbprintf(_("failed to set attr %s on inode %llu\n"), - args.name, (unsigned long long)iocur_top->ino); + error = -libxfs_attr_set(&args, op, false); + if (error) { + dbprintf(_("failed to set attr %s on inode %llu: %s\n"), + args.name, (unsigned long long)iocur_top->ino, + strerror(error)); goto out; } @@ -291,6 +294,7 @@ attr_remove_f( }; char *name_from_file = NULL; int c; + int error; if (cur_typ == NULL) { dbprintf(_("no current type\n")); @@ -379,10 +383,12 @@ attr_remove_f( args.owner = iocur_top->ino; libxfs_attr_sethash(&args); - if (libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false)) { - dbprintf(_("failed to remove attr %s from inode %llu\n"), + error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, false); + if (error) { + dbprintf(_("failed to remove attr %s from inode %llu: %s\n"), (unsigned char *)args.name, - (unsigned long long)iocur_top->ino); + (unsigned long long)iocur_top->ino, + strerror(error)); goto out; } From patchwork Tue Jul 30 01:26:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746197 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 AAAE68BE8 for ; Tue, 30 Jul 2024 01:26:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302785; cv=none; b=jpzxhv/mIFUjxK386rVsbzI0/QbOUBEOrpDx+Fd67Q29drow2S4aBtvWu+L7v+xKYLE006CRRc/a52lOY4o7uy3vqZUd5smiEE26+Fl2m4VzeOT38bRBspmE9ImEIRivSYI7X99dvWHv0Q6CnkxHpm0alvhRDAf/5T/R3S8dHhM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302785; c=relaxed/simple; bh=bytfYXM9QwgTSE6ZeGvnFYko9RGfE+VoP1CvxWz3J7Y=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p0kf7uryyIX922T7wUOiCr4Z0ZSywnPD1qiQl/2vzUviqkkBq+qeMM0TL6EkqAcjKpzwrEDCKtIUDZ1QKdU+bMUUYY13HcTtRUG5BND+dxaDffQCv+GCt4oDRj8ZhOTxwLx2t3No4NlmG4OPoeOMys0XI8OlJdiXAo5Vmn805NI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AR5Y9w2O; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AR5Y9w2O" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 856F5C32786; Tue, 30 Jul 2024 01:26:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302785; bh=bytfYXM9QwgTSE6ZeGvnFYko9RGfE+VoP1CvxWz3J7Y=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=AR5Y9w2OjVasvxY0OAkxlQQU7O6TKiIexWCSC5R/sDPxtz3JZ1mQDMDw2oEGDRHPT LEy8IcsOVrUuJA2nHHuls3r2Eipy/KqRVXrEh/yh4X3+NYTI2y0S9LkaG01ZWMpTA8 5ezTLNlno1QXiznIa6AswFTyLUrjm5ymWDWhISGwwTJuGCg6504kyecJBd5o/L2Wuc kLPhiqL7f8XPMtDfYpmcs6/GgRt4oSd8SUs25MAvlyimWxrUGoZCnN/3VzAytfEVRj fbTL/pnx5/GLY8x5w4SLNO4I8sFgscsQYKGD7ROX3Q9liQFgqPeGUuPBayff0RuUfq NjfEMzGrtHsjQ== Date: Mon, 29 Jul 2024 18:26:25 -0700 Subject: [PATCH 03/12] xfs_repair: junk parent pointer attributes when filesystem doesn't support them From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851527.1352527.17607890696167184163.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Drop a parent pointer xattr if the filesystem doesn't support parent pointers. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/attr_repair.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/repair/attr_repair.c b/repair/attr_repair.c index 2e97fd977..50159b9a5 100644 --- a/repair/attr_repair.c +++ b/repair/attr_repair.c @@ -327,6 +327,13 @@ process_shortform_attr( NULL, currententry->namelen, currententry->valuelen); + if ((currententry->flags & XFS_ATTR_PARENT) && + !xfs_has_parent(mp)) { + do_warn( + _("parent pointer found on filesystem that doesn't support parent pointers\n")); + junkit |= 1; + } + remainingspace = remainingspace - xfs_attr_sf_entsize(currententry); @@ -527,6 +534,15 @@ process_leaf_attr_local( return -1; } } + + if ((entry->flags & XFS_ATTR_PARENT) && !xfs_has_parent(mp)) { + do_warn( + _("parent pointer found in attribute entry %d in attr block %u, inode %" + PRIu64 " on filesystem that doesn't support parent pointers\n"), + i, da_bno, ino); + return -1; + } + return xfs_attr_leaf_entsize_local(local->namelen, be16_to_cpu(local->valuelen)); } @@ -562,6 +578,20 @@ process_leaf_attr_remote( return -1; } + if (entry->flags & XFS_ATTR_PARENT) { + if (!xfs_has_parent(mp)) + do_warn( + _("parent pointer found in attribute entry %d in attr block %u, inode %" + PRIu64 " on filesystem that doesn't support parent pointers\n"), + i, da_bno, ino); + else + do_warn( + _("parent pointer found in attribute entry %d in attr block %u, inode %" + PRIu64 " with bogus remote value\n"), + i, da_bno, ino); + return -1; + } + value = malloc(be32_to_cpu(remotep->valuelen)); if (value == NULL) { do_warn( From patchwork Tue Jul 30 01:26:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746198 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 47D5F881E for ; Tue, 30 Jul 2024 01:26:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302801; cv=none; b=tEfRS4+reHNm+5sclqmI/ADOw8kLdMt8Yvfqz9JBzdLJWwEBoeB2IM4g7wPInAaajy8lKmmd9U/vq2Nl+N3Dux4OT1/qRfs71NDIHGOXlq9AYL9y7oJNbkOaUzWcpyZJpGsl89mhvWkC/tQd5c2Jg8M6elT1FZl6k3QBXgcBf0o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302801; c=relaxed/simple; bh=L2YObl43EOWfu+VDFYE2h0qknryRWmfhwCmck0q2LDU=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gQLYJY/0vOtN0+HTmIISGX4/5RWTDH0F2UYUpIwey7DwSPcaLrjXojbsBoF/N5HMoGWeVMcCsT08KmIHsSDbzf/emCts8dc1dB+ubB3LapXeLr9Wsc5z2AUqC5U342GLsSm5tLkChJ9eWy92xMr4uKmTLyA/9rUur7YOBk6uRh4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oIBXyB/W; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oIBXyB/W" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 207FDC32786; Tue, 30 Jul 2024 01:26:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302801; bh=L2YObl43EOWfu+VDFYE2h0qknryRWmfhwCmck0q2LDU=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=oIBXyB/WexyzLFr2QBX/QjNTMKr6FhdwEH6v2Df+Zdaa2WFBt84Uoamtm8rNYuSMh 557duXfm2pgAgzDMOK5QXb5OslT8JGuW6xBhaY77eqGTxOMED5VpuqUfHKjhGvmv+X en/vVNEE6VuOCswFbAwyBwtpqb0Xsp4pbv0LbnFOEJR8tk58sDSpB1JdkYzHwJTSDj hU6rWy1yzLca4lakMhl2x8+tQtLzdylB6TG9FbUkHS/USht7HvPlWCG7dC/65xs4aZ mGgrHLzHaexXGg2+gMSgfqkr+es/CdcBqADh33x9jo2MAtvyTh/GHnnn5DU+dxOVKO VSIL3pNE3fvng== Date: Mon, 29 Jul 2024 18:26:40 -0700 Subject: [PATCH 04/12] xfs_repair: add parent pointers when messing with /lost+found From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851540.1352527.8664055953754357445.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Make sure that the /lost+found gets created with parent pointers, and that lost children being put in there get new parent pointers. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- libxfs/libxfs_api_defs.h | 2 + repair/phase6.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index bf1d3c9d3..d3611e05b 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -52,6 +52,7 @@ #define xfs_attr_shortform_verify libxfs_attr_shortform_verify #define __xfs_bmap_add_free __libxfs_bmap_add_free +#define xfs_bmap_add_attrfork libxfs_bmap_add_attrfork #define xfs_bmap_validate_extent libxfs_bmap_validate_extent #define xfs_bmapi_read libxfs_bmapi_read #define xfs_bmapi_remap libxfs_bmapi_remap @@ -205,6 +206,7 @@ #define xfs_parent_addname libxfs_parent_addname #define xfs_parent_finish libxfs_parent_finish #define xfs_parent_hashval libxfs_parent_hashval +#define xfs_parent_lookup libxfs_parent_lookup #define xfs_parent_removename libxfs_parent_removename #define xfs_parent_start libxfs_parent_start #define xfs_parent_from_attr libxfs_parent_from_attr diff --git a/repair/phase6.c b/repair/phase6.c index 47dd9de27..791f7d36f 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -903,6 +903,12 @@ mk_orphanage(xfs_mount_t *mp) const int mode = 0755; int nres; struct xfs_name xname; + struct xfs_parent_args *ppargs = NULL; + + i = -libxfs_parent_start(mp, &ppargs); + if (i) + do_error(_("%d - couldn't allocate parent pointer for %s\n"), + i, ORPHANAGE); /* * check for an existing lost+found first, if it exists, return @@ -994,6 +1000,14 @@ mk_orphanage(xfs_mount_t *mp) _("can't make %s, createname error %d\n"), ORPHANAGE, error); + if (ppargs) { + error = -libxfs_parent_addname(tp, ppargs, pip, &xname, ip); + if (error) + do_error( + _("can't make %s, parent addname error %d\n"), + ORPHANAGE, error); + } + /* * bump up the link count in the root directory to account * for .. in the new directory, and update the irec copy of the @@ -1016,10 +1030,52 @@ mk_orphanage(xfs_mount_t *mp) libxfs_irele(ip); out_pip: libxfs_irele(pip); + libxfs_parent_finish(mp, ppargs); return(ino); } +/* + * Add a parent pointer back to the orphanage for any file we're moving into + * the orphanage, being careful not to trip over any existing parent pointer. + * You never know when the orphanage might get corrupted. + */ +static void +add_orphan_pptr( + struct xfs_trans *tp, + struct xfs_inode *orphanage_ip, + const struct xfs_name *xname, + struct xfs_inode *ip, + struct xfs_parent_args *ppargs) +{ + struct xfs_parent_rec pptr = { }; + struct xfs_da_args scratch; + int error; + + xfs_inode_to_parent_rec(&pptr, orphanage_ip); + error = -libxfs_parent_lookup(tp, ip, xname, &pptr, &scratch); + if (!error) + return; + if (error != ENOATTR) + do_log( + _("cannot look up parent pointer for '%.*s', err %d\n"), + xname->len, xname->name, error); + + if (!xfs_inode_has_attr_fork(ip)) { + error = -libxfs_bmap_add_attrfork(tp, ip, + sizeof(struct xfs_attr_sf_hdr), true); + if (error) + do_error(_("can't add attr fork to inode 0x%llx\n"), + (unsigned long long)ip->i_ino); + } + + error = -libxfs_parent_addname(tp, ppargs, orphanage_ip, xname, ip); + if (error) + do_error( + _("can't add parent pointer for '%.*s', error %d\n"), + xname->len, xname->name, error); +} + /* * move a file to the orphange. */ @@ -1040,6 +1096,13 @@ mv_orphanage( ino_tree_node_t *irec; int ino_offset = 0; struct xfs_name xname; + struct xfs_parent_args *ppargs; + + err = -libxfs_parent_start(mp, &ppargs); + if (err) + do_error( + _("%d - couldn't allocate parent pointer for lost inode\n"), + err); xname.name = fname; xname.len = snprintf((char *)fname, sizeof(fname), "%llu", @@ -1091,6 +1154,10 @@ mv_orphanage( do_error( _("name create failed in %s (%d)\n"), ORPHANAGE, err); + if (ppargs) + add_orphan_pptr(tp, orphanage_ip, &xname, + ino_p, ppargs); + if (irec) add_inode_ref(irec, ino_offset); else @@ -1125,6 +1192,10 @@ mv_orphanage( do_error( _("name create failed in %s (%d)\n"), ORPHANAGE, err); + if (ppargs) + add_orphan_pptr(tp, orphanage_ip, &xname, + ino_p, ppargs); + if (irec) add_inode_ref(irec, ino_offset); else @@ -1173,6 +1244,10 @@ mv_orphanage( _("name create failed in %s (%d)\n"), ORPHANAGE, err); ASSERT(err == 0); + if (ppargs) + add_orphan_pptr(tp, orphanage_ip, &xname, ino_p, + ppargs); + set_nlink(VFS_I(ino_p), 1); libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE); err = -libxfs_trans_commit(tp); @@ -1182,6 +1257,7 @@ mv_orphanage( } libxfs_irele(ino_p); libxfs_irele(orphanage_ip); + libxfs_parent_finish(mp, ppargs); } static int From patchwork Tue Jul 30 01:26:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746199 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 2490C8821 for ; Tue, 30 Jul 2024 01:26:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302817; cv=none; b=tyXzSqxafN7W75wHqM/QpQIFtzD9umaA0tDxYykz/N3fbePFVsK5xWqwl4abFRRyjcDzmRy+eGi08mqAneUnYUzumo+H0wFQec7efxViv4c0G32h1G5425JK/YQzXMl6OYBdY+9tQFLotRZOeqJWK8VInjSM31XlsuB7LbH5twg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302817; c=relaxed/simple; bh=1bFdOG1IwA+Hsw41x7teFnDMdQoHt+PN7YqKKL01sDI=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gKMMZJDUE+uVsTCrZYxZ3nW1RxFDT9jrDI2WACpYito3jaWc3jcvBMgh/qWbwthuSqB1FZJ/BNZSxYdJUX2x+qULHK4pbeZ1pTNKoKlOJ2uf4AoOzHhSLWp2qof1U2Ya2j5+HXqVqySTYlPCqskkNw3fLpVwGruX1BBF3eljBj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ff4tXLmC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ff4tXLmC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BDAE1C32786; Tue, 30 Jul 2024 01:26:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302816; bh=1bFdOG1IwA+Hsw41x7teFnDMdQoHt+PN7YqKKL01sDI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=ff4tXLmCy9DQv/mQtRFq0xTot+7CDfeWgMPLXrcAMF3q5osP/+foW8ct6485N4f+r ytqmRQfVEcflUR6RaV0MFjuYZakTX+rGqnvDPN43sqWr2D/qnZvX0y57eYGok7v+h+ Gj6Ww6KI4cTMQI+RnnHRVmNYD7d3Hz8yTt3gW/Mvrr107vezBfDPshRi5Y/K25ROOA QexHtbrxQhwQkjk/SiaxG6eN2Vu9K41hqDEy+/PYaHUMD+lNbxpepP0JdM9I+Cd3cV GMZpHIgtOx2tYbkOEOrnVc315byMs3Tr2uTvWWzTGOjItNJElWuu7aZMDiuxNUZT5y QXOZFx6OUY9VQ== Date: Mon, 29 Jul 2024 18:26:56 -0700 Subject: [PATCH 05/12] xfs_repair: junk duplicate hashtab entries when processing sf dirents From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851552.1352527.2090171438986207052.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 dir_hash_add() adds the passed-in dirent to the directory hashtab even if there's already a duplicate. Therefore, if we detect a duplicate or a garbage entry while processing the a shortform directory's entries, we need to junk the newly added entry, just like we do when processing directory data blocks. This will become particularly relevant in the next patch, where we generate a master index of parent pointers from the non-junked hashtab entries of each directory that phase6 scans. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/phase6.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/repair/phase6.c b/repair/phase6.c index 791f7d36f..9d41aad78 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -2562,6 +2562,7 @@ shortform_dir2_entry_check( struct xfs_dir2_sf_entry *next_sfep; struct xfs_ifork *ifp; struct ino_tree_node *irec; + xfs_dir2_dataptr_t diroffset; int max_size; int ino_offset; int i; @@ -2739,8 +2740,9 @@ shortform_dir2_entry_check( /* * check for duplicate names in directory. */ - dup_inum = dir_hash_add(mp, hashtab, (xfs_dir2_dataptr_t) - (sfep - xfs_dir2_sf_firstentry(sfp)), + diroffset = xfs_dir2_byte_to_dataptr( + xfs_dir2_sf_get_offset(sfep)); + dup_inum = dir_hash_add(mp, hashtab, diroffset, lino, sfep->namelen, sfep->name, libxfs_dir2_sf_get_ftype(mp, sfep)); if (dup_inum != NULLFSINO) { @@ -2775,6 +2777,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino, &max_size, &i, &bytes_deleted, ino_dirty); + dir_hash_junkit(hashtab, diroffset); continue; } else if (parent == ino) { add_inode_reached(irec, ino_offset); @@ -2799,6 +2802,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino, &max_size, &i, &bytes_deleted, ino_dirty); + dir_hash_junkit(hashtab, diroffset); continue; } } From patchwork Tue Jul 30 01:27:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746200 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 BB6C8881E for ; Tue, 30 Jul 2024 01:27:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302832; cv=none; b=eLrDooLwA4Z4OrbS5L1NOMdPRG7JTnTLDXhFpD5V6NoQPxDkorXevplSlYYZdnDQze9cm9jfn4Scebhv79GUBgflJZbXE03TKW1po3Nf33nyO6nuNIJKt4PwFHmLTFHeLrbM5pVTdkPNYFU6LjsRhjbSoBfNWoq2blprslXVYpA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302832; c=relaxed/simple; bh=1ocNy94w/IbF8tMUtYjbYT/obhqQ5rZq9gMsEJlEDwM=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=heKHkMdzhEy1LjC4fyZQITwjCWGWUIgh44cbMFicjG7eVrXGxN8OKiOrlzj0IeuCgLfzCD6IyS4B33IGbaSjMv+cPGxm/9NJ6mkqXCGvnQicQ/JT6WQ+VSs/hBNNlDpOR/l96yj1VEj519Qv286BIv6GXvfpvnF7OFWQM030LSM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fX8n4n7O; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fX8n4n7O" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 59159C32786; Tue, 30 Jul 2024 01:27:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302832; bh=1ocNy94w/IbF8tMUtYjbYT/obhqQ5rZq9gMsEJlEDwM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=fX8n4n7OdkOWMegHAbfl3ANMNgf/s4q74act/ijWtd9c0xEShAKR73NtO7p9flfTt PaLgypVg/UyGjJyBsLZhagIIBZbzsyUhWJaaXz+HqD8ho6aF1rtBkG3lw7d1DwiKvu WHbARKywI9XLYYnuPqiYGFG6OCHWNCc/G+UDYqdjUhUhglynfrRWwq4EWq6qGjNJZf uTvGyH9qTM+06mS7qjMc9QBb6ct//+tdR9SUwTFL9RpQ2qcZWPYV1T4CgRd9MSaXCs DJDQTpCfeQ5hdw4JFkZbh2ZTcJOk8o9VxwsdYMeXPpQCMb70XdS6xIUy6P6VoKB4Vd pqeig3L80rj9Q== Date: Mon, 29 Jul 2024 18:27:11 -0700 Subject: [PATCH 06/12] xfs_repair: build a parent pointer index From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851565.1352527.16538692942339755411.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 When we're walking directories during phase 6, build an index of parent pointers that we expect to find. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/Makefile | 2 + repair/phase6.c | 35 +++++++++ repair/pptr.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ repair/pptr.h | 15 ++++ 4 files changed, 267 insertions(+) create mode 100644 repair/pptr.c create mode 100644 repair/pptr.h diff --git a/repair/Makefile b/repair/Makefile index d94858878..b0acc8c46 100644 --- a/repair/Makefile +++ b/repair/Makefile @@ -24,6 +24,7 @@ HFILES = \ err_protos.h \ globals.h \ incore.h \ + pptr.h \ prefetch.h \ progress.h \ protos.h \ @@ -63,6 +64,7 @@ CFILES = \ phase5.c \ phase6.c \ phase7.c \ + pptr.c \ prefetch.c \ progress.c \ quotacheck.c \ diff --git a/repair/phase6.c b/repair/phase6.c index 9d41aad78..fe56feb6e 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -18,6 +18,7 @@ #include "dinode.h" #include "progress.h" #include "versions.h" +#include "repair/pptr.h" static struct cred zerocr; static struct fsxattr zerofsx; @@ -999,6 +1000,7 @@ mk_orphanage(xfs_mount_t *mp) do_error( _("can't make %s, createname error %d\n"), ORPHANAGE, error); + add_parent_ptr(ip->i_ino, (unsigned char *)ORPHANAGE, pip, false); if (ppargs) { error = -libxfs_parent_addname(tp, ppargs, pip, &xname, ip); @@ -1255,6 +1257,10 @@ mv_orphanage( do_error( _("orphanage name create failed (%d)\n"), err); } + + if (xfs_has_parent(mp)) + add_parent_ptr(ino_p->i_ino, xname.name, orphanage_ip, false); + libxfs_irele(ino_p); libxfs_irele(orphanage_ip); libxfs_parent_finish(mp, ppargs); @@ -2894,6 +2900,30 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " already points to ino %" PR } } +static void +dir_hash_add_parent_ptrs( + struct xfs_inode *dp, + struct dir_hash_tab *hashtab) +{ + struct dir_hash_ent *p; + + if (!xfs_has_parent(dp->i_mount)) + return; + + for (p = hashtab->first; p; p = p->nextbyorder) { + if (p->junkit) + continue; + if (p->name.name[0] == '/') + continue; + if (p->name.name[0] == '.' && + (p->name.len == 1 || + (p->name.len == 2 && p->name.name[1] == '.'))) + continue; + + add_parent_ptr(p->inum, p->name.name, dp, dotdot_update); + } +} + /* * processes all reachable inodes in directories */ @@ -3020,6 +3050,7 @@ _("error %d fixing shortform directory %llu\n"), default: break; } + dir_hash_add_parent_ptrs(ip, hashtab); dir_hash_done(hashtab); /* @@ -3311,6 +3342,8 @@ phase6(xfs_mount_t *mp) ino_tree_node_t *irec; int i; + parent_ptr_init(mp); + memset(&zerocr, 0, sizeof(struct cred)); memset(&zerofsx, 0, sizeof(struct fsxattr)); orphanage_ino = 0; @@ -3411,4 +3444,6 @@ _(" - resetting contents of realtime bitmap and summary inodes\n")); irec = next_ino_rec(irec); } } + + parent_ptr_free(mp); } diff --git a/repair/pptr.c b/repair/pptr.c new file mode 100644 index 000000000..193daa0a5 --- /dev/null +++ b/repair/pptr.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#include "libxfs.h" +#include "libxfs/xfile.h" +#include "libxfs/xfblob.h" +#include "libfrog/platform.h" +#include "repair/err_protos.h" +#include "repair/slab.h" +#include "repair/pptr.h" + +#undef PPTR_DEBUG + +#ifdef PPTR_DEBUG +# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0) +#else +# define dbg_printf(f, a...) +#endif + +/* + * Parent Pointer Validation + * ========================= + * + * Phase 6 validates the connectivity of the directory tree after validating + * that all the space metadata are correct, and confirming all the inodes that + * we intend to keep. The first part of phase 6 walks the directories of the + * filesystem to ensure that every file that isn't the root directory has a + * parent. Unconnected files are attached to the orphanage. Filesystems with + * the directory parent pointer feature enabled must also ensure that for every + * directory entry that points to a child file, that child has a matching + * parent pointer. + * + * There are many ways that we could check the parent pointers, but the means + * that we have chosen is to build a per-AG master index of all parent pointers + * of all inodes stored in that AG, and use that as the basis for comparison. + * This consumes a lot of memory, but performing both a forward scan to check + * dirent -> parent pointer and a backwards scan of parent pointer -> dirent + * takes longer than the simple method presented here. Userspace adds the + * additional twist that inodes are not cached (and there are no ILOCKs), which + * makes that approach even less attractive. + * + * During the directory walk at the start of phase 6, we transform each child + * directory entry found into its parent pointer equivalent. In other words, + * the forward information: + * + * (dir_ino, name, child_ino) + * + * becomes this backwards information: + * + * (child_agino*, dir_ino*, dir_gen, name*) + * + * Key fields are starred. + * + * This tuple is recorded in the per-AG master parent pointer index. Note + * that names are stored separately in an xfblob data structure so that the + * rest of the information can be sorted and processed as fixed-size records; + * the incore parent pointer record contains a pointer to the xfblob data. + */ + +struct ag_pptr { + /* parent directory handle */ + xfs_ino_t parent_ino; + uint32_t parent_gen; + + /* dirent name length */ + unsigned short namelen; + + /* AG_PPTR_* flags */ + unsigned short flags; + + /* cookie for the actual dirent name */ + xfblob_cookie name_cookie; + + /* agino of the child file */ + xfs_agino_t child_agino; + + /* hash of the dirent name */ + xfs_dahash_t namehash; +}; + +/* This might be a duplicate due to dotdot reprocessing */ +#define AG_PPTR_POSSIBLE_DUP (1U << 0) + +struct ag_pptrs { + /* Lock to protect pptr_recs during the dirent scan. */ + pthread_mutex_t lock; + + /* Parent pointer records for files in this AG. */ + struct xfs_slab *pptr_recs; +}; + +/* Global names storage file. */ +static struct xfblob *names; +static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct ag_pptrs *fs_pptrs; + +void +parent_ptr_free( + struct xfs_mount *mp) +{ + xfs_agnumber_t agno; + + if (!xfs_has_parent(mp)) + return; + + for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { + free_slab(&fs_pptrs[agno].pptr_recs); + pthread_mutex_destroy(&fs_pptrs[agno].lock); + } + free(fs_pptrs); + fs_pptrs = NULL; + + xfblob_destroy(names); +} + +void +parent_ptr_init( + struct xfs_mount *mp) +{ + char *descr; + xfs_agnumber_t agno; + int error; + + if (!xfs_has_parent(mp)) + return; + + descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names", + mp->m_fsname); + error = -xfblob_create(descr, &names); + kfree(descr); + if (error) + do_error(_("init parent pointer names failed: %s\n"), + strerror(error)); + + fs_pptrs = calloc(mp->m_sb.sb_agcount, sizeof(struct ag_pptrs)); + if (!fs_pptrs) + do_error( + _("init parent pointer per-AG record array failed: %s\n"), + strerror(errno)); + + for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { + error = pthread_mutex_init(&fs_pptrs[agno].lock, NULL); + if (error) + do_error( + _("init agno %u parent pointer lock failed: %s\n"), + agno, strerror(error)); + + error = -init_slab(&fs_pptrs[agno].pptr_recs, + sizeof(struct ag_pptr)); + if (error) + do_error( + _("init agno %u parent pointer recs failed: %s\n"), + agno, strerror(error)); + } +} + +/* Remember that @dp has a dirent (@fname, @ino). */ +void +add_parent_ptr( + xfs_ino_t ino, + const unsigned char *fname, + struct xfs_inode *dp, + bool possible_dup) +{ + struct xfs_mount *mp = dp->i_mount; + struct xfs_name dname = { + .name = fname, + .len = strlen((char *)fname), + }; + struct ag_pptr ag_pptr = { + .child_agino = XFS_INO_TO_AGINO(mp, ino), + .parent_ino = dp->i_ino, + .parent_gen = VFS_I(dp)->i_generation, + .namelen = dname.len, + }; + struct ag_pptrs *ag_pptrs; + xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ino); + int error; + + if (!xfs_has_parent(mp)) + return; + + if (possible_dup) + ag_pptr.flags |= AG_PPTR_POSSIBLE_DUP; + + ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname); + + pthread_mutex_lock(&names_mutex); + error = -xfblob_store(names, &ag_pptr.name_cookie, fname, + ag_pptr.namelen); + pthread_mutex_unlock(&names_mutex); + if (error) + do_error(_("storing name '%s' failed: %s\n"), + fname, strerror(error)); + + ag_pptrs = &fs_pptrs[agno]; + pthread_mutex_lock(&ag_pptrs->lock); + error = -slab_add(ag_pptrs->pptr_recs, &ag_pptr); + pthread_mutex_unlock(&ag_pptrs->lock); + if (error) + do_error(_("storing name '%s' key failed: %s\n"), + fname, strerror(error)); + + dbg_printf( + _("%s: dp %llu gen 0x%x fname '%s' namehash 0x%x ino %llu namecookie 0x%llx\n"), + __func__, + (unsigned long long)dp->i_ino, + VFS_I(dp)->i_generation, + fname, + ag_pptr.namehash, + (unsigned long long)ino, + (unsigned long long)ag_pptr.name_cookie); +} diff --git a/repair/pptr.h b/repair/pptr.h new file mode 100644 index 000000000..752237942 --- /dev/null +++ b/repair/pptr.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#ifndef __REPAIR_PPTR_H__ +#define __REPAIR_PPTR_H__ + +void parent_ptr_free(struct xfs_mount *mp); +void parent_ptr_init(struct xfs_mount *mp); + +void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname, + struct xfs_inode *dp, bool possible_dup); + +#endif /* __REPAIR_PPTR_H__ */ From patchwork Tue Jul 30 01:27:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746201 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 69CD679E1 for ; Tue, 30 Jul 2024 01:27:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302848; cv=none; b=KAMkAw2eb0nonQt9Kn5q27xqlOLzx0hiRun0ijGzB1VsO4ZJ+Rgy1KO3hu5Ehdplb9A+94YJBaUwwEqIRfgmweQ4t00MeNYk8fIbqWknRaigoNUZApoT9RTmL3E07txVw82xf6BlwyfNkfn8zh0ud+dNccAzctKQm6FDzRSbMXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302848; c=relaxed/simple; bh=jDZO6fH0jCpXlxZH3VmMBL4xEuA4GCCj2cFzfRbboxo=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iMsUQywoI4jeMDehtj0PmSgwggoTbAtvNuNalTZE6WPkqUKA3+7V8rhQWh+MT9b4u5WMQkV5cYURNXNAppBWFfXix/EhicxnAYUOmlxTegZ0aCZ5GX3HfR7u5SUNAiUBjtu+o1AOLFYZbbvLn3lqDG8emOHQVyXJQ/8Aic2yMro= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kb1YEeKk; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kb1YEeKk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 013B6C32786; Tue, 30 Jul 2024 01:27:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302848; bh=jDZO6fH0jCpXlxZH3VmMBL4xEuA4GCCj2cFzfRbboxo=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=kb1YEeKkEFCvfGCmM7p5rxQTkZOvLfdqnSmKFUuSfRls3g2m1ADt2mJdB/9I+5crJ gSa0uPG60nW54brg9bAUShPOMIouLwC9XajwqwOm8dQU29bRAxSwyf9IPc2ahjKz5B aP4TTP6WpU+AQUkhMELsT4qPLX35ZaHiLYWLUMuvbvdjj7fFMzPA6uswegW8ZheXCF FtU/ydccTzWl1X2z7i0/SSxNkXUmXIwQ6myuJc8S/Ww6v6pYXMlMHncNvvK3inBEcf RCRzRbWKL7f3aQbXeF6mJoxzmZCRTeVMhl/ZzMlUfp1+7LrB129IeUBMyOXFTxUnFK BhamtyeQkCEPA== Date: Mon, 29 Jul 2024 18:27:27 -0700 Subject: [PATCH 07/12] xfs_repair: move the global dirent name store to a separate object From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851577.1352527.16558988859597637029.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Abstract the main parent pointer dirent names xfblob object into a separate data structure to hide implementation details. The goals here are (a) reduce memory usage when we can by deduplicating dirent names that exist in multiple directories; and (b) provide a unique id for each name in the system so that sorting incore parent pointer records can be done in a stable manner. Fast stable sorting of records is required for the dirent <-> pptr matching algorithm. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/Makefile | 2 + repair/pptr.c | 11 ++++--- repair/strblobs.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ repair/strblobs.h | 19 +++++++++++++ 4 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 repair/strblobs.c create mode 100644 repair/strblobs.h diff --git a/repair/Makefile b/repair/Makefile index b0acc8c46..a36a95e35 100644 --- a/repair/Makefile +++ b/repair/Makefile @@ -35,6 +35,7 @@ HFILES = \ rt.h \ scan.h \ slab.h \ + strblobs.h \ threads.h \ versions.h @@ -75,6 +76,7 @@ CFILES = \ sb.c \ scan.c \ slab.c \ + strblobs.c \ threads.c \ versions.c \ xfs_repair.c diff --git a/repair/pptr.c b/repair/pptr.c index 193daa0a5..9f219b398 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -10,6 +10,7 @@ #include "repair/err_protos.h" #include "repair/slab.h" #include "repair/pptr.h" +#include "repair/strblobs.h" #undef PPTR_DEBUG @@ -56,7 +57,7 @@ * This tuple is recorded in the per-AG master parent pointer index. Note * that names are stored separately in an xfblob data structure so that the * rest of the information can be sorted and processed as fixed-size records; - * the incore parent pointer record contains a pointer to the xfblob data. + * the incore parent pointer record contains a pointer to the strblob data. */ struct ag_pptr { @@ -92,7 +93,7 @@ struct ag_pptrs { }; /* Global names storage file. */ -static struct xfblob *names; +static struct strblobs *nameblobs; static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER; static struct ag_pptrs *fs_pptrs; @@ -112,7 +113,7 @@ parent_ptr_free( free(fs_pptrs); fs_pptrs = NULL; - xfblob_destroy(names); + strblobs_destroy(&nameblobs); } void @@ -128,7 +129,7 @@ parent_ptr_init( descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names", mp->m_fsname); - error = -xfblob_create(descr, &names); + error = strblobs_init(descr, &nameblobs); kfree(descr); if (error) do_error(_("init parent pointer names failed: %s\n"), @@ -188,7 +189,7 @@ add_parent_ptr( ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname); pthread_mutex_lock(&names_mutex); - error = -xfblob_store(names, &ag_pptr.name_cookie, fname, + error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname, ag_pptr.namelen); pthread_mutex_unlock(&names_mutex); if (error) diff --git a/repair/strblobs.c b/repair/strblobs.c new file mode 100644 index 000000000..45d2559c7 --- /dev/null +++ b/repair/strblobs.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#include "libxfs.h" +#include "libxfs/xfile.h" +#include "libxfs/xfblob.h" +#include "repair/strblobs.h" + +/* + * String Blob Structure + * ===================== + * + * This data structure wraps the storage of strings with explicit length in an + * xfblob structure. + */ +struct strblobs { + struct xfblob *strings; +}; + +/* Initialize a string blob structure. */ +int +strblobs_init( + const char *descr, + struct strblobs **sblobs) +{ + struct strblobs *sb; + int error; + + sb = malloc(sizeof(struct strblobs)); + if (!sb) + return ENOMEM; + + error = -xfblob_create(descr, &sb->strings); + if (error) + goto out_free; + + *sblobs = sb; + return 0; + +out_free: + free(sb); + return error; +} + +/* Deconstruct a string blob structure. */ +void +strblobs_destroy( + struct strblobs **sblobs) +{ + struct strblobs *sb = *sblobs; + + xfblob_destroy(sb->strings); + free(sb); + *sblobs = NULL; +} + +/* Store a string and return a cookie for its retrieval. */ +int +strblobs_store( + struct strblobs *sblobs, + xfblob_cookie *str_cookie, + const unsigned char *str, + unsigned int str_len) +{ + return -xfblob_store(sblobs->strings, str_cookie, str, str_len); +} + +/* Retrieve a previously stored string. */ +int +strblobs_load( + struct strblobs *sblobs, + xfblob_cookie str_cookie, + unsigned char *str, + unsigned int str_len) +{ + return -xfblob_load(sblobs->strings, str_cookie, str, str_len); +} diff --git a/repair/strblobs.h b/repair/strblobs.h new file mode 100644 index 000000000..27e98eee2 --- /dev/null +++ b/repair/strblobs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#ifndef __REPAIR_STRBLOBS_H__ +#define __REPAIR_STRBLOBS_H__ + +struct strblobs; + +int strblobs_init(const char *descr, struct strblobs **sblobs); +void strblobs_destroy(struct strblobs **sblobs); + +int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie, + const unsigned char *str, unsigned int str_len); +int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie, + unsigned char *str, unsigned int str_len); + +#endif /* __REPAIR_STRBLOBS_H__ */ From patchwork Tue Jul 30 01:27:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746202 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 1C70179E1 for ; Tue, 30 Jul 2024 01:27:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302864; cv=none; b=CK2RhQBk6CO7qL3XD7W3jI+5LlCFrWBEcQaNHp7o54QZU/WzY1XZ8F8T/dYkk+njA3PKiOv2CntzVvRQp1B4TRtNhVPjLS5nrWmVg4ztBKlfZmrlDMWFkxbTwIaDO+DIlg3XFPaEakYEpCTaib6BcC7XihN2bHaPWxP5ZwZgNHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302864; c=relaxed/simple; bh=u3007zI65Nr7bsUX+kYnKHB7x9TLDULJegU5EjLJDqE=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ajXk4gFdevuXSba/bxgXLBTME6Dbitmt6ComWHp3ypAmH0ZcUOrrGsVl3cvwiUoL2KWUVznhyqnIkp9sa93Cx0nNeQWVthgdULwA1qpdw/NUQLM74kVda8E4CIgowYAPSCuDcQMP+rG8KdrgsYkz0O4Fxab4n4DnRPvxMXFj35Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=P/DaxTrE; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="P/DaxTrE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 92A2CC32786; Tue, 30 Jul 2024 01:27:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302863; bh=u3007zI65Nr7bsUX+kYnKHB7x9TLDULJegU5EjLJDqE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=P/DaxTrE2rAf2MhGng/0gX/5C9N3h84Z3ZiJRbQ+NWxxpJM7vU0tQZePJ/P0Hypat kEPuEKIW63y1vVGnm9sSCrMxARs34iQUuVopdHzmxdVvQ73NETBE6kM72pFSqHYuY5 DqOgqAcbo49HYvOzToymq2yZ+KijQFnpWtUTTIagIec1dICTR/D9v0Eu1w1Bh+9Wyk v1bgAhnWoHK6UmCcKq9BUt5UyNctdU8k4Z+djtdXqr43Nd18ejiaKCcnZvVOPn0U1c Yxg/7vBk130+VohiIi5IvdNR5fgu71tP8yOqr3Vd8IYSef5BBEH+lVBuJCeG54jAUM A6z1bYOZYXjNw== Date: Mon, 29 Jul 2024 18:27:43 -0700 Subject: [PATCH 08/12] xfs_repair: deduplicate strings stored in string blob From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851590.1352527.9464164025188189424.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Reduce the memory requirements of the string blob structure by deduplicating the strings stored within. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/pptr.c | 13 ++++- repair/strblobs.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++-- repair/strblobs.h | 9 +++ 3 files changed, 153 insertions(+), 9 deletions(-) diff --git a/repair/pptr.c b/repair/pptr.c index 9f219b398..f8db57b2c 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -50,7 +50,7 @@ * * becomes this backwards information: * - * (child_agino*, dir_ino*, dir_gen, name*) + * (child_agino*, dir_ino*, dir_gen, name_cookie*) * * Key fields are starred. * @@ -58,6 +58,10 @@ * that names are stored separately in an xfblob data structure so that the * rest of the information can be sorted and processed as fixed-size records; * the incore parent pointer record contains a pointer to the strblob data. + * Because string blobs are deduplicated, there's a 1:1 mapping of name cookies + * to strings, which means that we can use the name cookie as a comparison key + * instead of loading the full dentry name every time we want to perform a + * comparison. */ struct ag_pptr { @@ -121,15 +125,18 @@ parent_ptr_init( struct xfs_mount *mp) { char *descr; + uint64_t iused; xfs_agnumber_t agno; int error; if (!xfs_has_parent(mp)) return; + /* One hash bucket per inode, up to about 8M of memory on 64-bit. */ + iused = min(mp->m_sb.sb_icount - mp->m_sb.sb_ifree, 1048573); descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): parent pointer names", mp->m_fsname); - error = strblobs_init(descr, &nameblobs); + error = strblobs_init(descr, iused, &nameblobs); kfree(descr); if (error) do_error(_("init parent pointer names failed: %s\n"), @@ -190,7 +197,7 @@ add_parent_ptr( pthread_mutex_lock(&names_mutex); error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname, - ag_pptr.namelen); + ag_pptr.namelen, ag_pptr.namehash); pthread_mutex_unlock(&names_mutex); if (error) do_error(_("storing name '%s' failed: %s\n"), diff --git a/repair/strblobs.c b/repair/strblobs.c index 45d2559c7..3cd678dbb 100644 --- a/repair/strblobs.c +++ b/repair/strblobs.c @@ -13,22 +13,42 @@ * ===================== * * This data structure wraps the storage of strings with explicit length in an - * xfblob structure. + * xfblob structure. It stores a hashtable of string checksums to provide + * fast(ish) lookups of existing strings to enable deduplication of the strings + * contained within. */ +struct strblob_hashent { + struct strblob_hashent *next; + + xfblob_cookie str_cookie; + unsigned int str_len; + xfs_dahash_t str_hash; +}; + struct strblobs { struct xfblob *strings; + unsigned int nr_buckets; + + struct strblob_hashent *buckets[]; }; +static inline size_t strblobs_sizeof(unsigned int nr_buckets) +{ + return sizeof(struct strblobs) + + (nr_buckets * sizeof(struct strblobs_hashent *)); +} + /* Initialize a string blob structure. */ int strblobs_init( const char *descr, + unsigned int hash_buckets, struct strblobs **sblobs) { struct strblobs *sb; int error; - sb = malloc(sizeof(struct strblobs)); + sb = calloc(strblobs_sizeof(hash_buckets), 1); if (!sb) return ENOMEM; @@ -36,6 +56,7 @@ strblobs_init( if (error) goto out_free; + sb->nr_buckets = hash_buckets; *sblobs = sb; return 0; @@ -50,21 +71,132 @@ strblobs_destroy( struct strblobs **sblobs) { struct strblobs *sb = *sblobs; + struct strblob_hashent *ent, *ent_next; + unsigned int bucket; + + for (bucket = 0; bucket < sb->nr_buckets; bucket++) { + ent = sb->buckets[bucket]; + while (ent != NULL) { + ent_next = ent->next; + free(ent); + ent = ent_next; + } + } xfblob_destroy(sb->strings); free(sb); *sblobs = NULL; } +/* + * Search the string hashtable for a matching entry. Sets sets the cookie and + * returns 0 if one is found; ENOENT if there is no match; or a positive errno. + */ +static int +__strblobs_lookup( + struct strblobs *sblobs, + xfblob_cookie *str_cookie, + const unsigned char *str, + unsigned int str_len, + xfs_dahash_t str_hash) +{ + struct strblob_hashent *ent; + unsigned char *buf = NULL; + unsigned int bucket; + int error; + + bucket = str_hash % sblobs->nr_buckets; + ent = sblobs->buckets[bucket]; + + for (ent = sblobs->buckets[bucket]; ent != NULL; ent = ent->next) { + if (ent->str_len != str_len || ent->str_hash != str_hash) + continue; + + if (!buf) { + buf = malloc(str_len); + if (!buf) + return ENOMEM; + } + + error = strblobs_load(sblobs, ent->str_cookie, buf, str_len); + if (error) + goto out; + + if (memcmp(str, buf, str_len)) + continue; + + *str_cookie = ent->str_cookie; + goto out; + } + error = ENOENT; + +out: + free(buf); + return error; +} + +/* + * Search the string hashtable for a matching entry. Sets sets the cookie and + * returns 0 if one is found; ENOENT if there is no match; or a positive errno. + */ +int +strblobs_lookup( + struct strblobs *sblobs, + xfblob_cookie *str_cookie, + const unsigned char *str, + unsigned int str_len, + xfs_dahash_t str_hash) +{ + return __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash); +} + +/* Remember a string in the hashtable. */ +static int +strblobs_hash( + struct strblobs *sblobs, + xfblob_cookie str_cookie, + const unsigned char *str, + unsigned int str_len, + xfs_dahash_t str_hash) +{ + struct strblob_hashent *ent; + unsigned int bucket; + + bucket = str_hash % sblobs->nr_buckets; + + ent = malloc(sizeof(struct strblob_hashent)); + if (!ent) + return ENOMEM; + + ent->str_cookie = str_cookie; + ent->str_len = str_len; + ent->str_hash = str_hash; + ent->next = sblobs->buckets[bucket]; + + sblobs->buckets[bucket] = ent; + return 0; +} + /* Store a string and return a cookie for its retrieval. */ int strblobs_store( struct strblobs *sblobs, xfblob_cookie *str_cookie, const unsigned char *str, - unsigned int str_len) + unsigned int str_len, + xfs_dahash_t str_hash) { - return -xfblob_store(sblobs->strings, str_cookie, str, str_len); + int error; + + error = __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash); + if (error != ENOENT) + return error; + + error = -xfblob_store(sblobs->strings, str_cookie, str, str_len); + if (error) + return error; + + return strblobs_hash(sblobs, *str_cookie, str, str_len, str_hash); } /* Retrieve a previously stored string. */ diff --git a/repair/strblobs.h b/repair/strblobs.h index 27e98eee2..40cd6d8e9 100644 --- a/repair/strblobs.h +++ b/repair/strblobs.h @@ -8,12 +8,17 @@ struct strblobs; -int strblobs_init(const char *descr, struct strblobs **sblobs); +int strblobs_init(const char *descr, unsigned int hash_buckets, + struct strblobs **sblobs); void strblobs_destroy(struct strblobs **sblobs); int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie, - const unsigned char *str, unsigned int str_len); + const unsigned char *str, unsigned int str_len, + xfs_dahash_t hash); int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie, unsigned char *str, unsigned int str_len); +int strblobs_lookup(struct strblobs *sblobs, xfblob_cookie *str_cookie, + const unsigned char *str, unsigned int str_len, + xfs_dahash_t hash); #endif /* __REPAIR_STRBLOBS_H__ */ From patchwork Tue Jul 30 01:27:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746203 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 AD78779E1 for ; Tue, 30 Jul 2024 01:27:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302879; cv=none; b=qxM9Y0R3cENUydhpFn1Wk7bufqVXLNJV40kdC/VZWbs5kG1Ke1C5zvH0ViclVZKtASU9WKnoB97bpnpHe4+EbX94Otxa+ctFgw7saIh1g2AoGU2kWyTygrlM9xiXyKi2DXuA2HOUyDlJlRh0gP1o24ioW7OpUMJ9E+mMpC9p6qs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302879; c=relaxed/simple; bh=X08u5xmkZOCY56Q2EfJlgwiF9xI3xPJcTGG7VVoaOrM=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ard6nQfS6MfuM5jQ2EHHyW4T/RlUQ5mEi528ar5QsGzeL2a9kFdJrv/zgg9U+iZzPVWVGtTY+L0o8jF2I6sWCafoQI9w9RHxmTqLaVxn98zkQNavz5+i6efRMxmheDgf/vy29K7jZf425Im+/5e8aaU/B54/U+rMXvp1mPmPk8M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=l92ZLH07; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="l92ZLH07" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3A774C32786; Tue, 30 Jul 2024 01:27:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302879; bh=X08u5xmkZOCY56Q2EfJlgwiF9xI3xPJcTGG7VVoaOrM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=l92ZLH07VTDC6iB5zFZSOVQbte70z5ws1L3ogjnaeopeKK8PdVYUrAGTA8FCwWhRq 4I+cF+eyJ7U8D5lvsbHyqckZtNJhAqxk1YkclGJEqcybvUqrsFlqNPMsFQ/9k8tne3 mYbWK2N8ZPSAXOmjVVTa2iQn49xvMYzUaj0kngDUY4M1SIps1sXwfnklyPTaG1cNXP VoIITfKYR8cGeW74knZnaE0+8jKTk7dZ+A98jk7bn3yf/dbCzwji6HjIr7rSYQU93M Z5g7XyZiC35n/sPKOQS/TD1lNphmZhKNPOooT63Z/4UUoTVURwq4j5WTv/ldSrtDFZ ZUREF4vO6X/6w== Date: Mon, 29 Jul 2024 18:27:58 -0700 Subject: [PATCH 09/12] xfs_repair: check parent pointers From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851602.1352527.7408410125266746673.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Use the parent pointer index that we constructed in the previous patch to check that each file's parent pointer records exactly match the directory entries that we recorded while walking directory entries. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- libxfs/xfblob.c | 9 + libxfs/xfblob.h | 2 repair/Makefile | 2 repair/listxattr.c | 271 +++++++++++++++++ repair/listxattr.h | 15 + repair/phase6.c | 2 repair/pptr.c | 846 ++++++++++++++++++++++++++++++++++++++++++++++++++++ repair/pptr.h | 2 8 files changed, 1149 insertions(+) create mode 100644 repair/listxattr.c create mode 100644 repair/listxattr.h diff --git a/libxfs/xfblob.c b/libxfs/xfblob.c index 7d8caaa4c..00f8ed5e5 100644 --- a/libxfs/xfblob.c +++ b/libxfs/xfblob.c @@ -145,3 +145,12 @@ xfblob_free( xfile_discard(blob->xfile, cookie, sizeof(key) + key.xb_size); return 0; } + +/* Drop all the blobs. */ +void +xfblob_truncate( + struct xfblob *blob) +{ + xfile_discard(blob->xfile, PAGE_SIZE, blob->last_offset - PAGE_SIZE); + blob->last_offset = PAGE_SIZE; +} diff --git a/libxfs/xfblob.h b/libxfs/xfblob.h index 28bf4ab28..1939202e1 100644 --- a/libxfs/xfblob.h +++ b/libxfs/xfblob.h @@ -21,4 +21,6 @@ int xfblob_store(struct xfblob *blob, xfblob_cookie *cookie, const void *ptr, uint32_t size); int xfblob_free(struct xfblob *blob, xfblob_cookie cookie); +void xfblob_truncate(struct xfblob *blob); + #endif /* __XFS_SCRUB_XFBLOB_H__ */ diff --git a/repair/Makefile b/repair/Makefile index a36a95e35..e7445d53e 100644 --- a/repair/Makefile +++ b/repair/Makefile @@ -24,6 +24,7 @@ HFILES = \ err_protos.h \ globals.h \ incore.h \ + listxattr.h \ pptr.h \ prefetch.h \ progress.h \ @@ -58,6 +59,7 @@ CFILES = \ incore_ext.c \ incore_ino.c \ init.c \ + listxattr.c \ phase1.c \ phase2.c \ phase3.c \ diff --git a/repair/listxattr.c b/repair/listxattr.c new file mode 100644 index 000000000..2af77b7b2 --- /dev/null +++ b/repair/listxattr.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#include "libxfs.h" +#include "libxlog.h" +#include "libfrog/bitmap.h" +#include "repair/listxattr.h" + +/* Call a function for every entry in a shortform xattr structure. */ +STATIC int +xattr_walk_sf( + struct xfs_inode *ip, + xattr_walk_fn attr_fn, + void *priv) +{ + struct xfs_attr_sf_hdr *hdr = ip->i_af.if_data; + struct xfs_attr_sf_entry *sfe; + unsigned int i; + int error; + + sfe = libxfs_attr_sf_firstentry(hdr); + for (i = 0; i < hdr->count; i++) { + error = attr_fn(ip, sfe->flags, sfe->nameval, sfe->namelen, + &sfe->nameval[sfe->namelen], sfe->valuelen, + priv); + if (error) + return error; + + sfe = xfs_attr_sf_nextentry(sfe); + } + + return 0; +} + +/* Call a function for every entry in this xattr leaf block. */ +STATIC int +xattr_walk_leaf_entries( + struct xfs_inode *ip, + xattr_walk_fn attr_fn, + struct xfs_buf *bp, + void *priv) +{ + struct xfs_attr3_icleaf_hdr ichdr; + struct xfs_mount *mp = ip->i_mount; + struct xfs_attr_leafblock *leaf = bp->b_addr; + struct xfs_attr_leaf_entry *entry; + unsigned int i; + int error; + + libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf); + entry = xfs_attr3_leaf_entryp(leaf); + + for (i = 0; i < ichdr.count; entry++, i++) { + void *value; + unsigned char *name; + unsigned int namelen, valuelen; + + if (entry->flags & XFS_ATTR_LOCAL) { + struct xfs_attr_leaf_name_local *name_loc; + + name_loc = xfs_attr3_leaf_name_local(leaf, i); + name = name_loc->nameval; + namelen = name_loc->namelen; + value = &name_loc->nameval[name_loc->namelen]; + valuelen = be16_to_cpu(name_loc->valuelen); + } else { + struct xfs_attr_leaf_name_remote *name_rmt; + + name_rmt = xfs_attr3_leaf_name_remote(leaf, i); + name = name_rmt->name; + namelen = name_rmt->namelen; + value = NULL; + valuelen = be32_to_cpu(name_rmt->valuelen); + } + + error = attr_fn(ip, entry->flags, name, namelen, value, + valuelen, priv); + if (error) + return error; + + } + + return 0; +} + +/* + * Call a function for every entry in a leaf-format xattr structure. Avoid + * memory allocations for the loop detector since there's only one block. + */ +STATIC int +xattr_walk_leaf( + struct xfs_inode *ip, + xattr_walk_fn attr_fn, + void *priv) +{ + struct xfs_buf *leaf_bp; + int error; + + error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp); + if (error) + return error; + + error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp, priv); + libxfs_trans_brelse(NULL, leaf_bp); + return error; +} + +/* Find the leftmost leaf in the xattr dabtree. */ +STATIC int +xattr_walk_find_leftmost_leaf( + struct xfs_inode *ip, + struct bitmap *seen_blocks, + struct xfs_buf **leaf_bpp) +{ + struct xfs_da3_icnode_hdr nodehdr; + struct xfs_mount *mp = ip->i_mount; + struct xfs_da_intnode *node; + struct xfs_da_node_entry *btree; + struct xfs_buf *bp; + //xfs_failaddr_t fa; + xfs_dablk_t blkno = 0; + unsigned int expected_level = 0; + int error; + + for (;;) { + uint16_t magic; + + error = -libxfs_da3_node_read(NULL, ip, blkno, &bp, + XFS_ATTR_FORK); + if (error) + return error; + + node = bp->b_addr; + magic = be16_to_cpu(node->hdr.info.magic); + if (magic == XFS_ATTR_LEAF_MAGIC || + magic == XFS_ATTR3_LEAF_MAGIC) + break; + + error = EFSCORRUPTED; + if (magic != XFS_DA_NODE_MAGIC && + magic != XFS_DA3_NODE_MAGIC) + goto out_buf; + + libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node); + + if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH) + goto out_buf; + + /* Check the level from the root node. */ + if (blkno == 0) + expected_level = nodehdr.level - 1; + else if (expected_level != nodehdr.level) + goto out_buf; + else + expected_level--; + + /* Remember that we've seen this node. */ + error = -bitmap_set(seen_blocks, blkno, 1); + if (error) + goto out_buf; + + /* Find the next level towards the leaves of the dabtree. */ + btree = nodehdr.btree; + blkno = be32_to_cpu(btree->before); + libxfs_trans_brelse(NULL, bp); + + /* Make sure we haven't seen this new block already. */ + if (bitmap_test(seen_blocks, blkno, 1)) + return EFSCORRUPTED; + } + + error = EFSCORRUPTED; + if (expected_level != 0) + goto out_buf; + + /* Remember that we've seen this leaf. */ + error = -bitmap_set(seen_blocks, blkno, 1); + if (error) + goto out_buf; + + *leaf_bpp = bp; + return 0; + +out_buf: + libxfs_trans_brelse(NULL, bp); + return error; +} + +/* Call a function for every entry in a node-format xattr structure. */ +STATIC int +xattr_walk_node( + struct xfs_inode *ip, + xattr_walk_fn attr_fn, + void *priv) +{ + struct xfs_attr3_icleaf_hdr leafhdr; + struct bitmap *seen_blocks; + struct xfs_mount *mp = ip->i_mount; + struct xfs_attr_leafblock *leaf; + struct xfs_buf *leaf_bp; + int error; + + bitmap_alloc(&seen_blocks); + + error = xattr_walk_find_leftmost_leaf(ip, seen_blocks, &leaf_bp); + if (error) + goto out_bitmap; + + for (;;) { + error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp, + priv); + if (error) + goto out_leaf; + + /* Find the right sibling of this leaf block. */ + leaf = leaf_bp->b_addr; + libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); + if (leafhdr.forw == 0) + goto out_leaf; + + libxfs_trans_brelse(NULL, leaf_bp); + + /* Make sure we haven't seen this new leaf already. */ + if (bitmap_test(seen_blocks, leafhdr.forw, 1)) + goto out_bitmap; + + error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, + leafhdr.forw, &leaf_bp); + if (error) + goto out_bitmap; + + /* Remember that we've seen this new leaf. */ + error = -bitmap_set(seen_blocks, leafhdr.forw, 1); + if (error) + goto out_leaf; + } + +out_leaf: + libxfs_trans_brelse(NULL, leaf_bp); +out_bitmap: + bitmap_free(&seen_blocks); + return error; +} + +/* Call a function for every extended attribute in a file. */ +int +xattr_walk( + struct xfs_inode *ip, + xattr_walk_fn attr_fn, + void *priv) +{ + int error; + + if (!libxfs_inode_hasattr(ip)) + return 0; + + if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL) + return xattr_walk_sf(ip, attr_fn, priv); + + /* attr functions require that the attr fork is loaded */ + error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK); + if (error) + return error; + + if (libxfs_attr_is_leaf(ip)) + return xattr_walk_leaf(ip, attr_fn, priv); + + return xattr_walk_node(ip, attr_fn, priv); +} diff --git a/repair/listxattr.h b/repair/listxattr.h new file mode 100644 index 000000000..2d26fce0f --- /dev/null +++ b/repair/listxattr.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#ifndef __REPAIR_LISTXATTR_H__ +#define __REPAIR_LISTXATTR_H__ + +typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags, + const unsigned char *name, unsigned int namelen, + const void *value, unsigned int valuelen, void *priv); + +int xattr_walk(struct xfs_inode *ip, xattr_walk_fn attr_fn, void *priv); + +#endif /* __REPAIR_LISTXATTR_H__ */ diff --git a/repair/phase6.c b/repair/phase6.c index fe56feb6e..ad067ba0a 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -3445,5 +3445,7 @@ _(" - resetting contents of realtime bitmap and summary inodes\n")); } } + /* Check and repair directory parent pointers, if enabled. */ + check_parent_ptrs(mp); parent_ptr_free(mp); } diff --git a/repair/pptr.c b/repair/pptr.c index f8db57b2c..bd9bb6f8b 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -7,8 +7,13 @@ #include "libxfs/xfile.h" #include "libxfs/xfblob.h" #include "libfrog/platform.h" +#include "libfrog/workqueue.h" +#include "repair/globals.h" #include "repair/err_protos.h" #include "repair/slab.h" +#include "repair/listxattr.h" +#include "repair/threads.h" +#include "repair/incore.h" #include "repair/pptr.h" #include "repair/strblobs.h" @@ -62,6 +67,65 @@ * to strings, which means that we can use the name cookie as a comparison key * instead of loading the full dentry name every time we want to perform a * comparison. + * + * Once we've finished with the forward scan, we get to work on the backwards + * scan. Each AG is processed independently. First, we sort the per-AG master + * records in order of child_agino, dir_ino, and name_cookie. Each inode in + * the AG is then processed in numerical order. + * + * The first thing that happens to the file is that we read all the extended + * attributes to look for parent pointers. Attributes that claim to be parent + * pointers but are obviously garbage are thrown away. The rest of the ondisk + * parent pointers for that file are stored in memory like this: + * + * (dir_ino*, dir_gen, name_cookie*) + * + * After loading the ondisk parent pointer name, we search the strblobs + * structure to see if it has already recorded the name. If so, this value is + * used as the name cookie. If the name has not yet been recorded, we flag the + * incore record for later deletion. + * + * When we've concluded the xattr scan, the per-file records are sorted in + * order of dir_ino and name_cookie. + * + * There are three possibilities here: + * + * A. The first record in the per-AG master index is an exact match for the + * first record in the per-file index. Everything is consistent, and we can + * proceed with the lockstep scan detailed below. + * + * B. The per-AG master index cursor points to a higher inode number than the + * first inode we are scanning. Delete the ondisk parent pointers + * corresponding to the per-file records until condition (B) is no longer true. + * + * C. The per-AG master index cursor instead points to a lower inode number + * than the one we are scanning. This means that there exists a directory + * entry pointing at an inode that is free. We supposedly already settled + * which inodes are free and which aren't, which means in-memory information is + * inconsistent. Abort. + * + * Otherwise, we are ready to check the file parent pointers against the + * master. If the ondisk directory metadata are all consistent, this recordset + * should correspond exactly to the subset of the master records with a + * child_agino matching the file that we're scanning. We should be able to + * walk both sets in lockstep, and find one of the following outcomes: + * + * 1) The master index cursor is ahead of the ondisk index cursor. This means + * that the inode has parent pointers that were not found during the dirent + * scan. These should be deleted. + * + * 2) The ondisk index gets ahead of the master index. This means that the + * dirent scan found parent pointers that are not attached to the inode. + * These should be added. + * + * 3) The parent_gen or (dirent) name are not consistent. Update the parent + * pointer to the values that we found during the dirent scan. + * + * 4) Everything matches. Move on to the next parent pointer. + * + * The current implementation does not try to rebuild directories from parent + * pointer information, as this requires a lengthy scan of the filesystem for + * each broken directory. */ struct ag_pptr { @@ -88,6 +152,24 @@ struct ag_pptr { /* This might be a duplicate due to dotdot reprocessing */ #define AG_PPTR_POSSIBLE_DUP (1U << 0) +struct file_pptr { + /* parent directory handle */ + xfs_ino_t parent_ino; + uint32_t parent_gen; + + /* Is the name stored in the global nameblobs structure? */ + unsigned int name_in_nameblobs; + + /* hash of the dirent name */ + xfs_dahash_t namehash; + + /* parent pointer name length */ + unsigned int namelen; + + /* cookie for the file dirent name */ + xfblob_cookie name_cookie; +}; + struct ag_pptrs { /* Lock to protect pptr_recs during the dirent scan. */ pthread_mutex_t lock; @@ -96,11 +178,99 @@ struct ag_pptrs { struct xfs_slab *pptr_recs; }; +struct file_scan { + struct ag_pptrs *ag_pptrs; + + /* cursor for comparing ag_pptrs.pptr_recs against file_pptrs_recs */ + struct xfs_slab_cursor *ag_pptr_recs_cur; + + /* xfs_parent_rec records for a file that we're checking */ + struct xfs_slab *file_pptr_recs; + + /* cursor for comparing file_pptr_recs against pptrs_recs */ + struct xfs_slab_cursor *file_pptr_recs_cur; + + /* names associated with file_pptr_recs */ + struct xfblob *file_pptr_names; + + /* Number of parent pointers recorded for this file. */ + unsigned int nr_file_pptrs; + + /* Does this file have garbage xattrs with ATTR_PARENT set? */ + bool have_garbage; +}; + /* Global names storage file. */ static struct strblobs *nameblobs; static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER; static struct ag_pptrs *fs_pptrs; +static int +cmp_ag_pptr( + const void *a, + const void *b) +{ + const struct ag_pptr *pa = a; + const struct ag_pptr *pb = b; + + if (pa->child_agino < pb->child_agino) + return -1; + if (pa->child_agino > pb->child_agino) + return 1; + + if (pa->parent_ino < pb->parent_ino) + return -1; + if (pa->parent_ino > pb->parent_ino) + return 1; + + if (pa->namehash < pb->namehash) + return -1; + if (pa->namehash > pb->namehash) + return 1; + + if (pa->name_cookie < pb->name_cookie) + return -1; + if (pa->name_cookie > pb->name_cookie) + return 1; + + return 0; +} + +static int +cmp_file_pptr( + const void *a, + const void *b) +{ + const struct file_pptr *pa = a; + const struct file_pptr *pb = b; + + if (pa->parent_ino < pb->parent_ino) + return -1; + if (pa->parent_ino > pb->parent_ino) + return 1; + + /* + * Push the parent pointer names that we didn't find in the dirent scan + * towards the end of the list so that we delete them as excess. + */ + if (!pa->name_in_nameblobs && pb->name_in_nameblobs) + return 1; + if (pa->name_in_nameblobs && !pb->name_in_nameblobs) + return -1; + + if (pa->namehash < pb->namehash) + return -1; + if (pa->namehash > pb->namehash) + return 1; + + if (pa->name_cookie < pb->name_cookie) + return -1; + if (pa->name_cookie > pb->name_cookie) + return 1; + + return 0; +} + void parent_ptr_free( struct xfs_mount *mp) @@ -221,3 +391,679 @@ add_parent_ptr( (unsigned long long)ino, (unsigned long long)ag_pptr.name_cookie); } + +/* Schedule this ATTR_PARENT extended attribute for deletion. */ +static void +record_garbage_xattr( + struct xfs_inode *ip, + struct file_scan *fscan, + unsigned int attr_filter, + const unsigned char *name, + unsigned int namelen, + const void *value, + unsigned int valuelen) +{ + if (no_modify) { + if (!fscan->have_garbage) + do_warn( + _("would delete garbage parent pointer extended attributes in ino %llu\n"), + (unsigned long long)ip->i_ino); + fscan->have_garbage = true; + return; + } + + if (fscan->have_garbage) + return; + fscan->have_garbage = true; + + do_warn( + _("deleting garbage parent pointer extended attributes in ino %llu\n"), + (unsigned long long)ip->i_ino); + /* XXX do the work */ +} + +/* + * Store this file parent pointer's name in the file scan namelist unless it's + * already in the global list. + */ +static int +store_file_pptr_name( + struct file_scan *fscan, + struct file_pptr *file_pptr, + const struct xfs_name *xname) +{ + int error; + + error = strblobs_lookup(nameblobs, &file_pptr->name_cookie, + xname->name, xname->len, file_pptr->namehash); + if (!error) { + file_pptr->name_in_nameblobs = true; + return 0; + } + if (error != ENOENT) + return error; + + file_pptr->name_in_nameblobs = false; + return -xfblob_store(fscan->file_pptr_names, &file_pptr->name_cookie, + xname->name, xname->len); +} + +/* Decide if this is a directory parent pointer and stash it if so. */ +static int +examine_xattr( + struct xfs_inode *ip, + unsigned int attr_flags, + const unsigned char *name, + unsigned int namelen, + const void *value, + unsigned int valuelen, + void *priv) +{ + struct file_pptr file_pptr = { + .namelen = namelen, + }; + struct xfs_name xname = { + .name = name, + .len = namelen, + }; + struct xfs_mount *mp = ip->i_mount; + struct file_scan *fscan = priv; + int error; + + if (!(attr_flags & XFS_ATTR_PARENT)) + return 0; + + error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value, + valuelen, &file_pptr.parent_ino, &file_pptr.parent_gen); + if (error) + goto corrupt; + + file_pptr.namehash = libxfs_dir2_hashname(mp, &xname); + + error = store_file_pptr_name(fscan, &file_pptr, &xname); + if (error) + do_error( + _("storing ino %llu parent pointer '%.*s' failed: %s\n"), + (unsigned long long)ip->i_ino, + namelen, + (const char *)name, + strerror(error)); + + error = -slab_add(fscan->file_pptr_recs, &file_pptr); + if (error) + do_error(_("storing ino %llu parent pointer rec failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + + dbg_printf( + _("%s: dp %llu gen 0x%x fname '%.*s' namelen %u namehash 0x%x ino %llu namecookie 0x%llx global? %d\n"), + __func__, + (unsigned long long)file_pptr.parent_ino, + file_pptr.parent_gen, + namelen, + (const char *)name, + namelen, + file_pptr.namehash, + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr.name_cookie, + file_pptr.name_in_nameblobs); + + fscan->nr_file_pptrs++; + return 0; +corrupt: + record_garbage_xattr(ip, fscan, attr_flags, name, namelen, value, + valuelen); + return 0; +} + +/* Load a file parent pointer name from wherever we stored it. */ +static int +load_file_pptr_name( + struct file_scan *fscan, + const struct file_pptr *file_pptr, + unsigned char *name) +{ + if (file_pptr->name_in_nameblobs) + return strblobs_load(nameblobs, file_pptr->name_cookie, + name, file_pptr->namelen); + + return -xfblob_load(fscan->file_pptr_names, file_pptr->name_cookie, + name, file_pptr->namelen); +} + +/* Remove all pptrs from @ip. */ +static void +clear_all_pptrs( + struct xfs_inode *ip) +{ + if (no_modify) { + do_warn(_("would delete unlinked ino %llu parent pointers\n"), + (unsigned long long)ip->i_ino); + return; + } + + do_warn(_("deleting unlinked ino %llu parent pointers\n"), + (unsigned long long)ip->i_ino); + /* XXX actually do the work */ +} + +/* Add @ag_pptr to @ip. */ +static void +add_missing_parent_ptr( + struct xfs_inode *ip, + struct file_scan *fscan, + const struct ag_pptr *ag_pptr) +{ + unsigned char name[MAXNAMELEN]; + int error; + + error = strblobs_load(nameblobs, ag_pptr->name_cookie, name, + ag_pptr->namelen); + if (error) + do_error( + _("loading missing name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + (unsigned long long)ag_pptr->name_cookie, + strerror(error)); + + if (no_modify) { + do_warn( + _("would add missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name); + return; + } else { + do_warn( + _("adding missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name); + } + + /* XXX actually do the work */ +} + +/* Remove @file_pptr from @ip. */ +static void +remove_incorrect_parent_ptr( + struct xfs_inode *ip, + struct file_scan *fscan, + const struct file_pptr *file_pptr) +{ + unsigned char name[MAXNAMELEN] = { }; + int error; + + error = load_file_pptr_name(fscan, file_pptr, name); + if (error) + do_error( + _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + (unsigned long long)file_pptr->name_cookie, + strerror(error)); + + if (no_modify) { + do_warn( + _("would remove bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name); + return; + } + + do_warn( + _("removing bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name); + + /* XXX actually do the work */ +} + +/* + * We found parent pointers that point to the same inode and directory offset. + * Make sure they have the same generation number and dirent name. + */ +static void +compare_parent_ptrs( + struct xfs_inode *ip, + struct file_scan *fscan, + const struct ag_pptr *ag_pptr, + const struct file_pptr *file_pptr) +{ + unsigned char name1[MAXNAMELEN] = { }; + unsigned char name2[MAXNAMELEN] = { }; + int error; + + error = strblobs_load(nameblobs, ag_pptr->name_cookie, name1, + ag_pptr->namelen); + if (error) + do_error( + _("loading master-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + (unsigned long long)ag_pptr->name_cookie, + ag_pptr->namelen, + strerror(error)); + + error = load_file_pptr_name(fscan, file_pptr, name2); + if (error) + do_error( + _("loading file-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + (unsigned long long)file_pptr->name_cookie, + ag_pptr->namelen, + strerror(error)); + + if (ag_pptr->parent_gen != file_pptr->parent_gen) + goto reset; + if (ag_pptr->namelen != file_pptr->namelen) + goto reset; + if (ag_pptr->namehash != file_pptr->namehash) + goto reset; + if (memcmp(name1, name2, ag_pptr->namelen)) + goto reset; + + return; + +reset: + if (no_modify) { + do_warn( + _("would update ino %llu parent pointer (ino %llu gen 0x%x name '%.*s') -> (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name2, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name1); + return; + } + + do_warn( + _("updating ino %llu parent pointer (ino %llu gen 0x%x name '%.*s') -> (ino %llu gen 0x%x name '%.*s')\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name2, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name1); + + /* XXX do the work */ +} + +static int +cmp_file_to_ag_pptr( + const struct file_pptr *fp, + const struct ag_pptr *ap) +{ + /* + * We finished iterating all the pptrs attached to the file before we + * ran out of pptrs that we found in the directory scan. Return 1 so + * the caller adds the pptr from the dir scan. + */ + if (!fp) + return 1; + + if (fp->parent_ino > ap->parent_ino) + return 1; + if (fp->parent_ino < ap->parent_ino) + return -1; + + if (fp->namehash < ap->namehash) + return -1; + if (fp->namehash > ap->namehash) + return 1; + + /* + * If this parent pointer wasn't found in the dirent scan, we know it + * should be removed. + */ + if (!fp->name_in_nameblobs) + return -1; + + if (fp->name_cookie < ap->name_cookie) + return -1; + if (fp->name_cookie > ap->name_cookie) + return 1; + + return 0; +} + +/* + * If this parent pointer that we got via directory scan thinks it might be a + * duplicate, compare it to the previous pptr found by the directory scan. If + * they are the same, then we got duplicate entries on account of dotdot + * reprocessing and we can ignore this one. + */ +static inline bool +crosscheck_want_skip_dup( + const struct ag_pptr *ag_pptr, + const struct ag_pptr *prev_ag_pptr) +{ + if (!(ag_pptr->flags & AG_PPTR_POSSIBLE_DUP) || !prev_ag_pptr) + return false; + + if (ag_pptr->parent_ino == prev_ag_pptr->parent_ino && + ag_pptr->parent_gen == prev_ag_pptr->parent_gen && + ag_pptr->namelen == prev_ag_pptr->namelen && + ag_pptr->name_cookie == prev_ag_pptr->name_cookie && + ag_pptr->child_agino == prev_ag_pptr->child_agino) + return true; + + return false; +} + +/* + * Make sure that the parent pointers we observed match the ones ondisk. + * + * Earlier, we generated a master list of parent pointers for files in this AG + * based on what we saw during the directory walk at the start of phase 6. + * Now that we've read in all of this file's parent pointers, make sure the + * lists match. + */ +static void +crosscheck_file_parent_ptrs( + struct xfs_inode *ip, + struct file_scan *fscan) +{ + struct ag_pptr *ag_pptr, *prev_ag_pptr = NULL; + struct file_pptr *file_pptr; + struct xfs_mount *mp = ip->i_mount; + xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino); + xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); + int error; + + ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur); + + if (!ag_pptr || ag_pptr->child_agino > agino) { + /* + * The cursor for the master pptr list has gone beyond this + * file that we're scanning. Evidently it has no parents at + * all, so we better not have found any pptrs attached to the + * file. + */ + if (fscan->nr_file_pptrs > 0) + clear_all_pptrs(ip); + + return; + } + + if (ag_pptr->child_agino < agino) { + /* + * The cursor for the master pptr list is behind the file that + * we're scanning. This suggests that the incore inode tree + * doesn't know about a file that is mentioned by a dirent. + * At this point the inode liveness is supposed to be settled, + * which means our incore information is inconsistent. + */ + do_error( + _("found dirent referring to ino %llu even though inobt scan moved on to ino %llu?!\n"), + (unsigned long long)XFS_AGINO_TO_INO(mp, agno, + ag_pptr->child_agino), + (unsigned long long)ip->i_ino); + /* does not return */ + } + + /* + * The master pptr list cursor is pointing to the inode that we want + * to check. Sort the pptr records that we recorded from the ondisk + * pptrs for this file, then set up for the comparison. + */ + qsort_slab(fscan->file_pptr_recs, cmp_file_pptr); + + error = -init_slab_cursor(fscan->file_pptr_recs, cmp_file_pptr, + &fscan->file_pptr_recs_cur); + if (error) + do_error(_("init ino %llu parent pointer cursor failed: %s\n"), + (unsigned long long)ip->i_ino, strerror(error)); + + do { + int cmp_result; + + if (crosscheck_want_skip_dup(ag_pptr, prev_ag_pptr)) { + /* + * This master parent pointer thinks it's a duplicate + * and it matches the previous master parent pointer. + * We don't want to add duplicate parent pointers, so + * advance the master pptr cursor and loop again. + */ + dbg_printf( + _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (skip_dup)\n"), + __func__, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->name_cookie); + prev_ag_pptr = ag_pptr; + advance_slab_cursor(fscan->ag_pptr_recs_cur); + ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur); + continue; + } + prev_ag_pptr = ag_pptr; + + file_pptr = peek_slab_cursor(fscan->file_pptr_recs_cur); + + dbg_printf( + _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (master)\n"), + __func__, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->name_cookie); + + if (file_pptr) { + dbg_printf( + _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (file)\n"), + __func__, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->name_cookie); + } else { + dbg_printf( + _("%s: ran out of parent pointers for ino %llu (file)\n"), + __func__, + (unsigned long long)ip->i_ino); + } + + cmp_result = cmp_file_to_ag_pptr(file_pptr, ag_pptr); + if (cmp_result > 0) { + /* + * The master pptr list knows about pptrs that are not + * in the ondisk metadata. Add the missing pptr and + * advance only the master pptr cursor. + */ + add_missing_parent_ptr(ip, fscan, ag_pptr); + advance_slab_cursor(fscan->ag_pptr_recs_cur); + } else if (cmp_result < 0) { + /* + * The ondisk pptrs mention a link that is not in the + * master list. Delete the extra pptr and advance only + * the file pptr cursor. + */ + remove_incorrect_parent_ptr(ip, fscan, file_pptr); + advance_slab_cursor(fscan->file_pptr_recs_cur); + } else { + /* + * Exact match, make sure the parent_gen and dirent + * name parts of the parent pointer match. Move both + * cursors forward. + */ + compare_parent_ptrs(ip, fscan, ag_pptr, file_pptr); + advance_slab_cursor(fscan->ag_pptr_recs_cur); + advance_slab_cursor(fscan->file_pptr_recs_cur); + } + + ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur); + } while (ag_pptr && ag_pptr->child_agino == agino); + + while ((file_pptr = pop_slab_cursor(fscan->file_pptr_recs_cur))) { + dbg_printf( + _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (excess)\n"), + __func__, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->name_cookie); + + /* + * The master pptr list does not have any more pptrs for this + * file, but we still have unprocessed ondisk pptrs. Delete + * all these ondisk pptrs. + */ + remove_incorrect_parent_ptr(ip, fscan, file_pptr); + } +} + +/* Ensure this file's parent pointers match what we found in the dirent scan. */ +static void +check_file_parent_ptrs( + struct xfs_inode *ip, + struct file_scan *fscan) +{ + int error; + + error = -init_slab(&fscan->file_pptr_recs, sizeof(struct file_pptr)); + if (error) + do_error(_("init file parent pointer recs failed: %s\n"), + strerror(error)); + + fscan->have_garbage = false; + fscan->nr_file_pptrs = 0; + + error = xattr_walk(ip, examine_xattr, fscan); + if (error && !no_modify) + do_error(_("ino %llu parent pointer scan failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + if (error) { + do_warn(_("ino %llu parent pointer scan failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + goto out_free; + } + + crosscheck_file_parent_ptrs(ip, fscan); + +out_free: + free_slab(&fscan->file_pptr_recs); + xfblob_truncate(fscan->file_pptr_names); +} + +/* Check all the parent pointers of files in this AG. */ +static void +check_ag_parent_ptrs( + struct workqueue *wq, + uint32_t agno, + void *arg) +{ + struct xfs_mount *mp = wq->wq_ctx; + struct file_scan fscan = { + .ag_pptrs = &fs_pptrs[agno], + }; + struct ag_pptrs *ag_pptrs = &fs_pptrs[agno]; + struct ino_tree_node *irec; + char *descr; + int error; + + qsort_slab(ag_pptrs->pptr_recs, cmp_ag_pptr); + + error = -init_slab_cursor(ag_pptrs->pptr_recs, cmp_ag_pptr, + &fscan.ag_pptr_recs_cur); + if (error) + do_error( + _("init agno %u parent pointer slab cursor failed: %s\n"), + agno, strerror(error)); + + descr = kasprintf(GFP_KERNEL, + "xfs_repair (%s): file parent pointer names", + mp->m_fsname); + error = -xfblob_create(descr, &fscan.file_pptr_names); + kfree(descr); + if (error) + do_error( + _("init agno %u file parent pointer names failed: %s\n"), + agno, strerror(error)); + + for (irec = findfirst_inode_rec(agno); + irec != NULL; + irec = next_ino_rec(irec)) { + unsigned int ino_offset; + + for (ino_offset = 0; + ino_offset < XFS_INODES_PER_CHUNK; + ino_offset++) { + struct xfs_inode *ip; + xfs_ino_t ino; + + if (is_inode_free(irec, ino_offset)) + continue; + + ino = XFS_AGINO_TO_INO(mp, agno, + irec->ino_startnum + ino_offset); + error = -libxfs_iget(mp, NULL, ino, 0, &ip); + if (error && !no_modify) + do_error( + _("loading ino %llu for parent pointer check failed: %s\n"), + (unsigned long long)ino, + strerror(error)); + if (error) { + do_warn( + _("loading ino %llu for parent pointer check failed: %s\n"), + (unsigned long long)ino, + strerror(error)); + continue; + } + + check_file_parent_ptrs(ip, &fscan); + libxfs_irele(ip); + } + } + + xfblob_destroy(fscan.file_pptr_names); + free_slab_cursor(&fscan.ag_pptr_recs_cur); +} + +/* Check all the parent pointers of all files in this filesystem. */ +void +check_parent_ptrs( + struct xfs_mount *mp) +{ + struct workqueue wq; + xfs_agnumber_t agno; + + if (!xfs_has_parent(mp)) + return; + + create_work_queue(&wq, mp, ag_stride); + + for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) + queue_work(&wq, check_ag_parent_ptrs, agno, NULL); + + destroy_work_queue(&wq); +} diff --git a/repair/pptr.h b/repair/pptr.h index 752237942..65acff963 100644 --- a/repair/pptr.h +++ b/repair/pptr.h @@ -12,4 +12,6 @@ void parent_ptr_init(struct xfs_mount *mp); void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname, struct xfs_inode *dp, bool possible_dup); +void check_parent_ptrs(struct xfs_mount *mp); + #endif /* __REPAIR_PPTR_H__ */ From patchwork Tue Jul 30 01:28:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746204 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 40EA979E1 for ; Tue, 30 Jul 2024 01:28:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302895; cv=none; b=S+lmyLBxNJ5cwwwPG7613lsyKjrB6vqm3OT4XhrE/gVGvf9iGvOVetG5xAj6xmcvYLa+PaCSFjnU7koBBLdHRUMo73PxO0hiRYYBRyzUmB7FDzcNOz9TSCj8YAq5MzZSxon4XSxkwB5LulY7GC+W8simICBRhO9R4j/X7AWpyRs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302895; c=relaxed/simple; bh=+332OqTogjcnwGSAjsdKVBIxvJE7256qC82/Ue9mxps=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=aKLTMuj/MFpMwH/bc4K2ZXL9Y7VBUwP3tE/aS3cU/gb4lUgjRFgSWPvF0ImWeIPBMG3KI9AZoLu/81UcyJsgbt9Uk8Adov8qdXyDjxjC+2N9JGn9J+ZI+zizQ2O2fd9WtJdAfL1ufWX8hI7ewM84jrZ5h+VPujHdvhAkC1Xk5Nc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iBBU2j4M; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iBBU2j4M" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D350EC32786; Tue, 30 Jul 2024 01:28:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302894; bh=+332OqTogjcnwGSAjsdKVBIxvJE7256qC82/Ue9mxps=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=iBBU2j4Mbo5E0uqHCBxAmZmrylGBX5ahbf/mrlaROIyaZ9AjmTQez3inI8ql6yshB svm+2n8hC8O36sSTfn+TsdNMhrOpskfCWxOKVTnXq+y6FWM1hNHOBUNyibB21smHTw huGSvZbJAzYy0PKLh8BUp54wcaz14EVI3hiKcyhOvRZBih1n0s3JsBaawnv24IfSsu kmCNRqfXKyHYQ+bTQEWiohd/9iK2AUSvJi1WIBM++R4l3v31jWgMZVXNGG09HY4kfQ 2gU5xWhTTir9coyimA3uLJrf/+UMEzpZnBJ9lzmmjMkWQfLwduB7QoI5l6uZqTXOXO VD7MTxmXYLzLA== Date: Mon, 29 Jul 2024 18:28:14 -0700 Subject: [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851615.1352527.5960312187554970011.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Delete xattrs that have ATTR_PARENT set but are so garbage that they clearly aren't parent pointers. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- libxfs/libxfs_api_defs.h | 1 repair/pptr.c | 149 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index d3611e05b..e12f0a40b 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -46,6 +46,7 @@ #define xfs_attr_is_leaf libxfs_attr_is_leaf #define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize #define xfs_attr_namecheck libxfs_attr_namecheck +#define xfs_attr_removename libxfs_attr_removename #define xfs_attr_set libxfs_attr_set #define xfs_attr_sethash libxfs_attr_sethash #define xfs_attr_sf_firstentry libxfs_attr_sf_firstentry diff --git a/repair/pptr.c b/repair/pptr.c index bd9bb6f8b..61466009d 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -198,6 +198,29 @@ struct file_scan { /* Does this file have garbage xattrs with ATTR_PARENT set? */ bool have_garbage; + + /* xattrs that we have to remove from this file */ + struct xfs_slab *garbage_xattr_recs; + + /* attr names associated with garbage_xattr_recs */ + struct xfblob *garbage_xattr_names; +}; + +struct garbage_xattr { + /* xfs_da_args.attr_filter for the attribute being removed */ + unsigned int attr_filter; + + /* attribute name length */ + unsigned int attrnamelen; + + /* attribute value length */ + unsigned int attrvaluelen; + + /* cookie for the attribute name */ + xfblob_cookie attrname_cookie; + + /* cookie for the attribute value */ + xfblob_cookie attrvalue_cookie; }; /* Global names storage file. */ @@ -392,6 +415,82 @@ add_parent_ptr( (unsigned long long)ag_pptr.name_cookie); } +/* Remove garbage extended attributes that have ATTR_PARENT set. */ +static void +remove_garbage_xattrs( + struct xfs_inode *ip, + struct file_scan *fscan) +{ + struct xfs_slab_cursor *cur; + struct garbage_xattr *ga; + void *buf = NULL; + size_t bufsize = 0; + int error; + + error = -init_slab_cursor(fscan->garbage_xattr_recs, NULL, &cur); + if (error) + do_error(_("init garbage xattr cursor failed: %s\n"), + strerror(error)); + + while ((ga = pop_slab_cursor(cur)) != NULL) { + struct xfs_da_args args = { + .dp = ip, + .attr_filter = ga->attr_filter, + .namelen = ga->attrnamelen, + .valuelen = ga->attrvaluelen, + .owner = ip->i_ino, + .geo = ip->i_mount->m_attr_geo, + .whichfork = XFS_ATTR_FORK, + .op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED, + }; + size_t desired = ga->attrnamelen + ga->attrvaluelen; + + if (desired > bufsize) { + free(buf); + buf = malloc(desired); + if (!buf) + do_error( + _("allocating %zu bytes to remove ino %llu garbage xattr failed: %s\n"), + desired, + (unsigned long long)ip->i_ino, + strerror(errno)); + bufsize = desired; + } + + args.name = buf; + args.value = buf + ga->attrnamelen; + + error = -xfblob_load(fscan->garbage_xattr_names, + ga->attrname_cookie, buf, ga->attrnamelen); + if (error) + do_error( + _("loading garbage xattr name failed: %s\n"), + strerror(error)); + + error = -xfblob_load(fscan->garbage_xattr_names, + ga->attrvalue_cookie, args.value, + ga->attrvaluelen); + if (error) + do_error( + _("loading garbage xattr value failed: %s\n"), + strerror(error)); + + libxfs_attr_sethash(&args); + error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, true); + if (error) + do_error( + _("removing ino %llu garbage xattr failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + } + + free(buf); + free_slab_cursor(&cur); + free_slab(&fscan->garbage_xattr_recs); + xfblob_destroy(fscan->garbage_xattr_names); + fscan->garbage_xattr_names = NULL; +} + /* Schedule this ATTR_PARENT extended attribute for deletion. */ static void record_garbage_xattr( @@ -403,6 +502,15 @@ record_garbage_xattr( const void *value, unsigned int valuelen) { + struct garbage_xattr garbage_xattr = { + .attr_filter = attr_filter, + .attrnamelen = namelen, + .attrvaluelen = valuelen, + }; + struct xfs_mount *mp = ip->i_mount; + char *descr; + int error; + if (no_modify) { if (!fscan->have_garbage) do_warn( @@ -413,13 +521,47 @@ record_garbage_xattr( } if (fscan->have_garbage) - return; + goto stuffit; fscan->have_garbage = true; do_warn( _("deleting garbage parent pointer extended attributes in ino %llu\n"), (unsigned long long)ip->i_ino); - /* XXX do the work */ + + error = -init_slab(&fscan->garbage_xattr_recs, + sizeof(struct garbage_xattr)); + if (error) + do_error(_("init garbage xattr recs failed: %s\n"), + strerror(error)); + + descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): garbage xattr names", + mp->m_fsname); + error = -xfblob_create(descr, &fscan->garbage_xattr_names); + kfree(descr); + if (error) + do_error("init garbage xattr names failed: %s\n", + strerror(error)); + +stuffit: + error = -xfblob_store(fscan->garbage_xattr_names, + &garbage_xattr.attrname_cookie, name, namelen); + if (error) + do_error(_("storing ino %llu garbage xattr failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + + error = -xfblob_store(fscan->garbage_xattr_names, + &garbage_xattr.attrvalue_cookie, value, valuelen); + if (error) + do_error(_("storing ino %llu garbage xattr failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + + error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr); + if (error) + do_error(_("storing ino %llu garbage xattr rec failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); } /* @@ -968,6 +1110,9 @@ check_file_parent_ptrs( goto out_free; } + if (!no_modify && fscan->have_garbage) + remove_garbage_xattrs(ip, fscan); + crosscheck_file_parent_ptrs(ip, fscan); out_free: From patchwork Tue Jul 30 01:28:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746205 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 9D6098F5B for ; Tue, 30 Jul 2024 01:28:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302910; cv=none; b=WIK6YhPU6kiqj2Fia7xXVyl+y6hmxPkMi0qa5/hYr5ckmAR8n+B510TeHybEzaYe9OtbRHO9v/MytG0Q+IcoUKzKCHB2SU8hJRBZf5oeeQqGj1itZtww7m3jVGvhGqbfaYGx8NojlKZGo+lGXWmcbSgjkViA+tZ/27kT4k3vJAE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302910; c=relaxed/simple; bh=2fUNGrKK54NkVJ9cChuMo5LInMUbjjaT1OY1yxSRxfw=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dg5I8IWg7ywXwfURFXfQub4uvZW/+To169yS1BM2eo6lBG+w1+1Kp5xdl9nZq4UcHjfECPn2CdxWYbTE17ZcZkF1iVP0bS+YER4nFO7ymKxD39Hy1EjmuYtjFOV7UqkWqCy1zp5QUb18DJjIh0n+SMZnJIemi71AQ06OBAKrnQs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nCwuee40; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nCwuee40" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 76FD4C4AF0A; Tue, 30 Jul 2024 01:28:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302910; bh=2fUNGrKK54NkVJ9cChuMo5LInMUbjjaT1OY1yxSRxfw=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=nCwuee40OjkZOUOTqJLFkQG81NKh14f13mIztL94oxljYCXs9y9EbiGU4XAvaG8/3 dIa31x7orA5YLGV3QVUPGX06Vdu+F4HR9NR6X0F2WFJfigPdWcU4TqsnVdVw+Cb+Uy xw29wPGOj0KGUCZ5tjD4nP9XqznRHGgQx0VrZcmNhZQ6/eWO1Zt0gYR5IRzXLyanw9 wyNpkXsblzh4WXp7M5PKcRb1yHr95f40icbAz1U2oyHwrJvp34dfn8ZWPL9GilLpcK ijb3ZlOIT7vUW6vRG1S8dTLfyHoloykjhA52K+tKSJohBXXR40HG3o94Hs6Tdlpfbr xNN1uFCjF2AbQ== Date: Mon, 29 Jul 2024 18:28:30 -0700 Subject: [PATCH 11/12] xfs_repair: update ondisk parent pointer records From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851628.1352527.106880217821874506.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Update the ondisk parent pointer records as necessary. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- libxfs/libxfs_api_defs.h | 2 + repair/pptr.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h index e12f0a40b..df316727b 100644 --- a/libxfs/libxfs_api_defs.h +++ b/libxfs/libxfs_api_defs.h @@ -209,8 +209,10 @@ #define xfs_parent_hashval libxfs_parent_hashval #define xfs_parent_lookup libxfs_parent_lookup #define xfs_parent_removename libxfs_parent_removename +#define xfs_parent_set libxfs_parent_set #define xfs_parent_start libxfs_parent_start #define xfs_parent_from_attr libxfs_parent_from_attr +#define xfs_parent_unset libxfs_parent_unset #define xfs_perag_get libxfs_perag_get #define xfs_perag_hold libxfs_perag_hold #define xfs_perag_put libxfs_perag_put diff --git a/repair/pptr.c b/repair/pptr.c index 61466009d..94d6d8346 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -673,6 +673,44 @@ load_file_pptr_name( name, file_pptr->namelen); } +/* Add an on disk parent pointer to a file. */ +static int +add_file_pptr( + struct xfs_inode *ip, + const struct ag_pptr *ag_pptr, + const unsigned char *name) +{ + struct xfs_name xname = { + .name = name, + .len = ag_pptr->namelen, + }; + struct xfs_parent_rec pptr_rec = { }; + struct xfs_da_args scratch; + + xfs_parent_rec_init(&pptr_rec, ag_pptr->parent_ino, + ag_pptr->parent_gen); + return -libxfs_parent_set(ip, ip->i_ino, &xname, &pptr_rec, &scratch); +} + +/* Remove an on disk parent pointer from a file. */ +static int +remove_file_pptr( + struct xfs_inode *ip, + const struct file_pptr *file_pptr, + const unsigned char *name) +{ + struct xfs_name xname = { + .name = name, + .len = file_pptr->namelen, + }; + struct xfs_parent_rec pptr_rec = { }; + struct xfs_da_args scratch; + + xfs_parent_rec_init(&pptr_rec, file_pptr->parent_ino, + file_pptr->parent_gen); + return -libxfs_parent_unset(ip, ip->i_ino, &xname, &pptr_rec, &scratch); +} + /* Remove all pptrs from @ip. */ static void clear_all_pptrs( @@ -729,7 +767,16 @@ add_missing_parent_ptr( name); } - /* XXX actually do the work */ + error = add_file_pptr(ip, ag_pptr, name); + if (error) + do_error( + _("adding ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name, + strerror(error)); } /* Remove @file_pptr from @ip. */ @@ -771,7 +818,16 @@ remove_incorrect_parent_ptr( file_pptr->namelen, name); - /* XXX actually do the work */ + error = remove_file_pptr(ip, file_pptr, name); + if (error) + do_error( + _("removing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name, + strerror(error)); } /* @@ -851,7 +907,33 @@ compare_parent_ptrs( ag_pptr->namelen, name1); - /* XXX do the work */ + /* Remove the parent pointer that we don't want. */ + error = remove_file_pptr(ip, file_pptr, name2); + if (error) + do_error( +_("erasing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + file_pptr->namelen, + name2, + strerror(error)); + + /* + * Add the parent pointer that we do want. It's possible that this + * parent pointer already exists but we haven't gotten that far in the + * scan, so we'll keep going on EEXIST. + */ + error = add_file_pptr(ip, ag_pptr, name1); + if (error && error != EEXIST) + do_error( + _("updating ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)ag_pptr->parent_ino, + ag_pptr->parent_gen, + ag_pptr->namelen, + name1, + strerror(error)); } static int From patchwork Tue Jul 30 01:28:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13746206 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 4803C881E for ; Tue, 30 Jul 2024 01:28:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302926; cv=none; b=KnvMXg+BHR/B8ra/R23y9/23bVRKlA7lF7BFw2lEJ7OChXi7hKNTaaqmN9r38wEVkqhGcEDufYSTgw2y/vsmtOXGoVbBGL+83NmBHd6LDqGY1OqFX252BHT16PhHJ+QgP0jjcl3tIfJMGD9W/0wGisrETCkOD1v8SBtx0GNYHDw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1722302926; c=relaxed/simple; bh=B05Bsxy/CNfQQUMk/nwB8TbTiqiQFfzCa/rVhDIKkRw=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=XRx6Fxuke9fenm3wyqUwEvFMcc9wsXiTJiIhDvqpF2BvlS+sBxw+0sbsZMycMwmnRM0uX/hQUnqkfhzVFOH7R6Uw8FE31hUb3DDEQTu0OOWnCuHY1lrv7fmyo5HiiYgd9zJTJVF5ezmtiokMZY80HH7Gzo8h+yOAIa7gtfcTdFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=USMbk3JM; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="USMbk3JM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 22714C32786; Tue, 30 Jul 2024 01:28:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1722302926; bh=B05Bsxy/CNfQQUMk/nwB8TbTiqiQFfzCa/rVhDIKkRw=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=USMbk3JMc2Bi3z5ypJX7S8eGQg40m5/WI9ouM0IjwDr+N0E6MW5iwpqu8nRp1qKIm 82eKqd1kb/77Z8iTOpVGDQbBulIrbP215Tf5EROxfKLeFKVNkZp0ktddK0WC59S5Zb 0GI15KA/SCD89rq86RHrJnFoPhS+uXA9MdX+mC8OFzShUL8e913V52am3UnaMRXrvo 6pbAxoCeypUA2hFIfFg5NUgaBoIRvZG/UaEqlzKdaJ1xgZJEE3fUQB+0cnr6b7w6jY bQT9TFeJ2xAIsToKn7+Urt7YDY4S+WM/YQsnHRExpb6/qkeX/QeBm6gwjGTA9qMn/U fJDPh+3k5J84g== Date: Mon, 29 Jul 2024 18:28:45 -0700 Subject: [PATCH 12/12] xfs_repair: wipe ondisk parent pointers when there are none From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: Christoph Hellwig , linux-xfs@vger.kernel.org, catherine.hoang@oracle.com, allison.henderson@oracle.com Message-ID: <172229851640.1352527.8340902280666804954.stgit@frogsfrogsfrogs> In-Reply-To: <172229851481.1352527.11812121319440135994.stgit@frogsfrogsfrogs> References: <172229851481.1352527.11812121319440135994.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 Erase all the parent pointers when there aren't any found by the directory entry scan. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- repair/pptr.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/repair/pptr.c b/repair/pptr.c index 94d6d8346..8ec6a51d2 100644 --- a/repair/pptr.c +++ b/repair/pptr.c @@ -714,8 +714,13 @@ remove_file_pptr( /* Remove all pptrs from @ip. */ static void clear_all_pptrs( - struct xfs_inode *ip) + struct xfs_inode *ip, + struct file_scan *fscan) { + struct xfs_slab_cursor *cur; + struct file_pptr *file_pptr; + int error; + if (no_modify) { do_warn(_("would delete unlinked ino %llu parent pointers\n"), (unsigned long long)ip->i_ino); @@ -724,7 +729,37 @@ clear_all_pptrs( do_warn(_("deleting unlinked ino %llu parent pointers\n"), (unsigned long long)ip->i_ino); - /* XXX actually do the work */ + + error = -init_slab_cursor(fscan->file_pptr_recs, NULL, &cur); + if (error) + do_error(_("init ino %llu pptr cursor failed: %s\n"), + (unsigned long long)ip->i_ino, + strerror(error)); + + while ((file_pptr = pop_slab_cursor(cur)) != NULL) { + unsigned char name[MAXNAMELEN]; + + error = load_file_pptr_name(fscan, file_pptr, name); + if (error) + do_error( + _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + (unsigned long long)file_pptr->name_cookie, + strerror(error)); + + error = remove_file_pptr(ip, file_pptr, name); + if (error) + do_error( + _("wiping ino %llu pptr (ino %llu gen 0x%x) failed: %s\n"), + (unsigned long long)ip->i_ino, + (unsigned long long)file_pptr->parent_ino, + file_pptr->parent_gen, + strerror(error)); + } + + free_slab_cursor(&cur); } /* Add @ag_pptr to @ip. */ @@ -1028,7 +1063,7 @@ crosscheck_file_parent_ptrs( * file. */ if (fscan->nr_file_pptrs > 0) - clear_all_pptrs(ip); + clear_all_pptrs(ip, fscan); return; }