18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Oracle. All Rights Reserved. 48c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "xfs.h" 78c2ecf20Sopenharmony_ci#include "xfs_fs.h" 88c2ecf20Sopenharmony_ci#include "xfs_shared.h" 98c2ecf20Sopenharmony_ci#include "xfs_format.h" 108c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h" 118c2ecf20Sopenharmony_ci#include "xfs_mount.h" 128c2ecf20Sopenharmony_ci#include "xfs_log_format.h" 138c2ecf20Sopenharmony_ci#include "xfs_inode.h" 148c2ecf20Sopenharmony_ci#include "xfs_da_format.h" 158c2ecf20Sopenharmony_ci#include "xfs_da_btree.h" 168c2ecf20Sopenharmony_ci#include "xfs_attr.h" 178c2ecf20Sopenharmony_ci#include "xfs_attr_leaf.h" 188c2ecf20Sopenharmony_ci#include "scrub/scrub.h" 198c2ecf20Sopenharmony_ci#include "scrub/common.h" 208c2ecf20Sopenharmony_ci#include "scrub/dabtree.h" 218c2ecf20Sopenharmony_ci#include "scrub/attr.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Allocate enough memory to hold an attr value and attr block bitmaps, 258c2ecf20Sopenharmony_ci * reallocating the buffer if necessary. Buffer contents are not preserved 268c2ecf20Sopenharmony_ci * across a reallocation. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ciint 298c2ecf20Sopenharmony_cixchk_setup_xattr_buf( 308c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 318c2ecf20Sopenharmony_ci size_t value_size, 328c2ecf20Sopenharmony_ci xfs_km_flags_t flags) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci size_t sz; 358c2ecf20Sopenharmony_ci struct xchk_xattr_buf *ab = sc->buf; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* 388c2ecf20Sopenharmony_ci * We need enough space to read an xattr value from the file or enough 398c2ecf20Sopenharmony_ci * space to hold three copies of the xattr free space bitmap. We don't 408c2ecf20Sopenharmony_ci * need the buffer space for both purposes at the same time. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci sz = 3 * sizeof(long) * BITS_TO_LONGS(sc->mp->m_attr_geo->blksize); 438c2ecf20Sopenharmony_ci sz = max_t(size_t, sz, value_size); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * If there's already a buffer, figure out if we need to reallocate it 478c2ecf20Sopenharmony_ci * to accommodate a larger size. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci if (ab) { 508c2ecf20Sopenharmony_ci if (sz <= ab->sz) 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci kmem_free(ab); 538c2ecf20Sopenharmony_ci sc->buf = NULL; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * Don't zero the buffer upon allocation to avoid runtime overhead. 588c2ecf20Sopenharmony_ci * All users must be careful never to read uninitialized contents. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci ab = kmem_alloc_large(sizeof(*ab) + sz, flags); 618c2ecf20Sopenharmony_ci if (!ab) 628c2ecf20Sopenharmony_ci return -ENOMEM; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ab->sz = sz; 658c2ecf20Sopenharmony_ci sc->buf = ab; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Set us up to scrub an inode's extended attributes. */ 708c2ecf20Sopenharmony_ciint 718c2ecf20Sopenharmony_cixchk_setup_xattr( 728c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 738c2ecf20Sopenharmony_ci struct xfs_inode *ip) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int error; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * We failed to get memory while checking attrs, so this time try to 798c2ecf20Sopenharmony_ci * get all the memory we're ever going to need. Allocate the buffer 808c2ecf20Sopenharmony_ci * without the inode lock held, which means we can sleep. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci if (sc->flags & XCHK_TRY_HARDER) { 838c2ecf20Sopenharmony_ci error = xchk_setup_xattr_buf(sc, XATTR_SIZE_MAX, 0); 848c2ecf20Sopenharmony_ci if (error) 858c2ecf20Sopenharmony_ci return error; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return xchk_setup_inode_contents(sc, ip, 0); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* Extended Attributes */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct xchk_xattr { 948c2ecf20Sopenharmony_ci struct xfs_attr_list_context context; 958c2ecf20Sopenharmony_ci struct xfs_scrub *sc; 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * Check that an extended attribute key can be looked up by hash. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * We use the XFS attribute list iterator (i.e. xfs_attr_list_ilocked) 1028c2ecf20Sopenharmony_ci * to call this function for every attribute key in an inode. Once 1038c2ecf20Sopenharmony_ci * we're here, we load the attribute value to see if any errors happen, 1048c2ecf20Sopenharmony_ci * or if we get more or less data than we expected. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic void 1078c2ecf20Sopenharmony_cixchk_xattr_listent( 1088c2ecf20Sopenharmony_ci struct xfs_attr_list_context *context, 1098c2ecf20Sopenharmony_ci int flags, 1108c2ecf20Sopenharmony_ci unsigned char *name, 1118c2ecf20Sopenharmony_ci int namelen, 1128c2ecf20Sopenharmony_ci int valuelen) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct xchk_xattr *sx; 1158c2ecf20Sopenharmony_ci struct xfs_da_args args = { NULL }; 1168c2ecf20Sopenharmony_ci int error = 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci sx = container_of(context, struct xchk_xattr, context); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (xchk_should_terminate(sx->sc, &error)) { 1218c2ecf20Sopenharmony_ci context->seen_enough = error; 1228c2ecf20Sopenharmony_ci return; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (flags & XFS_ATTR_INCOMPLETE) { 1268c2ecf20Sopenharmony_ci /* Incomplete attr key, just mark the inode for preening. */ 1278c2ecf20Sopenharmony_ci xchk_ino_set_preen(sx->sc, context->dp->i_ino); 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Does this name make sense? */ 1328c2ecf20Sopenharmony_ci if (!xfs_attr_namecheck(name, namelen)) { 1338c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno); 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * Try to allocate enough memory to extrat the attr value. If that 1398c2ecf20Sopenharmony_ci * doesn't work, we overload the seen_enough variable to convey 1408c2ecf20Sopenharmony_ci * the error message back to the main scrub function. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci error = xchk_setup_xattr_buf(sx->sc, valuelen, KM_MAYFAIL); 1438c2ecf20Sopenharmony_ci if (error == -ENOMEM) 1448c2ecf20Sopenharmony_ci error = -EDEADLOCK; 1458c2ecf20Sopenharmony_ci if (error) { 1468c2ecf20Sopenharmony_ci context->seen_enough = error; 1478c2ecf20Sopenharmony_ci return; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci args.op_flags = XFS_DA_OP_NOTIME; 1518c2ecf20Sopenharmony_ci args.attr_filter = flags & XFS_ATTR_NSP_ONDISK_MASK; 1528c2ecf20Sopenharmony_ci args.geo = context->dp->i_mount->m_attr_geo; 1538c2ecf20Sopenharmony_ci args.whichfork = XFS_ATTR_FORK; 1548c2ecf20Sopenharmony_ci args.dp = context->dp; 1558c2ecf20Sopenharmony_ci args.name = name; 1568c2ecf20Sopenharmony_ci args.namelen = namelen; 1578c2ecf20Sopenharmony_ci args.hashval = xfs_da_hashname(args.name, args.namelen); 1588c2ecf20Sopenharmony_ci args.trans = context->tp; 1598c2ecf20Sopenharmony_ci args.value = xchk_xattr_valuebuf(sx->sc); 1608c2ecf20Sopenharmony_ci args.valuelen = valuelen; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci error = xfs_attr_get_ilocked(&args); 1638c2ecf20Sopenharmony_ci /* ENODATA means the hash lookup failed and the attr is bad */ 1648c2ecf20Sopenharmony_ci if (error == -ENODATA) 1658c2ecf20Sopenharmony_ci error = -EFSCORRUPTED; 1668c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno, 1678c2ecf20Sopenharmony_ci &error)) 1688c2ecf20Sopenharmony_ci goto fail_xref; 1698c2ecf20Sopenharmony_ci if (args.valuelen != valuelen) 1708c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, 1718c2ecf20Sopenharmony_ci args.blkno); 1728c2ecf20Sopenharmony_cifail_xref: 1738c2ecf20Sopenharmony_ci if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1748c2ecf20Sopenharmony_ci context->seen_enough = 1; 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * Mark a range [start, start+len) in this map. Returns true if the 1808c2ecf20Sopenharmony_ci * region was free, and false if there's a conflict or a problem. 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Within a char, the lowest bit of the char represents the byte with 1838c2ecf20Sopenharmony_ci * the smallest address 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ciSTATIC bool 1868c2ecf20Sopenharmony_cixchk_xattr_set_map( 1878c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 1888c2ecf20Sopenharmony_ci unsigned long *map, 1898c2ecf20Sopenharmony_ci unsigned int start, 1908c2ecf20Sopenharmony_ci unsigned int len) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci unsigned int mapsize = sc->mp->m_attr_geo->blksize; 1938c2ecf20Sopenharmony_ci bool ret = true; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (start >= mapsize) 1968c2ecf20Sopenharmony_ci return false; 1978c2ecf20Sopenharmony_ci if (start + len > mapsize) { 1988c2ecf20Sopenharmony_ci len = mapsize - start; 1998c2ecf20Sopenharmony_ci ret = false; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (find_next_bit(map, mapsize, start) < start + len) 2038c2ecf20Sopenharmony_ci ret = false; 2048c2ecf20Sopenharmony_ci bitmap_set(map, start, len); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * Check the leaf freemap from the usage bitmap. Returns false if the 2118c2ecf20Sopenharmony_ci * attr freemap has problems or points to used space. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ciSTATIC bool 2148c2ecf20Sopenharmony_cixchk_xattr_check_freemap( 2158c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 2168c2ecf20Sopenharmony_ci unsigned long *map, 2178c2ecf20Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leafhdr) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci unsigned long *freemap = xchk_xattr_freemap(sc); 2208c2ecf20Sopenharmony_ci unsigned long *dstmap = xchk_xattr_dstmap(sc); 2218c2ecf20Sopenharmony_ci unsigned int mapsize = sc->mp->m_attr_geo->blksize; 2228c2ecf20Sopenharmony_ci int i; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Construct bitmap of freemap contents. */ 2258c2ecf20Sopenharmony_ci bitmap_zero(freemap, mapsize); 2268c2ecf20Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 2278c2ecf20Sopenharmony_ci if (!xchk_xattr_set_map(sc, freemap, 2288c2ecf20Sopenharmony_ci leafhdr->freemap[i].base, 2298c2ecf20Sopenharmony_ci leafhdr->freemap[i].size)) 2308c2ecf20Sopenharmony_ci return false; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Look for bits that are set in freemap and are marked in use. */ 2348c2ecf20Sopenharmony_ci return bitmap_and(dstmap, freemap, map, mapsize) == 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * Check this leaf entry's relations to everything else. 2398c2ecf20Sopenharmony_ci * Returns the number of bytes used for the name/value data. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ciSTATIC void 2428c2ecf20Sopenharmony_cixchk_xattr_entry( 2438c2ecf20Sopenharmony_ci struct xchk_da_btree *ds, 2448c2ecf20Sopenharmony_ci int level, 2458c2ecf20Sopenharmony_ci char *buf_end, 2468c2ecf20Sopenharmony_ci struct xfs_attr_leafblock *leaf, 2478c2ecf20Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leafhdr, 2488c2ecf20Sopenharmony_ci struct xfs_attr_leaf_entry *ent, 2498c2ecf20Sopenharmony_ci int idx, 2508c2ecf20Sopenharmony_ci unsigned int *usedbytes, 2518c2ecf20Sopenharmony_ci __u32 *last_hashval) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 2548c2ecf20Sopenharmony_ci unsigned long *usedmap = xchk_xattr_usedmap(ds->sc); 2558c2ecf20Sopenharmony_ci char *name_end; 2568c2ecf20Sopenharmony_ci struct xfs_attr_leaf_name_local *lentry; 2578c2ecf20Sopenharmony_ci struct xfs_attr_leaf_name_remote *rentry; 2588c2ecf20Sopenharmony_ci unsigned int nameidx; 2598c2ecf20Sopenharmony_ci unsigned int namesize; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ent->pad2 != 0) 2628c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Hash values in order? */ 2658c2ecf20Sopenharmony_ci if (be32_to_cpu(ent->hashval) < *last_hashval) 2668c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2678c2ecf20Sopenharmony_ci *last_hashval = be32_to_cpu(ent->hashval); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci nameidx = be16_to_cpu(ent->nameidx); 2708c2ecf20Sopenharmony_ci if (nameidx < leafhdr->firstused || 2718c2ecf20Sopenharmony_ci nameidx >= mp->m_attr_geo->blksize) { 2728c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Check the name information. */ 2778c2ecf20Sopenharmony_ci if (ent->flags & XFS_ATTR_LOCAL) { 2788c2ecf20Sopenharmony_ci lentry = xfs_attr3_leaf_name_local(leaf, idx); 2798c2ecf20Sopenharmony_ci namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 2808c2ecf20Sopenharmony_ci be16_to_cpu(lentry->valuelen)); 2818c2ecf20Sopenharmony_ci name_end = (char *)lentry + namesize; 2828c2ecf20Sopenharmony_ci if (lentry->namelen == 0) 2838c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci rentry = xfs_attr3_leaf_name_remote(leaf, idx); 2868c2ecf20Sopenharmony_ci namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 2878c2ecf20Sopenharmony_ci name_end = (char *)rentry + namesize; 2888c2ecf20Sopenharmony_ci if (rentry->namelen == 0 || rentry->valueblk == 0) 2898c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci if (name_end > buf_end) 2928c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize)) 2958c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 2968c2ecf20Sopenharmony_ci if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 2978c2ecf20Sopenharmony_ci *usedbytes += namesize; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* Scrub an attribute leaf. */ 3018c2ecf20Sopenharmony_ciSTATIC int 3028c2ecf20Sopenharmony_cixchk_xattr_block( 3038c2ecf20Sopenharmony_ci struct xchk_da_btree *ds, 3048c2ecf20Sopenharmony_ci int level) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct xfs_attr3_icleaf_hdr leafhdr; 3078c2ecf20Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 3088c2ecf20Sopenharmony_ci struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 3098c2ecf20Sopenharmony_ci struct xfs_buf *bp = blk->bp; 3108c2ecf20Sopenharmony_ci xfs_dablk_t *last_checked = ds->private; 3118c2ecf20Sopenharmony_ci struct xfs_attr_leafblock *leaf = bp->b_addr; 3128c2ecf20Sopenharmony_ci struct xfs_attr_leaf_entry *ent; 3138c2ecf20Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 3148c2ecf20Sopenharmony_ci unsigned long *usedmap; 3158c2ecf20Sopenharmony_ci char *buf_end; 3168c2ecf20Sopenharmony_ci size_t off; 3178c2ecf20Sopenharmony_ci __u32 last_hashval = 0; 3188c2ecf20Sopenharmony_ci unsigned int usedbytes = 0; 3198c2ecf20Sopenharmony_ci unsigned int hdrsize; 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci int error; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (*last_checked == blk->blkno) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Allocate memory for block usage checking. */ 3278c2ecf20Sopenharmony_ci error = xchk_setup_xattr_buf(ds->sc, 0, KM_MAYFAIL); 3288c2ecf20Sopenharmony_ci if (error == -ENOMEM) 3298c2ecf20Sopenharmony_ci return -EDEADLOCK; 3308c2ecf20Sopenharmony_ci if (error) 3318c2ecf20Sopenharmony_ci return error; 3328c2ecf20Sopenharmony_ci usedmap = xchk_xattr_usedmap(ds->sc); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci *last_checked = blk->blkno; 3358c2ecf20Sopenharmony_ci bitmap_zero(usedmap, mp->m_attr_geo->blksize); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Check all the padding. */ 3388c2ecf20Sopenharmony_ci if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) { 3398c2ecf20Sopenharmony_ci struct xfs_attr3_leafblock *leaf = bp->b_addr; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 || 3428c2ecf20Sopenharmony_ci leaf->hdr.info.hdr.pad != 0) 3438c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0) 3468c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Check the leaf header */ 3508c2ecf20Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 3518c2ecf20Sopenharmony_ci hdrsize = xfs_attr3_leaf_hdr_size(leaf); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (leafhdr.usedbytes > mp->m_attr_geo->blksize) 3548c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3558c2ecf20Sopenharmony_ci if (leafhdr.firstused > mp->m_attr_geo->blksize) 3568c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3578c2ecf20Sopenharmony_ci if (leafhdr.firstused < hdrsize) 3588c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3598c2ecf20Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize)) 3608c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 3638c2ecf20Sopenharmony_ci goto out; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 3668c2ecf20Sopenharmony_ci if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused) 3678c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 3708c2ecf20Sopenharmony_ci for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) { 3718c2ecf20Sopenharmony_ci /* Mark the leaf entry itself. */ 3728c2ecf20Sopenharmony_ci off = (char *)ent - (char *)leaf; 3738c2ecf20Sopenharmony_ci if (!xchk_xattr_set_map(ds->sc, usedmap, off, 3748c2ecf20Sopenharmony_ci sizeof(xfs_attr_leaf_entry_t))) { 3758c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3768c2ecf20Sopenharmony_ci goto out; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Check the entry and nameval. */ 3808c2ecf20Sopenharmony_ci xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr, 3818c2ecf20Sopenharmony_ci ent, i, &usedbytes, &last_hashval); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 3848c2ecf20Sopenharmony_ci goto out; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr)) 3888c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (leafhdr.usedbytes != usedbytes) 3918c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciout: 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* Scrub a attribute btree record. */ 3988c2ecf20Sopenharmony_ciSTATIC int 3998c2ecf20Sopenharmony_cixchk_xattr_rec( 4008c2ecf20Sopenharmony_ci struct xchk_da_btree *ds, 4018c2ecf20Sopenharmony_ci int level) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct xfs_mount *mp = ds->state->mp; 4048c2ecf20Sopenharmony_ci struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 4058c2ecf20Sopenharmony_ci struct xfs_attr_leaf_name_local *lentry; 4068c2ecf20Sopenharmony_ci struct xfs_attr_leaf_name_remote *rentry; 4078c2ecf20Sopenharmony_ci struct xfs_buf *bp; 4088c2ecf20Sopenharmony_ci struct xfs_attr_leaf_entry *ent; 4098c2ecf20Sopenharmony_ci xfs_dahash_t calc_hash; 4108c2ecf20Sopenharmony_ci xfs_dahash_t hash; 4118c2ecf20Sopenharmony_ci int nameidx; 4128c2ecf20Sopenharmony_ci int hdrsize; 4138c2ecf20Sopenharmony_ci unsigned int badflags; 4148c2ecf20Sopenharmony_ci int error; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ent = xfs_attr3_leaf_entryp(blk->bp->b_addr) + blk->index; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Check the whole block, if necessary. */ 4218c2ecf20Sopenharmony_ci error = xchk_xattr_block(ds, level); 4228c2ecf20Sopenharmony_ci if (error) 4238c2ecf20Sopenharmony_ci goto out; 4248c2ecf20Sopenharmony_ci if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 4258c2ecf20Sopenharmony_ci goto out; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Check the hash of the entry. */ 4288c2ecf20Sopenharmony_ci error = xchk_da_btree_hash(ds, level, &ent->hashval); 4298c2ecf20Sopenharmony_ci if (error) 4308c2ecf20Sopenharmony_ci goto out; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Find the attr entry's location. */ 4338c2ecf20Sopenharmony_ci bp = blk->bp; 4348c2ecf20Sopenharmony_ci hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr); 4358c2ecf20Sopenharmony_ci nameidx = be16_to_cpu(ent->nameidx); 4368c2ecf20Sopenharmony_ci if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) { 4378c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 4388c2ecf20Sopenharmony_ci goto out; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Retrieve the entry and check it. */ 4428c2ecf20Sopenharmony_ci hash = be32_to_cpu(ent->hashval); 4438c2ecf20Sopenharmony_ci badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE | 4448c2ecf20Sopenharmony_ci XFS_ATTR_INCOMPLETE); 4458c2ecf20Sopenharmony_ci if ((ent->flags & badflags) != 0) 4468c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 4478c2ecf20Sopenharmony_ci if (ent->flags & XFS_ATTR_LOCAL) { 4488c2ecf20Sopenharmony_ci lentry = (struct xfs_attr_leaf_name_local *) 4498c2ecf20Sopenharmony_ci (((char *)bp->b_addr) + nameidx); 4508c2ecf20Sopenharmony_ci if (lentry->namelen <= 0) { 4518c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 4528c2ecf20Sopenharmony_ci goto out; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen); 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci rentry = (struct xfs_attr_leaf_name_remote *) 4578c2ecf20Sopenharmony_ci (((char *)bp->b_addr) + nameidx); 4588c2ecf20Sopenharmony_ci if (rentry->namelen <= 0) { 4598c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci calc_hash = xfs_da_hashname(rentry->name, rentry->namelen); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci if (calc_hash != hash) 4658c2ecf20Sopenharmony_ci xchk_da_set_corrupt(ds, level); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciout: 4688c2ecf20Sopenharmony_ci return error; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* Scrub the extended attribute metadata. */ 4728c2ecf20Sopenharmony_ciint 4738c2ecf20Sopenharmony_cixchk_xattr( 4748c2ecf20Sopenharmony_ci struct xfs_scrub *sc) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct xchk_xattr sx; 4778c2ecf20Sopenharmony_ci xfs_dablk_t last_checked = -1U; 4788c2ecf20Sopenharmony_ci int error = 0; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!xfs_inode_hasattr(sc->ip)) 4818c2ecf20Sopenharmony_ci return -ENOENT; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci memset(&sx, 0, sizeof(sx)); 4848c2ecf20Sopenharmony_ci /* Check attribute tree structure */ 4858c2ecf20Sopenharmony_ci error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec, 4868c2ecf20Sopenharmony_ci &last_checked); 4878c2ecf20Sopenharmony_ci if (error) 4888c2ecf20Sopenharmony_ci goto out; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 4918c2ecf20Sopenharmony_ci goto out; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Check that every attr key can also be looked up by hash. */ 4948c2ecf20Sopenharmony_ci sx.context.dp = sc->ip; 4958c2ecf20Sopenharmony_ci sx.context.resynch = 1; 4968c2ecf20Sopenharmony_ci sx.context.put_listent = xchk_xattr_listent; 4978c2ecf20Sopenharmony_ci sx.context.tp = sc->tp; 4988c2ecf20Sopenharmony_ci sx.context.allow_incomplete = true; 4998c2ecf20Sopenharmony_ci sx.sc = sc; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * Look up every xattr in this file by name. 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * Use the backend implementation of xfs_attr_list to call 5058c2ecf20Sopenharmony_ci * xchk_xattr_listent on every attribute key in this inode. 5068c2ecf20Sopenharmony_ci * In other words, we use the same iterator/callback mechanism 5078c2ecf20Sopenharmony_ci * that listattr uses to scrub extended attributes, though in our 5088c2ecf20Sopenharmony_ci * _listent function, we check the value of the attribute. 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * The VFS only locks i_rwsem when modifying attrs, so keep all 5118c2ecf20Sopenharmony_ci * three locks held because that's the only way to ensure we're 5128c2ecf20Sopenharmony_ci * the only thread poking into the da btree. We traverse the da 5138c2ecf20Sopenharmony_ci * btree while holding a leaf buffer locked for the xattr name 5148c2ecf20Sopenharmony_ci * iteration, which doesn't really follow the usual buffer 5158c2ecf20Sopenharmony_ci * locking order. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci error = xfs_attr_list_ilocked(&sx.context); 5188c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error)) 5198c2ecf20Sopenharmony_ci goto out; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Did our listent function try to return any errors? */ 5228c2ecf20Sopenharmony_ci if (sx.context.seen_enough < 0) 5238c2ecf20Sopenharmony_ci error = sx.context.seen_enough; 5248c2ecf20Sopenharmony_ciout: 5258c2ecf20Sopenharmony_ci return error; 5268c2ecf20Sopenharmony_ci} 527