From patchwork Tue Jul 2 01:17: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: 13718857 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 8EB368489 for ; Tue, 2 Jul 2024 01:17:44 +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=1719883064; cv=none; b=ERRAZA6ggr0qBP0Gdg+h4jllLNVvtkgcZVR1xD5mrPV+/m+kA1m6DEZ3SA5kqEbdOjuzsQGEsAzA8BEuXjoqmVG/61ljyMQpbuR+AfyV/CdbH+bcXNSwkDFe4heoOXvm3qD8wN+qzzo3NEndGJlklMvYDQE0BkvCoRRVuVvQUP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883064; c=relaxed/simple; bh=tW66rJzXnO2YVvcEPHKEPTyG4uFUCQF8Q+zmHXkXNgM=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=t6ZDEC1yPYUXpOMgaZ7yXKQd33jikCx9ETUsbs+TOCeFNXmKDFBWOpltJONAbntPWY6kQy7Qf5PcQoKps+VTWgVJCoaagRjmUWGSorLRAO1bwdBPWsFh9HsyII8IajGDS8V/16Um2SbYAi3MUgvMWYLId8igmu47l8uBGYkcCNk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ng5M21b+; 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="Ng5M21b+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 604C6C116B1; Tue, 2 Jul 2024 01:17:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883064; bh=tW66rJzXnO2YVvcEPHKEPTyG4uFUCQF8Q+zmHXkXNgM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=Ng5M21b+mqQGE9ODuI1Y5TeTrWiixVV06miSy120L1AsNy08NDkuTB6Oq1kk2Nbnr A8QaH8c06/x8UDWXe7Ksai4VEeEZlyWXUnoflJXobQptXSxGwqiWHG4f2qijyFEj+v XSxD+HSnTuioABDrZ4eq0gOH7V2yhad46HsQ+EnKp1tPz+ub26Ce+RFc3spupRnU2p YSXxstWOJXozDqLrRx4ACmbpa6TLn1gsSZbPjG9H7Efq3b8oXunW258b+D/kp44tOk Na0Jo7RYtoxTwjVuLonrPvJbb6X5Zyy9/i+dRBgJD1JY3Uhen2h1oY3/eUVdAd7Rgx aDPsSahY+eM/A== Date: Mon, 01 Jul 2024 18:17:43 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122181.2010218.3340368343323073575.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 d9ab79fa7849..008662571323 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 df83aabdc13d..bf1d3c9d37f6 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 2 01:17:59 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: 13718858 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 315189441 for ; Tue, 2 Jul 2024 01:18:00 +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=1719883080; cv=none; b=UawTA/WboS2QuibLkYFgjKah+vxXD8fAsRNV0gIhYnA5XR7ieq8r73dLcz72bK34RVkpvUi9Ol9WnqKT/hkVGRNuHaC5AoD2Yruamz/FDHavEmyoPF7oIaSDlinnPSRne7KFjjkYWum1IP/Ec1AY6OCPAkE7IuGgYcea2R8n7Sg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883080; c=relaxed/simple; bh=ev+lTVOZMqEJ2xN8sWE5NkTqrqiD+SeFS1bgl8g6TGY=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=euJocdsKZoowLX/psSMqAMR6j0FPbYQBcHMB/B15qphcFxrzirZgT9Wt98RdIo7Ajp+m8vC/2xjIu+5OoWgLz6zdCzyN4ZsSyDfEMb43mQQFuSo7eol5qCLTDlWdbryN+kBWGgr1M5WgqoHOZMKtg2D9eFaola+b357NxsgAeCY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lF+miUSJ; 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="lF+miUSJ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 09486C116B1; Tue, 2 Jul 2024 01:18:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883080; bh=ev+lTVOZMqEJ2xN8sWE5NkTqrqiD+SeFS1bgl8g6TGY=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=lF+miUSJk7MF/1uU7qluczb6TOeFtD0qfynHU0e1U+lKXQY1jJjXkbdAF0hlPbt7G /T/V8v5N2Zz8IUx2qyyvz2Z+3xHzut3Tzz5Cz4ws3hokuWyv43tvOL/pnkJkXcr77t dB6dltReatzpjCV1hujKH+YKMFlqCNk+RLtFrFahlOk4ouh0PRSzEI6qa+G9WqYcm7 s2d7dgEv3evjQhTZZMB3Jfyb0X4llyZh5wObO8G2UeydYCYBlsHMp0xx0Cf9r9J4es IodZpc08Hs4zYIeloH4rIPq/JSw4ahmV0wJyefEX51cmxnXK3bYhchxdH8TDCh5juJ La2HQoOpMtwJA== Date: Mon, 01 Jul 2024 18:17:59 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122196.2010218.8191843801782872283.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 008662571323..81d530055193 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 2 01:18:15 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: 13718859 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 BF59B9449 for ; Tue, 2 Jul 2024 01:18:15 +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=1719883095; cv=none; b=T/lTpt19WZvPDawAzG7jKDxtouUXU9yzBK0JcIxzeu18U80LiOxCL95Dzzy8B5FNZjl9dWORDHlI/DdqNB++AODLsdKqetlycJWnYnipc4hBnTFxqPLXjqOx1jl6/22sX2DdWgX2rpl9IYyi/qt1whnWvuQdmoE0sFIOEUsaivo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883095; c=relaxed/simple; bh=K59YIOkGywNUw3KSqnhG75Iref+m01Q98q76CNm2HrI=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=aqq9SaCA1ErLgF9v/Lv2uuGmLvLXKe0jNeSA1fkTqL3ZoIupbreqndoJJ/Y1QR5PAqexYZbhKYU72RXVEtA+2wlrw5O3Na/eKkHOIX9htzqeStYj679ghVI5BZDyOk7ekgVjv4nWd6f5VARdPCGVNX8TGc+VJEOlkYnUdwm112Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GUR+mwtk; 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="GUR+mwtk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9AC4BC116B1; Tue, 2 Jul 2024 01:18:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883095; bh=K59YIOkGywNUw3KSqnhG75Iref+m01Q98q76CNm2HrI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=GUR+mwtkT91MTQWZMjT2s/mUrzCno2UbOZSMULkkCDbOuPO2gXSsCcYsJgiKRyADr GzvzG8X/AaCpdYKLQFzAltS3O4FJjk/eea+43a+OjLiN/8pXhSMJmzxd+uysJI3pzM pV1dy92zNCt/aVHiYX9pmsJAdxdOFcRva71OPkOyCbAGUjVhO3tMVejHexVKKTDWl8 8gSZkQJ2E7EbOJ//kXl9tStwn6doX+z223D9GjKzEQokXsA5kPlprFOfXye4r7SHU7 Br+tkjMvEDHLsx52zD2mxML3o6ZyBBYPH/HbEfz6aYBOwIX6sExpYslQHLo2ZxgPe4 UbdkXtCUO/pVg== Date: Mon, 01 Jul 2024 18:18:15 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122212.2010218.8882336678990830158.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 2e97fd9775ba..50159b9a5338 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 2 01:18: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: 13718860 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 716D29441 for ; Tue, 2 Jul 2024 01:18:31 +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=1719883111; cv=none; b=UbvyHhDuvbT57bKdE3bqdoqhbgnP7Moo+JGuHHErQOy5llCg2/7fHBDRDIRN6/vmJtCANtkEnZsAW0SrwppHfpLArKu2KfdffS5i4QY9Smyy+XPWoewG7nlQ9t7u0wKNlg8N42UZj2vJ39KxUAqaGCIFb9io99yT8ejK8FJ4f60= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883111; c=relaxed/simple; bh=e2qt/IoH35gJdraxnMyAzvVBxCKaalV4Z+JOGoo9ifI=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=t+9+1F93mvpIFfjjj9hTE88BLHBSbxA9k83TO1U+U1yjVTwDkJgQ87KTSkzI5CtuftWdI6AqWK64kNNvD02ESXWDmwLIrGj7b9z3Alm2lZzGRUaF3AuCn5BDRTkiOvJ0lmmSs3PUYH9k2Wpj7hHVCKPRIWiDaEGFLovQNkbaxvA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cAcO8JPn; 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="cAcO8JPn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4C46FC116B1; Tue, 2 Jul 2024 01:18:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883111; bh=e2qt/IoH35gJdraxnMyAzvVBxCKaalV4Z+JOGoo9ifI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=cAcO8JPn8Sg/iUZp+5Dx/79kNAPztk10IB3gR5q5fR14Ak86NNeAmzM0Iqo4/TgjT GNmY2/cdJAnXteHdtwtObFa+IrSxB0nLKN8m9LNmSSifU9W2iY6BpuV1nffPC2Qsa6 fRcg8i2wzgCY+qS7xfgrE4H0L/yjOoGHwuuu/UqEPHyoRG11zGCOwbz5Cd1xEX8y/D OipTOunbpoOef1GiLffdehCVbK6eeqoPY1aL+MlQQ31JS4mibOJkDOh4PhjNf2uYuk ZHmicQwjsG/VMtKmM4NrxIgEdoKbARPAkKdJfGS6omppMhjSREXiWDL6RcLnY157Up QJ3MjQjEio3lw== Date: Mon, 01 Jul 2024 18:18:30 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122227.2010218.6936061653638637272.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 bf1d3c9d37f6..d3611e05bade 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 47dd9de2741c..791f7d36fa8a 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 2 01:18:46 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: 13718861 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 1A5AB9441 for ; Tue, 2 Jul 2024 01:18:47 +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=1719883127; cv=none; b=nI7X1fMTYy1hf1TMZjJKOmT8P1BQ86QaoKB1R8DkfCfB4ckDwFsc1JfsaYhWb8JKmOqYBS91ilb/GqmSzr9JfHDA3hb+D9NzqB+FuUb4LOmw9mAPmHJsxFFVSzkGioK8gZWw7hip2nrt1kXh4gfC7BVhfcScM2K+N+PxC2ajklE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883127; c=relaxed/simple; bh=kplgIM4rEH+cKd18bV5k0EYgBt96QWYrQORfj5l9g+4=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=e360QROHF+C6tJak/ZSFOGCo8Or6x/ChXEOoGnpHei2dPMt71kAMPpsL6z4YzINEVszoSfmCmJxfcRXZejii7SQ7zIq/6hrVzb4R69DZALciJgTndy2CCNmSdnunrt9PQmc/rSsIuaGb69AE7zUYYuJZliakOXc/jbwA8cdVl+g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZqlLvkyt; 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="ZqlLvkyt" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E8FCFC116B1; Tue, 2 Jul 2024 01:18:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883127; bh=kplgIM4rEH+cKd18bV5k0EYgBt96QWYrQORfj5l9g+4=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=ZqlLvkytqtzU815g92H/FWg7XwK+BnogAHkIvSQnZSd7ucIIjpFOcHdLMRhxkTyt6 7pe3o5diQ1wRQL/niHFA9y3+CojpKN5zNzsIpFvOWiTXXVFdzJjnGIr3WzMQhXDa3D C4uiQ/6RRHvNW+w10Zejy87Xpv67WCTWUXx/UgHPXEwmAc8txlIw1k78aOqJHUCXEN qVfjTjO7nOVw5sWWGXkm9w6ljF7Il7/ThetOPApQ2Lvc5h69ngevP67Qrdh3C7UDIb oj597ytCay+aotxo+SlMdY1L06HW26xGeYe5JwBi80lyJU7UUWL8TEDqPj8k/Q9+dc pc2niMeaOYo7A== Date: Mon, 01 Jul 2024 18:18:46 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122243.2010218.604571260560807315.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 791f7d36fa8a..9d41aad784a1 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 2 01:19:02 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: 13718862 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 B06689441 for ; Tue, 2 Jul 2024 01:19:02 +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=1719883142; cv=none; b=p5xe87encTb6OU5V8w16m2aPk53YLmz2fpx8huT5XylIpDHU3y1VuOD3Q2no4WscEPwMQ4wCD31dz97XsIvMWOzoR0xIY7c9HDwGqP93QmjZBcipfZK3o6M0p3aK1130A80yk5UXFhwtx2ljUnz0kOJfiJnxAxVONRcXGEVrfiM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883142; c=relaxed/simple; bh=hXsbBjwfoge+L76tmqFWO+yD9d6+EWYi6zO7dS5agp8=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lSoZkZrUfC0skC3gW4wDxWx3gyjWTKaesCJsSfLWu0RlAGZhYKcofEyxmHLViuE6/fFd+M6e/w+Aw0LmekfbkcmG17wgNECGaJ2T0FERArN2GSJYz47KJJfMGhYfXX2/oxr193x9EClaEnvayXni2tb7hRJVNy7dIF2LCWkn/W4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VDWn1hgk; 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="VDWn1hgk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87F3EC116B1; Tue, 2 Jul 2024 01:19:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883142; bh=hXsbBjwfoge+L76tmqFWO+yD9d6+EWYi6zO7dS5agp8=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=VDWn1hgkhWlRoJY1u+8/XN4/+l2QAHmtNlvg/4v9/bQyuNAHbiEXL3InSe9pM9uSC UHsx2p1jn3Ma4h9Biy+CyRzQNCg/2tY9V2Ifwq2j32dfGUy8KZViCAgMqvD8mINpwE I8LFjADo9SrEmlminxJFkDNVTMS4QykJ6OYMSsI85uQwAI5yNueUWR0pLtdIX02OsX LF84dpzKhd3t7kyP/9J6vq2df5dzYw+p/CgeZpAQ31+udiF/OxhWNEFo1FwqW+/1yu RQy8m72bpkivZo8Mc5cN/v4X2QXL8ZlnY4ynaw8sdljvJ+J+XCoNeY8Tgf7k5+yMyO /OX4CUDfxBUUQ== Date: Mon, 01 Jul 2024 18:19:02 -0700 Subject: [PATCH 06/12] xfs_repair: build a parent pointer index From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122258.2010218.748165248811754209.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 d948588781de..b0acc8c46a33 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 9d41aad784a1..fe56feb6ecef 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 000000000000..193daa0a5467 --- /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 000000000000..7522379423ff --- /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 2 01:19:17 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: 13718863 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 B7BA69441 for ; Tue, 2 Jul 2024 01:19:18 +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=1719883158; cv=none; b=BBfeEnTfmywfFh1/0tRMtc5ypnBQNE+LD1x2oaiNHODv1VZPjZj9lY8IMNZ/PI6DBqZlGZfzteMdTmk8ZFMXiBVkWkNdl4S/1/W8lsI78dKqz4n+t/30FOUzKFvvX07yyFF/OOatNV7dtnWOvk/9fKoIRntWrmHfFwqhgmG6YvY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883158; c=relaxed/simple; bh=rOPHKtAQt6DKduNW1mLOh6UVQ8kvWRphpvzgApjgtgs=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BrUU+p29vsA/q2Ko4GnHpROjQAh6MsYvDZmyNOoMMt6VLn+Q1mDBdjtfeSvFcfJGGnySFhnsWpO0FDJ3zosX80saJaLoOzN3HsVuQW15888Dhfi2QnkevFd+1gxqDAsnfEHOOldXYKGn7AsXjYvqYipUDAN5p02qCO/qifxJjJQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=m5sdkXKz; 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="m5sdkXKz" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3B327C116B1; Tue, 2 Jul 2024 01:19:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883158; bh=rOPHKtAQt6DKduNW1mLOh6UVQ8kvWRphpvzgApjgtgs=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=m5sdkXKzcDLO9t1VOJNt/97Ib5x2Z8QnLJeaFt1uh3F1EHwgcFGzpLGwuMVCLE7J7 TgvyVdsj0aLmP3emA8Ioxpi3yqlNdn9nlCQ8cLjPFmQVICmjicGFufJ8cW25u4xC/a 494hf3dRvXAwE0mec5UwjsQ8s1Dj31v3eq7C6yXB7ye4CaUKBK8g8k3ps0ADMdDnOB 1qHmMWhbldumNUUwxzQcZuIDEhHkZQhr5eXu0jWfLUTleM5ssgU5IEN9cd+X57LQXU VSSxHs1QJD10p0GzVE+yzNfD9IKFmmN1gdud+8eXfXhzTD/4aZZ1PCue/gxCKJHv9H 4hsY/uEWdoOAg== Date: Mon, 01 Jul 2024 18:19:17 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122273.2010218.9308056457503800032.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 b0acc8c46a33..a36a95e353a5 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 193daa0a5467..9f219b398208 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 000000000000..45d2559c7223 --- /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 000000000000..27e98eee2080 --- /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 2 01:19:33 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: 13718864 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 094F09441 for ; Tue, 2 Jul 2024 01:19:34 +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=1719883174; cv=none; b=oaibGWW5sYZz15F4kC865qhM+Ob0MQeKxrWEVI6lOv3UYNjQ9R6eqcIl7HCyvcsvEWUyCSz0k/2MwWPN4SOs3JIWs9F/I0PSkiMT/p/EJH7LHdHVLRaKjMVHqWRVNPYAX4t1PkeW07aQ23a8fReaR1selM3ATjjhQpNeAKCvEfw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883174; c=relaxed/simple; bh=wluRUZIYb6SDa6PHqnH+g2iJAegK/WoJ98d01C6y6Yo=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=g7VP39l21f3p5G7ucWmouzXbhwx6SBkeZ5HRL7L2bDWyqLzeHMZK5EkDo/gZFUO8BgLDauBbZIieV5s6NbdULn2H1NAmIS0vDKGEEcqrz7Z7vctsyu1j8Lpz/AE2KHPpCzLa7OFMXLp8ONpRbpRIWmEAv8Qty5EOrrlY0EgnT+Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mf+wUyO8; 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="mf+wUyO8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D0812C116B1; Tue, 2 Jul 2024 01:19:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883173; bh=wluRUZIYb6SDa6PHqnH+g2iJAegK/WoJ98d01C6y6Yo=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=mf+wUyO8OWVo6AufyVIjpWth+VNG37u4j8fD0RBQlDlGspRVUd+JUFn8IAmIZ39pv vcm1KR1AfFBesYxAcix0/kEN4C1ylnBXQd1963hClmhzaA6985RfK/Pxl/YSg6iQrj EY70SL8gFEdZKt5mtmy4SuAJldu4AGERYXOQIHHnk5OfD9BNccJgj7f/Eg7ThjefQa 3DBUAg/Ml5Png6/JGcJBtxbu7E3+q1KEmgI2D9ZyoTlkbRTNJdgWNSudN92U2wCTpT tkRI2VNgzsbjXuRO+2m88BuY5GeqNavUDsvp3lL61Gctrki7XiXEiu1vnYl8Hy6g01 g9+B4MmhRGzFA== Date: Mon, 01 Jul 2024 18:19:33 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122289.2010218.15312017622674815172.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 9f219b398208..f8db57b2c6f6 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 45d2559c7223..3cd678dbbab9 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 27e98eee2080..40cd6d8e91c0 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 2 01:19:49 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: 13718865 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 A39099441 for ; Tue, 2 Jul 2024 01:19:49 +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=1719883189; cv=none; b=p9ban4xz6w76MU3IzDaAcewAfbSet0YLA/sLMuPTqZNzcodhkaTRbxuH9yzkNapj0zRxl032DxOBwg+DnhUk9oW/uovsz0kZCVRRIETk1yeua6tmtcXmHfUAVVvBP6vMlShtZWfVTZQOLUM2QjbCiwG1JzagJaAw3DeqgF3m1Z4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883189; c=relaxed/simple; bh=GcRYYGFQXLN5/jlpOA6ICl2ZgWjE1tbYwSZ+wRhGnwI=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=RgZtxBdOGTe4CfrC/6fb86DY2T3XPhFaTCkpd7z6qJTKvEyT20TbO42veFh9o2277mKohqBi71lX2OI7knqPanr2htpMuo+jBIwnOzXdgGg9ng0BdowzjQGi6Q8dQfMrBlJ1mJH//3THrcNa/1IcCssgf3P+0Y3XP+d1lpEHbRA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PrCIFJmn; 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="PrCIFJmn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 727A7C2BD10; Tue, 2 Jul 2024 01:19:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883189; bh=GcRYYGFQXLN5/jlpOA6ICl2ZgWjE1tbYwSZ+wRhGnwI=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=PrCIFJmnbymZg31vf3/hpAx6ZQCBv7rboJ9CFXP3+Bb1PfGzUY+zO6GULxaEzOJPz XlXhI96B6u8atCPFBd0MHKrTsX0hras2v52y5PsAQStd0OvsjnC0ilqAwLvPprRLt0 G7F7J+8/+9/OwkbVCnvJ1lDUSc/IRVD7Q7lXZ2iTnjJW0NaE/xE+SFf+Dr5b23HxpV +iTAya6SZjb80Do/QWzLbnXTtjTaTNwu5Lt3l/7ctcsMTYezuoGtsijGV8CNat4Aby X0Y6v+r1wdCXrKhbKn8F3vceSFDDbuepg4ueoV9Ln3iZwm4WYjCn9PGarPEEfRfQG/ j1PwL63Ge6s1g== Date: Mon, 01 Jul 2024 18:19:49 -0700 Subject: [PATCH 09/12] xfs_repair: check parent pointers From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122305.2010218.16086560804318113038.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 7d8caaa4c164..00f8ed5e5a7b 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 28bf4ab2898f..1939202e12db 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 a36a95e353a5..e7445d53e918 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 000000000000..2af77b7b2195 --- /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 000000000000..2d26fce0f323 --- /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 fe56feb6ecef..ad067ba0ac87 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 f8db57b2c6f6..bd9bb6f8bf36 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 7522379423ff..65acff963a3f 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 2 01:20:04 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: 13718866 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 5672E9441 for ; Tue, 2 Jul 2024 01:20:05 +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=1719883205; cv=none; b=GxoCyFcV5TQCOHoOowmImhoXPVD9pIaKiladLdDAn6Tpm+dfowx5ycO4mqRFGPSTnmYOYUG5RjTV+xqNKkPtHBInrvrnH+pIDLz7UtNeEH3bAVfqmOvrq0tkz9P1SXTs9y9tz9Q5RDSMKA2/l9gU7Cg85yv2x7sXShhc6zSOohw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883205; c=relaxed/simple; bh=2q0J+hoE8GFWQYfY0bryUKQ4yWyuGKJV5KJ8D1wVsuA=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sdq1zgMqBW5IZQQHKgrhEnkf6R9v3d5mxl8Qq2LZ+xBWPCuApnb/zRCKgA/RdmEa/wH/KhRI42DVOiPr138HqJ5GxHr6hEuCfKtslEwj8Bb4V0qz7Okpe50qqahIGHutDyA9rs7fJ5at21VC6LWdRaHvyIGTZLA+Qn8wFrIDKkY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TO3YeAlW; 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="TO3YeAlW" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 298CFC116B1; Tue, 2 Jul 2024 01:20:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883205; bh=2q0J+hoE8GFWQYfY0bryUKQ4yWyuGKJV5KJ8D1wVsuA=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=TO3YeAlWWfnY/pCxz4Uiv6pAvopKehwoW7LPNKHXRfLnlWNw5I0ENxvKE+eRFZ2kp DkAQqL4WcONqbnnq6h7Ht9tGWj5Wx6zbKfTleMhqaAkG3Io2PtUqXb0hjJSn+a/mXG mE7cgPlT9zqIUry5lplaPPwp7jpK5POap55qjApKIoFvqZqLXoUgdvDjLOgi8FNrDN 0jbjchH4foqxgqO8aZALyfXykogT5VogvOj3+qYrdzOTnT5oTLFCv88bAZ8dRx6dKG qWMFtY7pGU3ElGRtZ6Tdc+48Qg29XztGCTAKG+DBV3KfhbAeucl6YWsl5cjkNQRHrw 8xHQdLGmUnoAg== Date: Mon, 01 Jul 2024 18:20:04 -0700 Subject: [PATCH 10/12] xfs_repair: dump garbage parent pointer attributes From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122321.2010218.4681521171948051075.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 d3611e05bade..e12f0a40b00a 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 bd9bb6f8bf36..61466009d88b 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 2 01:20:20 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: 13718867 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 3B52038B for ; Tue, 2 Jul 2024 01:20:20 +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=1719883221; cv=none; b=kDrf4Llth4hu+CaLzUQZRnn9XYMbeOYC3Wn2avL6bre7mPasHKbPp4cRpCgx/Y/8dthOoTLDKuBR5I2JhFzQg6qAoBk1iSKNI9sfMZQky60K1wl2NpXGEkw6NoPNDbuQwWo8wx3wPykuWVuNTaK2gO5wDHYn47qdkutuzkGNHUM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883221; c=relaxed/simple; bh=kDhpKlLishcifl0GAzSHTkL/OL/dIV07jKXSEpMri2Q=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lgTUhEaeiteG6GMsWFv2rormTFdOPp3CP7kyEZV1wEiD2borHTBuyn6yZ/qD3MkfnYTb5jxYxSmqdghbNz9jjkc+1+bTafVLAglch4tms7cPWXT2hFQMBCluVTRgqIFT5BhVr1Yvm+6GnvdbSMlQF4uN9RkCqcqkfL6y23XNnvc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LmmedEn7; 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="LmmedEn7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CA7FFC116B1; Tue, 2 Jul 2024 01:20:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883220; bh=kDhpKlLishcifl0GAzSHTkL/OL/dIV07jKXSEpMri2Q=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=LmmedEn7CdLkw4teLVptjvQ9IHTKTv5sp4ZuNTEAhBjZlIqCRLOVTQrDZXsZGIsSb eklX1nEsTf/Mivg0vHbjYmvPqm5FWvfH7fEsxEvqUqKgfxuWRs1bfpIXmFzIwCbFrC U3KWci6XfRUqqr/gUY3vdDEkOqo87IG2OvNZGDqrxHXqfyb668YoMGIDL07B6OyeHa RJDBssM9xkB2QL5bq5DVmO0glFZ2FOzuePIT3fUI4TxPtUUVhIUj0JsgOitGWgbH4H vK8wSsM/N6gkJorqMfE5dNF3xwZuyuf2Q8bSKnD/3XefwcdulG80Vhhx72NSVcB9js /ad+2kGHASDuQ== Date: Mon, 01 Jul 2024 18:20:20 -0700 Subject: [PATCH 11/12] xfs_repair: update ondisk parent pointer records From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122336.2010218.16979367018169015160.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 e12f0a40b00a..df316727bd62 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 61466009d88b..94d6d834627c 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 2 01:20:36 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: 13718868 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 E04834A20 for ; Tue, 2 Jul 2024 01:20:36 +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=1719883237; cv=none; b=GnNglsekHOVqE0dREGdXe7slNK96w+mX5CI0LoxTSetHmId+CKztcNPfELY0EO7WrqA7ZsausMAp+poo9lPXRwkHmZaRjq0oTFGlzOPDhPI8WAXw3lyQXdSQHow1JRL53cAr+gQ7oaF9/01NNxR9hHQK1oIKvXeZ/HJeUdpPLpc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883237; c=relaxed/simple; bh=hXeDFFPLdsOFsiXDyS5BzKNrXOnC+gvIs6cLwSHF21M=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FmpId5T7AcHK1DI2lQAFzioTSiR2UpWPe/ctQLz1SiTfceiIdgGclOs37/8htjK5LytQBEgTFeq6YSC0zb08bjLDWyvZ8AYfGmbr9eSV8Qf9akK2oh2vdT4KzRpGrYMlxdPny5TWVoznYObziNSpOCXca7qJi+LfYgX90tqMxk4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VKHiMe43; 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="VKHiMe43" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6BE88C116B1; Tue, 2 Jul 2024 01:20:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883236; bh=hXeDFFPLdsOFsiXDyS5BzKNrXOnC+gvIs6cLwSHF21M=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=VKHiMe43y+AX+SjLm3rJHQjwIQCofzaYIRnJnx1SEAc+F48c+1JUCflxgegCTiNC9 o0+9tz+uSfwXKjeA5kxHYwQjqaplUd2rBsqDHoB/yciJCNvgL7XChOr4HIU7scLP4Q P51GhADpKGbNEu9lDLx+CUcqcvOwzqONVP8uPPQbdPSZH+nSoF+J84FX8wJXfhHuUk a9GwomfyblONGcMiREdkvEIgiJpRcgdIFRsTMIds3u25RmjLFxGOba3f1PdMpWLuCh ZsXaIYKH/RdHWPtJ4xQXeTCbFG9VTcHAUi4xsi64/LKEsMa8e+HL1J4wLZVtaiMl6r EebA6LzJ0gxSw== Date: Mon, 01 Jul 2024 18:20:36 -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: catherine.hoang@oracle.com, linux-xfs@vger.kernel.org, allison.henderson@oracle.com, hch@lst.de Message-ID: <171988122352.2010218.17899349898608092894.stgit@frogsfrogsfrogs> In-Reply-To: <171988122147.2010218.8997075209577453030.stgit@frogsfrogsfrogs> References: <171988122147.2010218.8997075209577453030.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 94d6d834627c..8ec6a51d2c3d 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; }