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 * 10117e1dd83SChristoph Hellwig * We use the XFS attribute list iterator (i.e. xfs_attr_list_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 1501d733019SChristoph Hellwig args.op_flags = XFS_DA_OP_NOTIME; 151d5f0f49aSChristoph Hellwig args.attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK; 152eec0482eSDarrick J. Wong args.geo = context->dp->i_mount->m_attr_geo; 153eec0482eSDarrick J. Wong args.whichfork = XFS_ATTR_FORK; 154eec0482eSDarrick J. Wong args.dp = context->dp; 155eec0482eSDarrick J. Wong args.name = name; 156eec0482eSDarrick J. Wong args.namelen = namelen; 157eec0482eSDarrick J. Wong args.hashval = xfs_da_hashname(args.name, args.namelen); 158eec0482eSDarrick J. Wong args.trans = context->tp; 1593addd248SDarrick J. Wong args.value = xchk_xattr_valuebuf(sx->sc); 1606d6cceddSDarrick J. Wong args.valuelen = valuelen; 161eec0482eSDarrick J. Wong 162c36f533fSChristoph Hellwig error = xfs_attr_get_ilocked(&args); 163806d3909SDarrick J. Wong /* ENODATA means the hash lookup failed and the attr is bad */ 164806d3909SDarrick J. Wong if (error == -ENODATA) 165806d3909SDarrick J. Wong error = -EFSCORRUPTED; 166c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 167eec0482eSDarrick J. Wong &error)) 168eec0482eSDarrick J. Wong goto fail_xref; 169eec0482eSDarrick J. Wong if (args.valuelen != valuelen) 170c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 171eec0482eSDarrick J. Wong args.blkno); 172eec0482eSDarrick J. Wong fail_xref: 1738bc763c2SDarrick J. Wong if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 174e7ee96dfSDarrick J. Wong context->seen_enough = 1; 175eec0482eSDarrick J. Wong return; 176eec0482eSDarrick J. Wong } 177eec0482eSDarrick J. Wong 17813791d3bSDarrick J. Wong /* 17913791d3bSDarrick J. Wong * Mark a range [start, start+len) in this map. Returns true if the 18013791d3bSDarrick J. Wong * region was free, and false if there's a conflict or a problem. 18113791d3bSDarrick J. Wong * 18213791d3bSDarrick J. Wong * Within a char, the lowest bit of the char represents the byte with 18313791d3bSDarrick J. Wong * the smallest address 18413791d3bSDarrick J. Wong */ 18513791d3bSDarrick J. Wong STATIC bool 186c517b3aaSDarrick J. Wong xchk_xattr_set_map( 1871d8a748aSDarrick J. Wong struct xfs_scrub *sc, 18813791d3bSDarrick J. Wong unsigned long *map, 18913791d3bSDarrick J. Wong unsigned int start, 19013791d3bSDarrick J. Wong unsigned int len) 19113791d3bSDarrick J. Wong { 19213791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 19313791d3bSDarrick J. Wong bool ret = true; 19413791d3bSDarrick J. Wong 19513791d3bSDarrick J. Wong if (start >= mapsize) 19613791d3bSDarrick J. Wong return false; 19713791d3bSDarrick J. Wong if (start + len > mapsize) { 19813791d3bSDarrick J. Wong len = mapsize - start; 19913791d3bSDarrick J. Wong ret = false; 20013791d3bSDarrick J. Wong } 20113791d3bSDarrick J. Wong 20213791d3bSDarrick J. Wong if (find_next_bit(map, mapsize, start) < start + len) 20313791d3bSDarrick J. Wong ret = false; 20413791d3bSDarrick J. Wong bitmap_set(map, start, len); 20513791d3bSDarrick J. Wong 20613791d3bSDarrick J. Wong return ret; 20713791d3bSDarrick J. Wong } 20813791d3bSDarrick J. Wong 20913791d3bSDarrick J. Wong /* 21013791d3bSDarrick J. Wong * Check the leaf freemap from the usage bitmap. Returns false if the 21113791d3bSDarrick J. Wong * attr freemap has problems or points to used space. 21213791d3bSDarrick J. Wong */ 21313791d3bSDarrick J. Wong STATIC bool 214c517b3aaSDarrick J. Wong xchk_xattr_check_freemap( 2151d8a748aSDarrick J. Wong struct xfs_scrub *sc, 21613791d3bSDarrick J. Wong unsigned long *map, 21713791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr) 21813791d3bSDarrick J. Wong { 2193addd248SDarrick J. Wong unsigned long *freemap = xchk_xattr_freemap(sc); 2203addd248SDarrick J. Wong unsigned long *dstmap = xchk_xattr_dstmap(sc); 22113791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 22213791d3bSDarrick J. Wong int i; 22313791d3bSDarrick J. Wong 22413791d3bSDarrick J. Wong /* Construct bitmap of freemap contents. */ 22513791d3bSDarrick J. Wong bitmap_zero(freemap, mapsize); 22613791d3bSDarrick J. Wong for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 227c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(sc, freemap, 22813791d3bSDarrick J. Wong leafhdr->freemap[i].base, 22913791d3bSDarrick J. Wong leafhdr->freemap[i].size)) 23013791d3bSDarrick J. Wong return false; 23113791d3bSDarrick J. Wong } 23213791d3bSDarrick J. Wong 23313791d3bSDarrick J. Wong /* Look for bits that are set in freemap and are marked in use. */ 23413791d3bSDarrick J. Wong return bitmap_and(dstmap, freemap, map, mapsize) == 0; 23513791d3bSDarrick J. Wong } 23613791d3bSDarrick J. Wong 23713791d3bSDarrick J. Wong /* 23813791d3bSDarrick J. Wong * Check this leaf entry's relations to everything else. 23913791d3bSDarrick J. Wong * Returns the number of bytes used for the name/value data. 24013791d3bSDarrick J. Wong */ 24113791d3bSDarrick J. Wong STATIC void 242c517b3aaSDarrick J. Wong xchk_xattr_entry( 243c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 24413791d3bSDarrick J. Wong int level, 24513791d3bSDarrick J. Wong char *buf_end, 24613791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf, 24713791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr, 24813791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent, 24913791d3bSDarrick J. Wong int idx, 25013791d3bSDarrick J. Wong unsigned int *usedbytes, 25113791d3bSDarrick J. Wong __u32 *last_hashval) 25213791d3bSDarrick J. Wong { 25313791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 2543addd248SDarrick J. Wong unsigned long *usedmap = xchk_xattr_usedmap(ds->sc); 25513791d3bSDarrick J. Wong char *name_end; 25613791d3bSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 25713791d3bSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 25813791d3bSDarrick J. Wong unsigned int nameidx; 25913791d3bSDarrick J. Wong unsigned int namesize; 26013791d3bSDarrick J. Wong 26113791d3bSDarrick J. Wong if (ent->pad2 != 0) 262c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 26313791d3bSDarrick J. Wong 26413791d3bSDarrick J. Wong /* Hash values in order? */ 26513791d3bSDarrick J. Wong if (be32_to_cpu(ent->hashval) < *last_hashval) 266c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 26713791d3bSDarrick J. Wong *last_hashval = be32_to_cpu(ent->hashval); 26813791d3bSDarrick J. Wong 26913791d3bSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 27013791d3bSDarrick J. Wong if (nameidx < leafhdr->firstused || 27113791d3bSDarrick J. Wong nameidx >= mp->m_attr_geo->blksize) { 272c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 27313791d3bSDarrick J. Wong return; 27413791d3bSDarrick J. Wong } 27513791d3bSDarrick J. Wong 27613791d3bSDarrick J. Wong /* Check the name information. */ 27713791d3bSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 27813791d3bSDarrick J. Wong lentry = xfs_attr3_leaf_name_local(leaf, idx); 27913791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 28013791d3bSDarrick J. Wong be16_to_cpu(lentry->valuelen)); 28113791d3bSDarrick J. Wong name_end = (char *)lentry + namesize; 28213791d3bSDarrick J. Wong if (lentry->namelen == 0) 283c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 28413791d3bSDarrick J. Wong } else { 28513791d3bSDarrick J. Wong rentry = xfs_attr3_leaf_name_remote(leaf, idx); 28613791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 28713791d3bSDarrick J. Wong name_end = (char *)rentry + namesize; 28813791d3bSDarrick J. Wong if (rentry->namelen == 0 || rentry->valueblk == 0) 289c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29013791d3bSDarrick J. Wong } 29113791d3bSDarrick J. Wong if (name_end > buf_end) 292c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29313791d3bSDarrick J. Wong 294c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize)) 295c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29613791d3bSDarrick J. Wong if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 29713791d3bSDarrick J. Wong *usedbytes += namesize; 29813791d3bSDarrick J. Wong } 29913791d3bSDarrick J. Wong 30013791d3bSDarrick J. Wong /* Scrub an attribute leaf. */ 30113791d3bSDarrick J. Wong STATIC int 302c517b3aaSDarrick J. Wong xchk_xattr_block( 303c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 30413791d3bSDarrick J. Wong int level) 30513791d3bSDarrick J. Wong { 30613791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr leafhdr; 30713791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 30813791d3bSDarrick J. Wong struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 30913791d3bSDarrick J. Wong struct xfs_buf *bp = blk->bp; 31013791d3bSDarrick J. Wong xfs_dablk_t *last_checked = ds->private; 31113791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf = bp->b_addr; 31213791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent; 31313791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *entries; 3146d6cceddSDarrick J. Wong unsigned long *usedmap; 31513791d3bSDarrick J. Wong char *buf_end; 31613791d3bSDarrick J. Wong size_t off; 31713791d3bSDarrick J. Wong __u32 last_hashval = 0; 31813791d3bSDarrick J. Wong unsigned int usedbytes = 0; 31913791d3bSDarrick J. Wong unsigned int hdrsize; 32013791d3bSDarrick J. Wong int i; 3216d6cceddSDarrick J. Wong int error; 32213791d3bSDarrick J. Wong 32313791d3bSDarrick J. Wong if (*last_checked == blk->blkno) 32413791d3bSDarrick J. Wong return 0; 3256d6cceddSDarrick J. Wong 3266d6cceddSDarrick J. Wong /* Allocate memory for block usage checking. */ 3276d6cceddSDarrick J. Wong error = xchk_setup_xattr_buf(ds->sc, 0, KM_MAYFAIL); 3286d6cceddSDarrick J. Wong if (error == -ENOMEM) 3296d6cceddSDarrick J. Wong return -EDEADLOCK; 3306d6cceddSDarrick J. Wong if (error) 3316d6cceddSDarrick J. Wong return error; 3326d6cceddSDarrick J. Wong usedmap = xchk_xattr_usedmap(ds->sc); 3336d6cceddSDarrick J. Wong 33413791d3bSDarrick J. Wong *last_checked = blk->blkno; 33513791d3bSDarrick J. Wong bitmap_zero(usedmap, mp->m_attr_geo->blksize); 33613791d3bSDarrick J. Wong 33713791d3bSDarrick J. Wong /* Check all the padding. */ 33813791d3bSDarrick J. Wong if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) { 33913791d3bSDarrick J. Wong struct xfs_attr3_leafblock *leaf = bp->b_addr; 34013791d3bSDarrick J. Wong 34113791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 || 34213791d3bSDarrick J. Wong leaf->hdr.info.hdr.pad != 0) 343c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 34413791d3bSDarrick J. Wong } else { 34513791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 346c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 34713791d3bSDarrick J. Wong } 34813791d3bSDarrick J. Wong 34913791d3bSDarrick J. Wong /* Check the leaf header */ 35013791d3bSDarrick J. Wong xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 35113791d3bSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(leaf); 35213791d3bSDarrick J. Wong 35313791d3bSDarrick J. Wong if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 354c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35513791d3bSDarrick J. Wong if (leafhdr.firstused > mp->m_attr_geo->blksize) 356c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35713791d3bSDarrick J. Wong if (leafhdr.firstused < hdrsize) 358c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 359c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize)) 360c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36113791d3bSDarrick J. Wong 36213791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 36313791d3bSDarrick J. Wong goto out; 36413791d3bSDarrick J. Wong 36513791d3bSDarrick J. Wong entries = xfs_attr3_leaf_entryp(leaf); 36613791d3bSDarrick J. Wong if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 367c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36813791d3bSDarrick J. Wong 36913791d3bSDarrick J. Wong buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 37013791d3bSDarrick J. Wong for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 37113791d3bSDarrick J. Wong /* Mark the leaf entry itself. */ 37213791d3bSDarrick J. Wong off = (char *)ent - (char *)leaf; 373c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, off, 37413791d3bSDarrick J. Wong sizeof(xfs_attr_leaf_entry_t))) { 375c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37613791d3bSDarrick J. Wong goto out; 37713791d3bSDarrick J. Wong } 37813791d3bSDarrick J. Wong 37913791d3bSDarrick J. Wong /* Check the entry and nameval. */ 380c517b3aaSDarrick J. Wong xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 3813addd248SDarrick J. Wong ent, i, &usedbytes, &last_hashval); 38213791d3bSDarrick J. Wong 38313791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 38413791d3bSDarrick J. Wong goto out; 38513791d3bSDarrick J. Wong } 38613791d3bSDarrick J. Wong 387c517b3aaSDarrick J. Wong if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr)) 388c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 38913791d3bSDarrick J. Wong 39013791d3bSDarrick J. Wong if (leafhdr.usedbytes != usedbytes) 391c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39213791d3bSDarrick J. Wong 39313791d3bSDarrick J. Wong out: 39413791d3bSDarrick J. Wong return 0; 39513791d3bSDarrick J. Wong } 39613791d3bSDarrick J. Wong 397eec0482eSDarrick J. Wong /* Scrub a attribute btree record. */ 398eec0482eSDarrick J. Wong STATIC int 399c517b3aaSDarrick J. Wong xchk_xattr_rec( 400c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 401649d9d98SChristoph Hellwig int level) 402eec0482eSDarrick J. Wong { 403eec0482eSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 404649d9d98SChristoph Hellwig struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 405eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 406eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 407eec0482eSDarrick J. Wong struct xfs_buf *bp; 408649d9d98SChristoph Hellwig struct xfs_attr_leaf_entry *ent; 409eec0482eSDarrick J. Wong xfs_dahash_t calc_hash; 410eec0482eSDarrick J. Wong xfs_dahash_t hash; 411eec0482eSDarrick J. Wong int nameidx; 412eec0482eSDarrick J. Wong int hdrsize; 413eec0482eSDarrick J. Wong unsigned int badflags; 414eec0482eSDarrick J. Wong int error; 415eec0482eSDarrick J. Wong 416649d9d98SChristoph Hellwig ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); 417649d9d98SChristoph Hellwig 418649d9d98SChristoph Hellwig ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index; 419eec0482eSDarrick J. Wong 42013791d3bSDarrick J. Wong /* Check the whole block, if necessary. */ 421c517b3aaSDarrick J. Wong error = xchk_xattr_block(ds, level); 42213791d3bSDarrick J. Wong if (error) 42313791d3bSDarrick J. Wong goto out; 42413791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 42513791d3bSDarrick J. Wong goto out; 42613791d3bSDarrick J. Wong 427eec0482eSDarrick J. Wong /* Check the hash of the entry. */ 428c517b3aaSDarrick J. Wong error = xchk_da_btree_hash(ds, level, &ent->hashval); 429eec0482eSDarrick J. Wong if (error) 430eec0482eSDarrick J. Wong goto out; 431eec0482eSDarrick J. Wong 432eec0482eSDarrick J. Wong /* Find the attr entry's location. */ 433eec0482eSDarrick J. Wong bp = blk->bp; 434eec0482eSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 435eec0482eSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 436eec0482eSDarrick J. Wong if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 437c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 438eec0482eSDarrick J. Wong goto out; 439eec0482eSDarrick J. Wong } 440eec0482eSDarrick J. Wong 441eec0482eSDarrick J. Wong /* Retrieve the entry and check it. */ 442eec0482eSDarrick J. Wong hash = be32_to_cpu(ent->hashval); 443eec0482eSDarrick J. Wong badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 444eec0482eSDarrick J. Wong XFS_ATTR_INCOMPLETE); 445eec0482eSDarrick J. Wong if ((ent->flags & badflags) != 0) 446c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 447eec0482eSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 448eec0482eSDarrick J. Wong lentry = (struct xfs_attr_leaf_name_local *) 449eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 450eec0482eSDarrick J. Wong if (lentry->namelen <= 0) { 451c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 452eec0482eSDarrick J. Wong goto out; 453eec0482eSDarrick J. Wong } 454eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 455eec0482eSDarrick J. Wong } else { 456eec0482eSDarrick J. Wong rentry = (struct xfs_attr_leaf_name_remote *) 457eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 458eec0482eSDarrick J. Wong if (rentry->namelen <= 0) { 459c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 460eec0482eSDarrick J. Wong goto out; 461eec0482eSDarrick J. Wong } 462eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 463eec0482eSDarrick J. Wong } 464eec0482eSDarrick J. Wong if (calc_hash != hash) 465c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 466eec0482eSDarrick J. Wong 467eec0482eSDarrick J. Wong out: 468eec0482eSDarrick J. Wong return error; 469eec0482eSDarrick J. Wong } 470eec0482eSDarrick J. Wong 471eec0482eSDarrick J. Wong /* Scrub the extended attribute metadata. */ 472eec0482eSDarrick J. Wong int 473c517b3aaSDarrick J. Wong xchk_xattr( 4741d8a748aSDarrick J. Wong struct xfs_scrub *sc) 475eec0482eSDarrick J. Wong { 476c517b3aaSDarrick J. Wong struct xchk_xattr sx; 47713791d3bSDarrick J. Wong xfs_dablk_t last_checked = -1U; 478eec0482eSDarrick J. Wong int error = 0; 479eec0482eSDarrick J. Wong 480eec0482eSDarrick J. Wong if (!xfs_inode_hasattr(sc->ip)) 481eec0482eSDarrick J. Wong return -ENOENT; 482eec0482eSDarrick J. Wong 483eec0482eSDarrick J. Wong memset(&sx, 0, sizeof(sx)); 484eec0482eSDarrick J. Wong /* Check attribute tree structure */ 485c517b3aaSDarrick J. Wong error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 48613791d3bSDarrick J. Wong &last_checked); 487eec0482eSDarrick J. Wong if (error) 488eec0482eSDarrick J. Wong goto out; 489eec0482eSDarrick J. Wong 490eec0482eSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 491eec0482eSDarrick J. Wong goto out; 492eec0482eSDarrick J. Wong 493eec0482eSDarrick J. Wong /* Check that every attr key can also be looked up by hash. */ 494eec0482eSDarrick J. Wong sx.context.dp = sc->ip; 495eec0482eSDarrick J. Wong sx.context.resynch = 1; 496c517b3aaSDarrick J. Wong sx.context.put_listent = xchk_xattr_listent; 497eec0482eSDarrick J. Wong sx.context.tp = sc->tp; 4985e813574SChristoph Hellwig sx.context.allow_incomplete = true; 499eec0482eSDarrick J. Wong sx.sc = sc; 500eec0482eSDarrick J. Wong 501eec0482eSDarrick J. Wong /* 502eec0482eSDarrick J. Wong * Look up every xattr in this file by name. 503eec0482eSDarrick J. Wong * 504eec0482eSDarrick J. Wong * Use the backend implementation of xfs_attr_list to call 505c517b3aaSDarrick J. Wong * xchk_xattr_listent on every attribute key in this inode. 506eec0482eSDarrick J. Wong * In other words, we use the same iterator/callback mechanism 507eec0482eSDarrick J. Wong * that listattr uses to scrub extended attributes, though in our 508eec0482eSDarrick J. Wong * _listent function, we check the value of the attribute. 509eec0482eSDarrick J. Wong * 510eec0482eSDarrick J. Wong * The VFS only locks i_rwsem when modifying attrs, so keep all 511eec0482eSDarrick J. Wong * three locks held because that's the only way to ensure we're 512eec0482eSDarrick J. Wong * the only thread poking into the da btree. We traverse the da 513eec0482eSDarrick J. Wong * btree while holding a leaf buffer locked for the xattr name 514eec0482eSDarrick J. Wong * iteration, which doesn't really follow the usual buffer 515eec0482eSDarrick J. Wong * locking order. 516eec0482eSDarrick J. Wong */ 51717e1dd83SChristoph Hellwig error = xfs_attr_list_ilocked(&sx.context); 518c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 519eec0482eSDarrick J. Wong goto out; 5202c3b83d7SDarrick J. Wong 5212c3b83d7SDarrick J. Wong /* Did our listent function try to return any errors? */ 5222c3b83d7SDarrick J. Wong if (sx.context.seen_enough < 0) 5232c3b83d7SDarrick J. Wong error = sx.context.seen_enough; 524eec0482eSDarrick J. Wong out: 525eec0482eSDarrick J. Wong return error; 526eec0482eSDarrick J. Wong } 527