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_defer.h" 13eec0482eSDarrick J. Wong #include "xfs_btree.h" 14eec0482eSDarrick J. Wong #include "xfs_bit.h" 15eec0482eSDarrick J. Wong #include "xfs_log_format.h" 16eec0482eSDarrick J. Wong #include "xfs_trans.h" 17eec0482eSDarrick J. Wong #include "xfs_sb.h" 18eec0482eSDarrick J. Wong #include "xfs_inode.h" 19eec0482eSDarrick J. Wong #include "xfs_da_format.h" 20eec0482eSDarrick J. Wong #include "xfs_da_btree.h" 21eec0482eSDarrick J. Wong #include "xfs_dir2.h" 22eec0482eSDarrick J. Wong #include "xfs_attr.h" 23eec0482eSDarrick J. Wong #include "xfs_attr_leaf.h" 24eec0482eSDarrick J. Wong #include "scrub/xfs_scrub.h" 25eec0482eSDarrick J. Wong #include "scrub/scrub.h" 26eec0482eSDarrick J. Wong #include "scrub/common.h" 27eec0482eSDarrick J. Wong #include "scrub/dabtree.h" 28eec0482eSDarrick J. Wong #include "scrub/trace.h" 29eec0482eSDarrick J. Wong 30eec0482eSDarrick J. Wong #include <linux/posix_acl_xattr.h> 31eec0482eSDarrick J. Wong #include <linux/xattr.h> 32eec0482eSDarrick J. Wong 33eec0482eSDarrick J. Wong /* Set us up to scrub an inode's extended attributes. */ 34eec0482eSDarrick J. Wong int 35eec0482eSDarrick J. Wong xfs_scrub_setup_xattr( 36eec0482eSDarrick J. Wong struct xfs_scrub_context *sc, 37eec0482eSDarrick J. Wong struct xfs_inode *ip) 38eec0482eSDarrick J. Wong { 3913791d3bSDarrick J. Wong size_t sz; 4013791d3bSDarrick J. Wong 4113791d3bSDarrick J. Wong /* 4213791d3bSDarrick J. Wong * Allocate the buffer without the inode lock held. We need enough 4313791d3bSDarrick J. Wong * space to read every xattr value in the file or enough space to 4413791d3bSDarrick J. Wong * hold three copies of the xattr free space bitmap. (Not both at 4513791d3bSDarrick J. Wong * the same time.) 4613791d3bSDarrick J. Wong */ 4713791d3bSDarrick J. Wong sz = max_t(size_t, XATTR_SIZE_MAX, 3 * sizeof(long) * 4813791d3bSDarrick J. Wong BITS_TO_LONGS(sc->mp->m_attr_geo->blksize)); 4913791d3bSDarrick J. Wong sc->buf = kmem_zalloc_large(sz, KM_SLEEP); 50eec0482eSDarrick J. Wong if (!sc->buf) 51eec0482eSDarrick J. Wong return -ENOMEM; 52eec0482eSDarrick J. Wong 53eec0482eSDarrick J. Wong return xfs_scrub_setup_inode_contents(sc, ip, 0); 54eec0482eSDarrick J. Wong } 55eec0482eSDarrick J. Wong 56eec0482eSDarrick J. Wong /* Extended Attributes */ 57eec0482eSDarrick J. Wong 58eec0482eSDarrick J. Wong struct xfs_scrub_xattr { 59eec0482eSDarrick J. Wong struct xfs_attr_list_context context; 60eec0482eSDarrick J. Wong struct xfs_scrub_context *sc; 61eec0482eSDarrick J. Wong }; 62eec0482eSDarrick J. Wong 63eec0482eSDarrick J. Wong /* 64eec0482eSDarrick J. Wong * Check that an extended attribute key can be looked up by hash. 65eec0482eSDarrick J. Wong * 66eec0482eSDarrick J. Wong * We use the XFS attribute list iterator (i.e. xfs_attr_list_int_ilocked) 67eec0482eSDarrick J. Wong * to call this function for every attribute key in an inode. Once 68eec0482eSDarrick J. Wong * we're here, we load the attribute value to see if any errors happen, 69eec0482eSDarrick J. Wong * or if we get more or less data than we expected. 70eec0482eSDarrick J. Wong */ 71eec0482eSDarrick J. Wong static void 72eec0482eSDarrick J. Wong xfs_scrub_xattr_listent( 73eec0482eSDarrick J. Wong struct xfs_attr_list_context *context, 74eec0482eSDarrick J. Wong int flags, 75eec0482eSDarrick J. Wong unsigned char *name, 76eec0482eSDarrick J. Wong int namelen, 77eec0482eSDarrick J. Wong int valuelen) 78eec0482eSDarrick J. Wong { 79eec0482eSDarrick J. Wong struct xfs_scrub_xattr *sx; 8088aa5de4SChristoph Hellwig struct xfs_da_args args = { NULL }; 81eec0482eSDarrick J. Wong int error = 0; 82eec0482eSDarrick J. Wong 83eec0482eSDarrick J. Wong sx = container_of(context, struct xfs_scrub_xattr, context); 84eec0482eSDarrick J. Wong 85eec0482eSDarrick J. Wong if (flags & XFS_ATTR_INCOMPLETE) { 86eec0482eSDarrick J. Wong /* Incomplete attr key, just mark the inode for preening. */ 877e56d9eaSDarrick J. Wong xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino); 88eec0482eSDarrick J. Wong return; 89eec0482eSDarrick J. Wong } 90eec0482eSDarrick J. Wong 91eec0482eSDarrick J. Wong args.flags = ATTR_KERNOTIME; 92eec0482eSDarrick J. Wong if (flags & XFS_ATTR_ROOT) 93eec0482eSDarrick J. Wong args.flags |= ATTR_ROOT; 94eec0482eSDarrick J. Wong else if (flags & XFS_ATTR_SECURE) 95eec0482eSDarrick J. Wong args.flags |= ATTR_SECURE; 96eec0482eSDarrick J. Wong args.geo = context->dp->i_mount->m_attr_geo; 97eec0482eSDarrick J. Wong args.whichfork = XFS_ATTR_FORK; 98eec0482eSDarrick J. Wong args.dp = context->dp; 99eec0482eSDarrick J. Wong args.name = name; 100eec0482eSDarrick J. Wong args.namelen = namelen; 101eec0482eSDarrick J. Wong args.hashval = xfs_da_hashname(args.name, args.namelen); 102eec0482eSDarrick J. Wong args.trans = context->tp; 103eec0482eSDarrick J. Wong args.value = sx->sc->buf; 104eec0482eSDarrick J. Wong args.valuelen = XATTR_SIZE_MAX; 105eec0482eSDarrick J. Wong 106eec0482eSDarrick J. Wong error = xfs_attr_get_ilocked(context->dp, &args); 107eec0482eSDarrick J. Wong if (error == -EEXIST) 108eec0482eSDarrick J. Wong error = 0; 109eec0482eSDarrick J. Wong if (!xfs_scrub_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 110eec0482eSDarrick J. Wong &error)) 111eec0482eSDarrick J. Wong goto fail_xref; 112eec0482eSDarrick J. Wong if (args.valuelen != valuelen) 113eec0482eSDarrick J. Wong xfs_scrub_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 114eec0482eSDarrick J. Wong args.blkno); 115eec0482eSDarrick J. Wong fail_xref: 1168bc763c2SDarrick J. Wong if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1178bc763c2SDarrick J. Wong context->seen_enough = 1; 118eec0482eSDarrick J. Wong return; 119eec0482eSDarrick J. Wong } 120eec0482eSDarrick J. Wong 12113791d3bSDarrick J. Wong /* 12213791d3bSDarrick J. Wong * Mark a range [start, start+len) in this map. Returns true if the 12313791d3bSDarrick J. Wong * region was free, and false if there's a conflict or a problem. 12413791d3bSDarrick J. Wong * 12513791d3bSDarrick J. Wong * Within a char, the lowest bit of the char represents the byte with 12613791d3bSDarrick J. Wong * the smallest address 12713791d3bSDarrick J. Wong */ 12813791d3bSDarrick J. Wong STATIC bool 12913791d3bSDarrick J. Wong xfs_scrub_xattr_set_map( 13013791d3bSDarrick J. Wong struct xfs_scrub_context *sc, 13113791d3bSDarrick J. Wong unsigned long *map, 13213791d3bSDarrick J. Wong unsigned int start, 13313791d3bSDarrick J. Wong unsigned int len) 13413791d3bSDarrick J. Wong { 13513791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 13613791d3bSDarrick J. Wong bool ret = true; 13713791d3bSDarrick J. Wong 13813791d3bSDarrick J. Wong if (start >= mapsize) 13913791d3bSDarrick J. Wong return false; 14013791d3bSDarrick J. Wong if (start + len > mapsize) { 14113791d3bSDarrick J. Wong len = mapsize - start; 14213791d3bSDarrick J. Wong ret = false; 14313791d3bSDarrick J. Wong } 14413791d3bSDarrick J. Wong 14513791d3bSDarrick J. Wong if (find_next_bit(map, mapsize, start) < start + len) 14613791d3bSDarrick J. Wong ret = false; 14713791d3bSDarrick J. Wong bitmap_set(map, start, len); 14813791d3bSDarrick J. Wong 14913791d3bSDarrick J. Wong return ret; 15013791d3bSDarrick J. Wong } 15113791d3bSDarrick J. Wong 15213791d3bSDarrick J. Wong /* 15313791d3bSDarrick J. Wong * Check the leaf freemap from the usage bitmap. Returns false if the 15413791d3bSDarrick J. Wong * attr freemap has problems or points to used space. 15513791d3bSDarrick J. Wong */ 15613791d3bSDarrick J. Wong STATIC bool 15713791d3bSDarrick J. Wong xfs_scrub_xattr_check_freemap( 15813791d3bSDarrick J. Wong struct xfs_scrub_context *sc, 15913791d3bSDarrick J. Wong unsigned long *map, 16013791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr) 16113791d3bSDarrick J. Wong { 16213791d3bSDarrick J. Wong unsigned long *freemap; 16313791d3bSDarrick J. Wong unsigned long *dstmap; 16413791d3bSDarrick J. Wong unsigned int mapsize = sc->mp->m_attr_geo->blksize; 16513791d3bSDarrick J. Wong int i; 16613791d3bSDarrick J. Wong 16713791d3bSDarrick J. Wong /* Construct bitmap of freemap contents. */ 16813791d3bSDarrick J. Wong freemap = (unsigned long *)sc->buf + BITS_TO_LONGS(mapsize); 16913791d3bSDarrick J. Wong bitmap_zero(freemap, mapsize); 17013791d3bSDarrick J. Wong for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 17113791d3bSDarrick J. Wong if (!xfs_scrub_xattr_set_map(sc, freemap, 17213791d3bSDarrick J. Wong leafhdr->freemap[i].base, 17313791d3bSDarrick J. Wong leafhdr->freemap[i].size)) 17413791d3bSDarrick J. Wong return false; 17513791d3bSDarrick J. Wong } 17613791d3bSDarrick J. Wong 17713791d3bSDarrick J. Wong /* Look for bits that are set in freemap and are marked in use. */ 17813791d3bSDarrick J. Wong dstmap = freemap + BITS_TO_LONGS(mapsize); 17913791d3bSDarrick J. Wong return bitmap_and(dstmap, freemap, map, mapsize) == 0; 18013791d3bSDarrick J. Wong } 18113791d3bSDarrick J. Wong 18213791d3bSDarrick J. Wong /* 18313791d3bSDarrick J. Wong * Check this leaf entry's relations to everything else. 18413791d3bSDarrick J. Wong * Returns the number of bytes used for the name/value data. 18513791d3bSDarrick J. Wong */ 18613791d3bSDarrick J. Wong STATIC void 18713791d3bSDarrick J. Wong xfs_scrub_xattr_entry( 18813791d3bSDarrick J. Wong struct xfs_scrub_da_btree *ds, 18913791d3bSDarrick J. Wong int level, 19013791d3bSDarrick J. Wong char *buf_end, 19113791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf, 19213791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr *leafhdr, 19313791d3bSDarrick J. Wong unsigned long *usedmap, 19413791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent, 19513791d3bSDarrick J. Wong int idx, 19613791d3bSDarrick J. Wong unsigned int *usedbytes, 19713791d3bSDarrick J. Wong __u32 *last_hashval) 19813791d3bSDarrick J. Wong { 19913791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 20013791d3bSDarrick J. Wong char *name_end; 20113791d3bSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 20213791d3bSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 20313791d3bSDarrick J. Wong unsigned int nameidx; 20413791d3bSDarrick J. Wong unsigned int namesize; 20513791d3bSDarrick J. Wong 20613791d3bSDarrick J. Wong if (ent->pad2 != 0) 20713791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 20813791d3bSDarrick J. Wong 20913791d3bSDarrick J. Wong /* Hash values in order? */ 21013791d3bSDarrick J. Wong if (be32_to_cpu(ent->hashval) < *last_hashval) 21113791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 21213791d3bSDarrick J. Wong *last_hashval = be32_to_cpu(ent->hashval); 21313791d3bSDarrick J. Wong 21413791d3bSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 21513791d3bSDarrick J. Wong if (nameidx < leafhdr->firstused || 21613791d3bSDarrick J. Wong nameidx >= mp->m_attr_geo->blksize) { 21713791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 21813791d3bSDarrick J. Wong return; 21913791d3bSDarrick J. Wong } 22013791d3bSDarrick J. Wong 22113791d3bSDarrick J. Wong /* Check the name information. */ 22213791d3bSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 22313791d3bSDarrick J. Wong lentry = xfs_attr3_leaf_name_local(leaf, idx); 22413791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 22513791d3bSDarrick J. Wong be16_to_cpu(lentry->valuelen)); 22613791d3bSDarrick J. Wong name_end = (char *)lentry + namesize; 22713791d3bSDarrick J. Wong if (lentry->namelen == 0) 22813791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 22913791d3bSDarrick J. Wong } else { 23013791d3bSDarrick J. Wong rentry = xfs_attr3_leaf_name_remote(leaf, idx); 23113791d3bSDarrick J. Wong namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 23213791d3bSDarrick J. Wong name_end = (char *)rentry + namesize; 23313791d3bSDarrick J. Wong if (rentry->namelen == 0 || rentry->valueblk == 0) 23413791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 23513791d3bSDarrick J. Wong } 23613791d3bSDarrick J. Wong if (name_end > buf_end) 23713791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 23813791d3bSDarrick J. Wong 23913791d3bSDarrick J. Wong if (!xfs_scrub_xattr_set_map(ds->sc, usedmap, nameidx, namesize)) 24013791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 24113791d3bSDarrick J. Wong if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 24213791d3bSDarrick J. Wong *usedbytes += namesize; 24313791d3bSDarrick J. Wong } 24413791d3bSDarrick J. Wong 24513791d3bSDarrick J. Wong /* Scrub an attribute leaf. */ 24613791d3bSDarrick J. Wong STATIC int 24713791d3bSDarrick J. Wong xfs_scrub_xattr_block( 24813791d3bSDarrick J. Wong struct xfs_scrub_da_btree *ds, 24913791d3bSDarrick J. Wong int level) 25013791d3bSDarrick J. Wong { 25113791d3bSDarrick J. Wong struct xfs_attr3_icleaf_hdr leafhdr; 25213791d3bSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 25313791d3bSDarrick J. Wong struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 25413791d3bSDarrick J. Wong struct xfs_buf *bp = blk->bp; 25513791d3bSDarrick J. Wong xfs_dablk_t *last_checked = ds->private; 25613791d3bSDarrick J. Wong struct xfs_attr_leafblock *leaf = bp->b_addr; 25713791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *ent; 25813791d3bSDarrick J. Wong struct xfs_attr_leaf_entry *entries; 25913791d3bSDarrick J. Wong unsigned long *usedmap = ds->sc->buf; 26013791d3bSDarrick J. Wong char *buf_end; 26113791d3bSDarrick J. Wong size_t off; 26213791d3bSDarrick J. Wong __u32 last_hashval = 0; 26313791d3bSDarrick J. Wong unsigned int usedbytes = 0; 26413791d3bSDarrick J. Wong unsigned int hdrsize; 26513791d3bSDarrick J. Wong int i; 26613791d3bSDarrick J. Wong 26713791d3bSDarrick J. Wong if (*last_checked == blk->blkno) 26813791d3bSDarrick J. Wong return 0; 26913791d3bSDarrick J. Wong *last_checked = blk->blkno; 27013791d3bSDarrick J. Wong bitmap_zero(usedmap, mp->m_attr_geo->blksize); 27113791d3bSDarrick J. Wong 27213791d3bSDarrick J. Wong /* Check all the padding. */ 27313791d3bSDarrick J. Wong if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) { 27413791d3bSDarrick J. Wong struct xfs_attr3_leafblock *leaf = bp->b_addr; 27513791d3bSDarrick J. Wong 27613791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 || 27713791d3bSDarrick J. Wong leaf->hdr.info.hdr.pad != 0) 27813791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 27913791d3bSDarrick J. Wong } else { 28013791d3bSDarrick J. Wong if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 28113791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 28213791d3bSDarrick J. Wong } 28313791d3bSDarrick J. Wong 28413791d3bSDarrick J. Wong /* Check the leaf header */ 28513791d3bSDarrick J. Wong xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 28613791d3bSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(leaf); 28713791d3bSDarrick J. Wong 28813791d3bSDarrick J. Wong if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 28913791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 29013791d3bSDarrick J. Wong if (leafhdr.firstused > mp->m_attr_geo->blksize) 29113791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 29213791d3bSDarrick J. Wong if (leafhdr.firstused < hdrsize) 29313791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 29413791d3bSDarrick J. Wong if (!xfs_scrub_xattr_set_map(ds->sc, usedmap, 0, hdrsize)) 29513791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 29613791d3bSDarrick J. Wong 29713791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 29813791d3bSDarrick J. Wong goto out; 29913791d3bSDarrick J. Wong 30013791d3bSDarrick J. Wong entries = xfs_attr3_leaf_entryp(leaf); 30113791d3bSDarrick J. Wong if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 30213791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 30313791d3bSDarrick J. Wong 30413791d3bSDarrick J. Wong buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 30513791d3bSDarrick J. Wong for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 30613791d3bSDarrick J. Wong /* Mark the leaf entry itself. */ 30713791d3bSDarrick J. Wong off = (char *)ent - (char *)leaf; 30813791d3bSDarrick J. Wong if (!xfs_scrub_xattr_set_map(ds->sc, usedmap, off, 30913791d3bSDarrick J. Wong sizeof(xfs_attr_leaf_entry_t))) { 31013791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 31113791d3bSDarrick J. Wong goto out; 31213791d3bSDarrick J. Wong } 31313791d3bSDarrick J. Wong 31413791d3bSDarrick J. Wong /* Check the entry and nameval. */ 31513791d3bSDarrick J. Wong xfs_scrub_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 31613791d3bSDarrick J. Wong usedmap, ent, i, &usedbytes, &last_hashval); 31713791d3bSDarrick J. Wong 31813791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 31913791d3bSDarrick J. Wong goto out; 32013791d3bSDarrick J. Wong } 32113791d3bSDarrick J. Wong 32213791d3bSDarrick J. Wong if (!xfs_scrub_xattr_check_freemap(ds->sc, usedmap, &leafhdr)) 32313791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 32413791d3bSDarrick J. Wong 32513791d3bSDarrick J. Wong if (leafhdr.usedbytes != usedbytes) 32613791d3bSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 32713791d3bSDarrick J. Wong 32813791d3bSDarrick J. Wong out: 32913791d3bSDarrick J. Wong return 0; 33013791d3bSDarrick J. Wong } 33113791d3bSDarrick J. Wong 332eec0482eSDarrick J. Wong /* Scrub a attribute btree record. */ 333eec0482eSDarrick J. Wong STATIC int 334eec0482eSDarrick J. Wong xfs_scrub_xattr_rec( 335eec0482eSDarrick J. Wong struct xfs_scrub_da_btree *ds, 336eec0482eSDarrick J. Wong int level, 337eec0482eSDarrick J. Wong void *rec) 338eec0482eSDarrick J. Wong { 339eec0482eSDarrick J. Wong struct xfs_mount *mp = ds->state->mp; 340eec0482eSDarrick J. Wong struct xfs_attr_leaf_entry *ent = rec; 341eec0482eSDarrick J. Wong struct xfs_da_state_blk *blk; 342eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_local *lentry; 343eec0482eSDarrick J. Wong struct xfs_attr_leaf_name_remote *rentry; 344eec0482eSDarrick J. Wong struct xfs_buf *bp; 345eec0482eSDarrick J. Wong xfs_dahash_t calc_hash; 346eec0482eSDarrick J. Wong xfs_dahash_t hash; 347eec0482eSDarrick J. Wong int nameidx; 348eec0482eSDarrick J. Wong int hdrsize; 349eec0482eSDarrick J. Wong unsigned int badflags; 350eec0482eSDarrick J. Wong int error; 351eec0482eSDarrick J. Wong 352eec0482eSDarrick J. Wong blk = &ds->state->path.blk[level]; 353eec0482eSDarrick J. Wong 35413791d3bSDarrick J. Wong /* Check the whole block, if necessary. */ 35513791d3bSDarrick J. Wong error = xfs_scrub_xattr_block(ds, level); 35613791d3bSDarrick J. Wong if (error) 35713791d3bSDarrick J. Wong goto out; 35813791d3bSDarrick J. Wong if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 35913791d3bSDarrick J. Wong goto out; 36013791d3bSDarrick J. Wong 361eec0482eSDarrick J. Wong /* Check the hash of the entry. */ 362eec0482eSDarrick J. Wong error = xfs_scrub_da_btree_hash(ds, level, &ent->hashval); 363eec0482eSDarrick J. Wong if (error) 364eec0482eSDarrick J. Wong goto out; 365eec0482eSDarrick J. Wong 366eec0482eSDarrick J. Wong /* Find the attr entry's location. */ 367eec0482eSDarrick J. Wong bp = blk->bp; 368eec0482eSDarrick J. Wong hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 369eec0482eSDarrick J. Wong nameidx = be16_to_cpu(ent->nameidx); 370eec0482eSDarrick J. Wong if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 371eec0482eSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 372eec0482eSDarrick J. Wong goto out; 373eec0482eSDarrick J. Wong } 374eec0482eSDarrick J. Wong 375eec0482eSDarrick J. Wong /* Retrieve the entry and check it. */ 376eec0482eSDarrick J. Wong hash = be32_to_cpu(ent->hashval); 377eec0482eSDarrick J. Wong badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 378eec0482eSDarrick J. Wong XFS_ATTR_INCOMPLETE); 379eec0482eSDarrick J. Wong if ((ent->flags & badflags) != 0) 380eec0482eSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 381eec0482eSDarrick J. Wong if (ent->flags & XFS_ATTR_LOCAL) { 382eec0482eSDarrick J. Wong lentry = (struct xfs_attr_leaf_name_local *) 383eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 384eec0482eSDarrick J. Wong if (lentry->namelen <= 0) { 385eec0482eSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 386eec0482eSDarrick J. Wong goto out; 387eec0482eSDarrick J. Wong } 388eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 389eec0482eSDarrick J. Wong } else { 390eec0482eSDarrick J. Wong rentry = (struct xfs_attr_leaf_name_remote *) 391eec0482eSDarrick J. Wong (((char *)bp->b_addr) + nameidx); 392eec0482eSDarrick J. Wong if (rentry->namelen <= 0) { 393eec0482eSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 394eec0482eSDarrick J. Wong goto out; 395eec0482eSDarrick J. Wong } 396eec0482eSDarrick J. Wong calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 397eec0482eSDarrick J. Wong } 398eec0482eSDarrick J. Wong if (calc_hash != hash) 399eec0482eSDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 400eec0482eSDarrick J. Wong 401eec0482eSDarrick J. Wong out: 402eec0482eSDarrick J. Wong return error; 403eec0482eSDarrick J. Wong } 404eec0482eSDarrick J. Wong 405eec0482eSDarrick J. Wong /* Scrub the extended attribute metadata. */ 406eec0482eSDarrick J. Wong int 407eec0482eSDarrick J. Wong xfs_scrub_xattr( 408eec0482eSDarrick J. Wong struct xfs_scrub_context *sc) 409eec0482eSDarrick J. Wong { 41088aa5de4SChristoph Hellwig struct xfs_scrub_xattr sx; 411eec0482eSDarrick J. Wong struct attrlist_cursor_kern cursor = { 0 }; 41213791d3bSDarrick J. Wong xfs_dablk_t last_checked = -1U; 413eec0482eSDarrick J. Wong int error = 0; 414eec0482eSDarrick J. Wong 415eec0482eSDarrick J. Wong if (!xfs_inode_hasattr(sc->ip)) 416eec0482eSDarrick J. Wong return -ENOENT; 417eec0482eSDarrick J. Wong 418eec0482eSDarrick J. Wong memset(&sx, 0, sizeof(sx)); 419eec0482eSDarrick J. Wong /* Check attribute tree structure */ 42013791d3bSDarrick J. Wong error = xfs_scrub_da_btree(sc, XFS_ATTR_FORK, xfs_scrub_xattr_rec, 42113791d3bSDarrick J. Wong &last_checked); 422eec0482eSDarrick J. Wong if (error) 423eec0482eSDarrick J. Wong goto out; 424eec0482eSDarrick J. Wong 425eec0482eSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 426eec0482eSDarrick J. Wong goto out; 427eec0482eSDarrick J. Wong 428eec0482eSDarrick J. Wong /* Check that every attr key can also be looked up by hash. */ 429eec0482eSDarrick J. Wong sx.context.dp = sc->ip; 430eec0482eSDarrick J. Wong sx.context.cursor = &cursor; 431eec0482eSDarrick J. Wong sx.context.resynch = 1; 432eec0482eSDarrick J. Wong sx.context.put_listent = xfs_scrub_xattr_listent; 433eec0482eSDarrick J. Wong sx.context.tp = sc->tp; 434eec0482eSDarrick J. Wong sx.context.flags = ATTR_INCOMPLETE; 435eec0482eSDarrick J. Wong sx.sc = sc; 436eec0482eSDarrick J. Wong 437eec0482eSDarrick J. Wong /* 438eec0482eSDarrick J. Wong * Look up every xattr in this file by name. 439eec0482eSDarrick J. Wong * 440eec0482eSDarrick J. Wong * Use the backend implementation of xfs_attr_list to call 441eec0482eSDarrick J. Wong * xfs_scrub_xattr_listent on every attribute key in this inode. 442eec0482eSDarrick J. Wong * In other words, we use the same iterator/callback mechanism 443eec0482eSDarrick J. Wong * that listattr uses to scrub extended attributes, though in our 444eec0482eSDarrick J. Wong * _listent function, we check the value of the attribute. 445eec0482eSDarrick J. Wong * 446eec0482eSDarrick J. Wong * The VFS only locks i_rwsem when modifying attrs, so keep all 447eec0482eSDarrick J. Wong * three locks held because that's the only way to ensure we're 448eec0482eSDarrick J. Wong * the only thread poking into the da btree. We traverse the da 449eec0482eSDarrick J. Wong * btree while holding a leaf buffer locked for the xattr name 450eec0482eSDarrick J. Wong * iteration, which doesn't really follow the usual buffer 451eec0482eSDarrick J. Wong * locking order. 452eec0482eSDarrick J. Wong */ 453eec0482eSDarrick J. Wong error = xfs_attr_list_int_ilocked(&sx.context); 454eec0482eSDarrick J. Wong if (!xfs_scrub_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 455eec0482eSDarrick J. Wong goto out; 456eec0482eSDarrick J. Wong out: 457eec0482eSDarrick J. Wong return error; 458eec0482eSDarrick J. Wong } 459