]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_attr_leaf.c
xfs: move local to extent inode logging into bmap helper
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_attr_leaf.c
index cc2506822da6d89fb06da92a1376696b5b82b041..0249a0a96c39e97a92481616fd2e5506fee6f861 100644 (file)
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.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 "libxfs_priv.h"
 #include "xfs_fs.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_mount.h"
 #include "xfs_da_format.h"
-#include "xfs_da_btree.h"
 #include "xfs_inode.h"
 #include "xfs_trans.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_trace.h"
-#include "xfs_cksum.h"
 #include "xfs_dir2.h"
 
 
@@ -242,50 +228,92 @@ xfs_attr3_leaf_hdr_to_disk(
        }
 }
 
-static bool
+static xfs_failaddr_t
 xfs_attr3_leaf_verify(
-       struct xfs_buf          *bp)
+       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;
+       struct xfs_attr3_icleaf_hdr     ichdr;
+       struct xfs_mount                *mp = bp->b_mount;
+       struct xfs_attr_leafblock       *leaf = bp->b_addr;
+       struct xfs_attr_leaf_entry      *entries;
+       uint32_t                        end;    /* must be 32bit - see below */
+       int                             i;
+       xfs_failaddr_t                  fa;
 
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
 
-       if (xfs_sb_version_hascrc(&mp->m_sb)) {
-               struct xfs_da3_node_hdr *hdr3 = bp->b_addr;
+       fa = xfs_da3_blkinfo_verify(bp, bp->b_addr);
+       if (fa)
+               return fa;
 
-               if (ichdr.magic != XFS_ATTR3_LEAF_MAGIC)
-                       return false;
+       /*
+        * In recovery there is a transient state where count == 0 is valid
+        * because we may have transitioned an empty shortform attr to a leaf
+        * if the attr didn't fit in shortform.
+        */
+       if (!xfs_log_in_recovery(mp) && ichdr.count == 0)
+               return __this_address;
 
-               if (!uuid_equal(&hdr3->info.uuid, &mp->m_sb.sb_meta_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;
+       /*
+        * firstused is the block offset of the first name info structure.
+        * Make sure it doesn't go off the block or crash into the header.
+        */
+       if (ichdr.firstused > mp->m_attr_geo->blksize)
+               return __this_address;
+       if (ichdr.firstused < xfs_attr3_leaf_hdr_size(leaf))
+               return __this_address;
+
+       /* Make sure the entries array doesn't crash into the name info. */
+       entries = xfs_attr3_leaf_entryp(bp->b_addr);
+       if ((char *)&entries[ichdr.count] >
+           (char *)bp->b_addr + ichdr.firstused)
+               return __this_address;
 
        /* XXX: need to range check rest of attr header values */
        /* XXX: hash order check? */
 
-       return true;
+       /*
+        * Quickly check the freemap information.  Attribute data has to be
+        * aligned to 4-byte boundaries, and likewise for the free space.
+        *
+        * Note that for 64k block size filesystems, the freemap entries cannot
+        * overflow as they are only be16 fields. However, when checking end
+        * pointer of the freemap, we have to be careful to detect overflows and
+        * so use uint32_t for those checks.
+        */
+       for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
+               if (ichdr.freemap[i].base > mp->m_attr_geo->blksize)
+                       return __this_address;
+               if (ichdr.freemap[i].base & 0x3)
+                       return __this_address;
+               if (ichdr.freemap[i].size > mp->m_attr_geo->blksize)
+                       return __this_address;
+               if (ichdr.freemap[i].size & 0x3)
+                       return __this_address;
+
+               /* be care of 16 bit overflows here */
+               end = (uint32_t)ichdr.freemap[i].base + ichdr.freemap[i].size;
+               if (end < ichdr.freemap[i].base)
+                       return __this_address;
+               if (end > mp->m_attr_geo->blksize)
+                       return __this_address;
+       }
+
+       return NULL;
 }
 
 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_mount        *mp = bp->b_mount;
+       struct xfs_buf_log_item *bip = bp->b_log_item;
        struct xfs_attr3_leaf_hdr *hdr3 = bp->b_addr;
+       xfs_failaddr_t          fa;
 
-       if (!xfs_attr3_leaf_verify(bp)) {
-               xfs_buf_ioerror(bp, -EFSCORRUPTED);
-               xfs_verifier_error(bp);
+       fa = xfs_attr3_leaf_verify(bp);
+       if (fa) {
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
                return;
        }
 
@@ -308,21 +336,26 @@ static void
 xfs_attr3_leaf_read_verify(
        struct xfs_buf          *bp)
 {
-       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_mount        *mp = bp->b_mount;
+       xfs_failaddr_t          fa;
 
        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);
+               xfs_verifier_error(bp, -EFSBADCRC, __this_address);
+       else {
+               fa = xfs_attr3_leaf_verify(bp);
+               if (fa)
+                       xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+       }
 }
 
 const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = {
+       .name = "xfs_attr3_leaf",
+       .magic16 = { cpu_to_be16(XFS_ATTR_LEAF_MAGIC),
+                    cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) },
        .verify_read = xfs_attr3_leaf_read_verify,
        .verify_write = xfs_attr3_leaf_write_verify,
+       .verify_struct = xfs_attr3_leaf_verify,
 };
 
 int
@@ -337,7 +370,7 @@ xfs_attr3_leaf_read(
 
        err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
                                XFS_ATTR_FORK, &xfs_attr3_leaf_buf_ops);
-       if (!err && tp)
+       if (!err && tp && *bpp)
                xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF);
        return err;
 }
@@ -356,6 +389,50 @@ 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);
 }
 
+static int
+xfs_attr_copy_value(
+       struct xfs_da_args      *args,
+       unsigned char           *value,
+       int                     valuelen)
+{
+       /*
+        * No copy if all we have to do is get the length
+        */
+       if (args->flags & ATTR_KERNOVAL) {
+               args->valuelen = valuelen;
+               return 0;
+       }
+
+       /*
+        * No copy if the length of the existing buffer is too small
+        */
+       if (args->valuelen < valuelen) {
+               args->valuelen = valuelen;
+               return -ERANGE;
+       }
+
+       if (args->op_flags & XFS_DA_OP_ALLOCVAL) {
+               args->value = kmem_alloc_large(valuelen, 0);
+               if (!args->value)
+                       return -ENOMEM;
+       }
+       args->valuelen = valuelen;
+
+       /* remote block xattr requires IO for copy-in */
+       if (args->rmtblkno)
+               return xfs_attr_rmtval_get(args);
+
+       /*
+        * This is to prevent a GCC warning because the remote xattr case
+        * doesn't have a value to pass in. In that case, we never reach here,
+        * but GCC can't work that out and so throws a "passing NULL to
+        * memcpy" warning.
+        */
+       if (!value)
+               return -EINVAL;
+       memcpy(args->value, value, valuelen);
+       return 0;
+}
 
 /*========================================================================
  * External routines when attribute fork size < XFS_LITINO(mp).
@@ -383,13 +460,9 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
        /* rounded down */
        offset = (XFS_LITINO(mp, dp->i_d.di_version) - bytes) >> 3;
 
-       switch (dp->i_d.di_format) {
-       case XFS_DINODE_FMT_DEV:
+       if (dp->i_d.di_format == 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;
        }
 
        /*
@@ -418,7 +491,7 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
        switch (dp->i_d.di_format) {
        case XFS_DINODE_FMT_EXTENTS:
                /*
-                * If there is no attr fork and the data fork is 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
@@ -448,7 +521,7 @@ xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
         * 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 = 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 */
@@ -489,7 +562,7 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
 {
        xfs_attr_sf_hdr_t *hdr;
        xfs_inode_t *dp;
-       xfs_ifork_t *ifp;
+       struct xfs_ifork *ifp;
 
        trace_xfs_attr_sf_create(args);
 
@@ -524,7 +597,7 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
        int i, offset, size;
        xfs_mount_t *mp;
        xfs_inode_t *dp;
-       xfs_ifork_t *ifp;
+       struct xfs_ifork *ifp;
 
        trace_xfs_attr_sf_add(args);
 
@@ -665,7 +738,7 @@ 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;
+       struct xfs_ifork *ifp;
 
        trace_xfs_attr_sf_lookup(args);
 
@@ -687,15 +760,19 @@ xfs_attr_shortform_lookup(xfs_da_args_t *args)
 }
 
 /*
- * Look up a name in a shortform attribute list structure.
+ * Retreive the attribute value and length.
+ *
+ * If ATTR_KERNOVAL is specified, only the length needs to be returned.
+ * Unlike a lookup, we only return an error if the attribute does not
+ * exist or we can't retrieve the value.
  */
-/*ARGSUSED*/
 int
-xfs_attr_shortform_getvalue(xfs_da_args_t *args)
+xfs_attr_shortform_getvalue(
+       struct xfs_da_args      *args)
 {
-       xfs_attr_shortform_t *sf;
-       xfs_attr_sf_entry_t *sfe;
-       int i;
+       struct xfs_attr_shortform *sf;
+       struct xfs_attr_sf_entry *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;
@@ -708,37 +785,30 @@ xfs_attr_shortform_getvalue(xfs_da_args_t *args)
                        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 xfs_attr_copy_value(args, &sfe->nameval[args->namelen],
+                                               sfe->valuelen);
        }
        return -ENOATTR;
 }
 
 /*
- * Convert from using the shortform to the leaf.
+ * Convert from using the shortform to the leaf.  On success, return the
+ * buffer so that we can keep it locked until we're totally done with it.
  */
 int
-xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
+xfs_attr_shortform_to_leaf(
+       struct xfs_da_args              *args,
+       struct xfs_buf                  **leaf_bp)
 {
-       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;
+       struct xfs_inode                *dp;
+       struct xfs_attr_shortform       *sf;
+       struct xfs_attr_sf_entry        *sfe;
+       struct xfs_da_args              nargs;
+       char                            *tmpbuffer;
+       int                             error, i, size;
+       xfs_dablk_t                     blkno;
+       struct xfs_buf                  *bp;
+       struct xfs_ifork                *ifp;
 
        trace_xfs_attr_sf_to_leaf(args);
 
@@ -746,45 +816,27 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
        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);
+       tmpbuffer = kmem_alloc(size, 0);
        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);
+       xfs_bmap_local_to_extents_empty(args->trans, 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 */
+       if (error)
                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 */
+       if (error)
                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;
@@ -808,7 +860,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
                sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
        }
        error = 0;
-
+       *leaf_bp = bp;
 out:
        kmem_free(tmpbuffer);
        return error;
@@ -829,7 +881,7 @@ xfs_attr_shortform_allfit(
        struct xfs_attr3_icleaf_hdr leafhdr;
        int                     bytes;
        int                     i;
-       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_mount        *mp = bp->b_mount;
 
        leaf = bp->b_addr;
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
@@ -857,6 +909,80 @@ xfs_attr_shortform_allfit(
        return xfs_attr_shortform_bytesfit(dp, bytes);
 }
 
+/* Verify the consistency of an inline attribute fork. */
+xfs_failaddr_t
+xfs_attr_shortform_verify(
+       struct xfs_inode                *ip)
+{
+       struct xfs_attr_shortform       *sfp;
+       struct xfs_attr_sf_entry        *sfep;
+       struct xfs_attr_sf_entry        *next_sfep;
+       char                            *endp;
+       struct xfs_ifork                *ifp;
+       int                             i;
+       int                             size;
+
+       ASSERT(ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL);
+       ifp = XFS_IFORK_PTR(ip, XFS_ATTR_FORK);
+       sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
+       size = ifp->if_bytes;
+
+       /*
+        * Give up if the attribute is way too short.
+        */
+       if (size < sizeof(struct xfs_attr_sf_hdr))
+               return __this_address;
+
+       endp = (char *)sfp + size;
+
+       /* Check all reported entries */
+       sfep = &sfp->list[0];
+       for (i = 0; i < sfp->hdr.count; i++) {
+               /*
+                * struct xfs_attr_sf_entry has a variable length.
+                * Check the fixed-offset parts of the structure are
+                * within the data buffer.
+                */
+               if (((char *)sfep + sizeof(*sfep)) >= endp)
+                       return __this_address;
+
+               /* Don't allow names with known bad length. */
+               if (sfep->namelen == 0)
+                       return __this_address;
+
+               /*
+                * Check that the variable-length part of the structure is
+                * within the data buffer.  The next entry starts after the
+                * name component, so nextentry is an acceptable test.
+                */
+               next_sfep = XFS_ATTR_SF_NEXTENTRY(sfep);
+               if ((char *)next_sfep > endp)
+                       return __this_address;
+
+               /*
+                * Check for unknown flags.  Short form doesn't support
+                * the incomplete or local bits, so we can use the namespace
+                * mask here.
+                */
+               if (sfep->flags & ~XFS_ATTR_NSP_ONDISK_MASK)
+                       return __this_address;
+
+               /*
+                * Check for invalid namespace combinations.  We only allow
+                * one namespace flag per xattr, so we can just count the
+                * bits (i.e. hweight) here.
+                */
+               if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
+                       return __this_address;
+
+               sfep = next_sfep;
+       }
+       if ((void *)sfep != (void *)endp)
+               return __this_address;
+
+       return NULL;
+}
+
 /*
  * Convert a leaf attribute list to shortform attribute list
  */
@@ -878,7 +1004,7 @@ xfs_attr3_leaf_to_shortform(
 
        trace_xfs_attr_leaf_to_sf(args);
 
-       tmpbuffer = kmem_alloc(args->geo->blksize, KM_SLEEP);
+       tmpbuffer = kmem_alloc(args->geo->blksize, 0);
        if (!tmpbuffer)
                return -ENOMEM;
 
@@ -913,8 +1039,6 @@ xfs_attr3_leaf_to_shortform(
        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;
@@ -1343,7 +1467,7 @@ xfs_attr3_leaf_compact(
 
        trace_xfs_attr_leaf_compact(args);
 
-       tmpbuffer = kmem_alloc(args->geo->blksize, KM_SLEEP);
+       tmpbuffer = kmem_alloc(args->geo->blksize, 0);
        memcpy(tmpbuffer, bp->b_addr, args->geo->blksize);
        memset(bp->b_addr, 0, args->geo->blksize);
        leaf_src = (xfs_attr_leafblock_t *)tmpbuffer;
@@ -1417,7 +1541,7 @@ xfs_attr_leaf_order(
 {
        struct xfs_attr3_icleaf_hdr ichdr1;
        struct xfs_attr3_icleaf_hdr ichdr2;
-       struct xfs_mount *mp = leaf1_bp->b_target->bt_mount;
+       struct xfs_mount *mp = leaf1_bp->b_mount;
 
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr1, leaf1_bp->b_addr);
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr2, leaf2_bp->b_addr);
@@ -1477,17 +1601,10 @@ xfs_attr3_leaf_rebalance(
         */
        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;
+               swap(blk1, blk2);
 
-               /* struct copies to swap them rather than reconverting */
-               tmp_ichdr = ichdr1;
-               ichdr1 = ichdr2;
-               ichdr2 = tmp_ichdr;
+               /* swap structures rather than reconverting them */
+               swap(ichdr1, ichdr2);
 
                leaf1 = blk1->bp->b_addr;
                leaf2 = blk2->bp->b_addr;
@@ -2069,7 +2186,7 @@ xfs_attr3_leaf_unbalance(
                struct xfs_attr_leafblock *tmp_leaf;
                struct xfs_attr3_icleaf_hdr tmphdr;
 
-               tmp_leaf = kmem_zalloc(state->args->geo->blksize, KM_SLEEP);
+               tmp_leaf = kmem_zalloc(state->args->geo->blksize, 0);
 
                /*
                 * Copy the header into the temp leaf so that all the stuff
@@ -2160,7 +2277,8 @@ xfs_attr3_leaf_lookup_int(
        leaf = bp->b_addr;
        xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
        entries = xfs_attr3_leaf_entryp(leaf);
-       ASSERT(ichdr.count < args->geo->blksize / 8);
+       if (ichdr.count >= args->geo->blksize / 8)
+               return -EFSCORRUPTED;
 
        /*
         * Binary search.  (note: small blocks will skip this loop)
@@ -2176,8 +2294,10 @@ xfs_attr3_leaf_lookup_int(
                else
                        break;
        }
-       ASSERT(probe >= 0 && (!ichdr.count || probe < ichdr.count));
-       ASSERT(span <= 4 || be32_to_cpu(entry->hashval) == hashval);
+       if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count)))
+               return -EFSCORRUPTED;
+       if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval))
+               return -EFSCORRUPTED;
 
        /*
         * Since we may have duplicate hashval's, find the first matching
@@ -2249,6 +2369,10 @@ xfs_attr3_leaf_lookup_int(
 /*
  * Get the value associated with an attribute name from a leaf attribute
  * list structure.
+ *
+ * If ATTR_KERNOVAL is specified, only the length needs to be returned.
+ * Unlike a lookup, we only return an error if the attribute does not
+ * exist or we can't retrieve the value.
  */
 int
 xfs_attr3_leaf_getvalue(
@@ -2260,7 +2384,6 @@ xfs_attr3_leaf_getvalue(
        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(args->geo, &ichdr, leaf);
@@ -2272,36 +2395,19 @@ xfs_attr3_leaf_getvalue(
                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;
+               return xfs_attr_copy_value(args,
+                                       &name_loc->nameval[args->namelen],
+                                       be16_to_cpu(name_loc->valuelen));
+       }
+
+       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);
+       return xfs_attr_copy_value(args, NULL, args->rmtvaluelen);
 }
 
 /*========================================================================
@@ -2464,7 +2570,7 @@ xfs_attr_leaf_lasthash(
 {
        struct xfs_attr3_icleaf_hdr ichdr;
        struct xfs_attr_leaf_entry *entries;
-       struct xfs_mount *mp = bp->b_target->bt_mount;
+       struct xfs_mount *mp = bp->b_mount;
 
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, bp->b_addr);
        entries = xfs_attr3_leaf_entryp(bp->b_addr);
@@ -2594,7 +2700,7 @@ xfs_attr3_leaf_clearflag(
        /*
         * Commit the flag value change and start the next trans in series.
         */
-       return xfs_trans_roll(&args->trans, args->dp);
+       return xfs_trans_roll_inode(&args->trans, args->dp);
 }
 
 /*
@@ -2645,7 +2751,7 @@ xfs_attr3_leaf_setflag(
        /*
         * Commit the flag value change and start the next trans in series.
         */
-       return xfs_trans_roll(&args->trans, args->dp);
+       return xfs_trans_roll_inode(&args->trans, args->dp);
 }
 
 /*
@@ -2763,7 +2869,7 @@ xfs_attr3_leaf_flipflags(
        /*
         * Commit the flag value change and start the next trans in series.
         */
-       error = xfs_trans_roll(&args->trans, args->dp);
+       error = xfs_trans_roll_inode(&args->trans, args->dp);
 
        return error;
 }