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 2391781ff5SDarrick J. Wong /* Free the buffers linked from the xattr buffer. */ 2491781ff5SDarrick J. Wong static void 2591781ff5SDarrick J. Wong xchk_xattr_buf_cleanup( 2691781ff5SDarrick J. Wong void *priv) 2791781ff5SDarrick J. Wong { 2891781ff5SDarrick J. Wong struct xchk_xattr_buf *ab = priv; 2991781ff5SDarrick J. Wong 3091781ff5SDarrick J. Wong kvfree(ab->freemap); 3191781ff5SDarrick J. Wong ab->freemap = NULL; 32*80069284SDarrick J. Wong kvfree(ab->usedmap); 33*80069284SDarrick J. Wong ab->usedmap = NULL; 3491781ff5SDarrick J. Wong } 3591781ff5SDarrick J. Wong 366d6cceddSDarrick J. Wong /* 376d6cceddSDarrick J. Wong * Allocate enough memory to hold an attr value and attr block bitmaps, 386d6cceddSDarrick J. Wong * reallocating the buffer if necessary. Buffer contents are not preserved 396d6cceddSDarrick J. Wong * across a reallocation. 406d6cceddSDarrick J. Wong */ 41d634525dSDave Chinner static int 4200816759SDarrick J. Wong xchk_setup_xattr_buf( 4300816759SDarrick J. Wong struct xfs_scrub *sc, 446d6cceddSDarrick J. Wong size_t value_size, 45d634525dSDave Chinner gfp_t flags) 4600816759SDarrick J. Wong { 47*80069284SDarrick J. Wong size_t sz = value_size; 4891781ff5SDarrick J. Wong size_t bmp_sz; 496d6cceddSDarrick J. Wong struct xchk_xattr_buf *ab = sc->buf; 50*80069284SDarrick J. Wong unsigned long *old_usedmap = NULL; 5191781ff5SDarrick J. Wong unsigned long *old_freemap = NULL; 5291781ff5SDarrick J. Wong 5391781ff5SDarrick J. Wong bmp_sz = sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize); 5400816759SDarrick J. Wong 5500816759SDarrick J. Wong /* 566d6cceddSDarrick J. Wong * If there's already a buffer, figure out if we need to reallocate it 576d6cceddSDarrick J. Wong * to accommodate a larger size. 586d6cceddSDarrick J. Wong */ 596d6cceddSDarrick J. Wong if (ab) { 606d6cceddSDarrick J. Wong if (sz <= ab->sz) 616d6cceddSDarrick J. Wong return 0; 6291781ff5SDarrick J. Wong old_freemap = ab->freemap; 63*80069284SDarrick J. Wong old_usedmap = ab->usedmap; 64306195f3SDarrick J. Wong kvfree(ab); 656d6cceddSDarrick J. Wong sc->buf = NULL; 666d6cceddSDarrick J. Wong } 676d6cceddSDarrick J. Wong 68036f463fSDarrick J. Wong /* 69036f463fSDarrick J. Wong * Don't zero the buffer upon allocation to avoid runtime overhead. 70036f463fSDarrick J. Wong * All users must be careful never to read uninitialized contents. 71036f463fSDarrick J. Wong */ 72d634525dSDave Chinner ab = kvmalloc(sizeof(*ab) + sz, flags); 736d6cceddSDarrick J. Wong if (!ab) 7400816759SDarrick J. Wong return -ENOMEM; 756d6cceddSDarrick J. Wong ab->sz = sz; 766d6cceddSDarrick J. Wong sc->buf = ab; 7791781ff5SDarrick J. Wong sc->buf_cleanup = xchk_xattr_buf_cleanup; 7891781ff5SDarrick J. Wong 79*80069284SDarrick J. Wong if (old_usedmap) { 80*80069284SDarrick J. Wong ab->usedmap = old_usedmap; 81*80069284SDarrick J. Wong } else { 82*80069284SDarrick J. Wong ab->usedmap = kvmalloc(bmp_sz, flags); 83*80069284SDarrick J. Wong if (!ab->usedmap) 84*80069284SDarrick J. Wong return -ENOMEM; 85*80069284SDarrick J. Wong } 86*80069284SDarrick J. Wong 8791781ff5SDarrick J. Wong if (old_freemap) { 8891781ff5SDarrick J. Wong ab->freemap = old_freemap; 8991781ff5SDarrick J. Wong } else { 9091781ff5SDarrick J. Wong ab->freemap = kvmalloc(bmp_sz, flags); 9191781ff5SDarrick J. Wong if (!ab->freemap) 9291781ff5SDarrick J. Wong return -ENOMEM; 9391781ff5SDarrick J. Wong } 9491781ff5SDarrick J. Wong 9500816759SDarrick J. Wong return 0; 9600816759SDarrick J. Wong } 9700816759SDarrick J. Wong 98eec0482eSDarrick J. Wong /* Set us up to scrub an inode's extended attributes. */ 99eec0482eSDarrick J. Wong int 100c517b3aaSDarrick J. Wong xchk_setup_xattr( 101026f57ebSDarrick J. Wong struct xfs_scrub *sc) 102eec0482eSDarrick J. Wong { 10300816759SDarrick J. Wong int error; 10413791d3bSDarrick J. Wong 1056d6cceddSDarrick J. Wong /* 1066d6cceddSDarrick J. Wong * We failed to get memory while checking attrs, so this time try to 1076d6cceddSDarrick J. Wong * get all the memory we're ever going to need. Allocate the buffer 1086d6cceddSDarrick J. Wong * without the inode lock held, which means we can sleep. 1096d6cceddSDarrick J. Wong */ 1106d6cceddSDarrick J. Wong if (sc->flags & XCHK_TRY_HARDER) { 11148ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX, 11248ff4045SDarrick J. Wong XCHK_GFP_FLAGS); 11300816759SDarrick J. Wong if (error) 11400816759SDarrick J. Wong return error; 1156d6cceddSDarrick J. Wong } 116eec0482eSDarrick J. Wong 117026f57ebSDarrick J. Wong return xchk_setup_inode_contents(sc, 0); 118eec0482eSDarrick J. Wong } 119eec0482eSDarrick J. Wong 120eec0482eSDarrick J. Wong /* Extended Attributes */ 121eec0482eSDarrick J. Wong 122c517b3aaSDarrick J. Wong struct xchk_xattr { 123eec0482eSDarrick J. Wong struct xfs_attr_list_context context; 1241d8a748aSDarrick J. Wong struct xfs_scrub *sc; 125eec0482eSDarrick J. Wong }; 126eec0482eSDarrick J. Wong 127eec0482eSDarrick J. Wong /* 128eec0482eSDarrick J. Wong * Check that an extended attribute key can be looked up by hash. 129eec0482eSDarrick J. Wong * 13017e1dd83SChristoph Hellwig * We use the XFS attribute list iterator (i.e. xfs_attr_list_ilocked) 131eec0482eSDarrick J. Wong * to call this function for every attribute key in an inode. Once 132eec0482eSDarrick J. Wong * we're here, we load the attribute value to see if any errors happen, 133eec0482eSDarrick J. Wong * or if we get more or less data than we expected. 134eec0482eSDarrick J. Wong */ 135eec0482eSDarrick J. Wong static void 136c517b3aaSDarrick J. Wong xchk_xattr_listent( 137eec0482eSDarrick J. Wong struct xfs_attr_list_context *context, 138eec0482eSDarrick J. Wong int flags, 139eec0482eSDarrick J. Wong unsigned char *name, 140eec0482eSDarrick J. Wong int namelen, 141eec0482eSDarrick J. Wong int valuelen) 142eec0482eSDarrick J. Wong { 143c517b3aaSDarrick J. Wong struct xchk_xattr *sx; 14488aa5de4SChristoph Hellwig struct xfs_da_args args = { NULL }; 145eec0482eSDarrick J. Wong int error = 0; 146eec0482eSDarrick J. Wong 147c517b3aaSDarrick J. Wong sx = container_of(context, struct xchk_xattr, context); 148eec0482eSDarrick J. Wong 1493258cb20SDarrick J. Wong if (xchk_should_terminate(sx->sc, &error)) { 1502c3b83d7SDarrick J. Wong context->seen_enough = error; 1513258cb20SDarrick J. Wong return; 1523258cb20SDarrick J. Wong } 1533258cb20SDarrick J. Wong 154eec0482eSDarrick J. Wong if (flags & XFS_ATTR_INCOMPLETE) { 155eec0482eSDarrick J. Wong /* Incomplete attr key, just mark the inode for preening. */ 156c517b3aaSDarrick J. Wong xchk_ino_set_preen(sx->sc, context->dp->i_ino); 157eec0482eSDarrick J. Wong return; 158eec0482eSDarrick J. Wong } 159eec0482eSDarrick J. Wong 160c12ad414SDarrick J. Wong /* Only one namespace bit allowed. */ 161c12ad414SDarrick J. Wong if (hweight32(flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) { 162c12ad414SDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 163c12ad414SDarrick J. Wong goto fail_xref; 164c12ad414SDarrick J. Wong } 165c12ad414SDarrick J. Wong 16665480536SDarrick J. Wong /* Does this name make sense? */ 16765480536SDarrick J. Wong if (!xfs_attr_namecheck(name, namelen)) { 16865480536SDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 169c12ad414SDarrick J. Wong goto fail_xref; 17065480536SDarrick J. Wong } 17165480536SDarrick J. Wong 1726d6cceddSDarrick J. Wong /* 1736d6cceddSDarrick J. Wong * Try to allocate enough memory to extrat the attr value. If that 1746d6cceddSDarrick J. Wong * doesn't work, we overload the seen_enough variable to convey 1756d6cceddSDarrick J. Wong * the error message back to the main scrub function. 1766d6cceddSDarrick J. Wong */ 17748ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(sx->sc, valuelen, XCHK_GFP_FLAGS); 1786d6cceddSDarrick J. Wong if (error == -ENOMEM) 1796d6cceddSDarrick J. Wong error = -EDEADLOCK; 1806d6cceddSDarrick J. Wong if (error) { 1816d6cceddSDarrick J. Wong context->seen_enough = error; 1826d6cceddSDarrick J. Wong return; 1836d6cceddSDarrick J. Wong } 1846d6cceddSDarrick J. Wong 1851d733019SChristoph Hellwig args.op_flags = XFS_DA_OP_NOTIME; 186d5f0f49aSChristoph Hellwig args.attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK; 187eec0482eSDarrick J. Wong args.geo = context->dp->i_mount->m_attr_geo; 188eec0482eSDarrick J. Wong args.whichfork = XFS_ATTR_FORK; 189eec0482eSDarrick J. Wong args.dp = context->dp; 190eec0482eSDarrick J. Wong args.name = name; 191eec0482eSDarrick J. Wong args.namelen = namelen; 192eec0482eSDarrick J. Wong args.hashval = xfs_da_hashname(args.name, args.namelen); 193eec0482eSDarrick J. Wong args.trans = context->tp; 1943addd248SDarrick J. Wong args.value = xchk_xattr_valuebuf(sx->sc); 1956d6cceddSDarrick J. Wong args.valuelen = valuelen; 196eec0482eSDarrick J. Wong 197c36f533fSChristoph Hellwig error = xfs_attr_get_ilocked(&args); 198806d3909SDarrick J. Wong /* ENODATA means the hash lookup failed and the attr is bad */ 199806d3909SDarrick J. Wong if (error == -ENODATA) 200806d3909SDarrick J. Wong error = -EFSCORRUPTED; 201c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 202eec0482eSDarrick J. Wong &error)) 203eec0482eSDarrick J. Wong goto fail_xref; 204eec0482eSDarrick J. Wong if (args.valuelen != valuelen) 205c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 206eec0482eSDarrick J. Wong args.blkno); 207eec0482eSDarrick J. Wong fail_xref: 2088bc763c2SDarrick J. Wong if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 209e7ee96dfSDarrick J. Wong context->seen_enough = 1; 210eec0482eSDarrick J. Wong return; 211eec0482eSDarrick J. Wong } 212eec0482eSDarrick J. Wong 21313791d3bSDarrick J. Wong /* 21413791d3bSDarrick J. Wong * Mark a range [start, start+len) in this map. Returns true if the 21513791d3bSDarrick J. Wong * region was free, and false if there's a conflict or a problem. 21613791d3bSDarrick J. Wong * 21713791d3bSDarrick J. Wong * Within a char, the lowest bit of the char represents the byte with 21813791d3bSDarrick J. Wong * the smallest address 21913791d3bSDarrick J. Wong */ 22013791d3bSDarrick J. Wong STATIC bool 221c517b3aaSDarrick J. Wong xchk_xattr_set_map( 2221d8a748aSDarrick J. Wong struct xfs_scrub *sc, 22313791d3bSDarrick J. Wong unsigned long *map, 22413791d3bSDarrick J. Wong unsigned int start, 22513791d3bSDarrick J. Wong unsigned int len) 22613791d3bSDarrick J. Wong { 22713791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 22813791d3bSDarrick J. Wong bool ret = true; 22913791d3bSDarrick J. Wong 23013791d3bSDarrick J. Wong if (start >= mapsize) 23113791d3bSDarrick J. Wong return false; 23213791d3bSDarrick J. Wong if (start + len > mapsize) { 23313791d3bSDarrick J. Wong len = mapsize - start; 23413791d3bSDarrick J. Wong ret = false; 23513791d3bSDarrick J. Wong } 23613791d3bSDarrick J. Wong 23713791d3bSDarrick J. Wong if (find_next_bit(map, mapsize, start) < start + len) 23813791d3bSDarrick J. Wong ret = false; 23913791d3bSDarrick J. Wong bitmap_set(map, start, len); 24013791d3bSDarrick J. Wong 24113791d3bSDarrick J. Wong return ret; 24213791d3bSDarrick J. Wong } 24313791d3bSDarrick J. Wong 24413791d3bSDarrick J. Wong /* 24513791d3bSDarrick J. Wong * Check the leaf freemap from the usage bitmap. Returns false if the 24613791d3bSDarrick J. Wong * attr freemap has problems or points to used space. 24713791d3bSDarrick J. Wong */ 24813791d3bSDarrick J. Wong STATIC bool 249c517b3aaSDarrick J. Wong xchk_xattr_check_freemap( 2501d8a748aSDarrick J. Wong struct xfs_scrub *sc, 25113791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr) 25213791d3bSDarrick J. Wong { 25391781ff5SDarrick J. Wong struct xchk_xattr_buf *ab = sc->buf; 25413791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 25513791d3bSDarrick J. Wong int i; 25613791d3bSDarrick J. Wong 25713791d3bSDarrick J. Wong /* Construct bitmap of freemap contents. */ 25891781ff5SDarrick J. Wong bitmap_zero(ab->freemap, mapsize); 25913791d3bSDarrick J. Wong for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 26091781ff5SDarrick J. Wong if (!xchk_xattr_set_map(sc, ab->freemap, 26113791d3bSDarrick J. Wong leafhdr->freemap[i].base, 26213791d3bSDarrick J. Wong leafhdr->freemap[i].size)) 26313791d3bSDarrick J. Wong return false; 26413791d3bSDarrick J. Wong } 26513791d3bSDarrick J. Wong 26613791d3bSDarrick J. Wong /* Look for bits that are set in freemap and are marked in use. */ 267*80069284SDarrick J. Wong return !bitmap_intersects(ab->freemap, ab->usedmap, mapsize); 26813791d3bSDarrick J. Wong } 26913791d3bSDarrick J. Wong 27013791d3bSDarrick J. Wong /* 27113791d3bSDarrick J. Wong * Check this leaf entry's relations to everything else. 27213791d3bSDarrick J. Wong * Returns the number of bytes used for the name/value data. 27313791d3bSDarrick J. Wong */ 27413791d3bSDarrick J. Wong STATIC void 275c517b3aaSDarrick J. Wong xchk_xattr_entry( 276c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 27713791d3bSDarrick J. Wong int level, 27813791d3bSDarrick J. Wong char *buf_end, 27913791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf, 28013791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr, 28113791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent, 28213791d3bSDarrick J. Wong int idx, 28313791d3bSDarrick J. Wong unsigned int *usedbytes, 28413791d3bSDarrick J. Wong __u32 *last_hashval) 28513791d3bSDarrick J. Wong { 28613791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 287*80069284SDarrick J. Wong struct xchk_xattr_buf *ab = ds->sc->buf; 28813791d3bSDarrick J. Wong char *name_end; 28913791d3bSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 29013791d3bSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 29113791d3bSDarrick J. Wong unsigned int nameidx; 29213791d3bSDarrick J. Wong unsigned int namesize; 29313791d3bSDarrick J. Wong 29413791d3bSDarrick J. Wong if (ent->pad2 != 0) 295c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 29613791d3bSDarrick J. Wong 29713791d3bSDarrick J. Wong /* Hash values in order? */ 29813791d3bSDarrick J. Wong if (be32_to_cpu(ent->hashval) < *last_hashval) 299c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 30013791d3bSDarrick J. Wong *last_hashval = be32_to_cpu(ent->hashval); 30113791d3bSDarrick J. Wong 30213791d3bSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 30313791d3bSDarrick J. Wong if (nameidx < leafhdr->firstused || 30413791d3bSDarrick J. Wong nameidx >= mp->m_attr_geo->blksize) { 305c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 30613791d3bSDarrick J. Wong return; 30713791d3bSDarrick J. Wong } 30813791d3bSDarrick J. Wong 30913791d3bSDarrick J. Wong /* Check the name information. */ 31013791d3bSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 31113791d3bSDarrick J. Wong lentry = xfs_attr3_leaf_name_local(leaf, idx); 31213791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 31313791d3bSDarrick J. Wong be16_to_cpu(lentry->valuelen)); 31413791d3bSDarrick J. Wong name_end = (char *)lentry + namesize; 31513791d3bSDarrick J. Wong if (lentry->namelen == 0) 316c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 31713791d3bSDarrick J. Wong } else { 31813791d3bSDarrick J. Wong rentry = xfs_attr3_leaf_name_remote(leaf, idx); 31913791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 32013791d3bSDarrick J. Wong name_end = (char *)rentry + namesize; 32113791d3bSDarrick J. Wong if (rentry->namelen == 0 || rentry->valueblk == 0) 322c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 32313791d3bSDarrick J. Wong } 32413791d3bSDarrick J. Wong if (name_end > buf_end) 325c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 32613791d3bSDarrick J. Wong 327*80069284SDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, ab->usedmap, nameidx, namesize)) 328c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 32913791d3bSDarrick J. Wong if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 33013791d3bSDarrick J. Wong *usedbytes += namesize; 33113791d3bSDarrick J. Wong } 33213791d3bSDarrick J. Wong 33313791d3bSDarrick J. Wong /* Scrub an attribute leaf. */ 33413791d3bSDarrick J. Wong STATIC int 335c517b3aaSDarrick J. Wong xchk_xattr_block( 336c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 33713791d3bSDarrick J. Wong int level) 33813791d3bSDarrick J. Wong { 33913791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr leafhdr; 34013791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 34113791d3bSDarrick J. Wong struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 34213791d3bSDarrick J. Wong struct xfs_buf *bp = blk->bp; 34313791d3bSDarrick J. Wong xfs_dablk_t *last_checked = ds->private; 34413791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf = bp->b_addr; 34513791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent; 34613791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *entries; 347*80069284SDarrick J. Wong struct xchk_xattr_buf *ab = ds->sc->buf; 34813791d3bSDarrick J. Wong char *buf_end; 34913791d3bSDarrick J. Wong size_t off; 35013791d3bSDarrick J. Wong __u32 last_hashval = 0; 35113791d3bSDarrick J. Wong unsigned int usedbytes = 0; 35213791d3bSDarrick J. Wong unsigned int hdrsize; 35313791d3bSDarrick J. Wong int i; 3546d6cceddSDarrick J. Wong int error; 35513791d3bSDarrick J. Wong 35613791d3bSDarrick J. Wong if (*last_checked == blk->blkno) 35713791d3bSDarrick J. Wong return 0; 3586d6cceddSDarrick J. Wong 3596d6cceddSDarrick J. Wong /* Allocate memory for block usage checking. */ 36048ff4045SDarrick J. Wong error = xchk_setup_xattr_buf(ds->sc, 0, XCHK_GFP_FLAGS); 3616d6cceddSDarrick J. Wong if (error == -ENOMEM) 3626d6cceddSDarrick J. Wong return -EDEADLOCK; 3636d6cceddSDarrick J. Wong if (error) 3646d6cceddSDarrick J. Wong return error; 3656d6cceddSDarrick J. Wong 36613791d3bSDarrick J. Wong *last_checked = blk->blkno; 367*80069284SDarrick J. Wong bitmap_zero(ab->usedmap, mp->m_attr_geo->blksize); 36813791d3bSDarrick J. Wong 36913791d3bSDarrick J. Wong /* Check all the padding. */ 37038c26bfdSDave Chinner if (xfs_has_crc(ds->sc->mp)) { 371ee366fe4SDarrick J. Wong struct xfs_attr3_leafblock *leaf3 = bp->b_addr; 37213791d3bSDarrick J. Wong 373ee366fe4SDarrick J. Wong if (leaf3->hdr.pad1 != 0 || leaf3->hdr.pad2 != 0 || 374ee366fe4SDarrick J. Wong leaf3->hdr.info.hdr.pad != 0) 375c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37613791d3bSDarrick J. Wong } else { 37713791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 378c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 37913791d3bSDarrick J. Wong } 38013791d3bSDarrick J. Wong 38113791d3bSDarrick J. Wong /* Check the leaf header */ 38213791d3bSDarrick J. Wong xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 38313791d3bSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(leaf); 38413791d3bSDarrick J. Wong 38513791d3bSDarrick J. Wong if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 386c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 38713791d3bSDarrick J. Wong if (leafhdr.firstused > mp->m_attr_geo->blksize) 388c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 38913791d3bSDarrick J. Wong if (leafhdr.firstused < hdrsize) 390c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 391*80069284SDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, ab->usedmap, 0, hdrsize)) 392c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 39313791d3bSDarrick J. Wong 39413791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 39513791d3bSDarrick J. Wong goto out; 39613791d3bSDarrick J. Wong 39713791d3bSDarrick J. Wong entries = xfs_attr3_leaf_entryp(leaf); 39813791d3bSDarrick J. Wong if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 399c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 40013791d3bSDarrick J. Wong 40113791d3bSDarrick J. Wong buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 40213791d3bSDarrick J. Wong for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 40313791d3bSDarrick J. Wong /* Mark the leaf entry itself. */ 40413791d3bSDarrick J. Wong off = (char *)ent - (char *)leaf; 405*80069284SDarrick J. Wong if (!xchk_xattr_set_map(ds->sc, ab->usedmap, off, 40613791d3bSDarrick J. Wong sizeof(xfs_attr_leaf_entry_t))) { 407c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 40813791d3bSDarrick J. Wong goto out; 40913791d3bSDarrick J. Wong } 41013791d3bSDarrick J. Wong 41113791d3bSDarrick J. Wong /* Check the entry and nameval. */ 412c517b3aaSDarrick J. Wong xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 4133addd248SDarrick J. Wong ent, i, &usedbytes, &last_hashval); 41413791d3bSDarrick J. Wong 41513791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 41613791d3bSDarrick J. Wong goto out; 41713791d3bSDarrick J. Wong } 41813791d3bSDarrick J. Wong 419*80069284SDarrick J. Wong if (!xchk_xattr_check_freemap(ds->sc, &leafhdr)) 420c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 42113791d3bSDarrick J. Wong 42213791d3bSDarrick J. Wong if (leafhdr.usedbytes != usedbytes) 423c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 42413791d3bSDarrick J. Wong 42513791d3bSDarrick J. Wong out: 42613791d3bSDarrick J. Wong return 0; 42713791d3bSDarrick J. Wong } 42813791d3bSDarrick J. Wong 429eec0482eSDarrick J. Wong /* Scrub a attribute btree record. */ 430eec0482eSDarrick J. Wong STATIC int 431c517b3aaSDarrick J. Wong xchk_xattr_rec( 432c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 433649d9d98SChristoph Hellwig int level) 434eec0482eSDarrick J. Wong { 435eec0482eSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 436649d9d98SChristoph Hellwig struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 437eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 438eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 439eec0482eSDarrick J. Wong struct xfs_buf *bp; 440649d9d98SChristoph Hellwig struct xfs_attr_leaf_entry *ent; 441eec0482eSDarrick J. Wong xfs_dahash_t calc_hash; 442eec0482eSDarrick J. Wong xfs_dahash_t hash; 443eec0482eSDarrick J. Wong int nameidx; 444eec0482eSDarrick J. Wong int hdrsize; 445eec0482eSDarrick J. Wong unsigned int badflags; 446eec0482eSDarrick J. Wong int error; 447eec0482eSDarrick J. Wong 448649d9d98SChristoph Hellwig ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); 449649d9d98SChristoph Hellwig 450649d9d98SChristoph Hellwig ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index; 451eec0482eSDarrick J. Wong 45213791d3bSDarrick J. Wong /* Check the whole block, if necessary. */ 453c517b3aaSDarrick J. Wong error = xchk_xattr_block(ds, level); 45413791d3bSDarrick J. Wong if (error) 45513791d3bSDarrick J. Wong goto out; 45613791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 45713791d3bSDarrick J. Wong goto out; 45813791d3bSDarrick J. Wong 459eec0482eSDarrick J. Wong /* Check the hash of the entry. */ 460c517b3aaSDarrick J. Wong error = xchk_da_btree_hash(ds, level, &ent->hashval); 461eec0482eSDarrick J. Wong if (error) 462eec0482eSDarrick J. Wong goto out; 463eec0482eSDarrick J. Wong 464eec0482eSDarrick J. Wong /* Find the attr entry's location. */ 465eec0482eSDarrick J. Wong bp = blk->bp; 466eec0482eSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 467eec0482eSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 468eec0482eSDarrick J. Wong if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 469c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 470eec0482eSDarrick J. Wong goto out; 471eec0482eSDarrick J. Wong } 472eec0482eSDarrick J. Wong 473eec0482eSDarrick J. Wong /* Retrieve the entry and check it. */ 474eec0482eSDarrick J. Wong hash = be32_to_cpu(ent->hashval); 475eec0482eSDarrick J. Wong badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 476eec0482eSDarrick J. Wong XFS_ATTR_INCOMPLETE); 477eec0482eSDarrick J. Wong if ((ent->flags & badflags) != 0) 478c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 479eec0482eSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 480eec0482eSDarrick J. Wong lentry = (struct xfs_attr_leaf_name_local *) 481eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 482eec0482eSDarrick J. Wong if (lentry->namelen <= 0) { 483c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 484eec0482eSDarrick J. Wong goto out; 485eec0482eSDarrick J. Wong } 486eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 487eec0482eSDarrick J. Wong } else { 488eec0482eSDarrick J. Wong rentry = (struct xfs_attr_leaf_name_remote *) 489eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 490eec0482eSDarrick J. Wong if (rentry->namelen <= 0) { 491c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 492eec0482eSDarrick J. Wong goto out; 493eec0482eSDarrick J. Wong } 494eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 495eec0482eSDarrick J. Wong } 496eec0482eSDarrick J. Wong if (calc_hash != hash) 497c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 498eec0482eSDarrick J. Wong 499eec0482eSDarrick J. Wong out: 500eec0482eSDarrick J. Wong return error; 501eec0482eSDarrick J. Wong } 502eec0482eSDarrick J. Wong 503eec0482eSDarrick J. Wong /* Scrub the extended attribute metadata. */ 504eec0482eSDarrick J. Wong int 505c517b3aaSDarrick J. Wong xchk_xattr( 5061d8a748aSDarrick J. Wong struct xfs_scrub *sc) 507eec0482eSDarrick J. Wong { 508c517b3aaSDarrick J. Wong struct xchk_xattr sx; 50913791d3bSDarrick J. Wong xfs_dablk_t last_checked = -1U; 510eec0482eSDarrick J. Wong int error = 0; 511eec0482eSDarrick J. Wong 512eec0482eSDarrick J. Wong if (!xfs_inode_hasattr(sc->ip)) 513eec0482eSDarrick J. Wong return -ENOENT; 514eec0482eSDarrick J. Wong 515eec0482eSDarrick J. Wong memset(&sx, 0, sizeof(sx)); 516eec0482eSDarrick J. Wong /* Check attribute tree structure */ 517c517b3aaSDarrick J. Wong error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 51813791d3bSDarrick J. Wong &last_checked); 519eec0482eSDarrick J. Wong if (error) 520eec0482eSDarrick J. Wong goto out; 521eec0482eSDarrick J. Wong 522eec0482eSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 523eec0482eSDarrick J. Wong goto out; 524eec0482eSDarrick J. Wong 525eec0482eSDarrick J. Wong /* Check that every attr key can also be looked up by hash. */ 526eec0482eSDarrick J. Wong sx.context.dp = sc->ip; 527eec0482eSDarrick J. Wong sx.context.resynch = 1; 528c517b3aaSDarrick J. Wong sx.context.put_listent = xchk_xattr_listent; 529eec0482eSDarrick J. Wong sx.context.tp = sc->tp; 5305e813574SChristoph Hellwig sx.context.allow_incomplete = true; 531eec0482eSDarrick J. Wong sx.sc = sc; 532eec0482eSDarrick J. Wong 533eec0482eSDarrick J. Wong /* 534eec0482eSDarrick J. Wong * Look up every xattr in this file by name. 535eec0482eSDarrick J. Wong * 536eec0482eSDarrick J. Wong * Use the backend implementation of xfs_attr_list to call 537c517b3aaSDarrick J. Wong * xchk_xattr_listent on every attribute key in this inode. 538eec0482eSDarrick J. Wong * In other words, we use the same iterator/callback mechanism 539eec0482eSDarrick J. Wong * that listattr uses to scrub extended attributes, though in our 540eec0482eSDarrick J. Wong * _listent function, we check the value of the attribute. 541eec0482eSDarrick J. Wong * 542eec0482eSDarrick J. Wong * The VFS only locks i_rwsem when modifying attrs, so keep all 543eec0482eSDarrick J. Wong * three locks held because that's the only way to ensure we're 544eec0482eSDarrick J. Wong * the only thread poking into the da btree. We traverse the da 545eec0482eSDarrick J. Wong * btree while holding a leaf buffer locked for the xattr name 546eec0482eSDarrick J. Wong * iteration, which doesn't really follow the usual buffer 547eec0482eSDarrick J. Wong * locking order. 548eec0482eSDarrick J. Wong */ 54917e1dd83SChristoph Hellwig error = xfs_attr_list_ilocked(&sx.context); 550c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 551eec0482eSDarrick J. Wong goto out; 5522c3b83d7SDarrick J. Wong 5532c3b83d7SDarrick J. Wong /* Did our listent function try to return any errors? */ 5542c3b83d7SDarrick J. Wong if (sx.context.seen_enough < 0) 5552c3b83d7SDarrick J. Wong error = sx.context.seen_enough; 556eec0482eSDarrick J. Wong out: 557eec0482eSDarrick J. Wong return error; 558eec0482eSDarrick J. Wong } 559