|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Copyright (c) 2023-2024 Oracle.  All Rights Reserved. | 
|  | * Author: Darrick J. Wong <djwong@kernel.org> | 
|  | */ | 
|  | #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_log_format.h" | 
|  | #include "xfs_trans.h" | 
|  | #include "xfs_inode.h" | 
|  | #include "xfs_icache.h" | 
|  | #include "xfs_dir2.h" | 
|  | #include "xfs_dir2_priv.h" | 
|  | #include "xfs_attr.h" | 
|  | #include "xfs_parent.h" | 
|  | #include "scrub/scrub.h" | 
|  | #include "scrub/common.h" | 
|  | #include "scrub/bitmap.h" | 
|  | #include "scrub/ino_bitmap.h" | 
|  | #include "scrub/xfile.h" | 
|  | #include "scrub/xfarray.h" | 
|  | #include "scrub/xfblob.h" | 
|  | #include "scrub/listxattr.h" | 
|  | #include "scrub/trace.h" | 
|  | #include "scrub/repair.h" | 
|  | #include "scrub/orphanage.h" | 
|  | #include "scrub/dirtree.h" | 
|  |  | 
|  | /* | 
|  | * Directory Tree Structure Validation | 
|  | * =================================== | 
|  | * | 
|  | * Validating the tree qualities of the directory tree structure can be | 
|  | * difficult.  If the tree is frozen, running a depth (or breadth) first search | 
|  | * and marking a bitmap suffices to determine if there is a cycle.  XORing the | 
|  | * mark bitmap with the inode bitmap afterwards tells us if there are | 
|  | * disconnected cycles.  If the tree is not frozen, directory updates can move | 
|  | * subtrees across the scanner wavefront, which complicates the design greatly. | 
|  | * | 
|  | * Directory parent pointers change that by enabling an incremental approach to | 
|  | * validation of the tree structure.  Instead of using one thread to scan the | 
|  | * entire filesystem, we instead can have multiple threads walking individual | 
|  | * subdirectories upwards to the root.  In a perfect world, the IOLOCK would | 
|  | * suffice to stabilize two directories in a parent -> child relationship. | 
|  | * Unfortunately, the VFS does not take the IOLOCK when moving a child | 
|  | * subdirectory, so we instead synchronize on ILOCK and use dirent update hooks | 
|  | * to detect a race.  If a race occurs in a path, we restart the scan. | 
|  | * | 
|  | * If the walk terminates without reaching the root, we know the path is | 
|  | * disconnected and ought to be attached to the lost and found.  If on the walk | 
|  | * we find the same subdir that we're scanning, we know this is a cycle and | 
|  | * should delete an incoming edge.  If we find multiple paths to the root, we | 
|  | * know to delete an incoming edge. | 
|  | * | 
|  | * There are two big hitches with this approach: first, all file link counts | 
|  | * must be correct to prevent other writers from doing the wrong thing with the | 
|  | * directory tree structure.  Second, because we're walking upwards in a tree | 
|  | * of arbitrary depth, we cannot hold all the ILOCKs.  Instead, we will use a | 
|  | * directory update hook to invalidate the scan results if one of the paths | 
|  | * we've scanned has changed. | 
|  | */ | 
|  |  | 
|  | /* Clean up the dirtree checking resources. */ | 
|  | STATIC void | 
|  | xchk_dirtree_buf_cleanup( | 
|  | void			*buf) | 
|  | { | 
|  | struct xchk_dirtree	*dl = buf; | 
|  | struct xchk_dirpath	*path, *n; | 
|  |  | 
|  | if (dl->scan_ino != NULLFSINO) | 
|  | xfs_dir_hook_del(dl->sc->mp, &dl->dhook); | 
|  |  | 
|  | xchk_dirtree_for_each_path_safe(dl, path, n) { | 
|  | list_del_init(&path->list); | 
|  | xino_bitmap_destroy(&path->seen_inodes); | 
|  | kfree(path); | 
|  | } | 
|  |  | 
|  | xfblob_destroy(dl->path_names); | 
|  | xfarray_destroy(dl->path_steps); | 
|  | mutex_destroy(&dl->lock); | 
|  | } | 
|  |  | 
|  | /* Set us up to look for directory loops. */ | 
|  | int | 
|  | xchk_setup_dirtree( | 
|  | struct xfs_scrub	*sc) | 
|  | { | 
|  | struct xchk_dirtree	*dl; | 
|  | char			*descr; | 
|  | int			error; | 
|  |  | 
|  | xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS); | 
|  |  | 
|  | if (xchk_could_repair(sc)) { | 
|  | error = xrep_setup_dirtree(sc); | 
|  | if (error) | 
|  | return error; | 
|  | } | 
|  |  | 
|  | dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS); | 
|  | if (!dl) | 
|  | return -ENOMEM; | 
|  | dl->sc = sc; | 
|  | dl->xname.name = dl->namebuf; | 
|  | dl->hook_xname.name = dl->hook_namebuf; | 
|  | INIT_LIST_HEAD(&dl->path_list); | 
|  | dl->root_ino = NULLFSINO; | 
|  | dl->scan_ino = NULLFSINO; | 
|  | dl->parent_ino = NULLFSINO; | 
|  |  | 
|  | mutex_init(&dl->lock); | 
|  |  | 
|  | descr = xchk_xfile_ino_descr(sc, "dirtree path steps"); | 
|  | error = xfarray_create(descr, 0, sizeof(struct xchk_dirpath_step), | 
|  | &dl->path_steps); | 
|  | kfree(descr); | 
|  | if (error) | 
|  | goto out_dl; | 
|  |  | 
|  | descr = xchk_xfile_ino_descr(sc, "dirtree path names"); | 
|  | error = xfblob_create(descr, &dl->path_names); | 
|  | kfree(descr); | 
|  | if (error) | 
|  | goto out_steps; | 
|  |  | 
|  | error = xchk_setup_inode_contents(sc, 0); | 
|  | if (error) | 
|  | goto out_names; | 
|  |  | 
|  | sc->buf = dl; | 
|  | sc->buf_cleanup = xchk_dirtree_buf_cleanup; | 
|  | return 0; | 
|  |  | 
|  | out_names: | 
|  | xfblob_destroy(dl->path_names); | 
|  | out_steps: | 
|  | xfarray_destroy(dl->path_steps); | 
|  | out_dl: | 
|  | mutex_destroy(&dl->lock); | 
|  | kvfree(dl); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add the parent pointer described by @dl->pptr to the given path as a new | 
|  | * step.  Returns -ELNRNG if the path is too deep. | 
|  | */ | 
|  | int | 
|  | xchk_dirpath_append( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xfs_inode		*ip, | 
|  | struct xchk_dirpath		*path, | 
|  | const struct xfs_name		*name, | 
|  | const struct xfs_parent_rec	*pptr) | 
|  | { | 
|  | struct xchk_dirpath_step	step = { | 
|  | .pptr_rec		= *pptr, /* struct copy */ | 
|  | .name_len		= name->len, | 
|  | }; | 
|  | int				error; | 
|  |  | 
|  | /* | 
|  | * If this path is more than 2 billion steps long, this directory tree | 
|  | * is too far gone to fix. | 
|  | */ | 
|  | if (path->nr_steps >= XFS_MAXLINK) | 
|  | return -ELNRNG; | 
|  |  | 
|  | error = xfblob_storename(dl->path_names, &step.name_cookie, name); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | error = xino_bitmap_set(&path->seen_inodes, ip->i_ino); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | error = xfarray_append(dl->path_steps, &step); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | path->nr_steps++; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create an xchk_path for each parent pointer of the directory that we're | 
|  | * scanning.  For each path created, we will eventually try to walk towards the | 
|  | * root with the goal of deleting all parents except for one that leads to the | 
|  | * root. | 
|  | * | 
|  | * Returns -EFSCORRUPTED to signal that the inode being scanned has a corrupt | 
|  | * parent pointer and hence there's no point in continuing; or -ENOSR if there | 
|  | * are too many parent pointers for this directory. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirtree_create_path( | 
|  | struct xfs_scrub		*sc, | 
|  | struct xfs_inode		*ip, | 
|  | unsigned int			attr_flags, | 
|  | const unsigned char		*name, | 
|  | unsigned int			namelen, | 
|  | const void			*value, | 
|  | unsigned int			valuelen, | 
|  | void				*priv) | 
|  | { | 
|  | struct xfs_name			xname = { | 
|  | .name			= name, | 
|  | .len			= namelen, | 
|  | }; | 
|  | struct xchk_dirtree		*dl = priv; | 
|  | struct xchk_dirpath		*path; | 
|  | const struct xfs_parent_rec	*rec = value; | 
|  | int				error; | 
|  |  | 
|  | if (!(attr_flags & XFS_ATTR_PARENT)) | 
|  | return 0; | 
|  |  | 
|  | error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value, | 
|  | valuelen, NULL, NULL); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | /* | 
|  | * If there are more than 2 billion actual parent pointers for this | 
|  | * subdirectory, this fs is too far gone to fix. | 
|  | */ | 
|  | if (dl->nr_paths >= XFS_MAXLINK) | 
|  | return -ENOSR; | 
|  |  | 
|  | trace_xchk_dirtree_create_path(sc, ip, dl->nr_paths, &xname, rec); | 
|  |  | 
|  | /* | 
|  | * Create a new xchk_path structure to remember this parent pointer | 
|  | * and record the first name step. | 
|  | */ | 
|  | path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS); | 
|  | if (!path) | 
|  | return -ENOMEM; | 
|  |  | 
|  | INIT_LIST_HEAD(&path->list); | 
|  | xino_bitmap_init(&path->seen_inodes); | 
|  | path->nr_steps = 0; | 
|  | path->outcome = XCHK_DIRPATH_SCANNING; | 
|  |  | 
|  | error = xchk_dirpath_append(dl, sc->ip, path, &xname, rec); | 
|  | if (error) | 
|  | goto out_path; | 
|  |  | 
|  | path->first_step = xfarray_length(dl->path_steps) - 1; | 
|  | path->second_step = XFARRAY_NULLIDX; | 
|  | path->path_nr = dl->nr_paths; | 
|  |  | 
|  | list_add_tail(&path->list, &dl->path_list); | 
|  | dl->nr_paths++; | 
|  | return 0; | 
|  | out_path: | 
|  | kfree(path); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Validate that the first step of this path still has a corresponding | 
|  | * parent pointer in @sc->ip.  We probably dropped @sc->ip's ILOCK while | 
|  | * walking towards the roots, which is why this is necessary. | 
|  | * | 
|  | * This function has a side effect of loading the first parent pointer of this | 
|  | * path into the parent pointer scratch pad.  This prepares us to walk up the | 
|  | * directory tree towards the root.  Returns -ESTALE if the scan data is now | 
|  | * out of date. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_revalidate( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirpath		*path) | 
|  | { | 
|  | struct xfs_scrub		*sc = dl->sc; | 
|  | int				error; | 
|  |  | 
|  | /* | 
|  | * Look up the parent pointer that corresponds to the start of this | 
|  | * path.  If the parent pointer has disappeared on us, dump all the | 
|  | * scan results and try again. | 
|  | */ | 
|  | error = xfs_parent_lookup(sc->tp, sc->ip, &dl->xname, &dl->pptr_rec, | 
|  | &dl->pptr_args); | 
|  | if (error == -ENOATTR) { | 
|  | trace_xchk_dirpath_disappeared(dl->sc, sc->ip, path->path_nr, | 
|  | path->first_step, &dl->xname, &dl->pptr_rec); | 
|  | dl->stale = true; | 
|  | return -ESTALE; | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the parent pointers of a directory at the end of a path and record | 
|  | * the parent that we find in @dl->xname/pptr_rec. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_find_next_step( | 
|  | struct xfs_scrub		*sc, | 
|  | struct xfs_inode		*ip, | 
|  | unsigned int			attr_flags, | 
|  | const unsigned char		*name, | 
|  | unsigned int			namelen, | 
|  | const void			*value, | 
|  | unsigned int			valuelen, | 
|  | void				*priv) | 
|  | { | 
|  | struct xchk_dirtree		*dl = priv; | 
|  | const struct xfs_parent_rec	*rec = value; | 
|  | int				error; | 
|  |  | 
|  | if (!(attr_flags & XFS_ATTR_PARENT)) | 
|  | return 0; | 
|  |  | 
|  | error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value, | 
|  | valuelen, NULL, NULL); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | /* | 
|  | * If we've already set @dl->pptr_rec, then this directory has multiple | 
|  | * parents.  Signal this back to the caller via -EMLINK. | 
|  | */ | 
|  | if (dl->parents_found > 0) | 
|  | return -EMLINK; | 
|  |  | 
|  | dl->parents_found++; | 
|  | memcpy(dl->namebuf, name, namelen); | 
|  | dl->xname.len = namelen; | 
|  | dl->pptr_rec = *rec; /* struct copy */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Set and log the outcome of a path walk. */ | 
|  | static inline void | 
|  | xchk_dirpath_set_outcome( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirpath		*path, | 
|  | enum xchk_dirpath_outcome	outcome) | 
|  | { | 
|  | trace_xchk_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps, | 
|  | outcome); | 
|  |  | 
|  | path->outcome = outcome; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Scan the directory at the end of this path for its parent directory link. | 
|  | * If we find one, extend the path.  Returns -ESTALE if the scan data out of | 
|  | * date.  Returns -EFSCORRUPTED if the parent pointer is bad; or -ELNRNG if | 
|  | * the path got too deep. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_step_up( | 
|  | struct xchk_dirtree	*dl, | 
|  | struct xchk_dirpath	*path, | 
|  | bool			is_metadir) | 
|  | { | 
|  | struct xfs_scrub	*sc = dl->sc; | 
|  | struct xfs_inode	*dp; | 
|  | xfs_ino_t		parent_ino = be64_to_cpu(dl->pptr_rec.p_ino); | 
|  | unsigned int		lock_mode; | 
|  | int			error; | 
|  |  | 
|  | /* Grab and lock the parent directory. */ | 
|  | error = xchk_iget(sc, parent_ino, &dp); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | lock_mode = xfs_ilock_attr_map_shared(dp); | 
|  | mutex_lock(&dl->lock); | 
|  |  | 
|  | if (dl->stale) { | 
|  | error = -ESTALE; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* We've reached the root directory; the path is ok. */ | 
|  | if (parent_ino == dl->root_ino) { | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK); | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The inode being scanned is its own distant ancestor!  Get rid of | 
|  | * this path. | 
|  | */ | 
|  | if (parent_ino == sc->ip->i_ino) { | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE); | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We've seen this inode before during the path walk.  There's a loop | 
|  | * above us in the directory tree.  This probably means that we cannot | 
|  | * continue, but let's keep walking paths to get a full picture. | 
|  | */ | 
|  | if (xino_bitmap_test(&path->seen_inodes, parent_ino)) { | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_LOOP); | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* The handle encoded in the parent pointer must match. */ | 
|  | if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) { | 
|  | trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr, | 
|  | path->nr_steps, &dl->xname, &dl->pptr_rec); | 
|  | error = -EFSCORRUPTED; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* Parent pointer must point up to a directory. */ | 
|  | if (!S_ISDIR(VFS_I(dp)->i_mode)) { | 
|  | trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr, | 
|  | path->nr_steps, &dl->xname, &dl->pptr_rec); | 
|  | error = -EFSCORRUPTED; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* Parent cannot be an unlinked directory. */ | 
|  | if (VFS_I(dp)->i_nlink == 0) { | 
|  | trace_xchk_dirpath_unlinked_parent(dl->sc, dp, path->path_nr, | 
|  | path->nr_steps, &dl->xname, &dl->pptr_rec); | 
|  | error = -EFSCORRUPTED; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* Parent must be in the same directory tree. */ | 
|  | if (is_metadir != xfs_is_metadir_inode(dp)) { | 
|  | trace_xchk_dirpath_crosses_tree(dl->sc, dp, path->path_nr, | 
|  | path->nr_steps, &dl->xname, &dl->pptr_rec); | 
|  | error = -EFSCORRUPTED; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If the extended attributes look as though they has been zapped by | 
|  | * the inode record repair code, we cannot scan for parent pointers. | 
|  | */ | 
|  | if (xchk_pptr_looks_zapped(dp)) { | 
|  | error = -EBUSY; | 
|  | xchk_set_incomplete(sc); | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the parent pointers of @dp to find the parent of this directory | 
|  | * to find the next step in our walk.  If we find that @dp has exactly | 
|  | * one parent, the parent pointer information will be stored in | 
|  | * @dl->pptr_rec.  This prepares us for the next step of the walk. | 
|  | */ | 
|  | mutex_unlock(&dl->lock); | 
|  | dl->parents_found = 0; | 
|  | error = xchk_xattr_walk(sc, dp, xchk_dirpath_find_next_step, NULL, dl); | 
|  | mutex_lock(&dl->lock); | 
|  | if (error == -EFSCORRUPTED || error == -EMLINK || | 
|  | (!error && dl->parents_found == 0)) { | 
|  | /* | 
|  | * Further up the directory tree from @sc->ip, we found a | 
|  | * corrupt parent pointer, multiple parent pointers while | 
|  | * finding this directory's parent, or zero parents despite | 
|  | * having a nonzero link count.  Keep looking for other paths. | 
|  | */ | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT); | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  | if (error) | 
|  | goto out_scanlock; | 
|  |  | 
|  | if (dl->stale) { | 
|  | error = -ESTALE; | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | trace_xchk_dirpath_found_next_step(sc, dp, path->path_nr, | 
|  | path->nr_steps, &dl->xname, &dl->pptr_rec); | 
|  |  | 
|  | /* Append to the path steps */ | 
|  | error = xchk_dirpath_append(dl, dp, path, &dl->xname, &dl->pptr_rec); | 
|  | if (error) | 
|  | goto out_scanlock; | 
|  |  | 
|  | if (path->second_step == XFARRAY_NULLIDX) | 
|  | path->second_step = xfarray_length(dl->path_steps) - 1; | 
|  |  | 
|  | out_scanlock: | 
|  | mutex_unlock(&dl->lock); | 
|  | xfs_iunlock(dp, lock_mode); | 
|  | xchk_irele(sc, dp); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the directory tree upwards towards what is hopefully the root | 
|  | * directory, recording path steps as we go.  The current path components are | 
|  | * stored in dl->pptr_rec and dl->xname. | 
|  | * | 
|  | * Returns -ESTALE if the scan data are out of date.  Returns -EFSCORRUPTED | 
|  | * only if the direct parent pointer of @sc->ip associated with this path is | 
|  | * corrupt. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_walk_upwards( | 
|  | struct xchk_dirtree	*dl, | 
|  | struct xchk_dirpath	*path) | 
|  | { | 
|  | struct xfs_scrub	*sc = dl->sc; | 
|  | bool			is_metadir; | 
|  | int			error; | 
|  |  | 
|  | ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL); | 
|  |  | 
|  | /* Reload the start of this path and make sure it's still there. */ | 
|  | error = xchk_dirpath_revalidate(dl, path); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | trace_xchk_dirpath_walk_upwards(sc, sc->ip, path->path_nr, &dl->xname, | 
|  | &dl->pptr_rec); | 
|  |  | 
|  | /* | 
|  | * The inode being scanned is its own direct ancestor! | 
|  | * Get rid of this path. | 
|  | */ | 
|  | if (be64_to_cpu(dl->pptr_rec.p_ino) == sc->ip->i_ino) { | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Drop ILOCK_EXCL on the inode being scanned.  We still hold | 
|  | * IOLOCK_EXCL on it, so it cannot move around or be renamed. | 
|  | * | 
|  | * Beyond this point we're walking up the directory tree, which means | 
|  | * that we can acquire and drop the ILOCK on an alias of sc->ip.  The | 
|  | * ILOCK state is no longer tracked in the scrub context.  Hence we | 
|  | * must drop @sc->ip's ILOCK during the walk. | 
|  | */ | 
|  | is_metadir = xfs_is_metadir_inode(sc->ip); | 
|  | mutex_unlock(&dl->lock); | 
|  | xchk_iunlock(sc, XFS_ILOCK_EXCL); | 
|  |  | 
|  | /* | 
|  | * Take the first step in the walk towards the root by checking the | 
|  | * start of this path, which is a direct parent pointer of @sc->ip. | 
|  | * If we see any kind of error here (including corruptions), the parent | 
|  | * pointer of @sc->ip is corrupt.  Stop the whole scan. | 
|  | */ | 
|  | error = xchk_dirpath_step_up(dl, path, is_metadir); | 
|  | if (error) { | 
|  | xchk_ilock(sc, XFS_ILOCK_EXCL); | 
|  | mutex_lock(&dl->lock); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Take steps upward from the second step in this path towards the | 
|  | * root.  If we hit corruption errors here, there's a problem | 
|  | * *somewhere* in the path, but we don't need to stop scanning. | 
|  | */ | 
|  | while (!error && path->outcome == XCHK_DIRPATH_SCANNING) | 
|  | error = xchk_dirpath_step_up(dl, path, is_metadir); | 
|  |  | 
|  | /* Retake the locks we had, mark paths, etc. */ | 
|  | xchk_ilock(sc, XFS_ILOCK_EXCL); | 
|  | mutex_lock(&dl->lock); | 
|  | if (error == -EFSCORRUPTED) { | 
|  | xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT); | 
|  | error = 0; | 
|  | } | 
|  | if (!error && dl->stale) | 
|  | return -ESTALE; | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Decide if this path step has been touched by this live update.  Returns | 
|  | * 1 for yes, 0 for no, or a negative errno. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_step_is_stale( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirpath		*path, | 
|  | unsigned int			step_nr, | 
|  | xfarray_idx_t			step_idx, | 
|  | struct xfs_dir_update_params	*p, | 
|  | xfs_ino_t			*cursor) | 
|  | { | 
|  | struct xchk_dirpath_step	step; | 
|  | xfs_ino_t			child_ino = *cursor; | 
|  | int				error; | 
|  |  | 
|  | error = xfarray_load(dl->path_steps, step_idx, &step); | 
|  | if (error) | 
|  | return error; | 
|  | *cursor = be64_to_cpu(step.pptr_rec.p_ino); | 
|  |  | 
|  | /* | 
|  | * If the parent and child being updated are not the ones mentioned in | 
|  | * this path step, the scan data is still ok. | 
|  | */ | 
|  | if (p->ip->i_ino != child_ino || p->dp->i_ino != *cursor) | 
|  | return 0; | 
|  |  | 
|  | /* | 
|  | * If the dirent name lengths or byte sequences are different, the scan | 
|  | * data is still ok. | 
|  | */ | 
|  | if (p->name->len != step.name_len) | 
|  | return 0; | 
|  |  | 
|  | error = xfblob_loadname(dl->path_names, step.name_cookie, | 
|  | &dl->hook_xname, step.name_len); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0) | 
|  | return 0; | 
|  |  | 
|  | /* | 
|  | * If the update comes from the repair code itself, walk the state | 
|  | * machine forward. | 
|  | */ | 
|  | if (p->ip->i_ino == dl->scan_ino && | 
|  | path->outcome == XREP_DIRPATH_ADOPTING) { | 
|  | xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_ADOPTED); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (p->ip->i_ino == dl->scan_ino && | 
|  | path->outcome == XREP_DIRPATH_DELETING) { | 
|  | xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETED); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Exact match, scan data is out of date. */ | 
|  | trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp, | 
|  | p->ip, p->name); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Decide if this path has been touched by this live update.  Returns 1 for | 
|  | * yes, 0 for no, or a negative errno. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirpath_is_stale( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirpath		*path, | 
|  | struct xfs_dir_update_params	*p) | 
|  | { | 
|  | xfs_ino_t			cursor = dl->scan_ino; | 
|  | xfarray_idx_t			idx = path->first_step; | 
|  | unsigned int			i; | 
|  | int				ret; | 
|  |  | 
|  | /* | 
|  | * The child being updated has not been seen by this path at all; this | 
|  | * path cannot be stale. | 
|  | */ | 
|  | if (!xino_bitmap_test(&path->seen_inodes, p->ip->i_ino)) | 
|  | return 0; | 
|  |  | 
|  | ret = xchk_dirpath_step_is_stale(dl, path, 0, idx, p, &cursor); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  |  | 
|  | for (i = 1, idx = path->second_step; i < path->nr_steps; i++, idx++) { | 
|  | ret = xchk_dirpath_step_is_stale(dl, path, i, idx, p, &cursor); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Decide if a directory update from the regular filesystem touches any of the | 
|  | * paths we've scanned, and invalidate the scan data if true. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirtree_live_update( | 
|  | struct notifier_block		*nb, | 
|  | unsigned long			action, | 
|  | void				*data) | 
|  | { | 
|  | struct xfs_dir_update_params	*p = data; | 
|  | struct xchk_dirtree		*dl; | 
|  | struct xchk_dirpath		*path; | 
|  | int				ret; | 
|  |  | 
|  | dl = container_of(nb, struct xchk_dirtree, dhook.dirent_hook.nb); | 
|  |  | 
|  | trace_xchk_dirtree_live_update(dl->sc, p->dp, action, p->ip, p->delta, | 
|  | p->name); | 
|  |  | 
|  | mutex_lock(&dl->lock); | 
|  |  | 
|  | if (dl->stale || dl->aborted) | 
|  | goto out_unlock; | 
|  |  | 
|  | xchk_dirtree_for_each_path(dl, path) { | 
|  | ret = xchk_dirpath_is_stale(dl, path, p); | 
|  | if (ret < 0) { | 
|  | dl->aborted = true; | 
|  | break; | 
|  | } | 
|  | if (ret == 1) { | 
|  | dl->stale = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | out_unlock: | 
|  | mutex_unlock(&dl->lock); | 
|  | return NOTIFY_DONE; | 
|  | } | 
|  |  | 
|  | /* Delete all the collected path information. */ | 
|  | STATIC void | 
|  | xchk_dirtree_reset( | 
|  | void			*buf) | 
|  | { | 
|  | struct xchk_dirtree	*dl = buf; | 
|  | struct xchk_dirpath	*path, *n; | 
|  |  | 
|  | ASSERT(dl->sc->ilock_flags & XFS_ILOCK_EXCL); | 
|  |  | 
|  | xchk_dirtree_for_each_path_safe(dl, path, n) { | 
|  | list_del_init(&path->list); | 
|  | xino_bitmap_destroy(&path->seen_inodes); | 
|  | kfree(path); | 
|  | } | 
|  | dl->nr_paths = 0; | 
|  |  | 
|  | xfarray_truncate(dl->path_steps); | 
|  | xfblob_truncate(dl->path_names); | 
|  |  | 
|  | dl->stale = false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Load the name/pptr from the first step in this path into @dl->pptr_rec and | 
|  | * @dl->xname. | 
|  | */ | 
|  | STATIC int | 
|  | xchk_dirtree_load_path( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirpath		*path) | 
|  | { | 
|  | struct xchk_dirpath_step	step; | 
|  | int				error; | 
|  |  | 
|  | error = xfarray_load(dl->path_steps, path->first_step, &step); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | error = xfblob_loadname(dl->path_names, step.name_cookie, &dl->xname, | 
|  | step.name_len); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | dl->pptr_rec = step.pptr_rec; /* struct copy */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * For each parent pointer of this subdir, trace a path upwards towards the | 
|  | * root directory and record what we find.  Returns 0 for success; | 
|  | * -EFSCORRUPTED if walking the parent pointers of @sc->ip failed, -ELNRNG if a | 
|  | * path was too deep; -ENOSR if there were too many parent pointers; or | 
|  | * a negative errno. | 
|  | */ | 
|  | int | 
|  | xchk_dirtree_find_paths_to_root( | 
|  | struct xchk_dirtree	*dl) | 
|  | { | 
|  | struct xfs_scrub	*sc = dl->sc; | 
|  | struct xchk_dirpath	*path; | 
|  | int			error = 0; | 
|  |  | 
|  | do { | 
|  | if (xchk_should_terminate(sc, &error)) | 
|  | return error; | 
|  |  | 
|  | xchk_dirtree_reset(dl); | 
|  |  | 
|  | /* | 
|  | * If the extended attributes look as though they has been | 
|  | * zapped by the inode record repair code, we cannot scan for | 
|  | * parent pointers. | 
|  | */ | 
|  | if (xchk_pptr_looks_zapped(sc->ip)) { | 
|  | xchk_set_incomplete(sc); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create path walk contexts for each parent of the directory | 
|  | * that is being scanned.  Directories are supposed to have | 
|  | * only one parent, but this is how we detect multiple parents. | 
|  | */ | 
|  | error = xchk_xattr_walk(sc, sc->ip, xchk_dirtree_create_path, | 
|  | NULL, dl); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | xchk_dirtree_for_each_path(dl, path) { | 
|  | /* Load path components into dl->pptr/xname */ | 
|  | error = xchk_dirtree_load_path(dl, path); | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | /* | 
|  | * Try to walk up each path to the root.  This enables | 
|  | * us to find directory loops in ancestors, and the | 
|  | * like. | 
|  | */ | 
|  | error = xchk_dirpath_walk_upwards(dl, path); | 
|  | if (error == -EFSCORRUPTED) { | 
|  | /* | 
|  | * A parent pointer of @sc->ip is bad, don't | 
|  | * bother continuing. | 
|  | */ | 
|  | break; | 
|  | } | 
|  | if (error == -ESTALE) { | 
|  | /* This had better be an invalidation. */ | 
|  | ASSERT(dl->stale); | 
|  | break; | 
|  | } | 
|  | if (error) | 
|  | return error; | 
|  | if (dl->aborted) | 
|  | return 0; | 
|  | } | 
|  | } while (dl->stale); | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Figure out what to do with the paths we tried to find.  Do not call this | 
|  | * if the scan results are stale. | 
|  | */ | 
|  | void | 
|  | xchk_dirtree_evaluate( | 
|  | struct xchk_dirtree		*dl, | 
|  | struct xchk_dirtree_outcomes	*oc) | 
|  | { | 
|  | struct xchk_dirpath		*path; | 
|  |  | 
|  | ASSERT(!dl->stale); | 
|  |  | 
|  | /* Scan the paths we have to decide what to do. */ | 
|  | memset(oc, 0, sizeof(struct xchk_dirtree_outcomes)); | 
|  | xchk_dirtree_for_each_path(dl, path) { | 
|  | trace_xchk_dirpath_evaluate_path(dl->sc, path->path_nr, | 
|  | path->nr_steps, path->outcome); | 
|  |  | 
|  | switch (path->outcome) { | 
|  | case XCHK_DIRPATH_SCANNING: | 
|  | /* shouldn't get here */ | 
|  | ASSERT(0); | 
|  | break; | 
|  | case XCHK_DIRPATH_DELETE: | 
|  | /* This one is already going away. */ | 
|  | oc->bad++; | 
|  | break; | 
|  | case XCHK_DIRPATH_CORRUPT: | 
|  | case XCHK_DIRPATH_LOOP: | 
|  | /* Couldn't find the end of this path. */ | 
|  | oc->suspect++; | 
|  | break; | 
|  | case XCHK_DIRPATH_STALE: | 
|  | /* shouldn't get here either */ | 
|  | ASSERT(0); | 
|  | break; | 
|  | case XCHK_DIRPATH_OK: | 
|  | /* This path got all the way to the root. */ | 
|  | oc->good++; | 
|  | break; | 
|  | case XREP_DIRPATH_DELETING: | 
|  | case XREP_DIRPATH_DELETED: | 
|  | case XREP_DIRPATH_ADOPTING: | 
|  | case XREP_DIRPATH_ADOPTED: | 
|  | /* These should not be in progress! */ | 
|  | ASSERT(0); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | trace_xchk_dirtree_evaluate(dl, oc); | 
|  | } | 
|  |  | 
|  | /* Look for directory loops. */ | 
|  | int | 
|  | xchk_dirtree( | 
|  | struct xfs_scrub		*sc) | 
|  | { | 
|  | struct xchk_dirtree_outcomes	oc; | 
|  | struct xchk_dirtree		*dl = sc->buf; | 
|  | int				error; | 
|  |  | 
|  | /* | 
|  | * Nondirectories do not point downwards to other files, so they cannot | 
|  | * cause a cycle in the directory tree. | 
|  | */ | 
|  | if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) | 
|  | return -ENOENT; | 
|  |  | 
|  | ASSERT(xfs_has_parent(sc->mp)); | 
|  |  | 
|  | /* | 
|  | * Find the root of the directory tree.  Remember which directory to | 
|  | * scan, because the hook doesn't detach until after sc->ip gets | 
|  | * released during teardown. | 
|  | */ | 
|  | dl->root_ino = xchk_inode_rootdir_inum(sc->ip); | 
|  | dl->scan_ino = sc->ip->i_ino; | 
|  |  | 
|  | trace_xchk_dirtree_start(sc->ip, sc->sm, 0); | 
|  |  | 
|  | /* | 
|  | * Hook into the directory entry code so that we can capture updates to | 
|  | * paths that we have already scanned.  The scanner thread takes each | 
|  | * directory's ILOCK, which means that any in-progress directory update | 
|  | * will finish before we can scan the directory. | 
|  | */ | 
|  | ASSERT(sc->flags & XCHK_FSGATES_DIRENTS); | 
|  | xfs_dir_hook_setup(&dl->dhook, xchk_dirtree_live_update); | 
|  | error = xfs_dir_hook_add(sc->mp, &dl->dhook); | 
|  | if (error) | 
|  | goto out; | 
|  |  | 
|  | mutex_lock(&dl->lock); | 
|  |  | 
|  | /* Trace each parent pointer's path to the root. */ | 
|  | error = xchk_dirtree_find_paths_to_root(dl); | 
|  | if (error == -EFSCORRUPTED || error == -ELNRNG || error == -ENOSR) { | 
|  | /* | 
|  | * Don't bother walking the paths if the xattr structure or the | 
|  | * parent pointers are corrupt; this scan cannot be completed | 
|  | * without full information. | 
|  | */ | 
|  | xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  | if (error == -EBUSY) { | 
|  | /* | 
|  | * We couldn't scan some directory's parent pointers because | 
|  | * the attr fork looked like it had been zapped.  The | 
|  | * scan was marked incomplete, so no further error code | 
|  | * is necessary. | 
|  | */ | 
|  | error = 0; | 
|  | goto out_scanlock; | 
|  | } | 
|  | if (error) | 
|  | goto out_scanlock; | 
|  | if (dl->aborted) { | 
|  | xchk_set_incomplete(sc); | 
|  | goto out_scanlock; | 
|  | } | 
|  |  | 
|  | /* Assess what we found in our path evaluation. */ | 
|  | xchk_dirtree_evaluate(dl, &oc); | 
|  | if (xchk_dirtree_parentless(dl)) { | 
|  | if (oc.good || oc.bad || oc.suspect) | 
|  | xchk_ino_set_corrupt(sc, sc->ip->i_ino); | 
|  | } else { | 
|  | if (oc.bad || oc.good + oc.suspect != 1) | 
|  | xchk_ino_set_corrupt(sc, sc->ip->i_ino); | 
|  | if (oc.suspect) | 
|  | xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); | 
|  | } | 
|  |  | 
|  | out_scanlock: | 
|  | mutex_unlock(&dl->lock); | 
|  | out: | 
|  | trace_xchk_dirtree_done(sc->ip, sc->sm, error); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* Does the directory targetted by this scrub have no parents? */ | 
|  | bool | 
|  | xchk_dirtree_parentless(const struct xchk_dirtree *dl) | 
|  | { | 
|  | struct xfs_scrub	*sc = dl->sc; | 
|  |  | 
|  | if (xchk_inode_is_dirtree_root(sc->ip)) | 
|  | return true; | 
|  | if (VFS_I(sc->ip)->i_nlink == 0) | 
|  | return true; | 
|  | return false; | 
|  | } |