// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <[email protected]> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_defer.h" #include "xfs_bit.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" #include "xfs_icache.h" #include "xfs_da_format.h" #include "xfs_da_btree.h" #include "xfs_dir2.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_priv.h" #include "xfs_trans_space.h" #include "xfs_health.h" #include "xfs_exchmaps.h" #include "xfs_parent.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/iscan.h" #include "scrub/findparent.h" #include "scrub/readdir.h" #include "scrub/tempfile.h" #include "scrub/listxattr.h" /* * Finding the Parent of a Directory * ================================= * * Directories have parent pointers, in the sense that each directory contains * a dotdot entry that points to the single allowed parent. The brute force * way to find the parent of a given directory is to scan every directory in * the filesystem looking for a child dirent that references this directory. * * This module wraps the process of scanning the directory tree. It requires * that @sc->ip is the directory whose parent we want to find, and that the * caller hold only the IOLOCK on that directory. The scan itself needs to * take the ILOCK of each directory visited. * * Because we cannot hold @sc->ip's ILOCK during a scan of the whole fs, it is * necessary to use dirent hook to update the parent scan results. Callers * must not read the scan results without re-taking @sc->ip's ILOCK. * * There are a few shortcuts that we can take to avoid scanning the entire * filesystem, such as noticing directory tree roots and querying the dentry * cache for parent information. */ struct xrep_findparent_info { … }; /* * If this directory entry points to the scrub target inode, then the directory * we're scanning is the parent of the scrub target inode. */ STATIC int xrep_findparent_dirent( struct xfs_scrub *sc, struct xfs_inode *dp, xfs_dir2_dataptr_t dapos, const struct xfs_name *name, xfs_ino_t ino, void *priv) { … } /* * If this is a directory, walk the dirents looking for any that point to the * scrub target inode. */ STATIC int xrep_findparent_walk_directory( struct xrep_findparent_info *fpi) { … } /* * Update this directory's dotdot pointer based on ongoing dirent updates. */ STATIC int xrep_findparent_live_update( struct notifier_block *nb, unsigned long action, void *data) { … } /* * Set up a scan to find the parent of a directory. The provided dirent hook * will be called when there is a dotdot update for the inode being repaired. */ int __xrep_findparent_scan_start( struct xfs_scrub *sc, struct xrep_parent_scan_info *pscan, notifier_fn_t custom_fn) { … } /* * Scan the entire filesystem looking for a parent inode for the inode being * scrubbed. @sc->ip must not be the root of a directory tree. Callers must * not hold a dirty transaction or any lock that would interfere with taking * an ILOCK. * * Returns 0 with @pscan->parent_ino set to the parent that we found. * Returns 0 with @pscan->parent_ino set to NULLFSINO if we found no parents. * Returns the usual negative errno if something else happened. */ int xrep_findparent_scan( struct xrep_parent_scan_info *pscan) { … } /* Tear down a parent scan. */ void xrep_findparent_scan_teardown( struct xrep_parent_scan_info *pscan) { … } /* Finish a parent scan early. */ void xrep_findparent_scan_finish_early( struct xrep_parent_scan_info *pscan, xfs_ino_t ino) { … } /* * Confirm that the directory @parent_ino actually contains a directory entry * pointing to the child @sc->ip->ino. This function returns one of several * ways: * * Returns 0 with @parent_ino unchanged if the parent was confirmed. * Returns 0 with @parent_ino set to NULLFSINO if the parent was not valid. * Returns the usual negative errno if something else happened. */ int xrep_findparent_confirm( struct xfs_scrub *sc, xfs_ino_t *parent_ino) { … } /* * If we're the root of a directory tree, we are our own parent. If we're an * unlinked directory, the parent /won't/ have a link to us. Set the parent * directory to the root for both cases. Returns NULLFSINO if we don't know * what to do. */ xfs_ino_t xrep_findparent_self_reference( struct xfs_scrub *sc) { … } /* Check the dentry cache to see if knows of a parent for the scrub target. */ xfs_ino_t xrep_findparent_from_dcache( struct xfs_scrub *sc) { … }