1189251Ssam// SPDX-License-Identifier: GPL-2.0-or-later
2252726Srpaulo/*
3252726Srpaulo * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
4189251Ssam * Author: Darrick J. Wong <djwong@kernel.org>
5252726Srpaulo */
6252726Srpaulo#include "xfs.h"
7189251Ssam#include "xfs_fs.h"
8189251Ssam#include "xfs_shared.h"
9189251Ssam#include "xfs_format.h"
10189251Ssam#include "xfs_trans_resv.h"
11189251Ssam#include "xfs_mount.h"
12214734Srpaulo#include "xfs_log_format.h"
13214734Srpaulo#include "xfs_trans.h"
14189251Ssam#include "xfs_inode.h"
15189251Ssam#include "xfs_icache.h"
16189251Ssam#include "xfs_dir2.h"
17189251Ssam#include "xfs_dir2_priv.h"
18189251Ssam#include "xfs_health.h"
19189251Ssam#include "xfs_attr.h"
20189251Ssam#include "xfs_parent.h"
21189251Ssam#include "scrub/scrub.h"
22189251Ssam#include "scrub/common.h"
23189251Ssam#include "scrub/dabtree.h"
24189251Ssam#include "scrub/readdir.h"
25189251Ssam#include "scrub/health.h"
26189251Ssam#include "scrub/repair.h"
27189251Ssam#include "scrub/trace.h"
28189251Ssam#include "scrub/xfile.h"
29189251Ssam#include "scrub/xfarray.h"
30189251Ssam#include "scrub/xfblob.h"
31189251Ssam
32189251Ssam/* Set us up to scrub directories. */
33189251Ssamint
34189251Ssamxchk_setup_directory(
35189251Ssam	struct xfs_scrub	*sc)
36189251Ssam{
37189251Ssam	int			error;
38189251Ssam
39189251Ssam	if (xchk_could_repair(sc)) {
40189251Ssam		error = xrep_setup_directory(sc);
41189251Ssam		if (error)
42189251Ssam			return error;
43189251Ssam	}
44189251Ssam
45189251Ssam	return xchk_setup_inode_contents(sc, 0);
46189251Ssam}
47189251Ssam
48189251Ssam/* Directories */
49189251Ssam
50189251Ssam/* Deferred directory entry that we saved for later. */
51189251Ssamstruct xchk_dirent {
52189251Ssam	/* Cookie for retrieval of the dirent name. */
53189251Ssam	xfblob_cookie		name_cookie;
54189251Ssam
55189251Ssam	/* Child inode number. */
56189251Ssam	xfs_ino_t		ino;
57189251Ssam
58189251Ssam	/* Length of the pptr name. */
59189251Ssam	uint8_t			namelen;
60189251Ssam};
61189251Ssam
62189251Ssamstruct xchk_dir {
63189251Ssam	struct xfs_scrub	*sc;
64252726Srpaulo
65252726Srpaulo	/* information for parent pointer validation. */
66189251Ssam	struct xfs_parent_rec	pptr_rec;
67189251Ssam	struct xfs_da_args	pptr_args;
68189251Ssam
69189251Ssam	/* Fixed-size array of xchk_dirent structures. */
70189251Ssam	struct xfarray		*dir_entries;
71189251Ssam
72189251Ssam	/* Blobs containing dirent names. */
73189251Ssam	struct xfblob		*dir_names;
74189251Ssam
75189251Ssam	/* If we've cycled the ILOCK, we must revalidate deferred dirents. */
76189251Ssam	bool			need_revalidate;
77189251Ssam
78252726Srpaulo	/* Name buffer for dirent revalidation. */
79252726Srpaulo	struct xfs_name		xname;
80252726Srpaulo	uint8_t			namebuf[MAXNAMELEN];
81252726Srpaulo};
82252726Srpaulo
83189251Ssam/* Scrub a directory entry. */
84189251Ssam
85189251Ssam/* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */
86189251SsamSTATIC void
87189251Ssamxchk_dir_check_ftype(
88189251Ssam	struct xfs_scrub	*sc,
89189251Ssam	xfs_fileoff_t		offset,
90189251Ssam	struct xfs_inode	*ip,
91189251Ssam	int			ftype)
92189251Ssam{
93189251Ssam	struct xfs_mount	*mp = sc->mp;
94189251Ssam
95189251Ssam	if (!xfs_has_ftype(mp)) {
96189251Ssam		if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR)
97189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
98189251Ssam		return;
99189251Ssam	}
100189251Ssam
101189251Ssam	if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype)
102189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
103189251Ssam}
104189251Ssam
105189251Ssam/*
106189251Ssam * Try to lock a child file for checking parent pointers.  Returns the inode
107252726Srpaulo * flags for the locks we now hold, or zero if we failed.
108252726Srpaulo */
109252726SrpauloSTATIC unsigned int
110252726Srpauloxchk_dir_lock_child(
111252726Srpaulo	struct xfs_scrub	*sc,
112252726Srpaulo	struct xfs_inode	*ip)
113252726Srpaulo{
114252726Srpaulo	if (!xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED))
115252726Srpaulo		return 0;
116252726Srpaulo
117252726Srpaulo	if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) {
118252726Srpaulo		xfs_iunlock(ip, XFS_IOLOCK_SHARED);
119252726Srpaulo		return 0;
120252726Srpaulo	}
121252726Srpaulo
122189251Ssam	if (!xfs_inode_has_attr_fork(ip) || !xfs_need_iread_extents(&ip->i_af))
123189251Ssam		return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;
124189251Ssam
125189251Ssam	xfs_iunlock(ip, XFS_ILOCK_SHARED);
126189251Ssam
127189251Ssam	if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
128189251Ssam		xfs_iunlock(ip, XFS_IOLOCK_SHARED);
129189251Ssam		return 0;
130189251Ssam	}
131189251Ssam
132189251Ssam	return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
133189251Ssam}
134189251Ssam
135252726Srpaulo/* Check the backwards link (parent pointer) associated with this dirent. */
136252726SrpauloSTATIC int
137189251Ssamxchk_dir_parent_pointer(
138189251Ssam	struct xchk_dir		*sd,
139189251Ssam	const struct xfs_name	*name,
140189251Ssam	struct xfs_inode	*ip)
141189251Ssam{
142252726Srpaulo	struct xfs_scrub	*sc = sd->sc;
143189251Ssam	int			error;
144189251Ssam
145252726Srpaulo	xfs_inode_to_parent_rec(&sd->pptr_rec, sc->ip);
146189251Ssam	error = xfs_parent_lookup(sc->tp, ip, name, &sd->pptr_rec,
147189251Ssam			&sd->pptr_args);
148252726Srpaulo	if (error == -ENOATTR)
149189251Ssam		xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0);
150252726Srpaulo
151252726Srpaulo	return 0;
152252726Srpaulo}
153189251Ssam
154189251Ssam/* Look for a parent pointer matching this dirent, if the child isn't busy. */
155189251SsamSTATIC int
156189251Ssamxchk_dir_check_pptr_fast(
157189251Ssam	struct xchk_dir		*sd,
158189251Ssam	xfs_dir2_dataptr_t	dapos,
159252726Srpaulo	const struct xfs_name	*name,
160252726Srpaulo	struct xfs_inode	*ip)
161252726Srpaulo{
162252726Srpaulo	struct xfs_scrub	*sc = sd->sc;
163252726Srpaulo	unsigned int		lockmode;
164252726Srpaulo	int			error;
165252726Srpaulo
166252726Srpaulo	/* dot and dotdot entries do not have parent pointers */
167252726Srpaulo	if (xfs_dir2_samename(name, &xfs_name_dot) ||
168252726Srpaulo	    xfs_dir2_samename(name, &xfs_name_dotdot))
169252726Srpaulo		return 0;
170252726Srpaulo
171252726Srpaulo	/* No self-referential non-dot or dotdot dirents. */
172189251Ssam	if (ip == sc->ip) {
173189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
174189251Ssam		return -ECANCELED;
175189251Ssam	}
176189251Ssam
177189251Ssam	/* Try to lock the inode. */
178189251Ssam	lockmode = xchk_dir_lock_child(sc, ip);
179189251Ssam	if (!lockmode) {
180189251Ssam		struct xchk_dirent	save_de = {
181189251Ssam			.namelen	= name->len,
182189251Ssam			.ino		= ip->i_ino,
183189251Ssam		};
184252726Srpaulo
185252726Srpaulo		/* Couldn't lock the inode, so save the dirent for later. */
186252726Srpaulo		trace_xchk_dir_defer(sc->ip, name, ip->i_ino);
187189251Ssam
188189251Ssam		error = xfblob_storename(sd->dir_names, &save_de.name_cookie,
189189251Ssam				name);
190189251Ssam		if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
191189251Ssam					&error))
192252726Srpaulo			return error;
193252726Srpaulo
194252726Srpaulo		error = xfarray_append(sd->dir_entries, &save_de);
195252726Srpaulo		if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
196252726Srpaulo					&error))
197252726Srpaulo			return error;
198252726Srpaulo
199252726Srpaulo		return 0;
200252726Srpaulo	}
201252726Srpaulo
202252726Srpaulo	error = xchk_dir_parent_pointer(sd, name, ip);
203252726Srpaulo	xfs_iunlock(ip, lockmode);
204252726Srpaulo	return error;
205252726Srpaulo}
206252726Srpaulo
207252726Srpaulo/*
208252726Srpaulo * Scrub a single directory entry.
209252726Srpaulo *
210252726Srpaulo * Check the inode number to make sure it's sane, then we check that we can
211189251Ssam * look up this filename.  Finally, we check the ftype.
212189251Ssam */
213189251SsamSTATIC int
214189251Ssamxchk_dir_actor(
215189251Ssam	struct xfs_scrub	*sc,
216189251Ssam	struct xfs_inode	*dp,
217189251Ssam	xfs_dir2_dataptr_t	dapos,
218189251Ssam	const struct xfs_name	*name,
219189251Ssam	xfs_ino_t		ino,
220189251Ssam	void			*priv)
221189251Ssam{
222189251Ssam	struct xfs_mount	*mp = dp->i_mount;
223189251Ssam	struct xfs_inode	*ip;
224189251Ssam	struct xchk_dir		*sd = priv;
225189251Ssam	xfs_ino_t		lookup_ino;
226189251Ssam	xfs_dablk_t		offset;
227189251Ssam	int			error = 0;
228252726Srpaulo
229189251Ssam	offset = xfs_dir2_db_to_da(mp->m_dir_geo,
230189251Ssam			xfs_dir2_dataptr_to_db(mp->m_dir_geo, dapos));
231189251Ssam
232189251Ssam	if (xchk_should_terminate(sc, &error))
233189251Ssam		return error;
234189251Ssam
235189251Ssam	/* Does this inode number make sense? */
236189251Ssam	if (!xfs_verify_dir_ino(mp, ino)) {
237189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
238189251Ssam		return -ECANCELED;
239189251Ssam	}
240252726Srpaulo
241252726Srpaulo	/* Does this name make sense? */
242189251Ssam	if (!xfs_dir2_namecheck(name->name, name->len)) {
243189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
244189251Ssam		return -ECANCELED;
245189251Ssam	}
246189251Ssam
247189251Ssam	if (xfs_dir2_samename(name, &xfs_name_dot)) {
248189251Ssam		/* If this is "." then check that the inum matches the dir. */
249189251Ssam		if (ino != dp->i_ino)
250189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
251189251Ssam	} else if (xfs_dir2_samename(name, &xfs_name_dotdot)) {
252252726Srpaulo		/*
253252726Srpaulo		 * If this is ".." in the root inode, check that the inum
254252726Srpaulo		 * matches this dir.
255252726Srpaulo		 */
256252726Srpaulo		if (dp->i_ino == mp->m_sb.sb_rootino && ino != dp->i_ino)
257189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
258189251Ssam	}
259189251Ssam
260189251Ssam	/* Verify that we can look up this name by hash. */
261189251Ssam	error = xchk_dir_lookup(sc, dp, name, &lookup_ino);
262189251Ssam	/* ENOENT means the hash lookup failed and the dir is corrupt */
263189251Ssam	if (error == -ENOENT)
264189251Ssam		error = -EFSCORRUPTED;
265189251Ssam	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error))
266189251Ssam		goto out;
267189251Ssam	if (lookup_ino != ino) {
268189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
269189251Ssam		return -ECANCELED;
270189251Ssam	}
271189251Ssam
272189251Ssam	/*
273189251Ssam	 * Grab the inode pointed to by the dirent.  We release the inode
274189251Ssam	 * before we cancel the scrub transaction.
275189251Ssam	 *
276189251Ssam	 * If _iget returns -EINVAL or -ENOENT then the child inode number is
277189251Ssam	 * garbage and the directory is corrupt.  If the _iget returns
278189251Ssam	 * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a
279189251Ssam	 *  cross referencing error.  Any other error is an operational error.
280189251Ssam	 */
281189251Ssam	error = xchk_iget(sc, ino, &ip);
282189251Ssam	if (error == -EINVAL || error == -ENOENT) {
283252726Srpaulo		error = -EFSCORRUPTED;
284189251Ssam		xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
285189251Ssam		goto out;
286189251Ssam	}
287189251Ssam	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, offset, &error))
288189251Ssam		goto out;
289189251Ssam
290189251Ssam	xchk_dir_check_ftype(sc, offset, ip, name->type);
291189251Ssam
292189251Ssam	if (xfs_has_parent(mp)) {
293189251Ssam		error = xchk_dir_check_pptr_fast(sd, dapos, name, ip);
294189251Ssam		if (error)
295189251Ssam			goto out_rele;
296189251Ssam	}
297189251Ssam
298189251Ssamout_rele:
299252726Srpaulo	xchk_irele(sc, ip);
300252726Srpauloout:
301252726Srpaulo	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
302189251Ssam		return -ECANCELED;
303189251Ssam	return error;
304189251Ssam}
305189251Ssam
306252726Srpaulo/* Scrub a directory btree record. */
307252726SrpauloSTATIC int
308252726Srpauloxchk_dir_rec(
309189251Ssam	struct xchk_da_btree		*ds,
310189251Ssam	int				level)
311252726Srpaulo{
312252726Srpaulo	struct xfs_name			dname = { };
313189251Ssam	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];
314252726Srpaulo	struct xfs_mount		*mp = ds->state->mp;
315189251Ssam	struct xfs_inode		*dp = ds->dargs.dp;
316252726Srpaulo	struct xfs_da_geometry		*geo = mp->m_dir_geo;
317252726Srpaulo	struct xfs_dir2_data_entry	*dent;
318252726Srpaulo	struct xfs_buf			*bp;
319252726Srpaulo	struct xfs_dir2_leaf_entry	*ent;
320252726Srpaulo	unsigned int			end;
321252726Srpaulo	unsigned int			iter_off;
322252726Srpaulo	xfs_ino_t			ino;
323252726Srpaulo	xfs_dablk_t			rec_bno;
324252726Srpaulo	xfs_dir2_db_t			db;
325252726Srpaulo	xfs_dir2_data_aoff_t		off;
326252726Srpaulo	xfs_dir2_dataptr_t		ptr;
327252726Srpaulo	xfs_dahash_t			calc_hash;
328252726Srpaulo	xfs_dahash_t			hash;
329252726Srpaulo	struct xfs_dir3_icleaf_hdr	hdr;
330252726Srpaulo	unsigned int			tag;
331189251Ssam	int				error;
332189251Ssam
333189251Ssam	ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC ||
334189251Ssam	       blk->magic == XFS_DIR2_LEAFN_MAGIC);
335252726Srpaulo
336252726Srpaulo	xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr);
337252726Srpaulo	ent = hdr.ents + blk->index;
338252726Srpaulo
339189251Ssam	/* Check the hash of the entry. */
340252726Srpaulo	error = xchk_da_btree_hash(ds, level, &ent->hashval);
341252726Srpaulo	if (error)
342252726Srpaulo		goto out;
343252726Srpaulo
344252726Srpaulo	/* Valid hash pointer? */
345189251Ssam	ptr = be32_to_cpu(ent->address);
346189251Ssam	if (ptr == 0)
347252726Srpaulo		return 0;
348189251Ssam
349252726Srpaulo	/* Find the directory entry's location. */
350252726Srpaulo	db = xfs_dir2_dataptr_to_db(geo, ptr);
351252726Srpaulo	off = xfs_dir2_dataptr_to_off(geo, ptr);
352252726Srpaulo	rec_bno = xfs_dir2_db_to_da(geo, db);
353252726Srpaulo
354252726Srpaulo	if (rec_bno >= geo->leafblk) {
355252726Srpaulo		xchk_da_set_corrupt(ds, level);
356252726Srpaulo		goto out;
357252726Srpaulo	}
358252726Srpaulo	error = xfs_dir3_data_read(ds->dargs.trans, dp, ds->dargs.owner,
359252726Srpaulo			rec_bno, XFS_DABUF_MAP_HOLE_OK, &bp);
360252726Srpaulo	if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno,
361252726Srpaulo			&error))
362252726Srpaulo		goto out;
363252726Srpaulo	if (!bp) {
364252726Srpaulo		xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
365189251Ssam		goto out;
366189251Ssam	}
367252726Srpaulo	xchk_buffer_recheck(ds->sc, bp);
368252726Srpaulo
369252726Srpaulo	if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
370252726Srpaulo		goto out_relse;
371252726Srpaulo
372252726Srpaulo	dent = bp->b_addr + off;
373252726Srpaulo
374252726Srpaulo	/* Make sure we got a real directory entry. */
375252726Srpaulo	iter_off = geo->data_entry_offset;
376252726Srpaulo	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
377252726Srpaulo	if (!end) {
378252726Srpaulo		xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
379252726Srpaulo		goto out_relse;
380252726Srpaulo	}
381252726Srpaulo	for (;;) {
382252726Srpaulo		struct xfs_dir2_data_entry	*dep = bp->b_addr + iter_off;
383252726Srpaulo		struct xfs_dir2_data_unused	*dup = bp->b_addr + iter_off;
384252726Srpaulo
385252726Srpaulo		if (iter_off >= end) {
386252726Srpaulo			xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
387252726Srpaulo			goto out_relse;
388252726Srpaulo		}
389252726Srpaulo
390252726Srpaulo		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
391252726Srpaulo			iter_off += be16_to_cpu(dup->length);
392252726Srpaulo			continue;
393252726Srpaulo		}
394252726Srpaulo		if (dep == dent)
395252726Srpaulo			break;
396252726Srpaulo		iter_off += xfs_dir2_data_entsize(mp, dep->namelen);
397189251Ssam	}
398189251Ssam
399252726Srpaulo	/* Retrieve the entry, sanity check it, and compare hashes. */
400252726Srpaulo	ino = be64_to_cpu(dent->inumber);
401252726Srpaulo	hash = be32_to_cpu(ent->hashval);
402252726Srpaulo	tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent));
403252726Srpaulo	if (!xfs_verify_dir_ino(mp, ino) || tag != off)
404252726Srpaulo		xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
405252726Srpaulo	if (dent->namelen == 0) {
406252726Srpaulo		xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
407252726Srpaulo		goto out_relse;
408252726Srpaulo	}
409189251Ssam
410189251Ssam	/* Does the directory hash match? */
411189251Ssam	dname.name = dent->name;
412189251Ssam	dname.len = dent->namelen;
413189251Ssam	calc_hash = xfs_dir2_hashname(mp, &dname);
414189251Ssam	if (calc_hash != hash)
415189251Ssam		xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
416189251Ssam
417189251Ssamout_relse:
418189251Ssam	xfs_trans_brelse(ds->dargs.trans, bp);
419189251Ssamout:
420189251Ssam	return error;
421189251Ssam}
422189251Ssam
423189251Ssam/*
424189251Ssam * Is this unused entry either in the bestfree or smaller than all of
425189251Ssam * them?  We've already checked that the bestfrees are sorted longest to
426189251Ssam * shortest, and that there aren't any bogus entries.
427189251Ssam */
428189251SsamSTATIC void
429189251Ssamxchk_directory_check_free_entry(
430189251Ssam	struct xfs_scrub		*sc,
431189251Ssam	xfs_dablk_t			lblk,
432189251Ssam	struct xfs_dir2_data_free	*bf,
433189251Ssam	struct xfs_dir2_data_unused	*dup)
434189251Ssam{
435189251Ssam	struct xfs_dir2_data_free	*dfp;
436189251Ssam	unsigned int			dup_length;
437189251Ssam
438189251Ssam	dup_length = be16_to_cpu(dup->length);
439189251Ssam
440189251Ssam	/* Unused entry is shorter than any of the bestfrees */
441189251Ssam	if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length))
442189251Ssam		return;
443189251Ssam
444189251Ssam	for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--)
445189251Ssam		if (dup_length == be16_to_cpu(dfp->length))
446189251Ssam			return;
447189251Ssam
448189251Ssam	/* Unused entry should be in the bestfrees but wasn't found. */
449189251Ssam	xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
450189251Ssam}
451189251Ssam
452189251Ssam/* Check free space info in a directory data block. */
453189251SsamSTATIC int
454189251Ssamxchk_directory_data_bestfree(
455189251Ssam	struct xfs_scrub		*sc,
456189251Ssam	xfs_dablk_t			lblk,
457189251Ssam	bool				is_block)
458189251Ssam{
459189251Ssam	struct xfs_dir2_data_unused	*dup;
460189251Ssam	struct xfs_dir2_data_free	*dfp;
461189251Ssam	struct xfs_buf			*bp;
462252726Srpaulo	struct xfs_dir2_data_free	*bf;
463189251Ssam	struct xfs_mount		*mp = sc->mp;
464252726Srpaulo	u16				tag;
465189251Ssam	unsigned int			nr_bestfrees = 0;
466189251Ssam	unsigned int			nr_frees = 0;
467189251Ssam	unsigned int			smallest_bestfree;
468189251Ssam	int				newlen;
469189251Ssam	unsigned int			offset;
470189251Ssam	unsigned int			end;
471252726Srpaulo	int				error;
472252726Srpaulo
473189251Ssam	if (is_block) {
474189251Ssam		/* dir block format */
475189251Ssam		if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET))
476189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
477189251Ssam		error = xfs_dir3_block_read(sc->tp, sc->ip, sc->ip->i_ino, &bp);
478189251Ssam	} else {
479189251Ssam		/* dir data format */
480189251Ssam		error = xfs_dir3_data_read(sc->tp, sc->ip, sc->ip->i_ino, lblk,
481189251Ssam				0, &bp);
482189251Ssam	}
483189251Ssam	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
484189251Ssam		goto out;
485189251Ssam	xchk_buffer_recheck(sc, bp);
486189251Ssam
487189251Ssam	/* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */
488189251Ssam
489189251Ssam	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
490189251Ssam		goto out_buf;
491252726Srpaulo
492189251Ssam	/* Do the bestfrees correspond to actual free space? */
493189251Ssam	bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr);
494189251Ssam	smallest_bestfree = UINT_MAX;
495189251Ssam	for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
496189251Ssam		offset = be16_to_cpu(dfp->offset);
497189251Ssam		if (offset == 0)
498189251Ssam			continue;
499189251Ssam		if (offset >= mp->m_dir_geo->blksize) {
500189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
501189251Ssam			goto out_buf;
502189251Ssam		}
503189251Ssam		dup = bp->b_addr + offset;
504189251Ssam		tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
505189251Ssam
506189251Ssam		/* bestfree doesn't match the entry it points at? */
507189251Ssam		if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) ||
508189251Ssam		    be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) ||
509189251Ssam		    tag != offset) {
510189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
511189251Ssam			goto out_buf;
512189251Ssam		}
513189251Ssam
514189251Ssam		/* bestfree records should be ordered largest to smallest */
515189251Ssam		if (smallest_bestfree < be16_to_cpu(dfp->length)) {
516189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
517189251Ssam			goto out_buf;
518189251Ssam		}
519189251Ssam
520189251Ssam		smallest_bestfree = be16_to_cpu(dfp->length);
521189251Ssam		nr_bestfrees++;
522189251Ssam	}
523189251Ssam
524189251Ssam	/* Make sure the bestfrees are actually the best free spaces. */
525189251Ssam	offset = mp->m_dir_geo->data_entry_offset;
526189251Ssam	end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr);
527189251Ssam
528189251Ssam	/* Iterate the entries, stopping when we hit or go past the end. */
529189251Ssam	while (offset < end) {
530189251Ssam		dup = bp->b_addr + offset;
531189251Ssam
532189251Ssam		/* Skip real entries */
533189251Ssam		if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
534189251Ssam			struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
535252726Srpaulo
536252726Srpaulo			newlen = xfs_dir2_data_entsize(mp, dep->namelen);
537189251Ssam			if (newlen <= 0) {
538189251Ssam				xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
539189251Ssam						lblk);
540189251Ssam				goto out_buf;
541189251Ssam			}
542189251Ssam			offset += newlen;
543189251Ssam			continue;
544189251Ssam		}
545189251Ssam
546189251Ssam		/* Spot check this free entry */
547189251Ssam		tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
548189251Ssam		if (tag != offset) {
549189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
550189251Ssam			goto out_buf;
551189251Ssam		}
552189251Ssam
553189251Ssam		/*
554189251Ssam		 * Either this entry is a bestfree or it's smaller than
555189251Ssam		 * any of the bestfrees.
556189251Ssam		 */
557189251Ssam		xchk_directory_check_free_entry(sc, lblk, bf, dup);
558189251Ssam		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
559189251Ssam			goto out_buf;
560189251Ssam
561189251Ssam		/* Move on. */
562189251Ssam		newlen = be16_to_cpu(dup->length);
563189251Ssam		if (newlen <= 0) {
564189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
565189251Ssam			goto out_buf;
566189251Ssam		}
567189251Ssam		offset += newlen;
568252726Srpaulo		if (offset <= end)
569252726Srpaulo			nr_frees++;
570252726Srpaulo	}
571189251Ssam
572189251Ssam	/* We're required to fill all the space. */
573189251Ssam	if (offset != end)
574189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
575189251Ssam
576189251Ssam	/* Did we see at least as many free slots as there are bestfrees? */
577252726Srpaulo	if (nr_frees < nr_bestfrees)
578252726Srpaulo		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
579252726Srpauloout_buf:
580189251Ssam	xfs_trans_brelse(sc->tp, bp);
581189251Ssamout:
582189251Ssam	return error;
583252726Srpaulo}
584252726Srpaulo
585252726Srpaulo/*
586189251Ssam * Does the free space length in the free space index block ($len) match
587189251Ssam * the longest length in the directory data block's bestfree array?
588189251Ssam * Assume that we've already checked that the data block's bestfree
589189251Ssam * array is in order.
590189251Ssam */
591189251SsamSTATIC void
592189251Ssamxchk_directory_check_freesp(
593189251Ssam	struct xfs_scrub		*sc,
594189251Ssam	xfs_dablk_t			lblk,
595189251Ssam	struct xfs_buf			*dbp,
596189251Ssam	unsigned int			len)
597189251Ssam{
598189251Ssam	struct xfs_dir2_data_free	*dfp;
599189251Ssam
600189251Ssam	dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr);
601189251Ssam
602189251Ssam	if (len != be16_to_cpu(dfp->length))
603189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
604189251Ssam
605189251Ssam	if (len > 0 && be16_to_cpu(dfp->offset) == 0)
606189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
607189251Ssam}
608189251Ssam
609189251Ssam/* Check free space info in a directory leaf1 block. */
610189251SsamSTATIC int
611189251Ssamxchk_directory_leaf1_bestfree(
612189251Ssam	struct xfs_scrub		*sc,
613189251Ssam	struct xfs_da_args		*args,
614189251Ssam	xfs_dir2_db_t			last_data_db,
615189251Ssam	xfs_dablk_t			lblk)
616189251Ssam{
617189251Ssam	struct xfs_dir3_icleaf_hdr	leafhdr;
618189251Ssam	struct xfs_dir2_leaf_tail	*ltp;
619189251Ssam	struct xfs_dir2_leaf		*leaf;
620189251Ssam	struct xfs_buf			*dbp;
621189251Ssam	struct xfs_buf			*bp;
622189251Ssam	struct xfs_da_geometry		*geo = sc->mp->m_dir_geo;
623189251Ssam	__be16				*bestp;
624189251Ssam	__u16				best;
625189251Ssam	__u32				hash;
626189251Ssam	__u32				lasthash = 0;
627189251Ssam	__u32				bestcount;
628189251Ssam	unsigned int			stale = 0;
629189251Ssam	int				i;
630189251Ssam	int				error;
631189251Ssam
632189251Ssam	/* Read the free space block. */
633189251Ssam	error = xfs_dir3_leaf_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, &bp);
634189251Ssam	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
635189251Ssam		return error;
636189251Ssam	xchk_buffer_recheck(sc, bp);
637189251Ssam
638189251Ssam	leaf = bp->b_addr;
639189251Ssam	xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf);
640189251Ssam	ltp = xfs_dir2_leaf_tail_p(geo, leaf);
641189251Ssam	bestcount = be32_to_cpu(ltp->bestcount);
642189251Ssam	bestp = xfs_dir2_leaf_bests_p(ltp);
643189251Ssam
644189251Ssam	if (xfs_has_crc(sc->mp)) {
645189251Ssam		struct xfs_dir3_leaf_hdr	*hdr3 = bp->b_addr;
646189251Ssam
647189251Ssam		if (hdr3->pad != cpu_to_be32(0))
648189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
649189251Ssam	}
650189251Ssam
651189251Ssam	/*
652189251Ssam	 * There must be enough bestfree slots to cover all the directory data
653189251Ssam	 * blocks that we scanned.  It is possible for there to be a hole
654189251Ssam	 * between the last data block and i_disk_size.  This seems like an
655189251Ssam	 * oversight to the scrub author, but as we have been writing out
656189251Ssam	 * directories like this (and xfs_repair doesn't mind them) for years,
657189251Ssam	 * that's what we have to check.
658189251Ssam	 */
659189251Ssam	if (bestcount != last_data_db + 1) {
660189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
661189251Ssam		goto out;
662189251Ssam	}
663189251Ssam
664189251Ssam	/* Is the leaf count even remotely sane? */
665189251Ssam	if (leafhdr.count > geo->leaf_max_ents) {
666189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
667189251Ssam		goto out;
668189251Ssam	}
669189251Ssam
670189251Ssam	/* Leaves and bests don't overlap in leaf format. */
671189251Ssam	if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) {
672189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
673189251Ssam		goto out;
674189251Ssam	}
675189251Ssam
676189251Ssam	/* Check hash value order, count stale entries.  */
677189251Ssam	for (i = 0; i < leafhdr.count; i++) {
678189251Ssam		hash = be32_to_cpu(leafhdr.ents[i].hashval);
679189251Ssam		if (i > 0 && lasthash > hash)
680189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
681189251Ssam		lasthash = hash;
682189251Ssam		if (leafhdr.ents[i].address ==
683189251Ssam		    cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
684189251Ssam			stale++;
685189251Ssam	}
686189251Ssam	if (leafhdr.stale != stale)
687189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
688189251Ssam	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
689189251Ssam		goto out;
690189251Ssam
691189251Ssam	/* Check all the bestfree entries. */
692189251Ssam	for (i = 0; i < bestcount; i++, bestp++) {
693189251Ssam		best = be16_to_cpu(*bestp);
694189251Ssam		error = xfs_dir3_data_read(sc->tp, sc->ip, args->owner,
695189251Ssam				xfs_dir2_db_to_da(args->geo, i),
696189251Ssam				XFS_DABUF_MAP_HOLE_OK, &dbp);
697189251Ssam		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
698189251Ssam				&error))
699189251Ssam			break;
700189251Ssam
701189251Ssam		if (!dbp) {
702189251Ssam			if (best != NULLDATAOFF) {
703189251Ssam				xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
704189251Ssam						lblk);
705189251Ssam				break;
706189251Ssam			}
707189251Ssam			continue;
708189251Ssam		}
709189251Ssam
710189251Ssam		if (best == NULLDATAOFF)
711189251Ssam			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
712189251Ssam		else
713189251Ssam			xchk_directory_check_freesp(sc, lblk, dbp, best);
714189251Ssam		xfs_trans_brelse(sc->tp, dbp);
715189251Ssam		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
716189251Ssam			break;
717189251Ssam	}
718189251Ssamout:
719189251Ssam	xfs_trans_brelse(sc->tp, bp);
720189251Ssam	return error;
721189251Ssam}
722189251Ssam
723189251Ssam/* Check free space info in a directory freespace block. */
724189251SsamSTATIC int
725189251Ssamxchk_directory_free_bestfree(
726189251Ssam	struct xfs_scrub		*sc,
727189251Ssam	struct xfs_da_args		*args,
728189251Ssam	xfs_dablk_t			lblk)
729189251Ssam{
730189251Ssam	struct xfs_dir3_icfree_hdr	freehdr;
731189251Ssam	struct xfs_buf			*dbp;
732189251Ssam	struct xfs_buf			*bp;
733189251Ssam	__u16				best;
734189251Ssam	unsigned int			stale = 0;
735189251Ssam	int				i;
736252726Srpaulo	int				error;
737189251Ssam
738252726Srpaulo	/* Read the free space block */
739189251Ssam	error = xfs_dir2_free_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, &bp);
740189251Ssam	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
741189251Ssam		return error;
742189251Ssam	xchk_buffer_recheck(sc, bp);
743209158Srpaulo
744209158Srpaulo	if (xfs_has_crc(sc->mp)) {
745209158Srpaulo		struct xfs_dir3_free_hdr	*hdr3 = bp->b_addr;
746209158Srpaulo
747209158Srpaulo		if (hdr3->pad != cpu_to_be32(0))
748209158Srpaulo			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
749209158Srpaulo	}
750209158Srpaulo
751209158Srpaulo	/* Check all the entries. */
752209158Srpaulo	xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr);
753209158Srpaulo	for (i = 0; i < freehdr.nvalid; i++) {
754189251Ssam		best = be16_to_cpu(freehdr.bests[i]);
755189251Ssam		if (best == NULLDATAOFF) {
756189251Ssam			stale++;
757189251Ssam			continue;
758189251Ssam		}
759189251Ssam		error = xfs_dir3_data_read(sc->tp, sc->ip, args->owner,
760189251Ssam				(freehdr.firstdb + i) * args->geo->fsbcount,
761189251Ssam				0, &dbp);
762189251Ssam		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
763189251Ssam				&error))
764189251Ssam			goto out;
765189251Ssam		xchk_directory_check_freesp(sc, lblk, dbp, best);
766189251Ssam		xfs_trans_brelse(sc->tp, dbp);
767189251Ssam	}
768189251Ssam
769189251Ssam	if (freehdr.nused + stale != freehdr.nvalid)
770189251Ssam		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
771189251Ssamout:
772189251Ssam	xfs_trans_brelse(sc->tp, bp);
773189251Ssam	return error;
774189251Ssam}
775189251Ssam
776189251Ssam/* Check free space information in directories. */
777189251SsamSTATIC int
778189251Ssamxchk_directory_blocks(
779189251Ssam	struct xfs_scrub	*sc)
780252726Srpaulo{
781252726Srpaulo	struct xfs_bmbt_irec	got;
782252726Srpaulo	struct xfs_da_args	args = {
783252726Srpaulo		.dp		= sc->ip,
784252726Srpaulo		.whichfork	= XFS_DATA_FORK,
785252726Srpaulo		.geo		= sc->mp->m_dir_geo,
786189251Ssam		.trans		= sc->tp,
787189251Ssam		.owner		= sc->ip->i_ino,
788189251Ssam	};
789189251Ssam	struct xfs_ifork	*ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
790189251Ssam	struct xfs_mount	*mp = sc->mp;
791189251Ssam	xfs_fileoff_t		leaf_lblk;
792189251Ssam	xfs_fileoff_t		free_lblk;
793189251Ssam	xfs_fileoff_t		lblk;
794189251Ssam	struct xfs_iext_cursor	icur;
795	xfs_dablk_t		dabno;
796	xfs_dir2_db_t		last_data_db = 0;
797	bool			found;
798	bool			is_block = false;
799	int			error;
800
801	/* Ignore local format directories. */
802	if (ifp->if_format != XFS_DINODE_FMT_EXTENTS &&
803	    ifp->if_format != XFS_DINODE_FMT_BTREE)
804		return 0;
805
806	lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
807	leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
808	free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
809
810	/* Is this a block dir? */
811	if (xfs_dir2_format(&args, &error) == XFS_DIR2_FMT_BLOCK)
812		is_block = true;
813	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
814		goto out;
815
816	/* Iterate all the data extents in the directory... */
817	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
818	while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
819		/* No more data blocks... */
820		if (got.br_startoff >= leaf_lblk)
821			break;
822
823		/*
824		 * Check each data block's bestfree data.
825		 *
826		 * Iterate all the fsbcount-aligned block offsets in
827		 * this directory.  The directory block reading code is
828		 * smart enough to do its own bmap lookups to handle
829		 * discontiguous directory blocks.  When we're done
830		 * with the extent record, re-query the bmap at the
831		 * next fsbcount-aligned offset to avoid redundant
832		 * block checks.
833		 */
834		for (lblk = roundup((xfs_dablk_t)got.br_startoff,
835				args.geo->fsbcount);
836		     lblk < got.br_startoff + got.br_blockcount;
837		     lblk += args.geo->fsbcount) {
838			last_data_db = xfs_dir2_da_to_db(args.geo, lblk);
839			error = xchk_directory_data_bestfree(sc, lblk,
840					is_block);
841			if (error)
842				goto out;
843		}
844		dabno = got.br_startoff + got.br_blockcount;
845		lblk = roundup(dabno, args.geo->fsbcount);
846		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
847	}
848
849	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
850		goto out;
851
852	/* Look for a leaf1 block, which has free info. */
853	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) &&
854	    got.br_startoff == leaf_lblk &&
855	    got.br_blockcount == args.geo->fsbcount &&
856	    !xfs_iext_next_extent(ifp, &icur, &got)) {
857		if (is_block) {
858			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
859			goto out;
860		}
861		error = xchk_directory_leaf1_bestfree(sc, &args, last_data_db,
862				leaf_lblk);
863		if (error)
864			goto out;
865	}
866
867	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
868		goto out;
869
870	/* Scan for free blocks */
871	lblk = free_lblk;
872	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
873	while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
874		/*
875		 * Dirs can't have blocks mapped above 2^32.
876		 * Single-block dirs shouldn't even be here.
877		 */
878		lblk = got.br_startoff;
879		if (lblk & ~0xFFFFFFFFULL) {
880			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
881			goto out;
882		}
883		if (is_block) {
884			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
885			goto out;
886		}
887
888		/*
889		 * Check each dir free block's bestfree data.
890		 *
891		 * Iterate all the fsbcount-aligned block offsets in
892		 * this directory.  The directory block reading code is
893		 * smart enough to do its own bmap lookups to handle
894		 * discontiguous directory blocks.  When we're done
895		 * with the extent record, re-query the bmap at the
896		 * next fsbcount-aligned offset to avoid redundant
897		 * block checks.
898		 */
899		for (lblk = roundup((xfs_dablk_t)got.br_startoff,
900				args.geo->fsbcount);
901		     lblk < got.br_startoff + got.br_blockcount;
902		     lblk += args.geo->fsbcount) {
903			error = xchk_directory_free_bestfree(sc, &args,
904					lblk);
905			if (error)
906				goto out;
907		}
908		dabno = got.br_startoff + got.br_blockcount;
909		lblk = roundup(dabno, args.geo->fsbcount);
910		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
911	}
912out:
913	return error;
914}
915
916/*
917 * Revalidate a dirent that we collected in the past but couldn't check because
918 * of lock contention.  Returns 0 if the dirent is still valid, -ENOENT if it
919 * has gone away on us, or a negative errno.
920 */
921STATIC int
922xchk_dir_revalidate_dirent(
923	struct xchk_dir		*sd,
924	const struct xfs_name	*xname,
925	xfs_ino_t		ino)
926{
927	struct xfs_scrub	*sc = sd->sc;
928	xfs_ino_t		child_ino;
929	int			error;
930
931	/*
932	 * Look up the directory entry.  If we get -ENOENT, the directory entry
933	 * went away and there's nothing to revalidate.  Return any other
934	 * error.
935	 */
936	error = xchk_dir_lookup(sc, sc->ip, xname, &child_ino);
937	if (error)
938		return error;
939
940	/* The inode number changed, nothing to revalidate. */
941	if (ino != child_ino)
942		return -ENOENT;
943
944	return 0;
945}
946
947/*
948 * Check a directory entry's parent pointers the slow way, which means we cycle
949 * locks a bunch and put up with revalidation until we get it done.
950 */
951STATIC int
952xchk_dir_slow_dirent(
953	struct xchk_dir		*sd,
954	struct xchk_dirent	*dirent,
955	const struct xfs_name	*xname)
956{
957	struct xfs_scrub	*sc = sd->sc;
958	struct xfs_inode	*ip;
959	unsigned int		lockmode;
960	int			error;
961
962	/* Check that the deferred dirent still exists. */
963	if (sd->need_revalidate) {
964		error = xchk_dir_revalidate_dirent(sd, xname, dirent->ino);
965		if (error == -ENOENT)
966			return 0;
967		if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
968					&error))
969			return error;
970	}
971
972	error = xchk_iget(sc, dirent->ino, &ip);
973	if (error == -EINVAL || error == -ENOENT) {
974		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
975		return 0;
976	}
977	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
978		return error;
979
980	/*
981	 * If we can grab both IOLOCK and ILOCK of the alleged child, we can
982	 * proceed with the validation.
983	 */
984	lockmode = xchk_dir_lock_child(sc, ip);
985	if (lockmode) {
986		trace_xchk_dir_slowpath(sc->ip, xname, ip->i_ino);
987		goto check_pptr;
988	}
989
990	/*
991	 * We couldn't lock the child file.  Drop all the locks and try to
992	 * get them again, one at a time.
993	 */
994	xchk_iunlock(sc, sc->ilock_flags);
995	sd->need_revalidate = true;
996
997	trace_xchk_dir_ultraslowpath(sc->ip, xname, ip->i_ino);
998
999	error = xchk_dir_trylock_for_pptrs(sc, ip, &lockmode);
1000	if (error)
1001		goto out_rele;
1002
1003	/* Revalidate, since we just cycled the locks. */
1004	error = xchk_dir_revalidate_dirent(sd, xname, dirent->ino);
1005	if (error == -ENOENT) {
1006		error = 0;
1007		goto out_unlock;
1008	}
1009	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
1010		goto out_unlock;
1011
1012check_pptr:
1013	error = xchk_dir_parent_pointer(sd, xname, ip);
1014out_unlock:
1015	xfs_iunlock(ip, lockmode);
1016out_rele:
1017	xchk_irele(sc, ip);
1018	return error;
1019}
1020
1021/* Check all the dirents that we deferred the first time around. */
1022STATIC int
1023xchk_dir_finish_slow_dirents(
1024	struct xchk_dir		*sd)
1025{
1026	xfarray_idx_t		array_cur;
1027	int			error;
1028
1029	foreach_xfarray_idx(sd->dir_entries, array_cur) {
1030		struct xchk_dirent	dirent;
1031
1032		if (sd->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
1033			return 0;
1034
1035		error = xfarray_load(sd->dir_entries, array_cur, &dirent);
1036		if (error)
1037			return error;
1038
1039		error = xfblob_loadname(sd->dir_names, dirent.name_cookie,
1040				&sd->xname, dirent.namelen);
1041		if (error)
1042			return error;
1043
1044		error = xchk_dir_slow_dirent(sd, &dirent, &sd->xname);
1045		if (error)
1046			return error;
1047	}
1048
1049	return 0;
1050}
1051
1052/* Scrub a whole directory. */
1053int
1054xchk_directory(
1055	struct xfs_scrub	*sc)
1056{
1057	struct xchk_dir		*sd;
1058	int			error;
1059
1060	if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
1061		return -ENOENT;
1062
1063	if (xchk_file_looks_zapped(sc, XFS_SICK_INO_DIR_ZAPPED)) {
1064		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
1065		return 0;
1066	}
1067
1068	/* Plausible size? */
1069	if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) {
1070		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
1071		return 0;
1072	}
1073
1074	/* Check directory tree structure */
1075	error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL);
1076	if (error)
1077		return error;
1078
1079	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
1080		return 0;
1081
1082	/* Check the freespace. */
1083	error = xchk_directory_blocks(sc);
1084	if (error)
1085		return error;
1086
1087	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
1088		return 0;
1089
1090	sd = kvzalloc(sizeof(struct xchk_dir), XCHK_GFP_FLAGS);
1091	if (!sd)
1092		return -ENOMEM;
1093	sd->sc = sc;
1094	sd->xname.name = sd->namebuf;
1095
1096	if (xfs_has_parent(sc->mp)) {
1097		char		*descr;
1098
1099		/*
1100		 * Set up some staging memory for dirents that we can't check
1101		 * due to locking contention.
1102		 */
1103		descr = xchk_xfile_ino_descr(sc, "slow directory entries");
1104		error = xfarray_create(descr, 0, sizeof(struct xchk_dirent),
1105				&sd->dir_entries);
1106		kfree(descr);
1107		if (error)
1108			goto out_sd;
1109
1110		descr = xchk_xfile_ino_descr(sc, "slow directory entry names");
1111		error = xfblob_create(descr, &sd->dir_names);
1112		kfree(descr);
1113		if (error)
1114			goto out_entries;
1115	}
1116
1117	/* Look up every name in this directory by hash. */
1118	error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, sd);
1119	if (error == -ECANCELED)
1120		error = 0;
1121	if (error)
1122		goto out_names;
1123
1124	if (xfs_has_parent(sc->mp)) {
1125		error = xchk_dir_finish_slow_dirents(sd);
1126		if (error == -ETIMEDOUT) {
1127			/* Couldn't grab a lock, scrub was marked incomplete */
1128			error = 0;
1129			goto out_names;
1130		}
1131		if (error)
1132			goto out_names;
1133	}
1134
1135out_names:
1136	if (sd->dir_names)
1137		xfblob_destroy(sd->dir_names);
1138out_entries:
1139	if (sd->dir_entries)
1140		xfarray_destroy(sd->dir_entries);
1141out_sd:
1142	kvfree(sd);
1143	if (error)
1144		return error;
1145
1146	/* If the dir is clean, it is clearly not zapped. */
1147	xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_DIR_ZAPPED);
1148	return 0;
1149}
1150
1151/*
1152 * Decide if this directory has been zapped to satisfy the inode and ifork
1153 * verifiers.  Checking and repairing should be postponed until the directory
1154 * is fixed.
1155 */
1156bool
1157xchk_dir_looks_zapped(
1158	struct xfs_inode	*dp)
1159{
1160	/* Repair zapped this dir's data fork a short time ago */
1161	if (xfs_ifork_zapped(dp, XFS_DATA_FORK))
1162		return true;
1163
1164	/*
1165	 * If the dinode repair found a bad data fork, it will reset the fork
1166	 * to extents format with zero records and wait for the bmapbtd
1167	 * scrubber to reconstruct the block mappings.  Directories always
1168	 * contain some content, so this is a clear sign of a zapped directory.
1169	 * The state checked by xfs_ifork_zapped is not persisted, so this is
1170	 * the secondary strategy if repairs are interrupted by a crash or an
1171	 * unmount.
1172	 */
1173	return dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS &&
1174	       dp->i_df.if_nextents == 0;
1175}
1176