1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 2eec0482eSDarrick J. Wong /* 3ecc73f8aSDarrick J. Wong * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 4739a2fe0SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 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 */ 28d634525dSDave Chinner static int 2900816759SDarrick J. Wong xchk_setup_xattr_buf( 3000816759SDarrick J. Wong struct xfs_scrub *sc, 316d6cceddSDarrick J. Wong size_t value_size, 32d634525dSDave Chinner gfp_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; 52306195f3SDarrick J. Wong kvfree(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 */ 60d634525dSDave Chinner ab = kvmalloc(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( 72026f57ebSDarrick J. Wong struct xfs_scrub *sc) 73eec0482eSDarrick J. Wong { 7400816759SDarrick J. Wong int error; 7513791d3bSDarrick J. Wong 766d6cceddSDarrick J. Wong /* 776d6cceddSDarrick J. Wong * We failed to get memory while checking attrs, so this time try to 786d6cceddSDarrick J. Wong * get all the memory we're ever going to need. Allocate the buffer 796d6cceddSDarrick J. Wong * without the inode lock held, which means we can sleep. 806d6cceddSDarrick J. Wong */ 816d6cceddSDarrick J. Wong if (sc->flags & XCHK_TRY_HARDER) { 8248ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX, 8348ff4045SDarrick J. Wong XCHK_GFP_FLAGS); 8400816759SDarrick J. Wong if (error) 8500816759SDarrick J. Wong return error; 866d6cceddSDarrick J. Wong } 87eec0482eSDarrick J. Wong 88026f57ebSDarrick J. Wong return xchk_setup_inode_contents(sc, 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 131c12ad414SDarrick J. Wong /* Only one namespace bit allowed. */ 132c12ad414SDarrick J. Wong if (hweight32(flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) { 133c12ad414SDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 134c12ad414SDarrick J. Wong goto fail_xref; 135c12ad414SDarrick J. Wong } 136c12ad414SDarrick J. Wong 13765480536SDarrick J. Wong /* Does this name make sense? */ 13865480536SDarrick J. Wong if (!xfs_attr_namecheck(name, namelen)) { 13965480536SDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 140c12ad414SDarrick J. Wong goto fail_xref; 14165480536SDarrick J. Wong } 14265480536SDarrick J. Wong 1436d6cceddSDarrick J. Wong /* 1446d6cceddSDarrick J. Wong * Try to allocate enough memory to extrat the attr value. If that 1456d6cceddSDarrick J. Wong * doesn't work, we overload the seen_enough variable to convey 1466d6cceddSDarrick J. Wong * the error message back to the main scrub function. 1476d6cceddSDarrick J. Wong */ 14848ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(sx->sc, valuelen, XCHK_GFP_FLAGS); 1496d6cceddSDarrick J. Wong if (error == -ENOMEM) 1506d6cceddSDarrick J. Wong error = -EDEADLOCK; 1516d6cceddSDarrick J. Wong if (error) { 1526d6cceddSDarrick J. Wong context->seen_enough = error; 1536d6cceddSDarrick J. Wong return; 1546d6cceddSDarrick J. Wong } 1556d6cceddSDarrick J. Wong 1561d733019SChristoph Hellwig args.op_flags = XFS_DA_OP_NOTIME; 157d5f0f49aSChristoph Hellwig args.attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK; 158eec0482eSDarrick J. Wong args.geo = context->dp->i_mount->m_attr_geo; 159eec0482eSDarrick J. Wong args.whichfork = XFS_ATTR_FORK; 160eec0482eSDarrick J. Wong args.dp = context->dp; 161eec0482eSDarrick J. Wong args.name = name; 162eec0482eSDarrick J. Wong args.namelen = namelen; 163eec0482eSDarrick J. Wong args.hashval = xfs_da_hashname(args.name, args.namelen); 164eec0482eSDarrick J. Wong args.trans = context->tp; 1653addd248SDarrick J. Wong args.value = xchk_xattr_valuebuf(sx->sc); 1666d6cceddSDarrick J. Wong args.valuelen = valuelen; 167eec0482eSDarrick J. Wong 168c36f533fSChristoph Hellwig error = xfs_attr_get_ilocked(&args); 169806d3909SDarrick J. Wong /* ENODATA means the hash lookup failed and the attr is bad */ 170806d3909SDarrick J. Wong if (error == -ENODATA) 171806d3909SDarrick J. Wong error = -EFSCORRUPTED; 172c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 173eec0482eSDarrick J. Wong &error)) 174eec0482eSDarrick J. Wong goto fail_xref; 175eec0482eSDarrick J. Wong if (args.valuelen != valuelen) 176c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 177eec0482eSDarrick J. Wong args.blkno); 178eec0482eSDarrick J. Wong fail_xref: 1798bc763c2SDarrick J. Wong if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 180e7ee96dfSDarrick J. Wong context->seen_enough = 1; 181eec0482eSDarrick J. Wong return; 182eec0482eSDarrick J. Wong } 183eec0482eSDarrick J. Wong 18413791d3bSDarrick J. Wong /* 18513791d3bSDarrick J. Wong * Mark a range [start, start+len) in this map. Returns true if the 18613791d3bSDarrick J. Wong * region was free, and false if there's a conflict or a problem. 18713791d3bSDarrick J. Wong * 18813791d3bSDarrick J. Wong * Within a char, the lowest bit of the char represents the byte with 18913791d3bSDarrick J. Wong * the smallest address 19013791d3bSDarrick J. Wong */ 19113791d3bSDarrick J. Wong STATIC bool 192c517b3aaSDarrick J. Wong xchk_xattr_set_map( 1931d8a748aSDarrick J. Wong struct xfs_scrub *sc, 19413791d3bSDarrick J. Wong unsigned long *map, 19513791d3bSDarrick J. Wong unsigned int start, 19613791d3bSDarrick J. Wong unsigned int len) 19713791d3bSDarrick J. Wong { 19813791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 19913791d3bSDarrick J. Wong bool ret = true; 20013791d3bSDarrick J. Wong 20113791d3bSDarrick J. Wong if (start >= mapsize) 20213791d3bSDarrick J. Wong return false; 20313791d3bSDarrick J. Wong if (start + len > mapsize) { 20413791d3bSDarrick J. Wong len = mapsize - start; 20513791d3bSDarrick J. Wong ret = false; 20613791d3bSDarrick J. Wong } 20713791d3bSDarrick J. Wong 20813791d3bSDarrick J. Wong if (find_next_bit(map, mapsize, start) < start + len) 20913791d3bSDarrick J. Wong ret = false; 21013791d3bSDarrick J. Wong bitmap_set(map, start, len); 21113791d3bSDarrick J. Wong 21213791d3bSDarrick J. Wong return ret; 21313791d3bSDarrick J. Wong } 21413791d3bSDarrick J. Wong 21513791d3bSDarrick J. Wong /* 21613791d3bSDarrick J. Wong * Check the leaf freemap from the usage bitmap. Returns false if the 21713791d3bSDarrick J. Wong * attr freemap has problems or points to used space. 21813791d3bSDarrick J. Wong */ 21913791d3bSDarrick J. Wong STATIC bool 220c517b3aaSDarrick J. Wong xchk_xattr_check_freemap( 2211d8a748aSDarrick J. Wong struct xfs_scrub *sc, 22213791d3bSDarrick J. Wong unsigned long *map, 22313791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr) 22413791d3bSDarrick J. Wong { 2253addd248SDarrick J. Wong unsigned long *freemap = xchk_xattr_freemap(sc); 2263addd248SDarrick J. Wong unsigned long *dstmap = xchk_xattr_dstmap(sc); 22713791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 22813791d3bSDarrick J. Wong int i; 22913791d3bSDarrick J. Wong 23013791d3bSDarrick J. Wong /* Construct bitmap of freemap contents. */ 23113791d3bSDarrick J. Wong bitmap_zero(freemap, mapsize); 23213791d3bSDarrick J. Wong for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 233c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(sc, freemap, 23413791d3bSDarrick J. Wong leafhdr->freemap[i].base, 23513791d3bSDarrick J. Wong leafhdr->freemap[i].size)) 23613791d3bSDarrick J. Wong return false; 23713791d3bSDarrick J. Wong } 23813791d3bSDarrick J. Wong 23913791d3bSDarrick J. Wong /* Look for bits that are set in freemap and are marked in use. */ 24013791d3bSDarrick J. Wong return bitmap_and(dstmap, freemap, map, mapsize) == 0; 24113791d3bSDarrick J. Wong } 24213791d3bSDarrick J. Wong 24313791d3bSDarrick J. Wong /* 24413791d3bSDarrick J. Wong * Check this leaf entry's relations to everything else. 24513791d3bSDarrick J. Wong * Returns the number of bytes used for the name/value data. 24613791d3bSDarrick J. Wong */ 24713791d3bSDarrick J. Wong STATIC void 248c517b3aaSDarrick J. Wong xchk_xattr_entry( 249c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 25013791d3bSDarrick J. Wong int level, 25113791d3bSDarrick J. Wong char *buf_end, 25213791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf, 25313791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr, 25413791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent, 25513791d3bSDarrick J. Wong int idx, 25613791d3bSDarrick J. Wong unsigned int *usedbytes, 25713791d3bSDarrick J. Wong __u32 *last_hashval) 25813791d3bSDarrick J. Wong { 25913791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 2603addd248SDarrick J. Wong unsigned long *usedmap = xchk_xattr_usedmap(ds->sc); 26113791d3bSDarrick J. Wong char *name_end; 26213791d3bSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 26313791d3bSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 26413791d3bSDarrick J. Wong unsigned int nameidx; 26513791d3bSDarrick J. Wong unsigned int namesize; 26613791d3bSDarrick J. Wong 26713791d3bSDarrick J. Wong if (ent->pad2 != 0) 268c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 26913791d3bSDarrick J. Wong 27013791d3bSDarrick J. Wong /* Hash values in order? */ 27113791d3bSDarrick J. Wong if (be32_to_cpu(ent->hashval) < *last_hashval) 272c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 27313791d3bSDarrick J. Wong *last_hashval = be32_to_cpu(ent->hashval); 27413791d3bSDarrick J. Wong 27513791d3bSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 27613791d3bSDarrick J. Wong if (nameidx < leafhdr->firstused || 27713791d3bSDarrick J. Wong nameidx >= mp->m_attr_geo->blksize) { 278c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 27913791d3bSDarrick J. Wong return; 28013791d3bSDarrick J. Wong } 28113791d3bSDarrick J. Wong 28213791d3bSDarrick J. Wong /* Check the name information. */ 28313791d3bSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 28413791d3bSDarrick J. Wong lentry = xfs_attr3_leaf_name_local(leaf, idx); 28513791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 28613791d3bSDarrick J. Wong be16_to_cpu(lentry->valuelen)); 28713791d3bSDarrick J. Wong name_end = (char *)lentry + namesize; 28813791d3bSDarrick J. Wong if (lentry->namelen == 0) 289c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29013791d3bSDarrick J. Wong } else { 29113791d3bSDarrick J. Wong rentry = xfs_attr3_leaf_name_remote(leaf, idx); 29213791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 29313791d3bSDarrick J. Wong name_end = (char *)rentry + namesize; 29413791d3bSDarrick J. Wong if (rentry->namelen == 0 || rentry->valueblk == 0) 295c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29613791d3bSDarrick J. Wong } 29713791d3bSDarrick J. Wong if (name_end > buf_end) 298c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29913791d3bSDarrick J. Wong 300c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize)) 301c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 30213791d3bSDarrick J. Wong if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 30313791d3bSDarrick J. Wong *usedbytes += namesize; 30413791d3bSDarrick J. Wong } 30513791d3bSDarrick J. Wong 30613791d3bSDarrick J. Wong /* Scrub an attribute leaf. */ 30713791d3bSDarrick J. Wong STATIC int 308c517b3aaSDarrick J. Wong xchk_xattr_block( 309c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 31013791d3bSDarrick J. Wong int level) 31113791d3bSDarrick J. Wong { 31213791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr leafhdr; 31313791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 31413791d3bSDarrick J. Wong struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 31513791d3bSDarrick J. Wong struct xfs_buf *bp = blk->bp; 31613791d3bSDarrick J. Wong xfs_dablk_t *last_checked = ds->private; 31713791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf = bp->b_addr; 31813791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent; 31913791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *entries; 3206d6cceddSDarrick J. Wong unsigned long *usedmap; 32113791d3bSDarrick J. Wong char *buf_end; 32213791d3bSDarrick J. Wong size_t off; 32313791d3bSDarrick J. Wong __u32 last_hashval = 0; 32413791d3bSDarrick J. Wong unsigned int usedbytes = 0; 32513791d3bSDarrick J. Wong unsigned int hdrsize; 32613791d3bSDarrick J. Wong int i; 3276d6cceddSDarrick J. Wong int error; 32813791d3bSDarrick J. Wong 32913791d3bSDarrick J. Wong if (*last_checked == blk->blkno) 33013791d3bSDarrick J. Wong return 0; 3316d6cceddSDarrick J. Wong 3326d6cceddSDarrick J. Wong /* Allocate memory for block usage checking. */ 33348ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(ds->sc, 0, XCHK_GFP_FLAGS); 3346d6cceddSDarrick J. Wong if (error == -ENOMEM) 3356d6cceddSDarrick J. Wong return -EDEADLOCK; 3366d6cceddSDarrick J. Wong if (error) 3376d6cceddSDarrick J. Wong return error; 3386d6cceddSDarrick J. Wong usedmap = xchk_xattr_usedmap(ds->sc); 3396d6cceddSDarrick J. Wong 34013791d3bSDarrick J. Wong *last_checked = blk->blkno; 34113791d3bSDarrick J. Wong bitmap_zero(usedmap, mp->m_attr_geo->blksize); 34213791d3bSDarrick J. Wong 34313791d3bSDarrick J. Wong /* Check all the padding. */ 34438c26bfdSDave Chinner if (xfs_has_crc(ds->sc->mp)) { 345*ee366fe4SDarrick J. Wong struct xfs_attr3_leafblock *leaf3 = bp->b_addr; 34613791d3bSDarrick J. Wong 347*ee366fe4SDarrick J. Wong if (leaf3->hdr.pad1 != 0 || leaf3->hdr.pad2 != 0 || 348*ee366fe4SDarrick J. Wong leaf3->hdr.info.hdr.pad != 0) 349c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35013791d3bSDarrick J. Wong } else { 35113791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 352c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 35313791d3bSDarrick J. Wong } 35413791d3bSDarrick J. Wong 35513791d3bSDarrick J. Wong /* Check the leaf header */ 35613791d3bSDarrick J. Wong xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 35713791d3bSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(leaf); 35813791d3bSDarrick J. Wong 35913791d3bSDarrick J. Wong if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 360c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36113791d3bSDarrick J. Wong if (leafhdr.firstused > mp->m_attr_geo->blksize) 362c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36313791d3bSDarrick J. Wong if (leafhdr.firstused < hdrsize) 364c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 365c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize)) 366c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 36713791d3bSDarrick J. Wong 36813791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 36913791d3bSDarrick J. Wong goto out; 37013791d3bSDarrick J. Wong 37113791d3bSDarrick J. Wong entries = xfs_attr3_leaf_entryp(leaf); 37213791d3bSDarrick J. Wong if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 373c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37413791d3bSDarrick J. Wong 37513791d3bSDarrick J. Wong buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 37613791d3bSDarrick J. Wong for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 37713791d3bSDarrick J. Wong /* Mark the leaf entry itself. */ 37813791d3bSDarrick J. Wong off = (char *)ent - (char *)leaf; 379c517b3aaSDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, usedmap, off, 38013791d3bSDarrick J. Wong sizeof(xfs_attr_leaf_entry_t))) { 381c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 38213791d3bSDarrick J. Wong goto out; 38313791d3bSDarrick J. Wong } 38413791d3bSDarrick J. Wong 38513791d3bSDarrick J. Wong /* Check the entry and nameval. */ 386c517b3aaSDarrick J. Wong xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 3873addd248SDarrick J. Wong ent, i, &usedbytes, &last_hashval); 38813791d3bSDarrick J. Wong 38913791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 39013791d3bSDarrick J. Wong goto out; 39113791d3bSDarrick J. Wong } 39213791d3bSDarrick J. Wong 393c517b3aaSDarrick J. Wong if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr)) 394c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39513791d3bSDarrick J. Wong 39613791d3bSDarrick J. Wong if (leafhdr.usedbytes != usedbytes) 397c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39813791d3bSDarrick J. Wong 39913791d3bSDarrick J. Wong out: 40013791d3bSDarrick J. Wong return 0; 40113791d3bSDarrick J. Wong } 40213791d3bSDarrick J. Wong 403eec0482eSDarrick J. Wong /* Scrub a attribute btree record. */ 404eec0482eSDarrick J. Wong STATIC int 405c517b3aaSDarrick J. Wong xchk_xattr_rec( 406c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 407649d9d98SChristoph Hellwig int level) 408eec0482eSDarrick J. Wong { 409eec0482eSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 410649d9d98SChristoph Hellwig struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 411eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 412eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 413eec0482eSDarrick J. Wong struct xfs_buf *bp; 414649d9d98SChristoph Hellwig struct xfs_attr_leaf_entry *ent; 415eec0482eSDarrick J. Wong xfs_dahash_t calc_hash; 416eec0482eSDarrick J. Wong xfs_dahash_t hash; 417eec0482eSDarrick J. Wong int nameidx; 418eec0482eSDarrick J. Wong int hdrsize; 419eec0482eSDarrick J. Wong unsigned int badflags; 420eec0482eSDarrick J. Wong int error; 421eec0482eSDarrick J. Wong 422649d9d98SChristoph Hellwig ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); 423649d9d98SChristoph Hellwig 424649d9d98SChristoph Hellwig ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index; 425eec0482eSDarrick J. Wong 42613791d3bSDarrick J. Wong /* Check the whole block, if necessary. */ 427c517b3aaSDarrick J. Wong error = xchk_xattr_block(ds, level); 42813791d3bSDarrick J. Wong if (error) 42913791d3bSDarrick J. Wong goto out; 43013791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 43113791d3bSDarrick J. Wong goto out; 43213791d3bSDarrick J. Wong 433eec0482eSDarrick J. Wong /* Check the hash of the entry. */ 434c517b3aaSDarrick J. Wong error = xchk_da_btree_hash(ds, level, &ent->hashval); 435eec0482eSDarrick J. Wong if (error) 436eec0482eSDarrick J. Wong goto out; 437eec0482eSDarrick J. Wong 438eec0482eSDarrick J. Wong /* Find the attr entry's location. */ 439eec0482eSDarrick J. Wong bp = blk->bp; 440eec0482eSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 441eec0482eSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 442eec0482eSDarrick J. Wong if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 443c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 444eec0482eSDarrick J. Wong goto out; 445eec0482eSDarrick J. Wong } 446eec0482eSDarrick J. Wong 447eec0482eSDarrick J. Wong /* Retrieve the entry and check it. */ 448eec0482eSDarrick J. Wong hash = be32_to_cpu(ent->hashval); 449eec0482eSDarrick J. Wong badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 450eec0482eSDarrick J. Wong XFS_ATTR_INCOMPLETE); 451eec0482eSDarrick J. Wong if ((ent->flags & badflags) != 0) 452c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 453eec0482eSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 454eec0482eSDarrick J. Wong lentry = (struct xfs_attr_leaf_name_local *) 455eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 456eec0482eSDarrick J. Wong if (lentry->namelen <= 0) { 457c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 458eec0482eSDarrick J. Wong goto out; 459eec0482eSDarrick J. Wong } 460eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 461eec0482eSDarrick J. Wong } else { 462eec0482eSDarrick J. Wong rentry = (struct xfs_attr_leaf_name_remote *) 463eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 464eec0482eSDarrick J. Wong if (rentry->namelen <= 0) { 465c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 466eec0482eSDarrick J. Wong goto out; 467eec0482eSDarrick J. Wong } 468eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 469eec0482eSDarrick J. Wong } 470eec0482eSDarrick J. Wong if (calc_hash != hash) 471c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 472eec0482eSDarrick J. Wong 473eec0482eSDarrick J. Wong out: 474eec0482eSDarrick J. Wong return error; 475eec0482eSDarrick J. Wong } 476eec0482eSDarrick J. Wong 477eec0482eSDarrick J. Wong /* Scrub the extended attribute metadata. */ 478eec0482eSDarrick J. Wong int 479c517b3aaSDarrick J. Wong xchk_xattr( 4801d8a748aSDarrick J. Wong struct xfs_scrub *sc) 481eec0482eSDarrick J. Wong { 482c517b3aaSDarrick J. Wong struct xchk_xattr sx; 48313791d3bSDarrick J. Wong xfs_dablk_t last_checked = -1U; 484eec0482eSDarrick J. Wong int error = 0; 485eec0482eSDarrick J. Wong 486eec0482eSDarrick J. Wong if (!xfs_inode_hasattr(sc->ip)) 487eec0482eSDarrick J. Wong return -ENOENT; 488eec0482eSDarrick J. Wong 489eec0482eSDarrick J. Wong memset(&sx, 0, sizeof(sx)); 490eec0482eSDarrick J. Wong /* Check attribute tree structure */ 491c517b3aaSDarrick J. Wong error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 49213791d3bSDarrick J. Wong &last_checked); 493eec0482eSDarrick J. Wong if (error) 494eec0482eSDarrick J. Wong goto out; 495eec0482eSDarrick J. Wong 496eec0482eSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 497eec0482eSDarrick J. Wong goto out; 498eec0482eSDarrick J. Wong 499eec0482eSDarrick J. Wong /* Check that every attr key can also be looked up by hash. */ 500eec0482eSDarrick J. Wong sx.context.dp = sc->ip; 501eec0482eSDarrick J. Wong sx.context.resynch = 1; 502c517b3aaSDarrick J. Wong sx.context.put_listent = xchk_xattr_listent; 503eec0482eSDarrick J. Wong sx.context.tp = sc->tp; 5045e813574SChristoph Hellwig sx.context.allow_incomplete = true; 505eec0482eSDarrick J. Wong sx.sc = sc; 506eec0482eSDarrick J. Wong 507eec0482eSDarrick J. Wong /* 508eec0482eSDarrick J. Wong * Look up every xattr in this file by name. 509eec0482eSDarrick J. Wong * 510eec0482eSDarrick J. Wong * Use the backend implementation of xfs_attr_list to call 511c517b3aaSDarrick J. Wong * xchk_xattr_listent on every attribute key in this inode. 512eec0482eSDarrick J. Wong * In other words, we use the same iterator/callback mechanism 513eec0482eSDarrick J. Wong * that listattr uses to scrub extended attributes, though in our 514eec0482eSDarrick J. Wong * _listent function, we check the value of the attribute. 515eec0482eSDarrick J. Wong * 516eec0482eSDarrick J. Wong * The VFS only locks i_rwsem when modifying attrs, so keep all 517eec0482eSDarrick J. Wong * three locks held because that's the only way to ensure we're 518eec0482eSDarrick J. Wong * the only thread poking into the da btree. We traverse the da 519eec0482eSDarrick J. Wong * btree while holding a leaf buffer locked for the xattr name 520eec0482eSDarrick J. Wong * iteration, which doesn't really follow the usual buffer 521eec0482eSDarrick J. Wong * locking order. 522eec0482eSDarrick J. Wong */ 52317e1dd83SChristoph Hellwig error = xfs_attr_list_ilocked(&sx.context); 524c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 525eec0482eSDarrick J. Wong goto out; 5262c3b83d7SDarrick J. Wong 5272c3b83d7SDarrick J. Wong /* Did our listent function try to return any errors? */ 5282c3b83d7SDarrick J. Wong if (sx.context.seen_enough < 0) 5292c3b83d7SDarrick J. Wong error = sx.context.seen_enough; 530eec0482eSDarrick J. Wong out: 531eec0482eSDarrick J. Wong return error; 532eec0482eSDarrick J. Wong } 533