diff options
author | Dave Chinner <dchinner@redhat.com> | 2014-06-25 06:57:53 +0200 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-06-25 06:57:53 +0200 |
commit | 30f712c9dd69348aa51351d5cb6d366bf4fae31d (patch) | |
tree | 9b246ca3986b784d8f9f6841466dfd4c90909e47 /fs/xfs/xfs_attr_leaf.c | |
parent | libxfs: move header files (diff) | |
download | linux-30f712c9dd69348aa51351d5cb6d366bf4fae31d.tar.xz linux-30f712c9dd69348aa51351d5cb6d366bf4fae31d.zip |
libxfs: move source files
Move all the source files that are shared with userspace into
libxfs/. This is done as one big chunk simpy to get it done
quickly
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs/xfs_attr_leaf.c')
-rw-r--r-- | fs/xfs/xfs_attr_leaf.c | 2697 |
1 files changed, 0 insertions, 2697 deletions
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c deleted file mode 100644 index 127d96aba845..000000000000 --- a/fs/xfs/xfs_attr_leaf.c +++ /dev/null @@ -1,2697 +0,0 @@ -/* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. - * Copyright (c) 2013 Red Hat, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_shared.h" -#include "xfs_format.h" -#include "xfs_log_format.h" -#include "xfs_trans_resv.h" -#include "xfs_bit.h" -#include "xfs_sb.h" -#include "xfs_ag.h" -#include "xfs_mount.h" -#include "xfs_da_format.h" -#include "xfs_da_btree.h" -#include "xfs_inode.h" -#include "xfs_trans.h" -#include "xfs_inode_item.h" -#include "xfs_bmap_btree.h" -#include "xfs_bmap.h" -#include "xfs_attr_sf.h" -#include "xfs_attr_remote.h" -#include "xfs_attr.h" -#include "xfs_attr_leaf.h" -#include "xfs_error.h" -#include "xfs_trace.h" -#include "xfs_buf_item.h" -#include "xfs_cksum.h" -#include "xfs_dinode.h" -#include "xfs_dir2.h" - - -/* - * xfs_attr_leaf.c - * - * Routines to implement leaf blocks of attributes as Btrees of hashed names. - */ - -/*======================================================================== - * Function prototypes for the kernel. - *========================================================================*/ - -/* - * Routines used for growing the Btree. - */ -STATIC int xfs_attr3_leaf_create(struct xfs_da_args *args, - xfs_dablk_t which_block, struct xfs_buf **bpp); -STATIC int xfs_attr3_leaf_add_work(struct xfs_buf *leaf_buffer, - struct xfs_attr3_icleaf_hdr *ichdr, - struct xfs_da_args *args, int freemap_index); -STATIC void xfs_attr3_leaf_compact(struct xfs_da_args *args, - struct xfs_attr3_icleaf_hdr *ichdr, - struct xfs_buf *leaf_buffer); -STATIC void xfs_attr3_leaf_rebalance(xfs_da_state_t *state, - xfs_da_state_blk_t *blk1, - xfs_da_state_blk_t *blk2); -STATIC int xfs_attr3_leaf_figure_balance(xfs_da_state_t *state, - xfs_da_state_blk_t *leaf_blk_1, - struct xfs_attr3_icleaf_hdr *ichdr1, - xfs_da_state_blk_t *leaf_blk_2, - struct xfs_attr3_icleaf_hdr *ichdr2, - int *number_entries_in_blk1, - int *number_usedbytes_in_blk1); - -/* - * Utility routines. - */ -STATIC void xfs_attr3_leaf_moveents(struct xfs_da_args *args, - struct xfs_attr_leafblock *src_leaf, - struct xfs_attr3_icleaf_hdr *src_ichdr, int src_start, - struct xfs_attr_leafblock *dst_leaf, - struct xfs_attr3_icleaf_hdr *dst_ichdr, int dst_start, - int move_count); -STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index); - -void -xfs_attr3_leaf_hdr_from_disk( - struct xfs_attr3_icleaf_hdr *to, - struct xfs_attr_leafblock *from) -{ - int i; - - ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) || - from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)); - - if (from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) { - struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)from; - - to->forw = be32_to_cpu(hdr3->info.hdr.forw); - to->back = be32_to_cpu(hdr3->info.hdr.back); - to->magic = be16_to_cpu(hdr3->info.hdr.magic); - to->count = be16_to_cpu(hdr3->count); - to->usedbytes = be16_to_cpu(hdr3->usedbytes); - to->firstused = be16_to_cpu(hdr3->firstused); - to->holes = hdr3->holes; - - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - to->freemap[i].base = be16_to_cpu(hdr3->freemap[i].base); - to->freemap[i].size = be16_to_cpu(hdr3->freemap[i].size); - } - return; - } - to->forw = be32_to_cpu(from->hdr.info.forw); - to->back = be32_to_cpu(from->hdr.info.back); - to->magic = be16_to_cpu(from->hdr.info.magic); - to->count = be16_to_cpu(from->hdr.count); - to->usedbytes = be16_to_cpu(from->hdr.usedbytes); - to->firstused = be16_to_cpu(from->hdr.firstused); - to->holes = from->hdr.holes; - - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - to->freemap[i].base = be16_to_cpu(from->hdr.freemap[i].base); - to->freemap[i].size = be16_to_cpu(from->hdr.freemap[i].size); - } -} - -void -xfs_attr3_leaf_hdr_to_disk( - struct xfs_attr_leafblock *to, - struct xfs_attr3_icleaf_hdr *from) -{ - int i; - - ASSERT(from->magic == XFS_ATTR_LEAF_MAGIC || - from->magic == XFS_ATTR3_LEAF_MAGIC); - - if (from->magic == XFS_ATTR3_LEAF_MAGIC) { - struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)to; - - hdr3->info.hdr.forw = cpu_to_be32(from->forw); - hdr3->info.hdr.back = cpu_to_be32(from->back); - hdr3->info.hdr.magic = cpu_to_be16(from->magic); - hdr3->count = cpu_to_be16(from->count); - hdr3->usedbytes = cpu_to_be16(from->usedbytes); - hdr3->firstused = cpu_to_be16(from->firstused); - hdr3->holes = from->holes; - hdr3->pad1 = 0; - - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base); - hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size); - } - return; - } - to->hdr.info.forw = cpu_to_be32(from->forw); - to->hdr.info.back = cpu_to_be32(from->back); - to->hdr.info.magic = cpu_to_be16(from->magic); - to->hdr.count = cpu_to_be16(from->count); - to->hdr.usedbytes = cpu_to_be16(from->usedbytes); - to->hdr.firstused = cpu_to_be16(from->firstused); - to->hdr.holes = from->holes; - to->hdr.pad1 = 0; - - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base); - to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size); - } -} - -static bool -xfs_attr3_leaf_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - struct xfs_attr_leafblock *leaf = bp->b_addr; - struct xfs_attr3_icleaf_hdr ichdr; - - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - - if (xfs_sb_version_hascrc(&mp->m_sb)) { - struct xfs_da3_node_hdr *hdr3 = bp->b_addr; - - if (ichdr.magic != XFS_ATTR3_LEAF_MAGIC) - return false; - - if (!uuid_equal(&hdr3->info.uuid, &mp->m_sb.sb_uuid)) - return false; - if (be64_to_cpu(hdr3->info.blkno) != bp->b_bn) - return false; - } else { - if (ichdr.magic != XFS_ATTR_LEAF_MAGIC) - return false; - } - if (ichdr.count == 0) - return false; - - /* XXX: need to range check rest of attr header values */ - /* XXX: hash order check? */ - - return true; -} - -static void -xfs_attr3_leaf_write_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - struct xfs_buf_log_item *bip = bp->b_fspriv; - struct xfs_attr3_leaf_hdr *hdr3 = bp->b_addr; - - if (!xfs_attr3_leaf_verify(bp)) { - xfs_buf_ioerror(bp, EFSCORRUPTED); - xfs_verifier_error(bp); - return; - } - - if (!xfs_sb_version_hascrc(&mp->m_sb)) - return; - - if (bip) - hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn); - - xfs_buf_update_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF); -} - -/* - * leaf/node format detection on trees is sketchy, so a node read can be done on - * leaf level blocks when detection identifies the tree as a node format tree - * incorrectly. In this case, we need to swap the verifier to match the correct - * format of the block being read. - */ -static void -xfs_attr3_leaf_read_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - - if (xfs_sb_version_hascrc(&mp->m_sb) && - !xfs_buf_verify_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF)) - xfs_buf_ioerror(bp, EFSBADCRC); - else if (!xfs_attr3_leaf_verify(bp)) - xfs_buf_ioerror(bp, EFSCORRUPTED); - - if (bp->b_error) - xfs_verifier_error(bp); -} - -const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = { - .verify_read = xfs_attr3_leaf_read_verify, - .verify_write = xfs_attr3_leaf_write_verify, -}; - -int -xfs_attr3_leaf_read( - struct xfs_trans *tp, - struct xfs_inode *dp, - xfs_dablk_t bno, - xfs_daddr_t mappedbno, - struct xfs_buf **bpp) -{ - int err; - - err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp, - XFS_ATTR_FORK, &xfs_attr3_leaf_buf_ops); - if (!err && tp) - xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF); - return err; -} - -/*======================================================================== - * Namespace helper routines - *========================================================================*/ - -/* - * If namespace bits don't match return 0. - * If all match then return 1. - */ -STATIC int -xfs_attr_namesp_match(int arg_flags, int ondisk_flags) -{ - return XFS_ATTR_NSP_ONDISK(ondisk_flags) == XFS_ATTR_NSP_ARGS_TO_ONDISK(arg_flags); -} - - -/*======================================================================== - * External routines when attribute fork size < XFS_LITINO(mp). - *========================================================================*/ - -/* - * Query whether the requested number of additional bytes of extended - * attribute space will be able to fit inline. - * - * Returns zero if not, else the di_forkoff fork offset to be used in the - * literal area for attribute data once the new bytes have been added. - * - * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value; - * special case for dev/uuid inodes, they have fixed size data forks. - */ -int -xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes) -{ - int offset; - int minforkoff; /* lower limit on valid forkoff locations */ - int maxforkoff; /* upper limit on valid forkoff locations */ - int dsize; - xfs_mount_t *mp = dp->i_mount; - - /* rounded down */ - offset = (XFS_LITINO(mp, dp->i_d.di_version) - bytes) >> 3; - - switch (dp->i_d.di_format) { - case XFS_DINODE_FMT_DEV: - minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; - return (offset >= minforkoff) ? minforkoff : 0; - case XFS_DINODE_FMT_UUID: - minforkoff = roundup(sizeof(uuid_t), 8) >> 3; - return (offset >= minforkoff) ? minforkoff : 0; - } - - /* - * If the requested numbers of bytes is smaller or equal to the - * current attribute fork size we can always proceed. - * - * Note that if_bytes in the data fork might actually be larger than - * the current data fork size is due to delalloc extents. In that - * case either the extent count will go down when they are converted - * to real extents, or the delalloc conversion will take care of the - * literal area rebalancing. - */ - if (bytes <= XFS_IFORK_ASIZE(dp)) - return dp->i_d.di_forkoff; - - /* - * For attr2 we can try to move the forkoff if there is space in the - * literal area, but for the old format we are done if there is no - * space in the fixed attribute fork. - */ - if (!(mp->m_flags & XFS_MOUNT_ATTR2)) - return 0; - - dsize = dp->i_df.if_bytes; - - switch (dp->i_d.di_format) { - case XFS_DINODE_FMT_EXTENTS: - /* - * If there is no attr fork and the data fork is extents, - * determine if creating the default attr fork will result - * in the extents form migrating to btree. If so, the - * minimum offset only needs to be the space required for - * the btree root. - */ - if (!dp->i_d.di_forkoff && dp->i_df.if_bytes > - xfs_default_attroffset(dp)) - dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS); - break; - case XFS_DINODE_FMT_BTREE: - /* - * If we have a data btree then keep forkoff if we have one, - * otherwise we are adding a new attr, so then we set - * minforkoff to where the btree root can finish so we have - * plenty of room for attrs - */ - if (dp->i_d.di_forkoff) { - if (offset < dp->i_d.di_forkoff) - return 0; - return dp->i_d.di_forkoff; - } - dsize = XFS_BMAP_BROOT_SPACE(mp, dp->i_df.if_broot); - break; - } - - /* - * A data fork btree root must have space for at least - * MINDBTPTRS key/ptr pairs if the data fork is small or empty. - */ - minforkoff = MAX(dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS)); - minforkoff = roundup(minforkoff, 8) >> 3; - - /* attr fork btree root can have at least this many key/ptr pairs */ - maxforkoff = XFS_LITINO(mp, dp->i_d.di_version) - - XFS_BMDR_SPACE_CALC(MINABTPTRS); - maxforkoff = maxforkoff >> 3; /* rounded down */ - - if (offset >= maxforkoff) - return maxforkoff; - if (offset >= minforkoff) - return offset; - return 0; -} - -/* - * Switch on the ATTR2 superblock bit (implies also FEATURES2) - */ -STATIC void -xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp) -{ - if ((mp->m_flags & XFS_MOUNT_ATTR2) && - !(xfs_sb_version_hasattr2(&mp->m_sb))) { - spin_lock(&mp->m_sb_lock); - if (!xfs_sb_version_hasattr2(&mp->m_sb)) { - xfs_sb_version_addattr2(&mp->m_sb); - spin_unlock(&mp->m_sb_lock); - xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2); - } else - spin_unlock(&mp->m_sb_lock); - } -} - -/* - * Create the initial contents of a shortform attribute list. - */ -void -xfs_attr_shortform_create(xfs_da_args_t *args) -{ - xfs_attr_sf_hdr_t *hdr; - xfs_inode_t *dp; - xfs_ifork_t *ifp; - - trace_xfs_attr_sf_create(args); - - dp = args->dp; - ASSERT(dp != NULL); - ifp = dp->i_afp; - ASSERT(ifp != NULL); - ASSERT(ifp->if_bytes == 0); - if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) { - ifp->if_flags &= ~XFS_IFEXTENTS; /* just in case */ - dp->i_d.di_aformat = XFS_DINODE_FMT_LOCAL; - ifp->if_flags |= XFS_IFINLINE; - } else { - ASSERT(ifp->if_flags & XFS_IFINLINE); - } - xfs_idata_realloc(dp, sizeof(*hdr), XFS_ATTR_FORK); - hdr = (xfs_attr_sf_hdr_t *)ifp->if_u1.if_data; - hdr->count = 0; - hdr->totsize = cpu_to_be16(sizeof(*hdr)); - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); -} - -/* - * Add a name/value pair to the shortform attribute list. - * Overflow from the inode has already been checked for. - */ -void -xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff) -{ - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int i, offset, size; - xfs_mount_t *mp; - xfs_inode_t *dp; - xfs_ifork_t *ifp; - - trace_xfs_attr_sf_add(args); - - dp = args->dp; - mp = dp->i_mount; - dp->i_d.di_forkoff = forkoff; - - ifp = dp->i_afp; - ASSERT(ifp->if_flags & XFS_IFINLINE); - sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { -#ifdef DEBUG - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - ASSERT(0); -#endif - } - - offset = (char *)sfe - (char *)sf; - size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen); - xfs_idata_realloc(dp, size, XFS_ATTR_FORK); - sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - sfe = (xfs_attr_sf_entry_t *)((char *)sf + offset); - - sfe->namelen = args->namelen; - sfe->valuelen = args->valuelen; - sfe->flags = XFS_ATTR_NSP_ARGS_TO_ONDISK(args->flags); - memcpy(sfe->nameval, args->name, args->namelen); - memcpy(&sfe->nameval[args->namelen], args->value, args->valuelen); - sf->hdr.count++; - be16_add_cpu(&sf->hdr.totsize, size); - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); - - xfs_sbversion_add_attr2(mp, args->trans); -} - -/* - * After the last attribute is removed revert to original inode format, - * making all literal area available to the data fork once more. - */ -STATIC void -xfs_attr_fork_reset( - struct xfs_inode *ip, - struct xfs_trans *tp) -{ - xfs_idestroy_fork(ip, XFS_ATTR_FORK); - ip->i_d.di_forkoff = 0; - ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; - - ASSERT(ip->i_d.di_anextents == 0); - ASSERT(ip->i_afp == NULL); - - xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); -} - -/* - * Remove an attribute from the shortform attribute list structure. - */ -int -xfs_attr_shortform_remove(xfs_da_args_t *args) -{ - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int base, size=0, end, totsize, i; - xfs_mount_t *mp; - xfs_inode_t *dp; - - trace_xfs_attr_sf_remove(args); - - dp = args->dp; - mp = dp->i_mount; - base = sizeof(xfs_attr_sf_hdr_t); - sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; - sfe = &sf->list[0]; - end = sf->hdr.count; - for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), - base += size, i++) { - size = XFS_ATTR_SF_ENTSIZE(sfe); - if (sfe->namelen != args->namelen) - continue; - if (memcmp(sfe->nameval, args->name, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - break; - } - if (i == end) - return ENOATTR; - - /* - * Fix up the attribute fork data, covering the hole - */ - end = base + size; - totsize = be16_to_cpu(sf->hdr.totsize); - if (end != totsize) - memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end); - sf->hdr.count--; - be16_add_cpu(&sf->hdr.totsize, -size); - - /* - * Fix up the start offset of the attribute fork - */ - totsize -= size; - if (totsize == sizeof(xfs_attr_sf_hdr_t) && - (mp->m_flags & XFS_MOUNT_ATTR2) && - (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) && - !(args->op_flags & XFS_DA_OP_ADDNAME)) { - xfs_attr_fork_reset(dp, args->trans); - } else { - xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); - dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize); - ASSERT(dp->i_d.di_forkoff); - ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || - (args->op_flags & XFS_DA_OP_ADDNAME) || - !(mp->m_flags & XFS_MOUNT_ATTR2) || - dp->i_d.di_format == XFS_DINODE_FMT_BTREE); - xfs_trans_log_inode(args->trans, dp, - XFS_ILOG_CORE | XFS_ILOG_ADATA); - } - - xfs_sbversion_add_attr2(mp, args->trans); - - return 0; -} - -/* - * Look up a name in a shortform attribute list structure. - */ -/*ARGSUSED*/ -int -xfs_attr_shortform_lookup(xfs_da_args_t *args) -{ - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int i; - xfs_ifork_t *ifp; - - trace_xfs_attr_sf_lookup(args); - - ifp = args->dp->i_afp; - ASSERT(ifp->if_flags & XFS_IFINLINE); - sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; - sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - return EEXIST; - } - return ENOATTR; -} - -/* - * Look up a name in a shortform attribute list structure. - */ -/*ARGSUSED*/ -int -xfs_attr_shortform_getvalue(xfs_da_args_t *args) -{ - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int i; - - ASSERT(args->dp->i_afp->if_flags == XFS_IFINLINE); - sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; - sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { - if (sfe->namelen != args->namelen) - continue; - if (memcmp(args->name, sfe->nameval, args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, sfe->flags)) - continue; - if (args->flags & ATTR_KERNOVAL) { - args->valuelen = sfe->valuelen; - return EEXIST; - } - if (args->valuelen < sfe->valuelen) { - args->valuelen = sfe->valuelen; - return ERANGE; - } - args->valuelen = sfe->valuelen; - memcpy(args->value, &sfe->nameval[args->namelen], - args->valuelen); - return EEXIST; - } - return ENOATTR; -} - -/* - * Convert from using the shortform to the leaf. - */ -int -xfs_attr_shortform_to_leaf(xfs_da_args_t *args) -{ - xfs_inode_t *dp; - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - xfs_da_args_t nargs; - char *tmpbuffer; - int error, i, size; - xfs_dablk_t blkno; - struct xfs_buf *bp; - xfs_ifork_t *ifp; - - trace_xfs_attr_sf_to_leaf(args); - - dp = args->dp; - ifp = dp->i_afp; - sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - size = be16_to_cpu(sf->hdr.totsize); - tmpbuffer = kmem_alloc(size, KM_SLEEP); - ASSERT(tmpbuffer != NULL); - memcpy(tmpbuffer, ifp->if_u1.if_data, size); - sf = (xfs_attr_shortform_t *)tmpbuffer; - - xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); - xfs_bmap_local_to_extents_empty(dp, XFS_ATTR_FORK); - - bp = NULL; - error = xfs_da_grow_inode(args, &blkno); - if (error) { - /* - * If we hit an IO error middle of the transaction inside - * grow_inode(), we may have inconsistent data. Bail out. - */ - if (error == EIO) - goto out; - xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */ - memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */ - goto out; - } - - ASSERT(blkno == 0); - error = xfs_attr3_leaf_create(args, blkno, &bp); - if (error) { - error = xfs_da_shrink_inode(args, 0, bp); - bp = NULL; - if (error) - goto out; - xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */ - memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */ - goto out; - } - - memset((char *)&nargs, 0, sizeof(nargs)); - nargs.dp = dp; - nargs.geo = args->geo; - nargs.firstblock = args->firstblock; - nargs.flist = args->flist; - nargs.total = args->total; - nargs.whichfork = XFS_ATTR_FORK; - nargs.trans = args->trans; - nargs.op_flags = XFS_DA_OP_OKNOENT; - - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; i++) { - nargs.name = sfe->nameval; - nargs.namelen = sfe->namelen; - nargs.value = &sfe->nameval[nargs.namelen]; - nargs.valuelen = sfe->valuelen; - nargs.hashval = xfs_da_hashname(sfe->nameval, - sfe->namelen); - nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags); - error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */ - ASSERT(error == ENOATTR); - error = xfs_attr3_leaf_add(bp, &nargs); - ASSERT(error != ENOSPC); - if (error) - goto out; - sfe = XFS_ATTR_SF_NEXTENTRY(sfe); - } - error = 0; - -out: - kmem_free(tmpbuffer); - return error; -} - -/* - * Check a leaf attribute block to see if all the entries would fit into - * a shortform attribute list. - */ -int -xfs_attr_shortform_allfit( - struct xfs_buf *bp, - struct xfs_inode *dp) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr_leaf_entry *entry; - xfs_attr_leaf_name_local_t *name_loc; - struct xfs_attr3_icleaf_hdr leafhdr; - int bytes; - int i; - - leaf = bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf); - entry = xfs_attr3_leaf_entryp(leaf); - - bytes = sizeof(struct xfs_attr_sf_hdr); - for (i = 0; i < leafhdr.count; entry++, i++) { - if (entry->flags & XFS_ATTR_INCOMPLETE) - continue; /* don't copy partial entries */ - if (!(entry->flags & XFS_ATTR_LOCAL)) - return 0; - name_loc = xfs_attr3_leaf_name_local(leaf, i); - if (name_loc->namelen >= XFS_ATTR_SF_ENTSIZE_MAX) - return 0; - if (be16_to_cpu(name_loc->valuelen) >= XFS_ATTR_SF_ENTSIZE_MAX) - return 0; - bytes += sizeof(struct xfs_attr_sf_entry) - 1 - + name_loc->namelen - + be16_to_cpu(name_loc->valuelen); - } - if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) && - (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) && - (bytes == sizeof(struct xfs_attr_sf_hdr))) - return -1; - return xfs_attr_shortform_bytesfit(dp, bytes); -} - -/* - * Convert a leaf attribute list to shortform attribute list - */ -int -xfs_attr3_leaf_to_shortform( - struct xfs_buf *bp, - struct xfs_da_args *args, - int forkoff) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_name_local *name_loc; - struct xfs_da_args nargs; - struct xfs_inode *dp = args->dp; - char *tmpbuffer; - int error; - int i; - - trace_xfs_attr_leaf_to_sf(args); - - tmpbuffer = kmem_alloc(args->geo->blksize, KM_SLEEP); - if (!tmpbuffer) - return ENOMEM; - - memcpy(tmpbuffer, bp->b_addr, args->geo->blksize); - - leaf = (xfs_attr_leafblock_t *)tmpbuffer; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - entry = xfs_attr3_leaf_entryp(leaf); - - /* XXX (dgc): buffer is about to be marked stale - why zero it? */ - memset(bp->b_addr, 0, args->geo->blksize); - - /* - * Clean out the prior contents of the attribute list. - */ - error = xfs_da_shrink_inode(args, 0, bp); - if (error) - goto out; - - if (forkoff == -1) { - ASSERT(dp->i_mount->m_flags & XFS_MOUNT_ATTR2); - ASSERT(dp->i_d.di_format != XFS_DINODE_FMT_BTREE); - xfs_attr_fork_reset(dp, args->trans); - goto out; - } - - xfs_attr_shortform_create(args); - - /* - * Copy the attributes - */ - memset((char *)&nargs, 0, sizeof(nargs)); - nargs.geo = args->geo; - nargs.dp = dp; - nargs.firstblock = args->firstblock; - nargs.flist = args->flist; - nargs.total = args->total; - nargs.whichfork = XFS_ATTR_FORK; - nargs.trans = args->trans; - nargs.op_flags = XFS_DA_OP_OKNOENT; - - for (i = 0; i < ichdr.count; entry++, i++) { - if (entry->flags & XFS_ATTR_INCOMPLETE) - continue; /* don't copy partial entries */ - if (!entry->nameidx) - continue; - ASSERT(entry->flags & XFS_ATTR_LOCAL); - name_loc = xfs_attr3_leaf_name_local(leaf, i); - nargs.name = name_loc->nameval; - nargs.namelen = name_loc->namelen; - nargs.value = &name_loc->nameval[nargs.namelen]; - nargs.valuelen = be16_to_cpu(name_loc->valuelen); - nargs.hashval = be32_to_cpu(entry->hashval); - nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(entry->flags); - xfs_attr_shortform_add(&nargs, forkoff); - } - error = 0; - -out: - kmem_free(tmpbuffer); - return error; -} - -/* - * Convert from using a single leaf to a root node and a leaf. - */ -int -xfs_attr3_leaf_to_node( - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr icleafhdr; - struct xfs_attr_leaf_entry *entries; - struct xfs_da_node_entry *btree; - struct xfs_da3_icnode_hdr icnodehdr; - struct xfs_da_intnode *node; - struct xfs_inode *dp = args->dp; - struct xfs_mount *mp = dp->i_mount; - struct xfs_buf *bp1 = NULL; - struct xfs_buf *bp2 = NULL; - xfs_dablk_t blkno; - int error; - - trace_xfs_attr_leaf_to_node(args); - - error = xfs_da_grow_inode(args, &blkno); - if (error) - goto out; - error = xfs_attr3_leaf_read(args->trans, dp, 0, -1, &bp1); - if (error) - goto out; - - error = xfs_da_get_buf(args->trans, dp, blkno, -1, &bp2, XFS_ATTR_FORK); - if (error) - goto out; - - /* copy leaf to new buffer, update identifiers */ - xfs_trans_buf_set_type(args->trans, bp2, XFS_BLFT_ATTR_LEAF_BUF); - bp2->b_ops = bp1->b_ops; - memcpy(bp2->b_addr, bp1->b_addr, args->geo->blksize); - if (xfs_sb_version_hascrc(&mp->m_sb)) { - struct xfs_da3_blkinfo *hdr3 = bp2->b_addr; - hdr3->blkno = cpu_to_be64(bp2->b_bn); - } - xfs_trans_log_buf(args->trans, bp2, 0, args->geo->blksize - 1); - - /* - * Set up the new root node. - */ - error = xfs_da3_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK); - if (error) - goto out; - node = bp1->b_addr; - dp->d_ops->node_hdr_from_disk(&icnodehdr, node); - btree = dp->d_ops->node_tree_p(node); - - leaf = bp2->b_addr; - xfs_attr3_leaf_hdr_from_disk(&icleafhdr, leaf); - entries = xfs_attr3_leaf_entryp(leaf); - - /* both on-disk, don't endian-flip twice */ - btree[0].hashval = entries[icleafhdr.count - 1].hashval; - btree[0].before = cpu_to_be32(blkno); - icnodehdr.count = 1; - dp->d_ops->node_hdr_to_disk(node, &icnodehdr); - xfs_trans_log_buf(args->trans, bp1, 0, args->geo->blksize - 1); - error = 0; -out: - return error; -} - -/*======================================================================== - * Routines used for growing the Btree. - *========================================================================*/ - -/* - * Create the initial contents of a leaf attribute list - * or a leaf in a node attribute list. - */ -STATIC int -xfs_attr3_leaf_create( - struct xfs_da_args *args, - xfs_dablk_t blkno, - struct xfs_buf **bpp) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_inode *dp = args->dp; - struct xfs_mount *mp = dp->i_mount; - struct xfs_buf *bp; - int error; - - trace_xfs_attr_leaf_create(args); - - error = xfs_da_get_buf(args->trans, args->dp, blkno, -1, &bp, - XFS_ATTR_FORK); - if (error) - return error; - bp->b_ops = &xfs_attr3_leaf_buf_ops; - xfs_trans_buf_set_type(args->trans, bp, XFS_BLFT_ATTR_LEAF_BUF); - leaf = bp->b_addr; - memset(leaf, 0, args->geo->blksize); - - memset(&ichdr, 0, sizeof(ichdr)); - ichdr.firstused = args->geo->blksize; - - if (xfs_sb_version_hascrc(&mp->m_sb)) { - struct xfs_da3_blkinfo *hdr3 = bp->b_addr; - - ichdr.magic = XFS_ATTR3_LEAF_MAGIC; - - hdr3->blkno = cpu_to_be64(bp->b_bn); - hdr3->owner = cpu_to_be64(dp->i_ino); - uuid_copy(&hdr3->uuid, &mp->m_sb.sb_uuid); - - ichdr.freemap[0].base = sizeof(struct xfs_attr3_leaf_hdr); - } else { - ichdr.magic = XFS_ATTR_LEAF_MAGIC; - ichdr.freemap[0].base = sizeof(struct xfs_attr_leaf_hdr); - } - ichdr.freemap[0].size = ichdr.firstused - ichdr.freemap[0].base; - - xfs_attr3_leaf_hdr_to_disk(leaf, &ichdr); - xfs_trans_log_buf(args->trans, bp, 0, args->geo->blksize - 1); - - *bpp = bp; - return 0; -} - -/* - * Split the leaf node, rebalance, then add the new entry. - */ -int -xfs_attr3_leaf_split( - struct xfs_da_state *state, - struct xfs_da_state_blk *oldblk, - struct xfs_da_state_blk *newblk) -{ - xfs_dablk_t blkno; - int error; - - trace_xfs_attr_leaf_split(state->args); - - /* - * Allocate space for a new leaf node. - */ - ASSERT(oldblk->magic == XFS_ATTR_LEAF_MAGIC); - error = xfs_da_grow_inode(state->args, &blkno); - if (error) - return error; - error = xfs_attr3_leaf_create(state->args, blkno, &newblk->bp); - if (error) - return error; - newblk->blkno = blkno; - newblk->magic = XFS_ATTR_LEAF_MAGIC; - - /* - * Rebalance the entries across the two leaves. - * NOTE: rebalance() currently depends on the 2nd block being empty. - */ - xfs_attr3_leaf_rebalance(state, oldblk, newblk); - error = xfs_da3_blk_link(state, oldblk, newblk); - if (error) - return error; - - /* - * Save info on "old" attribute for "atomic rename" ops, leaf_add() - * modifies the index/blkno/rmtblk/rmtblkcnt fields to show the - * "new" attrs info. Will need the "old" info to remove it later. - * - * Insert the "new" entry in the correct block. - */ - if (state->inleaf) { - trace_xfs_attr_leaf_add_old(state->args); - error = xfs_attr3_leaf_add(oldblk->bp, state->args); - } else { - trace_xfs_attr_leaf_add_new(state->args); - error = xfs_attr3_leaf_add(newblk->bp, state->args); - } - - /* - * Update last hashval in each block since we added the name. - */ - oldblk->hashval = xfs_attr_leaf_lasthash(oldblk->bp, NULL); - newblk->hashval = xfs_attr_leaf_lasthash(newblk->bp, NULL); - return error; -} - -/* - * Add a name to the leaf attribute list structure. - */ -int -xfs_attr3_leaf_add( - struct xfs_buf *bp, - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - int tablesize; - int entsize; - int sum; - int tmp; - int i; - - trace_xfs_attr_leaf_add(args); - - leaf = bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - ASSERT(args->index >= 0 && args->index <= ichdr.count); - entsize = xfs_attr_leaf_newentsize(args, NULL); - - /* - * Search through freemap for first-fit on new name length. - * (may need to figure in size of entry struct too) - */ - tablesize = (ichdr.count + 1) * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); - for (sum = 0, i = XFS_ATTR_LEAF_MAPSIZE - 1; i >= 0; i--) { - if (tablesize > ichdr.firstused) { - sum += ichdr.freemap[i].size; - continue; - } - if (!ichdr.freemap[i].size) - continue; /* no space in this map */ - tmp = entsize; - if (ichdr.freemap[i].base < ichdr.firstused) - tmp += sizeof(xfs_attr_leaf_entry_t); - if (ichdr.freemap[i].size >= tmp) { - tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, i); - goto out_log_hdr; - } - sum += ichdr.freemap[i].size; - } - - /* - * If there are no holes in the address space of the block, - * and we don't have enough freespace, then compaction will do us - * no good and we should just give up. - */ - if (!ichdr.holes && sum < entsize) - return ENOSPC; - - /* - * Compact the entries to coalesce free space. - * This may change the hdr->count via dropping INCOMPLETE entries. - */ - xfs_attr3_leaf_compact(args, &ichdr, bp); - - /* - * After compaction, the block is guaranteed to have only one - * free region, in freemap[0]. If it is not big enough, give up. - */ - if (ichdr.freemap[0].size < (entsize + sizeof(xfs_attr_leaf_entry_t))) { - tmp = ENOSPC; - goto out_log_hdr; - } - - tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, 0); - -out_log_hdr: - xfs_attr3_leaf_hdr_to_disk(leaf, &ichdr); - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, &leaf->hdr, - xfs_attr3_leaf_hdr_size(leaf))); - return tmp; -} - -/* - * Add a name to a leaf attribute list structure. - */ -STATIC int -xfs_attr3_leaf_add_work( - struct xfs_buf *bp, - struct xfs_attr3_icleaf_hdr *ichdr, - struct xfs_da_args *args, - int mapindex) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_name_local *name_loc; - struct xfs_attr_leaf_name_remote *name_rmt; - struct xfs_mount *mp; - int tmp; - int i; - - trace_xfs_attr_leaf_add_work(args); - - leaf = bp->b_addr; - ASSERT(mapindex >= 0 && mapindex < XFS_ATTR_LEAF_MAPSIZE); - ASSERT(args->index >= 0 && args->index <= ichdr->count); - - /* - * Force open some space in the entry array and fill it in. - */ - entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; - if (args->index < ichdr->count) { - tmp = ichdr->count - args->index; - tmp *= sizeof(xfs_attr_leaf_entry_t); - memmove(entry + 1, entry, tmp); - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry))); - } - ichdr->count++; - - /* - * Allocate space for the new string (at the end of the run). - */ - mp = args->trans->t_mountp; - ASSERT(ichdr->freemap[mapindex].base < args->geo->blksize); - ASSERT((ichdr->freemap[mapindex].base & 0x3) == 0); - ASSERT(ichdr->freemap[mapindex].size >= - xfs_attr_leaf_newentsize(args, NULL)); - ASSERT(ichdr->freemap[mapindex].size < args->geo->blksize); - ASSERT((ichdr->freemap[mapindex].size & 0x3) == 0); - - ichdr->freemap[mapindex].size -= xfs_attr_leaf_newentsize(args, &tmp); - - entry->nameidx = cpu_to_be16(ichdr->freemap[mapindex].base + - ichdr->freemap[mapindex].size); - entry->hashval = cpu_to_be32(args->hashval); - entry->flags = tmp ? XFS_ATTR_LOCAL : 0; - entry->flags |= XFS_ATTR_NSP_ARGS_TO_ONDISK(args->flags); - if (args->op_flags & XFS_DA_OP_RENAME) { - entry->flags |= XFS_ATTR_INCOMPLETE; - if ((args->blkno2 == args->blkno) && - (args->index2 <= args->index)) { - args->index2++; - } - } - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); - ASSERT((args->index == 0) || - (be32_to_cpu(entry->hashval) >= be32_to_cpu((entry-1)->hashval))); - ASSERT((args->index == ichdr->count - 1) || - (be32_to_cpu(entry->hashval) <= be32_to_cpu((entry+1)->hashval))); - - /* - * For "remote" attribute values, simply note that we need to - * allocate space for the "remote" value. We can't actually - * allocate the extents in this transaction, and we can't decide - * which blocks they should be as we might allocate more blocks - * as part of this transaction (a split operation for example). - */ - if (entry->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf, args->index); - name_loc->namelen = args->namelen; - name_loc->valuelen = cpu_to_be16(args->valuelen); - memcpy((char *)name_loc->nameval, args->name, args->namelen); - memcpy((char *)&name_loc->nameval[args->namelen], args->value, - be16_to_cpu(name_loc->valuelen)); - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); - name_rmt->namelen = args->namelen; - memcpy((char *)name_rmt->name, args->name, args->namelen); - entry->flags |= XFS_ATTR_INCOMPLETE; - /* just in case */ - name_rmt->valuelen = 0; - name_rmt->valueblk = 0; - args->rmtblkno = 1; - args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); - args->rmtvaluelen = args->valuelen; - } - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), - xfs_attr_leaf_entsize(leaf, args->index))); - - /* - * Update the control info for this leaf node - */ - if (be16_to_cpu(entry->nameidx) < ichdr->firstused) - ichdr->firstused = be16_to_cpu(entry->nameidx); - - ASSERT(ichdr->firstused >= ichdr->count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf)); - tmp = (ichdr->count - 1) * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); - - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - if (ichdr->freemap[i].base == tmp) { - ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t); - ichdr->freemap[i].size -= sizeof(xfs_attr_leaf_entry_t); - } - } - ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index); - return 0; -} - -/* - * Garbage collect a leaf attribute list block by copying it to a new buffer. - */ -STATIC void -xfs_attr3_leaf_compact( - struct xfs_da_args *args, - struct xfs_attr3_icleaf_hdr *ichdr_dst, - struct xfs_buf *bp) -{ - struct xfs_attr_leafblock *leaf_src; - struct xfs_attr_leafblock *leaf_dst; - struct xfs_attr3_icleaf_hdr ichdr_src; - struct xfs_trans *trans = args->trans; - char *tmpbuffer; - - trace_xfs_attr_leaf_compact(args); - - tmpbuffer = kmem_alloc(args->geo->blksize, KM_SLEEP); - memcpy(tmpbuffer, bp->b_addr, args->geo->blksize); - memset(bp->b_addr, 0, args->geo->blksize); - leaf_src = (xfs_attr_leafblock_t *)tmpbuffer; - leaf_dst = bp->b_addr; - - /* - * Copy the on-disk header back into the destination buffer to ensure - * all the information in the header that is not part of the incore - * header structure is preserved. - */ - memcpy(bp->b_addr, tmpbuffer, xfs_attr3_leaf_hdr_size(leaf_src)); - - /* Initialise the incore headers */ - ichdr_src = *ichdr_dst; /* struct copy */ - ichdr_dst->firstused = args->geo->blksize; - ichdr_dst->usedbytes = 0; - ichdr_dst->count = 0; - ichdr_dst->holes = 0; - ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src); - ichdr_dst->freemap[0].size = ichdr_dst->firstused - - ichdr_dst->freemap[0].base; - - /* write the header back to initialise the underlying buffer */ - xfs_attr3_leaf_hdr_to_disk(leaf_dst, ichdr_dst); - - /* - * Copy all entry's in the same (sorted) order, - * but allocate name/value pairs packed and in sequence. - */ - xfs_attr3_leaf_moveents(args, leaf_src, &ichdr_src, 0, - leaf_dst, ichdr_dst, 0, ichdr_src.count); - /* - * this logs the entire buffer, but the caller must write the header - * back to the buffer when it is finished modifying it. - */ - xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1); - - kmem_free(tmpbuffer); -} - -/* - * Compare two leaf blocks "order". - * Return 0 unless leaf2 should go before leaf1. - */ -static int -xfs_attr3_leaf_order( - struct xfs_buf *leaf1_bp, - struct xfs_attr3_icleaf_hdr *leaf1hdr, - struct xfs_buf *leaf2_bp, - struct xfs_attr3_icleaf_hdr *leaf2hdr) -{ - struct xfs_attr_leaf_entry *entries1; - struct xfs_attr_leaf_entry *entries2; - - entries1 = xfs_attr3_leaf_entryp(leaf1_bp->b_addr); - entries2 = xfs_attr3_leaf_entryp(leaf2_bp->b_addr); - if (leaf1hdr->count > 0 && leaf2hdr->count > 0 && - ((be32_to_cpu(entries2[0].hashval) < - be32_to_cpu(entries1[0].hashval)) || - (be32_to_cpu(entries2[leaf2hdr->count - 1].hashval) < - be32_to_cpu(entries1[leaf1hdr->count - 1].hashval)))) { - return 1; - } - return 0; -} - -int -xfs_attr_leaf_order( - struct xfs_buf *leaf1_bp, - struct xfs_buf *leaf2_bp) -{ - struct xfs_attr3_icleaf_hdr ichdr1; - struct xfs_attr3_icleaf_hdr ichdr2; - - xfs_attr3_leaf_hdr_from_disk(&ichdr1, leaf1_bp->b_addr); - xfs_attr3_leaf_hdr_from_disk(&ichdr2, leaf2_bp->b_addr); - return xfs_attr3_leaf_order(leaf1_bp, &ichdr1, leaf2_bp, &ichdr2); -} - -/* - * Redistribute the attribute list entries between two leaf nodes, - * taking into account the size of the new entry. - * - * NOTE: if new block is empty, then it will get the upper half of the - * old block. At present, all (one) callers pass in an empty second block. - * - * This code adjusts the args->index/blkno and args->index2/blkno2 fields - * to match what it is doing in splitting the attribute leaf block. Those - * values are used in "atomic rename" operations on attributes. Note that - * the "new" and "old" values can end up in different blocks. - */ -STATIC void -xfs_attr3_leaf_rebalance( - struct xfs_da_state *state, - struct xfs_da_state_blk *blk1, - struct xfs_da_state_blk *blk2) -{ - struct xfs_da_args *args; - struct xfs_attr_leafblock *leaf1; - struct xfs_attr_leafblock *leaf2; - struct xfs_attr3_icleaf_hdr ichdr1; - struct xfs_attr3_icleaf_hdr ichdr2; - struct xfs_attr_leaf_entry *entries1; - struct xfs_attr_leaf_entry *entries2; - int count; - int totallen; - int max; - int space; - int swap; - - /* - * Set up environment. - */ - ASSERT(blk1->magic == XFS_ATTR_LEAF_MAGIC); - ASSERT(blk2->magic == XFS_ATTR_LEAF_MAGIC); - leaf1 = blk1->bp->b_addr; - leaf2 = blk2->bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr1, leaf1); - xfs_attr3_leaf_hdr_from_disk(&ichdr2, leaf2); - ASSERT(ichdr2.count == 0); - args = state->args; - - trace_xfs_attr_leaf_rebalance(args); - - /* - * Check ordering of blocks, reverse if it makes things simpler. - * - * NOTE: Given that all (current) callers pass in an empty - * second block, this code should never set "swap". - */ - swap = 0; - if (xfs_attr3_leaf_order(blk1->bp, &ichdr1, blk2->bp, &ichdr2)) { - struct xfs_da_state_blk *tmp_blk; - struct xfs_attr3_icleaf_hdr tmp_ichdr; - - tmp_blk = blk1; - blk1 = blk2; - blk2 = tmp_blk; - - /* struct copies to swap them rather than reconverting */ - tmp_ichdr = ichdr1; - ichdr1 = ichdr2; - ichdr2 = tmp_ichdr; - - leaf1 = blk1->bp->b_addr; - leaf2 = blk2->bp->b_addr; - swap = 1; - } - - /* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. Then get - * the direction to copy and the number of elements to move. - * - * "inleaf" is true if the new entry should be inserted into blk1. - * If "swap" is also true, then reverse the sense of "inleaf". - */ - state->inleaf = xfs_attr3_leaf_figure_balance(state, blk1, &ichdr1, - blk2, &ichdr2, - &count, &totallen); - if (swap) - state->inleaf = !state->inleaf; - - /* - * Move any entries required from leaf to leaf: - */ - if (count < ichdr1.count) { - /* - * Figure the total bytes to be added to the destination leaf. - */ - /* number entries being moved */ - count = ichdr1.count - count; - space = ichdr1.usedbytes - totallen; - space += count * sizeof(xfs_attr_leaf_entry_t); - - /* - * leaf2 is the destination, compact it if it looks tight. - */ - max = ichdr2.firstused - xfs_attr3_leaf_hdr_size(leaf1); - max -= ichdr2.count * sizeof(xfs_attr_leaf_entry_t); - if (space > max) - xfs_attr3_leaf_compact(args, &ichdr2, blk2->bp); - - /* - * Move high entries from leaf1 to low end of leaf2. - */ - xfs_attr3_leaf_moveents(args, leaf1, &ichdr1, - ichdr1.count - count, leaf2, &ichdr2, 0, count); - - } else if (count > ichdr1.count) { - /* - * I assert that since all callers pass in an empty - * second buffer, this code should never execute. - */ - ASSERT(0); - - /* - * Figure the total bytes to be added to the destination leaf. - */ - /* number entries being moved */ - count -= ichdr1.count; - space = totallen - ichdr1.usedbytes; - space += count * sizeof(xfs_attr_leaf_entry_t); - - /* - * leaf1 is the destination, compact it if it looks tight. - */ - max = ichdr1.firstused - xfs_attr3_leaf_hdr_size(leaf1); - max -= ichdr1.count * sizeof(xfs_attr_leaf_entry_t); - if (space > max) - xfs_attr3_leaf_compact(args, &ichdr1, blk1->bp); - - /* - * Move low entries from leaf2 to high end of leaf1. - */ - xfs_attr3_leaf_moveents(args, leaf2, &ichdr2, 0, leaf1, &ichdr1, - ichdr1.count, count); - } - - xfs_attr3_leaf_hdr_to_disk(leaf1, &ichdr1); - xfs_attr3_leaf_hdr_to_disk(leaf2, &ichdr2); - xfs_trans_log_buf(args->trans, blk1->bp, 0, args->geo->blksize - 1); - xfs_trans_log_buf(args->trans, blk2->bp, 0, args->geo->blksize - 1); - - /* - * Copy out last hashval in each block for B-tree code. - */ - entries1 = xfs_attr3_leaf_entryp(leaf1); - entries2 = xfs_attr3_leaf_entryp(leaf2); - blk1->hashval = be32_to_cpu(entries1[ichdr1.count - 1].hashval); - blk2->hashval = be32_to_cpu(entries2[ichdr2.count - 1].hashval); - - /* - * Adjust the expected index for insertion. - * NOTE: this code depends on the (current) situation that the - * second block was originally empty. - * - * If the insertion point moved to the 2nd block, we must adjust - * the index. We must also track the entry just following the - * new entry for use in an "atomic rename" operation, that entry - * is always the "old" entry and the "new" entry is what we are - * inserting. The index/blkno fields refer to the "old" entry, - * while the index2/blkno2 fields refer to the "new" entry. - */ - if (blk1->index > ichdr1.count) { - ASSERT(state->inleaf == 0); - blk2->index = blk1->index - ichdr1.count; - args->index = args->index2 = blk2->index; - args->blkno = args->blkno2 = blk2->blkno; - } else if (blk1->index == ichdr1.count) { - if (state->inleaf) { - args->index = blk1->index; - args->blkno = blk1->blkno; - args->index2 = 0; - args->blkno2 = blk2->blkno; - } else { - /* - * On a double leaf split, the original attr location - * is already stored in blkno2/index2, so don't - * overwrite it overwise we corrupt the tree. - */ - blk2->index = blk1->index - ichdr1.count; - args->index = blk2->index; - args->blkno = blk2->blkno; - if (!state->extravalid) { - /* - * set the new attr location to match the old - * one and let the higher level split code - * decide where in the leaf to place it. - */ - args->index2 = blk2->index; - args->blkno2 = blk2->blkno; - } - } - } else { - ASSERT(state->inleaf == 1); - args->index = args->index2 = blk1->index; - args->blkno = args->blkno2 = blk1->blkno; - } -} - -/* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. - * GROT: Is this really necessary? With other than a 512 byte blocksize, - * GROT: there will always be enough room in either block for a new entry. - * GROT: Do a double-split for this case? - */ -STATIC int -xfs_attr3_leaf_figure_balance( - struct xfs_da_state *state, - struct xfs_da_state_blk *blk1, - struct xfs_attr3_icleaf_hdr *ichdr1, - struct xfs_da_state_blk *blk2, - struct xfs_attr3_icleaf_hdr *ichdr2, - int *countarg, - int *usedbytesarg) -{ - struct xfs_attr_leafblock *leaf1 = blk1->bp->b_addr; - struct xfs_attr_leafblock *leaf2 = blk2->bp->b_addr; - struct xfs_attr_leaf_entry *entry; - int count; - int max; - int index; - int totallen = 0; - int half; - int lastdelta; - int foundit = 0; - int tmp; - - /* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. - */ - max = ichdr1->count + ichdr2->count; - half = (max + 1) * sizeof(*entry); - half += ichdr1->usedbytes + ichdr2->usedbytes + - xfs_attr_leaf_newentsize(state->args, NULL); - half /= 2; - lastdelta = state->args->geo->blksize; - entry = xfs_attr3_leaf_entryp(leaf1); - for (count = index = 0; count < max; entry++, index++, count++) { - -#define XFS_ATTR_ABS(A) (((A) < 0) ? -(A) : (A)) - /* - * The new entry is in the first block, account for it. - */ - if (count == blk1->index) { - tmp = totallen + sizeof(*entry) + - xfs_attr_leaf_newentsize(state->args, NULL); - if (XFS_ATTR_ABS(half - tmp) > lastdelta) - break; - lastdelta = XFS_ATTR_ABS(half - tmp); - totallen = tmp; - foundit = 1; - } - - /* - * Wrap around into the second block if necessary. - */ - if (count == ichdr1->count) { - leaf1 = leaf2; - entry = xfs_attr3_leaf_entryp(leaf1); - index = 0; - } - - /* - * Figure out if next leaf entry would be too much. - */ - tmp = totallen + sizeof(*entry) + xfs_attr_leaf_entsize(leaf1, - index); - if (XFS_ATTR_ABS(half - tmp) > lastdelta) - break; - lastdelta = XFS_ATTR_ABS(half - tmp); - totallen = tmp; -#undef XFS_ATTR_ABS - } - - /* - * Calculate the number of usedbytes that will end up in lower block. - * If new entry not in lower block, fix up the count. - */ - totallen -= count * sizeof(*entry); - if (foundit) { - totallen -= sizeof(*entry) + - xfs_attr_leaf_newentsize(state->args, NULL); - } - - *countarg = count; - *usedbytesarg = totallen; - return foundit; -} - -/*======================================================================== - * Routines used for shrinking the Btree. - *========================================================================*/ - -/* - * Check a leaf block and its neighbors to see if the block should be - * collapsed into one or the other neighbor. Always keep the block - * with the smaller block number. - * If the current block is over 50% full, don't try to join it, return 0. - * If the block is empty, fill in the state structure and return 2. - * If it can be collapsed, fill in the state structure and return 1. - * If nothing can be done, return 0. - * - * GROT: allow for INCOMPLETE entries in calculation. - */ -int -xfs_attr3_leaf_toosmall( - struct xfs_da_state *state, - int *action) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_da_state_blk *blk; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_buf *bp; - xfs_dablk_t blkno; - int bytes; - int forward; - int error; - int retval; - int i; - - trace_xfs_attr_leaf_toosmall(state->args); - - /* - * Check for the degenerate case of the block being over 50% full. - * If so, it's not worth even looking to see if we might be able - * to coalesce with a sibling. - */ - blk = &state->path.blk[ state->path.active-1 ]; - leaf = blk->bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - bytes = xfs_attr3_leaf_hdr_size(leaf) + - ichdr.count * sizeof(xfs_attr_leaf_entry_t) + - ichdr.usedbytes; - if (bytes > (state->args->geo->blksize >> 1)) { - *action = 0; /* blk over 50%, don't try to join */ - return 0; - } - - /* - * Check for the degenerate case of the block being empty. - * If the block is empty, we'll simply delete it, no need to - * coalesce it with a sibling block. We choose (arbitrarily) - * to merge with the forward block unless it is NULL. - */ - if (ichdr.count == 0) { - /* - * Make altpath point to the block we want to keep and - * path point to the block we want to drop (this one). - */ - forward = (ichdr.forw != 0); - memcpy(&state->altpath, &state->path, sizeof(state->path)); - error = xfs_da3_path_shift(state, &state->altpath, forward, - 0, &retval); - if (error) - return error; - if (retval) { - *action = 0; - } else { - *action = 2; - } - return 0; - } - - /* - * Examine each sibling block to see if we can coalesce with - * at least 25% free space to spare. We need to figure out - * whether to merge with the forward or the backward block. - * We prefer coalescing with the lower numbered sibling so as - * to shrink an attribute list over time. - */ - /* start with smaller blk num */ - forward = ichdr.forw < ichdr.back; - for (i = 0; i < 2; forward = !forward, i++) { - struct xfs_attr3_icleaf_hdr ichdr2; - if (forward) - blkno = ichdr.forw; - else - blkno = ichdr.back; - if (blkno == 0) - continue; - error = xfs_attr3_leaf_read(state->args->trans, state->args->dp, - blkno, -1, &bp); - if (error) - return error; - - xfs_attr3_leaf_hdr_from_disk(&ichdr2, bp->b_addr); - - bytes = state->args->geo->blksize - - (state->args->geo->blksize >> 2) - - ichdr.usedbytes - ichdr2.usedbytes - - ((ichdr.count + ichdr2.count) * - sizeof(xfs_attr_leaf_entry_t)) - - xfs_attr3_leaf_hdr_size(leaf); - - xfs_trans_brelse(state->args->trans, bp); - if (bytes >= 0) - break; /* fits with at least 25% to spare */ - } - if (i >= 2) { - *action = 0; - return 0; - } - - /* - * Make altpath point to the block we want to keep (the lower - * numbered block) and path point to the block we want to drop. - */ - memcpy(&state->altpath, &state->path, sizeof(state->path)); - if (blkno < blk->blkno) { - error = xfs_da3_path_shift(state, &state->altpath, forward, - 0, &retval); - } else { - error = xfs_da3_path_shift(state, &state->path, forward, - 0, &retval); - } - if (error) - return error; - if (retval) { - *action = 0; - } else { - *action = 1; - } - return 0; -} - -/* - * Remove a name from the leaf attribute list structure. - * - * Return 1 if leaf is less than 37% full, 0 if >= 37% full. - * If two leaves are 37% full, when combined they will leave 25% free. - */ -int -xfs_attr3_leaf_remove( - struct xfs_buf *bp, - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_attr_leaf_entry *entry; - int before; - int after; - int smallest; - int entsize; - int tablesize; - int tmp; - int i; - - trace_xfs_attr_leaf_remove(args); - - leaf = bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - - ASSERT(ichdr.count > 0 && ichdr.count < args->geo->blksize / 8); - ASSERT(args->index >= 0 && args->index < ichdr.count); - ASSERT(ichdr.firstused >= ichdr.count * sizeof(*entry) + - xfs_attr3_leaf_hdr_size(leaf)); - - entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; - - ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused); - ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize); - - /* - * Scan through free region table: - * check for adjacency of free'd entry with an existing one, - * find smallest free region in case we need to replace it, - * adjust any map that borders the entry table, - */ - tablesize = ichdr.count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf); - tmp = ichdr.freemap[0].size; - before = after = -1; - smallest = XFS_ATTR_LEAF_MAPSIZE - 1; - entsize = xfs_attr_leaf_entsize(leaf, args->index); - for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { - ASSERT(ichdr.freemap[i].base < args->geo->blksize); - ASSERT(ichdr.freemap[i].size < args->geo->blksize); - if (ichdr.freemap[i].base == tablesize) { - ichdr.freemap[i].base -= sizeof(xfs_attr_leaf_entry_t); - ichdr.freemap[i].size += sizeof(xfs_attr_leaf_entry_t); - } - - if (ichdr.freemap[i].base + ichdr.freemap[i].size == - be16_to_cpu(entry->nameidx)) { - before = i; - } else if (ichdr.freemap[i].base == - (be16_to_cpu(entry->nameidx) + entsize)) { - after = i; - } else if (ichdr.freemap[i].size < tmp) { - tmp = ichdr.freemap[i].size; - smallest = i; - } - } - - /* - * Coalesce adjacent freemap regions, - * or replace the smallest region. - */ - if ((before >= 0) || (after >= 0)) { - if ((before >= 0) && (after >= 0)) { - ichdr.freemap[before].size += entsize; - ichdr.freemap[before].size += ichdr.freemap[after].size; - ichdr.freemap[after].base = 0; - ichdr.freemap[after].size = 0; - } else if (before >= 0) { - ichdr.freemap[before].size += entsize; - } else { - ichdr.freemap[after].base = be16_to_cpu(entry->nameidx); - ichdr.freemap[after].size += entsize; - } - } else { - /* - * Replace smallest region (if it is smaller than free'd entry) - */ - if (ichdr.freemap[smallest].size < entsize) { - ichdr.freemap[smallest].base = be16_to_cpu(entry->nameidx); - ichdr.freemap[smallest].size = entsize; - } - } - - /* - * Did we remove the first entry? - */ - if (be16_to_cpu(entry->nameidx) == ichdr.firstused) - smallest = 1; - else - smallest = 0; - - /* - * Compress the remaining entries and zero out the removed stuff. - */ - memset(xfs_attr3_leaf_name(leaf, args->index), 0, entsize); - ichdr.usedbytes -= entsize; - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), - entsize)); - - tmp = (ichdr.count - args->index) * sizeof(xfs_attr_leaf_entry_t); - memmove(entry, entry + 1, tmp); - ichdr.count--; - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(xfs_attr_leaf_entry_t))); - - entry = &xfs_attr3_leaf_entryp(leaf)[ichdr.count]; - memset(entry, 0, sizeof(xfs_attr_leaf_entry_t)); - - /* - * If we removed the first entry, re-find the first used byte - * in the name area. Note that if the entry was the "firstused", - * then we don't have a "hole" in our block resulting from - * removing the name. - */ - if (smallest) { - tmp = args->geo->blksize; - entry = xfs_attr3_leaf_entryp(leaf); - for (i = ichdr.count - 1; i >= 0; entry++, i--) { - ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused); - ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize); - - if (be16_to_cpu(entry->nameidx) < tmp) - tmp = be16_to_cpu(entry->nameidx); - } - ichdr.firstused = tmp; - if (!ichdr.firstused) - ichdr.firstused = tmp - XFS_ATTR_LEAF_NAME_ALIGN; - } else { - ichdr.holes = 1; /* mark as needing compaction */ - } - xfs_attr3_leaf_hdr_to_disk(leaf, &ichdr); - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, &leaf->hdr, - xfs_attr3_leaf_hdr_size(leaf))); - - /* - * Check if leaf is less than 50% full, caller may want to - * "join" the leaf with a sibling if so. - */ - tmp = ichdr.usedbytes + xfs_attr3_leaf_hdr_size(leaf) + - ichdr.count * sizeof(xfs_attr_leaf_entry_t); - - return tmp < args->geo->magicpct; /* leaf is < 37% full */ -} - -/* - * Move all the attribute list entries from drop_leaf into save_leaf. - */ -void -xfs_attr3_leaf_unbalance( - struct xfs_da_state *state, - struct xfs_da_state_blk *drop_blk, - struct xfs_da_state_blk *save_blk) -{ - struct xfs_attr_leafblock *drop_leaf = drop_blk->bp->b_addr; - struct xfs_attr_leafblock *save_leaf = save_blk->bp->b_addr; - struct xfs_attr3_icleaf_hdr drophdr; - struct xfs_attr3_icleaf_hdr savehdr; - struct xfs_attr_leaf_entry *entry; - - trace_xfs_attr_leaf_unbalance(state->args); - - drop_leaf = drop_blk->bp->b_addr; - save_leaf = save_blk->bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&drophdr, drop_leaf); - xfs_attr3_leaf_hdr_from_disk(&savehdr, save_leaf); - entry = xfs_attr3_leaf_entryp(drop_leaf); - - /* - * Save last hashval from dying block for later Btree fixup. - */ - drop_blk->hashval = be32_to_cpu(entry[drophdr.count - 1].hashval); - - /* - * Check if we need a temp buffer, or can we do it in place. - * Note that we don't check "leaf" for holes because we will - * always be dropping it, toosmall() decided that for us already. - */ - if (savehdr.holes == 0) { - /* - * dest leaf has no holes, so we add there. May need - * to make some room in the entry array. - */ - if (xfs_attr3_leaf_order(save_blk->bp, &savehdr, - drop_blk->bp, &drophdr)) { - xfs_attr3_leaf_moveents(state->args, - drop_leaf, &drophdr, 0, - save_leaf, &savehdr, 0, - drophdr.count); - } else { - xfs_attr3_leaf_moveents(state->args, - drop_leaf, &drophdr, 0, - save_leaf, &savehdr, - savehdr.count, drophdr.count); - } - } else { - /* - * Destination has holes, so we make a temporary copy - * of the leaf and add them both to that. - */ - struct xfs_attr_leafblock *tmp_leaf; - struct xfs_attr3_icleaf_hdr tmphdr; - - tmp_leaf = kmem_zalloc(state->args->geo->blksize, KM_SLEEP); - - /* - * Copy the header into the temp leaf so that all the stuff - * not in the incore header is present and gets copied back in - * once we've moved all the entries. - */ - memcpy(tmp_leaf, save_leaf, xfs_attr3_leaf_hdr_size(save_leaf)); - - memset(&tmphdr, 0, sizeof(tmphdr)); - tmphdr.magic = savehdr.magic; - tmphdr.forw = savehdr.forw; - tmphdr.back = savehdr.back; - tmphdr.firstused = state->args->geo->blksize; - - /* write the header to the temp buffer to initialise it */ - xfs_attr3_leaf_hdr_to_disk(tmp_leaf, &tmphdr); - - if (xfs_attr3_leaf_order(save_blk->bp, &savehdr, - drop_blk->bp, &drophdr)) { - xfs_attr3_leaf_moveents(state->args, - drop_leaf, &drophdr, 0, - tmp_leaf, &tmphdr, 0, - drophdr.count); - xfs_attr3_leaf_moveents(state->args, - save_leaf, &savehdr, 0, - tmp_leaf, &tmphdr, tmphdr.count, - savehdr.count); - } else { - xfs_attr3_leaf_moveents(state->args, - save_leaf, &savehdr, 0, - tmp_leaf, &tmphdr, 0, - savehdr.count); - xfs_attr3_leaf_moveents(state->args, - drop_leaf, &drophdr, 0, - tmp_leaf, &tmphdr, tmphdr.count, - drophdr.count); - } - memcpy(save_leaf, tmp_leaf, state->args->geo->blksize); - savehdr = tmphdr; /* struct copy */ - kmem_free(tmp_leaf); - } - - xfs_attr3_leaf_hdr_to_disk(save_leaf, &savehdr); - xfs_trans_log_buf(state->args->trans, save_blk->bp, 0, - state->args->geo->blksize - 1); - - /* - * Copy out last hashval in each block for B-tree code. - */ - entry = xfs_attr3_leaf_entryp(save_leaf); - save_blk->hashval = be32_to_cpu(entry[savehdr.count - 1].hashval); -} - -/*======================================================================== - * Routines used for finding things in the Btree. - *========================================================================*/ - -/* - * Look up a name in a leaf attribute list structure. - * This is the internal routine, it uses the caller's buffer. - * - * Note that duplicate keys are allowed, but only check within the - * current leaf node. The Btree code must check in adjacent leaf nodes. - * - * Return in args->index the index into the entry[] array of either - * the found entry, or where the entry should have been (insert before - * that entry). - * - * Don't change the args->value unless we find the attribute. - */ -int -xfs_attr3_leaf_lookup_int( - struct xfs_buf *bp, - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_entry *entries; - struct xfs_attr_leaf_name_local *name_loc; - struct xfs_attr_leaf_name_remote *name_rmt; - xfs_dahash_t hashval; - int probe; - int span; - - trace_xfs_attr_leaf_lookup(args); - - leaf = bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - entries = xfs_attr3_leaf_entryp(leaf); - ASSERT(ichdr.count < args->geo->blksize / 8); - - /* - * Binary search. (note: small blocks will skip this loop) - */ - hashval = args->hashval; - probe = span = ichdr.count / 2; - for (entry = &entries[probe]; span > 4; entry = &entries[probe]) { - span /= 2; - if (be32_to_cpu(entry->hashval) < hashval) - probe += span; - else if (be32_to_cpu(entry->hashval) > hashval) - probe -= span; - else - break; - } - ASSERT(probe >= 0 && (!ichdr.count || probe < ichdr.count)); - ASSERT(span <= 4 || be32_to_cpu(entry->hashval) == hashval); - - /* - * Since we may have duplicate hashval's, find the first matching - * hashval in the leaf. - */ - while (probe > 0 && be32_to_cpu(entry->hashval) >= hashval) { - entry--; - probe--; - } - while (probe < ichdr.count && - be32_to_cpu(entry->hashval) < hashval) { - entry++; - probe++; - } - if (probe == ichdr.count || be32_to_cpu(entry->hashval) != hashval) { - args->index = probe; - return ENOATTR; - } - - /* - * Duplicate keys may be present, so search all of them for a match. - */ - for (; probe < ichdr.count && (be32_to_cpu(entry->hashval) == hashval); - entry++, probe++) { -/* - * GROT: Add code to remove incomplete entries. - */ - /* - * If we are looking for INCOMPLETE entries, show only those. - * If we are looking for complete entries, show only those. - */ - if ((args->flags & XFS_ATTR_INCOMPLETE) != - (entry->flags & XFS_ATTR_INCOMPLETE)) { - continue; - } - if (entry->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf, probe); - if (name_loc->namelen != args->namelen) - continue; - if (memcmp(args->name, name_loc->nameval, - args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, entry->flags)) - continue; - args->index = probe; - return EEXIST; - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf, probe); - if (name_rmt->namelen != args->namelen) - continue; - if (memcmp(args->name, name_rmt->name, - args->namelen) != 0) - continue; - if (!xfs_attr_namesp_match(args->flags, entry->flags)) - continue; - args->index = probe; - args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); - args->rmtblkno = be32_to_cpu(name_rmt->valueblk); - args->rmtblkcnt = xfs_attr3_rmt_blocks( - args->dp->i_mount, - args->rmtvaluelen); - return EEXIST; - } - } - args->index = probe; - return ENOATTR; -} - -/* - * Get the value associated with an attribute name from a leaf attribute - * list structure. - */ -int -xfs_attr3_leaf_getvalue( - struct xfs_buf *bp, - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_name_local *name_loc; - struct xfs_attr_leaf_name_remote *name_rmt; - int valuelen; - - leaf = bp->b_addr; - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - ASSERT(ichdr.count < args->geo->blksize / 8); - ASSERT(args->index < ichdr.count); - - entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; - if (entry->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf, args->index); - ASSERT(name_loc->namelen == args->namelen); - ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0); - valuelen = be16_to_cpu(name_loc->valuelen); - if (args->flags & ATTR_KERNOVAL) { - args->valuelen = valuelen; - return 0; - } - if (args->valuelen < valuelen) { - args->valuelen = valuelen; - return ERANGE; - } - args->valuelen = valuelen; - memcpy(args->value, &name_loc->nameval[args->namelen], valuelen); - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); - ASSERT(name_rmt->namelen == args->namelen); - ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0); - args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen); - args->rmtblkno = be32_to_cpu(name_rmt->valueblk); - args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount, - args->rmtvaluelen); - if (args->flags & ATTR_KERNOVAL) { - args->valuelen = args->rmtvaluelen; - return 0; - } - if (args->valuelen < args->rmtvaluelen) { - args->valuelen = args->rmtvaluelen; - return ERANGE; - } - args->valuelen = args->rmtvaluelen; - } - return 0; -} - -/*======================================================================== - * Utility routines. - *========================================================================*/ - -/* - * Move the indicated entries from one leaf to another. - * NOTE: this routine modifies both source and destination leaves. - */ -/*ARGSUSED*/ -STATIC void -xfs_attr3_leaf_moveents( - struct xfs_da_args *args, - struct xfs_attr_leafblock *leaf_s, - struct xfs_attr3_icleaf_hdr *ichdr_s, - int start_s, - struct xfs_attr_leafblock *leaf_d, - struct xfs_attr3_icleaf_hdr *ichdr_d, - int start_d, - int count) -{ - struct xfs_attr_leaf_entry *entry_s; - struct xfs_attr_leaf_entry *entry_d; - int desti; - int tmp; - int i; - - /* - * Check for nothing to do. - */ - if (count == 0) - return; - - /* - * Set up environment. - */ - ASSERT(ichdr_s->magic == XFS_ATTR_LEAF_MAGIC || - ichdr_s->magic == XFS_ATTR3_LEAF_MAGIC); - ASSERT(ichdr_s->magic == ichdr_d->magic); - ASSERT(ichdr_s->count > 0 && ichdr_s->count < args->geo->blksize / 8); - ASSERT(ichdr_s->firstused >= (ichdr_s->count * sizeof(*entry_s)) - + xfs_attr3_leaf_hdr_size(leaf_s)); - ASSERT(ichdr_d->count < args->geo->blksize / 8); - ASSERT(ichdr_d->firstused >= (ichdr_d->count * sizeof(*entry_d)) - + xfs_attr3_leaf_hdr_size(leaf_d)); - - ASSERT(start_s < ichdr_s->count); - ASSERT(start_d <= ichdr_d->count); - ASSERT(count <= ichdr_s->count); - - - /* - * Move the entries in the destination leaf up to make a hole? - */ - if (start_d < ichdr_d->count) { - tmp = ichdr_d->count - start_d; - tmp *= sizeof(xfs_attr_leaf_entry_t); - entry_s = &xfs_attr3_leaf_entryp(leaf_d)[start_d]; - entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d + count]; - memmove(entry_d, entry_s, tmp); - } - - /* - * Copy all entry's in the same (sorted) order, - * but allocate attribute info packed and in sequence. - */ - entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; - entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d]; - desti = start_d; - for (i = 0; i < count; entry_s++, entry_d++, desti++, i++) { - ASSERT(be16_to_cpu(entry_s->nameidx) >= ichdr_s->firstused); - tmp = xfs_attr_leaf_entsize(leaf_s, start_s + i); -#ifdef GROT - /* - * Code to drop INCOMPLETE entries. Difficult to use as we - * may also need to change the insertion index. Code turned - * off for 6.2, should be revisited later. - */ - if (entry_s->flags & XFS_ATTR_INCOMPLETE) { /* skip partials? */ - memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp); - ichdr_s->usedbytes -= tmp; - ichdr_s->count -= 1; - entry_d--; /* to compensate for ++ in loop hdr */ - desti--; - if ((start_s + i) < offset) - result++; /* insertion index adjustment */ - } else { -#endif /* GROT */ - ichdr_d->firstused -= tmp; - /* both on-disk, don't endian flip twice */ - entry_d->hashval = entry_s->hashval; - entry_d->nameidx = cpu_to_be16(ichdr_d->firstused); - entry_d->flags = entry_s->flags; - ASSERT(be16_to_cpu(entry_d->nameidx) + tmp - <= args->geo->blksize); - memmove(xfs_attr3_leaf_name(leaf_d, desti), - xfs_attr3_leaf_name(leaf_s, start_s + i), tmp); - ASSERT(be16_to_cpu(entry_s->nameidx) + tmp - <= args->geo->blksize); - memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp); - ichdr_s->usedbytes -= tmp; - ichdr_d->usedbytes += tmp; - ichdr_s->count -= 1; - ichdr_d->count += 1; - tmp = ichdr_d->count * sizeof(xfs_attr_leaf_entry_t) - + xfs_attr3_leaf_hdr_size(leaf_d); - ASSERT(ichdr_d->firstused >= tmp); -#ifdef GROT - } -#endif /* GROT */ - } - - /* - * Zero out the entries we just copied. - */ - if (start_s == ichdr_s->count) { - tmp = count * sizeof(xfs_attr_leaf_entry_t); - entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; - ASSERT(((char *)entry_s + tmp) <= - ((char *)leaf_s + args->geo->blksize)); - memset(entry_s, 0, tmp); - } else { - /* - * Move the remaining entries down to fill the hole, - * then zero the entries at the top. - */ - tmp = (ichdr_s->count - count) * sizeof(xfs_attr_leaf_entry_t); - entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s + count]; - entry_d = &xfs_attr3_leaf_entryp(leaf_s)[start_s]; - memmove(entry_d, entry_s, tmp); - - tmp = count * sizeof(xfs_attr_leaf_entry_t); - entry_s = &xfs_attr3_leaf_entryp(leaf_s)[ichdr_s->count]; - ASSERT(((char *)entry_s + tmp) <= - ((char *)leaf_s + args->geo->blksize)); - memset(entry_s, 0, tmp); - } - - /* - * Fill in the freemap information - */ - ichdr_d->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_d); - ichdr_d->freemap[0].base += ichdr_d->count * sizeof(xfs_attr_leaf_entry_t); - ichdr_d->freemap[0].size = ichdr_d->firstused - ichdr_d->freemap[0].base; - ichdr_d->freemap[1].base = 0; - ichdr_d->freemap[2].base = 0; - ichdr_d->freemap[1].size = 0; - ichdr_d->freemap[2].size = 0; - ichdr_s->holes = 1; /* leaf may not be compact */ -} - -/* - * Pick up the last hashvalue from a leaf block. - */ -xfs_dahash_t -xfs_attr_leaf_lasthash( - struct xfs_buf *bp, - int *count) -{ - struct xfs_attr3_icleaf_hdr ichdr; - struct xfs_attr_leaf_entry *entries; - - xfs_attr3_leaf_hdr_from_disk(&ichdr, bp->b_addr); - entries = xfs_attr3_leaf_entryp(bp->b_addr); - if (count) - *count = ichdr.count; - if (!ichdr.count) - return 0; - return be32_to_cpu(entries[ichdr.count - 1].hashval); -} - -/* - * Calculate the number of bytes used to store the indicated attribute - * (whether local or remote only calculate bytes in this block). - */ -STATIC int -xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index) -{ - struct xfs_attr_leaf_entry *entries; - xfs_attr_leaf_name_local_t *name_loc; - xfs_attr_leaf_name_remote_t *name_rmt; - int size; - - entries = xfs_attr3_leaf_entryp(leaf); - if (entries[index].flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf, index); - size = xfs_attr_leaf_entsize_local(name_loc->namelen, - be16_to_cpu(name_loc->valuelen)); - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf, index); - size = xfs_attr_leaf_entsize_remote(name_rmt->namelen); - } - return size; -} - -/* - * Calculate the number of bytes that would be required to store the new - * attribute (whether local or remote only calculate bytes in this block). - * This routine decides as a side effect whether the attribute will be - * a "local" or a "remote" attribute. - */ -int -xfs_attr_leaf_newentsize( - struct xfs_da_args *args, - int *local) -{ - int size; - - size = xfs_attr_leaf_entsize_local(args->namelen, args->valuelen); - if (size < xfs_attr_leaf_entsize_local_max(args->geo->blksize)) { - if (local) - *local = 1; - return size; - } - if (local) - *local = 0; - return xfs_attr_leaf_entsize_remote(args->namelen); -} - - -/*======================================================================== - * Manage the INCOMPLETE flag in a leaf entry - *========================================================================*/ - -/* - * Clear the INCOMPLETE flag on an entry in a leaf block. - */ -int -xfs_attr3_leaf_clearflag( - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_name_remote *name_rmt; - struct xfs_buf *bp; - int error; -#ifdef DEBUG - struct xfs_attr3_icleaf_hdr ichdr; - xfs_attr_leaf_name_local_t *name_loc; - int namelen; - char *name; -#endif /* DEBUG */ - - trace_xfs_attr_leaf_clearflag(args); - /* - * Set up the operation. - */ - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp); - if (error) - return error; - - leaf = bp->b_addr; - entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; - ASSERT(entry->flags & XFS_ATTR_INCOMPLETE); - -#ifdef DEBUG - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - ASSERT(args->index < ichdr.count); - ASSERT(args->index >= 0); - - if (entry->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf, args->index); - namelen = name_loc->namelen; - name = (char *)name_loc->nameval; - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); - namelen = name_rmt->namelen; - name = (char *)name_rmt->name; - } - ASSERT(be32_to_cpu(entry->hashval) == args->hashval); - ASSERT(namelen == args->namelen); - ASSERT(memcmp(name, args->name, namelen) == 0); -#endif /* DEBUG */ - - entry->flags &= ~XFS_ATTR_INCOMPLETE; - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); - - if (args->rmtblkno) { - ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0); - name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); - name_rmt->valueblk = cpu_to_be32(args->rmtblkno); - name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); - } - - /* - * Commit the flag value change and start the next trans in series. - */ - return xfs_trans_roll(&args->trans, args->dp); -} - -/* - * Set the INCOMPLETE flag on an entry in a leaf block. - */ -int -xfs_attr3_leaf_setflag( - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf; - struct xfs_attr_leaf_entry *entry; - struct xfs_attr_leaf_name_remote *name_rmt; - struct xfs_buf *bp; - int error; -#ifdef DEBUG - struct xfs_attr3_icleaf_hdr ichdr; -#endif - - trace_xfs_attr_leaf_setflag(args); - - /* - * Set up the operation. - */ - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp); - if (error) - return error; - - leaf = bp->b_addr; -#ifdef DEBUG - xfs_attr3_leaf_hdr_from_disk(&ichdr, leaf); - ASSERT(args->index < ichdr.count); - ASSERT(args->index >= 0); -#endif - entry = &xfs_attr3_leaf_entryp(leaf)[args->index]; - - ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0); - entry->flags |= XFS_ATTR_INCOMPLETE; - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); - if ((entry->flags & XFS_ATTR_LOCAL) == 0) { - name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index); - name_rmt->valueblk = 0; - name_rmt->valuelen = 0; - xfs_trans_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); - } - - /* - * Commit the flag value change and start the next trans in series. - */ - return xfs_trans_roll(&args->trans, args->dp); -} - -/* - * In a single transaction, clear the INCOMPLETE flag on the leaf entry - * given by args->blkno/index and set the INCOMPLETE flag on the leaf - * entry given by args->blkno2/index2. - * - * Note that they could be in different blocks, or in the same block. - */ -int -xfs_attr3_leaf_flipflags( - struct xfs_da_args *args) -{ - struct xfs_attr_leafblock *leaf1; - struct xfs_attr_leafblock *leaf2; - struct xfs_attr_leaf_entry *entry1; - struct xfs_attr_leaf_entry *entry2; - struct xfs_attr_leaf_name_remote *name_rmt; - struct xfs_buf *bp1; - struct xfs_buf *bp2; - int error; -#ifdef DEBUG - struct xfs_attr3_icleaf_hdr ichdr1; - struct xfs_attr3_icleaf_hdr ichdr2; - xfs_attr_leaf_name_local_t *name_loc; - int namelen1, namelen2; - char *name1, *name2; -#endif /* DEBUG */ - - trace_xfs_attr_leaf_flipflags(args); - - /* - * Read the block containing the "old" attr - */ - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp1); - if (error) - return error; - - /* - * Read the block containing the "new" attr, if it is different - */ - if (args->blkno2 != args->blkno) { - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno2, - -1, &bp2); - if (error) - return error; - } else { - bp2 = bp1; - } - - leaf1 = bp1->b_addr; - entry1 = &xfs_attr3_leaf_entryp(leaf1)[args->index]; - - leaf2 = bp2->b_addr; - entry2 = &xfs_attr3_leaf_entryp(leaf2)[args->index2]; - -#ifdef DEBUG - xfs_attr3_leaf_hdr_from_disk(&ichdr1, leaf1); - ASSERT(args->index < ichdr1.count); - ASSERT(args->index >= 0); - - xfs_attr3_leaf_hdr_from_disk(&ichdr2, leaf2); - ASSERT(args->index2 < ichdr2.count); - ASSERT(args->index2 >= 0); - - if (entry1->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf1, args->index); - namelen1 = name_loc->namelen; - name1 = (char *)name_loc->nameval; - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index); - namelen1 = name_rmt->namelen; - name1 = (char *)name_rmt->name; - } - if (entry2->flags & XFS_ATTR_LOCAL) { - name_loc = xfs_attr3_leaf_name_local(leaf2, args->index2); - namelen2 = name_loc->namelen; - name2 = (char *)name_loc->nameval; - } else { - name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2); - namelen2 = name_rmt->namelen; - name2 = (char *)name_rmt->name; - } - ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval)); - ASSERT(namelen1 == namelen2); - ASSERT(memcmp(name1, name2, namelen1) == 0); -#endif /* DEBUG */ - - ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE); - ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0); - - entry1->flags &= ~XFS_ATTR_INCOMPLETE; - xfs_trans_log_buf(args->trans, bp1, - XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1))); - if (args->rmtblkno) { - ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0); - name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index); - name_rmt->valueblk = cpu_to_be32(args->rmtblkno); - name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen); - xfs_trans_log_buf(args->trans, bp1, - XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt))); - } - - entry2->flags |= XFS_ATTR_INCOMPLETE; - xfs_trans_log_buf(args->trans, bp2, - XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2))); - if ((entry2->flags & XFS_ATTR_LOCAL) == 0) { - name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2); - name_rmt->valueblk = 0; - name_rmt->valuelen = 0; - xfs_trans_log_buf(args->trans, bp2, - XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt))); - } - - /* - * Commit the flag value change and start the next trans in series. - */ - error = xfs_trans_roll(&args->trans, args->dp); - - return error; -} |