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

xfs: move files to orphanage instead of letting nlinks drop to zero



If we encounter an inode with a nonzero link count but zero observed
links, move it to the orphanage.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 1e58a8cc
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -4789,7 +4789,8 @@ Orphaned files are adopted by the orphanage as follows:
   cache.

6. Call ``xrep_adoption_finish`` to commit any filesystem updates, release the
   orphanage ILOCK, and clean the scrub transaction.
   orphanage ILOCK, and clean the scrub transaction.  Call
   ``xrep_adoption_commit`` to commit the updates and the scrub transaction.

7. If a runtime error happens, call ``xrep_adoption_cancel`` to release all
   resources.
+15 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/iscan.h"
#include "scrub/orphanage.h"
#include "scrub/nlinks.h"
#include "scrub/trace.h"
#include "scrub/readdir.h"
@@ -44,11 +45,23 @@ int
xchk_setup_nlinks(
	struct xfs_scrub	*sc)
{
	struct xchk_nlink_ctrs	*xnc;
	int			error;

	xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);

	sc->buf = kzalloc(sizeof(struct xchk_nlink_ctrs), XCHK_GFP_FLAGS);
	if (!sc->buf)
	if (xchk_could_repair(sc)) {
		error = xrep_setup_nlinks(sc);
		if (error)
			return error;
	}

	xnc = kvzalloc(sizeof(struct xchk_nlink_ctrs), XCHK_GFP_FLAGS);
	if (!xnc)
		return -ENOMEM;
	xnc->xname.name = xnc->namebuf;
	xnc->sc = sc;
	sc->buf = xnc;

	return xchk_setup_fs(sc);
}
@@ -873,9 +886,6 @@ xchk_nlinks_setup_scan(
	xfs_agino_t		first_agino, last_agino;
	int			error;

	ASSERT(xnc->sc == NULL);
	xnc->sc = sc;

	mutex_init(&xnc->lock);

	/* Retry iget every tenth of a second for up to 30 seconds. */
+7 −0
Original line number Diff line number Diff line
@@ -28,6 +28,13 @@ struct xchk_nlink_ctrs {
	 * from other writer threads.
	 */
	struct xfs_dir_hook	dhook;

	/* Orphanage reparenting request. */
	struct xrep_adoption	adoption;

	/* Directory entry name, plus the trailing null. */
	struct xfs_name		xname;
	char			namebuf[MAXNAMELEN];
};

/*
+110 −13
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/iscan.h"
#include "scrub/orphanage.h"
#include "scrub/nlinks.h"
#include "scrub/trace.h"
#include "scrub/tempfile.h"
@@ -38,6 +39,34 @@
 * inode is locked.
 */

/* Set up to repair inode link counts. */
int
xrep_setup_nlinks(
	struct xfs_scrub	*sc)
{
	return xrep_orphanage_try_create(sc);
}

/*
 * Inodes that aren't the root directory or the orphanage, have a nonzero link
 * count, and no observed parents should be moved to the orphanage.
 */
static inline bool
xrep_nlinks_is_orphaned(
	struct xfs_scrub	*sc,
	struct xfs_inode	*ip,
	unsigned int		actual_nlink,
	const struct xchk_nlink	*obs)
{
	struct xfs_mount	*mp = ip->i_mount;

	if (obs->parents != 0)
		return false;
	if (ip == mp->m_rootip || ip == sc->orphanage)
		return false;
	return actual_nlink != 0;
}

/* Remove an inode from the unlinked list. */
STATIC int
xrep_nlinks_iunlink_remove(
@@ -66,6 +95,7 @@ xrep_nlinks_repair_inode(
	struct xfs_inode	*ip = sc->ip;
	uint64_t		total_links;
	uint64_t		actual_nlink;
	bool			orphanage_available = false;
	bool			dirty = false;
	int			error;

@@ -77,14 +107,41 @@ xrep_nlinks_repair_inode(
	if (xrep_is_tempfile(ip))
		return 0;

	/*
	 * If the filesystem has an orphanage attached to the scrub context,
	 * prepare for a link count repair that could involve @ip being adopted
	 * by the lost+found.
	 */
	if (xrep_orphanage_can_adopt(sc)) {
		error = xrep_orphanage_iolock_two(sc);
		if (error)
			return error;

		error = xrep_adoption_trans_alloc(sc, &xnc->adoption);
		if (error) {
			xchk_iunlock(sc, XFS_IOLOCK_EXCL);
			xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
		} else {
			orphanage_available = true;
		}
	}

	/*
	 * Either there is no orphanage or we couldn't allocate resources for
	 * that kind of update.  Let's try again with only the resources we
	 * need for a simple link count update, since that's much more common.
	 */
	if (!orphanage_available) {
		xchk_ilock(sc, XFS_IOLOCK_EXCL);

	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link, 0, 0, 0, &sc->tp);
		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link, 0, 0, 0,
				&sc->tp);
		if (error)
			return error;

		xchk_ilock(sc, XFS_ILOCK_EXCL);
		xfs_trans_ijoin(sc->tp, ip, 0);
	}

	mutex_lock(&xnc->lock);

@@ -122,6 +179,41 @@ xrep_nlinks_repair_inode(
		goto out_trans;
	}

	/*
	 * Decide if we're going to move this file to the orphanage, and fix
	 * up the incore link counts if we are.
	 */
	if (orphanage_available &&
	    xrep_nlinks_is_orphaned(sc, ip, actual_nlink, &obs)) {
		/* Figure out what name we're going to use here. */
		error = xrep_adoption_compute_name(&xnc->adoption, &xnc->xname);
		if (error)
			goto out_trans;

		/*
		 * Reattach this file to the directory tree by moving it to
		 * the orphanage per the adoption parameters that we already
		 * computed.
		 */
		error = xrep_adoption_move(&xnc->adoption);
		if (error)
			goto out_trans;

		/*
		 * Re-read the link counts since the reparenting will have
		 * updated our scan info.
		 */
		mutex_lock(&xnc->lock);
		error = xfarray_load_sparse(xnc->nlinks, ip->i_ino, &obs);
		mutex_unlock(&xnc->lock);
		if (error)
			goto out_trans;

		total_links = xchk_nlink_total(ip, &obs);
		actual_nlink = VFS_I(ip)->i_nlink;
		dirty = true;
	}

	/*
	 * If this inode is linked from the directory tree and on the unlinked
	 * list, remove it from the unlinked list.
@@ -165,14 +257,19 @@ xrep_nlinks_repair_inode(
	xfs_trans_log_inode(sc->tp, ip, XFS_ILOG_CORE);

	error = xrep_trans_commit(sc);
	xchk_iunlock(sc, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
	return error;
	goto out_unlock;

out_scanlock:
	mutex_unlock(&xnc->lock);
out_trans:
	xchk_trans_cancel(sc);
	xchk_iunlock(sc, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
out_unlock:
	xchk_iunlock(sc, XFS_ILOCK_EXCL);
	if (orphanage_available) {
		xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
		xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
	}
	xchk_iunlock(sc, XFS_IOLOCK_EXCL);
	return error;
}

@@ -205,10 +302,10 @@ xrep_nlinks(
	/*
	 * We need ftype for an accurate count of the number of child
	 * subdirectory links.  Child subdirectories with a back link (dotdot
	 * entry) but no forward link are unfixable, so we cannot repair the
	 * link count of the parent directory based on the back link count
	 * alone.  Filesystems without ftype support are rare (old V4) so we
	 * just skip out here.
	 * entry) but no forward link are moved to the orphanage, so we cannot
	 * repair the link count of the parent directory based on the back link
	 * count alone.  Filesystems without ftype support are rare (old V4) so
	 * we just skip out here.
	 */
	if (!xfs_has_ftype(sc->mp))
		return -EOPNOTSUPP;
+2 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ int xrep_setup_ag_refcountbt(struct xfs_scrub *sc);
int xrep_setup_xattr(struct xfs_scrub *sc);
int xrep_setup_directory(struct xfs_scrub *sc);
int xrep_setup_parent(struct xfs_scrub *sc);
int xrep_setup_nlinks(struct xfs_scrub *sc);

/* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
@@ -201,6 +202,7 @@ xrep_setup_nothing(
#define xrep_setup_xattr		xrep_setup_nothing
#define xrep_setup_directory		xrep_setup_nothing
#define xrep_setup_parent		xrep_setup_nothing
#define xrep_setup_nlinks		xrep_setup_nothing

#define xrep_setup_inode(sc, imap)	((void)0)

Loading