xref: /linux/fs/xfs/scrub/dirtree.c (revision c771600c6af14749609b49565ffb4cac2959710d)
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