]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/attr_repair.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / repair / attr_repair.c
index 62f80e7abd65646244fecefbb249932a9cc2c271..5ad81c091ea4d3317d1288ee07166ea6e700a52b 100644 (file)
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2002,2004-2005 Silicon Graphics, 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.h"
@@ -24,6 +12,7 @@
 #include "bmap.h"
 #include "protos.h"
 #include "dir2.h"
+#include "da_util.h"
 
 static int xfs_acl_valid(struct xfs_mount *mp, struct xfs_acl *daclp);
 static int xfs_mac_valid(xfs_mac_label_t *lp);
@@ -34,51 +23,9 @@ static int xfs_mac_valid(xfs_mac_label_t *lp);
  * dir v1 code, but that format is no longer supported yb the userspace
  * utilities and hence is now specific to the attribute tree implementation.
  */
-#define XR_DA_LEAF_MAPSIZE     XFS_ATTR_LEAF_MAPSIZE
 
 typedef unsigned char  da_freemap_t;
 
-/*
- * the cursor gets passed up and down the da btree processing
- * routines.  The interior block processing routines use the
- * cursor to determine if the pointers to and from the preceding
- * and succeeding sibling blocks are ok and whether the values in
- * the current block are consistent with the entries in the parent
- * nodes.  When a block is traversed, a parent-verification routine
- * is called to verify if the next logical entry in the next level up
- * is consistent with the greatest hashval in the next block of the
- * current level.  The verification routine is itself recursive and
- * calls itself if it has to traverse an interior block to get
- * the next logical entry.  The routine recurses upwards through
- * the tree until it finds a block where it can simply step to
- * the next entry.  The hashval in that entry should be equal to
- * the hashval being passed to it (the greatest hashval in the block
- * that the entry points to).  If that isn't true, then the tree
- * is blown and we need to trash it, salvage and trash it, or fix it.
- * Currently, we just trash it.
- */
-typedef struct da_level_state  {
-       xfs_buf_t       *bp;            /* block bp */
-#ifdef XR_DIR_TRACE
-       xfs_da_intnode_t *n;            /* bp data */
-#endif
-       xfs_dablk_t     bno;            /* file block number */
-       xfs_dahash_t    hashval;        /* last verified hashval */
-       int             index;          /* current index in block */
-       int             dirty;          /* is buffer dirty ? (1 == yes) */
-} da_level_state_t;
-
-typedef struct da_bt_cursor  {
-       int                     active; /* highest level in tree (# levels-1) */
-       int                     type;   /* 0 if dir, 1 if attr */
-       xfs_ino_t               ino;
-       xfs_dablk_t             greatest_bno;
-       xfs_dinode_t            *dip;
-       da_level_state_t        level[XFS_DA_NODE_MAXDEPTH];
-       struct blkmap           *blkmap;
-} da_bt_cursor_t;
-
-
 /*
  * Allocate a freespace map for directory or attr leaf blocks (1 bit per byte)
  * 1 == used, 0 == free.
@@ -130,559 +77,6 @@ set_da_freemap(xfs_mount_t *mp, da_freemap_t *map, int start, int stop)
        return(0);
 }
 
-/*
- * walk tree from root to the left-most leaf block reading in
- * blocks and setting up cursor.  passes back file block number of the
- * left-most leaf block if successful (bno).  returns 1 if successful,
- * 0 if unsuccessful.
- */
-static int
-traverse_int_dablock(xfs_mount_t       *mp,
-               da_bt_cursor_t          *da_cursor,
-               xfs_dablk_t             *rbno,
-               int                     whichfork)
-{
-       xfs_dablk_t             bno;
-       int                     i;
-       xfs_da_intnode_t        *node;
-       xfs_fsblock_t           fsbno;
-       xfs_buf_t               *bp;
-       struct xfs_da_node_entry *btree;
-       struct xfs_da3_icnode_hdr nodehdr;
-
-       /*
-        * traverse down left-side of tree until we hit the
-        * left-most leaf block setting up the btree cursor along
-        * the way.
-        */
-       bno = 0;
-       i = -1;
-       node = NULL;
-       da_cursor->active = 0;
-
-       do {
-               /*
-                * read in each block along the way and set up cursor
-                */
-               fsbno = blkmap_get(da_cursor->blkmap, bno);
-
-               if (fsbno == NULLFSBLOCK)
-                       goto error_out;
-
-               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
-                               XFS_FSB_TO_BB(mp, 1), 0, &xfs_da3_node_buf_ops);
-               if (!bp) {
-                       if (whichfork == XFS_DATA_FORK)
-                               do_warn(
-       _("can't read block %u (fsbno %" PRIu64 ") for directory inode %" PRIu64 "\n"),
-                                       bno, fsbno, da_cursor->ino);
-                       else
-                               do_warn(
-       _("can't read block %u (fsbno %" PRIu64 ") for attrbute fork of inode %" PRIu64 "\n"),
-                                       bno, fsbno, da_cursor->ino);
-                       goto error_out;
-               }
-
-               node = (xfs_da_intnode_t *)XFS_BUF_PTR(bp);
-               btree = M_DIROPS(mp)->node_tree_p(node);
-               M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node);
-
-               if (nodehdr.magic != XFS_DA_NODE_MAGIC &&
-                   nodehdr.magic != XFS_DA3_NODE_MAGIC)  {
-                       do_warn(_("bad dir/attr magic number in inode %" PRIu64 ", "
-                                 "file bno = %u, fsbno = %" PRIu64 "\n"),
-                               da_cursor->ino, bno, fsbno);
-                       libxfs_putbuf(bp);
-                       goto error_out;
-               }
-
-               if (nodehdr.count > mp->m_attr_geo->node_ents)  {
-                       do_warn(_("bad record count in inode %" PRIu64 ", "
-                                 "count = %d, max = %d\n"),
-                               da_cursor->ino,
-                               nodehdr.count,
-                               mp->m_attr_geo->node_ents);
-                       libxfs_putbuf(bp);
-                       goto error_out;
-               }
-
-               /*
-                * maintain level counter
-                */
-               if (i == -1)
-                       i = da_cursor->active = nodehdr.level;
-               else  {
-                       if (nodehdr.level == i - 1)  {
-                               i--;
-                       } else  {
-                               if (whichfork == XFS_DATA_FORK)
-                                       do_warn(_("bad directory btree for "
-                                                 "directory inode %" PRIu64 "\n"),
-                                               da_cursor->ino);
-                               else
-                                       do_warn(_("bad attribute fork btree "
-                                                 "for inode %" PRIu64 "\n"),
-                                               da_cursor->ino);
-                               libxfs_putbuf(bp);
-                               goto error_out;
-                       }
-               }
-
-               da_cursor->level[i].hashval = be32_to_cpu(btree[0].hashval);
-               da_cursor->level[i].bp = bp;
-               da_cursor->level[i].bno = bno;
-               da_cursor->level[i].index = 0;
-#ifdef XR_DIR_TRACE
-               da_cursor->level[i].n = XFS_BUF_TO_DA_INTNODE(bp);
-#endif
-
-               /*
-                * set up new bno for next level down
-                */
-               bno = be32_to_cpu(btree[0].before);
-       } while (node != NULL && i > 1);
-
-       /*
-        * now return block number and get out
-        */
-       *rbno = da_cursor->level[0].bno = bno;
-       return(1);
-
-error_out:
-       while (i > 1 && i <= da_cursor->active)  {
-               libxfs_putbuf(da_cursor->level[i].bp);
-               i++;
-       }
-
-       return(0);
-}
-
-/*
- * blow out buffer for this level and all the rest above as well
- * if error == 0, we are not expecting to encounter any unreleased
- * buffers (e.g. if we do, it's a mistake).  if error == 1, we're
- * in an error-handling case so unreleased buffers may exist.
- */
-static void
-release_da_cursor_int(xfs_mount_t      *mp,
-                       da_bt_cursor_t  *cursor,
-                       int             prev_level,
-                       int             error)
-{
-       int     level = prev_level + 1;
-
-       if (cursor->level[level].bp != NULL)  {
-               if (!error)  {
-                       do_warn(_("release_da_cursor_int got unexpected "
-                                 "non-null bp, dabno = %u\n"),
-                               cursor->level[level].bno);
-               }
-               ASSERT(error != 0);
-
-               libxfs_putbuf(cursor->level[level].bp);
-               cursor->level[level].bp = NULL;
-       }
-
-       if (level < cursor->active)
-               release_da_cursor_int(mp, cursor, level, error);
-
-       return;
-}
-
-static void
-release_da_cursor(xfs_mount_t  *mp,
-               da_bt_cursor_t  *cursor,
-               int             prev_level)
-{
-       release_da_cursor_int(mp, cursor, prev_level, 0);
-}
-
-static void
-err_release_da_cursor(xfs_mount_t      *mp,
-                       da_bt_cursor_t  *cursor,
-                       int             prev_level)
-{
-       release_da_cursor_int(mp, cursor, prev_level, 1);
-}
-
-/*
- * make sure that all entries in all blocks along the right side of
- * of the tree are used and hashval's are consistent.  level is the
- * level of the descendent block.  returns 0 if good (even if it had
- * to be fixed up), and 1 if bad.  The right edge of the tree is
- * technically a block boundary.  this routine should be used then
- * instead of verify_da_path().
- */
-static int
-verify_final_da_path(xfs_mount_t       *mp,
-               da_bt_cursor_t          *cursor,
-               const int               p_level)
-{
-       xfs_da_intnode_t        *node;
-       xfs_dahash_t            hashval;
-       int                     bad = 0;
-       int                     entry;
-       int                     this_level = p_level + 1;
-       struct xfs_da_node_entry *btree;
-       struct xfs_da3_icnode_hdr nodehdr;
-
-#ifdef XR_DIR_TRACE
-       fprintf(stderr, "in verify_final_da_path, this_level = %d\n",
-               this_level);
-#endif
-       /*
-        * the index should point to the next "unprocessed" entry
-        * in the block which should be the final (rightmost) entry
-        */
-       entry = cursor->level[this_level].index;
-       node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp);
-       btree = M_DIROPS(mp)->node_tree_p(node);
-       M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node);
-
-       /*
-        * check internal block consistency on this level -- ensure
-        * that all entries are used, encountered and expected hashvals
-        * match, etc.
-        */
-       if (entry != nodehdr.count - 1)  {
-               do_warn(_("directory/attribute block used/count "
-                         "inconsistency - %d/%hu\n"),
-                       entry, nodehdr.count);
-               bad++;
-       }
-       /*
-        * hash values monotonically increasing ???
-        */
-       if (cursor->level[this_level].hashval >= 
-                               be32_to_cpu(btree[entry].hashval)) {
-               do_warn(_("directory/attribute block hashvalue inconsistency, "
-                         "expected > %u / saw %u\n"),
-                       cursor->level[this_level].hashval,
-                       be32_to_cpu(btree[entry].hashval));
-               bad++;
-       }
-       if (nodehdr.forw != 0)  {
-               do_warn(_("bad directory/attribute forward block pointer, "
-                         "expected 0, saw %u\n"),
-                       nodehdr.forw);
-               bad++;
-       }
-       if (bad) {
-               do_warn(_("bad directory block in dir ino %" PRIu64 "\n"),
-                       cursor->ino);
-               return(1);
-       }
-       /*
-        * keep track of greatest block # -- that gets
-        * us the length of the directory
-        */
-       if (cursor->level[this_level].bno > cursor->greatest_bno)
-               cursor->greatest_bno = cursor->level[this_level].bno;
-
-       /*
-        * ok, now check descendant block number against this level
-        */
-       if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before)) {
-#ifdef XR_DIR_TRACE
-               fprintf(stderr, "bad directory btree pointer, child bno should "
-                               "be %d, block bno is %d, hashval is %u\n",
-                       be16_to_cpu(btree[entry].before),
-                       cursor->level[p_level].bno,
-                       cursor->level[p_level].hashval);
-               fprintf(stderr, "verify_final_da_path returns 1 (bad) #1a\n");
-#endif
-               return(1);
-       }
-
-       if (cursor->level[p_level].hashval != be32_to_cpu(btree[entry].hashval)) {
-               if (!no_modify)  {
-                       do_warn(_("correcting bad hashval in non-leaf "
-                                 "dir/attr block\n\tin (level %d) in "
-                                 "inode %" PRIu64 ".\n"),
-                               this_level, cursor->ino);
-                       btree[entry].hashval = cpu_to_be32(
-                                               cursor->level[p_level].hashval);
-                       cursor->level[this_level].dirty++;
-               } else  {
-                       do_warn(_("would correct bad hashval in non-leaf "
-                                 "dir/attr block\n\tin (level %d) in "
-                                 "inode %" PRIu64 ".\n"),
-                               this_level, cursor->ino);
-               }
-       }
-
-       /*
-        * Note: squirrel hashval away _before_ releasing the
-        * buffer, preventing a use-after-free problem.
-        */
-       hashval = be32_to_cpu(btree[entry].hashval);
-
-       /*
-        * release/write buffer
-        */
-       ASSERT(cursor->level[this_level].dirty == 0 ||
-               (cursor->level[this_level].dirty && !no_modify));
-
-       if (cursor->level[this_level].dirty && !no_modify)
-               libxfs_writebuf(cursor->level[this_level].bp, 0);
-       else
-               libxfs_putbuf(cursor->level[this_level].bp);
-
-       cursor->level[this_level].bp = NULL;
-
-       /*
-        * bail out if this is the root block (top of tree)
-        */
-       if (this_level >= cursor->active)  {
-#ifdef XR_DIR_TRACE
-               fprintf(stderr, "verify_final_da_path returns 0 (ok)\n");
-#endif
-               return(0);
-       }
-       /*
-        * set hashvalue to correctly reflect the now-validated
-        * last entry in this block and continue upwards validation
-        */
-       cursor->level[this_level].hashval = hashval;
-       return(verify_final_da_path(mp, cursor, this_level));
-}
-
-/*
- * Verifies the path from a descendant block up to the root.
- * Should be called when the descendant level traversal hits
- * a block boundary before crossing the boundary (reading in a new
- * block).
- *
- * the directory/attr btrees work differently to the other fs btrees.
- * each interior block contains records that are <hashval, bno>
- * pairs.  The bno is a file bno, not a filesystem bno.  The last
- * hashvalue in the block <bno> will be <hashval>.  BUT unlike
- * the freespace btrees, the *last* value in each block gets
- * propagated up the tree instead of the first value in each block.
- * that is, the interior records point to child blocks and the *greatest*
- * hash value contained by the child block is the one the block above
- * uses as the key for the child block.
- *
- * level is the level of the descendent block.  returns 0 if good,
- * and 1 if bad.  The descendant block may be a leaf block.
- *
- * the invariant here is that the values in the cursor for the
- * levels beneath this level (this_level) and the cursor index
- * for this level *must* be valid.
- *
- * that is, the hashval/bno info is accurate for all
- * DESCENDANTS and match what the node[index] information
- * for the current index in the cursor for this level.
- *
- * the index values in the cursor for the descendant level
- * are allowed to be off by one as they will reflect the
- * next entry at those levels to be processed.
- *
- * the hashvalue for the current level can't be set until
- * we hit the last entry in the block so, it's garbage
- * until set by this routine.
- *
- * bno and bp for the current block/level are always valid
- * since they have to be set so we can get a buffer for the
- * block.
- */
-static int
-verify_da_path(xfs_mount_t     *mp,
-       da_bt_cursor_t          *cursor,
-       const int               p_level)
-{
-       xfs_da_intnode_t        *node;
-       xfs_da_intnode_t        *newnode;
-       xfs_fsblock_t           fsbno;
-       xfs_dablk_t             dabno;
-       xfs_buf_t               *bp;
-       int                     bad;
-       int                     entry;
-       int                     this_level = p_level + 1;
-       struct xfs_da_node_entry *btree;
-       struct xfs_da3_icnode_hdr nodehdr;
-
-       /*
-        * index is currently set to point to the entry that
-        * should be processed now in this level.
-        */
-       entry = cursor->level[this_level].index;
-       node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp);
-       btree = M_DIROPS(mp)->node_tree_p(node);
-       M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node);
-
-       /*
-        * if this block is out of entries, validate this
-        * block and move on to the next block.
-        * and update cursor value for said level
-        */
-       if (entry >= nodehdr.count)  {
-               /*
-                * update the hash value for this level before
-                * validating it.  bno value should be ok since
-                * it was set when the block was first read in.
-                */
-               cursor->level[this_level].hashval =
-                               be32_to_cpu(btree[entry - 1].hashval);
-
-               /*
-                * keep track of greatest block # -- that gets
-                * us the length of the directory
-                */
-               if (cursor->level[this_level].bno > cursor->greatest_bno)
-                       cursor->greatest_bno = cursor->level[this_level].bno;
-
-               /*
-                * validate the path for the current used-up block
-                * before we trash it
-                */
-               if (verify_da_path(mp, cursor, this_level))
-                       return(1);
-               /*
-                * ok, now get the next buffer and check sibling pointers
-                */
-               dabno = nodehdr.forw;
-               ASSERT(dabno != 0);
-               fsbno = blkmap_get(cursor->blkmap, dabno);
-
-               if (fsbno == NULLFSBLOCK) {
-                       do_warn(_("can't get map info for block %u "
-                                 "of directory inode %" PRIu64 "\n"),
-                               dabno, cursor->ino);
-                       return(1);
-               }
-
-               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
-                               XFS_FSB_TO_BB(mp, 1), 0, &xfs_da3_node_buf_ops);
-               if (!bp) {
-                       do_warn(
-       _("can't read block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
-                               dabno, fsbno, cursor->ino);
-                       return(1);
-               }
-
-               newnode = (xfs_da_intnode_t *)XFS_BUF_PTR(bp);
-               btree = M_DIROPS(mp)->node_tree_p(node);
-               M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, newnode);
-
-               /*
-                * verify magic number and back pointer, sanity-check
-                * entry count, verify level
-                */
-               bad = 0;
-               if (nodehdr.magic != XFS_DA_NODE_MAGIC ||
-                   nodehdr.magic != XFS_DA3_NODE_MAGIC)  {
-                       do_warn(
-       _("bad magic number %x in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
-                               nodehdr.magic,
-                               dabno, fsbno, cursor->ino);
-                       bad++;
-               }
-               if (nodehdr.back != cursor->level[this_level].bno) {
-                       do_warn(
-       _("bad back pointer in block %u (%"PRIu64 ") for directory inode %" PRIu64 "\n"),
-                               dabno, fsbno, cursor->ino);
-                       bad++;
-               }
-               if (nodehdr.count > mp->m_attr_geo->node_ents) {
-                       do_warn(
-       _("entry count %d too large in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
-                               nodehdr.count,
-                               dabno, fsbno, cursor->ino);
-                       bad++;
-               }
-               if (nodehdr.level != this_level) {
-                       do_warn(
-       _("bad level %d in block %u (%" PRIu64 ") for directory inode %" PRIu64 "\n"),
-                               nodehdr.level,
-                               dabno, fsbno, cursor->ino);
-                       bad++;
-               }
-               if (bad)  {
-#ifdef XR_DIR_TRACE
-                       fprintf(stderr, "verify_da_path returns 1 (bad) #4\n");
-#endif
-                       libxfs_putbuf(bp);
-                       return(1);
-               }
-
-               /*
-                * update cursor, write out the *current* level if
-                * required.  don't write out the descendant level
-                */
-               ASSERT(cursor->level[this_level].dirty == 0 ||
-                       (cursor->level[this_level].dirty && !no_modify));
-
-               if (cursor->level[this_level].dirty && !no_modify)
-                       libxfs_writebuf(cursor->level[this_level].bp, 0);
-               else
-                       libxfs_putbuf(cursor->level[this_level].bp);
-
-               /* switch cursor to point at the new buffer we just read */
-               cursor->level[this_level].bp = bp;
-               cursor->level[this_level].dirty = 0;
-               cursor->level[this_level].bno = dabno;
-               cursor->level[this_level].hashval =
-                                       be32_to_cpu(btree[0].hashval);
-#ifdef XR_DIR_TRACE
-               cursor->level[this_level].n = newnode;
-#endif
-               entry = cursor->level[this_level].index = 0;
-
-               /*
-                * We want to rewrite the buffer on a CRC error seeing as it
-                * contains what appears to be a valid node block, but only if
-                * we are fixing errors.
-                */
-               if (bp->b_error == -EFSBADCRC && !no_modify)
-                       cursor->level[this_level].dirty++;
-       }
-       /*
-        * ditto for block numbers
-        */
-       if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before))  {
-#ifdef XR_DIR_TRACE
-               fprintf(stderr, "bad directory btree pointer, child bno "
-                       "should be %d, block bno is %d, hashval is %u\n",
-                       be32_to_cpu(btree[entry].before),
-                       cursor->level[p_level].bno,
-                       cursor->level[p_level].hashval);
-               fprintf(stderr, "verify_da_path returns 1 (bad) #1a\n");
-#endif
-               return(1);
-       }
-       /*
-        * ok, now validate last hashvalue in the descendant
-        * block against the hashval in the current entry
-        */
-       if (cursor->level[p_level].hashval !=
-                               be32_to_cpu(btree[entry].hashval))  {
-               if (!no_modify)  {
-                       do_warn(_("correcting bad hashval in interior "
-                                 "dir/attr block\n\tin (level %d) in "
-                                 "inode %" PRIu64 ".\n"),
-                               this_level, cursor->ino);
-                       btree[entry].hashval = cpu_to_be32(
-                                               cursor->level[p_level].hashval);
-                       cursor->level[this_level].dirty++;
-               } else  {
-                       do_warn(_("would correct bad hashval in interior "
-                                 "dir/attr block\n\tin (level %d) in "
-                                 "inode %" PRIu64 ".\n"),
-                               this_level, cursor->ino);
-               }
-       }
-       /*
-        * increment index for this level to point to next entry
-        * (which should point to the next descendant block)
-        */
-       cursor->level[this_level].index++;
-#ifdef XR_DIR_TRACE
-       fprintf(stderr, "verify_da_path returns 0 (ok)\n");
-#endif
-       return(0);
-}
-
 /*
  * For attribute repair, there are 3 formats to worry about. First, is
  * shortform attributes which reside in the inode. Second is the leaf
@@ -728,6 +122,14 @@ verify_da_path(xfs_mount_t *mp,
  * fork being emptied and put in shortform format.
  */
 
+static int
+attr_namecheck(
+       uint8_t *name,
+       int     length)
+{
+       return namecheck((char *)name, length, false);
+}
+
 /*
  * This routine just checks what security needs are for attribute values
  * only called when root flag is set, otherwise these names could exist in
@@ -836,7 +238,7 @@ process_shortform_attr(
                                do_warn(
        _("there are no attributes in the fork for inode %" PRIu64 "\n"),
                                        ino);
-                               asf->hdr.totsize = 
+                               asf->hdr.totsize =
                                        cpu_to_be16(sizeof(xfs_attr_sf_hdr_t));
                                *repair = 1;
                                return(1);
@@ -898,11 +300,9 @@ process_shortform_attr(
                        }
                }
 
-               /* namecheck checks for / and null terminated for file names.
-                * attributes names currently follow the same rules.
-               */
-               if (namecheck((char *)&currententry->nameval[0],
-                                               currententry->namelen))  {
+               /* namecheck checks for null chars in attr names. */
+               if (attr_namecheck(currententry->nameval,
+                                               currententry->namelen)) {
                        do_warn(
        _("entry contains illegal character in shortform attribute name\n"));
                        junkit = 1;
@@ -918,7 +318,7 @@ process_shortform_attr(
                if (currententry->flags & XFS_ATTR_ROOT)
                       junkit |= valuecheck(mp,
                                        (char *)&currententry->nameval[0],
-                                       NULL, currententry->namelen, 
+                                       NULL, currententry->namelen,
                                        currententry->valuelen);
 
                remainingspace = remainingspace -
@@ -1026,14 +426,15 @@ rmtval_get(xfs_mount_t *mp, xfs_ino_t ino, blkmap_t *blkmap,
                if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) {
                        do_warn(
        _("Corrupt remote block for attributes of inode %" PRIu64 "\n"), ino);
+                       libxfs_putbuf(bp);
                        clearit = 1;
                        break;
                }
 
-               ASSERT(mp->m_sb.sb_blocksize == XFS_BUF_COUNT(bp));
+               ASSERT(mp->m_sb.sb_blocksize == bp->b_bcount);
 
-               length = MIN(XFS_BUF_COUNT(bp) - hdrsize, valuelen - amountdone);
-               memmove(value, XFS_BUF_PTR(bp) + hdrsize, length);
+               length = min(bp->b_bcount - hdrsize, valuelen - amountdone);
+               memmove(value, bp->b_addr + hdrsize, length);
                amountdone += length;
                value += length;
                i++;
@@ -1063,7 +464,7 @@ process_leaf_attr_local(
        xfs_attr_leaf_name_local_t *local;
 
        local = xfs_attr3_leaf_name_local(leaf, i);
-       if (local->namelen == 0 || namecheck((char *)&local->nameval[0], 
+       if (local->namelen == 0 || attr_namecheck(local->nameval,
                                                        local->namelen)) {
                do_warn(
        _("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"),
@@ -1090,7 +491,7 @@ process_leaf_attr_local(
 
        /* Only check values for root security attributes */
        if (entry->flags & XFS_ATTR_ROOT) {
-               if (valuecheck(mp, (char *)&local->nameval[0], NULL, 
+               if (valuecheck(mp, (char *)&local->nameval[0], NULL,
                                local->namelen, be16_to_cpu(local->valuelen))) {
                        do_warn(
        _("bad security value for attribute entry %d in attr block %u, inode %" PRIu64 "\n"),
@@ -1098,7 +499,7 @@ process_leaf_attr_local(
                        return -1;
                }
        }
-       return xfs_attr_leaf_entsize_local(local->namelen, 
+       return xfs_attr_leaf_entsize_local(local->namelen,
                                                be16_to_cpu(local->valuelen));
 }
 
@@ -1118,10 +519,10 @@ process_leaf_attr_remote(
 
        remotep = xfs_attr3_leaf_name_remote(leaf, i);
 
-       if (remotep->namelen == 0 || namecheck((char *)&remotep->name[0], 
-                                               remotep->namelen) || 
-                       be32_to_cpu(entry->hashval) != 
-                               libxfs_da_hashname((unsigned char *)&remotep->name[0], 
+       if (remotep->namelen == 0 || attr_namecheck(remotep->name,
+                                               remotep->namelen) ||
+                       be32_to_cpu(entry->hashval) !=
+                               libxfs_da_hashname((unsigned char *)&remotep->name[0],
                                                remotep->namelen) ||
                        be32_to_cpu(entry->hashval) < last_hashval ||
                        be32_to_cpu(remotep->valueblk) == 0) {
@@ -1130,9 +531,6 @@ process_leaf_attr_remote(
                return -1;
        }
 
-       if (!(entry->flags & XFS_ATTR_ROOT))
-               goto out;
        value = malloc(be32_to_cpu(remotep->valuelen));
        if (value == NULL) {
                do_warn(
@@ -1148,7 +546,8 @@ process_leaf_attr_remote(
                        i, ino);
                goto bad_free_out;
        }
-       if (valuecheck(mp, (char *)&remotep->name[0], value, remotep->namelen,
+       if ((entry->flags & XFS_ATTR_ROOT) &&
+           valuecheck(mp, (char *)&remotep->name[0], value, remotep->namelen,
                                be32_to_cpu(remotep->valuelen))) {
                do_warn(
        _("remote attribute value check failed for entry %d, inode %" PRIu64 "\n"),
@@ -1186,7 +585,8 @@ process_leaf_attr_block(
        stop = xfs_attr3_leaf_hdr_size(leaf);
 
        /* does the count look sorta valid? */
-       if (leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop >
+       if (!leafhdr.count ||
+           leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop >
                                                mp->m_sb.sb_blocksize) {
                do_warn(
        _("bad attribute count %d in attr block %u, inode %" PRIu64 "\n"),
@@ -1266,14 +666,14 @@ process_leaf_attr_block(
                 * since the block will get compacted anyhow by the kernel.
                 */
 
-               if ((leafhdr.holes == 0 && 
+               if ((leafhdr.holes == 0 &&
                                firstb != leafhdr.firstused) ||
                                leafhdr.firstused > firstb)  {
                        if (!no_modify)  {
                                do_warn(
        _("- resetting first used heap value from %d to %d in "
          "block %u of attribute fork of inode %" PRIu64 "\n"),
-                                       leafhdr.firstused, 
+                                       leafhdr.firstused,
                                        firstb, da_bno, ino);
                                leafhdr.firstused = firstb;
                                *repair = 1;
@@ -1281,7 +681,7 @@ process_leaf_attr_block(
                                do_warn(
        _("- would reset first used value from %d to %d in "
          "block %u of attribute fork of inode %" PRIu64 "\n"),
-                                       leafhdr.firstused, 
+                                       leafhdr.firstused,
                                        firstb, da_bno, ino);
                        }
                }
@@ -1291,7 +691,7 @@ process_leaf_attr_block(
                                do_warn(
        _("- resetting usedbytes cnt from %d to %d in "
          "block %u of attribute fork of inode %" PRIu64 "\n"),
-                                       leafhdr.usedbytes, 
+                                       leafhdr.usedbytes,
                                        usedbs, da_bno, ino);
                                leafhdr.usedbytes = usedbs;
                                *repair = 1;
@@ -1299,7 +699,7 @@ process_leaf_attr_block(
                                do_warn(
        _("- would reset usedbytes cnt from %d to %d in "
          "block %u of attribute fork of %" PRIu64 "\n"),
-                                       leafhdr.usedbytes, 
+                                       leafhdr.usedbytes,
                                        usedbs, da_bno, ino);
                        }
                }
@@ -1311,6 +711,13 @@ process_leaf_attr_block(
                * we can add it then.
                */
        }
+       /*
+        * If we're just going to zap the block, don't pretend like we
+        * repaired it, because repairing the block stops the clear
+        * operation.
+        */
+       if (clearit)
+               *repair = 0;
        if (*repair)
                xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, leaf, &leafhdr);
 
@@ -1339,17 +746,22 @@ process_leaf_attr_level(xfs_mount_t      *mp,
 
        da_bno = da_cursor->level[0].bno;
        ino = da_cursor->ino;
+       /*
+        * 0 is the root block and no block
+        * pointer can point to the root block of the btree
+        */
+       if (da_bno == 0) {
+               do_warn(
+       _("btree cycle detected in attribute fork for inode %" PRIu64 "\n"),
+                       ino);
+               goto error_out;
+       }
+
        prev_bno = 0;
 
        do {
                repair = 0;
                dev_bno = blkmap_get(da_cursor->blkmap, da_bno);
-               /*
-                * 0 is the root block and no block
-                * pointer can point to the root block of the btree
-                */
-               ASSERT(da_bno != 0);
-
                if (dev_bno == NULLFSBLOCK) {
                        do_warn(
        _("can't map block %u for attribute fork for inode %" PRIu64 "\n"),
@@ -1366,8 +778,6 @@ process_leaf_attr_level(xfs_mount_t        *mp,
                                da_bno, dev_bno, ino);
                        goto error_out;
                }
-               if (bp->b_error == -EFSBADCRC)
-                       repair++;
 
                leaf = bp->b_addr;
                xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
@@ -1416,12 +826,20 @@ process_leaf_attr_level(xfs_mount_t      *mp,
                prev_bno = da_bno;
                da_bno = leafhdr.forw;
 
-               if (da_bno != 0 && verify_da_path(mp, da_cursor, 0))  {
-                       libxfs_putbuf(bp);
-                       goto error_out;
+               if (da_bno != 0) {
+                       if (verify_da_path(mp, da_cursor, 0, XFS_ATTR_FORK)) {
+                               libxfs_putbuf(bp);
+                               goto error_out;
+                       }
                }
 
                current_hashval = greatest_hashval;
+                /*
+                * If block looks ok but CRC didn't match, make sure to
+                * recompute it.
+                */
+               if (!no_modify && bp->b_error == -EFSBADCRC)
+                       repair++;
 
                if (repair && !no_modify)
                        libxfs_writebuf(bp, 0);
@@ -1429,7 +847,7 @@ process_leaf_attr_level(xfs_mount_t        *mp,
                        libxfs_putbuf(bp);
        } while (da_bno != 0);
 
-       if (verify_final_da_path(mp, da_cursor, 0))  {
+       if (verify_final_da_path(mp, da_cursor, 0, XFS_ATTR_FORK))  {
                /*
                 * verify the final path up (right-hand-side) if still ok
                 */
@@ -1479,7 +897,6 @@ process_node_attr(
         */
        memset(&da_cursor, 0, sizeof(da_bt_cursor_t));
        da_cursor.active = 0;
-       da_cursor.type = 0;
        da_cursor.ino = ino;
        da_cursor.dip = dip;
        da_cursor.greatest_bno = 0;
@@ -1501,6 +918,46 @@ process_node_attr(
        return (process_leaf_attr_level(mp, &da_cursor));
 }
 
+/* check v5 metadata */
+static int
+__check_attr_header(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp,
+       xfs_ino_t               ino)
+{
+       struct xfs_da3_blkinfo  *info = bp->b_addr;
+
+       if (info->hdr.magic != cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) &&
+           info->hdr.magic != cpu_to_be16(XFS_DA3_NODE_MAGIC))
+               return 0;
+
+       /* verify owner */
+       if (be64_to_cpu(info->owner) != ino) {
+               do_warn(
+_("expected owner inode %" PRIu64 ", got %llu, attr block %" PRIu64 "\n"),
+                       ino, (unsigned long long)be64_to_cpu(info->owner),
+                       bp->b_bn);
+               return 1;
+       }
+       /* verify block number */
+       if (be64_to_cpu(info->blkno) != bp->b_bn) {
+               do_warn(
+_("expected block %" PRIu64 ", got %llu, inode %" PRIu64 "attr block\n"),
+                       bp->b_bn, (unsigned long long)be64_to_cpu(info->blkno),
+                       ino);
+               return 1;
+       }
+       /* verify uuid */
+       if (platform_uuid_compare(&info->uuid, &mp->m_sb.sb_meta_uuid) != 0) {
+               do_warn(
+_("wrong FS UUID, inode %" PRIu64 " attr block %" PRIu64 "\n"),
+                       ino, bp->b_bn);
+               return 1;
+       }
+
+       return 0;
+}
+
 /*
  * Start processing for a leaf or fuller btree.
  * A leaf directory is one where the attribute fork is too big for
@@ -1524,13 +981,14 @@ process_longform_attr(
        xfs_dahash_t    next_hashval;
        int             repairlinks = 0;
        struct xfs_attr3_icleaf_hdr leafhdr;
+       int             error;
 
        *repair = 0;
 
        bno = blkmap_get(blkmap, 0);
 
        if ( bno == NULLFSBLOCK ) {
-               if (dip->di_aformat == XFS_DINODE_FMT_EXTENTS && 
+               if (dip->di_aformat == XFS_DINODE_FMT_EXTENTS &&
                                be16_to_cpu(dip->di_anextents) == 0)
                        return(0); /* the kernel can handle this state */
                do_warn(
@@ -1556,8 +1014,15 @@ process_longform_attr(
        if (bp->b_error == -EFSBADCRC)
                (*repair)++;
 
+       /* is this block sane? */
+       if (__check_attr_header(mp, bp, ino)) {
+               *repair = 0;
+               libxfs_putbuf(bp);
+               return 1;
+       }
+
        /* verify leaf block */
-       leaf = (xfs_attr_leafblock_t *)XFS_BUF_PTR(bp);
+       leaf = bp->b_addr;
        xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
 
        /* check sibling pointers in leaf block or root block 0 before
@@ -1589,6 +1054,7 @@ process_longform_attr(
        case XFS_ATTR3_LEAF_MAGIC:
                if (process_leaf_attr_block(mp, leaf, 0, ino, blkmap,
                                0, &next_hashval, repair)) {
+                       *repair = 0;
                        /* the block is bad.  lose the attribute fork. */
                        libxfs_putbuf(bp);
                        return(1);
@@ -1604,12 +1070,16 @@ process_longform_attr(
                        libxfs_writebuf(bp, 0);
                } else
                        libxfs_putbuf(bp);
-               return (process_node_attr(mp, ino, dip, blkmap)); /* + repair */
+               error = process_node_attr(mp, ino, dip, blkmap); /* + repair */
+               if (error)
+                       *repair = 0;
+               return error;
        default:
                do_warn(
        _("bad attribute leaf magic # %#x for dir ino %" PRIu64 "\n"),
                        be16_to_cpu(leaf->hdr.info.magic), ino);
                libxfs_putbuf(bp);
+               *repair = 0;
                return(1);
        }