1b3c03efaSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 2b3c03efaSDarrick J. Wong /* 3b3c03efaSDarrick J. Wong * Copyright (c) 2023-2024 Oracle. All Rights Reserved. 4b3c03efaSDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 5b3c03efaSDarrick J. Wong */ 6b3c03efaSDarrick J. Wong #include "xfs.h" 7b3c03efaSDarrick J. Wong #include "xfs_fs.h" 8b3c03efaSDarrick J. Wong #include "xfs_shared.h" 9b3c03efaSDarrick J. Wong #include "xfs_format.h" 10b3c03efaSDarrick J. Wong #include "xfs_trans_resv.h" 11b3c03efaSDarrick J. Wong #include "xfs_mount.h" 12b3c03efaSDarrick J. Wong #include "xfs_log_format.h" 13b3c03efaSDarrick J. Wong #include "xfs_trans.h" 14b3c03efaSDarrick J. Wong #include "xfs_inode.h" 15b3c03efaSDarrick J. Wong #include "xfs_metafile.h" 16b3c03efaSDarrick J. Wong #include "xfs_quota.h" 17b3c03efaSDarrick J. Wong #include "xfs_qm.h" 18b3c03efaSDarrick J. Wong #include "xfs_dir2.h" 190d2c636eSDarrick J. Wong #include "xfs_parent.h" 200d2c636eSDarrick J. Wong #include "xfs_bmap_btree.h" 210d2c636eSDarrick J. Wong #include "xfs_trans_space.h" 220d2c636eSDarrick J. Wong #include "xfs_attr.h" 23a7492333SDarrick J. Wong #include "xfs_rtgroup.h" 24b3c03efaSDarrick J. Wong #include "scrub/scrub.h" 25b3c03efaSDarrick J. Wong #include "scrub/common.h" 26b3c03efaSDarrick J. Wong #include "scrub/trace.h" 27b3c03efaSDarrick J. Wong #include "scrub/readdir.h" 280d2c636eSDarrick J. Wong #include "scrub/repair.h" 29b3c03efaSDarrick J. Wong 30b3c03efaSDarrick J. Wong /* 31b3c03efaSDarrick J. Wong * Metadata Directory Tree Paths 32b3c03efaSDarrick J. Wong * ============================= 33b3c03efaSDarrick J. Wong * 34b3c03efaSDarrick J. Wong * A filesystem with metadir enabled expects to find metadata structures 35b3c03efaSDarrick J. Wong * attached to files that are accessible by walking a path down the metadata 36b3c03efaSDarrick J. Wong * directory tree. Given the metadir path and the incore inode storing the 37b3c03efaSDarrick J. Wong * metadata, this scrubber ensures that the ondisk metadir path points to the 38b3c03efaSDarrick J. Wong * ondisk inode represented by the incore inode. 39b3c03efaSDarrick J. Wong */ 40b3c03efaSDarrick J. Wong 41b3c03efaSDarrick J. Wong struct xchk_metapath { 42b3c03efaSDarrick J. Wong struct xfs_scrub *sc; 43b3c03efaSDarrick J. Wong 44b3c03efaSDarrick J. Wong /* Name for lookup */ 45b3c03efaSDarrick J. Wong struct xfs_name xname; 46b3c03efaSDarrick J. Wong 470d2c636eSDarrick J. Wong /* Directory update for repairs */ 480d2c636eSDarrick J. Wong struct xfs_dir_update du; 490d2c636eSDarrick J. Wong 500d2c636eSDarrick J. Wong /* Path down to this metadata file from the parent directory */ 51b3c03efaSDarrick J. Wong const char *path; 52b3c03efaSDarrick J. Wong 53b3c03efaSDarrick J. Wong /* Directory parent of the metadata file. */ 54b3c03efaSDarrick J. Wong struct xfs_inode *dp; 55b3c03efaSDarrick J. Wong 56b3c03efaSDarrick J. Wong /* Locks held on dp */ 57b3c03efaSDarrick J. Wong unsigned int dp_ilock_flags; 580d2c636eSDarrick J. Wong 590d2c636eSDarrick J. Wong /* Transaction block reservations */ 600d2c636eSDarrick J. Wong unsigned int link_resblks; 610d2c636eSDarrick J. Wong unsigned int unlink_resblks; 620d2c636eSDarrick J. Wong 630d2c636eSDarrick J. Wong /* Parent pointer updates */ 640d2c636eSDarrick J. Wong struct xfs_parent_args link_ppargs; 650d2c636eSDarrick J. Wong struct xfs_parent_args unlink_ppargs; 660d2c636eSDarrick J. Wong 670d2c636eSDarrick J. Wong /* Scratchpads for removing links */ 680d2c636eSDarrick J. Wong struct xfs_da_args pptr_args; 69b3c03efaSDarrick J. Wong }; 70b3c03efaSDarrick J. Wong 71b3c03efaSDarrick J. Wong /* Release resources tracked in the buffer. */ 72b3c03efaSDarrick J. Wong static inline void 73b3c03efaSDarrick J. Wong xchk_metapath_cleanup( 74b3c03efaSDarrick J. Wong void *buf) 75b3c03efaSDarrick J. Wong { 76b3c03efaSDarrick J. Wong struct xchk_metapath *mpath = buf; 77b3c03efaSDarrick J. Wong 78b3c03efaSDarrick J. Wong if (mpath->dp_ilock_flags) 79b3c03efaSDarrick J. Wong xfs_iunlock(mpath->dp, mpath->dp_ilock_flags); 80b3c03efaSDarrick J. Wong kfree(mpath->path); 81b3c03efaSDarrick J. Wong } 82b3c03efaSDarrick J. Wong 83a7492333SDarrick J. Wong /* Set up a metadir path scan. @path must be dynamically allocated. */ 84a7492333SDarrick J. Wong static inline int 85a7492333SDarrick J. Wong xchk_setup_metapath_scan( 86a7492333SDarrick J. Wong struct xfs_scrub *sc, 87a7492333SDarrick J. Wong struct xfs_inode *dp, 88a7492333SDarrick J. Wong const char *path, 89a7492333SDarrick J. Wong struct xfs_inode *ip) 90a7492333SDarrick J. Wong { 91a7492333SDarrick J. Wong struct xchk_metapath *mpath; 92a7492333SDarrick J. Wong int error; 93a7492333SDarrick J. Wong 94a7492333SDarrick J. Wong if (!path) 95a7492333SDarrick J. Wong return -ENOMEM; 96a7492333SDarrick J. Wong 97a7492333SDarrick J. Wong error = xchk_install_live_inode(sc, ip); 98a7492333SDarrick J. Wong if (error) { 99a7492333SDarrick J. Wong kfree(path); 100a7492333SDarrick J. Wong return error; 101a7492333SDarrick J. Wong } 102a7492333SDarrick J. Wong 103a7492333SDarrick J. Wong mpath = kzalloc(sizeof(struct xchk_metapath), XCHK_GFP_FLAGS); 104a7492333SDarrick J. Wong if (!mpath) { 105a7492333SDarrick J. Wong kfree(path); 106a7492333SDarrick J. Wong return -ENOMEM; 107a7492333SDarrick J. Wong } 108a7492333SDarrick J. Wong 109a7492333SDarrick J. Wong mpath->sc = sc; 110a7492333SDarrick J. Wong sc->buf = mpath; 111a7492333SDarrick J. Wong sc->buf_cleanup = xchk_metapath_cleanup; 112a7492333SDarrick J. Wong 113a7492333SDarrick J. Wong mpath->dp = dp; 114a7492333SDarrick J. Wong mpath->path = path; /* path is now owned by mpath */ 115a7492333SDarrick J. Wong 116a7492333SDarrick J. Wong mpath->xname.name = mpath->path; 117a7492333SDarrick J. Wong mpath->xname.len = strlen(mpath->path); 118a7492333SDarrick J. Wong mpath->xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode); 119a7492333SDarrick J. Wong 120a7492333SDarrick J. Wong return 0; 121a7492333SDarrick J. Wong } 122a7492333SDarrick J. Wong 123a7492333SDarrick J. Wong #ifdef CONFIG_XFS_RT 124a7492333SDarrick J. Wong /* Scan the /rtgroups directory itself. */ 125a7492333SDarrick J. Wong static int 126a7492333SDarrick J. Wong xchk_setup_metapath_rtdir( 127a7492333SDarrick J. Wong struct xfs_scrub *sc) 128a7492333SDarrick J. Wong { 129a7492333SDarrick J. Wong if (!sc->mp->m_rtdirip) 130a7492333SDarrick J. Wong return -ENOENT; 131a7492333SDarrick J. Wong 132a7492333SDarrick J. Wong return xchk_setup_metapath_scan(sc, sc->mp->m_metadirip, 133a7492333SDarrick J. Wong kasprintf(GFP_KERNEL, "rtgroups"), sc->mp->m_rtdirip); 134a7492333SDarrick J. Wong } 135a7492333SDarrick J. Wong 136a7492333SDarrick J. Wong /* Scan a rtgroup inode under the /rtgroups directory. */ 137a7492333SDarrick J. Wong static int 138a7492333SDarrick J. Wong xchk_setup_metapath_rtginode( 139a7492333SDarrick J. Wong struct xfs_scrub *sc, 140a7492333SDarrick J. Wong enum xfs_rtg_inodes type) 141a7492333SDarrick J. Wong { 142a7492333SDarrick J. Wong struct xfs_rtgroup *rtg; 143a7492333SDarrick J. Wong struct xfs_inode *ip; 144a7492333SDarrick J. Wong int error; 145a7492333SDarrick J. Wong 146a7492333SDarrick J. Wong rtg = xfs_rtgroup_get(sc->mp, sc->sm->sm_agno); 147a7492333SDarrick J. Wong if (!rtg) 148a7492333SDarrick J. Wong return -ENOENT; 149a7492333SDarrick J. Wong 150a7492333SDarrick J. Wong ip = rtg->rtg_inodes[type]; 151a7492333SDarrick J. Wong if (!ip) { 152a7492333SDarrick J. Wong error = -ENOENT; 153a7492333SDarrick J. Wong goto out_put_rtg; 154a7492333SDarrick J. Wong } 155a7492333SDarrick J. Wong 156a7492333SDarrick J. Wong error = xchk_setup_metapath_scan(sc, sc->mp->m_rtdirip, 157a7492333SDarrick J. Wong xfs_rtginode_path(rtg_rgno(rtg), type), ip); 158a7492333SDarrick J. Wong 159a7492333SDarrick J. Wong out_put_rtg: 160a7492333SDarrick J. Wong xfs_rtgroup_put(rtg); 161a7492333SDarrick J. Wong return error; 162a7492333SDarrick J. Wong } 163a7492333SDarrick J. Wong #else 164a7492333SDarrick J. Wong # define xchk_setup_metapath_rtdir(...) (-ENOENT) 165a7492333SDarrick J. Wong # define xchk_setup_metapath_rtginode(...) (-ENOENT) 166a7492333SDarrick J. Wong #endif /* CONFIG_XFS_RT */ 167a7492333SDarrick J. Wong 168*128a0552SDarrick J. Wong #ifdef CONFIG_XFS_QUOTA 169*128a0552SDarrick J. Wong /* Scan the /quota directory itself. */ 170*128a0552SDarrick J. Wong static int 171*128a0552SDarrick J. Wong xchk_setup_metapath_quotadir( 172*128a0552SDarrick J. Wong struct xfs_scrub *sc) 173*128a0552SDarrick J. Wong { 174*128a0552SDarrick J. Wong struct xfs_trans *tp; 175*128a0552SDarrick J. Wong struct xfs_inode *dp = NULL; 176*128a0552SDarrick J. Wong int error; 177*128a0552SDarrick J. Wong 178*128a0552SDarrick J. Wong error = xfs_trans_alloc_empty(sc->mp, &tp); 179*128a0552SDarrick J. Wong if (error) 180*128a0552SDarrick J. Wong return error; 181*128a0552SDarrick J. Wong 182*128a0552SDarrick J. Wong error = xfs_dqinode_load_parent(tp, &dp); 183*128a0552SDarrick J. Wong xfs_trans_cancel(tp); 184*128a0552SDarrick J. Wong if (error) 185*128a0552SDarrick J. Wong return error; 186*128a0552SDarrick J. Wong 187*128a0552SDarrick J. Wong error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip, 188*128a0552SDarrick J. Wong kasprintf(GFP_KERNEL, "quota"), dp); 189*128a0552SDarrick J. Wong xfs_irele(dp); 190*128a0552SDarrick J. Wong return error; 191*128a0552SDarrick J. Wong } 192*128a0552SDarrick J. Wong 193*128a0552SDarrick J. Wong /* Scan a quota inode under the /quota directory. */ 194*128a0552SDarrick J. Wong static int 195*128a0552SDarrick J. Wong xchk_setup_metapath_dqinode( 196*128a0552SDarrick J. Wong struct xfs_scrub *sc, 197*128a0552SDarrick J. Wong xfs_dqtype_t type) 198*128a0552SDarrick J. Wong { 199*128a0552SDarrick J. Wong struct xfs_trans *tp = NULL; 200*128a0552SDarrick J. Wong struct xfs_inode *dp = NULL; 201*128a0552SDarrick J. Wong struct xfs_inode *ip = NULL; 202*128a0552SDarrick J. Wong const char *path; 203*128a0552SDarrick J. Wong int error; 204*128a0552SDarrick J. Wong 205*128a0552SDarrick J. Wong error = xfs_trans_alloc_empty(sc->mp, &tp); 206*128a0552SDarrick J. Wong if (error) 207*128a0552SDarrick J. Wong return error; 208*128a0552SDarrick J. Wong 209*128a0552SDarrick J. Wong error = xfs_dqinode_load_parent(tp, &dp); 210*128a0552SDarrick J. Wong if (error) 211*128a0552SDarrick J. Wong goto out_cancel; 212*128a0552SDarrick J. Wong 213*128a0552SDarrick J. Wong error = xfs_dqinode_load(tp, dp, type, &ip); 214*128a0552SDarrick J. Wong if (error) 215*128a0552SDarrick J. Wong goto out_dp; 216*128a0552SDarrick J. Wong 217*128a0552SDarrick J. Wong xfs_trans_cancel(tp); 218*128a0552SDarrick J. Wong tp = NULL; 219*128a0552SDarrick J. Wong 220*128a0552SDarrick J. Wong path = kasprintf(GFP_KERNEL, "%s", xfs_dqinode_path(type)); 221*128a0552SDarrick J. Wong error = xchk_setup_metapath_scan(sc, dp, path, ip); 222*128a0552SDarrick J. Wong 223*128a0552SDarrick J. Wong xfs_irele(ip); 224*128a0552SDarrick J. Wong out_dp: 225*128a0552SDarrick J. Wong xfs_irele(dp); 226*128a0552SDarrick J. Wong out_cancel: 227*128a0552SDarrick J. Wong if (tp) 228*128a0552SDarrick J. Wong xfs_trans_cancel(tp); 229*128a0552SDarrick J. Wong return error; 230*128a0552SDarrick J. Wong } 231*128a0552SDarrick J. Wong #else 232*128a0552SDarrick J. Wong # define xchk_setup_metapath_quotadir(...) (-ENOENT) 233*128a0552SDarrick J. Wong # define xchk_setup_metapath_dqinode(...) (-ENOENT) 234*128a0552SDarrick J. Wong #endif /* CONFIG_XFS_QUOTA */ 235*128a0552SDarrick J. Wong 236b3c03efaSDarrick J. Wong int 237b3c03efaSDarrick J. Wong xchk_setup_metapath( 238b3c03efaSDarrick J. Wong struct xfs_scrub *sc) 239b3c03efaSDarrick J. Wong { 240b3c03efaSDarrick J. Wong if (!xfs_has_metadir(sc->mp)) 241b3c03efaSDarrick J. Wong return -ENOENT; 242b3c03efaSDarrick J. Wong if (sc->sm->sm_gen) 243b3c03efaSDarrick J. Wong return -EINVAL; 244b3c03efaSDarrick J. Wong 245b3c03efaSDarrick J. Wong switch (sc->sm->sm_ino) { 246b3c03efaSDarrick J. Wong case XFS_SCRUB_METAPATH_PROBE: 247b3c03efaSDarrick J. Wong /* Just probing, nothing else to do. */ 248b3c03efaSDarrick J. Wong if (sc->sm->sm_agno) 249b3c03efaSDarrick J. Wong return -EINVAL; 250b3c03efaSDarrick J. Wong return 0; 251a7492333SDarrick J. Wong case XFS_SCRUB_METAPATH_RTDIR: 252a7492333SDarrick J. Wong return xchk_setup_metapath_rtdir(sc); 253a7492333SDarrick J. Wong case XFS_SCRUB_METAPATH_RTBITMAP: 254a7492333SDarrick J. Wong return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP); 255a7492333SDarrick J. Wong case XFS_SCRUB_METAPATH_RTSUMMARY: 256a7492333SDarrick J. Wong return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY); 257*128a0552SDarrick J. Wong case XFS_SCRUB_METAPATH_QUOTADIR: 258*128a0552SDarrick J. Wong return xchk_setup_metapath_quotadir(sc); 259*128a0552SDarrick J. Wong case XFS_SCRUB_METAPATH_USRQUOTA: 260*128a0552SDarrick J. Wong return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER); 261*128a0552SDarrick J. Wong case XFS_SCRUB_METAPATH_GRPQUOTA: 262*128a0552SDarrick J. Wong return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP); 263*128a0552SDarrick J. Wong case XFS_SCRUB_METAPATH_PRJQUOTA: 264*128a0552SDarrick J. Wong return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ); 265b3c03efaSDarrick J. Wong default: 266b3c03efaSDarrick J. Wong return -ENOENT; 267b3c03efaSDarrick J. Wong } 268b3c03efaSDarrick J. Wong } 269b3c03efaSDarrick J. Wong 270b3c03efaSDarrick J. Wong /* 271b3c03efaSDarrick J. Wong * Take the ILOCK on the metadata directory parent and child. We do not know 272b3c03efaSDarrick J. Wong * that the metadata directory is not corrupt, so we lock the parent and try 273b3c03efaSDarrick J. Wong * to lock the child. Returns 0 if successful, or -EINTR to abort the scrub. 274b3c03efaSDarrick J. Wong */ 275b3c03efaSDarrick J. Wong STATIC int 276b3c03efaSDarrick J. Wong xchk_metapath_ilock_both( 277b3c03efaSDarrick J. Wong struct xchk_metapath *mpath) 278b3c03efaSDarrick J. Wong { 279b3c03efaSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 280b3c03efaSDarrick J. Wong int error = 0; 281b3c03efaSDarrick J. Wong 282b3c03efaSDarrick J. Wong while (true) { 283b3c03efaSDarrick J. Wong xfs_ilock(mpath->dp, XFS_ILOCK_EXCL); 284b3c03efaSDarrick J. Wong if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) { 285b3c03efaSDarrick J. Wong mpath->dp_ilock_flags |= XFS_ILOCK_EXCL; 286b3c03efaSDarrick J. Wong return 0; 287b3c03efaSDarrick J. Wong } 288b3c03efaSDarrick J. Wong xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 289b3c03efaSDarrick J. Wong 290b3c03efaSDarrick J. Wong if (xchk_should_terminate(sc, &error)) 291b3c03efaSDarrick J. Wong return error; 292b3c03efaSDarrick J. Wong 293b3c03efaSDarrick J. Wong delay(1); 294b3c03efaSDarrick J. Wong } 295b3c03efaSDarrick J. Wong 296b3c03efaSDarrick J. Wong ASSERT(0); 297b3c03efaSDarrick J. Wong return -EINTR; 298b3c03efaSDarrick J. Wong } 299b3c03efaSDarrick J. Wong 300b3c03efaSDarrick J. Wong /* Unlock parent and child inodes. */ 301b3c03efaSDarrick J. Wong static inline void 302b3c03efaSDarrick J. Wong xchk_metapath_iunlock( 303b3c03efaSDarrick J. Wong struct xchk_metapath *mpath) 304b3c03efaSDarrick J. Wong { 305b3c03efaSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 306b3c03efaSDarrick J. Wong 307b3c03efaSDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL); 308b3c03efaSDarrick J. Wong 309b3c03efaSDarrick J. Wong mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL; 310b3c03efaSDarrick J. Wong xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 311b3c03efaSDarrick J. Wong } 312b3c03efaSDarrick J. Wong 313b3c03efaSDarrick J. Wong int 314b3c03efaSDarrick J. Wong xchk_metapath( 315b3c03efaSDarrick J. Wong struct xfs_scrub *sc) 316b3c03efaSDarrick J. Wong { 317b3c03efaSDarrick J. Wong struct xchk_metapath *mpath = sc->buf; 318b3c03efaSDarrick J. Wong xfs_ino_t ino = NULLFSINO; 319b3c03efaSDarrick J. Wong int error; 320b3c03efaSDarrick J. Wong 321b3c03efaSDarrick J. Wong /* Just probing, nothing else to do. */ 322b3c03efaSDarrick J. Wong if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE) 323b3c03efaSDarrick J. Wong return 0; 324b3c03efaSDarrick J. Wong 325b3c03efaSDarrick J. Wong /* Parent required to do anything else. */ 326b3c03efaSDarrick J. Wong if (mpath->dp == NULL) { 327b3c03efaSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->ip->i_ino); 328b3c03efaSDarrick J. Wong return 0; 329b3c03efaSDarrick J. Wong } 330b3c03efaSDarrick J. Wong 331b3c03efaSDarrick J. Wong error = xchk_trans_alloc_empty(sc); 332b3c03efaSDarrick J. Wong if (error) 333b3c03efaSDarrick J. Wong return error; 334b3c03efaSDarrick J. Wong 335b3c03efaSDarrick J. Wong error = xchk_metapath_ilock_both(mpath); 336b3c03efaSDarrick J. Wong if (error) 337b3c03efaSDarrick J. Wong goto out_cancel; 338b3c03efaSDarrick J. Wong 339b3c03efaSDarrick J. Wong /* Make sure the parent dir has a dirent pointing to this file. */ 340b3c03efaSDarrick J. Wong error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 341b3c03efaSDarrick J. Wong trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino); 342b3c03efaSDarrick J. Wong if (error == -ENOENT) { 343b3c03efaSDarrick J. Wong /* No directory entry at all */ 344b3c03efaSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->ip->i_ino); 345b3c03efaSDarrick J. Wong error = 0; 346b3c03efaSDarrick J. Wong goto out_ilock; 347b3c03efaSDarrick J. Wong } 348b3c03efaSDarrick J. Wong if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 349b3c03efaSDarrick J. Wong goto out_ilock; 350b3c03efaSDarrick J. Wong if (ino != sc->ip->i_ino) { 351b3c03efaSDarrick J. Wong /* Pointing to wrong inode */ 352b3c03efaSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->ip->i_ino); 353b3c03efaSDarrick J. Wong } 354b3c03efaSDarrick J. Wong 355b3c03efaSDarrick J. Wong out_ilock: 356b3c03efaSDarrick J. Wong xchk_metapath_iunlock(mpath); 357b3c03efaSDarrick J. Wong out_cancel: 358b3c03efaSDarrick J. Wong xchk_trans_cancel(sc); 359b3c03efaSDarrick J. Wong return error; 360b3c03efaSDarrick J. Wong } 3610d2c636eSDarrick J. Wong 3620d2c636eSDarrick J. Wong #ifdef CONFIG_XFS_ONLINE_REPAIR 3630d2c636eSDarrick J. Wong /* Create the dirent represented by the final component of the path. */ 3640d2c636eSDarrick J. Wong STATIC int 3650d2c636eSDarrick J. Wong xrep_metapath_link( 3660d2c636eSDarrick J. Wong struct xchk_metapath *mpath) 3670d2c636eSDarrick J. Wong { 3680d2c636eSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 3690d2c636eSDarrick J. Wong 3700d2c636eSDarrick J. Wong mpath->du.dp = mpath->dp; 3710d2c636eSDarrick J. Wong mpath->du.name = &mpath->xname; 3720d2c636eSDarrick J. Wong mpath->du.ip = sc->ip; 3730d2c636eSDarrick J. Wong 3740d2c636eSDarrick J. Wong if (xfs_has_parent(sc->mp)) 3750d2c636eSDarrick J. Wong mpath->du.ppargs = &mpath->link_ppargs; 3760d2c636eSDarrick J. Wong else 3770d2c636eSDarrick J. Wong mpath->du.ppargs = NULL; 3780d2c636eSDarrick J. Wong 3790d2c636eSDarrick J. Wong trace_xrep_metapath_link(sc, mpath->path, mpath->dp, sc->ip->i_ino); 3800d2c636eSDarrick J. Wong 3810d2c636eSDarrick J. Wong return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du); 3820d2c636eSDarrick J. Wong } 3830d2c636eSDarrick J. Wong 3840d2c636eSDarrick J. Wong /* Remove the dirent at the final component of the path. */ 3850d2c636eSDarrick J. Wong STATIC int 3860d2c636eSDarrick J. Wong xrep_metapath_unlink( 3870d2c636eSDarrick J. Wong struct xchk_metapath *mpath, 3880d2c636eSDarrick J. Wong xfs_ino_t ino, 3890d2c636eSDarrick J. Wong struct xfs_inode *ip) 3900d2c636eSDarrick J. Wong { 3910d2c636eSDarrick J. Wong struct xfs_parent_rec rec; 3920d2c636eSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 3930d2c636eSDarrick J. Wong struct xfs_mount *mp = sc->mp; 3940d2c636eSDarrick J. Wong int error; 3950d2c636eSDarrick J. Wong 3960d2c636eSDarrick J. Wong trace_xrep_metapath_unlink(sc, mpath->path, mpath->dp, ino); 3970d2c636eSDarrick J. Wong 3980d2c636eSDarrick J. Wong if (!ip) { 3990d2c636eSDarrick J. Wong /* The child inode isn't allocated. Junk the dirent. */ 4000d2c636eSDarrick J. Wong xfs_trans_log_inode(sc->tp, mpath->dp, XFS_ILOG_CORE); 4010d2c636eSDarrick J. Wong return xfs_dir_removename(sc->tp, mpath->dp, &mpath->xname, 4020d2c636eSDarrick J. Wong ino, mpath->unlink_resblks); 4030d2c636eSDarrick J. Wong } 4040d2c636eSDarrick J. Wong 4050d2c636eSDarrick J. Wong mpath->du.dp = mpath->dp; 4060d2c636eSDarrick J. Wong mpath->du.name = &mpath->xname; 4070d2c636eSDarrick J. Wong mpath->du.ip = ip; 4080d2c636eSDarrick J. Wong mpath->du.ppargs = NULL; 4090d2c636eSDarrick J. Wong 4100d2c636eSDarrick J. Wong /* Figure out if we're removing a parent pointer too. */ 4110d2c636eSDarrick J. Wong if (xfs_has_parent(mp)) { 4120d2c636eSDarrick J. Wong xfs_inode_to_parent_rec(&rec, ip); 4130d2c636eSDarrick J. Wong error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec, 4140d2c636eSDarrick J. Wong &mpath->pptr_args); 4150d2c636eSDarrick J. Wong switch (error) { 4160d2c636eSDarrick J. Wong case -ENOATTR: 4170d2c636eSDarrick J. Wong break; 4180d2c636eSDarrick J. Wong case 0: 4190d2c636eSDarrick J. Wong mpath->du.ppargs = &mpath->unlink_ppargs; 4200d2c636eSDarrick J. Wong break; 4210d2c636eSDarrick J. Wong default: 4220d2c636eSDarrick J. Wong return error; 4230d2c636eSDarrick J. Wong } 4240d2c636eSDarrick J. Wong } 4250d2c636eSDarrick J. Wong 4260d2c636eSDarrick J. Wong return xfs_dir_remove_child(sc->tp, mpath->unlink_resblks, &mpath->du); 4270d2c636eSDarrick J. Wong } 4280d2c636eSDarrick J. Wong 4290d2c636eSDarrick J. Wong /* 4300d2c636eSDarrick J. Wong * Try to create a dirent in @mpath->dp with the name @mpath->xname that points 4310d2c636eSDarrick J. Wong * to @sc->ip. Returns: 4320d2c636eSDarrick J. Wong * 4330d2c636eSDarrick J. Wong * -EEXIST and an @alleged_child if the dirent that points to the wrong inode; 4340d2c636eSDarrick J. Wong * 0 if there is now a dirent pointing to @sc->ip; or 4350d2c636eSDarrick J. Wong * A negative errno on error. 4360d2c636eSDarrick J. Wong */ 4370d2c636eSDarrick J. Wong STATIC int 4380d2c636eSDarrick J. Wong xrep_metapath_try_link( 4390d2c636eSDarrick J. Wong struct xchk_metapath *mpath, 4400d2c636eSDarrick J. Wong xfs_ino_t *alleged_child) 4410d2c636eSDarrick J. Wong { 4420d2c636eSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 4430d2c636eSDarrick J. Wong xfs_ino_t ino; 4440d2c636eSDarrick J. Wong int error; 4450d2c636eSDarrick J. Wong 4460d2c636eSDarrick J. Wong /* Allocate transaction, lock inodes, join to transaction. */ 4470d2c636eSDarrick J. Wong error = xchk_trans_alloc(sc, mpath->link_resblks); 4480d2c636eSDarrick J. Wong if (error) 4490d2c636eSDarrick J. Wong return error; 4500d2c636eSDarrick J. Wong 4510d2c636eSDarrick J. Wong error = xchk_metapath_ilock_both(mpath); 4520d2c636eSDarrick J. Wong if (error) { 4530d2c636eSDarrick J. Wong xchk_trans_cancel(sc); 4540d2c636eSDarrick J. Wong return error; 4550d2c636eSDarrick J. Wong } 4560d2c636eSDarrick J. Wong xfs_trans_ijoin(sc->tp, mpath->dp, 0); 4570d2c636eSDarrick J. Wong xfs_trans_ijoin(sc->tp, sc->ip, 0); 4580d2c636eSDarrick J. Wong 4590d2c636eSDarrick J. Wong error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 4600d2c636eSDarrick J. Wong trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino); 4610d2c636eSDarrick J. Wong if (error == -ENOENT) { 4620d2c636eSDarrick J. Wong /* 4630d2c636eSDarrick J. Wong * There is no dirent in the directory. Create an entry 4640d2c636eSDarrick J. Wong * pointing to @sc->ip. 4650d2c636eSDarrick J. Wong */ 4660d2c636eSDarrick J. Wong error = xrep_metapath_link(mpath); 4670d2c636eSDarrick J. Wong if (error) 4680d2c636eSDarrick J. Wong goto out_cancel; 4690d2c636eSDarrick J. Wong 4700d2c636eSDarrick J. Wong error = xrep_trans_commit(sc); 4710d2c636eSDarrick J. Wong xchk_metapath_iunlock(mpath); 4720d2c636eSDarrick J. Wong return error; 4730d2c636eSDarrick J. Wong } 4740d2c636eSDarrick J. Wong if (error) 4750d2c636eSDarrick J. Wong goto out_cancel; 4760d2c636eSDarrick J. Wong 4770d2c636eSDarrick J. Wong if (ino == sc->ip->i_ino) { 4780d2c636eSDarrick J. Wong /* The dirent already points to @sc->ip; we're done. */ 4790d2c636eSDarrick J. Wong error = 0; 4800d2c636eSDarrick J. Wong goto out_cancel; 4810d2c636eSDarrick J. Wong } 4820d2c636eSDarrick J. Wong 4830d2c636eSDarrick J. Wong /* 4840d2c636eSDarrick J. Wong * The dirent points elsewhere; pass that back so that the caller 4850d2c636eSDarrick J. Wong * can try to remove the dirent. 4860d2c636eSDarrick J. Wong */ 4870d2c636eSDarrick J. Wong *alleged_child = ino; 4880d2c636eSDarrick J. Wong error = -EEXIST; 4890d2c636eSDarrick J. Wong 4900d2c636eSDarrick J. Wong out_cancel: 4910d2c636eSDarrick J. Wong xchk_trans_cancel(sc); 4920d2c636eSDarrick J. Wong xchk_metapath_iunlock(mpath); 4930d2c636eSDarrick J. Wong return error; 4940d2c636eSDarrick J. Wong } 4950d2c636eSDarrick J. Wong 4960d2c636eSDarrick J. Wong /* 4970d2c636eSDarrick J. Wong * Take the ILOCK on the metadata directory parent and a bad child, if one is 4980d2c636eSDarrick J. Wong * supplied. We do not know that the metadata directory is not corrupt, so we 4990d2c636eSDarrick J. Wong * lock the parent and try to lock the child. Returns 0 if successful, or 5000d2c636eSDarrick J. Wong * -EINTR to abort the repair. The lock state of @dp is not recorded in @mpath. 5010d2c636eSDarrick J. Wong */ 5020d2c636eSDarrick J. Wong STATIC int 5030d2c636eSDarrick J. Wong xchk_metapath_ilock_parent_and_child( 5040d2c636eSDarrick J. Wong struct xchk_metapath *mpath, 5050d2c636eSDarrick J. Wong struct xfs_inode *ip) 5060d2c636eSDarrick J. Wong { 5070d2c636eSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 5080d2c636eSDarrick J. Wong int error = 0; 5090d2c636eSDarrick J. Wong 5100d2c636eSDarrick J. Wong while (true) { 5110d2c636eSDarrick J. Wong xfs_ilock(mpath->dp, XFS_ILOCK_EXCL); 5120d2c636eSDarrick J. Wong if (!ip || xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) 5130d2c636eSDarrick J. Wong return 0; 5140d2c636eSDarrick J. Wong xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 5150d2c636eSDarrick J. Wong 5160d2c636eSDarrick J. Wong if (xchk_should_terminate(sc, &error)) 5170d2c636eSDarrick J. Wong return error; 5180d2c636eSDarrick J. Wong 5190d2c636eSDarrick J. Wong delay(1); 5200d2c636eSDarrick J. Wong } 5210d2c636eSDarrick J. Wong 5220d2c636eSDarrick J. Wong ASSERT(0); 5230d2c636eSDarrick J. Wong return -EINTR; 5240d2c636eSDarrick J. Wong } 5250d2c636eSDarrick J. Wong 5260d2c636eSDarrick J. Wong /* 5270d2c636eSDarrick J. Wong * Try to remove a dirent in @mpath->dp with the name @mpath->xname that points 5280d2c636eSDarrick J. Wong * to @alleged_child. Returns: 5290d2c636eSDarrick J. Wong * 5300d2c636eSDarrick J. Wong * 0 if there is no longer a dirent; 5310d2c636eSDarrick J. Wong * -EEXIST if the dirent points to @sc->ip; 5320d2c636eSDarrick J. Wong * -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or 5330d2c636eSDarrick J. Wong * A negative errno for any other error. 5340d2c636eSDarrick J. Wong */ 5350d2c636eSDarrick J. Wong STATIC int 5360d2c636eSDarrick J. Wong xrep_metapath_try_unlink( 5370d2c636eSDarrick J. Wong struct xchk_metapath *mpath, 5380d2c636eSDarrick J. Wong xfs_ino_t *alleged_child) 5390d2c636eSDarrick J. Wong { 5400d2c636eSDarrick J. Wong struct xfs_scrub *sc = mpath->sc; 5410d2c636eSDarrick J. Wong struct xfs_inode *ip = NULL; 5420d2c636eSDarrick J. Wong xfs_ino_t ino; 5430d2c636eSDarrick J. Wong int error; 5440d2c636eSDarrick J. Wong 5450d2c636eSDarrick J. Wong ASSERT(*alleged_child != sc->ip->i_ino); 5460d2c636eSDarrick J. Wong 5470d2c636eSDarrick J. Wong trace_xrep_metapath_try_unlink(sc, mpath->path, mpath->dp, 5480d2c636eSDarrick J. Wong *alleged_child); 5490d2c636eSDarrick J. Wong 5500d2c636eSDarrick J. Wong /* 5510d2c636eSDarrick J. Wong * Allocate transaction, grab the alleged child inode, lock inodes, 5520d2c636eSDarrick J. Wong * join to transaction. 5530d2c636eSDarrick J. Wong */ 5540d2c636eSDarrick J. Wong error = xchk_trans_alloc(sc, mpath->unlink_resblks); 5550d2c636eSDarrick J. Wong if (error) 5560d2c636eSDarrick J. Wong return error; 5570d2c636eSDarrick J. Wong 5580d2c636eSDarrick J. Wong error = xchk_iget(sc, *alleged_child, &ip); 5590d2c636eSDarrick J. Wong if (error == -EINVAL || error == -ENOENT) { 5600d2c636eSDarrick J. Wong /* inode number is bogus, junk the dirent */ 5610d2c636eSDarrick J. Wong error = 0; 5620d2c636eSDarrick J. Wong } 5630d2c636eSDarrick J. Wong if (error) { 5640d2c636eSDarrick J. Wong xchk_trans_cancel(sc); 5650d2c636eSDarrick J. Wong return error; 5660d2c636eSDarrick J. Wong } 5670d2c636eSDarrick J. Wong 5680d2c636eSDarrick J. Wong error = xchk_metapath_ilock_parent_and_child(mpath, ip); 5690d2c636eSDarrick J. Wong if (error) { 5700d2c636eSDarrick J. Wong xchk_trans_cancel(sc); 5710d2c636eSDarrick J. Wong return error; 5720d2c636eSDarrick J. Wong } 5730d2c636eSDarrick J. Wong xfs_trans_ijoin(sc->tp, mpath->dp, 0); 5740d2c636eSDarrick J. Wong if (ip) 5750d2c636eSDarrick J. Wong xfs_trans_ijoin(sc->tp, ip, 0); 5760d2c636eSDarrick J. Wong 5770d2c636eSDarrick J. Wong error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino); 5780d2c636eSDarrick J. Wong trace_xrep_metapath_lookup(sc, mpath->path, mpath->dp, ino); 5790d2c636eSDarrick J. Wong if (error == -ENOENT) { 5800d2c636eSDarrick J. Wong /* 5810d2c636eSDarrick J. Wong * There is no dirent in the directory anymore. We're ready to 5820d2c636eSDarrick J. Wong * try the link operation again. 5830d2c636eSDarrick J. Wong */ 5840d2c636eSDarrick J. Wong error = 0; 5850d2c636eSDarrick J. Wong goto out_cancel; 5860d2c636eSDarrick J. Wong } 5870d2c636eSDarrick J. Wong if (error) 5880d2c636eSDarrick J. Wong goto out_cancel; 5890d2c636eSDarrick J. Wong 5900d2c636eSDarrick J. Wong if (ino == sc->ip->i_ino) { 5910d2c636eSDarrick J. Wong /* The dirent already points to @sc->ip; we're done. */ 5920d2c636eSDarrick J. Wong error = -EEXIST; 5930d2c636eSDarrick J. Wong goto out_cancel; 5940d2c636eSDarrick J. Wong } 5950d2c636eSDarrick J. Wong 5960d2c636eSDarrick J. Wong /* 5970d2c636eSDarrick J. Wong * The dirent does not point to the alleged child. Update the caller 5980d2c636eSDarrick J. Wong * and signal that we want to be called again. 5990d2c636eSDarrick J. Wong */ 6000d2c636eSDarrick J. Wong if (ino != *alleged_child) { 6010d2c636eSDarrick J. Wong *alleged_child = ino; 6020d2c636eSDarrick J. Wong error = -EAGAIN; 6030d2c636eSDarrick J. Wong goto out_cancel; 6040d2c636eSDarrick J. Wong } 6050d2c636eSDarrick J. Wong 6060d2c636eSDarrick J. Wong /* Remove the link to the child. */ 6070d2c636eSDarrick J. Wong error = xrep_metapath_unlink(mpath, ino, ip); 6080d2c636eSDarrick J. Wong if (error) 6090d2c636eSDarrick J. Wong goto out_cancel; 6100d2c636eSDarrick J. Wong 6110d2c636eSDarrick J. Wong error = xrep_trans_commit(sc); 6120d2c636eSDarrick J. Wong goto out_unlock; 6130d2c636eSDarrick J. Wong 6140d2c636eSDarrick J. Wong out_cancel: 6150d2c636eSDarrick J. Wong xchk_trans_cancel(sc); 6160d2c636eSDarrick J. Wong out_unlock: 6170d2c636eSDarrick J. Wong xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL); 6180d2c636eSDarrick J. Wong if (ip) { 6190d2c636eSDarrick J. Wong xfs_iunlock(ip, XFS_ILOCK_EXCL); 6200d2c636eSDarrick J. Wong xchk_irele(sc, ip); 6210d2c636eSDarrick J. Wong } 6220d2c636eSDarrick J. Wong return error; 6230d2c636eSDarrick J. Wong } 6240d2c636eSDarrick J. Wong 6250d2c636eSDarrick J. Wong /* 6260d2c636eSDarrick J. Wong * Make sure the metadata directory path points to the child being examined. 6270d2c636eSDarrick J. Wong * 6280d2c636eSDarrick J. Wong * Repair needs to be able to create a directory structure, create its own 6290d2c636eSDarrick J. Wong * transactions, and take ILOCKs. This function /must/ be called after all 6300d2c636eSDarrick J. Wong * other repairs have completed. 6310d2c636eSDarrick J. Wong */ 6320d2c636eSDarrick J. Wong int 6330d2c636eSDarrick J. Wong xrep_metapath( 6340d2c636eSDarrick J. Wong struct xfs_scrub *sc) 6350d2c636eSDarrick J. Wong { 6360d2c636eSDarrick J. Wong struct xchk_metapath *mpath = sc->buf; 6370d2c636eSDarrick J. Wong struct xfs_mount *mp = sc->mp; 6380d2c636eSDarrick J. Wong int error = 0; 6390d2c636eSDarrick J. Wong 6400d2c636eSDarrick J. Wong /* Just probing, nothing to repair. */ 6410d2c636eSDarrick J. Wong if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE) 6420d2c636eSDarrick J. Wong return 0; 6430d2c636eSDarrick J. Wong 6440d2c636eSDarrick J. Wong /* Parent required to do anything else. */ 6450d2c636eSDarrick J. Wong if (mpath->dp == NULL) 6460d2c636eSDarrick J. Wong return -EFSCORRUPTED; 6470d2c636eSDarrick J. Wong 6480d2c636eSDarrick J. Wong /* 6490d2c636eSDarrick J. Wong * Make sure the child file actually has an attr fork to receive a new 6500d2c636eSDarrick J. Wong * parent pointer if the fs has parent pointers. 6510d2c636eSDarrick J. Wong */ 6520d2c636eSDarrick J. Wong if (xfs_has_parent(mp)) { 6530d2c636eSDarrick J. Wong error = xfs_attr_add_fork(sc->ip, 6540d2c636eSDarrick J. Wong sizeof(struct xfs_attr_sf_hdr), 1); 6550d2c636eSDarrick J. Wong if (error) 6560d2c636eSDarrick J. Wong return error; 6570d2c636eSDarrick J. Wong } 6580d2c636eSDarrick J. Wong 6590d2c636eSDarrick J. Wong /* Compute block reservation required to unlink and link a file. */ 6600d2c636eSDarrick J. Wong mpath->unlink_resblks = xfs_remove_space_res(mp, MAXNAMELEN); 6610d2c636eSDarrick J. Wong mpath->link_resblks = xfs_link_space_res(mp, MAXNAMELEN); 6620d2c636eSDarrick J. Wong 6630d2c636eSDarrick J. Wong do { 6640d2c636eSDarrick J. Wong xfs_ino_t alleged_child; 6650d2c636eSDarrick J. Wong 6660d2c636eSDarrick J. Wong /* Re-establish the link, or tell us which inode to remove. */ 6670d2c636eSDarrick J. Wong error = xrep_metapath_try_link(mpath, &alleged_child); 6680d2c636eSDarrick J. Wong if (!error) 6690d2c636eSDarrick J. Wong return 0; 6700d2c636eSDarrick J. Wong if (error != -EEXIST) 6710d2c636eSDarrick J. Wong return error; 6720d2c636eSDarrick J. Wong 6730d2c636eSDarrick J. Wong /* 6740d2c636eSDarrick J. Wong * Remove an incorrect link to an alleged child, or tell us 6750d2c636eSDarrick J. Wong * which inode to remove. 6760d2c636eSDarrick J. Wong */ 6770d2c636eSDarrick J. Wong do { 6780d2c636eSDarrick J. Wong error = xrep_metapath_try_unlink(mpath, &alleged_child); 6790d2c636eSDarrick J. Wong } while (error == -EAGAIN); 6800d2c636eSDarrick J. Wong if (error == -EEXIST) { 6810d2c636eSDarrick J. Wong /* Link established; we're done. */ 6820d2c636eSDarrick J. Wong error = 0; 6830d2c636eSDarrick J. Wong break; 6840d2c636eSDarrick J. Wong } 6850d2c636eSDarrick J. Wong } while (!error); 6860d2c636eSDarrick J. Wong 6870d2c636eSDarrick J. Wong return error; 6880d2c636eSDarrick J. Wong } 6890d2c636eSDarrick J. Wong #endif /* CONFIG_XFS_ONLINE_REPAIR */ 690