Commit 669dfe88 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: update the unlinked list when repairing link counts



When we're repairing the link counts of a file, we must ensure either
that the file has zero link count and is on the unlinked list; or that
it has nonzero link count and is not on the unlinked list.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent e921533e
Loading
Loading
Loading
Loading
+33 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "xfs_iwalk.h"
#include "xfs_ialloc.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/repair.h"
@@ -36,6 +37,20 @@
 * inode is locked.
 */

/* Remove an inode from the unlinked list. */
STATIC int
xrep_nlinks_iunlink_remove(
	struct xfs_scrub	*sc)
{
	struct xfs_perag	*pag;
	int			error;

	pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, sc->ip->i_ino));
	error = xfs_iunlink_remove(sc->tp, pag, sc->ip);
	xfs_perag_put(pag);
	return error;
}

/*
 * Correct the link count of the given inode.  Because we have to grab locks
 * and resources in a certain order, it's possible that this will be a no-op.
@@ -99,16 +114,25 @@ xrep_nlinks_repair_inode(
	}

	/*
	 * We did not find any links to this inode.  If the inode agrees, we
	 * have nothing further to do.  If not, the inode has a nonzero link
	 * count and we don't have anywhere to graft the child onto.  Dropping
	 * a live inode's link count to zero can cause unexpected shutdowns in
	 * inactivation, so leave it alone.
	 * If this inode is linked from the directory tree and on the unlinked
	 * list, remove it from the unlinked list.
	 */
	if (total_links == 0) {
		if (actual_nlink != 0)
			trace_xrep_nlinks_unfixable_inode(mp, ip, &obs);
	if (total_links > 0 && xfs_inode_on_unlinked_list(ip)) {
		error = xrep_nlinks_iunlink_remove(sc);
		if (error)
			goto out_trans;
		dirty = true;
	}

	/*
	 * If this inode is not linked from the directory tree yet not on the
	 * unlinked list, put it on the unlinked list.
	 */
	if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
		error = xfs_iunlink(sc->tp, ip);
		if (error)
			goto out_trans;
		dirty = true;
	}

	/* Commit the new link count if it changed. */