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_sb.h" 1462306a36Sopenharmony_ci#include "xfs_mount.h" 1562306a36Sopenharmony_ci#include "xfs_da_format.h" 1662306a36Sopenharmony_ci#include "xfs_da_btree.h" 1762306a36Sopenharmony_ci#include "xfs_inode.h" 1862306a36Sopenharmony_ci#include "xfs_trans.h" 1962306a36Sopenharmony_ci#include "xfs_bmap_btree.h" 2062306a36Sopenharmony_ci#include "xfs_bmap.h" 2162306a36Sopenharmony_ci#include "xfs_attr_sf.h" 2262306a36Sopenharmony_ci#include "xfs_attr.h" 2362306a36Sopenharmony_ci#include "xfs_attr_remote.h" 2462306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 2562306a36Sopenharmony_ci#include "xfs_error.h" 2662306a36Sopenharmony_ci#include "xfs_trace.h" 2762306a36Sopenharmony_ci#include "xfs_buf_item.h" 2862306a36Sopenharmony_ci#include "xfs_dir2.h" 2962306a36Sopenharmony_ci#include "xfs_log.h" 3062306a36Sopenharmony_ci#include "xfs_ag.h" 3162306a36Sopenharmony_ci#include "xfs_errortag.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * xfs_attr_leaf.c 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Routines to implement leaf blocks of attributes as Btrees of hashed names. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/*======================================================================== 4162306a36Sopenharmony_ci * Function prototypes for the kernel. 4262306a36Sopenharmony_ci *========================================================================*/ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Routines used for growing the Btree. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ciSTATIC int xfs_attr3_leaf_create(struct xfs_da_args *args, 4862306a36Sopenharmony_ci xfs_dablk_t which_block, struct xfs_buf **bpp); 4962306a36Sopenharmony_ciSTATIC int xfs_attr3_leaf_add_work(struct xfs_buf *leaf_buffer, 5062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr, 5162306a36Sopenharmony_ci struct xfs_da_args *args, int freemap_index); 5262306a36Sopenharmony_ciSTATIC void xfs_attr3_leaf_compact(struct xfs_da_args *args, 5362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr, 5462306a36Sopenharmony_ci struct xfs_buf *leaf_buffer); 5562306a36Sopenharmony_ciSTATIC void xfs_attr3_leaf_rebalance(xfs_da_state_t *state, 5662306a36Sopenharmony_ci xfs_da_state_blk_t *blk1, 5762306a36Sopenharmony_ci xfs_da_state_blk_t *blk2); 5862306a36Sopenharmony_ciSTATIC int xfs_attr3_leaf_figure_balance(xfs_da_state_t *state, 5962306a36Sopenharmony_ci xfs_da_state_blk_t *leaf_blk_1, 6062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr1, 6162306a36Sopenharmony_ci xfs_da_state_blk_t *leaf_blk_2, 6262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr2, 6362306a36Sopenharmony_ci int *number_entries_in_blk1, 6462306a36Sopenharmony_ci int *number_usedbytes_in_blk1); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Utility routines. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ciSTATIC void xfs_attr3_leaf_moveents(struct xfs_da_args *args, 7062306a36Sopenharmony_ci struct xfs_attr_leafblock *src_leaf, 7162306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *src_ichdr, int src_start, 7262306a36Sopenharmony_ci struct xfs_attr_leafblock *dst_leaf, 7362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *dst_ichdr, int dst_start, 7462306a36Sopenharmony_ci int move_count); 7562306a36Sopenharmony_ciSTATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * attr3 block 'firstused' conversion helpers. 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * firstused refers to the offset of the first used byte of the nameval region 8162306a36Sopenharmony_ci * of an attr leaf block. The region starts at the tail of the block and expands 8262306a36Sopenharmony_ci * backwards towards the middle. As such, firstused is initialized to the block 8362306a36Sopenharmony_ci * size for an empty leaf block and is reduced from there. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * The attr3 block size is pegged to the fsb size and the maximum fsb is 64k. 8662306a36Sopenharmony_ci * The in-core firstused field is 32-bit and thus supports the maximum fsb size. 8762306a36Sopenharmony_ci * The on-disk field is only 16-bit, however, and overflows at 64k. Since this 8862306a36Sopenharmony_ci * only occurs at exactly 64k, we use zero as a magic on-disk value to represent 8962306a36Sopenharmony_ci * the attr block size. The following helpers manage the conversion between the 9062306a36Sopenharmony_ci * in-core and on-disk formats. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void 9462306a36Sopenharmony_cixfs_attr3_leaf_firstused_from_disk( 9562306a36Sopenharmony_ci struct xfs_da_geometry *geo, 9662306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *to, 9762306a36Sopenharmony_ci struct xfs_attr_leafblock *from) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct xfs_attr3_leaf_hdr *hdr3; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) { 10262306a36Sopenharmony_ci hdr3 = (struct xfs_attr3_leaf_hdr *) from; 10362306a36Sopenharmony_ci to->firstused = be16_to_cpu(hdr3->firstused); 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci to->firstused = be16_to_cpu(from->hdr.firstused); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * Convert from the magic fsb size value to actual blocksize. This 11062306a36Sopenharmony_ci * should only occur for empty blocks when the block size overflows 11162306a36Sopenharmony_ci * 16-bits. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci if (to->firstused == XFS_ATTR3_LEAF_NULLOFF) { 11462306a36Sopenharmony_ci ASSERT(!to->count && !to->usedbytes); 11562306a36Sopenharmony_ci ASSERT(geo->blksize > USHRT_MAX); 11662306a36Sopenharmony_ci to->firstused = geo->blksize; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void 12162306a36Sopenharmony_cixfs_attr3_leaf_firstused_to_disk( 12262306a36Sopenharmony_ci struct xfs_da_geometry *geo, 12362306a36Sopenharmony_ci struct xfs_attr_leafblock *to, 12462306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *from) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct xfs_attr3_leaf_hdr *hdr3; 12762306a36Sopenharmony_ci uint32_t firstused; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* magic value should only be seen on disk */ 13062306a36Sopenharmony_ci ASSERT(from->firstused != XFS_ATTR3_LEAF_NULLOFF); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Scale down the 32-bit in-core firstused value to the 16-bit on-disk 13462306a36Sopenharmony_ci * value. This only overflows at the max supported value of 64k. Use the 13562306a36Sopenharmony_ci * magic on-disk value to represent block size in this case. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci firstused = from->firstused; 13862306a36Sopenharmony_ci if (firstused > USHRT_MAX) { 13962306a36Sopenharmony_ci ASSERT(from->firstused == geo->blksize); 14062306a36Sopenharmony_ci firstused = XFS_ATTR3_LEAF_NULLOFF; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (from->magic == XFS_ATTR3_LEAF_MAGIC) { 14462306a36Sopenharmony_ci hdr3 = (struct xfs_attr3_leaf_hdr *) to; 14562306a36Sopenharmony_ci hdr3->firstused = cpu_to_be16(firstused); 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci to->hdr.firstused = cpu_to_be16(firstused); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid 15262306a36Sopenharmony_cixfs_attr3_leaf_hdr_from_disk( 15362306a36Sopenharmony_ci struct xfs_da_geometry *geo, 15462306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *to, 15562306a36Sopenharmony_ci struct xfs_attr_leafblock *from) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) || 16062306a36Sopenharmony_ci from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) { 16362306a36Sopenharmony_ci struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)from; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci to->forw = be32_to_cpu(hdr3->info.hdr.forw); 16662306a36Sopenharmony_ci to->back = be32_to_cpu(hdr3->info.hdr.back); 16762306a36Sopenharmony_ci to->magic = be16_to_cpu(hdr3->info.hdr.magic); 16862306a36Sopenharmony_ci to->count = be16_to_cpu(hdr3->count); 16962306a36Sopenharmony_ci to->usedbytes = be16_to_cpu(hdr3->usedbytes); 17062306a36Sopenharmony_ci xfs_attr3_leaf_firstused_from_disk(geo, to, from); 17162306a36Sopenharmony_ci to->holes = hdr3->holes; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 17462306a36Sopenharmony_ci to->freemap[i].base = be16_to_cpu(hdr3->freemap[i].base); 17562306a36Sopenharmony_ci to->freemap[i].size = be16_to_cpu(hdr3->freemap[i].size); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci to->forw = be32_to_cpu(from->hdr.info.forw); 18062306a36Sopenharmony_ci to->back = be32_to_cpu(from->hdr.info.back); 18162306a36Sopenharmony_ci to->magic = be16_to_cpu(from->hdr.info.magic); 18262306a36Sopenharmony_ci to->count = be16_to_cpu(from->hdr.count); 18362306a36Sopenharmony_ci to->usedbytes = be16_to_cpu(from->hdr.usedbytes); 18462306a36Sopenharmony_ci xfs_attr3_leaf_firstused_from_disk(geo, to, from); 18562306a36Sopenharmony_ci to->holes = from->hdr.holes; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 18862306a36Sopenharmony_ci to->freemap[i].base = be16_to_cpu(from->hdr.freemap[i].base); 18962306a36Sopenharmony_ci to->freemap[i].size = be16_to_cpu(from->hdr.freemap[i].size); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid 19462306a36Sopenharmony_cixfs_attr3_leaf_hdr_to_disk( 19562306a36Sopenharmony_ci struct xfs_da_geometry *geo, 19662306a36Sopenharmony_ci struct xfs_attr_leafblock *to, 19762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *from) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ASSERT(from->magic == XFS_ATTR_LEAF_MAGIC || 20262306a36Sopenharmony_ci from->magic == XFS_ATTR3_LEAF_MAGIC); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (from->magic == XFS_ATTR3_LEAF_MAGIC) { 20562306a36Sopenharmony_ci struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)to; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci hdr3->info.hdr.forw = cpu_to_be32(from->forw); 20862306a36Sopenharmony_ci hdr3->info.hdr.back = cpu_to_be32(from->back); 20962306a36Sopenharmony_ci hdr3->info.hdr.magic = cpu_to_be16(from->magic); 21062306a36Sopenharmony_ci hdr3->count = cpu_to_be16(from->count); 21162306a36Sopenharmony_ci hdr3->usedbytes = cpu_to_be16(from->usedbytes); 21262306a36Sopenharmony_ci xfs_attr3_leaf_firstused_to_disk(geo, to, from); 21362306a36Sopenharmony_ci hdr3->holes = from->holes; 21462306a36Sopenharmony_ci hdr3->pad1 = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 21762306a36Sopenharmony_ci hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base); 21862306a36Sopenharmony_ci hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci to->hdr.info.forw = cpu_to_be32(from->forw); 22362306a36Sopenharmony_ci to->hdr.info.back = cpu_to_be32(from->back); 22462306a36Sopenharmony_ci to->hdr.info.magic = cpu_to_be16(from->magic); 22562306a36Sopenharmony_ci to->hdr.count = cpu_to_be16(from->count); 22662306a36Sopenharmony_ci to->hdr.usedbytes = cpu_to_be16(from->usedbytes); 22762306a36Sopenharmony_ci xfs_attr3_leaf_firstused_to_disk(geo, to, from); 22862306a36Sopenharmony_ci to->hdr.holes = from->holes; 22962306a36Sopenharmony_ci to->hdr.pad1 = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 23262306a36Sopenharmony_ci to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base); 23362306a36Sopenharmony_ci to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic xfs_failaddr_t 23862306a36Sopenharmony_cixfs_attr3_leaf_verify_entry( 23962306a36Sopenharmony_ci struct xfs_mount *mp, 24062306a36Sopenharmony_ci char *buf_end, 24162306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf, 24262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leafhdr, 24362306a36Sopenharmony_ci struct xfs_attr_leaf_entry *ent, 24462306a36Sopenharmony_ci int idx, 24562306a36Sopenharmony_ci __u32 *last_hashval) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *lentry; 24862306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *rentry; 24962306a36Sopenharmony_ci char *name_end; 25062306a36Sopenharmony_ci unsigned int nameidx; 25162306a36Sopenharmony_ci unsigned int namesize; 25262306a36Sopenharmony_ci __u32 hashval; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* hash order check */ 25562306a36Sopenharmony_ci hashval = be32_to_cpu(ent->hashval); 25662306a36Sopenharmony_ci if (hashval < *last_hashval) 25762306a36Sopenharmony_ci return __this_address; 25862306a36Sopenharmony_ci *last_hashval = hashval; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci nameidx = be16_to_cpu(ent->nameidx); 26162306a36Sopenharmony_ci if (nameidx < leafhdr->firstused || nameidx >= mp->m_attr_geo->blksize) 26262306a36Sopenharmony_ci return __this_address; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* 26562306a36Sopenharmony_ci * Check the name information. The namelen fields are u8 so we can't 26662306a36Sopenharmony_ci * possibly exceed the maximum name length of 255 bytes. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (ent->flags & XFS_ATTR_LOCAL) { 26962306a36Sopenharmony_ci lentry = xfs_attr3_leaf_name_local(leaf, idx); 27062306a36Sopenharmony_ci namesize = xfs_attr_leaf_entsize_local(lentry->namelen, 27162306a36Sopenharmony_ci be16_to_cpu(lentry->valuelen)); 27262306a36Sopenharmony_ci name_end = (char *)lentry + namesize; 27362306a36Sopenharmony_ci if (lentry->namelen == 0) 27462306a36Sopenharmony_ci return __this_address; 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci rentry = xfs_attr3_leaf_name_remote(leaf, idx); 27762306a36Sopenharmony_ci namesize = xfs_attr_leaf_entsize_remote(rentry->namelen); 27862306a36Sopenharmony_ci name_end = (char *)rentry + namesize; 27962306a36Sopenharmony_ci if (rentry->namelen == 0) 28062306a36Sopenharmony_ci return __this_address; 28162306a36Sopenharmony_ci if (!(ent->flags & XFS_ATTR_INCOMPLETE) && 28262306a36Sopenharmony_ci rentry->valueblk == 0) 28362306a36Sopenharmony_ci return __this_address; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (name_end > buf_end) 28762306a36Sopenharmony_ci return __this_address; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return NULL; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * Validate an attribute leaf block. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * Empty leaf blocks can occur under the following circumstances: 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * 1. setxattr adds a new extended attribute to a file; 29862306a36Sopenharmony_ci * 2. The file has zero existing attributes; 29962306a36Sopenharmony_ci * 3. The attribute is too large to fit in the attribute fork; 30062306a36Sopenharmony_ci * 4. The attribute is small enough to fit in a leaf block; 30162306a36Sopenharmony_ci * 5. A log flush occurs after committing the transaction that creates 30262306a36Sopenharmony_ci * the (empty) leaf block; and 30362306a36Sopenharmony_ci * 6. The filesystem goes down after the log flush but before the new 30462306a36Sopenharmony_ci * attribute can be committed to the leaf block. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Hence we need to ensure that we don't fail the validation purely 30762306a36Sopenharmony_ci * because the leaf is empty. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic xfs_failaddr_t 31062306a36Sopenharmony_cixfs_attr3_leaf_verify( 31162306a36Sopenharmony_ci struct xfs_buf *bp) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 31462306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 31562306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf = bp->b_addr; 31662306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 31762306a36Sopenharmony_ci struct xfs_attr_leaf_entry *ent; 31862306a36Sopenharmony_ci char *buf_end; 31962306a36Sopenharmony_ci uint32_t end; /* must be 32bit - see below */ 32062306a36Sopenharmony_ci __u32 last_hashval = 0; 32162306a36Sopenharmony_ci int i; 32262306a36Sopenharmony_ci xfs_failaddr_t fa; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci fa = xfs_da3_blkinfo_verify(bp, bp->b_addr); 32762306a36Sopenharmony_ci if (fa) 32862306a36Sopenharmony_ci return fa; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * firstused is the block offset of the first name info structure. 33262306a36Sopenharmony_ci * Make sure it doesn't go off the block or crash into the header. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci if (ichdr.firstused > mp->m_attr_geo->blksize) 33562306a36Sopenharmony_ci return __this_address; 33662306a36Sopenharmony_ci if (ichdr.firstused < xfs_attr3_leaf_hdr_size(leaf)) 33762306a36Sopenharmony_ci return __this_address; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Make sure the entries array doesn't crash into the name info. */ 34062306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(bp->b_addr); 34162306a36Sopenharmony_ci if ((char *)&entries[ichdr.count] > 34262306a36Sopenharmony_ci (char *)bp->b_addr + ichdr.firstused) 34362306a36Sopenharmony_ci return __this_address; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * NOTE: This verifier historically failed empty leaf buffers because 34762306a36Sopenharmony_ci * we expect the fork to be in another format. Empty attr fork format 34862306a36Sopenharmony_ci * conversions are possible during xattr set, however, and format 34962306a36Sopenharmony_ci * conversion is not atomic with the xattr set that triggers it. We 35062306a36Sopenharmony_ci * cannot assume leaf blocks are non-empty until that is addressed. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize; 35362306a36Sopenharmony_ci for (i = 0, ent = entries; i < ichdr.count; ent++, i++) { 35462306a36Sopenharmony_ci fa = xfs_attr3_leaf_verify_entry(mp, buf_end, leaf, &ichdr, 35562306a36Sopenharmony_ci ent, i, &last_hashval); 35662306a36Sopenharmony_ci if (fa) 35762306a36Sopenharmony_ci return fa; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * Quickly check the freemap information. Attribute data has to be 36262306a36Sopenharmony_ci * aligned to 4-byte boundaries, and likewise for the free space. 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Note that for 64k block size filesystems, the freemap entries cannot 36562306a36Sopenharmony_ci * overflow as they are only be16 fields. However, when checking end 36662306a36Sopenharmony_ci * pointer of the freemap, we have to be careful to detect overflows and 36762306a36Sopenharmony_ci * so use uint32_t for those checks. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 37062306a36Sopenharmony_ci if (ichdr.freemap[i].base > mp->m_attr_geo->blksize) 37162306a36Sopenharmony_ci return __this_address; 37262306a36Sopenharmony_ci if (ichdr.freemap[i].base & 0x3) 37362306a36Sopenharmony_ci return __this_address; 37462306a36Sopenharmony_ci if (ichdr.freemap[i].size > mp->m_attr_geo->blksize) 37562306a36Sopenharmony_ci return __this_address; 37662306a36Sopenharmony_ci if (ichdr.freemap[i].size & 0x3) 37762306a36Sopenharmony_ci return __this_address; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* be care of 16 bit overflows here */ 38062306a36Sopenharmony_ci end = (uint32_t)ichdr.freemap[i].base + ichdr.freemap[i].size; 38162306a36Sopenharmony_ci if (end < ichdr.freemap[i].base) 38262306a36Sopenharmony_ci return __this_address; 38362306a36Sopenharmony_ci if (end > mp->m_attr_geo->blksize) 38462306a36Sopenharmony_ci return __this_address; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void 39162306a36Sopenharmony_cixfs_attr3_leaf_write_verify( 39262306a36Sopenharmony_ci struct xfs_buf *bp) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 39562306a36Sopenharmony_ci struct xfs_buf_log_item *bip = bp->b_log_item; 39662306a36Sopenharmony_ci struct xfs_attr3_leaf_hdr *hdr3 = bp->b_addr; 39762306a36Sopenharmony_ci xfs_failaddr_t fa; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci fa = xfs_attr3_leaf_verify(bp); 40062306a36Sopenharmony_ci if (fa) { 40162306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSCORRUPTED, fa); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!xfs_has_crc(mp)) 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (bip) 40962306a36Sopenharmony_ci hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci xfs_buf_update_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* 41562306a36Sopenharmony_ci * leaf/node format detection on trees is sketchy, so a node read can be done on 41662306a36Sopenharmony_ci * leaf level blocks when detection identifies the tree as a node format tree 41762306a36Sopenharmony_ci * incorrectly. In this case, we need to swap the verifier to match the correct 41862306a36Sopenharmony_ci * format of the block being read. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_cistatic void 42162306a36Sopenharmony_cixfs_attr3_leaf_read_verify( 42262306a36Sopenharmony_ci struct xfs_buf *bp) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 42562306a36Sopenharmony_ci xfs_failaddr_t fa; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (xfs_has_crc(mp) && 42862306a36Sopenharmony_ci !xfs_buf_verify_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF)) 42962306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSBADCRC, __this_address); 43062306a36Sopenharmony_ci else { 43162306a36Sopenharmony_ci fa = xfs_attr3_leaf_verify(bp); 43262306a36Sopenharmony_ci if (fa) 43362306a36Sopenharmony_ci xfs_verifier_error(bp, -EFSCORRUPTED, fa); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ciconst struct xfs_buf_ops xfs_attr3_leaf_buf_ops = { 43862306a36Sopenharmony_ci .name = "xfs_attr3_leaf", 43962306a36Sopenharmony_ci .magic16 = { cpu_to_be16(XFS_ATTR_LEAF_MAGIC), 44062306a36Sopenharmony_ci cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) }, 44162306a36Sopenharmony_ci .verify_read = xfs_attr3_leaf_read_verify, 44262306a36Sopenharmony_ci .verify_write = xfs_attr3_leaf_write_verify, 44362306a36Sopenharmony_ci .verify_struct = xfs_attr3_leaf_verify, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciint 44762306a36Sopenharmony_cixfs_attr3_leaf_read( 44862306a36Sopenharmony_ci struct xfs_trans *tp, 44962306a36Sopenharmony_ci struct xfs_inode *dp, 45062306a36Sopenharmony_ci xfs_dablk_t bno, 45162306a36Sopenharmony_ci struct xfs_buf **bpp) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci int err; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = xfs_da_read_buf(tp, dp, bno, 0, bpp, XFS_ATTR_FORK, 45662306a36Sopenharmony_ci &xfs_attr3_leaf_buf_ops); 45762306a36Sopenharmony_ci if (!err && tp && *bpp) 45862306a36Sopenharmony_ci xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF); 45962306a36Sopenharmony_ci return err; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/*======================================================================== 46362306a36Sopenharmony_ci * Namespace helper routines 46462306a36Sopenharmony_ci *========================================================================*/ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* 46762306a36Sopenharmony_ci * If we are in log recovery, then we want the lookup to ignore the INCOMPLETE 46862306a36Sopenharmony_ci * flag on disk - if there's an incomplete attr then recovery needs to tear it 46962306a36Sopenharmony_ci * down. If there's no incomplete attr, then recovery needs to tear that attr 47062306a36Sopenharmony_ci * down to replace it with the attr that has been logged. In this case, the 47162306a36Sopenharmony_ci * INCOMPLETE flag will not be set in attr->attr_filter, but rather 47262306a36Sopenharmony_ci * XFS_DA_OP_RECOVERY will be set in args->op_flags. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistatic bool 47562306a36Sopenharmony_cixfs_attr_match( 47662306a36Sopenharmony_ci struct xfs_da_args *args, 47762306a36Sopenharmony_ci uint8_t namelen, 47862306a36Sopenharmony_ci unsigned char *name, 47962306a36Sopenharmony_ci int flags) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (args->namelen != namelen) 48362306a36Sopenharmony_ci return false; 48462306a36Sopenharmony_ci if (memcmp(args->name, name, namelen) != 0) 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Recovery ignores the INCOMPLETE flag. */ 48862306a36Sopenharmony_ci if ((args->op_flags & XFS_DA_OP_RECOVERY) && 48962306a36Sopenharmony_ci args->attr_filter == (flags & XFS_ATTR_NSP_ONDISK_MASK)) 49062306a36Sopenharmony_ci return true; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* All remaining matches need to be filtered by INCOMPLETE state. */ 49362306a36Sopenharmony_ci if (args->attr_filter != 49462306a36Sopenharmony_ci (flags & (XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE))) 49562306a36Sopenharmony_ci return false; 49662306a36Sopenharmony_ci return true; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int 50062306a36Sopenharmony_cixfs_attr_copy_value( 50162306a36Sopenharmony_ci struct xfs_da_args *args, 50262306a36Sopenharmony_ci unsigned char *value, 50362306a36Sopenharmony_ci int valuelen) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * No copy if all we have to do is get the length 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci if (!args->valuelen) { 50962306a36Sopenharmony_ci args->valuelen = valuelen; 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * No copy if the length of the existing buffer is too small 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci if (args->valuelen < valuelen) { 51762306a36Sopenharmony_ci args->valuelen = valuelen; 51862306a36Sopenharmony_ci return -ERANGE; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!args->value) { 52262306a36Sopenharmony_ci args->value = kvmalloc(valuelen, GFP_KERNEL | __GFP_NOLOCKDEP); 52362306a36Sopenharmony_ci if (!args->value) 52462306a36Sopenharmony_ci return -ENOMEM; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci args->valuelen = valuelen; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* remote block xattr requires IO for copy-in */ 52962306a36Sopenharmony_ci if (args->rmtblkno) 53062306a36Sopenharmony_ci return xfs_attr_rmtval_get(args); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * This is to prevent a GCC warning because the remote xattr case 53462306a36Sopenharmony_ci * doesn't have a value to pass in. In that case, we never reach here, 53562306a36Sopenharmony_ci * but GCC can't work that out and so throws a "passing NULL to 53662306a36Sopenharmony_ci * memcpy" warning. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (!value) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci memcpy(args->value, value, valuelen); 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/*======================================================================== 54562306a36Sopenharmony_ci * External routines when attribute fork size < XFS_LITINO(mp). 54662306a36Sopenharmony_ci *========================================================================*/ 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* 54962306a36Sopenharmony_ci * Query whether the total requested number of attr fork bytes of extended 55062306a36Sopenharmony_ci * attribute space will be able to fit inline. 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * Returns zero if not, else the i_forkoff fork offset to be used in the 55362306a36Sopenharmony_ci * literal area for attribute data once the new bytes have been added. 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * i_forkoff must be 8 byte aligned, hence is stored as a >>3 value; 55662306a36Sopenharmony_ci * special case for dev/uuid inodes, they have fixed size data forks. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ciint 55962306a36Sopenharmony_cixfs_attr_shortform_bytesfit( 56062306a36Sopenharmony_ci struct xfs_inode *dp, 56162306a36Sopenharmony_ci int bytes) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 56462306a36Sopenharmony_ci int64_t dsize; 56562306a36Sopenharmony_ci int minforkoff; 56662306a36Sopenharmony_ci int maxforkoff; 56762306a36Sopenharmony_ci int offset; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * Check if the new size could fit at all first: 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci if (bytes > XFS_LITINO(mp)) 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* rounded down */ 57662306a36Sopenharmony_ci offset = (XFS_LITINO(mp) - bytes) >> 3; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (dp->i_df.if_format == XFS_DINODE_FMT_DEV) { 57962306a36Sopenharmony_ci minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; 58062306a36Sopenharmony_ci return (offset >= minforkoff) ? minforkoff : 0; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * If the requested numbers of bytes is smaller or equal to the 58562306a36Sopenharmony_ci * current attribute fork size we can always proceed. 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * Note that if_bytes in the data fork might actually be larger than 58862306a36Sopenharmony_ci * the current data fork size is due to delalloc extents. In that 58962306a36Sopenharmony_ci * case either the extent count will go down when they are converted 59062306a36Sopenharmony_ci * to real extents, or the delalloc conversion will take care of the 59162306a36Sopenharmony_ci * literal area rebalancing. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci if (bytes <= xfs_inode_attr_fork_size(dp)) 59462306a36Sopenharmony_ci return dp->i_forkoff; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* 59762306a36Sopenharmony_ci * For attr2 we can try to move the forkoff if there is space in the 59862306a36Sopenharmony_ci * literal area, but for the old format we are done if there is no 59962306a36Sopenharmony_ci * space in the fixed attribute fork. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci if (!xfs_has_attr2(mp)) 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dsize = dp->i_df.if_bytes; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci switch (dp->i_df.if_format) { 60762306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * If there is no attr fork and the data fork is extents, 61062306a36Sopenharmony_ci * determine if creating the default attr fork will result 61162306a36Sopenharmony_ci * in the extents form migrating to btree. If so, the 61262306a36Sopenharmony_ci * minimum offset only needs to be the space required for 61362306a36Sopenharmony_ci * the btree root. 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci if (!dp->i_forkoff && dp->i_df.if_bytes > 61662306a36Sopenharmony_ci xfs_default_attroffset(dp)) 61762306a36Sopenharmony_ci dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * If we have a data btree then keep forkoff if we have one, 62262306a36Sopenharmony_ci * otherwise we are adding a new attr, so then we set 62362306a36Sopenharmony_ci * minforkoff to where the btree root can finish so we have 62462306a36Sopenharmony_ci * plenty of room for attrs 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci if (dp->i_forkoff) { 62762306a36Sopenharmony_ci if (offset < dp->i_forkoff) 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci return dp->i_forkoff; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci dsize = XFS_BMAP_BROOT_SPACE(mp, dp->i_df.if_broot); 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * A data fork btree root must have space for at least 63762306a36Sopenharmony_ci * MINDBTPTRS key/ptr pairs if the data fork is small or empty. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci minforkoff = max_t(int64_t, dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS)); 64062306a36Sopenharmony_ci minforkoff = roundup(minforkoff, 8) >> 3; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* attr fork btree root can have at least this many key/ptr pairs */ 64362306a36Sopenharmony_ci maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS); 64462306a36Sopenharmony_ci maxforkoff = maxforkoff >> 3; /* rounded down */ 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (offset >= maxforkoff) 64762306a36Sopenharmony_ci return maxforkoff; 64862306a36Sopenharmony_ci if (offset >= minforkoff) 64962306a36Sopenharmony_ci return offset; 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* 65462306a36Sopenharmony_ci * Switch on the ATTR2 superblock bit (implies also FEATURES2) unless: 65562306a36Sopenharmony_ci * - noattr2 mount option is set, 65662306a36Sopenharmony_ci * - on-disk version bit says it is already set, or 65762306a36Sopenharmony_ci * - the attr2 mount option is not set to enable automatic upgrade from attr1. 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ciSTATIC void 66062306a36Sopenharmony_cixfs_sbversion_add_attr2( 66162306a36Sopenharmony_ci struct xfs_mount *mp, 66262306a36Sopenharmony_ci struct xfs_trans *tp) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci if (xfs_has_noattr2(mp)) 66562306a36Sopenharmony_ci return; 66662306a36Sopenharmony_ci if (mp->m_sb.sb_features2 & XFS_SB_VERSION2_ATTR2BIT) 66762306a36Sopenharmony_ci return; 66862306a36Sopenharmony_ci if (!xfs_has_attr2(mp)) 66962306a36Sopenharmony_ci return; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci spin_lock(&mp->m_sb_lock); 67262306a36Sopenharmony_ci xfs_add_attr2(mp); 67362306a36Sopenharmony_ci spin_unlock(&mp->m_sb_lock); 67462306a36Sopenharmony_ci xfs_log_sb(tp); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/* 67862306a36Sopenharmony_ci * Create the initial contents of a shortform attribute list. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_civoid 68162306a36Sopenharmony_cixfs_attr_shortform_create( 68262306a36Sopenharmony_ci struct xfs_da_args *args) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 68562306a36Sopenharmony_ci struct xfs_ifork *ifp = &dp->i_af; 68662306a36Sopenharmony_ci struct xfs_attr_sf_hdr *hdr; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci trace_xfs_attr_sf_create(args); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci ASSERT(ifp->if_bytes == 0); 69162306a36Sopenharmony_ci if (ifp->if_format == XFS_DINODE_FMT_EXTENTS) 69262306a36Sopenharmony_ci ifp->if_format = XFS_DINODE_FMT_LOCAL; 69362306a36Sopenharmony_ci xfs_idata_realloc(dp, sizeof(*hdr), XFS_ATTR_FORK); 69462306a36Sopenharmony_ci hdr = (struct xfs_attr_sf_hdr *)ifp->if_u1.if_data; 69562306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 69662306a36Sopenharmony_ci hdr->totsize = cpu_to_be16(sizeof(*hdr)); 69762306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/* 70162306a36Sopenharmony_ci * Return -EEXIST if attr is found, or -ENOATTR if not 70262306a36Sopenharmony_ci * args: args containing attribute name and namelen 70362306a36Sopenharmony_ci * sfep: If not null, pointer will be set to the last attr entry found on 70462306a36Sopenharmony_ci -EEXIST. On -ENOATTR pointer is left at the last entry in the list 70562306a36Sopenharmony_ci * basep: If not null, pointer is set to the byte offset of the entry in the 70662306a36Sopenharmony_ci * list on -EEXIST. On -ENOATTR, pointer is left at the byte offset of 70762306a36Sopenharmony_ci * the last entry in the list 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ciint 71062306a36Sopenharmony_cixfs_attr_sf_findname( 71162306a36Sopenharmony_ci struct xfs_da_args *args, 71262306a36Sopenharmony_ci struct xfs_attr_sf_entry **sfep, 71362306a36Sopenharmony_ci unsigned int *basep) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 71662306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 71762306a36Sopenharmony_ci unsigned int base = sizeof(struct xfs_attr_sf_hdr); 71862306a36Sopenharmony_ci int size = 0; 71962306a36Sopenharmony_ci int end; 72062306a36Sopenharmony_ci int i; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data; 72362306a36Sopenharmony_ci sfe = &sf->list[0]; 72462306a36Sopenharmony_ci end = sf->hdr.count; 72562306a36Sopenharmony_ci for (i = 0; i < end; sfe = xfs_attr_sf_nextentry(sfe), 72662306a36Sopenharmony_ci base += size, i++) { 72762306a36Sopenharmony_ci size = xfs_attr_sf_entsize(sfe); 72862306a36Sopenharmony_ci if (!xfs_attr_match(args, sfe->namelen, sfe->nameval, 72962306a36Sopenharmony_ci sfe->flags)) 73062306a36Sopenharmony_ci continue; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (sfep != NULL) 73562306a36Sopenharmony_ci *sfep = sfe; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (basep != NULL) 73862306a36Sopenharmony_ci *basep = base; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (i == end) 74162306a36Sopenharmony_ci return -ENOATTR; 74262306a36Sopenharmony_ci return -EEXIST; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* 74662306a36Sopenharmony_ci * Add a name/value pair to the shortform attribute list. 74762306a36Sopenharmony_ci * Overflow from the inode has already been checked for. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_civoid 75062306a36Sopenharmony_cixfs_attr_shortform_add( 75162306a36Sopenharmony_ci struct xfs_da_args *args, 75262306a36Sopenharmony_ci int forkoff) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 75562306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 75662306a36Sopenharmony_ci int offset, size; 75762306a36Sopenharmony_ci struct xfs_mount *mp; 75862306a36Sopenharmony_ci struct xfs_inode *dp; 75962306a36Sopenharmony_ci struct xfs_ifork *ifp; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci trace_xfs_attr_sf_add(args); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci dp = args->dp; 76462306a36Sopenharmony_ci mp = dp->i_mount; 76562306a36Sopenharmony_ci dp->i_forkoff = forkoff; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ifp = &dp->i_af; 76862306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 76962306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; 77062306a36Sopenharmony_ci if (xfs_attr_sf_findname(args, &sfe, NULL) == -EEXIST) 77162306a36Sopenharmony_ci ASSERT(0); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci offset = (char *)sfe - (char *)sf; 77462306a36Sopenharmony_ci size = xfs_attr_sf_entsize_byname(args->namelen, args->valuelen); 77562306a36Sopenharmony_ci xfs_idata_realloc(dp, size, XFS_ATTR_FORK); 77662306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; 77762306a36Sopenharmony_ci sfe = (struct xfs_attr_sf_entry *)((char *)sf + offset); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci sfe->namelen = args->namelen; 78062306a36Sopenharmony_ci sfe->valuelen = args->valuelen; 78162306a36Sopenharmony_ci sfe->flags = args->attr_filter; 78262306a36Sopenharmony_ci memcpy(sfe->nameval, args->name, args->namelen); 78362306a36Sopenharmony_ci memcpy(&sfe->nameval[args->namelen], args->value, args->valuelen); 78462306a36Sopenharmony_ci sf->hdr.count++; 78562306a36Sopenharmony_ci be16_add_cpu(&sf->hdr.totsize, size); 78662306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci xfs_sbversion_add_attr2(mp, args->trans); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* 79262306a36Sopenharmony_ci * After the last attribute is removed revert to original inode format, 79362306a36Sopenharmony_ci * making all literal area available to the data fork once more. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_civoid 79662306a36Sopenharmony_cixfs_attr_fork_remove( 79762306a36Sopenharmony_ci struct xfs_inode *ip, 79862306a36Sopenharmony_ci struct xfs_trans *tp) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci ASSERT(ip->i_af.if_nextents == 0); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci xfs_ifork_zap_attr(ip); 80362306a36Sopenharmony_ci ip->i_forkoff = 0; 80462306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/* 80862306a36Sopenharmony_ci * Remove an attribute from the shortform attribute list structure. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ciint 81162306a36Sopenharmony_cixfs_attr_sf_removename( 81262306a36Sopenharmony_ci struct xfs_da_args *args) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 81562306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 81662306a36Sopenharmony_ci int size = 0, end, totsize; 81762306a36Sopenharmony_ci unsigned int base; 81862306a36Sopenharmony_ci struct xfs_mount *mp; 81962306a36Sopenharmony_ci struct xfs_inode *dp; 82062306a36Sopenharmony_ci int error; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci trace_xfs_attr_sf_remove(args); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci dp = args->dp; 82562306a36Sopenharmony_ci mp = dp->i_mount; 82662306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci error = xfs_attr_sf_findname(args, &sfe, &base); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* 83162306a36Sopenharmony_ci * If we are recovering an operation, finding nothing to 83262306a36Sopenharmony_ci * remove is not an error - it just means there was nothing 83362306a36Sopenharmony_ci * to clean up. 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci if (error == -ENOATTR && (args->op_flags & XFS_DA_OP_RECOVERY)) 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci if (error != -EEXIST) 83862306a36Sopenharmony_ci return error; 83962306a36Sopenharmony_ci size = xfs_attr_sf_entsize(sfe); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * Fix up the attribute fork data, covering the hole 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_ci end = base + size; 84562306a36Sopenharmony_ci totsize = be16_to_cpu(sf->hdr.totsize); 84662306a36Sopenharmony_ci if (end != totsize) 84762306a36Sopenharmony_ci memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end); 84862306a36Sopenharmony_ci sf->hdr.count--; 84962306a36Sopenharmony_ci be16_add_cpu(&sf->hdr.totsize, -size); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* 85262306a36Sopenharmony_ci * Fix up the start offset of the attribute fork 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_ci totsize -= size; 85562306a36Sopenharmony_ci if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp) && 85662306a36Sopenharmony_ci (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) && 85762306a36Sopenharmony_ci !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) { 85862306a36Sopenharmony_ci xfs_attr_fork_remove(dp, args->trans); 85962306a36Sopenharmony_ci } else { 86062306a36Sopenharmony_ci xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); 86162306a36Sopenharmony_ci dp->i_forkoff = xfs_attr_shortform_bytesfit(dp, totsize); 86262306a36Sopenharmony_ci ASSERT(dp->i_forkoff); 86362306a36Sopenharmony_ci ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || 86462306a36Sopenharmony_ci (args->op_flags & XFS_DA_OP_ADDNAME) || 86562306a36Sopenharmony_ci !xfs_has_attr2(mp) || 86662306a36Sopenharmony_ci dp->i_df.if_format == XFS_DINODE_FMT_BTREE); 86762306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, 86862306a36Sopenharmony_ci XFS_ILOG_CORE | XFS_ILOG_ADATA); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci xfs_sbversion_add_attr2(mp, args->trans); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return 0; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci/* 87762306a36Sopenharmony_ci * Look up a name in a shortform attribute list structure. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_ci/*ARGSUSED*/ 88062306a36Sopenharmony_ciint 88162306a36Sopenharmony_cixfs_attr_shortform_lookup(xfs_da_args_t *args) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 88462306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 88562306a36Sopenharmony_ci int i; 88662306a36Sopenharmony_ci struct xfs_ifork *ifp; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci trace_xfs_attr_sf_lookup(args); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci ifp = &args->dp->i_af; 89162306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 89262306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; 89362306a36Sopenharmony_ci sfe = &sf->list[0]; 89462306a36Sopenharmony_ci for (i = 0; i < sf->hdr.count; 89562306a36Sopenharmony_ci sfe = xfs_attr_sf_nextentry(sfe), i++) { 89662306a36Sopenharmony_ci if (xfs_attr_match(args, sfe->namelen, sfe->nameval, 89762306a36Sopenharmony_ci sfe->flags)) 89862306a36Sopenharmony_ci return -EEXIST; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci return -ENOATTR; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* 90462306a36Sopenharmony_ci * Retrieve the attribute value and length. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * If args->valuelen is zero, only the length needs to be returned. Unlike a 90762306a36Sopenharmony_ci * lookup, we only return an error if the attribute does not exist or we can't 90862306a36Sopenharmony_ci * retrieve the value. 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_ciint 91162306a36Sopenharmony_cixfs_attr_shortform_getvalue( 91262306a36Sopenharmony_ci struct xfs_da_args *args) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 91562306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 91662306a36Sopenharmony_ci int i; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL); 91962306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data; 92062306a36Sopenharmony_ci sfe = &sf->list[0]; 92162306a36Sopenharmony_ci for (i = 0; i < sf->hdr.count; 92262306a36Sopenharmony_ci sfe = xfs_attr_sf_nextentry(sfe), i++) { 92362306a36Sopenharmony_ci if (xfs_attr_match(args, sfe->namelen, sfe->nameval, 92462306a36Sopenharmony_ci sfe->flags)) 92562306a36Sopenharmony_ci return xfs_attr_copy_value(args, 92662306a36Sopenharmony_ci &sfe->nameval[args->namelen], sfe->valuelen); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci return -ENOATTR; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci/* Convert from using the shortform to the leaf format. */ 93262306a36Sopenharmony_ciint 93362306a36Sopenharmony_cixfs_attr_shortform_to_leaf( 93462306a36Sopenharmony_ci struct xfs_da_args *args) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct xfs_inode *dp; 93762306a36Sopenharmony_ci struct xfs_attr_shortform *sf; 93862306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfe; 93962306a36Sopenharmony_ci struct xfs_da_args nargs; 94062306a36Sopenharmony_ci char *tmpbuffer; 94162306a36Sopenharmony_ci int error, i, size; 94262306a36Sopenharmony_ci xfs_dablk_t blkno; 94362306a36Sopenharmony_ci struct xfs_buf *bp; 94462306a36Sopenharmony_ci struct xfs_ifork *ifp; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci trace_xfs_attr_sf_to_leaf(args); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci dp = args->dp; 94962306a36Sopenharmony_ci ifp = &dp->i_af; 95062306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; 95162306a36Sopenharmony_ci size = be16_to_cpu(sf->hdr.totsize); 95262306a36Sopenharmony_ci tmpbuffer = kmem_alloc(size, 0); 95362306a36Sopenharmony_ci ASSERT(tmpbuffer != NULL); 95462306a36Sopenharmony_ci memcpy(tmpbuffer, ifp->if_u1.if_data, size); 95562306a36Sopenharmony_ci sf = (struct xfs_attr_shortform *)tmpbuffer; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); 95862306a36Sopenharmony_ci xfs_bmap_local_to_extents_empty(args->trans, dp, XFS_ATTR_FORK); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci bp = NULL; 96162306a36Sopenharmony_ci error = xfs_da_grow_inode(args, &blkno); 96262306a36Sopenharmony_ci if (error) 96362306a36Sopenharmony_ci goto out; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ASSERT(blkno == 0); 96662306a36Sopenharmony_ci error = xfs_attr3_leaf_create(args, blkno, &bp); 96762306a36Sopenharmony_ci if (error) 96862306a36Sopenharmony_ci goto out; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci memset((char *)&nargs, 0, sizeof(nargs)); 97162306a36Sopenharmony_ci nargs.dp = dp; 97262306a36Sopenharmony_ci nargs.geo = args->geo; 97362306a36Sopenharmony_ci nargs.total = args->total; 97462306a36Sopenharmony_ci nargs.whichfork = XFS_ATTR_FORK; 97562306a36Sopenharmony_ci nargs.trans = args->trans; 97662306a36Sopenharmony_ci nargs.op_flags = XFS_DA_OP_OKNOENT; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci sfe = &sf->list[0]; 97962306a36Sopenharmony_ci for (i = 0; i < sf->hdr.count; i++) { 98062306a36Sopenharmony_ci nargs.name = sfe->nameval; 98162306a36Sopenharmony_ci nargs.namelen = sfe->namelen; 98262306a36Sopenharmony_ci nargs.value = &sfe->nameval[nargs.namelen]; 98362306a36Sopenharmony_ci nargs.valuelen = sfe->valuelen; 98462306a36Sopenharmony_ci nargs.hashval = xfs_da_hashname(sfe->nameval, 98562306a36Sopenharmony_ci sfe->namelen); 98662306a36Sopenharmony_ci nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK; 98762306a36Sopenharmony_ci error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */ 98862306a36Sopenharmony_ci ASSERT(error == -ENOATTR); 98962306a36Sopenharmony_ci error = xfs_attr3_leaf_add(bp, &nargs); 99062306a36Sopenharmony_ci ASSERT(error != -ENOSPC); 99162306a36Sopenharmony_ci if (error) 99262306a36Sopenharmony_ci goto out; 99362306a36Sopenharmony_ci sfe = xfs_attr_sf_nextentry(sfe); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci error = 0; 99662306a36Sopenharmony_ciout: 99762306a36Sopenharmony_ci kmem_free(tmpbuffer); 99862306a36Sopenharmony_ci return error; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* 100262306a36Sopenharmony_ci * Check a leaf attribute block to see if all the entries would fit into 100362306a36Sopenharmony_ci * a shortform attribute list. 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_ciint 100662306a36Sopenharmony_cixfs_attr_shortform_allfit( 100762306a36Sopenharmony_ci struct xfs_buf *bp, 100862306a36Sopenharmony_ci struct xfs_inode *dp) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 101162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 101262306a36Sopenharmony_ci xfs_attr_leaf_name_local_t *name_loc; 101362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr leafhdr; 101462306a36Sopenharmony_ci int bytes; 101562306a36Sopenharmony_ci int i; 101662306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci leaf = bp->b_addr; 101962306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); 102062306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(leaf); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci bytes = sizeof(struct xfs_attr_sf_hdr); 102362306a36Sopenharmony_ci for (i = 0; i < leafhdr.count; entry++, i++) { 102462306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_INCOMPLETE) 102562306a36Sopenharmony_ci continue; /* don't copy partial entries */ 102662306a36Sopenharmony_ci if (!(entry->flags & XFS_ATTR_LOCAL)) 102762306a36Sopenharmony_ci return 0; 102862306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, i); 102962306a36Sopenharmony_ci if (name_loc->namelen >= XFS_ATTR_SF_ENTSIZE_MAX) 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci if (be16_to_cpu(name_loc->valuelen) >= XFS_ATTR_SF_ENTSIZE_MAX) 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci bytes += xfs_attr_sf_entsize_byname(name_loc->namelen, 103462306a36Sopenharmony_ci be16_to_cpu(name_loc->valuelen)); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci if (xfs_has_attr2(dp->i_mount) && 103762306a36Sopenharmony_ci (dp->i_df.if_format != XFS_DINODE_FMT_BTREE) && 103862306a36Sopenharmony_ci (bytes == sizeof(struct xfs_attr_sf_hdr))) 103962306a36Sopenharmony_ci return -1; 104062306a36Sopenharmony_ci return xfs_attr_shortform_bytesfit(dp, bytes); 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci/* Verify the consistency of an inline attribute fork. */ 104462306a36Sopenharmony_cixfs_failaddr_t 104562306a36Sopenharmony_cixfs_attr_shortform_verify( 104662306a36Sopenharmony_ci struct xfs_inode *ip) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct xfs_attr_shortform *sfp; 104962306a36Sopenharmony_ci struct xfs_attr_sf_entry *sfep; 105062306a36Sopenharmony_ci struct xfs_attr_sf_entry *next_sfep; 105162306a36Sopenharmony_ci char *endp; 105262306a36Sopenharmony_ci struct xfs_ifork *ifp; 105362306a36Sopenharmony_ci int i; 105462306a36Sopenharmony_ci int64_t size; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ASSERT(ip->i_af.if_format == XFS_DINODE_FMT_LOCAL); 105762306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK); 105862306a36Sopenharmony_ci sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data; 105962306a36Sopenharmony_ci size = ifp->if_bytes; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* 106262306a36Sopenharmony_ci * Give up if the attribute is way too short. 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_ci if (size < sizeof(struct xfs_attr_sf_hdr)) 106562306a36Sopenharmony_ci return __this_address; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci endp = (char *)sfp + size; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Check all reported entries */ 107062306a36Sopenharmony_ci sfep = &sfp->list[0]; 107162306a36Sopenharmony_ci for (i = 0; i < sfp->hdr.count; i++) { 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * struct xfs_attr_sf_entry has a variable length. 107462306a36Sopenharmony_ci * Check the fixed-offset parts of the structure are 107562306a36Sopenharmony_ci * within the data buffer. 107662306a36Sopenharmony_ci * xfs_attr_sf_entry is defined with a 1-byte variable 107762306a36Sopenharmony_ci * array at the end, so we must subtract that off. 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci if (((char *)sfep + sizeof(*sfep)) >= endp) 108062306a36Sopenharmony_ci return __this_address; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* Don't allow names with known bad length. */ 108362306a36Sopenharmony_ci if (sfep->namelen == 0) 108462306a36Sopenharmony_ci return __this_address; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * Check that the variable-length part of the structure is 108862306a36Sopenharmony_ci * within the data buffer. The next entry starts after the 108962306a36Sopenharmony_ci * name component, so nextentry is an acceptable test. 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_ci next_sfep = xfs_attr_sf_nextentry(sfep); 109262306a36Sopenharmony_ci if ((char *)next_sfep > endp) 109362306a36Sopenharmony_ci return __this_address; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* 109662306a36Sopenharmony_ci * Check for unknown flags. Short form doesn't support 109762306a36Sopenharmony_ci * the incomplete or local bits, so we can use the namespace 109862306a36Sopenharmony_ci * mask here. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci if (sfep->flags & ~XFS_ATTR_NSP_ONDISK_MASK) 110162306a36Sopenharmony_ci return __this_address; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* 110462306a36Sopenharmony_ci * Check for invalid namespace combinations. We only allow 110562306a36Sopenharmony_ci * one namespace flag per xattr, so we can just count the 110662306a36Sopenharmony_ci * bits (i.e. hweight) here. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) 110962306a36Sopenharmony_ci return __this_address; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci sfep = next_sfep; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci if ((void *)sfep != (void *)endp) 111462306a36Sopenharmony_ci return __this_address; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return NULL; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/* 112062306a36Sopenharmony_ci * Convert a leaf attribute list to shortform attribute list 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ciint 112362306a36Sopenharmony_cixfs_attr3_leaf_to_shortform( 112462306a36Sopenharmony_ci struct xfs_buf *bp, 112562306a36Sopenharmony_ci struct xfs_da_args *args, 112662306a36Sopenharmony_ci int forkoff) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 112962306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 113062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 113162306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *name_loc; 113262306a36Sopenharmony_ci struct xfs_da_args nargs; 113362306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 113462306a36Sopenharmony_ci char *tmpbuffer; 113562306a36Sopenharmony_ci int error; 113662306a36Sopenharmony_ci int i; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci trace_xfs_attr_leaf_to_sf(args); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci tmpbuffer = kmem_alloc(args->geo->blksize, 0); 114162306a36Sopenharmony_ci if (!tmpbuffer) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci memcpy(tmpbuffer, bp->b_addr, args->geo->blksize); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci leaf = (xfs_attr_leafblock_t *)tmpbuffer; 114762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 114862306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(leaf); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* XXX (dgc): buffer is about to be marked stale - why zero it? */ 115162306a36Sopenharmony_ci memset(bp->b_addr, 0, args->geo->blksize); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci /* 115462306a36Sopenharmony_ci * Clean out the prior contents of the attribute list. 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci error = xfs_da_shrink_inode(args, 0, bp); 115762306a36Sopenharmony_ci if (error) 115862306a36Sopenharmony_ci goto out; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (forkoff == -1) { 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * Don't remove the attr fork if this operation is the first 116362306a36Sopenharmony_ci * part of a attr replace operations. We're going to add a new 116462306a36Sopenharmony_ci * attr immediately, so we need to keep the attr fork around in 116562306a36Sopenharmony_ci * this case. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci if (!(args->op_flags & XFS_DA_OP_REPLACE)) { 116862306a36Sopenharmony_ci ASSERT(xfs_has_attr2(dp->i_mount)); 116962306a36Sopenharmony_ci ASSERT(dp->i_df.if_format != XFS_DINODE_FMT_BTREE); 117062306a36Sopenharmony_ci xfs_attr_fork_remove(dp, args->trans); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci goto out; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci xfs_attr_shortform_create(args); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* 117862306a36Sopenharmony_ci * Copy the attributes 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_ci memset((char *)&nargs, 0, sizeof(nargs)); 118162306a36Sopenharmony_ci nargs.geo = args->geo; 118262306a36Sopenharmony_ci nargs.dp = dp; 118362306a36Sopenharmony_ci nargs.total = args->total; 118462306a36Sopenharmony_ci nargs.whichfork = XFS_ATTR_FORK; 118562306a36Sopenharmony_ci nargs.trans = args->trans; 118662306a36Sopenharmony_ci nargs.op_flags = XFS_DA_OP_OKNOENT; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci for (i = 0; i < ichdr.count; entry++, i++) { 118962306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_INCOMPLETE) 119062306a36Sopenharmony_ci continue; /* don't copy partial entries */ 119162306a36Sopenharmony_ci if (!entry->nameidx) 119262306a36Sopenharmony_ci continue; 119362306a36Sopenharmony_ci ASSERT(entry->flags & XFS_ATTR_LOCAL); 119462306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, i); 119562306a36Sopenharmony_ci nargs.name = name_loc->nameval; 119662306a36Sopenharmony_ci nargs.namelen = name_loc->namelen; 119762306a36Sopenharmony_ci nargs.value = &name_loc->nameval[nargs.namelen]; 119862306a36Sopenharmony_ci nargs.valuelen = be16_to_cpu(name_loc->valuelen); 119962306a36Sopenharmony_ci nargs.hashval = be32_to_cpu(entry->hashval); 120062306a36Sopenharmony_ci nargs.attr_filter = entry->flags & XFS_ATTR_NSP_ONDISK_MASK; 120162306a36Sopenharmony_ci xfs_attr_shortform_add(&nargs, forkoff); 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci error = 0; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ciout: 120662306a36Sopenharmony_ci kmem_free(tmpbuffer); 120762306a36Sopenharmony_ci return error; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci/* 121162306a36Sopenharmony_ci * Convert from using a single leaf to a root node and a leaf. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_ciint 121462306a36Sopenharmony_cixfs_attr3_leaf_to_node( 121562306a36Sopenharmony_ci struct xfs_da_args *args) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 121862306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr icleafhdr; 121962306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 122062306a36Sopenharmony_ci struct xfs_da3_icnode_hdr icnodehdr; 122162306a36Sopenharmony_ci struct xfs_da_intnode *node; 122262306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 122362306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 122462306a36Sopenharmony_ci struct xfs_buf *bp1 = NULL; 122562306a36Sopenharmony_ci struct xfs_buf *bp2 = NULL; 122662306a36Sopenharmony_ci xfs_dablk_t blkno; 122762306a36Sopenharmony_ci int error; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci trace_xfs_attr_leaf_to_node(args); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_ATTR_LEAF_TO_NODE)) { 123262306a36Sopenharmony_ci error = -EIO; 123362306a36Sopenharmony_ci goto out; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci error = xfs_da_grow_inode(args, &blkno); 123762306a36Sopenharmony_ci if (error) 123862306a36Sopenharmony_ci goto out; 123962306a36Sopenharmony_ci error = xfs_attr3_leaf_read(args->trans, dp, 0, &bp1); 124062306a36Sopenharmony_ci if (error) 124162306a36Sopenharmony_ci goto out; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci error = xfs_da_get_buf(args->trans, dp, blkno, &bp2, XFS_ATTR_FORK); 124462306a36Sopenharmony_ci if (error) 124562306a36Sopenharmony_ci goto out; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* copy leaf to new buffer, update identifiers */ 124862306a36Sopenharmony_ci xfs_trans_buf_set_type(args->trans, bp2, XFS_BLFT_ATTR_LEAF_BUF); 124962306a36Sopenharmony_ci bp2->b_ops = bp1->b_ops; 125062306a36Sopenharmony_ci memcpy(bp2->b_addr, bp1->b_addr, args->geo->blksize); 125162306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 125262306a36Sopenharmony_ci struct xfs_da3_blkinfo *hdr3 = bp2->b_addr; 125362306a36Sopenharmony_ci hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp2)); 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp2, 0, args->geo->blksize - 1); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* 125862306a36Sopenharmony_ci * Set up the new root node. 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci error = xfs_da3_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK); 126162306a36Sopenharmony_ci if (error) 126262306a36Sopenharmony_ci goto out; 126362306a36Sopenharmony_ci node = bp1->b_addr; 126462306a36Sopenharmony_ci xfs_da3_node_hdr_from_disk(mp, &icnodehdr, node); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci leaf = bp2->b_addr; 126762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &icleafhdr, leaf); 126862306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* both on-disk, don't endian-flip twice */ 127162306a36Sopenharmony_ci icnodehdr.btree[0].hashval = entries[icleafhdr.count - 1].hashval; 127262306a36Sopenharmony_ci icnodehdr.btree[0].before = cpu_to_be32(blkno); 127362306a36Sopenharmony_ci icnodehdr.count = 1; 127462306a36Sopenharmony_ci xfs_da3_node_hdr_to_disk(dp->i_mount, node, &icnodehdr); 127562306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp1, 0, args->geo->blksize - 1); 127662306a36Sopenharmony_ci error = 0; 127762306a36Sopenharmony_ciout: 127862306a36Sopenharmony_ci return error; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/*======================================================================== 128262306a36Sopenharmony_ci * Routines used for growing the Btree. 128362306a36Sopenharmony_ci *========================================================================*/ 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci/* 128662306a36Sopenharmony_ci * Create the initial contents of a leaf attribute list 128762306a36Sopenharmony_ci * or a leaf in a node attribute list. 128862306a36Sopenharmony_ci */ 128962306a36Sopenharmony_ciSTATIC int 129062306a36Sopenharmony_cixfs_attr3_leaf_create( 129162306a36Sopenharmony_ci struct xfs_da_args *args, 129262306a36Sopenharmony_ci xfs_dablk_t blkno, 129362306a36Sopenharmony_ci struct xfs_buf **bpp) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 129662306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 129762306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 129862306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 129962306a36Sopenharmony_ci struct xfs_buf *bp; 130062306a36Sopenharmony_ci int error; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci trace_xfs_attr_leaf_create(args); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci error = xfs_da_get_buf(args->trans, args->dp, blkno, &bp, 130562306a36Sopenharmony_ci XFS_ATTR_FORK); 130662306a36Sopenharmony_ci if (error) 130762306a36Sopenharmony_ci return error; 130862306a36Sopenharmony_ci bp->b_ops = &xfs_attr3_leaf_buf_ops; 130962306a36Sopenharmony_ci xfs_trans_buf_set_type(args->trans, bp, XFS_BLFT_ATTR_LEAF_BUF); 131062306a36Sopenharmony_ci leaf = bp->b_addr; 131162306a36Sopenharmony_ci memset(leaf, 0, args->geo->blksize); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci memset(&ichdr, 0, sizeof(ichdr)); 131462306a36Sopenharmony_ci ichdr.firstused = args->geo->blksize; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (xfs_has_crc(mp)) { 131762306a36Sopenharmony_ci struct xfs_da3_blkinfo *hdr3 = bp->b_addr; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci ichdr.magic = XFS_ATTR3_LEAF_MAGIC; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp)); 132262306a36Sopenharmony_ci hdr3->owner = cpu_to_be64(dp->i_ino); 132362306a36Sopenharmony_ci uuid_copy(&hdr3->uuid, &mp->m_sb.sb_meta_uuid); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci ichdr.freemap[0].base = sizeof(struct xfs_attr3_leaf_hdr); 132662306a36Sopenharmony_ci } else { 132762306a36Sopenharmony_ci ichdr.magic = XFS_ATTR_LEAF_MAGIC; 132862306a36Sopenharmony_ci ichdr.freemap[0].base = sizeof(struct xfs_attr_leaf_hdr); 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci ichdr.freemap[0].size = ichdr.firstused - ichdr.freemap[0].base; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr); 133362306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 0, args->geo->blksize - 1); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci *bpp = bp; 133662306a36Sopenharmony_ci return 0; 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci/* 134062306a36Sopenharmony_ci * Split the leaf node, rebalance, then add the new entry. 134162306a36Sopenharmony_ci */ 134262306a36Sopenharmony_ciint 134362306a36Sopenharmony_cixfs_attr3_leaf_split( 134462306a36Sopenharmony_ci struct xfs_da_state *state, 134562306a36Sopenharmony_ci struct xfs_da_state_blk *oldblk, 134662306a36Sopenharmony_ci struct xfs_da_state_blk *newblk) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci xfs_dablk_t blkno; 134962306a36Sopenharmony_ci int error; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci trace_xfs_attr_leaf_split(state->args); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* 135462306a36Sopenharmony_ci * Allocate space for a new leaf node. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_ci ASSERT(oldblk->magic == XFS_ATTR_LEAF_MAGIC); 135762306a36Sopenharmony_ci error = xfs_da_grow_inode(state->args, &blkno); 135862306a36Sopenharmony_ci if (error) 135962306a36Sopenharmony_ci return error; 136062306a36Sopenharmony_ci error = xfs_attr3_leaf_create(state->args, blkno, &newblk->bp); 136162306a36Sopenharmony_ci if (error) 136262306a36Sopenharmony_ci return error; 136362306a36Sopenharmony_ci newblk->blkno = blkno; 136462306a36Sopenharmony_ci newblk->magic = XFS_ATTR_LEAF_MAGIC; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* 136762306a36Sopenharmony_ci * Rebalance the entries across the two leaves. 136862306a36Sopenharmony_ci * NOTE: rebalance() currently depends on the 2nd block being empty. 136962306a36Sopenharmony_ci */ 137062306a36Sopenharmony_ci xfs_attr3_leaf_rebalance(state, oldblk, newblk); 137162306a36Sopenharmony_ci error = xfs_da3_blk_link(state, oldblk, newblk); 137262306a36Sopenharmony_ci if (error) 137362306a36Sopenharmony_ci return error; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* 137662306a36Sopenharmony_ci * Save info on "old" attribute for "atomic rename" ops, leaf_add() 137762306a36Sopenharmony_ci * modifies the index/blkno/rmtblk/rmtblkcnt fields to show the 137862306a36Sopenharmony_ci * "new" attrs info. Will need the "old" info to remove it later. 137962306a36Sopenharmony_ci * 138062306a36Sopenharmony_ci * Insert the "new" entry in the correct block. 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci if (state->inleaf) { 138362306a36Sopenharmony_ci trace_xfs_attr_leaf_add_old(state->args); 138462306a36Sopenharmony_ci error = xfs_attr3_leaf_add(oldblk->bp, state->args); 138562306a36Sopenharmony_ci } else { 138662306a36Sopenharmony_ci trace_xfs_attr_leaf_add_new(state->args); 138762306a36Sopenharmony_ci error = xfs_attr3_leaf_add(newblk->bp, state->args); 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* 139162306a36Sopenharmony_ci * Update last hashval in each block since we added the name. 139262306a36Sopenharmony_ci */ 139362306a36Sopenharmony_ci oldblk->hashval = xfs_attr_leaf_lasthash(oldblk->bp, NULL); 139462306a36Sopenharmony_ci newblk->hashval = xfs_attr_leaf_lasthash(newblk->bp, NULL); 139562306a36Sopenharmony_ci return error; 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci/* 139962306a36Sopenharmony_ci * Add a name to the leaf attribute list structure. 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ciint 140262306a36Sopenharmony_cixfs_attr3_leaf_add( 140362306a36Sopenharmony_ci struct xfs_buf *bp, 140462306a36Sopenharmony_ci struct xfs_da_args *args) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 140762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 140862306a36Sopenharmony_ci int tablesize; 140962306a36Sopenharmony_ci int entsize; 141062306a36Sopenharmony_ci int sum; 141162306a36Sopenharmony_ci int tmp; 141262306a36Sopenharmony_ci int i; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci trace_xfs_attr_leaf_add(args); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci leaf = bp->b_addr; 141762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 141862306a36Sopenharmony_ci ASSERT(args->index >= 0 && args->index <= ichdr.count); 141962306a36Sopenharmony_ci entsize = xfs_attr_leaf_newentsize(args, NULL); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* 142262306a36Sopenharmony_ci * Search through freemap for first-fit on new name length. 142362306a36Sopenharmony_ci * (may need to figure in size of entry struct too) 142462306a36Sopenharmony_ci */ 142562306a36Sopenharmony_ci tablesize = (ichdr.count + 1) * sizeof(xfs_attr_leaf_entry_t) 142662306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf); 142762306a36Sopenharmony_ci for (sum = 0, i = XFS_ATTR_LEAF_MAPSIZE - 1; i >= 0; i--) { 142862306a36Sopenharmony_ci if (tablesize > ichdr.firstused) { 142962306a36Sopenharmony_ci sum += ichdr.freemap[i].size; 143062306a36Sopenharmony_ci continue; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci if (!ichdr.freemap[i].size) 143362306a36Sopenharmony_ci continue; /* no space in this map */ 143462306a36Sopenharmony_ci tmp = entsize; 143562306a36Sopenharmony_ci if (ichdr.freemap[i].base < ichdr.firstused) 143662306a36Sopenharmony_ci tmp += sizeof(xfs_attr_leaf_entry_t); 143762306a36Sopenharmony_ci if (ichdr.freemap[i].size >= tmp) { 143862306a36Sopenharmony_ci tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, i); 143962306a36Sopenharmony_ci goto out_log_hdr; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci sum += ichdr.freemap[i].size; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* 144562306a36Sopenharmony_ci * If there are no holes in the address space of the block, 144662306a36Sopenharmony_ci * and we don't have enough freespace, then compaction will do us 144762306a36Sopenharmony_ci * no good and we should just give up. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_ci if (!ichdr.holes && sum < entsize) 145062306a36Sopenharmony_ci return -ENOSPC; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* 145362306a36Sopenharmony_ci * Compact the entries to coalesce free space. 145462306a36Sopenharmony_ci * This may change the hdr->count via dropping INCOMPLETE entries. 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_ci xfs_attr3_leaf_compact(args, &ichdr, bp); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* 145962306a36Sopenharmony_ci * After compaction, the block is guaranteed to have only one 146062306a36Sopenharmony_ci * free region, in freemap[0]. If it is not big enough, give up. 146162306a36Sopenharmony_ci */ 146262306a36Sopenharmony_ci if (ichdr.freemap[0].size < (entsize + sizeof(xfs_attr_leaf_entry_t))) { 146362306a36Sopenharmony_ci tmp = -ENOSPC; 146462306a36Sopenharmony_ci goto out_log_hdr; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, 0); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ciout_log_hdr: 147062306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr); 147162306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 147262306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, &leaf->hdr, 147362306a36Sopenharmony_ci xfs_attr3_leaf_hdr_size(leaf))); 147462306a36Sopenharmony_ci return tmp; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci/* 147862306a36Sopenharmony_ci * Add a name to a leaf attribute list structure. 147962306a36Sopenharmony_ci */ 148062306a36Sopenharmony_ciSTATIC int 148162306a36Sopenharmony_cixfs_attr3_leaf_add_work( 148262306a36Sopenharmony_ci struct xfs_buf *bp, 148362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr, 148462306a36Sopenharmony_ci struct xfs_da_args *args, 148562306a36Sopenharmony_ci int mapindex) 148662306a36Sopenharmony_ci{ 148762306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 148862306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 148962306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *name_loc; 149062306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 149162306a36Sopenharmony_ci struct xfs_mount *mp; 149262306a36Sopenharmony_ci int tmp; 149362306a36Sopenharmony_ci int i; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci trace_xfs_attr_leaf_add_work(args); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci leaf = bp->b_addr; 149862306a36Sopenharmony_ci ASSERT(mapindex >= 0 && mapindex < XFS_ATTR_LEAF_MAPSIZE); 149962306a36Sopenharmony_ci ASSERT(args->index >= 0 && args->index <= ichdr->count); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* 150262306a36Sopenharmony_ci * Force open some space in the entry array and fill it in. 150362306a36Sopenharmony_ci */ 150462306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; 150562306a36Sopenharmony_ci if (args->index < ichdr->count) { 150662306a36Sopenharmony_ci tmp = ichdr->count - args->index; 150762306a36Sopenharmony_ci tmp *= sizeof(xfs_attr_leaf_entry_t); 150862306a36Sopenharmony_ci memmove(entry + 1, entry, tmp); 150962306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 151062306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry))); 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci ichdr->count++; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* 151562306a36Sopenharmony_ci * Allocate space for the new string (at the end of the run). 151662306a36Sopenharmony_ci */ 151762306a36Sopenharmony_ci mp = args->trans->t_mountp; 151862306a36Sopenharmony_ci ASSERT(ichdr->freemap[mapindex].base < args->geo->blksize); 151962306a36Sopenharmony_ci ASSERT((ichdr->freemap[mapindex].base & 0x3) == 0); 152062306a36Sopenharmony_ci ASSERT(ichdr->freemap[mapindex].size >= 152162306a36Sopenharmony_ci xfs_attr_leaf_newentsize(args, NULL)); 152262306a36Sopenharmony_ci ASSERT(ichdr->freemap[mapindex].size < args->geo->blksize); 152362306a36Sopenharmony_ci ASSERT((ichdr->freemap[mapindex].size & 0x3) == 0); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci ichdr->freemap[mapindex].size -= xfs_attr_leaf_newentsize(args, &tmp); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci entry->nameidx = cpu_to_be16(ichdr->freemap[mapindex].base + 152862306a36Sopenharmony_ci ichdr->freemap[mapindex].size); 152962306a36Sopenharmony_ci entry->hashval = cpu_to_be32(args->hashval); 153062306a36Sopenharmony_ci entry->flags = args->attr_filter; 153162306a36Sopenharmony_ci if (tmp) 153262306a36Sopenharmony_ci entry->flags |= XFS_ATTR_LOCAL; 153362306a36Sopenharmony_ci if (args->op_flags & XFS_DA_OP_REPLACE) { 153462306a36Sopenharmony_ci if (!(args->op_flags & XFS_DA_OP_LOGGED)) 153562306a36Sopenharmony_ci entry->flags |= XFS_ATTR_INCOMPLETE; 153662306a36Sopenharmony_ci if ((args->blkno2 == args->blkno) && 153762306a36Sopenharmony_ci (args->index2 <= args->index)) { 153862306a36Sopenharmony_ci args->index2++; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 154262306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); 154362306a36Sopenharmony_ci ASSERT((args->index == 0) || 154462306a36Sopenharmony_ci (be32_to_cpu(entry->hashval) >= be32_to_cpu((entry-1)->hashval))); 154562306a36Sopenharmony_ci ASSERT((args->index == ichdr->count - 1) || 154662306a36Sopenharmony_ci (be32_to_cpu(entry->hashval) <= be32_to_cpu((entry+1)->hashval))); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci /* 154962306a36Sopenharmony_ci * For "remote" attribute values, simply note that we need to 155062306a36Sopenharmony_ci * allocate space for the "remote" value. We can't actually 155162306a36Sopenharmony_ci * allocate the extents in this transaction, and we can't decide 155262306a36Sopenharmony_ci * which blocks they should be as we might allocate more blocks 155362306a36Sopenharmony_ci * as part of this transaction (a split operation for example). 155462306a36Sopenharmony_ci */ 155562306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_LOCAL) { 155662306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, args->index); 155762306a36Sopenharmony_ci name_loc->namelen = args->namelen; 155862306a36Sopenharmony_ci name_loc->valuelen = cpu_to_be16(args->valuelen); 155962306a36Sopenharmony_ci memcpy((char *)name_loc->nameval, args->name, args->namelen); 156062306a36Sopenharmony_ci memcpy((char *)&name_loc->nameval[args->namelen], args->value, 156162306a36Sopenharmony_ci be16_to_cpu(name_loc->valuelen)); 156262306a36Sopenharmony_ci } else { 156362306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); 156462306a36Sopenharmony_ci name_rmt->namelen = args->namelen; 156562306a36Sopenharmony_ci memcpy((char *)name_rmt->name, args->name, args->namelen); 156662306a36Sopenharmony_ci entry->flags |= XFS_ATTR_INCOMPLETE; 156762306a36Sopenharmony_ci /* just in case */ 156862306a36Sopenharmony_ci name_rmt->valuelen = 0; 156962306a36Sopenharmony_ci name_rmt->valueblk = 0; 157062306a36Sopenharmony_ci args->rmtblkno = 1; 157162306a36Sopenharmony_ci args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); 157262306a36Sopenharmony_ci args->rmtvaluelen = args->valuelen; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 157562306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), 157662306a36Sopenharmony_ci xfs_attr_leaf_entsize(leaf, args->index))); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci /* 157962306a36Sopenharmony_ci * Update the control info for this leaf node 158062306a36Sopenharmony_ci */ 158162306a36Sopenharmony_ci if (be16_to_cpu(entry->nameidx) < ichdr->firstused) 158262306a36Sopenharmony_ci ichdr->firstused = be16_to_cpu(entry->nameidx); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci ASSERT(ichdr->firstused >= ichdr->count * sizeof(xfs_attr_leaf_entry_t) 158562306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf)); 158662306a36Sopenharmony_ci tmp = (ichdr->count - 1) * sizeof(xfs_attr_leaf_entry_t) 158762306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 159062306a36Sopenharmony_ci if (ichdr->freemap[i].base == tmp) { 159162306a36Sopenharmony_ci ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t); 159262306a36Sopenharmony_ci ichdr->freemap[i].size -= 159362306a36Sopenharmony_ci min_t(uint16_t, ichdr->freemap[i].size, 159462306a36Sopenharmony_ci sizeof(xfs_attr_leaf_entry_t)); 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index); 159862306a36Sopenharmony_ci return 0; 159962306a36Sopenharmony_ci} 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci/* 160262306a36Sopenharmony_ci * Garbage collect a leaf attribute list block by copying it to a new buffer. 160362306a36Sopenharmony_ci */ 160462306a36Sopenharmony_ciSTATIC void 160562306a36Sopenharmony_cixfs_attr3_leaf_compact( 160662306a36Sopenharmony_ci struct xfs_da_args *args, 160762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr_dst, 160862306a36Sopenharmony_ci struct xfs_buf *bp) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf_src; 161162306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf_dst; 161262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr_src; 161362306a36Sopenharmony_ci struct xfs_trans *trans = args->trans; 161462306a36Sopenharmony_ci char *tmpbuffer; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci trace_xfs_attr_leaf_compact(args); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci tmpbuffer = kmem_alloc(args->geo->blksize, 0); 161962306a36Sopenharmony_ci memcpy(tmpbuffer, bp->b_addr, args->geo->blksize); 162062306a36Sopenharmony_ci memset(bp->b_addr, 0, args->geo->blksize); 162162306a36Sopenharmony_ci leaf_src = (xfs_attr_leafblock_t *)tmpbuffer; 162262306a36Sopenharmony_ci leaf_dst = bp->b_addr; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci /* 162562306a36Sopenharmony_ci * Copy the on-disk header back into the destination buffer to ensure 162662306a36Sopenharmony_ci * all the information in the header that is not part of the incore 162762306a36Sopenharmony_ci * header structure is preserved. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_ci memcpy(bp->b_addr, tmpbuffer, xfs_attr3_leaf_hdr_size(leaf_src)); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci /* Initialise the incore headers */ 163262306a36Sopenharmony_ci ichdr_src = *ichdr_dst; /* struct copy */ 163362306a36Sopenharmony_ci ichdr_dst->firstused = args->geo->blksize; 163462306a36Sopenharmony_ci ichdr_dst->usedbytes = 0; 163562306a36Sopenharmony_ci ichdr_dst->count = 0; 163662306a36Sopenharmony_ci ichdr_dst->holes = 0; 163762306a36Sopenharmony_ci ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src); 163862306a36Sopenharmony_ci ichdr_dst->freemap[0].size = ichdr_dst->firstused - 163962306a36Sopenharmony_ci ichdr_dst->freemap[0].base; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* write the header back to initialise the underlying buffer */ 164262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(args->geo, leaf_dst, ichdr_dst); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci /* 164562306a36Sopenharmony_ci * Copy all entry's in the same (sorted) order, 164662306a36Sopenharmony_ci * but allocate name/value pairs packed and in sequence. 164762306a36Sopenharmony_ci */ 164862306a36Sopenharmony_ci xfs_attr3_leaf_moveents(args, leaf_src, &ichdr_src, 0, 164962306a36Sopenharmony_ci leaf_dst, ichdr_dst, 0, ichdr_src.count); 165062306a36Sopenharmony_ci /* 165162306a36Sopenharmony_ci * this logs the entire buffer, but the caller must write the header 165262306a36Sopenharmony_ci * back to the buffer when it is finished modifying it. 165362306a36Sopenharmony_ci */ 165462306a36Sopenharmony_ci xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci kmem_free(tmpbuffer); 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci/* 166062306a36Sopenharmony_ci * Compare two leaf blocks "order". 166162306a36Sopenharmony_ci * Return 0 unless leaf2 should go before leaf1. 166262306a36Sopenharmony_ci */ 166362306a36Sopenharmony_cistatic int 166462306a36Sopenharmony_cixfs_attr3_leaf_order( 166562306a36Sopenharmony_ci struct xfs_buf *leaf1_bp, 166662306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leaf1hdr, 166762306a36Sopenharmony_ci struct xfs_buf *leaf2_bp, 166862306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *leaf2hdr) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries1; 167162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries2; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci entries1 = xfs_attr3_leaf_entryp(leaf1_bp->b_addr); 167462306a36Sopenharmony_ci entries2 = xfs_attr3_leaf_entryp(leaf2_bp->b_addr); 167562306a36Sopenharmony_ci if (leaf1hdr->count > 0 && leaf2hdr->count > 0 && 167662306a36Sopenharmony_ci ((be32_to_cpu(entries2[0].hashval) < 167762306a36Sopenharmony_ci be32_to_cpu(entries1[0].hashval)) || 167862306a36Sopenharmony_ci (be32_to_cpu(entries2[leaf2hdr->count - 1].hashval) < 167962306a36Sopenharmony_ci be32_to_cpu(entries1[leaf1hdr->count - 1].hashval)))) { 168062306a36Sopenharmony_ci return 1; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci return 0; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ciint 168662306a36Sopenharmony_cixfs_attr_leaf_order( 168762306a36Sopenharmony_ci struct xfs_buf *leaf1_bp, 168862306a36Sopenharmony_ci struct xfs_buf *leaf2_bp) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr1; 169162306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr2; 169262306a36Sopenharmony_ci struct xfs_mount *mp = leaf1_bp->b_mount; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr1, leaf1_bp->b_addr); 169562306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr2, leaf2_bp->b_addr); 169662306a36Sopenharmony_ci return xfs_attr3_leaf_order(leaf1_bp, &ichdr1, leaf2_bp, &ichdr2); 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci/* 170062306a36Sopenharmony_ci * Redistribute the attribute list entries between two leaf nodes, 170162306a36Sopenharmony_ci * taking into account the size of the new entry. 170262306a36Sopenharmony_ci * 170362306a36Sopenharmony_ci * NOTE: if new block is empty, then it will get the upper half of the 170462306a36Sopenharmony_ci * old block. At present, all (one) callers pass in an empty second block. 170562306a36Sopenharmony_ci * 170662306a36Sopenharmony_ci * This code adjusts the args->index/blkno and args->index2/blkno2 fields 170762306a36Sopenharmony_ci * to match what it is doing in splitting the attribute leaf block. Those 170862306a36Sopenharmony_ci * values are used in "atomic rename" operations on attributes. Note that 170962306a36Sopenharmony_ci * the "new" and "old" values can end up in different blocks. 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_ciSTATIC void 171262306a36Sopenharmony_cixfs_attr3_leaf_rebalance( 171362306a36Sopenharmony_ci struct xfs_da_state *state, 171462306a36Sopenharmony_ci struct xfs_da_state_blk *blk1, 171562306a36Sopenharmony_ci struct xfs_da_state_blk *blk2) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci struct xfs_da_args *args; 171862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf1; 171962306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf2; 172062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr1; 172162306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr2; 172262306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries1; 172362306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries2; 172462306a36Sopenharmony_ci int count; 172562306a36Sopenharmony_ci int totallen; 172662306a36Sopenharmony_ci int max; 172762306a36Sopenharmony_ci int space; 172862306a36Sopenharmony_ci int swap; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* 173162306a36Sopenharmony_ci * Set up environment. 173262306a36Sopenharmony_ci */ 173362306a36Sopenharmony_ci ASSERT(blk1->magic == XFS_ATTR_LEAF_MAGIC); 173462306a36Sopenharmony_ci ASSERT(blk2->magic == XFS_ATTR_LEAF_MAGIC); 173562306a36Sopenharmony_ci leaf1 = blk1->bp->b_addr; 173662306a36Sopenharmony_ci leaf2 = blk2->bp->b_addr; 173762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr1, leaf1); 173862306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr2, leaf2); 173962306a36Sopenharmony_ci ASSERT(ichdr2.count == 0); 174062306a36Sopenharmony_ci args = state->args; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci trace_xfs_attr_leaf_rebalance(args); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci /* 174562306a36Sopenharmony_ci * Check ordering of blocks, reverse if it makes things simpler. 174662306a36Sopenharmony_ci * 174762306a36Sopenharmony_ci * NOTE: Given that all (current) callers pass in an empty 174862306a36Sopenharmony_ci * second block, this code should never set "swap". 174962306a36Sopenharmony_ci */ 175062306a36Sopenharmony_ci swap = 0; 175162306a36Sopenharmony_ci if (xfs_attr3_leaf_order(blk1->bp, &ichdr1, blk2->bp, &ichdr2)) { 175262306a36Sopenharmony_ci swap(blk1, blk2); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* swap structures rather than reconverting them */ 175562306a36Sopenharmony_ci swap(ichdr1, ichdr2); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci leaf1 = blk1->bp->b_addr; 175862306a36Sopenharmony_ci leaf2 = blk2->bp->b_addr; 175962306a36Sopenharmony_ci swap = 1; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* 176362306a36Sopenharmony_ci * Examine entries until we reduce the absolute difference in 176462306a36Sopenharmony_ci * byte usage between the two blocks to a minimum. Then get 176562306a36Sopenharmony_ci * the direction to copy and the number of elements to move. 176662306a36Sopenharmony_ci * 176762306a36Sopenharmony_ci * "inleaf" is true if the new entry should be inserted into blk1. 176862306a36Sopenharmony_ci * If "swap" is also true, then reverse the sense of "inleaf". 176962306a36Sopenharmony_ci */ 177062306a36Sopenharmony_ci state->inleaf = xfs_attr3_leaf_figure_balance(state, blk1, &ichdr1, 177162306a36Sopenharmony_ci blk2, &ichdr2, 177262306a36Sopenharmony_ci &count, &totallen); 177362306a36Sopenharmony_ci if (swap) 177462306a36Sopenharmony_ci state->inleaf = !state->inleaf; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* 177762306a36Sopenharmony_ci * Move any entries required from leaf to leaf: 177862306a36Sopenharmony_ci */ 177962306a36Sopenharmony_ci if (count < ichdr1.count) { 178062306a36Sopenharmony_ci /* 178162306a36Sopenharmony_ci * Figure the total bytes to be added to the destination leaf. 178262306a36Sopenharmony_ci */ 178362306a36Sopenharmony_ci /* number entries being moved */ 178462306a36Sopenharmony_ci count = ichdr1.count - count; 178562306a36Sopenharmony_ci space = ichdr1.usedbytes - totallen; 178662306a36Sopenharmony_ci space += count * sizeof(xfs_attr_leaf_entry_t); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci /* 178962306a36Sopenharmony_ci * leaf2 is the destination, compact it if it looks tight. 179062306a36Sopenharmony_ci */ 179162306a36Sopenharmony_ci max = ichdr2.firstused - xfs_attr3_leaf_hdr_size(leaf1); 179262306a36Sopenharmony_ci max -= ichdr2.count * sizeof(xfs_attr_leaf_entry_t); 179362306a36Sopenharmony_ci if (space > max) 179462306a36Sopenharmony_ci xfs_attr3_leaf_compact(args, &ichdr2, blk2->bp); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* 179762306a36Sopenharmony_ci * Move high entries from leaf1 to low end of leaf2. 179862306a36Sopenharmony_ci */ 179962306a36Sopenharmony_ci xfs_attr3_leaf_moveents(args, leaf1, &ichdr1, 180062306a36Sopenharmony_ci ichdr1.count - count, leaf2, &ichdr2, 0, count); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci } else if (count > ichdr1.count) { 180362306a36Sopenharmony_ci /* 180462306a36Sopenharmony_ci * I assert that since all callers pass in an empty 180562306a36Sopenharmony_ci * second buffer, this code should never execute. 180662306a36Sopenharmony_ci */ 180762306a36Sopenharmony_ci ASSERT(0); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci /* 181062306a36Sopenharmony_ci * Figure the total bytes to be added to the destination leaf. 181162306a36Sopenharmony_ci */ 181262306a36Sopenharmony_ci /* number entries being moved */ 181362306a36Sopenharmony_ci count -= ichdr1.count; 181462306a36Sopenharmony_ci space = totallen - ichdr1.usedbytes; 181562306a36Sopenharmony_ci space += count * sizeof(xfs_attr_leaf_entry_t); 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci /* 181862306a36Sopenharmony_ci * leaf1 is the destination, compact it if it looks tight. 181962306a36Sopenharmony_ci */ 182062306a36Sopenharmony_ci max = ichdr1.firstused - xfs_attr3_leaf_hdr_size(leaf1); 182162306a36Sopenharmony_ci max -= ichdr1.count * sizeof(xfs_attr_leaf_entry_t); 182262306a36Sopenharmony_ci if (space > max) 182362306a36Sopenharmony_ci xfs_attr3_leaf_compact(args, &ichdr1, blk1->bp); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci /* 182662306a36Sopenharmony_ci * Move low entries from leaf2 to high end of leaf1. 182762306a36Sopenharmony_ci */ 182862306a36Sopenharmony_ci xfs_attr3_leaf_moveents(args, leaf2, &ichdr2, 0, leaf1, &ichdr1, 182962306a36Sopenharmony_ci ichdr1.count, count); 183062306a36Sopenharmony_ci } 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(state->args->geo, leaf1, &ichdr1); 183362306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(state->args->geo, leaf2, &ichdr2); 183462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, blk1->bp, 0, args->geo->blksize - 1); 183562306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, blk2->bp, 0, args->geo->blksize - 1); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* 183862306a36Sopenharmony_ci * Copy out last hashval in each block for B-tree code. 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci entries1 = xfs_attr3_leaf_entryp(leaf1); 184162306a36Sopenharmony_ci entries2 = xfs_attr3_leaf_entryp(leaf2); 184262306a36Sopenharmony_ci blk1->hashval = be32_to_cpu(entries1[ichdr1.count - 1].hashval); 184362306a36Sopenharmony_ci blk2->hashval = be32_to_cpu(entries2[ichdr2.count - 1].hashval); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* 184662306a36Sopenharmony_ci * Adjust the expected index for insertion. 184762306a36Sopenharmony_ci * NOTE: this code depends on the (current) situation that the 184862306a36Sopenharmony_ci * second block was originally empty. 184962306a36Sopenharmony_ci * 185062306a36Sopenharmony_ci * If the insertion point moved to the 2nd block, we must adjust 185162306a36Sopenharmony_ci * the index. We must also track the entry just following the 185262306a36Sopenharmony_ci * new entry for use in an "atomic rename" operation, that entry 185362306a36Sopenharmony_ci * is always the "old" entry and the "new" entry is what we are 185462306a36Sopenharmony_ci * inserting. The index/blkno fields refer to the "old" entry, 185562306a36Sopenharmony_ci * while the index2/blkno2 fields refer to the "new" entry. 185662306a36Sopenharmony_ci */ 185762306a36Sopenharmony_ci if (blk1->index > ichdr1.count) { 185862306a36Sopenharmony_ci ASSERT(state->inleaf == 0); 185962306a36Sopenharmony_ci blk2->index = blk1->index - ichdr1.count; 186062306a36Sopenharmony_ci args->index = args->index2 = blk2->index; 186162306a36Sopenharmony_ci args->blkno = args->blkno2 = blk2->blkno; 186262306a36Sopenharmony_ci } else if (blk1->index == ichdr1.count) { 186362306a36Sopenharmony_ci if (state->inleaf) { 186462306a36Sopenharmony_ci args->index = blk1->index; 186562306a36Sopenharmony_ci args->blkno = blk1->blkno; 186662306a36Sopenharmony_ci args->index2 = 0; 186762306a36Sopenharmony_ci args->blkno2 = blk2->blkno; 186862306a36Sopenharmony_ci } else { 186962306a36Sopenharmony_ci /* 187062306a36Sopenharmony_ci * On a double leaf split, the original attr location 187162306a36Sopenharmony_ci * is already stored in blkno2/index2, so don't 187262306a36Sopenharmony_ci * overwrite it overwise we corrupt the tree. 187362306a36Sopenharmony_ci */ 187462306a36Sopenharmony_ci blk2->index = blk1->index - ichdr1.count; 187562306a36Sopenharmony_ci args->index = blk2->index; 187662306a36Sopenharmony_ci args->blkno = blk2->blkno; 187762306a36Sopenharmony_ci if (!state->extravalid) { 187862306a36Sopenharmony_ci /* 187962306a36Sopenharmony_ci * set the new attr location to match the old 188062306a36Sopenharmony_ci * one and let the higher level split code 188162306a36Sopenharmony_ci * decide where in the leaf to place it. 188262306a36Sopenharmony_ci */ 188362306a36Sopenharmony_ci args->index2 = blk2->index; 188462306a36Sopenharmony_ci args->blkno2 = blk2->blkno; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci } else { 188862306a36Sopenharmony_ci ASSERT(state->inleaf == 1); 188962306a36Sopenharmony_ci args->index = args->index2 = blk1->index; 189062306a36Sopenharmony_ci args->blkno = args->blkno2 = blk1->blkno; 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci/* 189562306a36Sopenharmony_ci * Examine entries until we reduce the absolute difference in 189662306a36Sopenharmony_ci * byte usage between the two blocks to a minimum. 189762306a36Sopenharmony_ci * GROT: Is this really necessary? With other than a 512 byte blocksize, 189862306a36Sopenharmony_ci * GROT: there will always be enough room in either block for a new entry. 189962306a36Sopenharmony_ci * GROT: Do a double-split for this case? 190062306a36Sopenharmony_ci */ 190162306a36Sopenharmony_ciSTATIC int 190262306a36Sopenharmony_cixfs_attr3_leaf_figure_balance( 190362306a36Sopenharmony_ci struct xfs_da_state *state, 190462306a36Sopenharmony_ci struct xfs_da_state_blk *blk1, 190562306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr1, 190662306a36Sopenharmony_ci struct xfs_da_state_blk *blk2, 190762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr2, 190862306a36Sopenharmony_ci int *countarg, 190962306a36Sopenharmony_ci int *usedbytesarg) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf1 = blk1->bp->b_addr; 191262306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf2 = blk2->bp->b_addr; 191362306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 191462306a36Sopenharmony_ci int count; 191562306a36Sopenharmony_ci int max; 191662306a36Sopenharmony_ci int index; 191762306a36Sopenharmony_ci int totallen = 0; 191862306a36Sopenharmony_ci int half; 191962306a36Sopenharmony_ci int lastdelta; 192062306a36Sopenharmony_ci int foundit = 0; 192162306a36Sopenharmony_ci int tmp; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci /* 192462306a36Sopenharmony_ci * Examine entries until we reduce the absolute difference in 192562306a36Sopenharmony_ci * byte usage between the two blocks to a minimum. 192662306a36Sopenharmony_ci */ 192762306a36Sopenharmony_ci max = ichdr1->count + ichdr2->count; 192862306a36Sopenharmony_ci half = (max + 1) * sizeof(*entry); 192962306a36Sopenharmony_ci half += ichdr1->usedbytes + ichdr2->usedbytes + 193062306a36Sopenharmony_ci xfs_attr_leaf_newentsize(state->args, NULL); 193162306a36Sopenharmony_ci half /= 2; 193262306a36Sopenharmony_ci lastdelta = state->args->geo->blksize; 193362306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(leaf1); 193462306a36Sopenharmony_ci for (count = index = 0; count < max; entry++, index++, count++) { 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci#define XFS_ATTR_ABS(A) (((A) < 0) ? -(A) : (A)) 193762306a36Sopenharmony_ci /* 193862306a36Sopenharmony_ci * The new entry is in the first block, account for it. 193962306a36Sopenharmony_ci */ 194062306a36Sopenharmony_ci if (count == blk1->index) { 194162306a36Sopenharmony_ci tmp = totallen + sizeof(*entry) + 194262306a36Sopenharmony_ci xfs_attr_leaf_newentsize(state->args, NULL); 194362306a36Sopenharmony_ci if (XFS_ATTR_ABS(half - tmp) > lastdelta) 194462306a36Sopenharmony_ci break; 194562306a36Sopenharmony_ci lastdelta = XFS_ATTR_ABS(half - tmp); 194662306a36Sopenharmony_ci totallen = tmp; 194762306a36Sopenharmony_ci foundit = 1; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci /* 195162306a36Sopenharmony_ci * Wrap around into the second block if necessary. 195262306a36Sopenharmony_ci */ 195362306a36Sopenharmony_ci if (count == ichdr1->count) { 195462306a36Sopenharmony_ci leaf1 = leaf2; 195562306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(leaf1); 195662306a36Sopenharmony_ci index = 0; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* 196062306a36Sopenharmony_ci * Figure out if next leaf entry would be too much. 196162306a36Sopenharmony_ci */ 196262306a36Sopenharmony_ci tmp = totallen + sizeof(*entry) + xfs_attr_leaf_entsize(leaf1, 196362306a36Sopenharmony_ci index); 196462306a36Sopenharmony_ci if (XFS_ATTR_ABS(half - tmp) > lastdelta) 196562306a36Sopenharmony_ci break; 196662306a36Sopenharmony_ci lastdelta = XFS_ATTR_ABS(half - tmp); 196762306a36Sopenharmony_ci totallen = tmp; 196862306a36Sopenharmony_ci#undef XFS_ATTR_ABS 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* 197262306a36Sopenharmony_ci * Calculate the number of usedbytes that will end up in lower block. 197362306a36Sopenharmony_ci * If new entry not in lower block, fix up the count. 197462306a36Sopenharmony_ci */ 197562306a36Sopenharmony_ci totallen -= count * sizeof(*entry); 197662306a36Sopenharmony_ci if (foundit) { 197762306a36Sopenharmony_ci totallen -= sizeof(*entry) + 197862306a36Sopenharmony_ci xfs_attr_leaf_newentsize(state->args, NULL); 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci *countarg = count; 198262306a36Sopenharmony_ci *usedbytesarg = totallen; 198362306a36Sopenharmony_ci return foundit; 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci/*======================================================================== 198762306a36Sopenharmony_ci * Routines used for shrinking the Btree. 198862306a36Sopenharmony_ci *========================================================================*/ 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci/* 199162306a36Sopenharmony_ci * Check a leaf block and its neighbors to see if the block should be 199262306a36Sopenharmony_ci * collapsed into one or the other neighbor. Always keep the block 199362306a36Sopenharmony_ci * with the smaller block number. 199462306a36Sopenharmony_ci * If the current block is over 50% full, don't try to join it, return 0. 199562306a36Sopenharmony_ci * If the block is empty, fill in the state structure and return 2. 199662306a36Sopenharmony_ci * If it can be collapsed, fill in the state structure and return 1. 199762306a36Sopenharmony_ci * If nothing can be done, return 0. 199862306a36Sopenharmony_ci * 199962306a36Sopenharmony_ci * GROT: allow for INCOMPLETE entries in calculation. 200062306a36Sopenharmony_ci */ 200162306a36Sopenharmony_ciint 200262306a36Sopenharmony_cixfs_attr3_leaf_toosmall( 200362306a36Sopenharmony_ci struct xfs_da_state *state, 200462306a36Sopenharmony_ci int *action) 200562306a36Sopenharmony_ci{ 200662306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 200762306a36Sopenharmony_ci struct xfs_da_state_blk *blk; 200862306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 200962306a36Sopenharmony_ci struct xfs_buf *bp; 201062306a36Sopenharmony_ci xfs_dablk_t blkno; 201162306a36Sopenharmony_ci int bytes; 201262306a36Sopenharmony_ci int forward; 201362306a36Sopenharmony_ci int error; 201462306a36Sopenharmony_ci int retval; 201562306a36Sopenharmony_ci int i; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci trace_xfs_attr_leaf_toosmall(state->args); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci /* 202062306a36Sopenharmony_ci * Check for the degenerate case of the block being over 50% full. 202162306a36Sopenharmony_ci * If so, it's not worth even looking to see if we might be able 202262306a36Sopenharmony_ci * to coalesce with a sibling. 202362306a36Sopenharmony_ci */ 202462306a36Sopenharmony_ci blk = &state->path.blk[ state->path.active-1 ]; 202562306a36Sopenharmony_ci leaf = blk->bp->b_addr; 202662306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr, leaf); 202762306a36Sopenharmony_ci bytes = xfs_attr3_leaf_hdr_size(leaf) + 202862306a36Sopenharmony_ci ichdr.count * sizeof(xfs_attr_leaf_entry_t) + 202962306a36Sopenharmony_ci ichdr.usedbytes; 203062306a36Sopenharmony_ci if (bytes > (state->args->geo->blksize >> 1)) { 203162306a36Sopenharmony_ci *action = 0; /* blk over 50%, don't try to join */ 203262306a36Sopenharmony_ci return 0; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci /* 203662306a36Sopenharmony_ci * Check for the degenerate case of the block being empty. 203762306a36Sopenharmony_ci * If the block is empty, we'll simply delete it, no need to 203862306a36Sopenharmony_ci * coalesce it with a sibling block. We choose (arbitrarily) 203962306a36Sopenharmony_ci * to merge with the forward block unless it is NULL. 204062306a36Sopenharmony_ci */ 204162306a36Sopenharmony_ci if (ichdr.count == 0) { 204262306a36Sopenharmony_ci /* 204362306a36Sopenharmony_ci * Make altpath point to the block we want to keep and 204462306a36Sopenharmony_ci * path point to the block we want to drop (this one). 204562306a36Sopenharmony_ci */ 204662306a36Sopenharmony_ci forward = (ichdr.forw != 0); 204762306a36Sopenharmony_ci memcpy(&state->altpath, &state->path, sizeof(state->path)); 204862306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->altpath, forward, 204962306a36Sopenharmony_ci 0, &retval); 205062306a36Sopenharmony_ci if (error) 205162306a36Sopenharmony_ci return error; 205262306a36Sopenharmony_ci if (retval) { 205362306a36Sopenharmony_ci *action = 0; 205462306a36Sopenharmony_ci } else { 205562306a36Sopenharmony_ci *action = 2; 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci return 0; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* 206162306a36Sopenharmony_ci * Examine each sibling block to see if we can coalesce with 206262306a36Sopenharmony_ci * at least 25% free space to spare. We need to figure out 206362306a36Sopenharmony_ci * whether to merge with the forward or the backward block. 206462306a36Sopenharmony_ci * We prefer coalescing with the lower numbered sibling so as 206562306a36Sopenharmony_ci * to shrink an attribute list over time. 206662306a36Sopenharmony_ci */ 206762306a36Sopenharmony_ci /* start with smaller blk num */ 206862306a36Sopenharmony_ci forward = ichdr.forw < ichdr.back; 206962306a36Sopenharmony_ci for (i = 0; i < 2; forward = !forward, i++) { 207062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr2; 207162306a36Sopenharmony_ci if (forward) 207262306a36Sopenharmony_ci blkno = ichdr.forw; 207362306a36Sopenharmony_ci else 207462306a36Sopenharmony_ci blkno = ichdr.back; 207562306a36Sopenharmony_ci if (blkno == 0) 207662306a36Sopenharmony_ci continue; 207762306a36Sopenharmony_ci error = xfs_attr3_leaf_read(state->args->trans, state->args->dp, 207862306a36Sopenharmony_ci blkno, &bp); 207962306a36Sopenharmony_ci if (error) 208062306a36Sopenharmony_ci return error; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr2, bp->b_addr); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci bytes = state->args->geo->blksize - 208562306a36Sopenharmony_ci (state->args->geo->blksize >> 2) - 208662306a36Sopenharmony_ci ichdr.usedbytes - ichdr2.usedbytes - 208762306a36Sopenharmony_ci ((ichdr.count + ichdr2.count) * 208862306a36Sopenharmony_ci sizeof(xfs_attr_leaf_entry_t)) - 208962306a36Sopenharmony_ci xfs_attr3_leaf_hdr_size(leaf); 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci xfs_trans_brelse(state->args->trans, bp); 209262306a36Sopenharmony_ci if (bytes >= 0) 209362306a36Sopenharmony_ci break; /* fits with at least 25% to spare */ 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci if (i >= 2) { 209662306a36Sopenharmony_ci *action = 0; 209762306a36Sopenharmony_ci return 0; 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* 210162306a36Sopenharmony_ci * Make altpath point to the block we want to keep (the lower 210262306a36Sopenharmony_ci * numbered block) and path point to the block we want to drop. 210362306a36Sopenharmony_ci */ 210462306a36Sopenharmony_ci memcpy(&state->altpath, &state->path, sizeof(state->path)); 210562306a36Sopenharmony_ci if (blkno < blk->blkno) { 210662306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->altpath, forward, 210762306a36Sopenharmony_ci 0, &retval); 210862306a36Sopenharmony_ci } else { 210962306a36Sopenharmony_ci error = xfs_da3_path_shift(state, &state->path, forward, 211062306a36Sopenharmony_ci 0, &retval); 211162306a36Sopenharmony_ci } 211262306a36Sopenharmony_ci if (error) 211362306a36Sopenharmony_ci return error; 211462306a36Sopenharmony_ci if (retval) { 211562306a36Sopenharmony_ci *action = 0; 211662306a36Sopenharmony_ci } else { 211762306a36Sopenharmony_ci *action = 1; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci return 0; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci/* 212362306a36Sopenharmony_ci * Remove a name from the leaf attribute list structure. 212462306a36Sopenharmony_ci * 212562306a36Sopenharmony_ci * Return 1 if leaf is less than 37% full, 0 if >= 37% full. 212662306a36Sopenharmony_ci * If two leaves are 37% full, when combined they will leave 25% free. 212762306a36Sopenharmony_ci */ 212862306a36Sopenharmony_ciint 212962306a36Sopenharmony_cixfs_attr3_leaf_remove( 213062306a36Sopenharmony_ci struct xfs_buf *bp, 213162306a36Sopenharmony_ci struct xfs_da_args *args) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 213462306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 213562306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 213662306a36Sopenharmony_ci int before; 213762306a36Sopenharmony_ci int after; 213862306a36Sopenharmony_ci int smallest; 213962306a36Sopenharmony_ci int entsize; 214062306a36Sopenharmony_ci int tablesize; 214162306a36Sopenharmony_ci int tmp; 214262306a36Sopenharmony_ci int i; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci trace_xfs_attr_leaf_remove(args); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci leaf = bp->b_addr; 214762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci ASSERT(ichdr.count > 0 && ichdr.count < args->geo->blksize / 8); 215062306a36Sopenharmony_ci ASSERT(args->index >= 0 && args->index < ichdr.count); 215162306a36Sopenharmony_ci ASSERT(ichdr.firstused >= ichdr.count * sizeof(*entry) + 215262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_size(leaf)); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused); 215762306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci /* 216062306a36Sopenharmony_ci * Scan through free region table: 216162306a36Sopenharmony_ci * check for adjacency of free'd entry with an existing one, 216262306a36Sopenharmony_ci * find smallest free region in case we need to replace it, 216362306a36Sopenharmony_ci * adjust any map that borders the entry table, 216462306a36Sopenharmony_ci */ 216562306a36Sopenharmony_ci tablesize = ichdr.count * sizeof(xfs_attr_leaf_entry_t) 216662306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf); 216762306a36Sopenharmony_ci tmp = ichdr.freemap[0].size; 216862306a36Sopenharmony_ci before = after = -1; 216962306a36Sopenharmony_ci smallest = XFS_ATTR_LEAF_MAPSIZE - 1; 217062306a36Sopenharmony_ci entsize = xfs_attr_leaf_entsize(leaf, args->index); 217162306a36Sopenharmony_ci for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { 217262306a36Sopenharmony_ci ASSERT(ichdr.freemap[i].base < args->geo->blksize); 217362306a36Sopenharmony_ci ASSERT(ichdr.freemap[i].size < args->geo->blksize); 217462306a36Sopenharmony_ci if (ichdr.freemap[i].base == tablesize) { 217562306a36Sopenharmony_ci ichdr.freemap[i].base -= sizeof(xfs_attr_leaf_entry_t); 217662306a36Sopenharmony_ci ichdr.freemap[i].size += sizeof(xfs_attr_leaf_entry_t); 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci if (ichdr.freemap[i].base + ichdr.freemap[i].size == 218062306a36Sopenharmony_ci be16_to_cpu(entry->nameidx)) { 218162306a36Sopenharmony_ci before = i; 218262306a36Sopenharmony_ci } else if (ichdr.freemap[i].base == 218362306a36Sopenharmony_ci (be16_to_cpu(entry->nameidx) + entsize)) { 218462306a36Sopenharmony_ci after = i; 218562306a36Sopenharmony_ci } else if (ichdr.freemap[i].size < tmp) { 218662306a36Sopenharmony_ci tmp = ichdr.freemap[i].size; 218762306a36Sopenharmony_ci smallest = i; 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci /* 219262306a36Sopenharmony_ci * Coalesce adjacent freemap regions, 219362306a36Sopenharmony_ci * or replace the smallest region. 219462306a36Sopenharmony_ci */ 219562306a36Sopenharmony_ci if ((before >= 0) || (after >= 0)) { 219662306a36Sopenharmony_ci if ((before >= 0) && (after >= 0)) { 219762306a36Sopenharmony_ci ichdr.freemap[before].size += entsize; 219862306a36Sopenharmony_ci ichdr.freemap[before].size += ichdr.freemap[after].size; 219962306a36Sopenharmony_ci ichdr.freemap[after].base = 0; 220062306a36Sopenharmony_ci ichdr.freemap[after].size = 0; 220162306a36Sopenharmony_ci } else if (before >= 0) { 220262306a36Sopenharmony_ci ichdr.freemap[before].size += entsize; 220362306a36Sopenharmony_ci } else { 220462306a36Sopenharmony_ci ichdr.freemap[after].base = be16_to_cpu(entry->nameidx); 220562306a36Sopenharmony_ci ichdr.freemap[after].size += entsize; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci } else { 220862306a36Sopenharmony_ci /* 220962306a36Sopenharmony_ci * Replace smallest region (if it is smaller than free'd entry) 221062306a36Sopenharmony_ci */ 221162306a36Sopenharmony_ci if (ichdr.freemap[smallest].size < entsize) { 221262306a36Sopenharmony_ci ichdr.freemap[smallest].base = be16_to_cpu(entry->nameidx); 221362306a36Sopenharmony_ci ichdr.freemap[smallest].size = entsize; 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci /* 221862306a36Sopenharmony_ci * Did we remove the first entry? 221962306a36Sopenharmony_ci */ 222062306a36Sopenharmony_ci if (be16_to_cpu(entry->nameidx) == ichdr.firstused) 222162306a36Sopenharmony_ci smallest = 1; 222262306a36Sopenharmony_ci else 222362306a36Sopenharmony_ci smallest = 0; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* 222662306a36Sopenharmony_ci * Compress the remaining entries and zero out the removed stuff. 222762306a36Sopenharmony_ci */ 222862306a36Sopenharmony_ci memset(xfs_attr3_leaf_name(leaf, args->index), 0, entsize); 222962306a36Sopenharmony_ci ichdr.usedbytes -= entsize; 223062306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 223162306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), 223262306a36Sopenharmony_ci entsize)); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci tmp = (ichdr.count - args->index) * sizeof(xfs_attr_leaf_entry_t); 223562306a36Sopenharmony_ci memmove(entry, entry + 1, tmp); 223662306a36Sopenharmony_ci ichdr.count--; 223762306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 223862306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(xfs_attr_leaf_entry_t))); 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[ichdr.count]; 224162306a36Sopenharmony_ci memset(entry, 0, sizeof(xfs_attr_leaf_entry_t)); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci /* 224462306a36Sopenharmony_ci * If we removed the first entry, re-find the first used byte 224562306a36Sopenharmony_ci * in the name area. Note that if the entry was the "firstused", 224662306a36Sopenharmony_ci * then we don't have a "hole" in our block resulting from 224762306a36Sopenharmony_ci * removing the name. 224862306a36Sopenharmony_ci */ 224962306a36Sopenharmony_ci if (smallest) { 225062306a36Sopenharmony_ci tmp = args->geo->blksize; 225162306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(leaf); 225262306a36Sopenharmony_ci for (i = ichdr.count - 1; i >= 0; entry++, i--) { 225362306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused); 225462306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci if (be16_to_cpu(entry->nameidx) < tmp) 225762306a36Sopenharmony_ci tmp = be16_to_cpu(entry->nameidx); 225862306a36Sopenharmony_ci } 225962306a36Sopenharmony_ci ichdr.firstused = tmp; 226062306a36Sopenharmony_ci ASSERT(ichdr.firstused != 0); 226162306a36Sopenharmony_ci } else { 226262306a36Sopenharmony_ci ichdr.holes = 1; /* mark as needing compaction */ 226362306a36Sopenharmony_ci } 226462306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr); 226562306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 226662306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, &leaf->hdr, 226762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_size(leaf))); 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci /* 227062306a36Sopenharmony_ci * Check if leaf is less than 50% full, caller may want to 227162306a36Sopenharmony_ci * "join" the leaf with a sibling if so. 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_ci tmp = ichdr.usedbytes + xfs_attr3_leaf_hdr_size(leaf) + 227462306a36Sopenharmony_ci ichdr.count * sizeof(xfs_attr_leaf_entry_t); 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci return tmp < args->geo->magicpct; /* leaf is < 37% full */ 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci/* 228062306a36Sopenharmony_ci * Move all the attribute list entries from drop_leaf into save_leaf. 228162306a36Sopenharmony_ci */ 228262306a36Sopenharmony_civoid 228362306a36Sopenharmony_cixfs_attr3_leaf_unbalance( 228462306a36Sopenharmony_ci struct xfs_da_state *state, 228562306a36Sopenharmony_ci struct xfs_da_state_blk *drop_blk, 228662306a36Sopenharmony_ci struct xfs_da_state_blk *save_blk) 228762306a36Sopenharmony_ci{ 228862306a36Sopenharmony_ci struct xfs_attr_leafblock *drop_leaf = drop_blk->bp->b_addr; 228962306a36Sopenharmony_ci struct xfs_attr_leafblock *save_leaf = save_blk->bp->b_addr; 229062306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr drophdr; 229162306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr savehdr; 229262306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci trace_xfs_attr_leaf_unbalance(state->args); 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &drophdr, drop_leaf); 229762306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(state->args->geo, &savehdr, save_leaf); 229862306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(drop_leaf); 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci /* 230162306a36Sopenharmony_ci * Save last hashval from dying block for later Btree fixup. 230262306a36Sopenharmony_ci */ 230362306a36Sopenharmony_ci drop_blk->hashval = be32_to_cpu(entry[drophdr.count - 1].hashval); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci /* 230662306a36Sopenharmony_ci * Check if we need a temp buffer, or can we do it in place. 230762306a36Sopenharmony_ci * Note that we don't check "leaf" for holes because we will 230862306a36Sopenharmony_ci * always be dropping it, toosmall() decided that for us already. 230962306a36Sopenharmony_ci */ 231062306a36Sopenharmony_ci if (savehdr.holes == 0) { 231162306a36Sopenharmony_ci /* 231262306a36Sopenharmony_ci * dest leaf has no holes, so we add there. May need 231362306a36Sopenharmony_ci * to make some room in the entry array. 231462306a36Sopenharmony_ci */ 231562306a36Sopenharmony_ci if (xfs_attr3_leaf_order(save_blk->bp, &savehdr, 231662306a36Sopenharmony_ci drop_blk->bp, &drophdr)) { 231762306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 231862306a36Sopenharmony_ci drop_leaf, &drophdr, 0, 231962306a36Sopenharmony_ci save_leaf, &savehdr, 0, 232062306a36Sopenharmony_ci drophdr.count); 232162306a36Sopenharmony_ci } else { 232262306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 232362306a36Sopenharmony_ci drop_leaf, &drophdr, 0, 232462306a36Sopenharmony_ci save_leaf, &savehdr, 232562306a36Sopenharmony_ci savehdr.count, drophdr.count); 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci } else { 232862306a36Sopenharmony_ci /* 232962306a36Sopenharmony_ci * Destination has holes, so we make a temporary copy 233062306a36Sopenharmony_ci * of the leaf and add them both to that. 233162306a36Sopenharmony_ci */ 233262306a36Sopenharmony_ci struct xfs_attr_leafblock *tmp_leaf; 233362306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr tmphdr; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci tmp_leaf = kmem_zalloc(state->args->geo->blksize, 0); 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci /* 233862306a36Sopenharmony_ci * Copy the header into the temp leaf so that all the stuff 233962306a36Sopenharmony_ci * not in the incore header is present and gets copied back in 234062306a36Sopenharmony_ci * once we've moved all the entries. 234162306a36Sopenharmony_ci */ 234262306a36Sopenharmony_ci memcpy(tmp_leaf, save_leaf, xfs_attr3_leaf_hdr_size(save_leaf)); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci memset(&tmphdr, 0, sizeof(tmphdr)); 234562306a36Sopenharmony_ci tmphdr.magic = savehdr.magic; 234662306a36Sopenharmony_ci tmphdr.forw = savehdr.forw; 234762306a36Sopenharmony_ci tmphdr.back = savehdr.back; 234862306a36Sopenharmony_ci tmphdr.firstused = state->args->geo->blksize; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci /* write the header to the temp buffer to initialise it */ 235162306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(state->args->geo, tmp_leaf, &tmphdr); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci if (xfs_attr3_leaf_order(save_blk->bp, &savehdr, 235462306a36Sopenharmony_ci drop_blk->bp, &drophdr)) { 235562306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 235662306a36Sopenharmony_ci drop_leaf, &drophdr, 0, 235762306a36Sopenharmony_ci tmp_leaf, &tmphdr, 0, 235862306a36Sopenharmony_ci drophdr.count); 235962306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 236062306a36Sopenharmony_ci save_leaf, &savehdr, 0, 236162306a36Sopenharmony_ci tmp_leaf, &tmphdr, tmphdr.count, 236262306a36Sopenharmony_ci savehdr.count); 236362306a36Sopenharmony_ci } else { 236462306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 236562306a36Sopenharmony_ci save_leaf, &savehdr, 0, 236662306a36Sopenharmony_ci tmp_leaf, &tmphdr, 0, 236762306a36Sopenharmony_ci savehdr.count); 236862306a36Sopenharmony_ci xfs_attr3_leaf_moveents(state->args, 236962306a36Sopenharmony_ci drop_leaf, &drophdr, 0, 237062306a36Sopenharmony_ci tmp_leaf, &tmphdr, tmphdr.count, 237162306a36Sopenharmony_ci drophdr.count); 237262306a36Sopenharmony_ci } 237362306a36Sopenharmony_ci memcpy(save_leaf, tmp_leaf, state->args->geo->blksize); 237462306a36Sopenharmony_ci savehdr = tmphdr; /* struct copy */ 237562306a36Sopenharmony_ci kmem_free(tmp_leaf); 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci xfs_attr3_leaf_hdr_to_disk(state->args->geo, save_leaf, &savehdr); 237962306a36Sopenharmony_ci xfs_trans_log_buf(state->args->trans, save_blk->bp, 0, 238062306a36Sopenharmony_ci state->args->geo->blksize - 1); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci /* 238362306a36Sopenharmony_ci * Copy out last hashval in each block for B-tree code. 238462306a36Sopenharmony_ci */ 238562306a36Sopenharmony_ci entry = xfs_attr3_leaf_entryp(save_leaf); 238662306a36Sopenharmony_ci save_blk->hashval = be32_to_cpu(entry[savehdr.count - 1].hashval); 238762306a36Sopenharmony_ci} 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci/*======================================================================== 239062306a36Sopenharmony_ci * Routines used for finding things in the Btree. 239162306a36Sopenharmony_ci *========================================================================*/ 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci/* 239462306a36Sopenharmony_ci * Look up a name in a leaf attribute list structure. 239562306a36Sopenharmony_ci * This is the internal routine, it uses the caller's buffer. 239662306a36Sopenharmony_ci * 239762306a36Sopenharmony_ci * Note that duplicate keys are allowed, but only check within the 239862306a36Sopenharmony_ci * current leaf node. The Btree code must check in adjacent leaf nodes. 239962306a36Sopenharmony_ci * 240062306a36Sopenharmony_ci * Return in args->index the index into the entry[] array of either 240162306a36Sopenharmony_ci * the found entry, or where the entry should have been (insert before 240262306a36Sopenharmony_ci * that entry). 240362306a36Sopenharmony_ci * 240462306a36Sopenharmony_ci * Don't change the args->value unless we find the attribute. 240562306a36Sopenharmony_ci */ 240662306a36Sopenharmony_ciint 240762306a36Sopenharmony_cixfs_attr3_leaf_lookup_int( 240862306a36Sopenharmony_ci struct xfs_buf *bp, 240962306a36Sopenharmony_ci struct xfs_da_args *args) 241062306a36Sopenharmony_ci{ 241162306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 241262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 241362306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 241462306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 241562306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *name_loc; 241662306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 241762306a36Sopenharmony_ci xfs_dahash_t hashval; 241862306a36Sopenharmony_ci int probe; 241962306a36Sopenharmony_ci int span; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci trace_xfs_attr_leaf_lookup(args); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci leaf = bp->b_addr; 242462306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 242562306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 242662306a36Sopenharmony_ci if (ichdr.count >= args->geo->blksize / 8) { 242762306a36Sopenharmony_ci xfs_buf_mark_corrupt(bp); 242862306a36Sopenharmony_ci return -EFSCORRUPTED; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci /* 243262306a36Sopenharmony_ci * Binary search. (note: small blocks will skip this loop) 243362306a36Sopenharmony_ci */ 243462306a36Sopenharmony_ci hashval = args->hashval; 243562306a36Sopenharmony_ci probe = span = ichdr.count / 2; 243662306a36Sopenharmony_ci for (entry = &entries[probe]; span > 4; entry = &entries[probe]) { 243762306a36Sopenharmony_ci span /= 2; 243862306a36Sopenharmony_ci if (be32_to_cpu(entry->hashval) < hashval) 243962306a36Sopenharmony_ci probe += span; 244062306a36Sopenharmony_ci else if (be32_to_cpu(entry->hashval) > hashval) 244162306a36Sopenharmony_ci probe -= span; 244262306a36Sopenharmony_ci else 244362306a36Sopenharmony_ci break; 244462306a36Sopenharmony_ci } 244562306a36Sopenharmony_ci if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count))) { 244662306a36Sopenharmony_ci xfs_buf_mark_corrupt(bp); 244762306a36Sopenharmony_ci return -EFSCORRUPTED; 244862306a36Sopenharmony_ci } 244962306a36Sopenharmony_ci if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval)) { 245062306a36Sopenharmony_ci xfs_buf_mark_corrupt(bp); 245162306a36Sopenharmony_ci return -EFSCORRUPTED; 245262306a36Sopenharmony_ci } 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci /* 245562306a36Sopenharmony_ci * Since we may have duplicate hashval's, find the first matching 245662306a36Sopenharmony_ci * hashval in the leaf. 245762306a36Sopenharmony_ci */ 245862306a36Sopenharmony_ci while (probe > 0 && be32_to_cpu(entry->hashval) >= hashval) { 245962306a36Sopenharmony_ci entry--; 246062306a36Sopenharmony_ci probe--; 246162306a36Sopenharmony_ci } 246262306a36Sopenharmony_ci while (probe < ichdr.count && 246362306a36Sopenharmony_ci be32_to_cpu(entry->hashval) < hashval) { 246462306a36Sopenharmony_ci entry++; 246562306a36Sopenharmony_ci probe++; 246662306a36Sopenharmony_ci } 246762306a36Sopenharmony_ci if (probe == ichdr.count || be32_to_cpu(entry->hashval) != hashval) { 246862306a36Sopenharmony_ci args->index = probe; 246962306a36Sopenharmony_ci return -ENOATTR; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci /* 247362306a36Sopenharmony_ci * Duplicate keys may be present, so search all of them for a match. 247462306a36Sopenharmony_ci */ 247562306a36Sopenharmony_ci for (; probe < ichdr.count && (be32_to_cpu(entry->hashval) == hashval); 247662306a36Sopenharmony_ci entry++, probe++) { 247762306a36Sopenharmony_ci/* 247862306a36Sopenharmony_ci * GROT: Add code to remove incomplete entries. 247962306a36Sopenharmony_ci */ 248062306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_LOCAL) { 248162306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, probe); 248262306a36Sopenharmony_ci if (!xfs_attr_match(args, name_loc->namelen, 248362306a36Sopenharmony_ci name_loc->nameval, entry->flags)) 248462306a36Sopenharmony_ci continue; 248562306a36Sopenharmony_ci args->index = probe; 248662306a36Sopenharmony_ci return -EEXIST; 248762306a36Sopenharmony_ci } else { 248862306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, probe); 248962306a36Sopenharmony_ci if (!xfs_attr_match(args, name_rmt->namelen, 249062306a36Sopenharmony_ci name_rmt->name, entry->flags)) 249162306a36Sopenharmony_ci continue; 249262306a36Sopenharmony_ci args->index = probe; 249362306a36Sopenharmony_ci args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); 249462306a36Sopenharmony_ci args->rmtblkno = be32_to_cpu(name_rmt->valueblk); 249562306a36Sopenharmony_ci args->rmtblkcnt = xfs_attr3_rmt_blocks( 249662306a36Sopenharmony_ci args->dp->i_mount, 249762306a36Sopenharmony_ci args->rmtvaluelen); 249862306a36Sopenharmony_ci return -EEXIST; 249962306a36Sopenharmony_ci } 250062306a36Sopenharmony_ci } 250162306a36Sopenharmony_ci args->index = probe; 250262306a36Sopenharmony_ci return -ENOATTR; 250362306a36Sopenharmony_ci} 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci/* 250662306a36Sopenharmony_ci * Get the value associated with an attribute name from a leaf attribute 250762306a36Sopenharmony_ci * list structure. 250862306a36Sopenharmony_ci * 250962306a36Sopenharmony_ci * If args->valuelen is zero, only the length needs to be returned. Unlike a 251062306a36Sopenharmony_ci * lookup, we only return an error if the attribute does not exist or we can't 251162306a36Sopenharmony_ci * retrieve the value. 251262306a36Sopenharmony_ci */ 251362306a36Sopenharmony_ciint 251462306a36Sopenharmony_cixfs_attr3_leaf_getvalue( 251562306a36Sopenharmony_ci struct xfs_buf *bp, 251662306a36Sopenharmony_ci struct xfs_da_args *args) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 251962306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 252062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 252162306a36Sopenharmony_ci struct xfs_attr_leaf_name_local *name_loc; 252262306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci leaf = bp->b_addr; 252562306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 252662306a36Sopenharmony_ci ASSERT(ichdr.count < args->geo->blksize / 8); 252762306a36Sopenharmony_ci ASSERT(args->index < ichdr.count); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; 253062306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_LOCAL) { 253162306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, args->index); 253262306a36Sopenharmony_ci ASSERT(name_loc->namelen == args->namelen); 253362306a36Sopenharmony_ci ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0); 253462306a36Sopenharmony_ci return xfs_attr_copy_value(args, 253562306a36Sopenharmony_ci &name_loc->nameval[args->namelen], 253662306a36Sopenharmony_ci be16_to_cpu(name_loc->valuelen)); 253762306a36Sopenharmony_ci } 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); 254062306a36Sopenharmony_ci ASSERT(name_rmt->namelen == args->namelen); 254162306a36Sopenharmony_ci ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0); 254262306a36Sopenharmony_ci args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); 254362306a36Sopenharmony_ci args->rmtblkno = be32_to_cpu(name_rmt->valueblk); 254462306a36Sopenharmony_ci args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount, 254562306a36Sopenharmony_ci args->rmtvaluelen); 254662306a36Sopenharmony_ci return xfs_attr_copy_value(args, NULL, args->rmtvaluelen); 254762306a36Sopenharmony_ci} 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci/*======================================================================== 255062306a36Sopenharmony_ci * Utility routines. 255162306a36Sopenharmony_ci *========================================================================*/ 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci/* 255462306a36Sopenharmony_ci * Move the indicated entries from one leaf to another. 255562306a36Sopenharmony_ci * NOTE: this routine modifies both source and destination leaves. 255662306a36Sopenharmony_ci */ 255762306a36Sopenharmony_ci/*ARGSUSED*/ 255862306a36Sopenharmony_ciSTATIC void 255962306a36Sopenharmony_cixfs_attr3_leaf_moveents( 256062306a36Sopenharmony_ci struct xfs_da_args *args, 256162306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf_s, 256262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr_s, 256362306a36Sopenharmony_ci int start_s, 256462306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf_d, 256562306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr *ichdr_d, 256662306a36Sopenharmony_ci int start_d, 256762306a36Sopenharmony_ci int count) 256862306a36Sopenharmony_ci{ 256962306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry_s; 257062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry_d; 257162306a36Sopenharmony_ci int desti; 257262306a36Sopenharmony_ci int tmp; 257362306a36Sopenharmony_ci int i; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* 257662306a36Sopenharmony_ci * Check for nothing to do. 257762306a36Sopenharmony_ci */ 257862306a36Sopenharmony_ci if (count == 0) 257962306a36Sopenharmony_ci return; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci /* 258262306a36Sopenharmony_ci * Set up environment. 258362306a36Sopenharmony_ci */ 258462306a36Sopenharmony_ci ASSERT(ichdr_s->magic == XFS_ATTR_LEAF_MAGIC || 258562306a36Sopenharmony_ci ichdr_s->magic == XFS_ATTR3_LEAF_MAGIC); 258662306a36Sopenharmony_ci ASSERT(ichdr_s->magic == ichdr_d->magic); 258762306a36Sopenharmony_ci ASSERT(ichdr_s->count > 0 && ichdr_s->count < args->geo->blksize / 8); 258862306a36Sopenharmony_ci ASSERT(ichdr_s->firstused >= (ichdr_s->count * sizeof(*entry_s)) 258962306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf_s)); 259062306a36Sopenharmony_ci ASSERT(ichdr_d->count < args->geo->blksize / 8); 259162306a36Sopenharmony_ci ASSERT(ichdr_d->firstused >= (ichdr_d->count * sizeof(*entry_d)) 259262306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf_d)); 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci ASSERT(start_s < ichdr_s->count); 259562306a36Sopenharmony_ci ASSERT(start_d <= ichdr_d->count); 259662306a36Sopenharmony_ci ASSERT(count <= ichdr_s->count); 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci /* 260062306a36Sopenharmony_ci * Move the entries in the destination leaf up to make a hole? 260162306a36Sopenharmony_ci */ 260262306a36Sopenharmony_ci if (start_d < ichdr_d->count) { 260362306a36Sopenharmony_ci tmp = ichdr_d->count - start_d; 260462306a36Sopenharmony_ci tmp *= sizeof(xfs_attr_leaf_entry_t); 260562306a36Sopenharmony_ci entry_s = &xfs_attr3_leaf_entryp(leaf_d)[start_d]; 260662306a36Sopenharmony_ci entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d + count]; 260762306a36Sopenharmony_ci memmove(entry_d, entry_s, tmp); 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci /* 261162306a36Sopenharmony_ci * Copy all entry's in the same (sorted) order, 261262306a36Sopenharmony_ci * but allocate attribute info packed and in sequence. 261362306a36Sopenharmony_ci */ 261462306a36Sopenharmony_ci entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; 261562306a36Sopenharmony_ci entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d]; 261662306a36Sopenharmony_ci desti = start_d; 261762306a36Sopenharmony_ci for (i = 0; i < count; entry_s++, entry_d++, desti++, i++) { 261862306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry_s->nameidx) >= ichdr_s->firstused); 261962306a36Sopenharmony_ci tmp = xfs_attr_leaf_entsize(leaf_s, start_s + i); 262062306a36Sopenharmony_ci#ifdef GROT 262162306a36Sopenharmony_ci /* 262262306a36Sopenharmony_ci * Code to drop INCOMPLETE entries. Difficult to use as we 262362306a36Sopenharmony_ci * may also need to change the insertion index. Code turned 262462306a36Sopenharmony_ci * off for 6.2, should be revisited later. 262562306a36Sopenharmony_ci */ 262662306a36Sopenharmony_ci if (entry_s->flags & XFS_ATTR_INCOMPLETE) { /* skip partials? */ 262762306a36Sopenharmony_ci memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp); 262862306a36Sopenharmony_ci ichdr_s->usedbytes -= tmp; 262962306a36Sopenharmony_ci ichdr_s->count -= 1; 263062306a36Sopenharmony_ci entry_d--; /* to compensate for ++ in loop hdr */ 263162306a36Sopenharmony_ci desti--; 263262306a36Sopenharmony_ci if ((start_s + i) < offset) 263362306a36Sopenharmony_ci result++; /* insertion index adjustment */ 263462306a36Sopenharmony_ci } else { 263562306a36Sopenharmony_ci#endif /* GROT */ 263662306a36Sopenharmony_ci ichdr_d->firstused -= tmp; 263762306a36Sopenharmony_ci /* both on-disk, don't endian flip twice */ 263862306a36Sopenharmony_ci entry_d->hashval = entry_s->hashval; 263962306a36Sopenharmony_ci entry_d->nameidx = cpu_to_be16(ichdr_d->firstused); 264062306a36Sopenharmony_ci entry_d->flags = entry_s->flags; 264162306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry_d->nameidx) + tmp 264262306a36Sopenharmony_ci <= args->geo->blksize); 264362306a36Sopenharmony_ci memmove(xfs_attr3_leaf_name(leaf_d, desti), 264462306a36Sopenharmony_ci xfs_attr3_leaf_name(leaf_s, start_s + i), tmp); 264562306a36Sopenharmony_ci ASSERT(be16_to_cpu(entry_s->nameidx) + tmp 264662306a36Sopenharmony_ci <= args->geo->blksize); 264762306a36Sopenharmony_ci memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp); 264862306a36Sopenharmony_ci ichdr_s->usedbytes -= tmp; 264962306a36Sopenharmony_ci ichdr_d->usedbytes += tmp; 265062306a36Sopenharmony_ci ichdr_s->count -= 1; 265162306a36Sopenharmony_ci ichdr_d->count += 1; 265262306a36Sopenharmony_ci tmp = ichdr_d->count * sizeof(xfs_attr_leaf_entry_t) 265362306a36Sopenharmony_ci + xfs_attr3_leaf_hdr_size(leaf_d); 265462306a36Sopenharmony_ci ASSERT(ichdr_d->firstused >= tmp); 265562306a36Sopenharmony_ci#ifdef GROT 265662306a36Sopenharmony_ci } 265762306a36Sopenharmony_ci#endif /* GROT */ 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci /* 266162306a36Sopenharmony_ci * Zero out the entries we just copied. 266262306a36Sopenharmony_ci */ 266362306a36Sopenharmony_ci if (start_s == ichdr_s->count) { 266462306a36Sopenharmony_ci tmp = count * sizeof(xfs_attr_leaf_entry_t); 266562306a36Sopenharmony_ci entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; 266662306a36Sopenharmony_ci ASSERT(((char *)entry_s + tmp) <= 266762306a36Sopenharmony_ci ((char *)leaf_s + args->geo->blksize)); 266862306a36Sopenharmony_ci memset(entry_s, 0, tmp); 266962306a36Sopenharmony_ci } else { 267062306a36Sopenharmony_ci /* 267162306a36Sopenharmony_ci * Move the remaining entries down to fill the hole, 267262306a36Sopenharmony_ci * then zero the entries at the top. 267362306a36Sopenharmony_ci */ 267462306a36Sopenharmony_ci tmp = (ichdr_s->count - count) * sizeof(xfs_attr_leaf_entry_t); 267562306a36Sopenharmony_ci entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s + count]; 267662306a36Sopenharmony_ci entry_d = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; 267762306a36Sopenharmony_ci memmove(entry_d, entry_s, tmp); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci tmp = count * sizeof(xfs_attr_leaf_entry_t); 268062306a36Sopenharmony_ci entry_s = &xfs_attr3_leaf_entryp(leaf_s)[ichdr_s->count]; 268162306a36Sopenharmony_ci ASSERT(((char *)entry_s + tmp) <= 268262306a36Sopenharmony_ci ((char *)leaf_s + args->geo->blksize)); 268362306a36Sopenharmony_ci memset(entry_s, 0, tmp); 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci /* 268762306a36Sopenharmony_ci * Fill in the freemap information 268862306a36Sopenharmony_ci */ 268962306a36Sopenharmony_ci ichdr_d->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_d); 269062306a36Sopenharmony_ci ichdr_d->freemap[0].base += ichdr_d->count * sizeof(xfs_attr_leaf_entry_t); 269162306a36Sopenharmony_ci ichdr_d->freemap[0].size = ichdr_d->firstused - ichdr_d->freemap[0].base; 269262306a36Sopenharmony_ci ichdr_d->freemap[1].base = 0; 269362306a36Sopenharmony_ci ichdr_d->freemap[2].base = 0; 269462306a36Sopenharmony_ci ichdr_d->freemap[1].size = 0; 269562306a36Sopenharmony_ci ichdr_d->freemap[2].size = 0; 269662306a36Sopenharmony_ci ichdr_s->holes = 1; /* leaf may not be compact */ 269762306a36Sopenharmony_ci} 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci/* 270062306a36Sopenharmony_ci * Pick up the last hashvalue from a leaf block. 270162306a36Sopenharmony_ci */ 270262306a36Sopenharmony_cixfs_dahash_t 270362306a36Sopenharmony_cixfs_attr_leaf_lasthash( 270462306a36Sopenharmony_ci struct xfs_buf *bp, 270562306a36Sopenharmony_ci int *count) 270662306a36Sopenharmony_ci{ 270762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 270862306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 270962306a36Sopenharmony_ci struct xfs_mount *mp = bp->b_mount; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, bp->b_addr); 271262306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(bp->b_addr); 271362306a36Sopenharmony_ci if (count) 271462306a36Sopenharmony_ci *count = ichdr.count; 271562306a36Sopenharmony_ci if (!ichdr.count) 271662306a36Sopenharmony_ci return 0; 271762306a36Sopenharmony_ci return be32_to_cpu(entries[ichdr.count - 1].hashval); 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci/* 272162306a36Sopenharmony_ci * Calculate the number of bytes used to store the indicated attribute 272262306a36Sopenharmony_ci * (whether local or remote only calculate bytes in this block). 272362306a36Sopenharmony_ci */ 272462306a36Sopenharmony_ciSTATIC int 272562306a36Sopenharmony_cixfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index) 272662306a36Sopenharmony_ci{ 272762306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entries; 272862306a36Sopenharmony_ci xfs_attr_leaf_name_local_t *name_loc; 272962306a36Sopenharmony_ci xfs_attr_leaf_name_remote_t *name_rmt; 273062306a36Sopenharmony_ci int size; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci entries = xfs_attr3_leaf_entryp(leaf); 273362306a36Sopenharmony_ci if (entries[index].flags & XFS_ATTR_LOCAL) { 273462306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, index); 273562306a36Sopenharmony_ci size = xfs_attr_leaf_entsize_local(name_loc->namelen, 273662306a36Sopenharmony_ci be16_to_cpu(name_loc->valuelen)); 273762306a36Sopenharmony_ci } else { 273862306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, index); 273962306a36Sopenharmony_ci size = xfs_attr_leaf_entsize_remote(name_rmt->namelen); 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci return size; 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci/* 274562306a36Sopenharmony_ci * Calculate the number of bytes that would be required to store the new 274662306a36Sopenharmony_ci * attribute (whether local or remote only calculate bytes in this block). 274762306a36Sopenharmony_ci * This routine decides as a side effect whether the attribute will be 274862306a36Sopenharmony_ci * a "local" or a "remote" attribute. 274962306a36Sopenharmony_ci */ 275062306a36Sopenharmony_ciint 275162306a36Sopenharmony_cixfs_attr_leaf_newentsize( 275262306a36Sopenharmony_ci struct xfs_da_args *args, 275362306a36Sopenharmony_ci int *local) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci int size; 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci size = xfs_attr_leaf_entsize_local(args->namelen, args->valuelen); 275862306a36Sopenharmony_ci if (size < xfs_attr_leaf_entsize_local_max(args->geo->blksize)) { 275962306a36Sopenharmony_ci if (local) 276062306a36Sopenharmony_ci *local = 1; 276162306a36Sopenharmony_ci return size; 276262306a36Sopenharmony_ci } 276362306a36Sopenharmony_ci if (local) 276462306a36Sopenharmony_ci *local = 0; 276562306a36Sopenharmony_ci return xfs_attr_leaf_entsize_remote(args->namelen); 276662306a36Sopenharmony_ci} 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/*======================================================================== 277062306a36Sopenharmony_ci * Manage the INCOMPLETE flag in a leaf entry 277162306a36Sopenharmony_ci *========================================================================*/ 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci/* 277462306a36Sopenharmony_ci * Clear the INCOMPLETE flag on an entry in a leaf block. 277562306a36Sopenharmony_ci */ 277662306a36Sopenharmony_ciint 277762306a36Sopenharmony_cixfs_attr3_leaf_clearflag( 277862306a36Sopenharmony_ci struct xfs_da_args *args) 277962306a36Sopenharmony_ci{ 278062306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 278162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 278262306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 278362306a36Sopenharmony_ci struct xfs_buf *bp; 278462306a36Sopenharmony_ci int error; 278562306a36Sopenharmony_ci#ifdef DEBUG 278662306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 278762306a36Sopenharmony_ci xfs_attr_leaf_name_local_t *name_loc; 278862306a36Sopenharmony_ci int namelen; 278962306a36Sopenharmony_ci char *name; 279062306a36Sopenharmony_ci#endif /* DEBUG */ 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci trace_xfs_attr_leaf_clearflag(args); 279362306a36Sopenharmony_ci /* 279462306a36Sopenharmony_ci * Set up the operation. 279562306a36Sopenharmony_ci */ 279662306a36Sopenharmony_ci error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); 279762306a36Sopenharmony_ci if (error) 279862306a36Sopenharmony_ci return error; 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci leaf = bp->b_addr; 280162306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; 280262306a36Sopenharmony_ci ASSERT(entry->flags & XFS_ATTR_INCOMPLETE); 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci#ifdef DEBUG 280562306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 280662306a36Sopenharmony_ci ASSERT(args->index < ichdr.count); 280762306a36Sopenharmony_ci ASSERT(args->index >= 0); 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci if (entry->flags & XFS_ATTR_LOCAL) { 281062306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf, args->index); 281162306a36Sopenharmony_ci namelen = name_loc->namelen; 281262306a36Sopenharmony_ci name = (char *)name_loc->nameval; 281362306a36Sopenharmony_ci } else { 281462306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); 281562306a36Sopenharmony_ci namelen = name_rmt->namelen; 281662306a36Sopenharmony_ci name = (char *)name_rmt->name; 281762306a36Sopenharmony_ci } 281862306a36Sopenharmony_ci ASSERT(be32_to_cpu(entry->hashval) == args->hashval); 281962306a36Sopenharmony_ci ASSERT(namelen == args->namelen); 282062306a36Sopenharmony_ci ASSERT(memcmp(name, args->name, namelen) == 0); 282162306a36Sopenharmony_ci#endif /* DEBUG */ 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci entry->flags &= ~XFS_ATTR_INCOMPLETE; 282462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 282562306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci if (args->rmtblkno) { 282862306a36Sopenharmony_ci ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0); 282962306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); 283062306a36Sopenharmony_ci name_rmt->valueblk = cpu_to_be32(args->rmtblkno); 283162306a36Sopenharmony_ci name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); 283262306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 283362306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); 283462306a36Sopenharmony_ci } 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci return 0; 283762306a36Sopenharmony_ci} 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci/* 284062306a36Sopenharmony_ci * Set the INCOMPLETE flag on an entry in a leaf block. 284162306a36Sopenharmony_ci */ 284262306a36Sopenharmony_ciint 284362306a36Sopenharmony_cixfs_attr3_leaf_setflag( 284462306a36Sopenharmony_ci struct xfs_da_args *args) 284562306a36Sopenharmony_ci{ 284662306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf; 284762306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry; 284862306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 284962306a36Sopenharmony_ci struct xfs_buf *bp; 285062306a36Sopenharmony_ci int error; 285162306a36Sopenharmony_ci#ifdef DEBUG 285262306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr; 285362306a36Sopenharmony_ci#endif 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci trace_xfs_attr_leaf_setflag(args); 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci /* 285862306a36Sopenharmony_ci * Set up the operation. 285962306a36Sopenharmony_ci */ 286062306a36Sopenharmony_ci error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); 286162306a36Sopenharmony_ci if (error) 286262306a36Sopenharmony_ci return error; 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci leaf = bp->b_addr; 286562306a36Sopenharmony_ci#ifdef DEBUG 286662306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); 286762306a36Sopenharmony_ci ASSERT(args->index < ichdr.count); 286862306a36Sopenharmony_ci ASSERT(args->index >= 0); 286962306a36Sopenharmony_ci#endif 287062306a36Sopenharmony_ci entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0); 287362306a36Sopenharmony_ci entry->flags |= XFS_ATTR_INCOMPLETE; 287462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 287562306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); 287662306a36Sopenharmony_ci if ((entry->flags & XFS_ATTR_LOCAL) == 0) { 287762306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); 287862306a36Sopenharmony_ci name_rmt->valueblk = 0; 287962306a36Sopenharmony_ci name_rmt->valuelen = 0; 288062306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp, 288162306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci return 0; 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci/* 288862306a36Sopenharmony_ci * In a single transaction, clear the INCOMPLETE flag on the leaf entry 288962306a36Sopenharmony_ci * given by args->blkno/index and set the INCOMPLETE flag on the leaf 289062306a36Sopenharmony_ci * entry given by args->blkno2/index2. 289162306a36Sopenharmony_ci * 289262306a36Sopenharmony_ci * Note that they could be in different blocks, or in the same block. 289362306a36Sopenharmony_ci */ 289462306a36Sopenharmony_ciint 289562306a36Sopenharmony_cixfs_attr3_leaf_flipflags( 289662306a36Sopenharmony_ci struct xfs_da_args *args) 289762306a36Sopenharmony_ci{ 289862306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf1; 289962306a36Sopenharmony_ci struct xfs_attr_leafblock *leaf2; 290062306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry1; 290162306a36Sopenharmony_ci struct xfs_attr_leaf_entry *entry2; 290262306a36Sopenharmony_ci struct xfs_attr_leaf_name_remote *name_rmt; 290362306a36Sopenharmony_ci struct xfs_buf *bp1; 290462306a36Sopenharmony_ci struct xfs_buf *bp2; 290562306a36Sopenharmony_ci int error; 290662306a36Sopenharmony_ci#ifdef DEBUG 290762306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr1; 290862306a36Sopenharmony_ci struct xfs_attr3_icleaf_hdr ichdr2; 290962306a36Sopenharmony_ci xfs_attr_leaf_name_local_t *name_loc; 291062306a36Sopenharmony_ci int namelen1, namelen2; 291162306a36Sopenharmony_ci char *name1, *name2; 291262306a36Sopenharmony_ci#endif /* DEBUG */ 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci trace_xfs_attr_leaf_flipflags(args); 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci /* 291762306a36Sopenharmony_ci * Read the block containing the "old" attr 291862306a36Sopenharmony_ci */ 291962306a36Sopenharmony_ci error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp1); 292062306a36Sopenharmony_ci if (error) 292162306a36Sopenharmony_ci return error; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci /* 292462306a36Sopenharmony_ci * Read the block containing the "new" attr, if it is different 292562306a36Sopenharmony_ci */ 292662306a36Sopenharmony_ci if (args->blkno2 != args->blkno) { 292762306a36Sopenharmony_ci error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno2, 292862306a36Sopenharmony_ci &bp2); 292962306a36Sopenharmony_ci if (error) 293062306a36Sopenharmony_ci return error; 293162306a36Sopenharmony_ci } else { 293262306a36Sopenharmony_ci bp2 = bp1; 293362306a36Sopenharmony_ci } 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci leaf1 = bp1->b_addr; 293662306a36Sopenharmony_ci entry1 = &xfs_attr3_leaf_entryp(leaf1)[args->index]; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci leaf2 = bp2->b_addr; 293962306a36Sopenharmony_ci entry2 = &xfs_attr3_leaf_entryp(leaf2)[args->index2]; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci#ifdef DEBUG 294262306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr1, leaf1); 294362306a36Sopenharmony_ci ASSERT(args->index < ichdr1.count); 294462306a36Sopenharmony_ci ASSERT(args->index >= 0); 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr2, leaf2); 294762306a36Sopenharmony_ci ASSERT(args->index2 < ichdr2.count); 294862306a36Sopenharmony_ci ASSERT(args->index2 >= 0); 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci if (entry1->flags & XFS_ATTR_LOCAL) { 295162306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf1, args->index); 295262306a36Sopenharmony_ci namelen1 = name_loc->namelen; 295362306a36Sopenharmony_ci name1 = (char *)name_loc->nameval; 295462306a36Sopenharmony_ci } else { 295562306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index); 295662306a36Sopenharmony_ci namelen1 = name_rmt->namelen; 295762306a36Sopenharmony_ci name1 = (char *)name_rmt->name; 295862306a36Sopenharmony_ci } 295962306a36Sopenharmony_ci if (entry2->flags & XFS_ATTR_LOCAL) { 296062306a36Sopenharmony_ci name_loc = xfs_attr3_leaf_name_local(leaf2, args->index2); 296162306a36Sopenharmony_ci namelen2 = name_loc->namelen; 296262306a36Sopenharmony_ci name2 = (char *)name_loc->nameval; 296362306a36Sopenharmony_ci } else { 296462306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2); 296562306a36Sopenharmony_ci namelen2 = name_rmt->namelen; 296662306a36Sopenharmony_ci name2 = (char *)name_rmt->name; 296762306a36Sopenharmony_ci } 296862306a36Sopenharmony_ci ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval)); 296962306a36Sopenharmony_ci ASSERT(namelen1 == namelen2); 297062306a36Sopenharmony_ci ASSERT(memcmp(name1, name2, namelen1) == 0); 297162306a36Sopenharmony_ci#endif /* DEBUG */ 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE); 297462306a36Sopenharmony_ci ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0); 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci entry1->flags &= ~XFS_ATTR_INCOMPLETE; 297762306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp1, 297862306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1))); 297962306a36Sopenharmony_ci if (args->rmtblkno) { 298062306a36Sopenharmony_ci ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0); 298162306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index); 298262306a36Sopenharmony_ci name_rmt->valueblk = cpu_to_be32(args->rmtblkno); 298362306a36Sopenharmony_ci name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); 298462306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp1, 298562306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt))); 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci entry2->flags |= XFS_ATTR_INCOMPLETE; 298962306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp2, 299062306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2))); 299162306a36Sopenharmony_ci if ((entry2->flags & XFS_ATTR_LOCAL) == 0) { 299262306a36Sopenharmony_ci name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2); 299362306a36Sopenharmony_ci name_rmt->valueblk = 0; 299462306a36Sopenharmony_ci name_rmt->valuelen = 0; 299562306a36Sopenharmony_ci xfs_trans_log_buf(args->trans, bp2, 299662306a36Sopenharmony_ci XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt))); 299762306a36Sopenharmony_ci } 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci return 0; 300062306a36Sopenharmony_ci} 3001