162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2005 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2013 Red Hat, Inc. 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include "xfs.h" 862306a36Sopenharmony_ci#include "xfs_fs.h" 962306a36Sopenharmony_ci#include "xfs_shared.h" 1062306a36Sopenharmony_ci#include "xfs_format.h" 1162306a36Sopenharmony_ci#include "xfs_log_format.h" 1262306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1362306a36Sopenharmony_ci#include "xfs_mount.h" 1462306a36Sopenharmony_ci#include "xfs_da_format.h" 1562306a36Sopenharmony_ci#include "xfs_inode.h" 1662306a36Sopenharmony_ci#include "xfs_trans.h" 1762306a36Sopenharmony_ci#include "xfs_bmap.h" 1862306a36Sopenharmony_ci#include "xfs_da_btree.h" 1962306a36Sopenharmony_ci#include "xfs_attr.h" 2062306a36Sopenharmony_ci#include "xfs_attr_sf.h" 2162306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 2262306a36Sopenharmony_ci#include "xfs_error.h" 2362306a36Sopenharmony_ci#include "xfs_trace.h" 2462306a36Sopenharmony_ci#include "xfs_dir2.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciSTATIC int 2762306a36Sopenharmony_cixfs_attr_shortform_compare(const void *a, const void *b) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci xfs_attr_sf_sort_t *sa, *sb; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci sa = (xfs_attr_sf_sort_t *)a; 3262306a36Sopenharmony_ci sb = (xfs_attr_sf_sort_t *)b; 3362306a36Sopenharmony_ci if (sa->hash < sb->hash) { 3462306a36Sopenharmony_ci return -1; 3562306a36Sopenharmony_ci } else if (sa->hash > sb->hash) { 3662306a36Sopenharmony_ci return 1; 3762306a36Sopenharmony_ci } else { 3862306a36Sopenharmony_ci return sa->entno - sb->entno; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define XFS_ISRESET_CURSOR(cursor) \ 4362306a36Sopenharmony_ci (!((cursor)->initted) && !((cursor)->hashval) && \ 4462306a36Sopenharmony_ci !((cursor)->blkno) && !((cursor)->offset)) 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Copy out entries of shortform attribute lists for attr_list(). 4762306a36Sopenharmony_ci * Shortform attribute lists are not stored in hashval sorted order. 4862306a36Sopenharmony_ci * If the output buffer is not large enough to hold them all, then 4962306a36Sopenharmony_ci * we have to calculate each entries' hashvalue and sort them before 5062306a36Sopenharmony_ci * we can begin returning them to the user. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic int 5362306a36Sopenharmony_cixfs_attr_shortform_list( 5462306a36Sopenharmony_ci struct xfs_attr_list_context *context) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct xfs_attrlist_cursor_kern *cursor = &context->cursor; 5762306a36Sopenharmony_ci struct xfs_inode *dp = context->dp; 5862306a36Sopenharmony_ci struct xfs_attr_sf_sort *sbuf, *sbp; 5962306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 6062306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 6162306a36Sopenharmony_ci int sbsize, nsbuf, count, i; 6262306a36Sopenharmony_ci int error = 0; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data; 6562306a36Sopenharmony_ci ASSERT(sf != NULL); 6662306a36Sopenharmony_ci if (!sf->hdr.count) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci trace_xfs_attr_list_sf(context); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * If the buffer is large enough and the cursor is at the start, 7362306a36Sopenharmony_ci * do not bother with sorting since we will return everything in 7462306a36Sopenharmony_ci * one buffer and another call using the cursor won't need to be 7562306a36Sopenharmony_ci * made. 7662306a36Sopenharmony_ci * Note the generous fudge factor of 16 overhead bytes per entry. 7762306a36Sopenharmony_ci * If bufsize is zero then put_listent must be a search function 7862306a36Sopenharmony_ci * and can just scan through what we have. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci if (context->bufsize == 0 || 8162306a36Sopenharmony_ci (XFS_ISRESET_CURSOR(cursor) && 8262306a36Sopenharmony_ci (dp->i_af.if_bytes + sf->hdr.count * 16) < context->bufsize)) { 8362306a36Sopenharmony_ci for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) { 8462306a36Sopenharmony_ci if (XFS_IS_CORRUPT(context->dp->i_mount, 8562306a36Sopenharmony_ci !xfs_attr_namecheck(sfe->nameval, 8662306a36Sopenharmony_ci sfe->namelen))) 8762306a36Sopenharmony_ci return -EFSCORRUPTED; 8862306a36Sopenharmony_ci context->put_listent(context, 8962306a36Sopenharmony_ci sfe->flags, 9062306a36Sopenharmony_ci sfe->nameval, 9162306a36Sopenharmony_ci (int)sfe->namelen, 9262306a36Sopenharmony_ci (int)sfe->valuelen); 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Either search callback finished early or 9562306a36Sopenharmony_ci * didn't fit it all in the buffer after all. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci if (context->seen_enough) 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci sfe = xfs_attr_sf_nextentry(sfe); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci trace_xfs_attr_list_sf_all(context); 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* do no more for a search callback */ 10662306a36Sopenharmony_ci if (context->bufsize == 0) 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * It didn't all fit, so we have to sort everything on hashval. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci sbsize = sf->hdr.count * sizeof(*sbuf); 11362306a36Sopenharmony_ci sbp = sbuf = kmem_alloc(sbsize, KM_NOFS); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * Scan the attribute list for the rest of the entries, storing 11762306a36Sopenharmony_ci * the relevant info from only those that match into a buffer. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci nsbuf = 0; 12062306a36Sopenharmony_ci for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) { 12162306a36Sopenharmony_ci if (unlikely( 12262306a36Sopenharmony_ci ((char *)sfe < (char *)sf) || 12362306a36Sopenharmony_ci ((char *)sfe >= ((char *)sf + dp->i_af.if_bytes)))) { 12462306a36Sopenharmony_ci XFS_CORRUPTION_ERROR("xfs_attr_shortform_list", 12562306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, 12662306a36Sopenharmony_ci context->dp->i_mount, sfe, 12762306a36Sopenharmony_ci sizeof(*sfe)); 12862306a36Sopenharmony_ci kmem_free(sbuf); 12962306a36Sopenharmony_ci return -EFSCORRUPTED; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci sbp->entno = i; 13362306a36Sopenharmony_ci sbp->hash = xfs_da_hashname(sfe->nameval, sfe->namelen); 13462306a36Sopenharmony_ci sbp->name = sfe->nameval; 13562306a36Sopenharmony_ci sbp->namelen = sfe->namelen; 13662306a36Sopenharmony_ci /* These are bytes, and both on-disk, don't endian-flip */ 13762306a36Sopenharmony_ci sbp->valuelen = sfe->valuelen; 13862306a36Sopenharmony_ci sbp->flags = sfe->flags; 13962306a36Sopenharmony_ci sfe = xfs_attr_sf_nextentry(sfe); 14062306a36Sopenharmony_ci sbp++; 14162306a36Sopenharmony_ci nsbuf++; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Sort the entries on hash then entno. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_attr_shortform_compare); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Re-find our place IN THE SORTED LIST. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci count = 0; 15362306a36Sopenharmony_ci cursor->initted = 1; 15462306a36Sopenharmony_ci cursor->blkno = 0; 15562306a36Sopenharmony_ci for (sbp = sbuf, i = 0; i < nsbuf; i++, sbp++) { 15662306a36Sopenharmony_ci if (sbp->hash == cursor->hashval) { 15762306a36Sopenharmony_ci if (cursor->offset == count) { 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci count++; 16162306a36Sopenharmony_ci } else if (sbp->hash > cursor->hashval) { 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (i == nsbuf) 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Loop putting entries into the user buffer. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci for ( ; i < nsbuf; i++, sbp++) { 17262306a36Sopenharmony_ci if (cursor->hashval != sbp->hash) { 17362306a36Sopenharmony_ci cursor->hashval = sbp->hash; 17462306a36Sopenharmony_ci cursor->offset = 0; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(context->dp->i_mount, 17762306a36Sopenharmony_ci !xfs_attr_namecheck(sbp->name, 17862306a36Sopenharmony_ci sbp->namelen))) { 17962306a36Sopenharmony_ci error = -EFSCORRUPTED; 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci context->put_listent(context, 18362306a36Sopenharmony_ci sbp->flags, 18462306a36Sopenharmony_ci sbp->name, 18562306a36Sopenharmony_ci sbp->namelen, 18662306a36Sopenharmony_ci sbp->valuelen); 18762306a36Sopenharmony_ci if (context->seen_enough) 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci cursor->offset++; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ciout: 19262306a36Sopenharmony_ci kmem_free(sbuf); 19362306a36Sopenharmony_ci return error; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * We didn't find the block & hash mentioned in the cursor state, so 19862306a36Sopenharmony_ci * walk down the attr btree looking for the hash. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ciSTATIC int 20162306a36Sopenharmony_cixfs_attr_node_list_lookup( 20262306a36Sopenharmony_ci struct xfs_attr_list_context *context, 20362306a36Sopenharmony_ci struct xfs_attrlist_cursor_kern *cursor, 20462306a36Sopenharmony_ci struct xfs_buf **pbp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct xfs_da3_icnode_hdr nodehdr; 20762306a36Sopenharmony_ci struct xfs_da_intnode *node; 20862306a36Sopenharmony_ci struct xfs_da_node_entry *btree; 20962306a36Sopenharmony_ci struct xfs_inode *dp = context->dp; 21062306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 21162306a36Sopenharmony_ci struct xfs_trans *tp = context->tp; 21262306a36Sopenharmony_ci struct xfs_buf *bp; 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci int error = 0; 21562306a36Sopenharmony_ci unsigned int expected_level = 0; 21662306a36Sopenharmony_ci uint16_t magic; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci ASSERT(*pbp == NULL); 21962306a36Sopenharmony_ci cursor->blkno = 0; 22062306a36Sopenharmony_ci for (;;) { 22162306a36Sopenharmony_ci error = xfs_da3_node_read(tp, dp, cursor->blkno, &bp, 22262306a36Sopenharmony_ci XFS_ATTR_FORK); 22362306a36Sopenharmony_ci if (error) 22462306a36Sopenharmony_ci return error; 22562306a36Sopenharmony_ci node = bp->b_addr; 22662306a36Sopenharmony_ci magic = be16_to_cpu(node->hdr.info.magic); 22762306a36Sopenharmony_ci if (magic == XFS_ATTR_LEAF_MAGIC || 22862306a36Sopenharmony_ci magic == XFS_ATTR3_LEAF_MAGIC) 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci if (magic != XFS_DA_NODE_MAGIC && 23162306a36Sopenharmony_ci magic != XFS_DA3_NODE_MAGIC) { 23262306a36Sopenharmony_ci XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, 23362306a36Sopenharmony_ci node, sizeof(*node)); 23462306a36Sopenharmony_ci goto out_corruptbuf; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(mp, &nodehdr, node); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Tree taller than we can handle; bail out! */ 24062306a36Sopenharmony_ci if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) 24162306a36Sopenharmony_ci goto out_corruptbuf; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Check the level from the root node. */ 24462306a36Sopenharmony_ci if (cursor->blkno == 0) 24562306a36Sopenharmony_ci expected_level = nodehdr.level - 1; 24662306a36Sopenharmony_ci else if (expected_level != nodehdr.level) 24762306a36Sopenharmony_ci goto out_corruptbuf; 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci expected_level--; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci btree = nodehdr.btree; 25262306a36Sopenharmony_ci for (i = 0; i < nodehdr.count; btree++, i++) { 25362306a36Sopenharmony_ci if (cursor->hashval <= be32_to_cpu(btree->hashval)) { 25462306a36Sopenharmony_ci cursor->blkno = be32_to_cpu(btree->before); 25562306a36Sopenharmony_ci trace_xfs_attr_list_node_descend(context, 25662306a36Sopenharmony_ci btree); 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci xfs_trans_brelse(tp, bp); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (i == nodehdr.count) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* We can't point back to the root. */ 26662306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, cursor->blkno == 0)) 26762306a36Sopenharmony_ci return -EFSCORRUPTED; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (expected_level != 0) 27162306a36Sopenharmony_ci goto out_corruptbuf; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci *pbp = bp; 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciout_corruptbuf: 27762306a36Sopenharmony_ci xfs_buf_mark_corrupt(bp); 27862306a36Sopenharmony_ci xfs_trans_brelse(tp, bp); 27962306a36Sopenharmony_ci return -EFSCORRUPTED; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciSTATIC int 28362306a36Sopenharmony_cixfs_attr_node_list( 28462306a36Sopenharmony_ci struct xfs_attr_list_context *context) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct xfs_attrlist_cursor_kern *cursor = &context->cursor; 28762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr leafhdr; 28862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 28962306a36Sopenharmony_ci struct xfs_da_intnode *node; 29062306a36Sopenharmony_ci struct xfs_buf *bp; 29162306a36Sopenharmony_ci struct xfs_inode *dp = context->dp; 29262306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 29362306a36Sopenharmony_ci int error = 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci trace_xfs_attr_node_list(context); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci cursor->initted = 1; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * Do all sorts of validation on the passed-in cursor structure. 30162306a36Sopenharmony_ci * If anything is amiss, ignore the cursor and look up the hashval 30262306a36Sopenharmony_ci * starting from the btree root. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci bp = NULL; 30562306a36Sopenharmony_ci if (cursor->blkno > 0) { 30662306a36Sopenharmony_ci error = xfs_da3_node_read(context->tp, dp, cursor->blkno, &bp, 30762306a36Sopenharmony_ci XFS_ATTR_FORK); 30862306a36Sopenharmony_ci if ((error != 0) && (error != -EFSCORRUPTED)) 30962306a36Sopenharmony_ci return error; 31062306a36Sopenharmony_ci if (bp) { 31162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci node = bp->b_addr; 31462306a36Sopenharmony_ci switch (be16_to_cpu(node->hdr.info.magic)) { 31562306a36Sopenharmony_ci case XFS_DA_NODE_MAGIC: 31662306a36Sopenharmony_ci case XFS_DA3_NODE_MAGIC: 31762306a36Sopenharmony_ci trace_xfs_attr_list_wrong_blk(context); 31862306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 31962306a36Sopenharmony_ci bp = NULL; 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci case XFS_ATTR_LEAF_MAGIC: 32262306a36Sopenharmony_ci case XFS_ATTR3_LEAF_MAGIC: 32362306a36Sopenharmony_ci leaf = bp->b_addr; 32462306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, 32562306a36Sopenharmony_ci &leafhdr, leaf); 32662306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 32762306a36Sopenharmony_ci if (cursor->hashval > be32_to_cpu( 32862306a36Sopenharmony_ci entries[leafhdr.count - 1].hashval)) { 32962306a36Sopenharmony_ci trace_xfs_attr_list_wrong_blk(context); 33062306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 33162306a36Sopenharmony_ci bp = NULL; 33262306a36Sopenharmony_ci } else if (cursor->hashval <= be32_to_cpu( 33362306a36Sopenharmony_ci entries[0].hashval)) { 33462306a36Sopenharmony_ci trace_xfs_attr_list_wrong_blk(context); 33562306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 33662306a36Sopenharmony_ci bp = NULL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci default: 34062306a36Sopenharmony_ci trace_xfs_attr_list_wrong_blk(context); 34162306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 34262306a36Sopenharmony_ci bp = NULL; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * We did not find what we expected given the cursor's contents, 34962306a36Sopenharmony_ci * so we start from the top and work down based on the hash value. 35062306a36Sopenharmony_ci * Note that start of node block is same as start of leaf block. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci if (bp == NULL) { 35362306a36Sopenharmony_ci error = xfs_attr_node_list_lookup(context, cursor, &bp); 35462306a36Sopenharmony_ci if (error || !bp) 35562306a36Sopenharmony_ci return error; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci ASSERT(bp != NULL); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Roll upward through the blocks, processing each leaf block in 36162306a36Sopenharmony_ci * order. As long as there is space in the result buffer, keep 36262306a36Sopenharmony_ci * adding the information. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci for (;;) { 36562306a36Sopenharmony_ci leaf = bp->b_addr; 36662306a36Sopenharmony_ci error = xfs_attr3_leaf_list_int(bp, context); 36762306a36Sopenharmony_ci if (error) 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 37062306a36Sopenharmony_ci if (context->seen_enough || leafhdr.forw == 0) 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci cursor->blkno = leafhdr.forw; 37362306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 37462306a36Sopenharmony_ci error = xfs_attr3_leaf_read(context->tp, dp, cursor->blkno, 37562306a36Sopenharmony_ci &bp); 37662306a36Sopenharmony_ci if (error) 37762306a36Sopenharmony_ci return error; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 38062306a36Sopenharmony_ci return error; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * Copy out attribute list entries for attr_list(), for leaf attribute lists. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ciint 38762306a36Sopenharmony_cixfs_attr3_leaf_list_int( 38862306a36Sopenharmony_ci struct xfs_buf *bp, 38962306a36Sopenharmony_ci struct xfs_attr_list_context *context) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct xfs_attrlist_cursor_kern *cursor = &context->cursor; 39262306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 39362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 39462306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 39562306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 39662306a36Sopenharmony_ci int i; 39762306a36Sopenharmony_ci struct xfs_mount *mp = context->dp->i_mount; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci trace_xfs_attr_list_leaf(context); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci leaf = bp->b_addr; 40262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf); 40362306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci cursor->initted = 1; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * Re-find our place in the leaf block if this is a new syscall. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci if (context->resynch) { 41162306a36Sopenharmony_ci entry = &entries[0]; 41262306a36Sopenharmony_ci for (i = 0; i < ichdr.count; entry++, i++) { 41362306a36Sopenharmony_ci if (be32_to_cpu(entry->hashval) == cursor->hashval) { 41462306a36Sopenharmony_ci if (cursor->offset == context->dupcnt) { 41562306a36Sopenharmony_ci context->dupcnt = 0; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci context->dupcnt++; 41962306a36Sopenharmony_ci } else if (be32_to_cpu(entry->hashval) > 42062306a36Sopenharmony_ci cursor->hashval) { 42162306a36Sopenharmony_ci context->dupcnt = 0; 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci if (i == ichdr.count) { 42662306a36Sopenharmony_ci trace_xfs_attr_list_notfound(context); 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci entry = &entries[0]; 43162306a36Sopenharmony_ci i = 0; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci context->resynch = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * We have found our place, start copying out the new attributes. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci for (; i < ichdr.count; entry++, i++) { 43962306a36Sopenharmony_ci char *name; 44062306a36Sopenharmony_ci int namelen, valuelen; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (be32_to_cpu(entry->hashval) != cursor->hashval) { 44362306a36Sopenharmony_ci cursor->hashval = be32_to_cpu(entry->hashval); 44462306a36Sopenharmony_ci cursor->offset = 0; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if ((entry->flags & XFS_ATTR_INCOMPLETE) && 44862306a36Sopenharmony_ci !context->allow_incomplete) 44962306a36Sopenharmony_ci continue; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_LOCAL) { 45262306a36Sopenharmony_ci xfs_attr_leaf_name_local_t *name_loc; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, i); 45562306a36Sopenharmony_ci name = name_loc->nameval; 45662306a36Sopenharmony_ci namelen = name_loc->namelen; 45762306a36Sopenharmony_ci valuelen = be16_to_cpu(name_loc->valuelen); 45862306a36Sopenharmony_ci } else { 45962306a36Sopenharmony_ci xfs_attr_leaf_name_remote_t *name_rmt; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, i); 46262306a36Sopenharmony_ci name = name_rmt->name; 46362306a36Sopenharmony_ci namelen = name_rmt->namelen; 46462306a36Sopenharmony_ci valuelen = be32_to_cpu(name_rmt->valuelen); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (XFS_IS_CORRUPT(context->dp->i_mount, 46862306a36Sopenharmony_ci !xfs_attr_namecheck(name, namelen))) 46962306a36Sopenharmony_ci return -EFSCORRUPTED; 47062306a36Sopenharmony_ci context->put_listent(context, entry->flags, 47162306a36Sopenharmony_ci name, namelen, valuelen); 47262306a36Sopenharmony_ci if (context->seen_enough) 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci cursor->offset++; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci trace_xfs_attr_list_leaf_end(context); 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* 48162306a36Sopenharmony_ci * Copy out attribute entries for attr_list(), for leaf attribute lists. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ciSTATIC int 48462306a36Sopenharmony_cixfs_attr_leaf_list( 48562306a36Sopenharmony_ci struct xfs_attr_list_context *context) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct xfs_buf *bp; 48862306a36Sopenharmony_ci int error; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci trace_xfs_attr_leaf_list(context); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci context->cursor.blkno = 0; 49362306a36Sopenharmony_ci error = xfs_attr3_leaf_read(context->tp, context->dp, 0, &bp); 49462306a36Sopenharmony_ci if (error) 49562306a36Sopenharmony_ci return error; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci error = xfs_attr3_leaf_list_int(bp, context); 49862306a36Sopenharmony_ci xfs_trans_brelse(context->tp, bp); 49962306a36Sopenharmony_ci return error; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciint 50362306a36Sopenharmony_cixfs_attr_list_ilocked( 50462306a36Sopenharmony_ci struct xfs_attr_list_context *context) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct xfs_inode *dp = context->dp; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * Decide on what work routines to call based on the inode size. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (!xfs_inode_hasattr(dp)) 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) 51662306a36Sopenharmony_ci return xfs_attr_shortform_list(context); 51762306a36Sopenharmony_ci if (xfs_attr_is_leaf(dp)) 51862306a36Sopenharmony_ci return xfs_attr_leaf_list(context); 51962306a36Sopenharmony_ci return xfs_attr_node_list(context); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciint 52362306a36Sopenharmony_cixfs_attr_list( 52462306a36Sopenharmony_ci struct xfs_attr_list_context *context) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct xfs_inode *dp = context->dp; 52762306a36Sopenharmony_ci uint lock_mode; 52862306a36Sopenharmony_ci int error; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci XFS_STATS_INC(dp->i_mount, xs_attr_list); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (xfs_is_shutdown(dp->i_mount)) 53362306a36Sopenharmony_ci return -EIO; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci lock_mode = xfs_ilock_attr_map_shared(dp); 53662306a36Sopenharmony_ci error = xfs_attr_list_ilocked(context); 53762306a36Sopenharmony_ci xfs_iunlock(dp, lock_mode); 53862306a36Sopenharmony_ci return error; 53962306a36Sopenharmony_ci} 540