18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Oracle.  All Rights Reserved.
48c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include "xfs.h"
78c2ecf20Sopenharmony_ci#include "xfs_fs.h"
88c2ecf20Sopenharmony_ci#include "xfs_shared.h"
98c2ecf20Sopenharmony_ci#include "xfs_format.h"
108c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h"
118c2ecf20Sopenharmony_ci#include "xfs_mount.h"
128c2ecf20Sopenharmony_ci#include "xfs_log_format.h"
138c2ecf20Sopenharmony_ci#include "xfs_inode.h"
148c2ecf20Sopenharmony_ci#include "xfs_da_format.h"
158c2ecf20Sopenharmony_ci#include "xfs_da_btree.h"
168c2ecf20Sopenharmony_ci#include "xfs_attr.h"
178c2ecf20Sopenharmony_ci#include "xfs_attr_leaf.h"
188c2ecf20Sopenharmony_ci#include "scrub/scrub.h"
198c2ecf20Sopenharmony_ci#include "scrub/common.h"
208c2ecf20Sopenharmony_ci#include "scrub/dabtree.h"
218c2ecf20Sopenharmony_ci#include "scrub/attr.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * Allocate enough memory to hold an attr value and attr block bitmaps,
258c2ecf20Sopenharmony_ci * reallocating the buffer if necessary.  Buffer contents are not preserved
268c2ecf20Sopenharmony_ci * across a reallocation.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ciint
298c2ecf20Sopenharmony_cixchk_setup_xattr_buf(
308c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
318c2ecf20Sopenharmony_ci	size_t			value_size,
328c2ecf20Sopenharmony_ci	xfs_km_flags_t		flags)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	size_t			sz;
358c2ecf20Sopenharmony_ci	struct xchk_xattr_buf	*ab = sc->buf;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/*
388c2ecf20Sopenharmony_ci	 * We need enough space to read an xattr value from the file or enough
398c2ecf20Sopenharmony_ci	 * space to hold three copies of the xattr free space bitmap.  We don't
408c2ecf20Sopenharmony_ci	 * need the buffer space for both purposes at the same time.
418c2ecf20Sopenharmony_ci	 */
428c2ecf20Sopenharmony_ci	sz = 3 * sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize);
438c2ecf20Sopenharmony_ci	sz = max_t(size_t, sz, value_size);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * If there's already a buffer, figure out if we need to reallocate it
478c2ecf20Sopenharmony_ci	 * to accommodate a larger size.
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	if (ab) {
508c2ecf20Sopenharmony_ci		if (sz <= ab->sz)
518c2ecf20Sopenharmony_ci			return 0;
528c2ecf20Sopenharmony_ci		kmem_free(ab);
538c2ecf20Sopenharmony_ci		sc->buf = NULL;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * Don't zero the buffer upon allocation to avoid runtime overhead.
588c2ecf20Sopenharmony_ci	 * All users must be careful never to read uninitialized contents.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	ab = kmem_alloc_large(sizeof(*ab) + sz, flags);
618c2ecf20Sopenharmony_ci	if (!ab)
628c2ecf20Sopenharmony_ci		return -ENOMEM;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ab->sz = sz;
658c2ecf20Sopenharmony_ci	sc->buf = ab;
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* Set us up to scrub an inode's extended attributes. */
708c2ecf20Sopenharmony_ciint
718c2ecf20Sopenharmony_cixchk_setup_xattr(
728c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
738c2ecf20Sopenharmony_ci	struct xfs_inode	*ip)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int			error;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * We failed to get memory while checking attrs, so this time try to
798c2ecf20Sopenharmony_ci	 * get all the memory we're ever going to need.  Allocate the buffer
808c2ecf20Sopenharmony_ci	 * without the inode lock held, which means we can sleep.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	if (sc->flags & XCHK_TRY_HARDER) {
838c2ecf20Sopenharmony_ci		error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX, 0);
848c2ecf20Sopenharmony_ci		if (error)
858c2ecf20Sopenharmony_ci			return error;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return xchk_setup_inode_contents(sc, ip, 0);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Extended Attributes */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct xchk_xattr {
948c2ecf20Sopenharmony_ci	struct xfs_attr_list_context	context;
958c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * Check that an extended attribute key can be looked up by hash.
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * We use the XFS attribute list iterator (i.e. xfs_attr_list_ilocked)
1028c2ecf20Sopenharmony_ci * to call this function for every attribute key in an inode.  Once
1038c2ecf20Sopenharmony_ci * we're here, we load the attribute value to see if any errors happen,
1048c2ecf20Sopenharmony_ci * or if we get more or less data than we expected.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic void
1078c2ecf20Sopenharmony_cixchk_xattr_listent(
1088c2ecf20Sopenharmony_ci	struct xfs_attr_list_context	*context,
1098c2ecf20Sopenharmony_ci	int				flags,
1108c2ecf20Sopenharmony_ci	unsigned char			*name,
1118c2ecf20Sopenharmony_ci	int				namelen,
1128c2ecf20Sopenharmony_ci	int				valuelen)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct xchk_xattr		*sx;
1158c2ecf20Sopenharmony_ci	struct xfs_da_args		args = { NULL };
1168c2ecf20Sopenharmony_ci	int				error = 0;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	sx = container_of(context, struct xchk_xattr, context);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (xchk_should_terminate(sx->sc, &error)) {
1218c2ecf20Sopenharmony_ci		context->seen_enough = error;
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (flags & XFS_ATTR_INCOMPLETE) {
1268c2ecf20Sopenharmony_ci		/* Incomplete attr key, just mark the inode for preening. */
1278c2ecf20Sopenharmony_ci		xchk_ino_set_preen(sx->sc, context->dp->i_ino);
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Does this name make sense? */
1328c2ecf20Sopenharmony_ci	if (!xfs_attr_namecheck(name, namelen)) {
1338c2ecf20Sopenharmony_ci		xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno);
1348c2ecf20Sopenharmony_ci		return;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * Try to allocate enough memory to extrat the attr value.  If that
1398c2ecf20Sopenharmony_ci	 * doesn't work, we overload the seen_enough variable to convey
1408c2ecf20Sopenharmony_ci	 * the error message back to the main scrub function.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	error = xchk_setup_xattr_buf(sx->sc, valuelen, KM_MAYFAIL);
1438c2ecf20Sopenharmony_ci	if (error == -ENOMEM)
1448c2ecf20Sopenharmony_ci		error = -EDEADLOCK;
1458c2ecf20Sopenharmony_ci	if (error) {
1468c2ecf20Sopenharmony_ci		context->seen_enough = error;
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	args.op_flags = XFS_DA_OP_NOTIME;
1518c2ecf20Sopenharmony_ci	args.attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK;
1528c2ecf20Sopenharmony_ci	args.geo = context->dp->i_mount->m_attr_geo;
1538c2ecf20Sopenharmony_ci	args.whichfork = XFS_ATTR_FORK;
1548c2ecf20Sopenharmony_ci	args.dp = context->dp;
1558c2ecf20Sopenharmony_ci	args.name = name;
1568c2ecf20Sopenharmony_ci	args.namelen = namelen;
1578c2ecf20Sopenharmony_ci	args.hashval = xfs_da_hashname(args.name, args.namelen);
1588c2ecf20Sopenharmony_ci	args.trans = context->tp;
1598c2ecf20Sopenharmony_ci	args.value = xchk_xattr_valuebuf(sx->sc);
1608c2ecf20Sopenharmony_ci	args.valuelen = valuelen;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	error = xfs_attr_get_ilocked(&args);
1638c2ecf20Sopenharmony_ci	/* ENODATA means the hash lookup failed and the attr is bad */
1648c2ecf20Sopenharmony_ci	if (error == -ENODATA)
1658c2ecf20Sopenharmony_ci		error = -EFSCORRUPTED;
1668c2ecf20Sopenharmony_ci	if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno,
1678c2ecf20Sopenharmony_ci			&error))
1688c2ecf20Sopenharmony_ci		goto fail_xref;
1698c2ecf20Sopenharmony_ci	if (args.valuelen != valuelen)
1708c2ecf20Sopenharmony_ci		xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK,
1718c2ecf20Sopenharmony_ci					     args.blkno);
1728c2ecf20Sopenharmony_cifail_xref:
1738c2ecf20Sopenharmony_ci	if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
1748c2ecf20Sopenharmony_ci		context->seen_enough = 1;
1758c2ecf20Sopenharmony_ci	return;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/*
1798c2ecf20Sopenharmony_ci * Mark a range [start, start+len) in this map.  Returns true if the
1808c2ecf20Sopenharmony_ci * region was free, and false if there's a conflict or a problem.
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Within a char, the lowest bit of the char represents the byte with
1838c2ecf20Sopenharmony_ci * the smallest address
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_ciSTATIC bool
1868c2ecf20Sopenharmony_cixchk_xattr_set_map(
1878c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
1888c2ecf20Sopenharmony_ci	unsigned long		*map,
1898c2ecf20Sopenharmony_ci	unsigned int		start,
1908c2ecf20Sopenharmony_ci	unsigned int		len)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	unsigned int		mapsize = sc->mp->m_attr_geo->blksize;
1938c2ecf20Sopenharmony_ci	bool			ret = true;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (start >= mapsize)
1968c2ecf20Sopenharmony_ci		return false;
1978c2ecf20Sopenharmony_ci	if (start + len > mapsize) {
1988c2ecf20Sopenharmony_ci		len = mapsize - start;
1998c2ecf20Sopenharmony_ci		ret = false;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (find_next_bit(map, mapsize, start) < start + len)
2038c2ecf20Sopenharmony_ci		ret = false;
2048c2ecf20Sopenharmony_ci	bitmap_set(map, start, len);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return ret;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * Check the leaf freemap from the usage bitmap.  Returns false if the
2118c2ecf20Sopenharmony_ci * attr freemap has problems or points to used space.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_ciSTATIC bool
2148c2ecf20Sopenharmony_cixchk_xattr_check_freemap(
2158c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc,
2168c2ecf20Sopenharmony_ci	unsigned long			*map,
2178c2ecf20Sopenharmony_ci	struct xfs_attr3_icleaf_hdr	*leafhdr)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	unsigned long			*freemap = xchk_xattr_freemap(sc);
2208c2ecf20Sopenharmony_ci	unsigned long			*dstmap = xchk_xattr_dstmap(sc);
2218c2ecf20Sopenharmony_ci	unsigned int			mapsize = sc->mp->m_attr_geo->blksize;
2228c2ecf20Sopenharmony_ci	int				i;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Construct bitmap of freemap contents. */
2258c2ecf20Sopenharmony_ci	bitmap_zero(freemap, mapsize);
2268c2ecf20Sopenharmony_ci	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
2278c2ecf20Sopenharmony_ci		if (!xchk_xattr_set_map(sc, freemap,
2288c2ecf20Sopenharmony_ci				leafhdr->freemap[i].base,
2298c2ecf20Sopenharmony_ci				leafhdr->freemap[i].size))
2308c2ecf20Sopenharmony_ci			return false;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Look for bits that are set in freemap and are marked in use. */
2348c2ecf20Sopenharmony_ci	return bitmap_and(dstmap, freemap, map, mapsize) == 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*
2388c2ecf20Sopenharmony_ci * Check this leaf entry's relations to everything else.
2398c2ecf20Sopenharmony_ci * Returns the number of bytes used for the name/value data.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_ciSTATIC void
2428c2ecf20Sopenharmony_cixchk_xattr_entry(
2438c2ecf20Sopenharmony_ci	struct xchk_da_btree		*ds,
2448c2ecf20Sopenharmony_ci	int				level,
2458c2ecf20Sopenharmony_ci	char				*buf_end,
2468c2ecf20Sopenharmony_ci	struct xfs_attr_leafblock	*leaf,
2478c2ecf20Sopenharmony_ci	struct xfs_attr3_icleaf_hdr	*leafhdr,
2488c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_entry	*ent,
2498c2ecf20Sopenharmony_ci	int				idx,
2508c2ecf20Sopenharmony_ci	unsigned int			*usedbytes,
2518c2ecf20Sopenharmony_ci	__u32				*last_hashval)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct xfs_mount		*mp = ds->state->mp;
2548c2ecf20Sopenharmony_ci	unsigned long			*usedmap = xchk_xattr_usedmap(ds->sc);
2558c2ecf20Sopenharmony_ci	char				*name_end;
2568c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_name_local	*lentry;
2578c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_name_remote *rentry;
2588c2ecf20Sopenharmony_ci	unsigned int			nameidx;
2598c2ecf20Sopenharmony_ci	unsigned int			namesize;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (ent->pad2 != 0)
2628c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Hash values in order? */
2658c2ecf20Sopenharmony_ci	if (be32_to_cpu(ent->hashval) < *last_hashval)
2668c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
2678c2ecf20Sopenharmony_ci	*last_hashval = be32_to_cpu(ent->hashval);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	nameidx = be16_to_cpu(ent->nameidx);
2708c2ecf20Sopenharmony_ci	if (nameidx < leafhdr->firstused ||
2718c2ecf20Sopenharmony_ci	    nameidx >= mp->m_attr_geo->blksize) {
2728c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
2738c2ecf20Sopenharmony_ci		return;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Check the name information. */
2778c2ecf20Sopenharmony_ci	if (ent->flags & XFS_ATTR_LOCAL) {
2788c2ecf20Sopenharmony_ci		lentry = xfs_attr3_leaf_name_local(leaf, idx);
2798c2ecf20Sopenharmony_ci		namesize = xfs_attr_leaf_entsize_local(lentry->namelen,
2808c2ecf20Sopenharmony_ci				be16_to_cpu(lentry->valuelen));
2818c2ecf20Sopenharmony_ci		name_end = (char *)lentry + namesize;
2828c2ecf20Sopenharmony_ci		if (lentry->namelen == 0)
2838c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
2848c2ecf20Sopenharmony_ci	} else {
2858c2ecf20Sopenharmony_ci		rentry = xfs_attr3_leaf_name_remote(leaf, idx);
2868c2ecf20Sopenharmony_ci		namesize = xfs_attr_leaf_entsize_remote(rentry->namelen);
2878c2ecf20Sopenharmony_ci		name_end = (char *)rentry + namesize;
2888c2ecf20Sopenharmony_ci		if (rentry->namelen == 0 || rentry->valueblk == 0)
2898c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci	if (name_end > buf_end)
2928c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize))
2958c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
2968c2ecf20Sopenharmony_ci	if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
2978c2ecf20Sopenharmony_ci		*usedbytes += namesize;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/* Scrub an attribute leaf. */
3018c2ecf20Sopenharmony_ciSTATIC int
3028c2ecf20Sopenharmony_cixchk_xattr_block(
3038c2ecf20Sopenharmony_ci	struct xchk_da_btree		*ds,
3048c2ecf20Sopenharmony_ci	int				level)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct xfs_attr3_icleaf_hdr	leafhdr;
3078c2ecf20Sopenharmony_ci	struct xfs_mount		*mp = ds->state->mp;
3088c2ecf20Sopenharmony_ci	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];
3098c2ecf20Sopenharmony_ci	struct xfs_buf			*bp = blk->bp;
3108c2ecf20Sopenharmony_ci	xfs_dablk_t			*last_checked = ds->private;
3118c2ecf20Sopenharmony_ci	struct xfs_attr_leafblock	*leaf = bp->b_addr;
3128c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_entry	*ent;
3138c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_entry	*entries;
3148c2ecf20Sopenharmony_ci	unsigned long			*usedmap;
3158c2ecf20Sopenharmony_ci	char				*buf_end;
3168c2ecf20Sopenharmony_ci	size_t				off;
3178c2ecf20Sopenharmony_ci	__u32				last_hashval = 0;
3188c2ecf20Sopenharmony_ci	unsigned int			usedbytes = 0;
3198c2ecf20Sopenharmony_ci	unsigned int			hdrsize;
3208c2ecf20Sopenharmony_ci	int				i;
3218c2ecf20Sopenharmony_ci	int				error;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (*last_checked == blk->blkno)
3248c2ecf20Sopenharmony_ci		return 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Allocate memory for block usage checking. */
3278c2ecf20Sopenharmony_ci	error = xchk_setup_xattr_buf(ds->sc, 0, KM_MAYFAIL);
3288c2ecf20Sopenharmony_ci	if (error == -ENOMEM)
3298c2ecf20Sopenharmony_ci		return -EDEADLOCK;
3308c2ecf20Sopenharmony_ci	if (error)
3318c2ecf20Sopenharmony_ci		return error;
3328c2ecf20Sopenharmony_ci	usedmap = xchk_xattr_usedmap(ds->sc);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	*last_checked = blk->blkno;
3358c2ecf20Sopenharmony_ci	bitmap_zero(usedmap, mp->m_attr_geo->blksize);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Check all the padding. */
3388c2ecf20Sopenharmony_ci	if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) {
3398c2ecf20Sopenharmony_ci		struct xfs_attr3_leafblock	*leaf = bp->b_addr;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 ||
3428c2ecf20Sopenharmony_ci		    leaf->hdr.info.hdr.pad != 0)
3438c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
3448c2ecf20Sopenharmony_ci	} else {
3458c2ecf20Sopenharmony_ci		if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0)
3468c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Check the leaf header */
3508c2ecf20Sopenharmony_ci	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
3518c2ecf20Sopenharmony_ci	hdrsize = xfs_attr3_leaf_hdr_size(leaf);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (leafhdr.usedbytes > mp->m_attr_geo->blksize)
3548c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3558c2ecf20Sopenharmony_ci	if (leafhdr.firstused > mp->m_attr_geo->blksize)
3568c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3578c2ecf20Sopenharmony_ci	if (leafhdr.firstused < hdrsize)
3588c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3598c2ecf20Sopenharmony_ci	if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize))
3608c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
3638c2ecf20Sopenharmony_ci		goto out;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	entries = xfs_attr3_leaf_entryp(leaf);
3668c2ecf20Sopenharmony_ci	if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused)
3678c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize;
3708c2ecf20Sopenharmony_ci	for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) {
3718c2ecf20Sopenharmony_ci		/* Mark the leaf entry itself. */
3728c2ecf20Sopenharmony_ci		off = (char *)ent - (char *)leaf;
3738c2ecf20Sopenharmony_ci		if (!xchk_xattr_set_map(ds->sc, usedmap, off,
3748c2ecf20Sopenharmony_ci				sizeof(xfs_attr_leaf_entry_t))) {
3758c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
3768c2ecf20Sopenharmony_ci			goto out;
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		/* Check the entry and nameval. */
3808c2ecf20Sopenharmony_ci		xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr,
3818c2ecf20Sopenharmony_ci				ent, i, &usedbytes, &last_hashval);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
3848c2ecf20Sopenharmony_ci			goto out;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr))
3888c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (leafhdr.usedbytes != usedbytes)
3918c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciout:
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci/* Scrub a attribute btree record. */
3988c2ecf20Sopenharmony_ciSTATIC int
3998c2ecf20Sopenharmony_cixchk_xattr_rec(
4008c2ecf20Sopenharmony_ci	struct xchk_da_btree		*ds,
4018c2ecf20Sopenharmony_ci	int				level)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct xfs_mount		*mp = ds->state->mp;
4048c2ecf20Sopenharmony_ci	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];
4058c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_name_local	*lentry;
4068c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_name_remote	*rentry;
4078c2ecf20Sopenharmony_ci	struct xfs_buf			*bp;
4088c2ecf20Sopenharmony_ci	struct xfs_attr_leaf_entry	*ent;
4098c2ecf20Sopenharmony_ci	xfs_dahash_t			calc_hash;
4108c2ecf20Sopenharmony_ci	xfs_dahash_t			hash;
4118c2ecf20Sopenharmony_ci	int				nameidx;
4128c2ecf20Sopenharmony_ci	int				hdrsize;
4138c2ecf20Sopenharmony_ci	unsigned int			badflags;
4148c2ecf20Sopenharmony_ci	int				error;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* Check the whole block, if necessary. */
4218c2ecf20Sopenharmony_ci	error = xchk_xattr_block(ds, level);
4228c2ecf20Sopenharmony_ci	if (error)
4238c2ecf20Sopenharmony_ci		goto out;
4248c2ecf20Sopenharmony_ci	if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
4258c2ecf20Sopenharmony_ci		goto out;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* Check the hash of the entry. */
4288c2ecf20Sopenharmony_ci	error = xchk_da_btree_hash(ds, level, &ent->hashval);
4298c2ecf20Sopenharmony_ci	if (error)
4308c2ecf20Sopenharmony_ci		goto out;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Find the attr entry's location. */
4338c2ecf20Sopenharmony_ci	bp = blk->bp;
4348c2ecf20Sopenharmony_ci	hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr);
4358c2ecf20Sopenharmony_ci	nameidx = be16_to_cpu(ent->nameidx);
4368c2ecf20Sopenharmony_ci	if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) {
4378c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
4388c2ecf20Sopenharmony_ci		goto out;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Retrieve the entry and check it. */
4428c2ecf20Sopenharmony_ci	hash = be32_to_cpu(ent->hashval);
4438c2ecf20Sopenharmony_ci	badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE |
4448c2ecf20Sopenharmony_ci			XFS_ATTR_INCOMPLETE);
4458c2ecf20Sopenharmony_ci	if ((ent->flags & badflags) != 0)
4468c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
4478c2ecf20Sopenharmony_ci	if (ent->flags & XFS_ATTR_LOCAL) {
4488c2ecf20Sopenharmony_ci		lentry = (struct xfs_attr_leaf_name_local *)
4498c2ecf20Sopenharmony_ci				(((char *)bp->b_addr) + nameidx);
4508c2ecf20Sopenharmony_ci		if (lentry->namelen <= 0) {
4518c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
4528c2ecf20Sopenharmony_ci			goto out;
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci		calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen);
4558c2ecf20Sopenharmony_ci	} else {
4568c2ecf20Sopenharmony_ci		rentry = (struct xfs_attr_leaf_name_remote *)
4578c2ecf20Sopenharmony_ci				(((char *)bp->b_addr) + nameidx);
4588c2ecf20Sopenharmony_ci		if (rentry->namelen <= 0) {
4598c2ecf20Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
4608c2ecf20Sopenharmony_ci			goto out;
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci		calc_hash = xfs_da_hashname(rentry->name, rentry->namelen);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	if (calc_hash != hash)
4658c2ecf20Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ciout:
4688c2ecf20Sopenharmony_ci	return error;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/* Scrub the extended attribute metadata. */
4728c2ecf20Sopenharmony_ciint
4738c2ecf20Sopenharmony_cixchk_xattr(
4748c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct xchk_xattr		sx;
4778c2ecf20Sopenharmony_ci	xfs_dablk_t			last_checked = -1U;
4788c2ecf20Sopenharmony_ci	int				error = 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!xfs_inode_hasattr(sc->ip))
4818c2ecf20Sopenharmony_ci		return -ENOENT;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	memset(&sx, 0, sizeof(sx));
4848c2ecf20Sopenharmony_ci	/* Check attribute tree structure */
4858c2ecf20Sopenharmony_ci	error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec,
4868c2ecf20Sopenharmony_ci			&last_checked);
4878c2ecf20Sopenharmony_ci	if (error)
4888c2ecf20Sopenharmony_ci		goto out;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
4918c2ecf20Sopenharmony_ci		goto out;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Check that every attr key can also be looked up by hash. */
4948c2ecf20Sopenharmony_ci	sx.context.dp = sc->ip;
4958c2ecf20Sopenharmony_ci	sx.context.resynch = 1;
4968c2ecf20Sopenharmony_ci	sx.context.put_listent = xchk_xattr_listent;
4978c2ecf20Sopenharmony_ci	sx.context.tp = sc->tp;
4988c2ecf20Sopenharmony_ci	sx.context.allow_incomplete = true;
4998c2ecf20Sopenharmony_ci	sx.sc = sc;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/*
5028c2ecf20Sopenharmony_ci	 * Look up every xattr in this file by name.
5038c2ecf20Sopenharmony_ci	 *
5048c2ecf20Sopenharmony_ci	 * Use the backend implementation of xfs_attr_list to call
5058c2ecf20Sopenharmony_ci	 * xchk_xattr_listent on every attribute key in this inode.
5068c2ecf20Sopenharmony_ci	 * In other words, we use the same iterator/callback mechanism
5078c2ecf20Sopenharmony_ci	 * that listattr uses to scrub extended attributes, though in our
5088c2ecf20Sopenharmony_ci	 * _listent function, we check the value of the attribute.
5098c2ecf20Sopenharmony_ci	 *
5108c2ecf20Sopenharmony_ci	 * The VFS only locks i_rwsem when modifying attrs, so keep all
5118c2ecf20Sopenharmony_ci	 * three locks held because that's the only way to ensure we're
5128c2ecf20Sopenharmony_ci	 * the only thread poking into the da btree.  We traverse the da
5138c2ecf20Sopenharmony_ci	 * btree while holding a leaf buffer locked for the xattr name
5148c2ecf20Sopenharmony_ci	 * iteration, which doesn't really follow the usual buffer
5158c2ecf20Sopenharmony_ci	 * locking order.
5168c2ecf20Sopenharmony_ci	 */
5178c2ecf20Sopenharmony_ci	error = xfs_attr_list_ilocked(&sx.context);
5188c2ecf20Sopenharmony_ci	if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
5198c2ecf20Sopenharmony_ci		goto out;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* Did our listent function try to return any errors? */
5228c2ecf20Sopenharmony_ci	if (sx.context.seen_enough < 0)
5238c2ecf20Sopenharmony_ci		error = sx.context.seen_enough;
5248c2ecf20Sopenharmony_ciout:
5258c2ecf20Sopenharmony_ci	return error;
5268c2ecf20Sopenharmony_ci}
527