162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1162306a36Sopenharmony_ci#include "xfs_mount.h" 1262306a36Sopenharmony_ci#include "xfs_log_format.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_da_format.h" 1562306a36Sopenharmony_ci#include "xfs_da_btree.h" 1662306a36Sopenharmony_ci#include "xfs_attr.h" 1762306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 1862306a36Sopenharmony_ci#include "xfs_attr_sf.h" 1962306a36Sopenharmony_ci#include "scrub/scrub.h" 2062306a36Sopenharmony_ci#include "scrub/common.h" 2162306a36Sopenharmony_ci#include "scrub/dabtree.h" 2262306a36Sopenharmony_ci#include "scrub/attr.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Free the buffers linked from the xattr buffer. */ 2562306a36Sopenharmony_cistatic void 2662306a36Sopenharmony_cixchk_xattr_buf_cleanup( 2762306a36Sopenharmony_ci void *priv) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct xchk_xattr_buf *ab = priv; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci kvfree(ab->freemap); 3262306a36Sopenharmony_ci ab->freemap = NULL; 3362306a36Sopenharmony_ci kvfree(ab->usedmap); 3462306a36Sopenharmony_ci ab->usedmap = NULL; 3562306a36Sopenharmony_ci kvfree(ab->value); 3662306a36Sopenharmony_ci ab->value = NULL; 3762306a36Sopenharmony_ci ab->value_sz = 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Allocate the free space bitmap if we're trying harder; there are leaf blocks 4262306a36Sopenharmony_ci * in the attr fork; or we can't tell if there are leaf blocks. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic inline bool 4562306a36Sopenharmony_cixchk_xattr_want_freemap( 4662306a36Sopenharmony_ci struct xfs_scrub *sc) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct xfs_ifork *ifp; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (sc->flags & XCHK_TRY_HARDER) 5162306a36Sopenharmony_ci return true; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!sc->ip) 5462306a36Sopenharmony_ci return true; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK); 5762306a36Sopenharmony_ci if (!ifp) 5862306a36Sopenharmony_ci return false; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return xfs_ifork_has_extents(ifp); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Allocate enough memory to hold an attr value and attr block bitmaps, 6562306a36Sopenharmony_ci * reallocating the buffer if necessary. Buffer contents are not preserved 6662306a36Sopenharmony_ci * across a reallocation. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic int 6962306a36Sopenharmony_cixchk_setup_xattr_buf( 7062306a36Sopenharmony_ci struct xfs_scrub *sc, 7162306a36Sopenharmony_ci size_t value_size) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci size_t bmp_sz; 7462306a36Sopenharmony_ci struct xchk_xattr_buf *ab = sc->buf; 7562306a36Sopenharmony_ci void *new_val; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci bmp_sz = sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (ab) 8062306a36Sopenharmony_ci goto resize_value; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ab = kvzalloc(sizeof(struct xchk_xattr_buf), XCHK_GFP_FLAGS); 8362306a36Sopenharmony_ci if (!ab) 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci sc->buf = ab; 8662306a36Sopenharmony_ci sc->buf_cleanup = xchk_xattr_buf_cleanup; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ab->usedmap = kvmalloc(bmp_sz, XCHK_GFP_FLAGS); 8962306a36Sopenharmony_ci if (!ab->usedmap) 9062306a36Sopenharmony_ci return -ENOMEM; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (xchk_xattr_want_freemap(sc)) { 9362306a36Sopenharmony_ci ab->freemap = kvmalloc(bmp_sz, XCHK_GFP_FLAGS); 9462306a36Sopenharmony_ci if (!ab->freemap) 9562306a36Sopenharmony_ci return -ENOMEM; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciresize_value: 9962306a36Sopenharmony_ci if (ab->value_sz >= value_size) 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (ab->value) { 10362306a36Sopenharmony_ci kvfree(ab->value); 10462306a36Sopenharmony_ci ab->value = NULL; 10562306a36Sopenharmony_ci ab->value_sz = 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci new_val = kvmalloc(value_size, XCHK_GFP_FLAGS); 10962306a36Sopenharmony_ci if (!new_val) 11062306a36Sopenharmony_ci return -ENOMEM; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ab->value = new_val; 11362306a36Sopenharmony_ci ab->value_sz = value_size; 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* Set us up to scrub an inode's extended attributes. */ 11862306a36Sopenharmony_ciint 11962306a36Sopenharmony_cixchk_setup_xattr( 12062306a36Sopenharmony_ci struct xfs_scrub *sc) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int error; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * We failed to get memory while checking attrs, so this time try to 12662306a36Sopenharmony_ci * get all the memory we're ever going to need. Allocate the buffer 12762306a36Sopenharmony_ci * without the inode lock held, which means we can sleep. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci if (sc->flags & XCHK_TRY_HARDER) { 13062306a36Sopenharmony_ci error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX); 13162306a36Sopenharmony_ci if (error) 13262306a36Sopenharmony_ci return error; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return xchk_setup_inode_contents(sc, 0); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* Extended Attributes */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct xchk_xattr { 14162306a36Sopenharmony_ci struct xfs_attr_list_context context; 14262306a36Sopenharmony_ci struct xfs_scrub *sc; 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * Check that an extended attribute key can be looked up by hash. 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * We use the XFS attribute list iterator (i.e. xfs_attr_list_ilocked) 14962306a36Sopenharmony_ci * to call this function for every attribute key in an inode. Once 15062306a36Sopenharmony_ci * we're here, we load the attribute value to see if any errors happen, 15162306a36Sopenharmony_ci * or if we get more or less data than we expected. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic void 15462306a36Sopenharmony_cixchk_xattr_listent( 15562306a36Sopenharmony_ci struct xfs_attr_list_context *context, 15662306a36Sopenharmony_ci int flags, 15762306a36Sopenharmony_ci unsigned char *name, 15862306a36Sopenharmony_ci int namelen, 15962306a36Sopenharmony_ci int valuelen) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct xfs_da_args args = { 16262306a36Sopenharmony_ci .op_flags = XFS_DA_OP_NOTIME, 16362306a36Sopenharmony_ci .attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK, 16462306a36Sopenharmony_ci .geo = context->dp->i_mount->m_attr_geo, 16562306a36Sopenharmony_ci .whichfork = XFS_ATTR_FORK, 16662306a36Sopenharmony_ci .dp = context->dp, 16762306a36Sopenharmony_ci .name = name, 16862306a36Sopenharmony_ci .namelen = namelen, 16962306a36Sopenharmony_ci .hashval = xfs_da_hashname(name, namelen), 17062306a36Sopenharmony_ci .trans = context->tp, 17162306a36Sopenharmony_ci .valuelen = valuelen, 17262306a36Sopenharmony_ci }; 17362306a36Sopenharmony_ci struct xchk_xattr_buf *ab; 17462306a36Sopenharmony_ci struct xchk_xattr *sx; 17562306a36Sopenharmony_ci int error = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci sx = container_of(context, struct xchk_xattr, context); 17862306a36Sopenharmony_ci ab = sx->sc->buf; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (xchk_should_terminate(sx->sc, &error)) { 18162306a36Sopenharmony_ci context->seen_enough = error; 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (flags & XFS_ATTR_INCOMPLETE) { 18662306a36Sopenharmony_ci /* Incomplete attr key, just mark the inode for preening. */ 18762306a36Sopenharmony_ci xchk_ino_set_preen(sx->sc, context->dp->i_ino); 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Only one namespace bit allowed. */ 19262306a36Sopenharmony_ci if (hweight32(flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) { 19362306a36Sopenharmony_ci xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 19462306a36Sopenharmony_ci goto fail_xref; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Does this name make sense? */ 19862306a36Sopenharmony_ci if (!xfs_attr_namecheck(name, namelen)) { 19962306a36Sopenharmony_ci xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 20062306a36Sopenharmony_ci goto fail_xref; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * Local xattr values are stored in the attr leaf block, so we don't 20562306a36Sopenharmony_ci * need to retrieve the value from a remote block to detect corruption 20662306a36Sopenharmony_ci * problems. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci if (flags & XFS_ATTR_LOCAL) 20962306a36Sopenharmony_ci goto fail_xref; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * Try to allocate enough memory to extrat the attr value. If that 21362306a36Sopenharmony_ci * doesn't work, we overload the seen_enough variable to convey 21462306a36Sopenharmony_ci * the error message back to the main scrub function. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci error = xchk_setup_xattr_buf(sx->sc, valuelen); 21762306a36Sopenharmony_ci if (error == -ENOMEM) 21862306a36Sopenharmony_ci error = -EDEADLOCK; 21962306a36Sopenharmony_ci if (error) { 22062306a36Sopenharmony_ci context->seen_enough = error; 22162306a36Sopenharmony_ci return; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci args.value = ab->value; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci error = xfs_attr_get_ilocked(&args); 22762306a36Sopenharmony_ci /* ENODATA means the hash lookup failed and the attr is bad */ 22862306a36Sopenharmony_ci if (error == -ENODATA) 22962306a36Sopenharmony_ci error = -EFSCORRUPTED; 23062306a36Sopenharmony_ci if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 23162306a36Sopenharmony_ci &error)) 23262306a36Sopenharmony_ci goto fail_xref; 23362306a36Sopenharmony_ci if (args.valuelen != valuelen) 23462306a36Sopenharmony_ci xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 23562306a36Sopenharmony_ci args.blkno); 23662306a36Sopenharmony_cifail_xref: 23762306a36Sopenharmony_ci if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 23862306a36Sopenharmony_ci context->seen_enough = 1; 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* 24362306a36Sopenharmony_ci * Mark a range [start, start+len) in this map. Returns true if the 24462306a36Sopenharmony_ci * region was free, and false if there's a conflict or a problem. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Within a char, the lowest bit of the char represents the byte with 24762306a36Sopenharmony_ci * the smallest address 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ciSTATIC bool 25062306a36Sopenharmony_cixchk_xattr_set_map( 25162306a36Sopenharmony_ci struct xfs_scrub *sc, 25262306a36Sopenharmony_ci unsigned long *map, 25362306a36Sopenharmony_ci unsigned int start, 25462306a36Sopenharmony_ci unsigned int len) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unsigned int mapsize = sc->mp->m_attr_geo->blksize; 25762306a36Sopenharmony_ci bool ret = true; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (start >= mapsize) 26062306a36Sopenharmony_ci return false; 26162306a36Sopenharmony_ci if (start + len > mapsize) { 26262306a36Sopenharmony_ci len = mapsize - start; 26362306a36Sopenharmony_ci ret = false; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (find_next_bit(map, mapsize, start) < start + len) 26762306a36Sopenharmony_ci ret = false; 26862306a36Sopenharmony_ci bitmap_set(map, start, len); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* 27462306a36Sopenharmony_ci * Check the leaf freemap from the usage bitmap. Returns false if the 27562306a36Sopenharmony_ci * attr freemap has problems or points to used space. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ciSTATIC bool 27862306a36Sopenharmony_cixchk_xattr_check_freemap( 27962306a36Sopenharmony_ci struct xfs_scrub *sc, 28062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leafhdr) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct xchk_xattr_buf *ab = sc->buf; 28362306a36Sopenharmony_ci unsigned int mapsize = sc->mp->m_attr_geo->blksize; 28462306a36Sopenharmony_ci int i; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Construct bitmap of freemap contents. */ 28762306a36Sopenharmony_ci bitmap_zero(ab->freemap, mapsize); 28862306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 28962306a36Sopenharmony_ci if (!xchk_xattr_set_map(sc, ab->freemap, 29062306a36Sopenharmony_ci leafhdr->freemap[i].base, 29162306a36Sopenharmony_ci leafhdr->freemap[i].size)) 29262306a36Sopenharmony_ci return false; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Look for bits that are set in freemap and are marked in use. */ 29662306a36Sopenharmony_ci return !bitmap_intersects(ab->freemap, ab->usedmap, mapsize); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * Check this leaf entry's relations to everything else. 30162306a36Sopenharmony_ci * Returns the number of bytes used for the name/value data. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ciSTATIC void 30462306a36Sopenharmony_cixchk_xattr_entry( 30562306a36Sopenharmony_ci struct xchk_da_btree *ds, 30662306a36Sopenharmony_ci int level, 30762306a36Sopenharmony_ci char *buf_end, 30862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf, 30962306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leafhdr, 31062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *ent, 31162306a36Sopenharmony_ci int idx, 31262306a36Sopenharmony_ci unsigned int *usedbytes, 31362306a36Sopenharmony_ci __u32 *last_hashval) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 31662306a36Sopenharmony_ci struct xchk_xattr_buf *ab = ds->sc->buf; 31762306a36Sopenharmony_ci char *name_end; 31862306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *lentry; 31962306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *rentry; 32062306a36Sopenharmony_ci unsigned int nameidx; 32162306a36Sopenharmony_ci unsigned int namesize; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (ent->pad2 != 0) 32462306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Hash values in order? */ 32762306a36Sopenharmony_ci if (be32_to_cpu(ent->hashval) < *last_hashval) 32862306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 32962306a36Sopenharmony_ci *last_hashval = be32_to_cpu(ent->hashval); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci nameidx = be16_to_cpu(ent->nameidx); 33262306a36Sopenharmony_ci if (nameidx < leafhdr->firstused || 33362306a36Sopenharmony_ci nameidx >= mp->m_attr_geo->blksize) { 33462306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 33562306a36Sopenharmony_ci return; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Check the name information. */ 33962306a36Sopenharmony_ci if (ent->flags & XFS_ATTR_LOCAL) { 34062306a36Sopenharmony_ci lentry = xfs_attr3_leaf_name_local(leaf, idx); 34162306a36Sopenharmony_ci namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 34262306a36Sopenharmony_ci be16_to_cpu(lentry->valuelen)); 34362306a36Sopenharmony_ci name_end = (char *)lentry + namesize; 34462306a36Sopenharmony_ci if (lentry->namelen == 0) 34562306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci rentry = xfs_attr3_leaf_name_remote(leaf, idx); 34862306a36Sopenharmony_ci namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 34962306a36Sopenharmony_ci name_end = (char *)rentry + namesize; 35062306a36Sopenharmony_ci if (rentry->namelen == 0 || rentry->valueblk == 0) 35162306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (name_end > buf_end) 35462306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, ab->usedmap, nameidx, namesize)) 35762306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 35862306a36Sopenharmony_ci if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 35962306a36Sopenharmony_ci *usedbytes += namesize; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* Scrub an attribute leaf. */ 36362306a36Sopenharmony_ciSTATIC int 36462306a36Sopenharmony_cixchk_xattr_block( 36562306a36Sopenharmony_ci struct xchk_da_btree *ds, 36662306a36Sopenharmony_ci int level) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr leafhdr; 36962306a36Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 37062306a36Sopenharmony_ci struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 37162306a36Sopenharmony_ci struct xfs_buf *bp = blk->bp; 37262306a36Sopenharmony_ci xfs_dablk_t *last_checked = ds->private; 37362306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf = bp->b_addr; 37462306a36Sopenharmony_ci struct xfs_attr_leaf_entry *ent; 37562306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 37662306a36Sopenharmony_ci struct xchk_xattr_buf *ab = ds->sc->buf; 37762306a36Sopenharmony_ci char *buf_end; 37862306a36Sopenharmony_ci size_t off; 37962306a36Sopenharmony_ci __u32 last_hashval = 0; 38062306a36Sopenharmony_ci unsigned int usedbytes = 0; 38162306a36Sopenharmony_ci unsigned int hdrsize; 38262306a36Sopenharmony_ci int i; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (*last_checked == blk->blkno) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci *last_checked = blk->blkno; 38862306a36Sopenharmony_ci bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Check all the padding. */ 39162306a36Sopenharmony_ci if (xfs_has_crc(ds->sc->mp)) { 39262306a36Sopenharmony_ci struct xfs_attr3_leafblock *leaf3 = bp->b_addr; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (leaf3->hdr.pad1 != 0 || leaf3->hdr.pad2 != 0 || 39562306a36Sopenharmony_ci leaf3->hdr.info.hdr.pad != 0) 39662306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 39762306a36Sopenharmony_ci } else { 39862306a36Sopenharmony_ci if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 39962306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Check the leaf header */ 40362306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 40462306a36Sopenharmony_ci hdrsize = xfs_attr3_leaf_hdr_size(leaf); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 40762306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 40862306a36Sopenharmony_ci if (leafhdr.firstused > mp->m_attr_geo->blksize) 40962306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 41062306a36Sopenharmony_ci if (leafhdr.firstused < hdrsize) 41162306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 41262306a36Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, ab->usedmap, 0, hdrsize)) 41362306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 41662306a36Sopenharmony_ci goto out; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 41962306a36Sopenharmony_ci if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 42062306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 42362306a36Sopenharmony_ci for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 42462306a36Sopenharmony_ci /* Mark the leaf entry itself. */ 42562306a36Sopenharmony_ci off = (char *)ent - (char *)leaf; 42662306a36Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, ab->usedmap, off, 42762306a36Sopenharmony_ci sizeof(xfs_attr_leaf_entry_t))) { 42862306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 42962306a36Sopenharmony_ci goto out; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Check the entry and nameval. */ 43362306a36Sopenharmony_ci xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 43462306a36Sopenharmony_ci ent, i, &usedbytes, &last_hashval); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 43762306a36Sopenharmony_ci goto out; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!xchk_xattr_check_freemap(ds->sc, &leafhdr)) 44162306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (leafhdr.usedbytes != usedbytes) 44462306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciout: 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* Scrub a attribute btree record. */ 45162306a36Sopenharmony_ciSTATIC int 45262306a36Sopenharmony_cixchk_xattr_rec( 45362306a36Sopenharmony_ci struct xchk_da_btree *ds, 45462306a36Sopenharmony_ci int level) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 45762306a36Sopenharmony_ci struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 45862306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *lentry; 45962306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *rentry; 46062306a36Sopenharmony_ci struct xfs_buf *bp; 46162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *ent; 46262306a36Sopenharmony_ci xfs_dahash_t calc_hash; 46362306a36Sopenharmony_ci xfs_dahash_t hash; 46462306a36Sopenharmony_ci int nameidx; 46562306a36Sopenharmony_ci int hdrsize; 46662306a36Sopenharmony_ci unsigned int badflags; 46762306a36Sopenharmony_ci int error; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Check the whole block, if necessary. */ 47462306a36Sopenharmony_ci error = xchk_xattr_block(ds, level); 47562306a36Sopenharmony_ci if (error) 47662306a36Sopenharmony_ci goto out; 47762306a36Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 47862306a36Sopenharmony_ci goto out; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Check the hash of the entry. */ 48162306a36Sopenharmony_ci error = xchk_da_btree_hash(ds, level, &ent->hashval); 48262306a36Sopenharmony_ci if (error) 48362306a36Sopenharmony_ci goto out; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Find the attr entry's location. */ 48662306a36Sopenharmony_ci bp = blk->bp; 48762306a36Sopenharmony_ci hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 48862306a36Sopenharmony_ci nameidx = be16_to_cpu(ent->nameidx); 48962306a36Sopenharmony_ci if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 49062306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 49162306a36Sopenharmony_ci goto out; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Retrieve the entry and check it. */ 49562306a36Sopenharmony_ci hash = be32_to_cpu(ent->hashval); 49662306a36Sopenharmony_ci badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 49762306a36Sopenharmony_ci XFS_ATTR_INCOMPLETE); 49862306a36Sopenharmony_ci if ((ent->flags & badflags) != 0) 49962306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 50062306a36Sopenharmony_ci if (ent->flags & XFS_ATTR_LOCAL) { 50162306a36Sopenharmony_ci lentry = (struct xfs_attr_leaf_name_local *) 50262306a36Sopenharmony_ci (((char *)bp->b_addr) + nameidx); 50362306a36Sopenharmony_ci if (lentry->namelen <= 0) { 50462306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 50562306a36Sopenharmony_ci goto out; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci rentry = (struct xfs_attr_leaf_name_remote *) 51062306a36Sopenharmony_ci (((char *)bp->b_addr) + nameidx); 51162306a36Sopenharmony_ci if (rentry->namelen <= 0) { 51262306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 51362306a36Sopenharmony_ci goto out; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci if (calc_hash != hash) 51862306a36Sopenharmony_ci xchk_da_set_corrupt(ds, level); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ciout: 52162306a36Sopenharmony_ci return error; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* Check space usage of shortform attrs. */ 52562306a36Sopenharmony_ciSTATIC int 52662306a36Sopenharmony_cixchk_xattr_check_sf( 52762306a36Sopenharmony_ci struct xfs_scrub *sc) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct xchk_xattr_buf *ab = sc->buf; 53062306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 53162306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 53262306a36Sopenharmony_ci struct xfs_attr_sf_entry *next; 53362306a36Sopenharmony_ci struct xfs_ifork *ifp; 53462306a36Sopenharmony_ci unsigned char *end; 53562306a36Sopenharmony_ci int i; 53662306a36Sopenharmony_ci int error = 0; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci bitmap_zero(ab->usedmap, ifp->if_bytes); 54162306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)sc->ip->i_af.if_u1.if_data; 54262306a36Sopenharmony_ci end = (unsigned char *)ifp->if_u1.if_data + ifp->if_bytes; 54362306a36Sopenharmony_ci xchk_xattr_set_map(sc, ab->usedmap, 0, sizeof(sf->hdr)); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci sfe = &sf->list[0]; 54662306a36Sopenharmony_ci if ((unsigned char *)sfe > end) { 54762306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci for (i = 0; i < sf->hdr.count; i++) { 55262306a36Sopenharmony_ci unsigned char *name = sfe->nameval; 55362306a36Sopenharmony_ci unsigned char *value = &sfe->nameval[sfe->namelen]; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (xchk_should_terminate(sc, &error)) 55662306a36Sopenharmony_ci return error; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci next = xfs_attr_sf_nextentry(sfe); 55962306a36Sopenharmony_ci if ((unsigned char *)next > end) { 56062306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (!xchk_xattr_set_map(sc, ab->usedmap, 56562306a36Sopenharmony_ci (char *)sfe - (char *)sf, 56662306a36Sopenharmony_ci sizeof(struct xfs_attr_sf_entry))) { 56762306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!xchk_xattr_set_map(sc, ab->usedmap, 57262306a36Sopenharmony_ci (char *)name - (char *)sf, 57362306a36Sopenharmony_ci sfe->namelen)) { 57462306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!xchk_xattr_set_map(sc, ab->usedmap, 57962306a36Sopenharmony_ci (char *)value - (char *)sf, 58062306a36Sopenharmony_ci sfe->valuelen)) { 58162306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci sfe = next; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* Scrub the extended attribute metadata. */ 59262306a36Sopenharmony_ciint 59362306a36Sopenharmony_cixchk_xattr( 59462306a36Sopenharmony_ci struct xfs_scrub *sc) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct xchk_xattr sx = { 59762306a36Sopenharmony_ci .sc = sc, 59862306a36Sopenharmony_ci .context = { 59962306a36Sopenharmony_ci .dp = sc->ip, 60062306a36Sopenharmony_ci .tp = sc->tp, 60162306a36Sopenharmony_ci .resynch = 1, 60262306a36Sopenharmony_ci .put_listent = xchk_xattr_listent, 60362306a36Sopenharmony_ci .allow_incomplete = true, 60462306a36Sopenharmony_ci }, 60562306a36Sopenharmony_ci }; 60662306a36Sopenharmony_ci xfs_dablk_t last_checked = -1U; 60762306a36Sopenharmony_ci int error = 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!xfs_inode_hasattr(sc->ip)) 61062306a36Sopenharmony_ci return -ENOENT; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Allocate memory for xattr checking. */ 61362306a36Sopenharmony_ci error = xchk_setup_xattr_buf(sc, 0); 61462306a36Sopenharmony_ci if (error == -ENOMEM) 61562306a36Sopenharmony_ci return -EDEADLOCK; 61662306a36Sopenharmony_ci if (error) 61762306a36Sopenharmony_ci return error; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Check the physical structure of the xattr. */ 62062306a36Sopenharmony_ci if (sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL) 62162306a36Sopenharmony_ci error = xchk_xattr_check_sf(sc); 62262306a36Sopenharmony_ci else 62362306a36Sopenharmony_ci error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 62462306a36Sopenharmony_ci &last_checked); 62562306a36Sopenharmony_ci if (error) 62662306a36Sopenharmony_ci return error; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * Look up every xattr in this file by name and hash. 63362306a36Sopenharmony_ci * 63462306a36Sopenharmony_ci * Use the backend implementation of xfs_attr_list to call 63562306a36Sopenharmony_ci * xchk_xattr_listent on every attribute key in this inode. 63662306a36Sopenharmony_ci * In other words, we use the same iterator/callback mechanism 63762306a36Sopenharmony_ci * that listattr uses to scrub extended attributes, though in our 63862306a36Sopenharmony_ci * _listent function, we check the value of the attribute. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * The VFS only locks i_rwsem when modifying attrs, so keep all 64162306a36Sopenharmony_ci * three locks held because that's the only way to ensure we're 64262306a36Sopenharmony_ci * the only thread poking into the da btree. We traverse the da 64362306a36Sopenharmony_ci * btree while holding a leaf buffer locked for the xattr name 64462306a36Sopenharmony_ci * iteration, which doesn't really follow the usual buffer 64562306a36Sopenharmony_ci * locking order. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci error = xfs_attr_list_ilocked(&sx.context); 64862306a36Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 64962306a36Sopenharmony_ci return error; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Did our listent function try to return any errors? */ 65262306a36Sopenharmony_ci if (sx.context.seen_enough < 0) 65362306a36Sopenharmony_ci return sx.context.seen_enough; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 657