Commit 8f71bede authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: repair inode fork block mapping data structures



Use the reverse-mapping btree information to rebuild an inode block map.
Update the btree bulk loading code as necessary to support inode rooted
btrees and fix some bitrot problems.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 66da1128
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
xfs-y				+= $(addprefix scrub/, \
				   agheader_repair.o \
				   alloc_repair.o \
				   bmap_repair.o \
				   ialloc_repair.o \
				   inode_repair.o \
				   newbt.o \
+101 −20
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include "xfs_trans.h"
#include "xfs_alloc.h"
#include "xfs_btree.h"
#include "xfs_btree_staging.h"
#include "xfs_bmap_btree.h"
#include "xfs_bmap.h"
#include "xfs_error.h"
@@ -288,10 +289,7 @@ xfs_bmbt_get_minrecs(
	int			level)
{
	if (level == cur->bc_nlevels - 1) {
		struct xfs_ifork	*ifp;

		ifp = xfs_ifork_ptr(cur->bc_ino.ip,
				    cur->bc_ino.whichfork);
		struct xfs_ifork	*ifp = xfs_btree_ifork_ptr(cur);

		return xfs_bmbt_maxrecs(cur->bc_mp,
					ifp->if_broot_bytes, level == 0) / 2;
@@ -306,10 +304,7 @@ xfs_bmbt_get_maxrecs(
	int			level)
{
	if (level == cur->bc_nlevels - 1) {
		struct xfs_ifork	*ifp;

		ifp = xfs_ifork_ptr(cur->bc_ino.ip,
				    cur->bc_ino.whichfork);
		struct xfs_ifork	*ifp = xfs_btree_ifork_ptr(cur);

		return xfs_bmbt_maxrecs(cur->bc_mp,
					ifp->if_broot_bytes, level == 0);
@@ -543,23 +538,19 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
	.keys_contiguous	= xfs_bmbt_keys_contiguous,
};

/*
 * Allocate a new bmap btree cursor.
 */
struct xfs_btree_cur *				/* new bmap btree cursor */
xfs_bmbt_init_cursor(
	struct xfs_mount	*mp,		/* file system mount point */
	struct xfs_trans	*tp,		/* transaction pointer */
	struct xfs_inode	*ip,		/* inode owning the btree */
	int			whichfork)	/* data or attr fork */
static struct xfs_btree_cur *
xfs_bmbt_init_common(
	struct xfs_mount	*mp,
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	int			whichfork)
{
	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
	struct xfs_btree_cur	*cur;

	ASSERT(whichfork != XFS_COW_FORK);

	cur = xfs_btree_alloc_cursor(mp, tp, XFS_BTNUM_BMAP,
			mp->m_bm_maxlevels[whichfork], xfs_bmbt_cur_cache);
	cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
	cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_bmbt_2);

	cur->bc_ops = &xfs_bmbt_ops;
@@ -567,10 +558,30 @@ xfs_bmbt_init_cursor(
	if (xfs_has_crc(mp))
		cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;

	cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
	cur->bc_ino.ip = ip;
	cur->bc_ino.allocated = 0;
	cur->bc_ino.flags = 0;

	return cur;
}

/*
 * Allocate a new bmap btree cursor.
 */
struct xfs_btree_cur *
xfs_bmbt_init_cursor(
	struct xfs_mount	*mp,
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	int			whichfork)
{
	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
	struct xfs_btree_cur	*cur;

	cur = xfs_bmbt_init_common(mp, tp, ip, whichfork);

	cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
	cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
	cur->bc_ino.whichfork = whichfork;

	return cur;
@@ -587,6 +598,76 @@ xfs_bmbt_block_maxrecs(
	return blocklen / (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t));
}

/*
 * Allocate a new bmap btree cursor for reloading an inode block mapping data
 * structure.  Note that callers can use the staged cursor to reload extents
 * format inode forks if they rebuild the iext tree and commit the staged
 * cursor immediately.
 */
struct xfs_btree_cur *
xfs_bmbt_stage_cursor(
	struct xfs_mount	*mp,
	struct xfs_inode	*ip,
	struct xbtree_ifakeroot	*ifake)
{
	struct xfs_btree_cur	*cur;
	struct xfs_btree_ops	*ops;

	/* data fork always has larger maxheight */
	cur = xfs_bmbt_init_common(mp, NULL, ip, XFS_DATA_FORK);
	cur->bc_nlevels = ifake->if_levels;
	cur->bc_ino.forksize = ifake->if_fork_size;

	/* Don't let anyone think we're attached to the real fork yet. */
	cur->bc_ino.whichfork = -1;
	xfs_btree_stage_ifakeroot(cur, ifake, &ops);
	ops->update_cursor = NULL;
	return cur;
}

/*
 * Swap in the new inode fork root.  Once we pass this point the newly rebuilt
 * mappings are in place and we have to kill off any old btree blocks.
 */
void
xfs_bmbt_commit_staged_btree(
	struct xfs_btree_cur	*cur,
	struct xfs_trans	*tp,
	int			whichfork)
{
	struct xbtree_ifakeroot	*ifake = cur->bc_ino.ifake;
	struct xfs_ifork	*ifp;
	static const short	brootflag[2] = {XFS_ILOG_DBROOT, XFS_ILOG_ABROOT};
	static const short	extflag[2] = {XFS_ILOG_DEXT, XFS_ILOG_AEXT};
	int			flags = XFS_ILOG_CORE;

	ASSERT(cur->bc_flags & XFS_BTREE_STAGING);
	ASSERT(whichfork != XFS_COW_FORK);

	/*
	 * Free any resources hanging off the real fork, then shallow-copy the
	 * staging fork's contents into the real fork to transfer everything
	 * we just built.
	 */
	ifp = xfs_ifork_ptr(cur->bc_ino.ip, whichfork);
	xfs_idestroy_fork(ifp);
	memcpy(ifp, ifake->if_fork, sizeof(struct xfs_ifork));

	switch (ifp->if_format) {
	case XFS_DINODE_FMT_EXTENTS:
		flags |= extflag[whichfork];
		break;
	case XFS_DINODE_FMT_BTREE:
		flags |= brootflag[whichfork];
		break;
	default:
		ASSERT(0);
		break;
	}
	xfs_trans_log_inode(tp, cur->bc_ino.ip, flags);
	xfs_btree_commit_ifakeroot(cur, tp, whichfork, &xfs_bmbt_ops);
}

/*
 * Calculate number of records in a bmap btree block.
 */
+5 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ struct xfs_btree_block;
struct xfs_mount;
struct xfs_inode;
struct xfs_trans;
struct xbtree_ifakeroot;

/*
 * Btree block header size depends on a superblock flag.
@@ -106,6 +107,10 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,

extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
		struct xfs_trans *, struct xfs_inode *, int);
struct xfs_btree_cur *xfs_bmbt_stage_cursor(struct xfs_mount *mp,
		struct xfs_inode *ip, struct xbtree_ifakeroot *ifake);
void xfs_bmbt_commit_staged_btree(struct xfs_btree_cur *cur,
		struct xfs_trans *tp, int whichfork);

extern unsigned long long xfs_bmbt_calc_size(struct xfs_mount *mp,
		unsigned long long len);
+9 −2
Original line number Diff line number Diff line
@@ -405,7 +405,7 @@ xfs_btree_bload_prep_block(
		ASSERT(*bpp == NULL);

		/* Allocate a new incore btree root block. */
		new_size = bbl->iroot_size(cur, nr_this_block, priv);
		new_size = bbl->iroot_size(cur, level, nr_this_block, priv);
		ifp->if_broot = kmem_zalloc(new_size, 0);
		ifp->if_broot_bytes = (int)new_size;

@@ -596,6 +596,13 @@ xfs_btree_bload_level_geometry(
	unsigned int		desired_npb;
	unsigned int		maxnr;

	/*
	 * Compute the absolute maximum number of records that we can store in
	 * the ondisk block or inode root.
	 */
	if (cur->bc_ops->get_dmaxrecs)
		maxnr = cur->bc_ops->get_dmaxrecs(cur, level);
	else
		maxnr = cur->bc_ops->get_maxrecs(cur, level);

	/*
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ typedef int (*xfs_btree_bload_get_records_fn)(struct xfs_btree_cur *cur,
typedef int (*xfs_btree_bload_claim_block_fn)(struct xfs_btree_cur *cur,
		union xfs_btree_ptr *ptr, void *priv);
typedef size_t (*xfs_btree_bload_iroot_size_fn)(struct xfs_btree_cur *cur,
		unsigned int nr_this_level, void *priv);
		unsigned int level, unsigned int nr_this_level, void *priv);

struct xfs_btree_bload {
	/*
Loading