brintos

brintos / linux-shallow public Read only

0
0
Text · 25.4 KiB · bde58fb Raw
986 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_log_format.h"13#include "xfs_trans.h"14#include "xfs_inode.h"15#include "xfs_icache.h"16#include "xfs_dir2.h"17#include "xfs_dir2_priv.h"18#include "xfs_attr.h"19#include "xfs_parent.h"20#include "scrub/scrub.h"21#include "scrub/common.h"22#include "scrub/bitmap.h"23#include "scrub/ino_bitmap.h"24#include "scrub/xfile.h"25#include "scrub/xfarray.h"26#include "scrub/xfblob.h"27#include "scrub/listxattr.h"28#include "scrub/trace.h"29#include "scrub/repair.h"30#include "scrub/orphanage.h"31#include "scrub/dirtree.h"32 33/*34 * Directory Tree Structure Validation35 * ===================================36 *37 * Validating the tree qualities of the directory tree structure can be38 * difficult.  If the tree is frozen, running a depth (or breadth) first search39 * and marking a bitmap suffices to determine if there is a cycle.  XORing the40 * mark bitmap with the inode bitmap afterwards tells us if there are41 * disconnected cycles.  If the tree is not frozen, directory updates can move42 * subtrees across the scanner wavefront, which complicates the design greatly.43 *44 * Directory parent pointers change that by enabling an incremental approach to45 * validation of the tree structure.  Instead of using one thread to scan the46 * entire filesystem, we instead can have multiple threads walking individual47 * subdirectories upwards to the root.  In a perfect world, the IOLOCK would48 * suffice to stabilize two directories in a parent -> child relationship.49 * Unfortunately, the VFS does not take the IOLOCK when moving a child50 * subdirectory, so we instead synchronize on ILOCK and use dirent update hooks51 * to detect a race.  If a race occurs in a path, we restart the scan.52 *53 * If the walk terminates without reaching the root, we know the path is54 * disconnected and ought to be attached to the lost and found.  If on the walk55 * we find the same subdir that we're scanning, we know this is a cycle and56 * should delete an incoming edge.  If we find multiple paths to the root, we57 * know to delete an incoming edge.58 *59 * There are two big hitches with this approach: first, all file link counts60 * must be correct to prevent other writers from doing the wrong thing with the61 * directory tree structure.  Second, because we're walking upwards in a tree62 * of arbitrary depth, we cannot hold all the ILOCKs.  Instead, we will use a63 * directory update hook to invalidate the scan results if one of the paths64 * we've scanned has changed.65 */66 67/* Clean up the dirtree checking resources. */68STATIC void69xchk_dirtree_buf_cleanup(70	void			*buf)71{72	struct xchk_dirtree	*dl = buf;73	struct xchk_dirpath	*path, *n;74 75	if (dl->scan_ino != NULLFSINO)76		xfs_dir_hook_del(dl->sc->mp, &dl->dhook);77 78	xchk_dirtree_for_each_path_safe(dl, path, n) {79		list_del_init(&path->list);80		xino_bitmap_destroy(&path->seen_inodes);81		kfree(path);82	}83 84	xfblob_destroy(dl->path_names);85	xfarray_destroy(dl->path_steps);86	mutex_destroy(&dl->lock);87}88 89/* Set us up to look for directory loops. */90int91xchk_setup_dirtree(92	struct xfs_scrub	*sc)93{94	struct xchk_dirtree	*dl;95	char			*descr;96	int			error;97 98	xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);99 100	if (xchk_could_repair(sc)) {101		error = xrep_setup_dirtree(sc);102		if (error)103			return error;104	}105 106	dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS);107	if (!dl)108		return -ENOMEM;109	dl->sc = sc;110	dl->xname.name = dl->namebuf;111	dl->hook_xname.name = dl->hook_namebuf;112	INIT_LIST_HEAD(&dl->path_list);113	dl->root_ino = NULLFSINO;114	dl->scan_ino = NULLFSINO;115	dl->parent_ino = NULLFSINO;116 117	mutex_init(&dl->lock);118 119	descr = xchk_xfile_ino_descr(sc, "dirtree path steps");120	error = xfarray_create(descr, 0, sizeof(struct xchk_dirpath_step),121			&dl->path_steps);122	kfree(descr);123	if (error)124		goto out_dl;125 126	descr = xchk_xfile_ino_descr(sc, "dirtree path names");127	error = xfblob_create(descr, &dl->path_names);128	kfree(descr);129	if (error)130		goto out_steps;131 132	error = xchk_setup_inode_contents(sc, 0);133	if (error)134		goto out_names;135 136	sc->buf = dl;137	sc->buf_cleanup = xchk_dirtree_buf_cleanup;138	return 0;139 140out_names:141	xfblob_destroy(dl->path_names);142out_steps:143	xfarray_destroy(dl->path_steps);144out_dl:145	mutex_destroy(&dl->lock);146	kvfree(dl);147	return error;148}149 150/*151 * Add the parent pointer described by @dl->pptr to the given path as a new152 * step.  Returns -ELNRNG if the path is too deep.153 */154int155xchk_dirpath_append(156	struct xchk_dirtree		*dl,157	struct xfs_inode		*ip,158	struct xchk_dirpath		*path,159	const struct xfs_name		*name,160	const struct xfs_parent_rec	*pptr)161{162	struct xchk_dirpath_step	step = {163		.pptr_rec		= *pptr, /* struct copy */164		.name_len		= name->len,165	};166	int				error;167 168	/*169	 * If this path is more than 2 billion steps long, this directory tree170	 * is too far gone to fix.171	 */172	if (path->nr_steps >= XFS_MAXLINK)173		return -ELNRNG;174 175	error = xfblob_storename(dl->path_names, &step.name_cookie, name);176	if (error)177		return error;178 179	error = xino_bitmap_set(&path->seen_inodes, ip->i_ino);180	if (error)181		return error;182 183	error = xfarray_append(dl->path_steps, &step);184	if (error)185		return error;186 187	path->nr_steps++;188	return 0;189}190 191/*192 * Create an xchk_path for each parent pointer of the directory that we're193 * scanning.  For each path created, we will eventually try to walk towards the194 * root with the goal of deleting all parents except for one that leads to the195 * root.196 *197 * Returns -EFSCORRUPTED to signal that the inode being scanned has a corrupt198 * parent pointer and hence there's no point in continuing; or -ENOSR if there199 * are too many parent pointers for this directory.200 */201STATIC int202xchk_dirtree_create_path(203	struct xfs_scrub		*sc,204	struct xfs_inode		*ip,205	unsigned int			attr_flags,206	const unsigned char		*name,207	unsigned int			namelen,208	const void			*value,209	unsigned int			valuelen,210	void				*priv)211{212	struct xfs_name			xname = {213		.name			= name,214		.len			= namelen,215	};216	struct xchk_dirtree		*dl = priv;217	struct xchk_dirpath		*path;218	const struct xfs_parent_rec	*rec = value;219	int				error;220 221	if (!(attr_flags & XFS_ATTR_PARENT))222		return 0;223 224	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,225			valuelen, NULL, NULL);226	if (error)227		return error;228 229	/*230	 * If there are more than 2 billion actual parent pointers for this231	 * subdirectory, this fs is too far gone to fix.232	 */233	if (dl->nr_paths >= XFS_MAXLINK)234		return -ENOSR;235 236	trace_xchk_dirtree_create_path(sc, ip, dl->nr_paths, &xname, rec);237 238	/*239	 * Create a new xchk_path structure to remember this parent pointer240	 * and record the first name step.241	 */242	path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS);243	if (!path)244		return -ENOMEM;245 246	INIT_LIST_HEAD(&path->list);247	xino_bitmap_init(&path->seen_inodes);248	path->nr_steps = 0;249	path->outcome = XCHK_DIRPATH_SCANNING;250 251	error = xchk_dirpath_append(dl, sc->ip, path, &xname, rec);252	if (error)253		goto out_path;254 255	path->first_step = xfarray_length(dl->path_steps) - 1;256	path->second_step = XFARRAY_NULLIDX;257	path->path_nr = dl->nr_paths;258 259	list_add_tail(&path->list, &dl->path_list);260	dl->nr_paths++;261	return 0;262out_path:263	kfree(path);264	return error;265}266 267/*268 * Validate that the first step of this path still has a corresponding269 * parent pointer in @sc->ip.  We probably dropped @sc->ip's ILOCK while270 * walking towards the roots, which is why this is necessary.271 *272 * This function has a side effect of loading the first parent pointer of this273 * path into the parent pointer scratch pad.  This prepares us to walk up the274 * directory tree towards the root.  Returns -ESTALE if the scan data is now275 * out of date.276 */277STATIC int278xchk_dirpath_revalidate(279	struct xchk_dirtree		*dl,280	struct xchk_dirpath		*path)281{282	struct xfs_scrub		*sc = dl->sc;283	int				error;284 285	/*286	 * Look up the parent pointer that corresponds to the start of this287	 * path.  If the parent pointer has disappeared on us, dump all the288	 * scan results and try again.289	 */290	error = xfs_parent_lookup(sc->tp, sc->ip, &dl->xname, &dl->pptr_rec,291			&dl->pptr_args);292	if (error == -ENOATTR) {293		trace_xchk_dirpath_disappeared(dl->sc, sc->ip, path->path_nr,294				path->first_step, &dl->xname, &dl->pptr_rec);295		dl->stale = true;296		return -ESTALE;297	}298 299	return error;300}301 302/*303 * Walk the parent pointers of a directory at the end of a path and record304 * the parent that we find in @dl->xname/pptr_rec.305 */306STATIC int307xchk_dirpath_find_next_step(308	struct xfs_scrub		*sc,309	struct xfs_inode		*ip,310	unsigned int			attr_flags,311	const unsigned char		*name,312	unsigned int			namelen,313	const void			*value,314	unsigned int			valuelen,315	void				*priv)316{317	struct xchk_dirtree		*dl = priv;318	const struct xfs_parent_rec	*rec = value;319	int				error;320 321	if (!(attr_flags & XFS_ATTR_PARENT))322		return 0;323 324	error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,325			valuelen, NULL, NULL);326	if (error)327		return error;328 329	/*330	 * If we've already set @dl->pptr_rec, then this directory has multiple331	 * parents.  Signal this back to the caller via -EMLINK.332	 */333	if (dl->parents_found > 0)334		return -EMLINK;335 336	dl->parents_found++;337	memcpy(dl->namebuf, name, namelen);338	dl->xname.len = namelen;339	dl->pptr_rec = *rec; /* struct copy */340	return 0;341}342 343/* Set and log the outcome of a path walk. */344static inline void345xchk_dirpath_set_outcome(346	struct xchk_dirtree		*dl,347	struct xchk_dirpath		*path,348	enum xchk_dirpath_outcome	outcome)349{350	trace_xchk_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps,351			outcome);352 353	path->outcome = outcome;354}355 356/*357 * Scan the directory at the end of this path for its parent directory link.358 * If we find one, extend the path.  Returns -ESTALE if the scan data out of359 * date.  Returns -EFSCORRUPTED if the parent pointer is bad; or -ELNRNG if360 * the path got too deep.361 */362STATIC int363xchk_dirpath_step_up(364	struct xchk_dirtree	*dl,365	struct xchk_dirpath	*path)366{367	struct xfs_scrub	*sc = dl->sc;368	struct xfs_inode	*dp;369	xfs_ino_t		parent_ino = be64_to_cpu(dl->pptr_rec.p_ino);370	unsigned int		lock_mode;371	int			error;372 373	/* Grab and lock the parent directory. */374	error = xchk_iget(sc, parent_ino, &dp);375	if (error)376		return error;377 378	lock_mode = xfs_ilock_attr_map_shared(dp);379	mutex_lock(&dl->lock);380 381	if (dl->stale) {382		error = -ESTALE;383		goto out_scanlock;384	}385 386	/* We've reached the root directory; the path is ok. */387	if (parent_ino == dl->root_ino) {388		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_OK);389		error = 0;390		goto out_scanlock;391	}392 393	/*394	 * The inode being scanned is its own distant ancestor!  Get rid of395	 * this path.396	 */397	if (parent_ino == sc->ip->i_ino) {398		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);399		error = 0;400		goto out_scanlock;401	}402 403	/*404	 * We've seen this inode before during the path walk.  There's a loop405	 * above us in the directory tree.  This probably means that we cannot406	 * continue, but let's keep walking paths to get a full picture.407	 */408	if (xino_bitmap_test(&path->seen_inodes, parent_ino)) {409		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_LOOP);410		error = 0;411		goto out_scanlock;412	}413 414	/* The handle encoded in the parent pointer must match. */415	if (VFS_I(dp)->i_generation != be32_to_cpu(dl->pptr_rec.p_gen)) {416		trace_xchk_dirpath_badgen(dl->sc, dp, path->path_nr,417				path->nr_steps, &dl->xname, &dl->pptr_rec);418		error = -EFSCORRUPTED;419		goto out_scanlock;420	}421 422	/* Parent pointer must point up to a directory. */423	if (!S_ISDIR(VFS_I(dp)->i_mode)) {424		trace_xchk_dirpath_nondir_parent(dl->sc, dp, path->path_nr,425				path->nr_steps, &dl->xname, &dl->pptr_rec);426		error = -EFSCORRUPTED;427		goto out_scanlock;428	}429 430	/* Parent cannot be an unlinked directory. */431	if (VFS_I(dp)->i_nlink == 0) {432		trace_xchk_dirpath_unlinked_parent(dl->sc, dp, path->path_nr,433				path->nr_steps, &dl->xname, &dl->pptr_rec);434		error = -EFSCORRUPTED;435		goto out_scanlock;436	}437 438	/*439	 * If the extended attributes look as though they has been zapped by440	 * the inode record repair code, we cannot scan for parent pointers.441	 */442	if (xchk_pptr_looks_zapped(dp)) {443		error = -EBUSY;444		xchk_set_incomplete(sc);445		goto out_scanlock;446	}447 448	/*449	 * Walk the parent pointers of @dp to find the parent of this directory450	 * to find the next step in our walk.  If we find that @dp has exactly451	 * one parent, the parent pointer information will be stored in452	 * @dl->pptr_rec.  This prepares us for the next step of the walk.453	 */454	mutex_unlock(&dl->lock);455	dl->parents_found = 0;456	error = xchk_xattr_walk(sc, dp, xchk_dirpath_find_next_step, NULL, dl);457	mutex_lock(&dl->lock);458	if (error == -EFSCORRUPTED || error == -EMLINK ||459	    (!error && dl->parents_found == 0)) {460		/*461		 * Further up the directory tree from @sc->ip, we found a462		 * corrupt parent pointer, multiple parent pointers while463		 * finding this directory's parent, or zero parents despite464		 * having a nonzero link count.  Keep looking for other paths.465		 */466		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT);467		error = 0;468		goto out_scanlock;469	}470	if (error)471		goto out_scanlock;472 473	if (dl->stale) {474		error = -ESTALE;475		goto out_scanlock;476	}477 478	trace_xchk_dirpath_found_next_step(sc, dp, path->path_nr,479			path->nr_steps, &dl->xname, &dl->pptr_rec);480 481	/* Append to the path steps */482	error = xchk_dirpath_append(dl, dp, path, &dl->xname, &dl->pptr_rec);483	if (error)484		goto out_scanlock;485 486	if (path->second_step == XFARRAY_NULLIDX)487		path->second_step = xfarray_length(dl->path_steps) - 1;488 489out_scanlock:490	mutex_unlock(&dl->lock);491	xfs_iunlock(dp, lock_mode);492	xchk_irele(sc, dp);493	return error;494}495 496/*497 * Walk the directory tree upwards towards what is hopefully the root498 * directory, recording path steps as we go.  The current path components are499 * stored in dl->pptr_rec and dl->xname.500 *501 * Returns -ESTALE if the scan data are out of date.  Returns -EFSCORRUPTED502 * only if the direct parent pointer of @sc->ip associated with this path is503 * corrupt.504 */505STATIC int506xchk_dirpath_walk_upwards(507	struct xchk_dirtree	*dl,508	struct xchk_dirpath	*path)509{510	struct xfs_scrub	*sc = dl->sc;511	int			error;512 513	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);514 515	/* Reload the start of this path and make sure it's still there. */516	error = xchk_dirpath_revalidate(dl, path);517	if (error)518		return error;519 520	trace_xchk_dirpath_walk_upwards(sc, sc->ip, path->path_nr, &dl->xname,521			&dl->pptr_rec);522 523	/*524	 * The inode being scanned is its own direct ancestor!525	 * Get rid of this path.526	 */527	if (be64_to_cpu(dl->pptr_rec.p_ino) == sc->ip->i_ino) {528		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);529		return 0;530	}531 532	/*533	 * Drop ILOCK_EXCL on the inode being scanned.  We still hold534	 * IOLOCK_EXCL on it, so it cannot move around or be renamed.535	 *536	 * Beyond this point we're walking up the directory tree, which means537	 * that we can acquire and drop the ILOCK on an alias of sc->ip.  The538	 * ILOCK state is no longer tracked in the scrub context.  Hence we539	 * must drop @sc->ip's ILOCK during the walk.540	 */541	mutex_unlock(&dl->lock);542	xchk_iunlock(sc, XFS_ILOCK_EXCL);543 544	/*545	 * Take the first step in the walk towards the root by checking the546	 * start of this path, which is a direct parent pointer of @sc->ip.547	 * If we see any kind of error here (including corruptions), the parent548	 * pointer of @sc->ip is corrupt.  Stop the whole scan.549	 */550	error = xchk_dirpath_step_up(dl, path);551	if (error) {552		xchk_ilock(sc, XFS_ILOCK_EXCL);553		mutex_lock(&dl->lock);554		return error;555	}556 557	/*558	 * Take steps upward from the second step in this path towards the559	 * root.  If we hit corruption errors here, there's a problem560	 * *somewhere* in the path, but we don't need to stop scanning.561	 */562	while (!error && path->outcome == XCHK_DIRPATH_SCANNING)563		error = xchk_dirpath_step_up(dl, path);564 565	/* Retake the locks we had, mark paths, etc. */566	xchk_ilock(sc, XFS_ILOCK_EXCL);567	mutex_lock(&dl->lock);568	if (error == -EFSCORRUPTED) {569		xchk_dirpath_set_outcome(dl, path, XCHK_DIRPATH_CORRUPT);570		error = 0;571	}572	if (!error && dl->stale)573		return -ESTALE;574	return error;575}576 577/*578 * Decide if this path step has been touched by this live update.  Returns579 * 1 for yes, 0 for no, or a negative errno.580 */581STATIC int582xchk_dirpath_step_is_stale(583	struct xchk_dirtree		*dl,584	struct xchk_dirpath		*path,585	unsigned int			step_nr,586	xfarray_idx_t			step_idx,587	struct xfs_dir_update_params	*p,588	xfs_ino_t			*cursor)589{590	struct xchk_dirpath_step	step;591	xfs_ino_t			child_ino = *cursor;592	int				error;593 594	error = xfarray_load(dl->path_steps, step_idx, &step);595	if (error)596		return error;597	*cursor = be64_to_cpu(step.pptr_rec.p_ino);598 599	/*600	 * If the parent and child being updated are not the ones mentioned in601	 * this path step, the scan data is still ok.602	 */603	if (p->ip->i_ino != child_ino || p->dp->i_ino != *cursor)604		return 0;605 606	/*607	 * If the dirent name lengths or byte sequences are different, the scan608	 * data is still ok.609	 */610	if (p->name->len != step.name_len)611		return 0;612 613	error = xfblob_loadname(dl->path_names, step.name_cookie,614			&dl->hook_xname, step.name_len);615	if (error)616		return error;617 618	if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0)619		return 0;620 621	/*622	 * If the update comes from the repair code itself, walk the state623	 * machine forward.624	 */625	if (p->ip->i_ino == dl->scan_ino &&626	    path->outcome == XREP_DIRPATH_ADOPTING) {627		xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_ADOPTED);628		return 0;629	}630 631	if (p->ip->i_ino == dl->scan_ino &&632	    path->outcome == XREP_DIRPATH_DELETING) {633		xchk_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETED);634		return 0;635	}636 637	/* Exact match, scan data is out of date. */638	trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp,639			p->ip, p->name);640	return 1;641}642 643/*644 * Decide if this path has been touched by this live update.  Returns 1 for645 * yes, 0 for no, or a negative errno.646 */647STATIC int648xchk_dirpath_is_stale(649	struct xchk_dirtree		*dl,650	struct xchk_dirpath		*path,651	struct xfs_dir_update_params	*p)652{653	xfs_ino_t			cursor = dl->scan_ino;654	xfarray_idx_t			idx = path->first_step;655	unsigned int			i;656	int				ret;657 658	/*659	 * The child being updated has not been seen by this path at all; this660	 * path cannot be stale.661	 */662	if (!xino_bitmap_test(&path->seen_inodes, p->ip->i_ino))663		return 0;664 665	ret = xchk_dirpath_step_is_stale(dl, path, 0, idx, p, &cursor);666	if (ret != 0)667		return ret;668 669	for (i = 1, idx = path->second_step; i < path->nr_steps; i++, idx++) {670		ret = xchk_dirpath_step_is_stale(dl, path, i, idx, p, &cursor);671		if (ret != 0)672			return ret;673	}674 675	return 0;676}677 678/*679 * Decide if a directory update from the regular filesystem touches any of the680 * paths we've scanned, and invalidate the scan data if true.681 */682STATIC int683xchk_dirtree_live_update(684	struct notifier_block		*nb,685	unsigned long			action,686	void				*data)687{688	struct xfs_dir_update_params	*p = data;689	struct xchk_dirtree		*dl;690	struct xchk_dirpath		*path;691	int				ret;692 693	dl = container_of(nb, struct xchk_dirtree, dhook.dirent_hook.nb);694 695	trace_xchk_dirtree_live_update(dl->sc, p->dp, action, p->ip, p->delta,696			p->name);697 698	mutex_lock(&dl->lock);699 700	if (dl->stale || dl->aborted)701		goto out_unlock;702 703	xchk_dirtree_for_each_path(dl, path) {704		ret = xchk_dirpath_is_stale(dl, path, p);705		if (ret < 0) {706			dl->aborted = true;707			break;708		}709		if (ret == 1) {710			dl->stale = true;711			break;712		}713	}714 715out_unlock:716	mutex_unlock(&dl->lock);717	return NOTIFY_DONE;718}719 720/* Delete all the collected path information. */721STATIC void722xchk_dirtree_reset(723	void			*buf)724{725	struct xchk_dirtree	*dl = buf;726	struct xchk_dirpath	*path, *n;727 728	ASSERT(dl->sc->ilock_flags & XFS_ILOCK_EXCL);729 730	xchk_dirtree_for_each_path_safe(dl, path, n) {731		list_del_init(&path->list);732		xino_bitmap_destroy(&path->seen_inodes);733		kfree(path);734	}735	dl->nr_paths = 0;736 737	xfarray_truncate(dl->path_steps);738	xfblob_truncate(dl->path_names);739 740	dl->stale = false;741}742 743/*744 * Load the name/pptr from the first step in this path into @dl->pptr_rec and745 * @dl->xname.746 */747STATIC int748xchk_dirtree_load_path(749	struct xchk_dirtree		*dl,750	struct xchk_dirpath		*path)751{752	struct xchk_dirpath_step	step;753	int				error;754 755	error = xfarray_load(dl->path_steps, path->first_step, &step);756	if (error)757		return error;758 759	error = xfblob_loadname(dl->path_names, step.name_cookie, &dl->xname,760			step.name_len);761	if (error)762		return error;763 764	dl->pptr_rec = step.pptr_rec; /* struct copy */765	return 0;766}767 768/*769 * For each parent pointer of this subdir, trace a path upwards towards the770 * root directory and record what we find.  Returns 0 for success;771 * -EFSCORRUPTED if walking the parent pointers of @sc->ip failed, -ELNRNG if a772 * path was too deep; -ENOSR if there were too many parent pointers; or773 * a negative errno.774 */775int776xchk_dirtree_find_paths_to_root(777	struct xchk_dirtree	*dl)778{779	struct xfs_scrub	*sc = dl->sc;780	struct xchk_dirpath	*path;781	int			error = 0;782 783	do {784		if (xchk_should_terminate(sc, &error))785			return error;786 787		xchk_dirtree_reset(dl);788 789		/*790		 * If the extended attributes look as though they has been791		 * zapped by the inode record repair code, we cannot scan for792		 * parent pointers.793		 */794		if (xchk_pptr_looks_zapped(sc->ip)) {795			xchk_set_incomplete(sc);796			return -EBUSY;797		}798 799		/*800		 * Create path walk contexts for each parent of the directory801		 * that is being scanned.  Directories are supposed to have802		 * only one parent, but this is how we detect multiple parents.803		 */804		error = xchk_xattr_walk(sc, sc->ip, xchk_dirtree_create_path,805				NULL, dl);806		if (error)807			return error;808 809		xchk_dirtree_for_each_path(dl, path) {810			/* Load path components into dl->pptr/xname */811			error = xchk_dirtree_load_path(dl, path);812			if (error)813				return error;814 815			/*816			 * Try to walk up each path to the root.  This enables817			 * us to find directory loops in ancestors, and the818			 * like.819			 */820			error = xchk_dirpath_walk_upwards(dl, path);821			if (error == -EFSCORRUPTED) {822				/*823				 * A parent pointer of @sc->ip is bad, don't824				 * bother continuing.825				 */826				break;827			}828			if (error == -ESTALE) {829				/* This had better be an invalidation. */830				ASSERT(dl->stale);831				break;832			}833			if (error)834				return error;835			if (dl->aborted)836				return 0;837		}838	} while (dl->stale);839 840	return error;841}842 843/*844 * Figure out what to do with the paths we tried to find.  Do not call this845 * if the scan results are stale.846 */847void848xchk_dirtree_evaluate(849	struct xchk_dirtree		*dl,850	struct xchk_dirtree_outcomes	*oc)851{852	struct xchk_dirpath		*path;853 854	ASSERT(!dl->stale);855 856	/* Scan the paths we have to decide what to do. */857	memset(oc, 0, sizeof(struct xchk_dirtree_outcomes));858	xchk_dirtree_for_each_path(dl, path) {859		trace_xchk_dirpath_evaluate_path(dl->sc, path->path_nr,860				path->nr_steps, path->outcome);861 862		switch (path->outcome) {863		case XCHK_DIRPATH_SCANNING:864			/* shouldn't get here */865			ASSERT(0);866			break;867		case XCHK_DIRPATH_DELETE:868			/* This one is already going away. */869			oc->bad++;870			break;871		case XCHK_DIRPATH_CORRUPT:872		case XCHK_DIRPATH_LOOP:873			/* Couldn't find the end of this path. */874			oc->suspect++;875			break;876		case XCHK_DIRPATH_STALE:877			/* shouldn't get here either */878			ASSERT(0);879			break;880		case XCHK_DIRPATH_OK:881			/* This path got all the way to the root. */882			oc->good++;883			break;884		case XREP_DIRPATH_DELETING:885		case XREP_DIRPATH_DELETED:886		case XREP_DIRPATH_ADOPTING:887		case XREP_DIRPATH_ADOPTED:888			/* These should not be in progress! */889			ASSERT(0);890			break;891		}892	}893 894	trace_xchk_dirtree_evaluate(dl, oc);895}896 897/* Look for directory loops. */898int899xchk_dirtree(900	struct xfs_scrub		*sc)901{902	struct xchk_dirtree_outcomes	oc;903	struct xchk_dirtree		*dl = sc->buf;904	int				error;905 906	/*907	 * Nondirectories do not point downwards to other files, so they cannot908	 * cause a cycle in the directory tree.909	 */910	if (!S_ISDIR(VFS_I(sc->ip)->i_mode))911		return -ENOENT;912 913	ASSERT(xfs_has_parent(sc->mp));914 915	/*916	 * Find the root of the directory tree.  Remember which directory to917	 * scan, because the hook doesn't detach until after sc->ip gets918	 * released during teardown.919	 */920	dl->root_ino = sc->mp->m_rootip->i_ino;921	dl->scan_ino = sc->ip->i_ino;922 923	trace_xchk_dirtree_start(sc->ip, sc->sm, 0);924 925	/*926	 * Hook into the directory entry code so that we can capture updates to927	 * paths that we have already scanned.  The scanner thread takes each928	 * directory's ILOCK, which means that any in-progress directory update929	 * will finish before we can scan the directory.930	 */931	ASSERT(sc->flags & XCHK_FSGATES_DIRENTS);932	xfs_dir_hook_setup(&dl->dhook, xchk_dirtree_live_update);933	error = xfs_dir_hook_add(sc->mp, &dl->dhook);934	if (error)935		goto out;936 937	mutex_lock(&dl->lock);938 939	/* Trace each parent pointer's path to the root. */940	error = xchk_dirtree_find_paths_to_root(dl);941	if (error == -EFSCORRUPTED || error == -ELNRNG || error == -ENOSR) {942		/*943		 * Don't bother walking the paths if the xattr structure or the944		 * parent pointers are corrupt; this scan cannot be completed945		 * without full information.946		 */947		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);948		error = 0;949		goto out_scanlock;950	}951	if (error == -EBUSY) {952		/*953		 * We couldn't scan some directory's parent pointers because954		 * the attr fork looked like it had been zapped.  The955		 * scan was marked incomplete, so no further error code956		 * is necessary.957		 */958		error = 0;959		goto out_scanlock;960	}961	if (error)962		goto out_scanlock;963	if (dl->aborted) {964		xchk_set_incomplete(sc);965		goto out_scanlock;966	}967 968	/* Assess what we found in our path evaluation. */969	xchk_dirtree_evaluate(dl, &oc);970	if (xchk_dirtree_parentless(dl)) {971		if (oc.good || oc.bad || oc.suspect)972			xchk_ino_set_corrupt(sc, sc->ip->i_ino);973	} else {974		if (oc.bad || oc.good + oc.suspect != 1)975			xchk_ino_set_corrupt(sc, sc->ip->i_ino);976		if (oc.suspect)977			xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);978	}979 980out_scanlock:981	mutex_unlock(&dl->lock);982out:983	trace_xchk_dirtree_done(sc->ip, sc->sm, error);984	return error;985}986