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