10b61f8a4SDave Chinner // SPDX-License-Identifier: GPL-2.0+ 2eec0482eSDarrick J. Wong /* 3eec0482eSDarrick J. Wong * Copyright (C) 2017 Oracle. All Rights Reserved. 4eec0482eSDarrick J. Wong * Author: Darrick J. Wong <darrick.wong@oracle.com> 5eec0482eSDarrick J. Wong */ 6eec0482eSDarrick J. Wong #include "xfs.h" 7eec0482eSDarrick J. Wong #include "xfs_fs.h" 8eec0482eSDarrick J. Wong #include "xfs_shared.h" 9eec0482eSDarrick J. Wong #include "xfs_format.h" 10eec0482eSDarrick J. Wong #include "xfs_trans_resv.h" 11eec0482eSDarrick J. Wong #include "xfs_mount.h" 12eec0482eSDarrick J. Wong #include "xfs_log_format.h" 13eec0482eSDarrick J. Wong #include "xfs_inode.h" 14eec0482eSDarrick J. Wong #include "xfs_da_format.h" 15eec0482eSDarrick J. Wong #include "xfs_da_btree.h" 16eec0482eSDarrick J. Wong #include "xfs_attr.h" 17eec0482eSDarrick J. Wong #include "xfs_attr_leaf.h" 18eec0482eSDarrick J. Wong #include "scrub/scrub.h" 19eec0482eSDarrick J. Wong #include "scrub/common.h" 20eec0482eSDarrick J. Wong #include "scrub/dabtree.h" 213addd248SDarrick J. Wong #include "scrub/attr.h" 22eec0482eSDarrick J. Wong 236d6cceddSDarrick J. Wong /* 246d6cceddSDarrick J. Wong * Allocate enough memory to hold an attr value and attr block bitmaps, 256d6cceddSDarrick J. Wong * reallocating the buffer if necessary. Buffer contents are not preserved 266d6cceddSDarrick J. Wong * across a reallocation. 276d6cceddSDarrick J. Wong */ 2800816759SDarrick J. Wong int 2900816759SDarrick J. Wong xchk_setup_xattr_buf( 3000816759SDarrick J. Wong struct xfs_scrub *sc, 316d6cceddSDarrick J. Wong size_t value_size, 326d6cceddSDarrick J. Wong xfs_km_flags_t flags) 3300816759SDarrick J. Wong { 3400816759SDarrick J. Wong size_t sz; 356d6cceddSDarrick J. Wong struct xchk_xattr_buf *ab = sc->buf; 3600816759SDarrick J. Wong 3700816759SDarrick J. Wong /* 3800816759SDarrick J. Wong * We need enough space to read an xattr value from the file or enough 3900816759SDarrick J. Wong * space to hold three copies of the xattr free space bitmap. We don't 4000816759SDarrick J. Wong * need the buffer space for both purposes at the same time. 4100816759SDarrick J. Wong */ 4200816759SDarrick J. Wong sz = 3 * sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize); 4300816759SDarrick J. Wong sz = max_t(size_t, sz, value_size); 4400816759SDarrick J. Wong 456d6cceddSDarrick J. Wong /* 466d6cceddSDarrick J. Wong * If there's already a buffer, figure out if we need to reallocate it 476d6cceddSDarrick J. Wong * to accommodate a larger size. 486d6cceddSDarrick J. Wong */ 496d6cceddSDarrick J. Wong if (ab) { 506d6cceddSDarrick J. Wong if (sz <= ab->sz) 516d6cceddSDarrick J. Wong return 0; 526d6cceddSDarrick J. Wong kmem_free(ab); 536d6cceddSDarrick J. Wong sc->buf = NULL; 546d6cceddSDarrick J. Wong } 556d6cceddSDarrick J. Wong 56036f463fSDarrick J. Wong /* 57036f463fSDarrick J. Wong * Don't zero the buffer upon allocation to avoid runtime overhead. 58036f463fSDarrick J. Wong * All users must be careful never to read uninitialized contents. 59036f463fSDarrick J. Wong */ 60036f463fSDarrick J. Wong ab = kmem_alloc_large(sizeof(*ab) + sz, flags); 616d6cceddSDarrick J. Wong if (!ab) 6200816759SDarrick J. Wong return -ENOMEM; 6300816759SDarrick J. Wong 646d6cceddSDarrick J. Wong ab->sz = sz; 656d6cceddSDarrick J. Wong sc->buf = ab; 6600816759SDarrick J. Wong return 0; 6700816759SDarrick J. Wong } 6800816759SDarrick J. Wong 69eec0482eSDarrick J. Wong /* Set us up to scrub an inode's extended attributes. */ 70eec0482eSDarrick J. Wong int 71c517b3aaSDarrick J. Wong xchk_setup_xattr( 721d8a748aSDarrick J. Wong struct xfs_scrub *sc, 73eec0482eSDarrick J. Wong struct xfs_inode *ip) 74eec0482eSDarrick J. Wong { 7500816759SDarrick J. Wong int error; 7613791d3bSDarrick J. Wong 776d6cceddSDarrick J. Wong /* 786d6cceddSDarrick J. Wong * We failed to get memory while checking attrs, so this time try to 796d6cceddSDarrick J. Wong * get all the memory we're ever going to need. Allocate the buffer 806d6cceddSDarrick J. Wong * without the inode lock held, which means we can sleep. 816d6cceddSDarrick J. Wong */ 826d6cceddSDarrick J. Wong if (sc->flags & XCHK_TRY_HARDER) { 83707e0ddaSTetsuo Handa error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX, 0); 8400816759SDarrick J. Wong if (error) 8500816759SDarrick J. Wong return error; 866d6cceddSDarrick J. Wong } 87eec0482eSDarrick J. Wong 88c517b3aaSDarrick J. Wong return xchk_setup_inode_contents(sc, ip, 0); 89eec0482eSDarrick J. Wong } 90eec0482eSDarrick J. Wong 91eec0482eSDarrick J. Wong /* Extended Attributes */ 92eec0482eSDarrick J. Wong 93c517b3aaSDarrick J. Wong struct xchk_xattr { 94eec0482eSDarrick J. Wong struct xfs_attr_list_context context; 951d8a748aSDarrick J. Wong struct xfs_scrub *sc; 96eec0482eSDarrick J. Wong }; 97eec0482eSDarrick J. Wong 98eec0482eSDarrick J. Wong /* 99eec0482eSDarrick J. Wong * Check that an extended attribute key can be looked up by hash. 100eec0482eSDarrick J. Wong * 101eec0482eSDarrick J. Wong * We use the XFS attribute list iterator (i.e. xfs_attr_list_int_ilocked) 102eec0482eSDarrick J. Wong * to call this function for every attribute key in an inode. Once 103eec0482eSDarrick J. Wong * we're here, we load the attribute value to see if any errors happen, 104eec0482eSDarrick J. Wong * or if we get more or less data than we expected. 105eec0482eSDarrick J. Wong */ 106eec0482eSDarrick J. Wong static void 107c517b3aaSDarrick J. Wong xchk_xattr_listent( 108eec0482eSDarrick J. Wong struct xfs_attr_list_context *context, 109eec0482eSDarrick J. Wong int flags, 110eec0482eSDarrick J. Wong unsigned char *name, 111eec0482eSDarrick J. Wong int namelen, 112eec0482eSDarrick J. Wong int valuelen) 113eec0482eSDarrick J. Wong { 114c517b3aaSDarrick J. Wong struct xchk_xattr *sx; 11588aa5de4SChristoph Hellwig struct xfs_da_args args = { NULL }; 116eec0482eSDarrick J. Wong int error = 0; 117eec0482eSDarrick J. Wong 118c517b3aaSDarrick J. Wong sx = container_of(context, struct xchk_xattr, context); 119eec0482eSDarrick J. Wong 1203258cb20SDarrick J. Wong if (xchk_should_terminate(sx->sc, &error)) { 1212c3b83d7SDarrick J. Wong context->seen_enough = error; 1223258cb20SDarrick J. Wong return; 1233258cb20SDarrick J. Wong } 1243258cb20SDarrick J. Wong 125eec0482eSDarrick J. Wong if (flags & XFS_ATTR_INCOMPLETE) { 126eec0482eSDarrick J. Wong /* Incomplete attr key, just mark the inode for preening. */ 127c517b3aaSDarrick J. Wong xchk_ino_set_preen(sx->sc, context->dp->i_ino); 128eec0482eSDarrick J. Wong return; 129eec0482eSDarrick J. Wong } 130eec0482eSDarrick J. Wong 13165480536SDarrick J. Wong /* Does this name make sense? */ 13265480536SDarrick J. Wong if (!xfs_attr_namecheck(name, namelen)) { 13365480536SDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 13465480536SDarrick J. Wong return; 13565480536SDarrick J. Wong } 13665480536SDarrick J. Wong 1376d6cceddSDarrick J. Wong /* 1386d6cceddSDarrick J. Wong * Try to allocate enough memory to extrat the attr value. If that 1396d6cceddSDarrick J. Wong * doesn't work, we overload the seen_enough variable to convey 1406d6cceddSDarrick J. Wong * the error message back to the main scrub function. 1416d6cceddSDarrick J. Wong */ 1426d6cceddSDarrick J. Wong error = xchk_setup_xattr_buf(sx->sc, valuelen, KM_MAYFAIL); 1436d6cceddSDarrick J. Wong if (error == -ENOMEM) 1446d6cceddSDarrick J. Wong error = -EDEADLOCK; 1456d6cceddSDarrick J. Wong if (error) { 1466d6cceddSDarrick J. Wong context->seen_enough = error; 1476d6cceddSDarrick J. Wong return; 1486d6cceddSDarrick J. Wong } 1496d6cceddSDarrick J. Wong 150eec0482eSDarrick J. Wong args.flags = ATTR_KERNOTIME; 151eec0482eSDarrick J. Wong if (flags & XFS_ATTR_ROOT) 152eec0482eSDarrick J. Wong args.flags |= ATTR_ROOT; 153eec0482eSDarrick J. Wong else if (flags & XFS_ATTR_SECURE) 154eec0482eSDarrick J. Wong args.flags |= ATTR_SECURE; 155eec0482eSDarrick J. Wong args.geo = context->dp->i_mount->m_attr_geo; 156eec0482eSDarrick J. Wong args.whichfork = XFS_ATTR_FORK; 157eec0482eSDarrick J. Wong args.dp = context->dp; 158eec0482eSDarrick J. Wong args.name = name; 159eec0482eSDarrick J. Wong args.namelen = namelen; 160eec0482eSDarrick J. Wong args.hashval = xfs_da_hashname(args.name, args.namelen); 161eec0482eSDarrick J. Wong args.trans = context->tp; 1623addd248SDarrick J. Wong args.value = xchk_xattr_valuebuf(sx->sc); 1636d6cceddSDarrick J. Wong args.valuelen = valuelen; 164eec0482eSDarrick J. Wong 165eec0482eSDarrick J. Wong error = xfs_attr_get_ilocked(context->dp, &args); 166eec0482eSDarrick J. Wong if (error == -EEXIST) 167eec0482eSDarrick J. Wong error = 0; 168c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 169eec0482eSDarrick J. Wong &error)) 170eec0482eSDarrick J. Wong goto fail_xref; 171eec0482eSDarrick J. Wong if (args.valuelen != valuelen) 172c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 173eec0482eSDarrick J. Wong args.blkno); 174eec0482eSDarrick J. Wong fail_xref: 1758bc763c2SDarrick J. Wong if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 176e7ee96dfSDarrick J. Wong context->seen_enough = 1; 177eec0482eSDarrick J. Wong return; 178eec0482eSDarrick J. Wong } 179eec0482eSDarrick J. Wong 18013791d3bSDarrick J. Wong /* 18113791d3bSDarrick J. Wong * Mark a range [start, start+len) in this map. Returns true if the 18213791d3bSDarrick J. Wong * region was free, and false if there's a conflict or a problem. 18313791d3bSDarrick J. Wong * 18413791d3bSDarrick J. Wong * Within a char, the lowest bit of the char represents the byte with 18513791d3bSDarrick J. Wong * the smallest address 18613791d3bSDarrick J. Wong */ 18713791d3bSDarrick J. Wong STATIC bool 188c517b3aaSDarrick J. Wong xchk_xattr_set_map( 1891d8a748aSDarrick J. Wong struct xfs_scrub *sc, 19013791d3bSDarrick J. Wong unsigned long *map, 19113791d3bSDarrick J. Wong unsigned int start, 19213791d3bSDarrick J. Wong unsigned int len) 19313791d3bSDarrick J. Wong { 19413791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 19513791d3bSDarrick J. Wong bool ret = true; 19613791d3bSDarrick J. Wong 19713791d3bSDarrick J. Wong if (start >= mapsize) 19813791d3bSDarrick J. Wong return false; 19913791d3bSDarrick J. Wong if (start + len > mapsize) { 20013791d3bSDarrick J. Wong len = mapsize - start; 20113791d3bSDarrick J. Wong ret = false; 20213791d3bSDarrick J. Wong } 20313791d3bSDarrick J. Wong 20413791d3bSDarrick J. Wong if (find_next_bit(map, mapsize, start) < start + len) 20513791d3bSDarrick J. Wong ret = false; 20613791d3bSDarrick J. Wong bitmap_set(map, start, len); 20713791d3bSDarrick J. Wong 20813791d3bSDarrick J. Wong return ret; 20913791d3bSDarrick J. Wong } 21013791d3bSDarrick J. Wong 21113791d3bSDarrick J. Wong /* 21213791d3bSDarrick J. Wong * Check the leaf freemap from the usage bitmap. Returns false if the 21313791d3bSDarrick J. Wong * attr freemap has problems or points to used space. 21413791d3bSDarrick J. Wong */ 21513791d3bSDarrick J. Wong STATIC bool 216c517b3aaSDarrick J. Wong xchk_xattr_check_freemap( 2171d8a748aSDarrick J. Wong struct xfs_scrub *sc, 21813791d3bSDarrick J. Wong unsigned long *map, 21913791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr) 22013791d3bSDarrick J. Wong { 2213addd248SDarrick J. Wong unsigned long *freemap = xchk_xattr_freemap(sc); 2223addd248SDarrick J. Wong unsigned long *dstmap = xchk_xattr_dstmap(sc); 22313791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 22413791d3bSDarrick J. Wong int i; 22513791d3bSDarrick J. Wong 22613791d3bSDarrick J. Wong /* Construct bitmap of freemap contents. */ 22713791d3bSDarrick J. Wong bitmap_zero(freemap, mapsize); 22813791d3bSDarrick J. Wong for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 229c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(sc, freemap, 23013791d3bSDarrick J. Wong leafhdr->freemap[i].base, 23113791d3bSDarrick J. Wong leafhdr->freemap[i].size)) 23213791d3bSDarrick J. Wong return false; 23313791d3bSDarrick J. Wong } 23413791d3bSDarrick J. Wong 23513791d3bSDarrick J. Wong /* Look for bits that are set in freemap and are marked in use. */ 23613791d3bSDarrick J. Wong return bitmap_and(dstmap, freemap, map, mapsize) == 0; 23713791d3bSDarrick J. Wong } 23813791d3bSDarrick J. Wong 23913791d3bSDarrick J. Wong /* 24013791d3bSDarrick J. Wong * Check this leaf entry's relations to everything else. 24113791d3bSDarrick J. Wong * Returns the number of bytes used for the name/value data. 24213791d3bSDarrick J. Wong */ 24313791d3bSDarrick J. Wong STATIC void 244c517b3aaSDarrick J. Wong xchk_xattr_entry( 245c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 24613791d3bSDarrick J. Wong int level, 24713791d3bSDarrick J. Wong char *buf_end, 24813791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf, 24913791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr, 25013791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent, 25113791d3bSDarrick J. Wong int idx, 25213791d3bSDarrick J. Wong unsigned int *usedbytes, 25313791d3bSDarrick J. Wong __u32 *last_hashval) 25413791d3bSDarrick J. Wong { 25513791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 2563addd248SDarrick J. Wong unsigned long *usedmap = xchk_xattr_usedmap(ds->sc); 25713791d3bSDarrick J. Wong char *name_end; 25813791d3bSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 25913791d3bSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 26013791d3bSDarrick J. Wong unsigned int nameidx; 26113791d3bSDarrick J. Wong unsigned int namesize; 26213791d3bSDarrick J. Wong 26313791d3bSDarrick J. Wong if (ent->pad2 != 0) 264c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 26513791d3bSDarrick J. Wong 26613791d3bSDarrick J. Wong /* Hash values in order? */ 26713791d3bSDarrick J. Wong if (be32_to_cpu(ent->hashval) < *last_hashval) 268c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 26913791d3bSDarrick J. Wong *last_hashval = be32_to_cpu(ent->hashval); 27013791d3bSDarrick J. Wong 27113791d3bSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 27213791d3bSDarrick J. Wong if (nameidx < leafhdr->firstused || 27313791d3bSDarrick J. Wong nameidx >= mp->m_attr_geo->blksize) { 274c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 27513791d3bSDarrick J. Wong return; 27613791d3bSDarrick J. Wong } 27713791d3bSDarrick J. Wong 27813791d3bSDarrick J. Wong /* Check the name information. */ 27913791d3bSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 28013791d3bSDarrick J. Wong lentry = xfs_attr3_leaf_name_local(leaf, idx); 28113791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 28213791d3bSDarrick J. Wong be16_to_cpu(lentry->valuelen)); 28313791d3bSDarrick J. Wong name_end = (char *)lentry + namesize; 28413791d3bSDarrick J. Wong if (lentry->namelen == 0) 285c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 28613791d3bSDarrick J. Wong } else { 28713791d3bSDarrick J. Wong rentry = xfs_attr3_leaf_name_remote(leaf, idx); 28813791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 28913791d3bSDarrick J. Wong name_end = (char *)rentry + namesize; 29013791d3bSDarrick J. Wong if (rentry->namelen == 0 || rentry->valueblk == 0) 291c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29213791d3bSDarrick J. Wong } 29313791d3bSDarrick J. Wong if (name_end > buf_end) 294c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29513791d3bSDarrick J. Wong 296c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize)) 297c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29813791d3bSDarrick J. Wong if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 29913791d3bSDarrick J. Wong *usedbytes += namesize; 30013791d3bSDarrick J. Wong } 30113791d3bSDarrick J. Wong 30213791d3bSDarrick J. Wong /* Scrub an attribute leaf. */ 30313791d3bSDarrick J. Wong STATIC int 304c517b3aaSDarrick J. Wong xchk_xattr_block( 305c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 30613791d3bSDarrick J. Wong int level) 30713791d3bSDarrick J. Wong { 30813791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr leafhdr; 30913791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 31013791d3bSDarrick J. Wong struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 31113791d3bSDarrick J. Wong struct xfs_buf *bp = blk->bp; 31213791d3bSDarrick J. Wong xfs_dablk_t *last_checked = ds->private; 31313791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf = bp->b_addr; 31413791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent; 31513791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *entries; 3166d6cceddSDarrick J. Wong unsigned long *usedmap; 31713791d3bSDarrick J. Wong char *buf_end; 31813791d3bSDarrick J. Wong size_t off; 31913791d3bSDarrick J. Wong __u32 last_hashval = 0; 32013791d3bSDarrick J. Wong unsigned int usedbytes = 0; 32113791d3bSDarrick J. Wong unsigned int hdrsize; 32213791d3bSDarrick J. Wong int i; 3236d6cceddSDarrick J. Wong int error; 32413791d3bSDarrick J. Wong 32513791d3bSDarrick J. Wong if (*last_checked == blk->blkno) 32613791d3bSDarrick J. Wong return 0; 3276d6cceddSDarrick J. Wong 3286d6cceddSDarrick J. Wong /* Allocate memory for block usage checking. */ 3296d6cceddSDarrick J. Wong error = xchk_setup_xattr_buf(ds->sc, 0, KM_MAYFAIL); 3306d6cceddSDarrick J. Wong if (error == -ENOMEM) 3316d6cceddSDarrick J. Wong return -EDEADLOCK; 3326d6cceddSDarrick J. Wong if (error) 3336d6cceddSDarrick J. Wong return error; 3346d6cceddSDarrick J. Wong usedmap = xchk_xattr_usedmap(ds->sc); 3356d6cceddSDarrick J. Wong 33613791d3bSDarrick J. Wong *last_checked = blk->blkno; 33713791d3bSDarrick J. Wong bitmap_zero(usedmap, mp->m_attr_geo->blksize); 33813791d3bSDarrick J. Wong 33913791d3bSDarrick J. Wong /* Check all the padding. */ 34013791d3bSDarrick J. Wong if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) { 34113791d3bSDarrick J. Wong struct xfs_attr3_leafblock *leaf = bp->b_addr; 34213791d3bSDarrick J. Wong 34313791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 || 34413791d3bSDarrick J. Wong leaf->hdr.info.hdr.pad != 0) 345c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 34613791d3bSDarrick J. Wong } else { 34713791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 348c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 34913791d3bSDarrick J. Wong } 35013791d3bSDarrick J. Wong 35113791d3bSDarrick J. Wong /* Check the leaf header */ 35213791d3bSDarrick J. Wong xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 35313791d3bSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(leaf); 35413791d3bSDarrick J. Wong 35513791d3bSDarrick J. Wong if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 356c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35713791d3bSDarrick J. Wong if (leafhdr.firstused > mp->m_attr_geo->blksize) 358c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35913791d3bSDarrick J. Wong if (leafhdr.firstused < hdrsize) 360c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 361c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize)) 362c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36313791d3bSDarrick J. Wong 36413791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 36513791d3bSDarrick J. Wong goto out; 36613791d3bSDarrick J. Wong 36713791d3bSDarrick J. Wong entries = xfs_attr3_leaf_entryp(leaf); 36813791d3bSDarrick J. Wong if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 369c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37013791d3bSDarrick J. Wong 37113791d3bSDarrick J. Wong buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 37213791d3bSDarrick J. Wong for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 37313791d3bSDarrick J. Wong /* Mark the leaf entry itself. */ 37413791d3bSDarrick J. Wong off = (char *)ent - (char *)leaf; 375c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, off, 37613791d3bSDarrick J. Wong sizeof(xfs_attr_leaf_entry_t))) { 377c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37813791d3bSDarrick J. Wong goto out; 37913791d3bSDarrick J. Wong } 38013791d3bSDarrick J. Wong 38113791d3bSDarrick J. Wong /* Check the entry and nameval. */ 382c517b3aaSDarrick J. Wong xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 3833addd248SDarrick J. Wong ent, i, &usedbytes, &last_hashval); 38413791d3bSDarrick J. Wong 38513791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 38613791d3bSDarrick J. Wong goto out; 38713791d3bSDarrick J. Wong } 38813791d3bSDarrick J. Wong 389c517b3aaSDarrick J. Wong if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr)) 390c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39113791d3bSDarrick J. Wong 39213791d3bSDarrick J. Wong if (leafhdr.usedbytes != usedbytes) 393c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39413791d3bSDarrick J. Wong 39513791d3bSDarrick J. Wong out: 39613791d3bSDarrick J. Wong return 0; 39713791d3bSDarrick J. Wong } 39813791d3bSDarrick J. Wong 399eec0482eSDarrick J. Wong /* Scrub a attribute btree record. */ 400eec0482eSDarrick J. Wong STATIC int 401c517b3aaSDarrick J. Wong xchk_xattr_rec( 402c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 403eec0482eSDarrick J. Wong int level, 404eec0482eSDarrick J. Wong void *rec) 405eec0482eSDarrick J. Wong { 406eec0482eSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 407eec0482eSDarrick J. Wong struct xfs_attr_leaf_entry *ent = rec; 408eec0482eSDarrick J. Wong struct xfs_da_state_blk *blk; 409eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 410eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 411eec0482eSDarrick J. Wong struct xfs_buf *bp; 412eec0482eSDarrick J. Wong xfs_dahash_t calc_hash; 413eec0482eSDarrick J. Wong xfs_dahash_t hash; 414eec0482eSDarrick J. Wong int nameidx; 415eec0482eSDarrick J. Wong int hdrsize; 416eec0482eSDarrick J. Wong unsigned int badflags; 417eec0482eSDarrick J. Wong int error; 418eec0482eSDarrick J. Wong 419eec0482eSDarrick J. Wong blk = &ds->state->path.blk[level]; 420eec0482eSDarrick J. Wong 42113791d3bSDarrick J. Wong /* Check the whole block, if necessary. */ 422c517b3aaSDarrick J. Wong error = xchk_xattr_block(ds, level); 42313791d3bSDarrick J. Wong if (error) 42413791d3bSDarrick J. Wong goto out; 42513791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 42613791d3bSDarrick J. Wong goto out; 42713791d3bSDarrick J. Wong 428eec0482eSDarrick J. Wong /* Check the hash of the entry. */ 429c517b3aaSDarrick J. Wong error = xchk_da_btree_hash(ds, level, &ent->hashval); 430eec0482eSDarrick J. Wong if (error) 431eec0482eSDarrick J. Wong goto out; 432eec0482eSDarrick J. Wong 433eec0482eSDarrick J. Wong /* Find the attr entry's location. */ 434eec0482eSDarrick J. Wong bp = blk->bp; 435eec0482eSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 436eec0482eSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 437eec0482eSDarrick J. Wong if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 438c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 439eec0482eSDarrick J. Wong goto out; 440eec0482eSDarrick J. Wong } 441eec0482eSDarrick J. Wong 442eec0482eSDarrick J. Wong /* Retrieve the entry and check it. */ 443eec0482eSDarrick J. Wong hash = be32_to_cpu(ent->hashval); 444eec0482eSDarrick J. Wong badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 445eec0482eSDarrick J. Wong XFS_ATTR_INCOMPLETE); 446eec0482eSDarrick J. Wong if ((ent->flags & badflags) != 0) 447c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 448eec0482eSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 449eec0482eSDarrick J. Wong lentry = (struct xfs_attr_leaf_name_local *) 450eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 451eec0482eSDarrick J. Wong if (lentry->namelen <= 0) { 452c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 453eec0482eSDarrick J. Wong goto out; 454eec0482eSDarrick J. Wong } 455eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 456eec0482eSDarrick J. Wong } else { 457eec0482eSDarrick J. Wong rentry = (struct xfs_attr_leaf_name_remote *) 458eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 459eec0482eSDarrick J. Wong if (rentry->namelen <= 0) { 460c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 461eec0482eSDarrick J. Wong goto out; 462eec0482eSDarrick J. Wong } 463eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 464eec0482eSDarrick J. Wong } 465eec0482eSDarrick J. Wong if (calc_hash != hash) 466c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 467eec0482eSDarrick J. Wong 468eec0482eSDarrick J. Wong out: 469eec0482eSDarrick J. Wong return error; 470eec0482eSDarrick J. Wong } 471eec0482eSDarrick J. Wong 472eec0482eSDarrick J. Wong /* Scrub the extended attribute metadata. */ 473eec0482eSDarrick J. Wong int 474c517b3aaSDarrick J. Wong xchk_xattr( 4751d8a748aSDarrick J. Wong struct xfs_scrub *sc) 476eec0482eSDarrick J. Wong { 477c517b3aaSDarrick J. Wong struct xchk_xattr sx; 478eec0482eSDarrick J. Wong struct attrlist_cursor_kern cursor = { 0 }; 47913791d3bSDarrick J. Wong xfs_dablk_t last_checked = -1U; 480eec0482eSDarrick J. Wong int error = 0; 481eec0482eSDarrick J. Wong 482eec0482eSDarrick J. Wong if (!xfs_inode_hasattr(sc->ip)) 483eec0482eSDarrick J. Wong return -ENOENT; 484eec0482eSDarrick J. Wong 485eec0482eSDarrick J. Wong memset(&sx, 0, sizeof(sx)); 486eec0482eSDarrick J. Wong /* Check attribute tree structure */ 487c517b3aaSDarrick J. Wong error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 48813791d3bSDarrick J. Wong &last_checked); 489eec0482eSDarrick J. Wong if (error) 490eec0482eSDarrick J. Wong goto out; 491eec0482eSDarrick J. Wong 492eec0482eSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 493eec0482eSDarrick J. Wong goto out; 494eec0482eSDarrick J. Wong 495eec0482eSDarrick J. Wong /* Check that every attr key can also be looked up by hash. */ 496eec0482eSDarrick J. Wong sx.context.dp = sc->ip; 497eec0482eSDarrick J. Wong sx.context.cursor = &cursor; 498eec0482eSDarrick J. Wong sx.context.resynch = 1; 499c517b3aaSDarrick J. Wong sx.context.put_listent = xchk_xattr_listent; 500eec0482eSDarrick J. Wong sx.context.tp = sc->tp; 501eec0482eSDarrick J. Wong sx.context.flags = ATTR_INCOMPLETE; 502eec0482eSDarrick J. Wong sx.sc = sc; 503eec0482eSDarrick J. Wong 504eec0482eSDarrick J. Wong /* 505eec0482eSDarrick J. Wong * Look up every xattr in this file by name. 506eec0482eSDarrick J. Wong * 507eec0482eSDarrick J. Wong * Use the backend implementation of xfs_attr_list to call 508c517b3aaSDarrick J. Wong * xchk_xattr_listent on every attribute key in this inode. 509eec0482eSDarrick J. Wong * In other words, we use the same iterator/callback mechanism 510eec0482eSDarrick J. Wong * that listattr uses to scrub extended attributes, though in our 511eec0482eSDarrick J. Wong * _listent function, we check the value of the attribute. 512eec0482eSDarrick J. Wong * 513eec0482eSDarrick J. Wong * The VFS only locks i_rwsem when modifying attrs, so keep all 514eec0482eSDarrick J. Wong * three locks held because that's the only way to ensure we're 515eec0482eSDarrick J. Wong * the only thread poking into the da btree. We traverse the da 516eec0482eSDarrick J. Wong * btree while holding a leaf buffer locked for the xattr name 517eec0482eSDarrick J. Wong * iteration, which doesn't really follow the usual buffer 518eec0482eSDarrick J. Wong * locking order. 519eec0482eSDarrick J. Wong */ 520eec0482eSDarrick J. Wong error = xfs_attr_list_int_ilocked(&sx.context); 521c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 522eec0482eSDarrick J. Wong goto out; 5232c3b83d7SDarrick J. Wong 5242c3b83d7SDarrick J. Wong /* Did our listent function try to return any errors? */ 5252c3b83d7SDarrick J. Wong if (sx.context.seen_enough < 0) 5262c3b83d7SDarrick J. Wong error = sx.context.seen_enough; 527eec0482eSDarrick J. Wong out: 528eec0482eSDarrick J. Wong return error; 529eec0482eSDarrick J. Wong } 530