1928b721aSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 2928b721aSDarrick J. Wong /* 3928b721aSDarrick J. Wong * Copyright (c) 2023-2024 Oracle. All Rights Reserved. 4928b721aSDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 5928b721aSDarrick J. Wong */ 6928b721aSDarrick J. Wong #include "xfs.h" 7928b721aSDarrick J. Wong #include "xfs_fs.h" 8928b721aSDarrick J. Wong #include "xfs_shared.h" 9928b721aSDarrick J. Wong #include "xfs_format.h" 10928b721aSDarrick J. Wong #include "xfs_trans_resv.h" 11928b721aSDarrick J. Wong #include "xfs_mount.h" 12928b721aSDarrick J. Wong #include "xfs_log_format.h" 13928b721aSDarrick J. Wong #include "xfs_trans.h" 14928b721aSDarrick J. Wong #include "xfs_inode.h" 15928b721aSDarrick J. Wong #include "xfs_icache.h" 16928b721aSDarrick J. Wong #include "xfs_dir2.h" 17928b721aSDarrick J. Wong #include "xfs_dir2_priv.h" 18928b721aSDarrick J. Wong #include "xfs_attr.h" 19928b721aSDarrick J. Wong #include "xfs_parent.h" 20928b721aSDarrick J. Wong #include "scrub/scrub.h" 21928b721aSDarrick J. Wong #include "scrub/common.h" 22928b721aSDarrick J. Wong #include "scrub/bitmap.h" 23928b721aSDarrick J. Wong #include "scrub/ino_bitmap.h" 24928b721aSDarrick J. Wong #include "scrub/xfile.h" 25928b721aSDarrick J. Wong #include "scrub/xfarray.h" 26928b721aSDarrick J. Wong #include "scrub/xfblob.h" 27928b721aSDarrick J. Wong #include "scrub/listxattr.h" 28928b721aSDarrick J. Wong #include "scrub/trace.h" 293f31406aSDarrick J. Wong #include "scrub/repair.h" 303f31406aSDarrick J. Wong #include "scrub/orphanage.h" 31928b721aSDarrick J. Wong #include "scrub/dirtree.h" 32928b721aSDarrick J. Wong 33928b721aSDarrick J. Wong /* 34928b721aSDarrick J. Wong * Directory Tree Structure Validation 35928b721aSDarrick J. Wong * =================================== 36928b721aSDarrick J. Wong * 37928b721aSDarrick J. Wong * Validating the tree qualities of the directory tree structure can be 38928b721aSDarrick J. Wong * difficult. If the tree is frozen, running a depth (or breadth) first search 39928b721aSDarrick J. Wong * and marking a bitmap suffices to determine if there is a cycle. XORing the 40928b721aSDarrick J. Wong * mark bitmap with the inode bitmap afterwards tells us if there are 41928b721aSDarrick J. Wong * disconnected cycles. If the tree is not frozen, directory updates can move 42928b721aSDarrick J. Wong * subtrees across the scanner wavefront, which complicates the design greatly. 43928b721aSDarrick J. Wong * 44928b721aSDarrick J. Wong * Directory parent pointers change that by enabling an incremental approach to 45928b721aSDarrick J. Wong * validation of the tree structure. Instead of using one thread to scan the 46928b721aSDarrick J. Wong * entire filesystem, we instead can have multiple threads walking individual 47928b721aSDarrick J. Wong * subdirectories upwards to the root. In a perfect world, the IOLOCK would 48928b721aSDarrick J. Wong * suffice to stabilize two directories in a parent -> child relationship. 49928b721aSDarrick J. Wong * Unfortunately, the VFS does not take the IOLOCK when moving a child 50928b721aSDarrick J. Wong * subdirectory, so we instead synchronize on ILOCK and use dirent update hooks 51928b721aSDarrick J. Wong * to detect a race. If a race occurs in a path, we restart the scan. 52928b721aSDarrick J. Wong * 53928b721aSDarrick J. Wong * If the walk terminates without reaching the root, we know the path is 54928b721aSDarrick J. Wong * disconnected and ought to be attached to the lost and found. If on the walk 55928b721aSDarrick J. Wong * we find the same subdir that we're scanning, we know this is a cycle and 56928b721aSDarrick J. Wong * should delete an incoming edge. If we find multiple paths to the root, we 57928b721aSDarrick J. Wong * know to delete an incoming edge. 58928b721aSDarrick J. Wong * 59928b721aSDarrick J. Wong * There are two big hitches with this approach: first, all file link counts 60928b721aSDarrick J. Wong * must be correct to prevent other writers from doing the wrong thing with the 61928b721aSDarrick J. Wong * directory tree structure. Second, because we're walking upwards in a tree 62928b721aSDarrick J. Wong * of arbitrary depth, we cannot hold all the ILOCKs. Instead, we will use a 63928b721aSDarrick J. Wong * directory update hook to invalidate the scan results if one of the paths 64928b721aSDarrick J. Wong * we've scanned has changed. 65928b721aSDarrick J. Wong */ 66928b721aSDarrick J. Wong 67928b721aSDarrick J. Wong /* Clean up the dirtree checking resources. */ 68928b721aSDarrick J. Wong STATIC void 69928b721aSDarrick J. Wong xchk_dirtree_buf_cleanup( 70928b721aSDarrick J. Wong void *buf) 71928b721aSDarrick J. Wong { 72928b721aSDarrick J. Wong struct xchk_dirtree *dl = buf; 73928b721aSDarrick J. Wong struct xchk_dirpath *path, *n; 74928b721aSDarrick J. Wong 75d54c5ac8SDarrick J. Wong if (dl->scan_ino != NULLFSINO) 76d54c5ac8SDarrick J. Wong xfs_dir_hook_del(dl->sc->mp, &dl->dhook); 77d54c5ac8SDarrick J. Wong 78928b721aSDarrick J. Wong xchk_dirtree_for_each_path_safe(dl, path, n) { 79928b721aSDarrick J. Wong list_del_init(&path->list); 80928b721aSDarrick J. Wong xino_bitmap_destroy(&path->seen_inodes); 81928b721aSDarrick J. Wong kfree(path); 82928b721aSDarrick J. Wong } 83928b721aSDarrick J. Wong 84928b721aSDarrick J. Wong xfblob_destroy(dl->path_names); 85928b721aSDarrick J. Wong xfarray_destroy(dl->path_steps); 86928b721aSDarrick J. Wong mutex_destroy(&dl->lock); 87928b721aSDarrick J. Wong } 88928b721aSDarrick J. Wong 89928b721aSDarrick J. Wong /* Set us up to look for directory loops. */ 90928b721aSDarrick J. Wong int 91928b721aSDarrick J. Wong xchk_setup_dirtree( 92928b721aSDarrick J. Wong struct xfs_scrub *sc) 93928b721aSDarrick J. Wong { 94928b721aSDarrick J. Wong struct xchk_dirtree *dl; 95928b721aSDarrick J. Wong char *descr; 96928b721aSDarrick J. Wong int error; 97928b721aSDarrick J. Wong 98d54c5ac8SDarrick J. Wong xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS); 99d54c5ac8SDarrick J. Wong 1003f31406aSDarrick J. Wong if (xchk_could_repair(sc)) { 1013f31406aSDarrick J. Wong error = xrep_setup_dirtree(sc); 1023f31406aSDarrick J. Wong if (error) 1033f31406aSDarrick J. Wong return error; 1043f31406aSDarrick J. Wong } 1053f31406aSDarrick J. Wong 106928b721aSDarrick J. Wong dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS); 107928b721aSDarrick J. Wong if (!dl) 108928b721aSDarrick J. Wong return -ENOMEM; 109928b721aSDarrick J. Wong dl->sc = sc; 110928b721aSDarrick J. Wong dl->xname.name = dl->namebuf; 111d54c5ac8SDarrick J. Wong dl->hook_xname.name = dl->hook_namebuf; 112928b721aSDarrick J. Wong INIT_LIST_HEAD(&dl->path_list); 113928b721aSDarrick J. Wong dl->root_ino = NULLFSINO; 114d54c5ac8SDarrick J. Wong dl->scan_ino = NULLFSINO; 1153f31406aSDarrick J. Wong dl->parent_ino = NULLFSINO; 116928b721aSDarrick J. Wong 117928b721aSDarrick J. Wong mutex_init(&dl->lock); 118928b721aSDarrick J. Wong 119928b721aSDarrick J. Wong descr = xchk_xfile_ino_descr(sc, "dirtree path steps"); 120928b721aSDarrick J. Wong error = xfarray_create(descr, 0, sizeof(struct xchk_dirpath_step), 121928b721aSDarrick J. Wong &dl->path_steps); 122928b721aSDarrick J. Wong kfree(descr); 123928b721aSDarrick J. Wong if (error) 124928b721aSDarrick J. Wong goto out_dl; 125928b721aSDarrick J. Wong 126928b721aSDarrick J. Wong descr = xchk_xfile_ino_descr(sc, "dirtree path names"); 127928b721aSDarrick J. Wong error = xfblob_create(descr, &dl->path_names); 128928b721aSDarrick J. Wong kfree(descr); 129928b721aSDarrick J. Wong if (error) 130928b721aSDarrick J. Wong goto out_steps; 131928b721aSDarrick J. Wong 132928b721aSDarrick J. Wong error = xchk_setup_inode_contents(sc, 0); 133928b721aSDarrick J. Wong if (error) 134928b721aSDarrick J. Wong goto out_names; 135928b721aSDarrick J. Wong 136928b721aSDarrick J. Wong sc->buf = dl; 137928b721aSDarrick J. Wong sc->buf_cleanup = xchk_dirtree_buf_cleanup; 138928b721aSDarrick J. Wong return 0; 139928b721aSDarrick J. Wong 140928b721aSDarrick J. Wong out_names: 141928b721aSDarrick J. Wong xfblob_destroy(dl->path_names); 142928b721aSDarrick J. Wong out_steps: 143928b721aSDarrick J. Wong xfarray_destroy(dl->path_steps); 144928b721aSDarrick J. Wong out_dl: 145928b721aSDarrick J. Wong mutex_destroy(&dl->lock); 146928b721aSDarrick J. Wong kvfree(dl); 147928b721aSDarrick J. Wong return error; 148928b721aSDarrick J. Wong } 149928b721aSDarrick J. Wong 150928b721aSDarrick J. Wong /* 151928b721aSDarrick J. Wong * Add the parent pointer described by @dl->pptr to the given path as a new 152928b721aSDarrick J. Wong * step. Returns -ELNRNG if the path is too deep. 153928b721aSDarrick J. Wong */ 1543f31406aSDarrick J. Wong int 155928b721aSDarrick J. Wong xchk_dirpath_append( 156928b721aSDarrick J. Wong struct xchk_dirtree *dl, 157928b721aSDarrick J. Wong struct xfs_inode *ip, 158928b721aSDarrick J. Wong struct xchk_dirpath *path, 159928b721aSDarrick J. Wong const struct xfs_name *name, 160928b721aSDarrick J. Wong const struct xfs_parent_rec *pptr) 161928b721aSDarrick J. Wong { 162928b721aSDarrick J. Wong struct xchk_dirpath_step step = { 163928b721aSDarrick J. Wong .pptr_rec = *pptr, /* struct copy */ 164928b721aSDarrick J. Wong .name_len = name->len, 165928b721aSDarrick J. Wong }; 166928b721aSDarrick J. Wong int error; 167928b721aSDarrick J. Wong 168928b721aSDarrick J. Wong /* 169928b721aSDarrick J. Wong * If this path is more than 2 billion steps long, this directory tree 170928b721aSDarrick J. Wong * is too far gone to fix. 171928b721aSDarrick J. Wong */ 172928b721aSDarrick J. Wong if (path->nr_steps >= XFS_MAXLINK) 173928b721aSDarrick J. Wong return -ELNRNG; 174928b721aSDarrick J. Wong 175928b721aSDarrick J. Wong error = xfblob_storename(dl->path_names, &step.name_cookie, name); 176928b721aSDarrick J. Wong if (error) 177928b721aSDarrick J. Wong return error; 178928b721aSDarrick J. Wong 179928b721aSDarrick J. Wong error = xino_bitmap_set(&path->seen_inodes, ip->i_ino); 180928b721aSDarrick J. Wong if (error) 181928b721aSDarrick J. Wong return error; 182928b721aSDarrick J. Wong 183928b721aSDarrick J. Wong error = xfarray_append(dl->path_steps, &step); 184928b721aSDarrick J. Wong if (error) 185928b721aSDarrick J. Wong return error; 186928b721aSDarrick J. Wong 187928b721aSDarrick J. Wong path->nr_steps++; 188928b721aSDarrick J. Wong return 0; 189928b721aSDarrick J. Wong } 190928b721aSDarrick J. Wong 191928b721aSDarrick J. Wong /* 192928b721aSDarrick J. Wong * Create an xchk_path for each parent pointer of the directory that we're 193928b721aSDarrick J. Wong * scanning. For each path created, we will eventually try to walk towards the 194928b721aSDarrick J. Wong * root with the goal of deleting all parents except for one that leads to the 195928b721aSDarrick J. Wong * root. 196928b721aSDarrick J. Wong * 197928b721aSDarrick J. Wong * Returns -EFSCORRUPTED to signal that the inode being scanned has a corrupt 198928b721aSDarrick J. Wong * parent pointer and hence there's no point in continuing; or -ENOSR if there 199928b721aSDarrick J. Wong * are too many parent pointers for this directory. 200928b721aSDarrick J. Wong */ 201928b721aSDarrick J. Wong STATIC int 202928b721aSDarrick J. Wong xchk_dirtree_create_path( 203928b721aSDarrick J. Wong struct xfs_scrub *sc, 204928b721aSDarrick J. Wong struct xfs_inode *ip, 205928b721aSDarrick J. Wong unsigned int attr_flags, 206928b721aSDarrick J. Wong const unsigned char *name, 207928b721aSDarrick J. Wong unsigned int namelen, 208928b721aSDarrick J. Wong const void *value, 209928b721aSDarrick J. Wong unsigned int valuelen, 210928b721aSDarrick J. Wong void *priv) 211928b721aSDarrick J. Wong { 212928b721aSDarrick J. Wong struct xfs_name xname = { 213928b721aSDarrick J. Wong .name = name, 214928b721aSDarrick J. Wong .len = namelen, 215928b721aSDarrick J. Wong }; 216928b721aSDarrick J. Wong struct xchk_dirtree *dl = priv; 217928b721aSDarrick J. Wong struct xchk_dirpath *path; 218928b721aSDarrick J. Wong const struct xfs_parent_rec *rec = value; 219928b721aSDarrick J. Wong int error; 220928b721aSDarrick J. Wong 221928b721aSDarrick J. Wong if (!(attr_flags & XFS_ATTR_PARENT)) 222928b721aSDarrick J. Wong return 0; 223928b721aSDarrick J. Wong 224928b721aSDarrick J. Wong error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value, 225928b721aSDarrick J. Wong valuelen, NULL, NULL); 226928b721aSDarrick J. Wong if (error) 227928b721aSDarrick J. Wong return error; 228928b721aSDarrick J. Wong 229928b721aSDarrick J. Wong /* 230928b721aSDarrick J. Wong * If there are more than 2 billion actual parent pointers for this 231928b721aSDarrick J. Wong * subdirectory, this fs is too far gone to fix. 232928b721aSDarrick J. Wong */ 233928b721aSDarrick J. Wong if (dl->nr_paths >= XFS_MAXLINK) 234928b721aSDarrick J. Wong return -ENOSR; 235928b721aSDarrick J. Wong 236928b721aSDarrick J. Wong trace_xchk_dirtree_create_path(sc, ip, dl->nr_paths, &xname, rec); 237928b721aSDarrick J. Wong 238928b721aSDarrick J. Wong /* 239928b721aSDarrick J. Wong * Create a new xchk_path structure to remember this parent pointer 240928b721aSDarrick J. Wong * and record the first name step. 241928b721aSDarrick J. Wong */ 242928b721aSDarrick J. Wong path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS); 243928b721aSDarrick J. Wong if (!path) 244928b721aSDarrick J. Wong return -ENOMEM; 245928b721aSDarrick J. Wong 246928b721aSDarrick J. Wong INIT_LIST_HEAD(&path->list); 247928b721aSDarrick J. Wong xino_bitmap_init(&path->seen_inodes); 248928b721aSDarrick J. Wong path->nr_steps = 0; 249928b721aSDarrick J. Wong path->outcome = XCHK_DIRPATH_SCANNING; 250928b721aSDarrick J. Wong 251928b721aSDarrick J. Wong error = xchk_dirpath_append(dl, sc->ip, path, &xname, rec); 252928b721aSDarrick J. Wong if (error) 253928b721aSDarrick J. Wong goto out_path; 254928b721aSDarrick J. Wong 255928b721aSDarrick J. Wong path->first_step = xfarray_length(dl->path_steps) - 1; 256928b721aSDarrick J. Wong path->second_step = XFARRAY_NULLIDX; 257928b721aSDarrick J. Wong path->path_nr = dl->nr_paths; 258928b721aSDarrick J. Wong 259928b721aSDarrick J. Wong list_add_tail(&path->list, &dl->path_list); 260928b721aSDarrick J. Wong dl->nr_paths++; 261928b721aSDarrick J. Wong return 0; 262928b721aSDarrick J. Wong out_path: 263928b721aSDarrick J. Wong kfree(path); 264928b721aSDarrick J. Wong return error; 265928b721aSDarrick J. Wong } 266928b721aSDarrick J. Wong 267928b721aSDarrick J. Wong /* 268928b721aSDarrick J. Wong * Validate that the first step of this path still has a corresponding 269928b721aSDarrick J. Wong * parent pointer in @sc->ip. We probably dropped @sc->ip's ILOCK while 270928b721aSDarrick J. Wong * walking towards the roots, which is why this is necessary. 271928b721aSDarrick J. Wong * 272928b721aSDarrick J. Wong * This function has a side effect of loading the first parent pointer of this 273928b721aSDarrick J. Wong * path into the parent pointer scratch pad. This prepares us to walk up the 274928b721aSDarrick J. Wong * directory tree towards the root. Returns -ESTALE if the scan data is now 275928b721aSDarrick J. Wong * out of date. 276928b721aSDarrick J. Wong */ 277928b721aSDarrick J. Wong STATIC int 278928b721aSDarrick J. Wong xchk_dirpath_revalidate( 279928b721aSDarrick J. Wong struct xchk_dirtree *dl, 280928b721aSDarrick J. Wong struct xchk_dirpath *path) 281928b721aSDarrick J. Wong { 282928b721aSDarrick J. Wong struct xfs_scrub *sc = dl->sc; 283928b721aSDarrick J. Wong int error; 284928b721aSDarrick J. Wong 285928b721aSDarrick J. Wong /* 286928b721aSDarrick J. Wong * Look up the parent pointer that corresponds to the start of this 287928b721aSDarrick J. Wong * path. If the parent pointer has disappeared on us, dump all the 288928b721aSDarrick J. Wong * scan results and try again. 289928b721aSDarrick J. Wong */ 290928b721aSDarrick J. Wong error = xfs_parent_lookup(sc->tp, sc->ip, &dl->xname, &dl->pptr_rec, 291928b721aSDarrick J. Wong &dl->pptr_args); 292928b721aSDarrick J. Wong if (error == -ENOATTR) { 293928b721aSDarrick J. Wong trace_xchk_dirpath_disappeared(dl->sc, sc->ip, path->path_nr, 294928b721aSDarrick J. Wong path->first_step, &dl->xname, &dl->pptr_rec); 295928b721aSDarrick J. Wong dl->stale = true; 296928b721aSDarrick J. Wong return -ESTALE; 297928b721aSDarrick J. Wong } 298928b721aSDarrick J. Wong 299928b721aSDarrick J. Wong return error; 300928b721aSDarrick J. Wong } 301928b721aSDarrick J. Wong 302928b721aSDarrick J. Wong /* 303928b721aSDarrick J. Wong * Walk the parent pointers of a directory at the end of a path and record 304928b721aSDarrick J. Wong * the parent that we find in @dl->xname/pptr_rec. 305928b721aSDarrick J. Wong */ 306928b721aSDarrick J. Wong STATIC int 307928b721aSDarrick J. Wong xchk_dirpath_find_next_step( 308928b721aSDarrick J. Wong struct xfs_scrub *sc, 309928b721aSDarrick J. Wong struct xfs_inode *ip, 310928b721aSDarrick J. Wong unsigned int attr_flags, 311928b721aSDarrick J. Wong const unsigned char *name, 312928b721aSDarrick J. Wong unsigned int namelen, 313928b721aSDarrick J. Wong const void *value, 314928b721aSDarrick J. Wong unsigned int valuelen, 315928b721aSDarrick J. Wong void *priv) 316928b721aSDarrick J. Wong { 317928b721aSDarrick J. Wong struct xchk_dirtree *dl = priv; 318928b721aSDarrick J. Wong const struct xfs_parent_rec *rec = value; 319928b721aSDarrick J. Wong int error; 320928b721aSDarrick J. Wong 321928b721aSDarrick J. Wong if (!(attr_flags & XFS_ATTR_PARENT)) 322928b721aSDarrick J. Wong return 0; 323928b721aSDarrick J. Wong 324928b721aSDarrick J. Wong error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value, 325928b721aSDarrick J. Wong valuelen, NULL, NULL); 326928b721aSDarrick J. Wong if (error) 327928b721aSDarrick J. Wong return error; 328928b721aSDarrick J. Wong 329928b721aSDarrick J. Wong /* 330928b721aSDarrick J. Wong * If we've already set @dl->pptr_rec, then this directory has multiple 331928b721aSDarrick J. Wong * parents. Signal this back to the caller via -EMLINK. 332928b721aSDarrick J. Wong */ 333928b721aSDarrick J. Wong if (dl->parents_found > 0) 334928b721aSDarrick J. Wong return -EMLINK; 335928b721aSDarrick J. Wong 336928b721aSDarrick J. Wong dl->parents_found++; 337928b721aSDarrick J. Wong memcpy(dl->namebuf, name, namelen); 338928b721aSDarrick J. Wong dl->xname.len = namelen; 339928b721aSDarrick J. Wong dl->pptr_rec = *rec; /* struct copy */ 340928b721aSDarrick J. Wong return 0; 341928b721aSDarrick J. Wong } 342928b721aSDarrick J. Wong 343928b721aSDarrick J. Wong /* Set and log the outcome of a path walk. */ 344928b721aSDarrick J. Wong static inline void 345928b721aSDarrick J. Wong xchk_dirpath_set_outcome( 346928b721aSDarrick J. Wong struct xchk_dirtree *dl, 347928b721aSDarrick J. Wong struct xchk_dirpath *path, 348928b721aSDarrick J. Wong enum xchk_dirpath_outcome outcome) 349928b721aSDarrick J. Wong { 350928b721aSDarrick J. Wong trace_xchk_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps, 351928b721aSDarrick J. Wong outcome); 352928b721aSDarrick J. Wong 353928b721aSDarrick J. Wong path->outcome = outcome; 354928b721aSDarrick J. Wong } 355928b721aSDarrick J. Wong 356928b721aSDarrick J. Wong /* 357928b721aSDarrick J. Wong * Scan the directory at the end of this path for its parent directory link. 358928b721aSDarrick J. Wong * If we find one, extend the path. Returns -ESTALE if the scan data out of 359928b721aSDarrick J. Wong * date. Returns -EFSCORRUPTED if the parent pointer is bad; or -ELNRNG if 360928b721aSDarrick J. Wong * the path got too deep. 361928b721aSDarrick J. Wong */ 362928b721aSDarrick J. Wong STATIC int 363928b721aSDarrick J. Wong xchk_dirpath_step_up( 364928b721aSDarrick J. Wong struct xchk_dirtree *dl, 365*3d2c3411SDarrick J. Wong struct xchk_dirpath *path, 366*3d2c3411SDarrick J. Wong bool is_metadir) 367928b721aSDarrick J. Wong { 368928b721aSDarrick J. Wong struct xfs_scrub *sc = dl->sc; 369928b721aSDarrick J. Wong struct xfs_inode *dp; 370928b721aSDarrick J. Wong xfs_ino_t parent_ino = be64_to_cpu(dl->pptr_rec.p_ino); 371928b721aSDarrick J. Wong unsigned int lock_mode; 372928b721aSDarrick J. Wong int error; 373928b721aSDarrick J. Wong 374928b721aSDarrick J. Wong /* Grab and lock the parent directory. */ 375928b721aSDarrick J. Wong error = xchk_iget(sc, parent_ino, &dp); 376928b721aSDarrick J. Wong if (error) 377928b721aSDarrick J. Wong return error; 378928b721aSDarrick J. Wong 379928b721aSDarrick J. Wong lock_mode = xfs_ilock_attr_map_shared(dp); 380928b721aSDarrick J. Wong mutex_lock(&dl->lock); 381928b721aSDarrick J. Wong 382928b721aSDarrick J. Wong if (dl->stale) { 383928b721aSDarrick J. Wong error = -ESTALE; 384928b721aSDarrick J. Wong goto out_scanlock; 385928b721aSDarrick J. Wong } 386928b721aSDarrick J. Wong 387928b721aSDarrick J. Wong /* We've reached the root directory; the path is ok. */ 388928b721aSDarrick J. Wong if (parent_ino == dl->root_ino) { 389928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK); 390928b721aSDarrick J. Wong error = 0; 391928b721aSDarrick J. Wong goto out_scanlock; 392928b721aSDarrick J. Wong } 393928b721aSDarrick J. Wong 394928b721aSDarrick J. Wong /* 395928b721aSDarrick J. Wong * The inode being scanned is its own distant ancestor! Get rid of 396928b721aSDarrick J. Wong * this path. 397928b721aSDarrick J. Wong */ 398928b721aSDarrick J. Wong if (parent_ino == sc->ip->i_ino) { 399928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE); 400928b721aSDarrick J. Wong error = 0; 401928b721aSDarrick J. Wong goto out_scanlock; 402928b721aSDarrick J. Wong } 403928b721aSDarrick J. Wong 404928b721aSDarrick J. Wong /* 405928b721aSDarrick J. Wong * We've seen this inode before during the path walk. There's a loop 406928b721aSDarrick J. Wong * above us in the directory tree. This probably means that we cannot 407928b721aSDarrick J. Wong * continue, but let's keep walking paths to get a full picture. 408928b721aSDarrick J. Wong */ 409928b721aSDarrick J. Wong if (xino_bitmap_test(&path->seen_inodes, parent_ino)) { 410928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_LOOP); 411928b721aSDarrick J. Wong error = 0; 412928b721aSDarrick J. Wong goto out_scanlock; 413928b721aSDarrick J. Wong } 414928b721aSDarrick J. Wong 415928b721aSDarrick J. Wong /* The handle encoded in the parent pointer must match. */ 416928b721aSDarrick J. Wong if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { 417928b721aSDarrick J. Wong trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, 418928b721aSDarrick J. Wong path->nr_steps, &dl->xname, &dl->pptr_rec); 419928b721aSDarrick J. Wong error = -EFSCORRUPTED; 420928b721aSDarrick J. Wong goto out_scanlock; 421928b721aSDarrick J. Wong } 422928b721aSDarrick J. Wong 423928b721aSDarrick J. Wong /* Parent pointer must point up to a directory. */ 424928b721aSDarrick J. Wong if (!S_ISDIR(VFS_I(dp)->i_mode)) { 425928b721aSDarrick J. Wong trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr, 426928b721aSDarrick J. Wong path->nr_steps, &dl->xname, &dl->pptr_rec); 427928b721aSDarrick J. Wong error = -EFSCORRUPTED; 428928b721aSDarrick J. Wong goto out_scanlock; 429928b721aSDarrick J. Wong } 430928b721aSDarrick J. Wong 431928b721aSDarrick J. Wong /* Parent cannot be an unlinked directory. */ 432928b721aSDarrick J. Wong if (VFS_I(dp)->i_nlink == 0) { 433928b721aSDarrick J. Wong trace_xchk_dirpath_unlinked_parent(dl->sc, dp, path->path_nr, 434928b721aSDarrick J. Wong path->nr_steps, &dl->xname, &dl->pptr_rec); 435928b721aSDarrick J. Wong error = -EFSCORRUPTED; 436928b721aSDarrick J. Wong goto out_scanlock; 437928b721aSDarrick J. Wong } 438928b721aSDarrick J. Wong 439*3d2c3411SDarrick J. Wong /* Parent must be in the same directory tree. */ 440*3d2c3411SDarrick J. Wong if (is_metadir != xfs_is_metadir_inode(dp)) { 441*3d2c3411SDarrick J. Wong trace_xchk_dirpath_crosses_tree(dl->sc, dp, path->path_nr, 442*3d2c3411SDarrick J. Wong path->nr_steps, &dl->xname, &dl->pptr_rec); 443*3d2c3411SDarrick J. Wong error = -EFSCORRUPTED; 444*3d2c3411SDarrick J. Wong goto out_scanlock; 445*3d2c3411SDarrick J. Wong } 446*3d2c3411SDarrick J. Wong 447928b721aSDarrick J. Wong /* 448928b721aSDarrick J. Wong * If the extended attributes look as though they has been zapped by 449928b721aSDarrick J. Wong * the inode record repair code, we cannot scan for parent pointers. 450928b721aSDarrick J. Wong */ 451928b721aSDarrick J. Wong if (xchk_pptr_looks_zapped(dp)) { 452928b721aSDarrick J. Wong error = -EBUSY; 453928b721aSDarrick J. Wong xchk_set_incomplete(sc); 454928b721aSDarrick J. Wong goto out_scanlock; 455928b721aSDarrick J. Wong } 456928b721aSDarrick J. Wong 457928b721aSDarrick J. Wong /* 458928b721aSDarrick J. Wong * Walk the parent pointers of @dp to find the parent of this directory 459928b721aSDarrick J. Wong * to find the next step in our walk. If we find that @dp has exactly 460928b721aSDarrick J. Wong * one parent, the parent pointer information will be stored in 461928b721aSDarrick J. Wong * @dl->pptr_rec. This prepares us for the next step of the walk. 462928b721aSDarrick J. Wong */ 463928b721aSDarrick J. Wong mutex_unlock(&dl->lock); 464928b721aSDarrick J. Wong dl->parents_found = 0; 465928b721aSDarrick J. Wong error = xchk_xattr_walk(sc, dp, xchk_dirpath_find_next_step, NULL, dl); 466928b721aSDarrick J. Wong mutex_lock(&dl->lock); 467928b721aSDarrick J. Wong if (error == -EFSCORRUPTED || error == -EMLINK || 468928b721aSDarrick J. Wong (!error && dl->parents_found == 0)) { 469928b721aSDarrick J. Wong /* 470928b721aSDarrick J. Wong * Further up the directory tree from @sc->ip, we found a 471928b721aSDarrick J. Wong * corrupt parent pointer, multiple parent pointers while 472928b721aSDarrick J. Wong * finding this directory's parent, or zero parents despite 473928b721aSDarrick J. Wong * having a nonzero link count. Keep looking for other paths. 474928b721aSDarrick J. Wong */ 475928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT); 476928b721aSDarrick J. Wong error = 0; 477928b721aSDarrick J. Wong goto out_scanlock; 478928b721aSDarrick J. Wong } 479928b721aSDarrick J. Wong if (error) 480928b721aSDarrick J. Wong goto out_scanlock; 481928b721aSDarrick J. Wong 482928b721aSDarrick J. Wong if (dl->stale) { 483928b721aSDarrick J. Wong error = -ESTALE; 484928b721aSDarrick J. Wong goto out_scanlock; 485928b721aSDarrick J. Wong } 486928b721aSDarrick J. Wong 487928b721aSDarrick J. Wong trace_xchk_dirpath_found_next_step(sc, dp, path->path_nr, 488928b721aSDarrick J. Wong path->nr_steps, &dl->xname, &dl->pptr_rec); 489928b721aSDarrick J. Wong 490928b721aSDarrick J. Wong /* Append to the path steps */ 491928b721aSDarrick J. Wong error = xchk_dirpath_append(dl, dp, path, &dl->xname, &dl->pptr_rec); 492928b721aSDarrick J. Wong if (error) 493928b721aSDarrick J. Wong goto out_scanlock; 494928b721aSDarrick J. Wong 495928b721aSDarrick J. Wong if (path->second_step == XFARRAY_NULLIDX) 496928b721aSDarrick J. Wong path->second_step = xfarray_length(dl->path_steps) - 1; 497928b721aSDarrick J. Wong 498928b721aSDarrick J. Wong out_scanlock: 499928b721aSDarrick J. Wong mutex_unlock(&dl->lock); 500928b721aSDarrick J. Wong xfs_iunlock(dp, lock_mode); 501928b721aSDarrick J. Wong xchk_irele(sc, dp); 502928b721aSDarrick J. Wong return error; 503928b721aSDarrick J. Wong } 504928b721aSDarrick J. Wong 505928b721aSDarrick J. Wong /* 506928b721aSDarrick J. Wong * Walk the directory tree upwards towards what is hopefully the root 507928b721aSDarrick J. Wong * directory, recording path steps as we go. The current path components are 508928b721aSDarrick J. Wong * stored in dl->pptr_rec and dl->xname. 509928b721aSDarrick J. Wong * 510928b721aSDarrick J. Wong * Returns -ESTALE if the scan data are out of date. Returns -EFSCORRUPTED 511928b721aSDarrick J. Wong * only if the direct parent pointer of @sc->ip associated with this path is 512928b721aSDarrick J. Wong * corrupt. 513928b721aSDarrick J. Wong */ 514928b721aSDarrick J. Wong STATIC int 515928b721aSDarrick J. Wong xchk_dirpath_walk_upwards( 516928b721aSDarrick J. Wong struct xchk_dirtree *dl, 517928b721aSDarrick J. Wong struct xchk_dirpath *path) 518928b721aSDarrick J. Wong { 519928b721aSDarrick J. Wong struct xfs_scrub *sc = dl->sc; 520*3d2c3411SDarrick J. Wong bool is_metadir; 521928b721aSDarrick J. Wong int error; 522928b721aSDarrick J. Wong 523928b721aSDarrick J. Wong ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL); 524928b721aSDarrick J. Wong 525928b721aSDarrick J. Wong /* Reload the start of this path and make sure it's still there. */ 526928b721aSDarrick J. Wong error = xchk_dirpath_revalidate(dl, path); 527928b721aSDarrick J. Wong if (error) 528928b721aSDarrick J. Wong return error; 529928b721aSDarrick J. Wong 530928b721aSDarrick J. Wong trace_xchk_dirpath_walk_upwards(sc, sc->ip, path->path_nr, &dl->xname, 531928b721aSDarrick J. Wong &dl->pptr_rec); 532928b721aSDarrick J. Wong 533928b721aSDarrick J. Wong /* 534928b721aSDarrick J. Wong * The inode being scanned is its own direct ancestor! 535928b721aSDarrick J. Wong * Get rid of this path. 536928b721aSDarrick J. Wong */ 537928b721aSDarrick J. Wong if (be64_to_cpu(dl->pptr_rec.p_ino) == sc->ip->i_ino) { 538928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE); 539928b721aSDarrick J. Wong return 0; 540928b721aSDarrick J. Wong } 541928b721aSDarrick J. Wong 542928b721aSDarrick J. Wong /* 543928b721aSDarrick J. Wong * Drop ILOCK_EXCL on the inode being scanned. We still hold 544928b721aSDarrick J. Wong * IOLOCK_EXCL on it, so it cannot move around or be renamed. 545928b721aSDarrick J. Wong * 546928b721aSDarrick J. Wong * Beyond this point we're walking up the directory tree, which means 547928b721aSDarrick J. Wong * that we can acquire and drop the ILOCK on an alias of sc->ip. The 548928b721aSDarrick J. Wong * ILOCK state is no longer tracked in the scrub context. Hence we 549928b721aSDarrick J. Wong * must drop @sc->ip's ILOCK during the walk. 550928b721aSDarrick J. Wong */ 551*3d2c3411SDarrick J. Wong is_metadir = xfs_is_metadir_inode(sc->ip); 552928b721aSDarrick J. Wong mutex_unlock(&dl->lock); 553928b721aSDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL); 554928b721aSDarrick J. Wong 555928b721aSDarrick J. Wong /* 556928b721aSDarrick J. Wong * Take the first step in the walk towards the root by checking the 557928b721aSDarrick J. Wong * start of this path, which is a direct parent pointer of @sc->ip. 558928b721aSDarrick J. Wong * If we see any kind of error here (including corruptions), the parent 559928b721aSDarrick J. Wong * pointer of @sc->ip is corrupt. Stop the whole scan. 560928b721aSDarrick J. Wong */ 561*3d2c3411SDarrick J. Wong error = xchk_dirpath_step_up(dl, path, is_metadir); 562928b721aSDarrick J. Wong if (error) { 563928b721aSDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL); 564928b721aSDarrick J. Wong mutex_lock(&dl->lock); 565928b721aSDarrick J. Wong return error; 566928b721aSDarrick J. Wong } 567928b721aSDarrick J. Wong 568928b721aSDarrick J. Wong /* 569928b721aSDarrick J. Wong * Take steps upward from the second step in this path towards the 570928b721aSDarrick J. Wong * root. If we hit corruption errors here, there's a problem 571928b721aSDarrick J. Wong * *somewhere* in the path, but we don't need to stop scanning. 572928b721aSDarrick J. Wong */ 573928b721aSDarrick J. Wong while (!error && path->outcome == XCHK_DIRPATH_SCANNING) 574*3d2c3411SDarrick J. Wong error = xchk_dirpath_step_up(dl, path, is_metadir); 575928b721aSDarrick J. Wong 576928b721aSDarrick J. Wong /* Retake the locks we had, mark paths, etc. */ 577928b721aSDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL); 578928b721aSDarrick J. Wong mutex_lock(&dl->lock); 579928b721aSDarrick J. Wong if (error == -EFSCORRUPTED) { 580928b721aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT); 581928b721aSDarrick J. Wong error = 0; 582928b721aSDarrick J. Wong } 583928b721aSDarrick J. Wong if (!error && dl->stale) 584928b721aSDarrick J. Wong return -ESTALE; 585928b721aSDarrick J. Wong return error; 586928b721aSDarrick J. Wong } 587928b721aSDarrick J. Wong 588d54c5ac8SDarrick J. Wong /* 589d54c5ac8SDarrick J. Wong * Decide if this path step has been touched by this live update. Returns 590d54c5ac8SDarrick J. Wong * 1 for yes, 0 for no, or a negative errno. 591d54c5ac8SDarrick J. Wong */ 592d54c5ac8SDarrick J. Wong STATIC int 593d54c5ac8SDarrick J. Wong xchk_dirpath_step_is_stale( 594d54c5ac8SDarrick J. Wong struct xchk_dirtree *dl, 595d54c5ac8SDarrick J. Wong struct xchk_dirpath *path, 596d54c5ac8SDarrick J. Wong unsigned int step_nr, 597d54c5ac8SDarrick J. Wong xfarray_idx_t step_idx, 598d54c5ac8SDarrick J. Wong struct xfs_dir_update_params *p, 599d54c5ac8SDarrick J. Wong xfs_ino_t *cursor) 600d54c5ac8SDarrick J. Wong { 601d54c5ac8SDarrick J. Wong struct xchk_dirpath_step step; 602d54c5ac8SDarrick J. Wong xfs_ino_t child_ino = *cursor; 603d54c5ac8SDarrick J. Wong int error; 604d54c5ac8SDarrick J. Wong 605d54c5ac8SDarrick J. Wong error = xfarray_load(dl->path_steps, step_idx, &step); 606d54c5ac8SDarrick J. Wong if (error) 607d54c5ac8SDarrick J. Wong return error; 608d54c5ac8SDarrick J. Wong *cursor = be64_to_cpu(step.pptr_rec.p_ino); 609d54c5ac8SDarrick J. Wong 610d54c5ac8SDarrick J. Wong /* 611d54c5ac8SDarrick J. Wong * If the parent and child being updated are not the ones mentioned in 612d54c5ac8SDarrick J. Wong * this path step, the scan data is still ok. 613d54c5ac8SDarrick J. Wong */ 614d54c5ac8SDarrick J. Wong if (p->ip->i_ino != child_ino || p->dp->i_ino != *cursor) 615d54c5ac8SDarrick J. Wong return 0; 616d54c5ac8SDarrick J. Wong 617d54c5ac8SDarrick J. Wong /* 618d54c5ac8SDarrick J. Wong * If the dirent name lengths or byte sequences are different, the scan 619d54c5ac8SDarrick J. Wong * data is still ok. 620d54c5ac8SDarrick J. Wong */ 621d54c5ac8SDarrick J. Wong if (p->name->len != step.name_len) 622d54c5ac8SDarrick J. Wong return 0; 623d54c5ac8SDarrick J. Wong 624d54c5ac8SDarrick J. Wong error = xfblob_loadname(dl->path_names, step.name_cookie, 625d54c5ac8SDarrick J. Wong &dl->hook_xname, step.name_len); 626d54c5ac8SDarrick J. Wong if (error) 627d54c5ac8SDarrick J. Wong return error; 628d54c5ac8SDarrick J. Wong 629d54c5ac8SDarrick J. Wong if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0) 630d54c5ac8SDarrick J. Wong return 0; 631d54c5ac8SDarrick J. Wong 6323f31406aSDarrick J. Wong /* 6333f31406aSDarrick J. Wong * If the update comes from the repair code itself, walk the state 6343f31406aSDarrick J. Wong * machine forward. 6353f31406aSDarrick J. Wong */ 6363f31406aSDarrick J. Wong if (p->ip->i_ino == dl->scan_ino && 6373f31406aSDarrick J. Wong path->outcome == XREP_DIRPATH_ADOPTING) { 6383f31406aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_ADOPTED); 6393f31406aSDarrick J. Wong return 0; 6403f31406aSDarrick J. Wong } 6413f31406aSDarrick J. Wong 6423f31406aSDarrick J. Wong if (p->ip->i_ino == dl->scan_ino && 6433f31406aSDarrick J. Wong path->outcome == XREP_DIRPATH_DELETING) { 6443f31406aSDarrick J. Wong xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETED); 6453f31406aSDarrick J. Wong return 0; 6463f31406aSDarrick J. Wong } 6473f31406aSDarrick J. Wong 648d54c5ac8SDarrick J. Wong /* Exact match, scan data is out of date. */ 649d54c5ac8SDarrick J. Wong trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp, 650d54c5ac8SDarrick J. Wong p->ip, p->name); 651d54c5ac8SDarrick J. Wong return 1; 652d54c5ac8SDarrick J. Wong } 653d54c5ac8SDarrick J. Wong 654d54c5ac8SDarrick J. Wong /* 655d54c5ac8SDarrick J. Wong * Decide if this path has been touched by this live update. Returns 1 for 656d54c5ac8SDarrick J. Wong * yes, 0 for no, or a negative errno. 657d54c5ac8SDarrick J. Wong */ 658d54c5ac8SDarrick J. Wong STATIC int 659d54c5ac8SDarrick J. Wong xchk_dirpath_is_stale( 660d54c5ac8SDarrick J. Wong struct xchk_dirtree *dl, 661d54c5ac8SDarrick J. Wong struct xchk_dirpath *path, 662d54c5ac8SDarrick J. Wong struct xfs_dir_update_params *p) 663d54c5ac8SDarrick J. Wong { 664d54c5ac8SDarrick J. Wong xfs_ino_t cursor = dl->scan_ino; 665d54c5ac8SDarrick J. Wong xfarray_idx_t idx = path->first_step; 666d54c5ac8SDarrick J. Wong unsigned int i; 667d54c5ac8SDarrick J. Wong int ret; 668d54c5ac8SDarrick J. Wong 669d54c5ac8SDarrick J. Wong /* 670d54c5ac8SDarrick J. Wong * The child being updated has not been seen by this path at all; this 671d54c5ac8SDarrick J. Wong * path cannot be stale. 672d54c5ac8SDarrick J. Wong */ 673d54c5ac8SDarrick J. Wong if (!xino_bitmap_test(&path->seen_inodes, p->ip->i_ino)) 674d54c5ac8SDarrick J. Wong return 0; 675d54c5ac8SDarrick J. Wong 676d54c5ac8SDarrick J. Wong ret = xchk_dirpath_step_is_stale(dl, path, 0, idx, p, &cursor); 677d54c5ac8SDarrick J. Wong if (ret != 0) 678d54c5ac8SDarrick J. Wong return ret; 679d54c5ac8SDarrick J. Wong 680d54c5ac8SDarrick J. Wong for (i = 1, idx = path->second_step; i < path->nr_steps; i++, idx++) { 681d54c5ac8SDarrick J. Wong ret = xchk_dirpath_step_is_stale(dl, path, i, idx, p, &cursor); 682d54c5ac8SDarrick J. Wong if (ret != 0) 683d54c5ac8SDarrick J. Wong return ret; 684d54c5ac8SDarrick J. Wong } 685d54c5ac8SDarrick J. Wong 686d54c5ac8SDarrick J. Wong return 0; 687d54c5ac8SDarrick J. Wong } 688d54c5ac8SDarrick J. Wong 689d54c5ac8SDarrick J. Wong /* 690d54c5ac8SDarrick J. Wong * Decide if a directory update from the regular filesystem touches any of the 691d54c5ac8SDarrick J. Wong * paths we've scanned, and invalidate the scan data if true. 692d54c5ac8SDarrick J. Wong */ 693d54c5ac8SDarrick J. Wong STATIC int 694d54c5ac8SDarrick J. Wong xchk_dirtree_live_update( 695d54c5ac8SDarrick J. Wong struct notifier_block *nb, 696d54c5ac8SDarrick J. Wong unsigned long action, 697d54c5ac8SDarrick J. Wong void *data) 698d54c5ac8SDarrick J. Wong { 699d54c5ac8SDarrick J. Wong struct xfs_dir_update_params *p = data; 700d54c5ac8SDarrick J. Wong struct xchk_dirtree *dl; 701d54c5ac8SDarrick J. Wong struct xchk_dirpath *path; 702d54c5ac8SDarrick J. Wong int ret; 703d54c5ac8SDarrick J. Wong 704d54c5ac8SDarrick J. Wong dl = container_of(nb, struct xchk_dirtree, dhook.dirent_hook.nb); 705d54c5ac8SDarrick J. Wong 706d54c5ac8SDarrick J. Wong trace_xchk_dirtree_live_update(dl->sc, p->dp, action, p->ip, p->delta, 707d54c5ac8SDarrick J. Wong p->name); 708d54c5ac8SDarrick J. Wong 709d54c5ac8SDarrick J. Wong mutex_lock(&dl->lock); 710d54c5ac8SDarrick J. Wong 711d54c5ac8SDarrick J. Wong if (dl->stale || dl->aborted) 712d54c5ac8SDarrick J. Wong goto out_unlock; 713d54c5ac8SDarrick J. Wong 714d54c5ac8SDarrick J. Wong xchk_dirtree_for_each_path(dl, path) { 715d54c5ac8SDarrick J. Wong ret = xchk_dirpath_is_stale(dl, path, p); 716d54c5ac8SDarrick J. Wong if (ret < 0) { 717d54c5ac8SDarrick J. Wong dl->aborted = true; 718d54c5ac8SDarrick J. Wong break; 719d54c5ac8SDarrick J. Wong } 720d54c5ac8SDarrick J. Wong if (ret == 1) { 721d54c5ac8SDarrick J. Wong dl->stale = true; 722d54c5ac8SDarrick J. Wong break; 723d54c5ac8SDarrick J. Wong } 724d54c5ac8SDarrick J. Wong } 725d54c5ac8SDarrick J. Wong 726d54c5ac8SDarrick J. Wong out_unlock: 727d54c5ac8SDarrick J. Wong mutex_unlock(&dl->lock); 728d54c5ac8SDarrick J. Wong return NOTIFY_DONE; 729d54c5ac8SDarrick J. Wong } 730d54c5ac8SDarrick J. Wong 731928b721aSDarrick J. Wong /* Delete all the collected path information. */ 732928b721aSDarrick J. Wong STATIC void 733928b721aSDarrick J. Wong xchk_dirtree_reset( 734928b721aSDarrick J. Wong void *buf) 735928b721aSDarrick J. Wong { 736928b721aSDarrick J. Wong struct xchk_dirtree *dl = buf; 737928b721aSDarrick J. Wong struct xchk_dirpath *path, *n; 738928b721aSDarrick J. Wong 739928b721aSDarrick J. Wong ASSERT(dl->sc->ilock_flags & XFS_ILOCK_EXCL); 740928b721aSDarrick J. Wong 741928b721aSDarrick J. Wong xchk_dirtree_for_each_path_safe(dl, path, n) { 742928b721aSDarrick J. Wong list_del_init(&path->list); 743928b721aSDarrick J. Wong xino_bitmap_destroy(&path->seen_inodes); 744928b721aSDarrick J. Wong kfree(path); 745928b721aSDarrick J. Wong } 746928b721aSDarrick J. Wong dl->nr_paths = 0; 747928b721aSDarrick J. Wong 748928b721aSDarrick J. Wong xfarray_truncate(dl->path_steps); 749928b721aSDarrick J. Wong xfblob_truncate(dl->path_names); 750928b721aSDarrick J. Wong 751928b721aSDarrick J. Wong dl->stale = false; 752928b721aSDarrick J. Wong } 753928b721aSDarrick J. Wong 754928b721aSDarrick J. Wong /* 755928b721aSDarrick J. Wong * Load the name/pptr from the first step in this path into @dl->pptr_rec and 756928b721aSDarrick J. Wong * @dl->xname. 757928b721aSDarrick J. Wong */ 758928b721aSDarrick J. Wong STATIC int 759928b721aSDarrick J. Wong xchk_dirtree_load_path( 760928b721aSDarrick J. Wong struct xchk_dirtree *dl, 761928b721aSDarrick J. Wong struct xchk_dirpath *path) 762928b721aSDarrick J. Wong { 763928b721aSDarrick J. Wong struct xchk_dirpath_step step; 764928b721aSDarrick J. Wong int error; 765928b721aSDarrick J. Wong 766928b721aSDarrick J. Wong error = xfarray_load(dl->path_steps, path->first_step, &step); 767928b721aSDarrick J. Wong if (error) 768928b721aSDarrick J. Wong return error; 769928b721aSDarrick J. Wong 770928b721aSDarrick J. Wong error = xfblob_loadname(dl->path_names, step.name_cookie, &dl->xname, 771928b721aSDarrick J. Wong step.name_len); 772928b721aSDarrick J. Wong if (error) 773928b721aSDarrick J. Wong return error; 774928b721aSDarrick J. Wong 775928b721aSDarrick J. Wong dl->pptr_rec = step.pptr_rec; /* struct copy */ 776928b721aSDarrick J. Wong return 0; 777928b721aSDarrick J. Wong } 778928b721aSDarrick J. Wong 779928b721aSDarrick J. Wong /* 780928b721aSDarrick J. Wong * For each parent pointer of this subdir, trace a path upwards towards the 781928b721aSDarrick J. Wong * root directory and record what we find. Returns 0 for success; 782928b721aSDarrick J. Wong * -EFSCORRUPTED if walking the parent pointers of @sc->ip failed, -ELNRNG if a 783928b721aSDarrick J. Wong * path was too deep; -ENOSR if there were too many parent pointers; or 784928b721aSDarrick J. Wong * a negative errno. 785928b721aSDarrick J. Wong */ 7863f31406aSDarrick J. Wong int 787928b721aSDarrick J. Wong xchk_dirtree_find_paths_to_root( 788928b721aSDarrick J. Wong struct xchk_dirtree *dl) 789928b721aSDarrick J. Wong { 790928b721aSDarrick J. Wong struct xfs_scrub *sc = dl->sc; 791928b721aSDarrick J. Wong struct xchk_dirpath *path; 792928b721aSDarrick J. Wong int error = 0; 793928b721aSDarrick J. Wong 794928b721aSDarrick J. Wong do { 795928b721aSDarrick J. Wong if (xchk_should_terminate(sc, &error)) 796928b721aSDarrick J. Wong return error; 797928b721aSDarrick J. Wong 798928b721aSDarrick J. Wong xchk_dirtree_reset(dl); 799928b721aSDarrick J. Wong 800928b721aSDarrick J. Wong /* 801928b721aSDarrick J. Wong * If the extended attributes look as though they has been 802928b721aSDarrick J. Wong * zapped by the inode record repair code, we cannot scan for 803928b721aSDarrick J. Wong * parent pointers. 804928b721aSDarrick J. Wong */ 805928b721aSDarrick J. Wong if (xchk_pptr_looks_zapped(sc->ip)) { 806928b721aSDarrick J. Wong xchk_set_incomplete(sc); 807928b721aSDarrick J. Wong return -EBUSY; 808928b721aSDarrick J. Wong } 809928b721aSDarrick J. Wong 810928b721aSDarrick J. Wong /* 811928b721aSDarrick J. Wong * Create path walk contexts for each parent of the directory 812928b721aSDarrick J. Wong * that is being scanned. Directories are supposed to have 813928b721aSDarrick J. Wong * only one parent, but this is how we detect multiple parents. 814928b721aSDarrick J. Wong */ 815928b721aSDarrick J. Wong error = xchk_xattr_walk(sc, sc->ip, xchk_dirtree_create_path, 816928b721aSDarrick J. Wong NULL, dl); 817928b721aSDarrick J. Wong if (error) 818928b721aSDarrick J. Wong return error; 819928b721aSDarrick J. Wong 820928b721aSDarrick J. Wong xchk_dirtree_for_each_path(dl, path) { 821928b721aSDarrick J. Wong /* Load path components into dl->pptr/xname */ 822928b721aSDarrick J. Wong error = xchk_dirtree_load_path(dl, path); 823928b721aSDarrick J. Wong if (error) 824928b721aSDarrick J. Wong return error; 825928b721aSDarrick J. Wong 826928b721aSDarrick J. Wong /* 827928b721aSDarrick J. Wong * Try to walk up each path to the root. This enables 828928b721aSDarrick J. Wong * us to find directory loops in ancestors, and the 829928b721aSDarrick J. Wong * like. 830928b721aSDarrick J. Wong */ 831928b721aSDarrick J. Wong error = xchk_dirpath_walk_upwards(dl, path); 832928b721aSDarrick J. Wong if (error == -EFSCORRUPTED) { 833928b721aSDarrick J. Wong /* 834928b721aSDarrick J. Wong * A parent pointer of @sc->ip is bad, don't 835928b721aSDarrick J. Wong * bother continuing. 836928b721aSDarrick J. Wong */ 837928b721aSDarrick J. Wong break; 838928b721aSDarrick J. Wong } 839928b721aSDarrick J. Wong if (error == -ESTALE) { 840928b721aSDarrick J. Wong /* This had better be an invalidation. */ 841928b721aSDarrick J. Wong ASSERT(dl->stale); 842928b721aSDarrick J. Wong break; 843928b721aSDarrick J. Wong } 844928b721aSDarrick J. Wong if (error) 845928b721aSDarrick J. Wong return error; 846d54c5ac8SDarrick J. Wong if (dl->aborted) 847d54c5ac8SDarrick J. Wong return 0; 848928b721aSDarrick J. Wong } 849928b721aSDarrick J. Wong } while (dl->stale); 850928b721aSDarrick J. Wong 851928b721aSDarrick J. Wong return error; 852928b721aSDarrick J. Wong } 853928b721aSDarrick J. Wong 854928b721aSDarrick J. Wong /* 855928b721aSDarrick J. Wong * Figure out what to do with the paths we tried to find. Do not call this 856928b721aSDarrick J. Wong * if the scan results are stale. 857928b721aSDarrick J. Wong */ 8583f31406aSDarrick J. Wong void 859928b721aSDarrick J. Wong xchk_dirtree_evaluate( 860928b721aSDarrick J. Wong struct xchk_dirtree *dl, 861928b721aSDarrick J. Wong struct xchk_dirtree_outcomes *oc) 862928b721aSDarrick J. Wong { 863928b721aSDarrick J. Wong struct xchk_dirpath *path; 864928b721aSDarrick J. Wong 865928b721aSDarrick J. Wong ASSERT(!dl->stale); 866928b721aSDarrick J. Wong 867928b721aSDarrick J. Wong /* Scan the paths we have to decide what to do. */ 868928b721aSDarrick J. Wong memset(oc, 0, sizeof(struct xchk_dirtree_outcomes)); 869928b721aSDarrick J. Wong xchk_dirtree_for_each_path(dl, path) { 870928b721aSDarrick J. Wong trace_xchk_dirpath_evaluate_path(dl->sc, path->path_nr, 871928b721aSDarrick J. Wong path->nr_steps, path->outcome); 872928b721aSDarrick J. Wong 873928b721aSDarrick J. Wong switch (path->outcome) { 874928b721aSDarrick J. Wong case XCHK_DIRPATH_SCANNING: 875928b721aSDarrick J. Wong /* shouldn't get here */ 876928b721aSDarrick J. Wong ASSERT(0); 877928b721aSDarrick J. Wong break; 878928b721aSDarrick J. Wong case XCHK_DIRPATH_DELETE: 879928b721aSDarrick J. Wong /* This one is already going away. */ 880928b721aSDarrick J. Wong oc->bad++; 881928b721aSDarrick J. Wong break; 882928b721aSDarrick J. Wong case XCHK_DIRPATH_CORRUPT: 883928b721aSDarrick J. Wong case XCHK_DIRPATH_LOOP: 884928b721aSDarrick J. Wong /* Couldn't find the end of this path. */ 885928b721aSDarrick J. Wong oc->suspect++; 886928b721aSDarrick J. Wong break; 887928b721aSDarrick J. Wong case XCHK_DIRPATH_STALE: 888928b721aSDarrick J. Wong /* shouldn't get here either */ 889928b721aSDarrick J. Wong ASSERT(0); 890928b721aSDarrick J. Wong break; 891928b721aSDarrick J. Wong case XCHK_DIRPATH_OK: 892928b721aSDarrick J. Wong /* This path got all the way to the root. */ 893928b721aSDarrick J. Wong oc->good++; 894928b721aSDarrick J. Wong break; 8953f31406aSDarrick J. Wong case XREP_DIRPATH_DELETING: 8963f31406aSDarrick J. Wong case XREP_DIRPATH_DELETED: 8973f31406aSDarrick J. Wong case XREP_DIRPATH_ADOPTING: 8983f31406aSDarrick J. Wong case XREP_DIRPATH_ADOPTED: 8993f31406aSDarrick J. Wong /* These should not be in progress! */ 9003f31406aSDarrick J. Wong ASSERT(0); 9013f31406aSDarrick J. Wong break; 902928b721aSDarrick J. Wong } 903928b721aSDarrick J. Wong } 904928b721aSDarrick J. Wong 905928b721aSDarrick J. Wong trace_xchk_dirtree_evaluate(dl, oc); 906928b721aSDarrick J. Wong } 907928b721aSDarrick J. Wong 908928b721aSDarrick J. Wong /* Look for directory loops. */ 909928b721aSDarrick J. Wong int 910928b721aSDarrick J. Wong xchk_dirtree( 911928b721aSDarrick J. Wong struct xfs_scrub *sc) 912928b721aSDarrick J. Wong { 913928b721aSDarrick J. Wong struct xchk_dirtree_outcomes oc; 914928b721aSDarrick J. Wong struct xchk_dirtree *dl = sc->buf; 915928b721aSDarrick J. Wong int error; 916928b721aSDarrick J. Wong 917928b721aSDarrick J. Wong /* 918928b721aSDarrick J. Wong * Nondirectories do not point downwards to other files, so they cannot 919928b721aSDarrick J. Wong * cause a cycle in the directory tree. 920928b721aSDarrick J. Wong */ 921928b721aSDarrick J. Wong if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) 922928b721aSDarrick J. Wong return -ENOENT; 923928b721aSDarrick J. Wong 924928b721aSDarrick J. Wong ASSERT(xfs_has_parent(sc->mp)); 925928b721aSDarrick J. Wong 926d54c5ac8SDarrick J. Wong /* 927d54c5ac8SDarrick J. Wong * Find the root of the directory tree. Remember which directory to 928d54c5ac8SDarrick J. Wong * scan, because the hook doesn't detach until after sc->ip gets 929d54c5ac8SDarrick J. Wong * released during teardown. 930d54c5ac8SDarrick J. Wong */ 931679b098bSDarrick J. Wong dl->root_ino = xchk_inode_rootdir_inum(sc->ip); 932d54c5ac8SDarrick J. Wong dl->scan_ino = sc->ip->i_ino; 933928b721aSDarrick J. Wong 934928b721aSDarrick J. Wong trace_xchk_dirtree_start(sc->ip, sc->sm, 0); 935928b721aSDarrick J. Wong 936d54c5ac8SDarrick J. Wong /* 937d54c5ac8SDarrick J. Wong * Hook into the directory entry code so that we can capture updates to 938d54c5ac8SDarrick J. Wong * paths that we have already scanned. The scanner thread takes each 939d54c5ac8SDarrick J. Wong * directory's ILOCK, which means that any in-progress directory update 940d54c5ac8SDarrick J. Wong * will finish before we can scan the directory. 941d54c5ac8SDarrick J. Wong */ 942d54c5ac8SDarrick J. Wong ASSERT(sc->flags & XCHK_FSGATES_DIRENTS); 943d54c5ac8SDarrick J. Wong xfs_dir_hook_setup(&dl->dhook, xchk_dirtree_live_update); 944d54c5ac8SDarrick J. Wong error = xfs_dir_hook_add(sc->mp, &dl->dhook); 945d54c5ac8SDarrick J. Wong if (error) 946d54c5ac8SDarrick J. Wong goto out; 947d54c5ac8SDarrick J. Wong 948928b721aSDarrick J. Wong mutex_lock(&dl->lock); 949928b721aSDarrick J. Wong 950928b721aSDarrick J. Wong /* Trace each parent pointer's path to the root. */ 951928b721aSDarrick J. Wong error = xchk_dirtree_find_paths_to_root(dl); 952928b721aSDarrick J. Wong if (error == -EFSCORRUPTED || error == -ELNRNG || error == -ENOSR) { 953928b721aSDarrick J. Wong /* 954928b721aSDarrick J. Wong * Don't bother walking the paths if the xattr structure or the 955928b721aSDarrick J. Wong * parent pointers are corrupt; this scan cannot be completed 956928b721aSDarrick J. Wong * without full information. 957928b721aSDarrick J. Wong */ 958928b721aSDarrick J. Wong xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); 959928b721aSDarrick J. Wong error = 0; 960928b721aSDarrick J. Wong goto out_scanlock; 961928b721aSDarrick J. Wong } 962928b721aSDarrick J. Wong if (error == -EBUSY) { 963928b721aSDarrick J. Wong /* 964928b721aSDarrick J. Wong * We couldn't scan some directory's parent pointers because 965928b721aSDarrick J. Wong * the attr fork looked like it had been zapped. The 966928b721aSDarrick J. Wong * scan was marked incomplete, so no further error code 967928b721aSDarrick J. Wong * is necessary. 968928b721aSDarrick J. Wong */ 969928b721aSDarrick J. Wong error = 0; 970928b721aSDarrick J. Wong goto out_scanlock; 971928b721aSDarrick J. Wong } 972928b721aSDarrick J. Wong if (error) 973928b721aSDarrick J. Wong goto out_scanlock; 974d54c5ac8SDarrick J. Wong if (dl->aborted) { 975d54c5ac8SDarrick J. Wong xchk_set_incomplete(sc); 976d54c5ac8SDarrick J. Wong goto out_scanlock; 977d54c5ac8SDarrick J. Wong } 978928b721aSDarrick J. Wong 979928b721aSDarrick J. Wong /* Assess what we found in our path evaluation. */ 980928b721aSDarrick J. Wong xchk_dirtree_evaluate(dl, &oc); 981928b721aSDarrick J. Wong if (xchk_dirtree_parentless(dl)) { 982928b721aSDarrick J. Wong if (oc.good || oc.bad || oc.suspect) 983928b721aSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->ip->i_ino); 984928b721aSDarrick J. Wong } else { 985928b721aSDarrick J. Wong if (oc.bad || oc.good + oc.suspect != 1) 986928b721aSDarrick J. Wong xchk_ino_set_corrupt(sc, sc->ip->i_ino); 987928b721aSDarrick J. Wong if (oc.suspect) 988928b721aSDarrick J. Wong xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); 989928b721aSDarrick J. Wong } 990928b721aSDarrick J. Wong 991928b721aSDarrick J. Wong out_scanlock: 992928b721aSDarrick J. Wong mutex_unlock(&dl->lock); 993d54c5ac8SDarrick J. Wong out: 994928b721aSDarrick J. Wong trace_xchk_dirtree_done(sc->ip, sc->sm, error); 995928b721aSDarrick J. Wong return error; 996928b721aSDarrick J. Wong } 997679b098bSDarrick J. Wong 998679b098bSDarrick J. Wong /* Does the directory targetted by this scrub have no parents? */ 999679b098bSDarrick J. Wong bool 1000679b098bSDarrick J. Wong xchk_dirtree_parentless(const struct xchk_dirtree *dl) 1001679b098bSDarrick J. Wong { 1002679b098bSDarrick J. Wong struct xfs_scrub *sc = dl->sc; 1003679b098bSDarrick J. Wong 1004679b098bSDarrick J. Wong if (xchk_inode_is_dirtree_root(sc->ip)) 1005679b098bSDarrick J. Wong return true; 1006679b098bSDarrick J. Wong if (VFS_I(sc->ip)->i_nlink == 0) 1007679b098bSDarrick J. Wong return true; 1008679b098bSDarrick J. Wong return false; 1009679b098bSDarrick J. Wong } 1010