Commit 3d2c3411 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: scrub metadata directories



Teach online scrub about the metadata directory tree.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 5dab2daa
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -100,6 +100,14 @@ xchk_dir_check_ftype(

	if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype)
		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);

	/*
	 * Metadata and regular inodes cannot cross trees.  This property
	 * cannot change without a full inode free and realloc cycle, so it's
	 * safe to check this without holding locks.
	 */
	if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(sc->ip))
		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
}

/*
+6 −0
Original line number Diff line number Diff line
@@ -415,6 +415,12 @@ xrep_dir_salvage_entry(
	if (error)
		return 0;

	/* Don't mix metadata and regular directory trees. */
	if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(rd->sc->ip)) {
		xchk_irele(sc, ip);
		return 0;
	}

	xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
	xchk_irele(sc, ip);

+14 −3
Original line number Diff line number Diff line
@@ -362,7 +362,8 @@ xchk_dirpath_set_outcome(
STATIC int
xchk_dirpath_step_up(
	struct xchk_dirtree	*dl,
	struct xchk_dirpath	*path)
	struct xchk_dirpath	*path,
	bool			is_metadir)
{
	struct xfs_scrub	*sc = dl->sc;
	struct xfs_inode	*dp;
@@ -435,6 +436,14 @@ xchk_dirpath_step_up(
		goto out_scanlock;
	}

	/* Parent must be in the same directory tree. */
	if (is_metadir != xfs_is_metadir_inode(dp)) {
		trace_xchk_dirpath_crosses_tree(dl->sc, dp, path->path_nr,
				path->nr_steps, &dl->xname, &dl->pptr_rec);
		error = -EFSCORRUPTED;
		goto out_scanlock;
	}

	/*
	 * If the extended attributes look as though they has been zapped by
	 * the inode record repair code, we cannot scan for parent pointers.
@@ -508,6 +517,7 @@ xchk_dirpath_walk_upwards(
	struct xchk_dirpath	*path)
{
	struct xfs_scrub	*sc = dl->sc;
	bool			is_metadir;
	int			error;

	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
@@ -538,6 +548,7 @@ xchk_dirpath_walk_upwards(
	 * ILOCK state is no longer tracked in the scrub context.  Hence we
	 * must drop @sc->ip's ILOCK during the walk.
	 */
	is_metadir = xfs_is_metadir_inode(sc->ip);
	mutex_unlock(&dl->lock);
	xchk_iunlock(sc, XFS_ILOCK_EXCL);

@@ -547,7 +558,7 @@ xchk_dirpath_walk_upwards(
	 * If we see any kind of error here (including corruptions), the parent
	 * pointer of @sc->ip is corrupt.  Stop the whole scan.
	 */
	error = xchk_dirpath_step_up(dl, path);
	error = xchk_dirpath_step_up(dl, path, is_metadir);
	if (error) {
		xchk_ilock(sc, XFS_ILOCK_EXCL);
		mutex_lock(&dl->lock);
@@ -560,7 +571,7 @@ xchk_dirpath_walk_upwards(
	 * *somewhere* in the path, but we don't need to stop scanning.
	 */
	while (!error && path->outcome == XCHK_DIRPATH_SCANNING)
		error = xchk_dirpath_step_up(dl, path);
		error = xchk_dirpath_step_up(dl, path, is_metadir);

	/* Retake the locks we had, mark paths, etc. */
	xchk_ilock(sc, XFS_ILOCK_EXCL);
+13 −0
Original line number Diff line number Diff line
@@ -172,6 +172,10 @@ xrep_findparent_walk_directory(
	 */
	lock_mode = xfs_ilock_data_map_shared(dp);

	/* Don't mix metadata and regular directory trees. */
	if (xfs_is_metadir_inode(dp) != xfs_is_metadir_inode(sc->ip))
		goto out_unlock;

	/*
	 * If this directory is known to be sick, we cannot scan it reliably
	 * and must abort.
@@ -368,6 +372,12 @@ xrep_findparent_confirm(
		return 0;
	}

	/* The metadata root directory always points to itself. */
	if (sc->ip == sc->mp->m_metadirip) {
		*parent_ino = sc->mp->m_sb.sb_metadirino;
		return 0;
	}

	/* Unlinked dirs can point anywhere; point them up to the root dir. */
	if (VFS_I(sc->ip)->i_nlink == 0) {
		*parent_ino = xchk_inode_rootdir_inum(sc->ip);
@@ -415,6 +425,9 @@ xrep_findparent_self_reference(
	if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino)
		return sc->mp->m_sb.sb_rootino;

	if (sc->ip->i_ino == sc->mp->m_sb.sb_metadirino)
		return sc->mp->m_sb.sb_metadirino;

	if (VFS_I(sc->ip)->i_nlink == 0)
		return xchk_inode_rootdir_inum(sc->ip);

+14 −0
Original line number Diff line number Diff line
@@ -132,6 +132,14 @@ xchk_parent_validate(
		return 0;
	}

	/* Is this the metadata root dir?  Then '..' must point to itself. */
	if (sc->ip == mp->m_metadirip) {
		if (sc->ip->i_ino != mp->m_sb.sb_metadirino ||
		    sc->ip->i_ino != parent_ino)
			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
		return 0;
	}

	/* '..' must not point to ourselves. */
	if (sc->ip->i_ino == parent_ino) {
		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
@@ -185,6 +193,12 @@ xchk_parent_validate(
		goto out_unlock;
	}

	/* Metadata and regular inodes cannot cross trees. */
	if (xfs_is_metadir_inode(dp) != xfs_is_metadir_inode(sc->ip)) {
		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
		goto out_unlock;
	}

	/* Look for a directory entry in the parent pointing to the child. */
	error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);
	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
Loading