xref: /linux/fs/xfs/scrub/orphanage.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
11e58a8ccSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
21e58a8ccSDarrick J. Wong /*
31e58a8ccSDarrick J. Wong  * Copyright (c) 2021-2024 Oracle.  All Rights Reserved.
41e58a8ccSDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
51e58a8ccSDarrick J. Wong  */
61e58a8ccSDarrick J. Wong #include "xfs.h"
71e58a8ccSDarrick J. Wong #include "xfs_fs.h"
81e58a8ccSDarrick J. Wong #include "xfs_shared.h"
91e58a8ccSDarrick J. Wong #include "xfs_format.h"
101e58a8ccSDarrick J. Wong #include "xfs_trans_resv.h"
111e58a8ccSDarrick J. Wong #include "xfs_mount.h"
121e58a8ccSDarrick J. Wong #include "xfs_log_format.h"
131e58a8ccSDarrick J. Wong #include "xfs_trans.h"
141e58a8ccSDarrick J. Wong #include "xfs_inode.h"
151e58a8ccSDarrick J. Wong #include "xfs_ialloc.h"
161e58a8ccSDarrick J. Wong #include "xfs_quota.h"
171e58a8ccSDarrick J. Wong #include "xfs_trans_space.h"
181e58a8ccSDarrick J. Wong #include "xfs_dir2.h"
191e58a8ccSDarrick J. Wong #include "xfs_icache.h"
201e58a8ccSDarrick J. Wong #include "xfs_bmap.h"
211e58a8ccSDarrick J. Wong #include "xfs_bmap_btree.h"
227be3d20bSDarrick J. Wong #include "xfs_parent.h"
237be3d20bSDarrick J. Wong #include "xfs_attr_sf.h"
241e58a8ccSDarrick J. Wong #include "scrub/scrub.h"
251e58a8ccSDarrick J. Wong #include "scrub/common.h"
261e58a8ccSDarrick J. Wong #include "scrub/repair.h"
271e58a8ccSDarrick J. Wong #include "scrub/trace.h"
281e58a8ccSDarrick J. Wong #include "scrub/orphanage.h"
291e58a8ccSDarrick J. Wong #include "scrub/readdir.h"
301e58a8ccSDarrick J. Wong 
311e58a8ccSDarrick J. Wong #include <linux/namei.h>
321e58a8ccSDarrick J. Wong 
331e58a8ccSDarrick J. Wong /*
341e58a8ccSDarrick J. Wong  * The Orphanage
351e58a8ccSDarrick J. Wong  * =============
361e58a8ccSDarrick J. Wong  *
371e58a8ccSDarrick J. Wong  * If the directory tree is damaged, children of that directory become
381e58a8ccSDarrick J. Wong  * inaccessible via that file path.  If a child has no other parents, the file
391e58a8ccSDarrick J. Wong  * is said to be orphaned.  xfs_repair fixes this situation by creating a
401e58a8ccSDarrick J. Wong  * orphanage directory (specifically, /lost+found) and creating a directory
411e58a8ccSDarrick J. Wong  * entry pointing to the orphaned file.
421e58a8ccSDarrick J. Wong  *
431e58a8ccSDarrick J. Wong  * Online repair follows this tactic by creating a root-owned /lost+found
441e58a8ccSDarrick J. Wong  * directory if one does not exist.  If an orphan is found, it will move that
451e58a8ccSDarrick J. Wong  * files into orphanage.
461e58a8ccSDarrick J. Wong  */
471e58a8ccSDarrick J. Wong 
481e58a8ccSDarrick J. Wong /* Make the orphanage owned by root. */
491e58a8ccSDarrick J. Wong STATIC int
501e58a8ccSDarrick J. Wong xrep_chown_orphanage(
511e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc,
521e58a8ccSDarrick J. Wong 	struct xfs_inode	*dp)
531e58a8ccSDarrick J. Wong {
541e58a8ccSDarrick J. Wong 	struct xfs_trans	*tp;
551e58a8ccSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
561e58a8ccSDarrick J. Wong 	struct xfs_dquot	*udqp = NULL, *gdqp = NULL, *pdqp = NULL;
571e58a8ccSDarrick J. Wong 	struct xfs_dquot	*oldu = NULL, *oldg = NULL, *oldp = NULL;
581e58a8ccSDarrick J. Wong 	struct inode		*inode = VFS_I(dp);
591e58a8ccSDarrick J. Wong 	int			error;
601e58a8ccSDarrick J. Wong 
611e58a8ccSDarrick J. Wong 	error = xfs_qm_vop_dqalloc(dp, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,
621e58a8ccSDarrick J. Wong 			XFS_QMOPT_QUOTALL, &udqp, &gdqp, &pdqp);
631e58a8ccSDarrick J. Wong 	if (error)
641e58a8ccSDarrick J. Wong 		return error;
651e58a8ccSDarrick J. Wong 
661e58a8ccSDarrick J. Wong 	error = xfs_trans_alloc_ichange(dp, udqp, gdqp, pdqp, true, &tp);
671e58a8ccSDarrick J. Wong 	if (error)
681e58a8ccSDarrick J. Wong 		goto out_dqrele;
691e58a8ccSDarrick J. Wong 
701e58a8ccSDarrick J. Wong 	/*
711e58a8ccSDarrick J. Wong 	 * Always clear setuid/setgid/sticky on the orphanage since we don't
721e58a8ccSDarrick J. Wong 	 * normally want that functionality on this directory and xfs_repair
731e58a8ccSDarrick J. Wong 	 * doesn't create it this way either.  Leave the other access bits
741e58a8ccSDarrick J. Wong 	 * unchanged.
751e58a8ccSDarrick J. Wong 	 */
761e58a8ccSDarrick J. Wong 	inode->i_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
771e58a8ccSDarrick J. Wong 
781e58a8ccSDarrick J. Wong 	/*
791e58a8ccSDarrick J. Wong 	 * Change the ownerships and register quota modifications
801e58a8ccSDarrick J. Wong 	 * in the transaction.
811e58a8ccSDarrick J. Wong 	 */
821e58a8ccSDarrick J. Wong 	if (!uid_eq(inode->i_uid, GLOBAL_ROOT_UID)) {
831e58a8ccSDarrick J. Wong 		if (XFS_IS_UQUOTA_ON(mp))
841e58a8ccSDarrick J. Wong 			oldu = xfs_qm_vop_chown(tp, dp, &dp->i_udquot, udqp);
851e58a8ccSDarrick J. Wong 		inode->i_uid = GLOBAL_ROOT_UID;
861e58a8ccSDarrick J. Wong 	}
871e58a8ccSDarrick J. Wong 	if (!gid_eq(inode->i_gid, GLOBAL_ROOT_GID)) {
881e58a8ccSDarrick J. Wong 		if (XFS_IS_GQUOTA_ON(mp))
891e58a8ccSDarrick J. Wong 			oldg = xfs_qm_vop_chown(tp, dp, &dp->i_gdquot, gdqp);
901e58a8ccSDarrick J. Wong 		inode->i_gid = GLOBAL_ROOT_GID;
911e58a8ccSDarrick J. Wong 	}
921e58a8ccSDarrick J. Wong 	if (dp->i_projid != 0) {
931e58a8ccSDarrick J. Wong 		if (XFS_IS_PQUOTA_ON(mp))
941e58a8ccSDarrick J. Wong 			oldp = xfs_qm_vop_chown(tp, dp, &dp->i_pdquot, pdqp);
951e58a8ccSDarrick J. Wong 		dp->i_projid = 0;
961e58a8ccSDarrick J. Wong 	}
971e58a8ccSDarrick J. Wong 
981e58a8ccSDarrick J. Wong 	dp->i_diflags &= ~(XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT);
991e58a8ccSDarrick J. Wong 	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
1001e58a8ccSDarrick J. Wong 
1011e58a8ccSDarrick J. Wong 	XFS_STATS_INC(mp, xs_ig_attrchg);
1021e58a8ccSDarrick J. Wong 
1031e58a8ccSDarrick J. Wong 	if (xfs_has_wsync(mp))
1041e58a8ccSDarrick J. Wong 		xfs_trans_set_sync(tp);
1051e58a8ccSDarrick J. Wong 	error = xfs_trans_commit(tp);
1061e58a8ccSDarrick J. Wong 
1071e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(oldu);
1081e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(oldg);
1091e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(oldp);
1101e58a8ccSDarrick J. Wong 
1111e58a8ccSDarrick J. Wong out_dqrele:
1121e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(udqp);
1131e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(gdqp);
1141e58a8ccSDarrick J. Wong 	xfs_qm_dqrele(pdqp);
1151e58a8ccSDarrick J. Wong 	return error;
1161e58a8ccSDarrick J. Wong }
1171e58a8ccSDarrick J. Wong 
1181e58a8ccSDarrick J. Wong #define ORPHANAGE	"lost+found"
1191e58a8ccSDarrick J. Wong 
1201e58a8ccSDarrick J. Wong /* Create the orphanage directory, and set sc->orphanage to it. */
1211e58a8ccSDarrick J. Wong int
1221e58a8ccSDarrick J. Wong xrep_orphanage_create(
1231e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc)
1241e58a8ccSDarrick J. Wong {
1251e58a8ccSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
1261e58a8ccSDarrick J. Wong 	struct dentry		*root_dentry, *orphanage_dentry;
1271e58a8ccSDarrick J. Wong 	struct inode		*root_inode = VFS_I(sc->mp->m_rootip);
1281e58a8ccSDarrick J. Wong 	struct inode		*orphanage_inode;
1291e58a8ccSDarrick J. Wong 	int			error;
1301e58a8ccSDarrick J. Wong 
1311e58a8ccSDarrick J. Wong 	if (xfs_is_shutdown(mp))
1321e58a8ccSDarrick J. Wong 		return -EIO;
1331e58a8ccSDarrick J. Wong 	if (xfs_is_readonly(mp)) {
1341e58a8ccSDarrick J. Wong 		sc->orphanage = NULL;
1351e58a8ccSDarrick J. Wong 		return 0;
1361e58a8ccSDarrick J. Wong 	}
1371e58a8ccSDarrick J. Wong 
1381e58a8ccSDarrick J. Wong 	ASSERT(sc->tp == NULL);
1391e58a8ccSDarrick J. Wong 	ASSERT(sc->orphanage == NULL);
1401e58a8ccSDarrick J. Wong 
1411e58a8ccSDarrick J. Wong 	/* Find the dentry for the root directory... */
1421e58a8ccSDarrick J. Wong 	root_dentry = d_find_alias(root_inode);
1431e58a8ccSDarrick J. Wong 	if (!root_dentry) {
1441e58a8ccSDarrick J. Wong 		error = -EFSCORRUPTED;
1451e58a8ccSDarrick J. Wong 		goto out;
1461e58a8ccSDarrick J. Wong 	}
1471e58a8ccSDarrick J. Wong 
1481e58a8ccSDarrick J. Wong 	/* ...which is a directory, right? */
1491e58a8ccSDarrick J. Wong 	if (!d_is_dir(root_dentry)) {
1501e58a8ccSDarrick J. Wong 		error = -EFSCORRUPTED;
1511e58a8ccSDarrick J. Wong 		goto out_dput_root;
1521e58a8ccSDarrick J. Wong 	}
1531e58a8ccSDarrick J. Wong 
1541e58a8ccSDarrick J. Wong 	/* Try to find the orphanage directory. */
1551e58a8ccSDarrick J. Wong 	inode_lock_nested(root_inode, I_MUTEX_PARENT);
1561e58a8ccSDarrick J. Wong 	orphanage_dentry = lookup_one_len(ORPHANAGE, root_dentry,
1571e58a8ccSDarrick J. Wong 			strlen(ORPHANAGE));
1581e58a8ccSDarrick J. Wong 	if (IS_ERR(orphanage_dentry)) {
1591e58a8ccSDarrick J. Wong 		error = PTR_ERR(orphanage_dentry);
1601e58a8ccSDarrick J. Wong 		goto out_unlock_root;
1611e58a8ccSDarrick J. Wong 	}
1621e58a8ccSDarrick J. Wong 
1631e58a8ccSDarrick J. Wong 	/*
1641e58a8ccSDarrick J. Wong 	 * Nothing found?  Call mkdir to create the orphanage.  Create the
1651e58a8ccSDarrick J. Wong 	 * directory without other-user access because we're live and someone
1661e58a8ccSDarrick J. Wong 	 * could have been relying partly on minimal access to a parent
1671e58a8ccSDarrick J. Wong 	 * directory to control access to a file we put in here.
1681e58a8ccSDarrick J. Wong 	 */
1691e58a8ccSDarrick J. Wong 	if (d_really_is_negative(orphanage_dentry)) {
170*c54b3869SNeilBrown 		orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
171*c54b3869SNeilBrown 					     orphanage_dentry, 0750);
172*c54b3869SNeilBrown 		error = PTR_ERR(orphanage_dentry);
173*c54b3869SNeilBrown 		if (IS_ERR(orphanage_dentry))
174*c54b3869SNeilBrown 			goto out_unlock_root;
1751e58a8ccSDarrick J. Wong 	}
1761e58a8ccSDarrick J. Wong 
1771e58a8ccSDarrick J. Wong 	/* Not a directory? Bail out. */
1781e58a8ccSDarrick J. Wong 	if (!d_is_dir(orphanage_dentry)) {
1791e58a8ccSDarrick J. Wong 		error = -ENOTDIR;
1801e58a8ccSDarrick J. Wong 		goto out_dput_orphanage;
1811e58a8ccSDarrick J. Wong 	}
1821e58a8ccSDarrick J. Wong 
1831e58a8ccSDarrick J. Wong 	/*
1841e58a8ccSDarrick J. Wong 	 * Grab a reference to the orphanage.  This /should/ succeed since
1851e58a8ccSDarrick J. Wong 	 * we hold the root directory locked and therefore nobody can delete
1861e58a8ccSDarrick J. Wong 	 * the orphanage.
1871e58a8ccSDarrick J. Wong 	 */
1881e58a8ccSDarrick J. Wong 	orphanage_inode = igrab(d_inode(orphanage_dentry));
1891e58a8ccSDarrick J. Wong 	if (!orphanage_inode) {
1901e58a8ccSDarrick J. Wong 		error = -ENOENT;
1911e58a8ccSDarrick J. Wong 		goto out_dput_orphanage;
1921e58a8ccSDarrick J. Wong 	}
1931e58a8ccSDarrick J. Wong 
1941e58a8ccSDarrick J. Wong 	/* Make sure the orphanage is owned by root. */
1951e58a8ccSDarrick J. Wong 	error = xrep_chown_orphanage(sc, XFS_I(orphanage_inode));
1961e58a8ccSDarrick J. Wong 	if (error)
1971e58a8ccSDarrick J. Wong 		goto out_dput_orphanage;
1981e58a8ccSDarrick J. Wong 
1991e58a8ccSDarrick J. Wong 	/* Stash the reference for later and bail out. */
2001e58a8ccSDarrick J. Wong 	sc->orphanage = XFS_I(orphanage_inode);
2011e58a8ccSDarrick J. Wong 	sc->orphanage_ilock_flags = 0;
2021e58a8ccSDarrick J. Wong 
2031e58a8ccSDarrick J. Wong out_dput_orphanage:
2041e58a8ccSDarrick J. Wong 	dput(orphanage_dentry);
2051e58a8ccSDarrick J. Wong out_unlock_root:
2061e58a8ccSDarrick J. Wong 	inode_unlock(VFS_I(sc->mp->m_rootip));
2071e58a8ccSDarrick J. Wong out_dput_root:
2081e58a8ccSDarrick J. Wong 	dput(root_dentry);
2091e58a8ccSDarrick J. Wong out:
2101e58a8ccSDarrick J. Wong 	return error;
2111e58a8ccSDarrick J. Wong }
2121e58a8ccSDarrick J. Wong 
2131e58a8ccSDarrick J. Wong void
2141e58a8ccSDarrick J. Wong xrep_orphanage_ilock(
2151e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc,
2161e58a8ccSDarrick J. Wong 	unsigned int		ilock_flags)
2171e58a8ccSDarrick J. Wong {
2181e58a8ccSDarrick J. Wong 	sc->orphanage_ilock_flags |= ilock_flags;
2191e58a8ccSDarrick J. Wong 	xfs_ilock(sc->orphanage, ilock_flags);
2201e58a8ccSDarrick J. Wong }
2211e58a8ccSDarrick J. Wong 
2221e58a8ccSDarrick J. Wong bool
2231e58a8ccSDarrick J. Wong xrep_orphanage_ilock_nowait(
2241e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc,
2251e58a8ccSDarrick J. Wong 	unsigned int		ilock_flags)
2261e58a8ccSDarrick J. Wong {
2271e58a8ccSDarrick J. Wong 	if (xfs_ilock_nowait(sc->orphanage, ilock_flags)) {
2281e58a8ccSDarrick J. Wong 		sc->orphanage_ilock_flags |= ilock_flags;
2291e58a8ccSDarrick J. Wong 		return true;
2301e58a8ccSDarrick J. Wong 	}
2311e58a8ccSDarrick J. Wong 
2321e58a8ccSDarrick J. Wong 	return false;
2331e58a8ccSDarrick J. Wong }
2341e58a8ccSDarrick J. Wong 
2351e58a8ccSDarrick J. Wong void
2361e58a8ccSDarrick J. Wong xrep_orphanage_iunlock(
2371e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc,
2381e58a8ccSDarrick J. Wong 	unsigned int		ilock_flags)
2391e58a8ccSDarrick J. Wong {
2401e58a8ccSDarrick J. Wong 	xfs_iunlock(sc->orphanage, ilock_flags);
2411e58a8ccSDarrick J. Wong 	sc->orphanage_ilock_flags &= ~ilock_flags;
2421e58a8ccSDarrick J. Wong }
2431e58a8ccSDarrick J. Wong 
2441e58a8ccSDarrick J. Wong /* Grab the IOLOCK of the orphanage and sc->ip. */
2451e58a8ccSDarrick J. Wong int
2461e58a8ccSDarrick J. Wong xrep_orphanage_iolock_two(
2471e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc)
2481e58a8ccSDarrick J. Wong {
2491e58a8ccSDarrick J. Wong 	int			error = 0;
2501e58a8ccSDarrick J. Wong 
2511e58a8ccSDarrick J. Wong 	while (true) {
2521e58a8ccSDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
2531e58a8ccSDarrick J. Wong 			return error;
2541e58a8ccSDarrick J. Wong 
2551e58a8ccSDarrick J. Wong 		/*
2561e58a8ccSDarrick J. Wong 		 * Normal XFS takes the IOLOCK before grabbing a transaction.
2571e58a8ccSDarrick J. Wong 		 * Scrub holds a transaction, which means that we can't block
2581e58a8ccSDarrick J. Wong 		 * on either IOLOCK.
2591e58a8ccSDarrick J. Wong 		 */
2601e58a8ccSDarrick J. Wong 		if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {
2611e58a8ccSDarrick J. Wong 			if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
2621e58a8ccSDarrick J. Wong 				break;
2631e58a8ccSDarrick J. Wong 			xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
2641e58a8ccSDarrick J. Wong 		}
2651e58a8ccSDarrick J. Wong 		delay(1);
2661e58a8ccSDarrick J. Wong 	}
2671e58a8ccSDarrick J. Wong 
2681e58a8ccSDarrick J. Wong 	return 0;
2691e58a8ccSDarrick J. Wong }
2701e58a8ccSDarrick J. Wong 
2711e58a8ccSDarrick J. Wong /* Release the orphanage. */
2721e58a8ccSDarrick J. Wong void
2731e58a8ccSDarrick J. Wong xrep_orphanage_rele(
2741e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc)
2751e58a8ccSDarrick J. Wong {
2761e58a8ccSDarrick J. Wong 	if (!sc->orphanage)
2771e58a8ccSDarrick J. Wong 		return;
2781e58a8ccSDarrick J. Wong 
2791e58a8ccSDarrick J. Wong 	if (sc->orphanage_ilock_flags)
2801e58a8ccSDarrick J. Wong 		xfs_iunlock(sc->orphanage, sc->orphanage_ilock_flags);
2811e58a8ccSDarrick J. Wong 
2821e58a8ccSDarrick J. Wong 	xchk_irele(sc, sc->orphanage);
2831e58a8ccSDarrick J. Wong 	sc->orphanage = NULL;
2841e58a8ccSDarrick J. Wong }
2851e58a8ccSDarrick J. Wong 
2861e58a8ccSDarrick J. Wong /* Adoption moves a file into /lost+found */
2871e58a8ccSDarrick J. Wong 
2881e58a8ccSDarrick J. Wong /* Can the orphanage adopt @sc->ip? */
2891e58a8ccSDarrick J. Wong bool
2901e58a8ccSDarrick J. Wong xrep_orphanage_can_adopt(
2911e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc)
2921e58a8ccSDarrick J. Wong {
2931e58a8ccSDarrick J. Wong 	ASSERT(sc->ip != NULL);
2941e58a8ccSDarrick J. Wong 
2951e58a8ccSDarrick J. Wong 	if (!sc->orphanage)
2961e58a8ccSDarrick J. Wong 		return false;
2971e58a8ccSDarrick J. Wong 	if (sc->ip == sc->orphanage)
2981e58a8ccSDarrick J. Wong 		return false;
299679b098bSDarrick J. Wong 	if (xchk_inode_is_sb_rooted(sc->ip))
300679b098bSDarrick J. Wong 		return false;
301679b098bSDarrick J. Wong 	if (xfs_is_internal_inode(sc->ip))
3021e58a8ccSDarrick J. Wong 		return false;
3031e58a8ccSDarrick J. Wong 	return true;
3041e58a8ccSDarrick J. Wong }
3051e58a8ccSDarrick J. Wong 
3061e58a8ccSDarrick J. Wong /*
3071e58a8ccSDarrick J. Wong  * Create a new transaction to send a child to the orphanage.
3081e58a8ccSDarrick J. Wong  *
3091e58a8ccSDarrick J. Wong  * Allocate a new transaction with sufficient disk space to handle the
3101e58a8ccSDarrick J. Wong  * adoption, take ILOCK_EXCL of the orphanage and sc->ip, joins them to the
3111e58a8ccSDarrick J. Wong  * transaction, and reserve quota to reparent the latter.  Caller must hold the
3121e58a8ccSDarrick J. Wong  * IOLOCK of the orphanage and sc->ip.
3131e58a8ccSDarrick J. Wong  */
3141e58a8ccSDarrick J. Wong int
3151e58a8ccSDarrick J. Wong xrep_adoption_trans_alloc(
3161e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc,
3171e58a8ccSDarrick J. Wong 	struct xrep_adoption	*adopt)
3181e58a8ccSDarrick J. Wong {
3191e58a8ccSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
3201e58a8ccSDarrick J. Wong 	unsigned int		child_blkres = 0;
3211e58a8ccSDarrick J. Wong 	int			error;
3221e58a8ccSDarrick J. Wong 
3231e58a8ccSDarrick J. Wong 	ASSERT(sc->tp == NULL);
3241e58a8ccSDarrick J. Wong 	ASSERT(sc->ip != NULL);
3251e58a8ccSDarrick J. Wong 	ASSERT(sc->orphanage != NULL);
3261e58a8ccSDarrick J. Wong 	ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
3271e58a8ccSDarrick J. Wong 	ASSERT(sc->orphanage_ilock_flags & XFS_IOLOCK_EXCL);
3281e58a8ccSDarrick J. Wong 	ASSERT(!(sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)));
3291e58a8ccSDarrick J. Wong 	ASSERT(!(sc->orphanage_ilock_flags &
3301e58a8ccSDarrick J. Wong 				(XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)));
3311e58a8ccSDarrick J. Wong 
3321e58a8ccSDarrick J. Wong 	/* Compute the worst case space reservation that we need. */
3331e58a8ccSDarrick J. Wong 	adopt->sc = sc;
334f1097be2SAllison Henderson 	adopt->orphanage_blkres = xfs_link_space_res(mp, MAXNAMELEN);
3351e58a8ccSDarrick J. Wong 	if (S_ISDIR(VFS_I(sc->ip)->i_mode))
3365a8338c8SAllison Henderson 		child_blkres = xfs_rename_space_res(mp, 0, false,
3375a8338c8SAllison Henderson 						    xfs_name_dotdot.len, false);
3387be3d20bSDarrick J. Wong 	if (xfs_has_parent(mp))
3397be3d20bSDarrick J. Wong 		child_blkres += XFS_ADDAFORK_SPACE_RES(mp);
3401e58a8ccSDarrick J. Wong 	adopt->child_blkres = child_blkres;
3411e58a8ccSDarrick J. Wong 
3421e58a8ccSDarrick J. Wong 	/*
3431e58a8ccSDarrick J. Wong 	 * Allocate a transaction to link the child into the parent, along with
3441e58a8ccSDarrick J. Wong 	 * enough disk space to handle expansion of both the orphanage and the
3451e58a8ccSDarrick J. Wong 	 * dotdot entry of a child directory.
3461e58a8ccSDarrick J. Wong 	 */
3471e58a8ccSDarrick J. Wong 	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link,
3481e58a8ccSDarrick J. Wong 			adopt->orphanage_blkres + adopt->child_blkres, 0, 0,
3491e58a8ccSDarrick J. Wong 			&sc->tp);
3501e58a8ccSDarrick J. Wong 	if (error)
3511e58a8ccSDarrick J. Wong 		return error;
3521e58a8ccSDarrick J. Wong 
3531e58a8ccSDarrick J. Wong 	xfs_lock_two_inodes(sc->orphanage, XFS_ILOCK_EXCL,
3541e58a8ccSDarrick J. Wong 			    sc->ip, XFS_ILOCK_EXCL);
3551e58a8ccSDarrick J. Wong 	sc->ilock_flags |= XFS_ILOCK_EXCL;
3561e58a8ccSDarrick J. Wong 	sc->orphanage_ilock_flags |= XFS_ILOCK_EXCL;
3571e58a8ccSDarrick J. Wong 
3581e58a8ccSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->orphanage, 0);
3591e58a8ccSDarrick J. Wong 	xfs_trans_ijoin(sc->tp, sc->ip, 0);
3601e58a8ccSDarrick J. Wong 
3611e58a8ccSDarrick J. Wong 	/*
3621e58a8ccSDarrick J. Wong 	 * Reserve enough quota in the orphan directory to add the new name.
3631e58a8ccSDarrick J. Wong 	 * Normally the orphanage should have user/group/project ids of zero
3641e58a8ccSDarrick J. Wong 	 * and hence is not subject to quota enforcement, but we're allowed to
3651e58a8ccSDarrick J. Wong 	 * exceed quota to reattach disconnected parts of the directory tree.
3661e58a8ccSDarrick J. Wong 	 */
3671e58a8ccSDarrick J. Wong 	error = xfs_trans_reserve_quota_nblks(sc->tp, sc->orphanage,
3681e58a8ccSDarrick J. Wong 			adopt->orphanage_blkres, 0, true);
3691e58a8ccSDarrick J. Wong 	if (error)
3701e58a8ccSDarrick J. Wong 		goto out_cancel;
3711e58a8ccSDarrick J. Wong 
3721e58a8ccSDarrick J. Wong 	/*
3731e58a8ccSDarrick J. Wong 	 * Reserve enough quota in the child directory to change dotdot.
3741e58a8ccSDarrick J. Wong 	 * Here we're also allowed to exceed file quota to repair inconsistent
3751e58a8ccSDarrick J. Wong 	 * metadata.
3761e58a8ccSDarrick J. Wong 	 */
3771e58a8ccSDarrick J. Wong 	if (adopt->child_blkres) {
3781e58a8ccSDarrick J. Wong 		error = xfs_trans_reserve_quota_nblks(sc->tp, sc->ip,
3791e58a8ccSDarrick J. Wong 				adopt->child_blkres, 0, true);
3801e58a8ccSDarrick J. Wong 		if (error)
3811e58a8ccSDarrick J. Wong 			goto out_cancel;
3821e58a8ccSDarrick J. Wong 	}
3831e58a8ccSDarrick J. Wong 
3841e58a8ccSDarrick J. Wong 	return 0;
3851e58a8ccSDarrick J. Wong out_cancel:
3861e58a8ccSDarrick J. Wong 	xchk_trans_cancel(sc);
3871e58a8ccSDarrick J. Wong 	xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
388b44bfc06SDarrick J. Wong 	xchk_iunlock(sc, XFS_ILOCK_EXCL);
3891e58a8ccSDarrick J. Wong 	return error;
3901e58a8ccSDarrick J. Wong }
3911e58a8ccSDarrick J. Wong 
3921e58a8ccSDarrick J. Wong /*
3931e58a8ccSDarrick J. Wong  * Compute the xfs_name for the directory entry that we're adding to the
3941e58a8ccSDarrick J. Wong  * orphanage.  Caller must hold ILOCKs of sc->ip and the orphanage and must not
3951e58a8ccSDarrick J. Wong  * reuse namebuf until the adoption completes or is dissolved.
3961e58a8ccSDarrick J. Wong  */
3971e58a8ccSDarrick J. Wong int
3981e58a8ccSDarrick J. Wong xrep_adoption_compute_name(
3991e58a8ccSDarrick J. Wong 	struct xrep_adoption	*adopt,
4001e58a8ccSDarrick J. Wong 	struct xfs_name		*xname)
4011e58a8ccSDarrick J. Wong {
4021e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc = adopt->sc;
4031e58a8ccSDarrick J. Wong 	char			*namebuf = (void *)xname->name;
4041e58a8ccSDarrick J. Wong 	xfs_ino_t		ino;
4051e58a8ccSDarrick J. Wong 	unsigned int		incr = 0;
4061e58a8ccSDarrick J. Wong 	int			error = 0;
4071e58a8ccSDarrick J. Wong 
4081e58a8ccSDarrick J. Wong 	adopt->xname = xname;
4091e58a8ccSDarrick J. Wong 	xname->len = snprintf(namebuf, MAXNAMELEN, "%llu", sc->ip->i_ino);
4101e58a8ccSDarrick J. Wong 	xname->type = xfs_mode_to_ftype(VFS_I(sc->ip)->i_mode);
4111e58a8ccSDarrick J. Wong 
4121e58a8ccSDarrick J. Wong 	/* Make sure the filename is unique in the lost+found. */
4131e58a8ccSDarrick J. Wong 	error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino);
4141e58a8ccSDarrick J. Wong 	while (error == 0 && incr < 10000) {
4151e58a8ccSDarrick J. Wong 		xname->len = snprintf(namebuf, MAXNAMELEN, "%llu.%u",
4161e58a8ccSDarrick J. Wong 				sc->ip->i_ino, ++incr);
4171e58a8ccSDarrick J. Wong 		error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino);
4181e58a8ccSDarrick J. Wong 	}
4191e58a8ccSDarrick J. Wong 	if (error == 0) {
4201e58a8ccSDarrick J. Wong 		/* We already have 10,000 entries in the orphanage? */
4211e58a8ccSDarrick J. Wong 		return -EFSCORRUPTED;
4221e58a8ccSDarrick J. Wong 	}
4231e58a8ccSDarrick J. Wong 
4241e58a8ccSDarrick J. Wong 	if (error != -ENOENT)
4251e58a8ccSDarrick J. Wong 		return error;
4261e58a8ccSDarrick J. Wong 	return 0;
4271e58a8ccSDarrick J. Wong }
4281e58a8ccSDarrick J. Wong 
4291e58a8ccSDarrick J. Wong /*
43073597e3eSDarrick J. Wong  * Make sure the dcache does not have a positive dentry for the name we've
43173597e3eSDarrick J. Wong  * chosen.  The caller should have checked with the ondisk directory, so any
43273597e3eSDarrick J. Wong  * discrepancy is a sign that something is seriously wrong.
43373597e3eSDarrick J. Wong  */
43473597e3eSDarrick J. Wong static int
43573597e3eSDarrick J. Wong xrep_adoption_check_dcache(
43673597e3eSDarrick J. Wong 	struct xrep_adoption	*adopt)
43773597e3eSDarrick J. Wong {
43873597e3eSDarrick J. Wong 	struct qstr		qname = QSTR_INIT(adopt->xname->name,
43973597e3eSDarrick J. Wong 						  adopt->xname->len);
4405e1c7d0bSDarrick J. Wong 	struct xfs_scrub	*sc = adopt->sc;
44173597e3eSDarrick J. Wong 	struct dentry		*d_orphanage, *d_child;
44273597e3eSDarrick J. Wong 	int			error = 0;
44373597e3eSDarrick J. Wong 
4445e1c7d0bSDarrick J. Wong 	d_orphanage = d_find_alias(VFS_I(sc->orphanage));
44573597e3eSDarrick J. Wong 	if (!d_orphanage)
44673597e3eSDarrick J. Wong 		return 0;
44773597e3eSDarrick J. Wong 
44873597e3eSDarrick J. Wong 	d_child = d_hash_and_lookup(d_orphanage, &qname);
44973597e3eSDarrick J. Wong 	if (d_child) {
4505e1c7d0bSDarrick J. Wong 		trace_xrep_adoption_check_child(sc->mp, d_child);
45173597e3eSDarrick J. Wong 
45273597e3eSDarrick J. Wong 		if (d_is_positive(d_child)) {
45373597e3eSDarrick J. Wong 			ASSERT(d_is_negative(d_child));
45473597e3eSDarrick J. Wong 			error = -EFSCORRUPTED;
45573597e3eSDarrick J. Wong 		}
45673597e3eSDarrick J. Wong 
45773597e3eSDarrick J. Wong 		dput(d_child);
45873597e3eSDarrick J. Wong 	}
45973597e3eSDarrick J. Wong 
46073597e3eSDarrick J. Wong 	dput(d_orphanage);
46173597e3eSDarrick J. Wong 	return error;
46273597e3eSDarrick J. Wong }
46373597e3eSDarrick J. Wong 
46473597e3eSDarrick J. Wong /*
4655e1c7d0bSDarrick J. Wong  * Invalidate all dentries for the name that was added to the orphanage
4665e1c7d0bSDarrick J. Wong  * directory, and all dentries pointing to the child inode that was moved.
4675e1c7d0bSDarrick J. Wong  *
4685e1c7d0bSDarrick J. Wong  * There should not be any positive entries for the name, since we've
4695e1c7d0bSDarrick J. Wong  * maintained our lock on the orphanage directory.
47073597e3eSDarrick J. Wong  */
47173597e3eSDarrick J. Wong static void
47273597e3eSDarrick J. Wong xrep_adoption_zap_dcache(
47373597e3eSDarrick J. Wong 	struct xrep_adoption	*adopt)
47473597e3eSDarrick J. Wong {
47573597e3eSDarrick J. Wong 	struct qstr		qname = QSTR_INIT(adopt->xname->name,
47673597e3eSDarrick J. Wong 						  adopt->xname->len);
4775e1c7d0bSDarrick J. Wong 	struct xfs_scrub	*sc = adopt->sc;
47873597e3eSDarrick J. Wong 	struct dentry		*d_orphanage, *d_child;
47973597e3eSDarrick J. Wong 
4805e1c7d0bSDarrick J. Wong 	/* Invalidate all dentries for the adoption name */
4815e1c7d0bSDarrick J. Wong 	d_orphanage = d_find_alias(VFS_I(sc->orphanage));
48273597e3eSDarrick J. Wong 	if (!d_orphanage)
48373597e3eSDarrick J. Wong 		return;
48473597e3eSDarrick J. Wong 
48573597e3eSDarrick J. Wong 	d_child = d_hash_and_lookup(d_orphanage, &qname);
48673597e3eSDarrick J. Wong 	while (d_child != NULL) {
4875e1c7d0bSDarrick J. Wong 		trace_xrep_adoption_invalidate_child(sc->mp, d_child);
48873597e3eSDarrick J. Wong 
48973597e3eSDarrick J. Wong 		ASSERT(d_is_negative(d_child));
49073597e3eSDarrick J. Wong 		d_invalidate(d_child);
49173597e3eSDarrick J. Wong 		dput(d_child);
49273597e3eSDarrick J. Wong 		d_child = d_lookup(d_orphanage, &qname);
49373597e3eSDarrick J. Wong 	}
49473597e3eSDarrick J. Wong 
49573597e3eSDarrick J. Wong 	dput(d_orphanage);
4965e1c7d0bSDarrick J. Wong 
4975e1c7d0bSDarrick J. Wong 	/* Invalidate all the dentries pointing down to this file. */
4985e1c7d0bSDarrick J. Wong 	while ((d_child = d_find_alias(VFS_I(sc->ip))) != NULL) {
4995e1c7d0bSDarrick J. Wong 		trace_xrep_adoption_invalidate_child(sc->mp, d_child);
5005e1c7d0bSDarrick J. Wong 
5015e1c7d0bSDarrick J. Wong 		d_invalidate(d_child);
5025e1c7d0bSDarrick J. Wong 		dput(d_child);
5035e1c7d0bSDarrick J. Wong 	}
50473597e3eSDarrick J. Wong }
50573597e3eSDarrick J. Wong 
50673597e3eSDarrick J. Wong /*
5077be3d20bSDarrick J. Wong  * If we have to add an attr fork ahead of a parent pointer update, how much
5087be3d20bSDarrick J. Wong  * space should we ask for?
5097be3d20bSDarrick J. Wong  */
5107be3d20bSDarrick J. Wong static inline int
5117be3d20bSDarrick J. Wong xrep_adoption_attr_sizeof(
5127be3d20bSDarrick J. Wong 	const struct xrep_adoption	*adopt)
5137be3d20bSDarrick J. Wong {
5147be3d20bSDarrick J. Wong 	return sizeof(struct xfs_attr_sf_hdr) +
5157be3d20bSDarrick J. Wong 		xfs_attr_sf_entsize_byname(sizeof(struct xfs_parent_rec),
5167be3d20bSDarrick J. Wong 					   adopt->xname->len);
5177be3d20bSDarrick J. Wong }
5187be3d20bSDarrick J. Wong 
5197be3d20bSDarrick J. Wong /*
5201e58a8ccSDarrick J. Wong  * Move the current file to the orphanage under the computed name.
5211e58a8ccSDarrick J. Wong  *
5221e58a8ccSDarrick J. Wong  * Returns with a dirty transaction so that the caller can handle any other
5231e58a8ccSDarrick J. Wong  * work, such as fixing up unlinked lists or resetting link counts.
5241e58a8ccSDarrick J. Wong  */
5251e58a8ccSDarrick J. Wong int
5261e58a8ccSDarrick J. Wong xrep_adoption_move(
5271e58a8ccSDarrick J. Wong 	struct xrep_adoption	*adopt)
5281e58a8ccSDarrick J. Wong {
5291e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc = adopt->sc;
5301e58a8ccSDarrick J. Wong 	bool			isdir = S_ISDIR(VFS_I(sc->ip)->i_mode);
5311e58a8ccSDarrick J. Wong 	int			error;
5321e58a8ccSDarrick J. Wong 
5331e58a8ccSDarrick J. Wong 	trace_xrep_adoption_reparent(sc->orphanage, adopt->xname,
5341e58a8ccSDarrick J. Wong 			sc->ip->i_ino);
5351e58a8ccSDarrick J. Wong 
53673597e3eSDarrick J. Wong 	error = xrep_adoption_check_dcache(adopt);
53773597e3eSDarrick J. Wong 	if (error)
53873597e3eSDarrick J. Wong 		return error;
53973597e3eSDarrick J. Wong 
5407be3d20bSDarrick J. Wong 	/*
5417be3d20bSDarrick J. Wong 	 * If this filesystem has parent pointers, ensure that the file being
5427be3d20bSDarrick J. Wong 	 * moved to the orphanage has an attribute fork.  This is required
5437be3d20bSDarrick J. Wong 	 * because the parent pointer code does not itself add attr forks.
5447be3d20bSDarrick J. Wong 	 */
5457be3d20bSDarrick J. Wong 	if (!xfs_inode_has_attr_fork(sc->ip) && xfs_has_parent(sc->mp)) {
5467be3d20bSDarrick J. Wong 		int sf_size = xrep_adoption_attr_sizeof(adopt);
5477be3d20bSDarrick J. Wong 
5487be3d20bSDarrick J. Wong 		error = xfs_bmap_add_attrfork(sc->tp, sc->ip, sf_size, true);
5497be3d20bSDarrick J. Wong 		if (error)
5507be3d20bSDarrick J. Wong 			return error;
5517be3d20bSDarrick J. Wong 	}
5527be3d20bSDarrick J. Wong 
5531e58a8ccSDarrick J. Wong 	/* Create the new name in the orphanage. */
5541e58a8ccSDarrick J. Wong 	error = xfs_dir_createname(sc->tp, sc->orphanage, adopt->xname,
5551e58a8ccSDarrick J. Wong 			sc->ip->i_ino, adopt->orphanage_blkres);
5561e58a8ccSDarrick J. Wong 	if (error)
5571e58a8ccSDarrick J. Wong 		return error;
5581e58a8ccSDarrick J. Wong 
5591e58a8ccSDarrick J. Wong 	/*
5601e58a8ccSDarrick J. Wong 	 * Bump the link count of the orphanage if we just added a
5611e58a8ccSDarrick J. Wong 	 * subdirectory, and update its timestamps.
5621e58a8ccSDarrick J. Wong 	 */
5631e58a8ccSDarrick J. Wong 	xfs_trans_ichgtime(sc->tp, sc->orphanage,
5641e58a8ccSDarrick J. Wong 			XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
5651e58a8ccSDarrick J. Wong 	if (isdir)
5661e58a8ccSDarrick J. Wong 		xfs_bumplink(sc->tp, sc->orphanage);
5671e58a8ccSDarrick J. Wong 	xfs_trans_log_inode(sc->tp, sc->orphanage, XFS_ILOG_CORE);
5681e58a8ccSDarrick J. Wong 
5693f31406aSDarrick J. Wong 	/* Bump the link count of the child. */
5703f31406aSDarrick J. Wong 	if (adopt->bump_child_nlink) {
5713f31406aSDarrick J. Wong 		xfs_bumplink(sc->tp, sc->ip);
5723f31406aSDarrick J. Wong 		xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
5733f31406aSDarrick J. Wong 	}
5743f31406aSDarrick J. Wong 
5751e58a8ccSDarrick J. Wong 	/* Replace the dotdot entry if the child is a subdirectory. */
5761e58a8ccSDarrick J. Wong 	if (isdir) {
5771e58a8ccSDarrick J. Wong 		error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
5781e58a8ccSDarrick J. Wong 				sc->orphanage->i_ino, adopt->child_blkres);
5791e58a8ccSDarrick J. Wong 		if (error)
5801e58a8ccSDarrick J. Wong 			return error;
5811e58a8ccSDarrick J. Wong 	}
5821e58a8ccSDarrick J. Wong 
5837be3d20bSDarrick J. Wong 	/* Add a parent pointer from the file back to the lost+found. */
5847be3d20bSDarrick J. Wong 	if (xfs_has_parent(sc->mp)) {
5857be3d20bSDarrick J. Wong 		error = xfs_parent_addname(sc->tp, &adopt->ppargs,
5867be3d20bSDarrick J. Wong 				sc->orphanage, adopt->xname, sc->ip);
5877be3d20bSDarrick J. Wong 		if (error)
5887be3d20bSDarrick J. Wong 			return error;
5897be3d20bSDarrick J. Wong 	}
5907be3d20bSDarrick J. Wong 
5911e58a8ccSDarrick J. Wong 	/*
5921e58a8ccSDarrick J. Wong 	 * Notify dirent hooks that we moved the file to /lost+found, and
5931e58a8ccSDarrick J. Wong 	 * finish all the deferred work so that we know the adoption is fully
5941e58a8ccSDarrick J. Wong 	 * recorded in the log.
5951e58a8ccSDarrick J. Wong 	 */
5961e58a8ccSDarrick J. Wong 	xfs_dir_update_hook(sc->orphanage, sc->ip, 1, adopt->xname);
59773597e3eSDarrick J. Wong 
59873597e3eSDarrick J. Wong 	/* Remove negative dentries from the lost+found's dcache */
59973597e3eSDarrick J. Wong 	xrep_adoption_zap_dcache(adopt);
6001e58a8ccSDarrick J. Wong 	return 0;
6011e58a8ccSDarrick J. Wong }
6021e58a8ccSDarrick J. Wong 
6031e58a8ccSDarrick J. Wong /*
6041e58a8ccSDarrick J. Wong  * Roll to a clean scrub transaction so that we can release the orphanage,
6051e58a8ccSDarrick J. Wong  * even if xrep_adoption_move was not called.
6061e58a8ccSDarrick J. Wong  *
6071e58a8ccSDarrick J. Wong  * Commits all the work and deferred ops attached to an adoption request and
6081e58a8ccSDarrick J. Wong  * rolls to a clean scrub transaction.  On success, returns 0 with the scrub
6091e58a8ccSDarrick J. Wong  * context holding a clean transaction with no inodes joined.  On failure,
6101e58a8ccSDarrick J. Wong  * returns negative errno with no scrub transaction.  All inode locks are
6111e58a8ccSDarrick J. Wong  * still held after this function returns.
6121e58a8ccSDarrick J. Wong  */
6131e58a8ccSDarrick J. Wong int
6141e58a8ccSDarrick J. Wong xrep_adoption_trans_roll(
6151e58a8ccSDarrick J. Wong 	struct xrep_adoption	*adopt)
6161e58a8ccSDarrick J. Wong {
6171e58a8ccSDarrick J. Wong 	struct xfs_scrub	*sc = adopt->sc;
6181e58a8ccSDarrick J. Wong 	int			error;
6191e58a8ccSDarrick J. Wong 
6201e58a8ccSDarrick J. Wong 	trace_xrep_adoption_trans_roll(sc->orphanage, sc->ip,
6211e58a8ccSDarrick J. Wong 			!!(sc->tp->t_flags & XFS_TRANS_DIRTY));
6221e58a8ccSDarrick J. Wong 
6231e58a8ccSDarrick J. Wong 	/* Finish all the deferred ops to commit all repairs. */
6241e58a8ccSDarrick J. Wong 	error = xrep_defer_finish(sc);
6251e58a8ccSDarrick J. Wong 	if (error)
6261e58a8ccSDarrick J. Wong 		return error;
6271e58a8ccSDarrick J. Wong 
6281e58a8ccSDarrick J. Wong 	/* Roll the transaction once more to detach the inodes. */
6291e58a8ccSDarrick J. Wong 	return xfs_trans_roll(&sc->tp);
6301e58a8ccSDarrick J. Wong }
631