OSDN Git Service

Merge tag 'for-linus-v3.10-rc1-2' of git://oss.sgi.com/xfs/xfs
[android-x86/kernel.git] / fs / xfs / xfs_alloc_btree.c
index b1ddef6..cafc902 100644 (file)
@@ -33,6 +33,7 @@
 #include "xfs_extent_busy.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_cksum.h"
 
 
 STATIC struct xfs_btree_cur *
@@ -272,7 +273,7 @@ xfs_allocbt_key_diff(
        return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
 }
 
-static void
+static bool
 xfs_allocbt_verify(
        struct xfs_buf          *bp)
 {
@@ -280,66 +281,103 @@ xfs_allocbt_verify(
        struct xfs_btree_block  *block = XFS_BUF_TO_BLOCK(bp);
        struct xfs_perag        *pag = bp->b_pag;
        unsigned int            level;
-       int                     sblock_ok; /* block passes checks */
 
        /*
         * magic number and level verification
         *
-        * During growfs operations, we can't verify the exact level as the
-        * perag is not fully initialised and hence not attached to the buffer.
-        * In this case, check against the maximum tree depth.
+        * During growfs operations, we can't verify the exact level or owner as
+        * the perag is not fully initialised and hence not attached to the
+        * buffer.  In this case, check against the maximum tree depth.
+        *
+        * Similarly, during log recovery we will have a perag structure
+        * attached, but the agf information will not yet have been initialised
+        * from the on disk AGF. Again, we can only check against maximum limits
+        * in this case.
         */
        level = be16_to_cpu(block->bb_level);
        switch (block->bb_magic) {
+       case cpu_to_be32(XFS_ABTB_CRC_MAGIC):
+               if (!xfs_sb_version_hascrc(&mp->m_sb))
+                       return false;
+               if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
+                       return false;
+               if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
+                       return false;
+               if (pag &&
+                   be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+                       return false;
+               /* fall through */
        case cpu_to_be32(XFS_ABTB_MAGIC):
-               if (pag)
-                       sblock_ok = level < pag->pagf_levels[XFS_BTNUM_BNOi];
-               else
-                       sblock_ok = level < mp->m_ag_maxlevels;
+               if (pag && pag->pagf_init) {
+                       if (level >= pag->pagf_levels[XFS_BTNUM_BNOi])
+                               return false;
+               } else if (level >= mp->m_ag_maxlevels)
+                       return false;
                break;
+       case cpu_to_be32(XFS_ABTC_CRC_MAGIC):
+               if (!xfs_sb_version_hascrc(&mp->m_sb))
+                       return false;
+               if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
+                       return false;
+               if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
+                       return false;
+               if (pag &&
+                   be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+                       return false;
+               /* fall through */
        case cpu_to_be32(XFS_ABTC_MAGIC):
-               if (pag)
-                       sblock_ok = level < pag->pagf_levels[XFS_BTNUM_CNTi];
-               else
-                       sblock_ok = level < mp->m_ag_maxlevels;
+               if (pag && pag->pagf_init) {
+                       if (level >= pag->pagf_levels[XFS_BTNUM_CNTi])
+                               return false;
+               } else if (level >= mp->m_ag_maxlevels)
+                       return false;
                break;
        default:
-               sblock_ok = 0;
-               break;
+               return false;
        }
 
        /* numrecs verification */
-       sblock_ok = sblock_ok &&
-               be16_to_cpu(block->bb_numrecs) <= mp->m_alloc_mxr[level != 0];
+       if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[level != 0])
+               return false;
 
        /* sibling pointer verification */
-       sblock_ok = sblock_ok &&
-               (block->bb_u.s.bb_leftsib == cpu_to_be32(NULLAGBLOCK) ||
-                be32_to_cpu(block->bb_u.s.bb_leftsib) < mp->m_sb.sb_agblocks) &&
-               block->bb_u.s.bb_leftsib &&
-               (block->bb_u.s.bb_rightsib == cpu_to_be32(NULLAGBLOCK) ||
-                be32_to_cpu(block->bb_u.s.bb_rightsib) < mp->m_sb.sb_agblocks) &&
-               block->bb_u.s.bb_rightsib;
-
-       if (!sblock_ok) {
-               trace_xfs_btree_corrupt(bp, _RET_IP_);
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, block);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-       }
+       if (!block->bb_u.s.bb_leftsib ||
+           (be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
+            block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
+               return false;
+       if (!block->bb_u.s.bb_rightsib ||
+           (be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
+            block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
+               return false;
+
+       return true;
 }
 
 static void
 xfs_allocbt_read_verify(
        struct xfs_buf  *bp)
 {
-       xfs_allocbt_verify(bp);
+       if (!(xfs_btree_sblock_verify_crc(bp) &&
+             xfs_allocbt_verify(bp))) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
+                                    bp->b_target->bt_mount, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
 }
 
 static void
 xfs_allocbt_write_verify(
        struct xfs_buf  *bp)
 {
-       xfs_allocbt_verify(bp);
+       if (!xfs_allocbt_verify(bp)) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
+                                    bp->b_target->bt_mount, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
+       xfs_btree_sblock_calc_crc(bp);
+
 }
 
 const struct xfs_buf_ops xfs_allocbt_buf_ops = {
@@ -348,7 +386,7 @@ const struct xfs_buf_ops xfs_allocbt_buf_ops = {
 };
 
 
-#ifdef DEBUG
+#if defined(DEBUG) || defined(XFS_WARN)
 STATIC int
 xfs_allocbt_keys_inorder(
        struct xfs_btree_cur    *cur,
@@ -404,7 +442,7 @@ static const struct xfs_btree_ops xfs_allocbt_ops = {
        .init_ptr_from_cur      = xfs_allocbt_init_ptr_from_cur,
        .key_diff               = xfs_allocbt_key_diff,
        .buf_ops                = &xfs_allocbt_buf_ops,
-#ifdef DEBUG
+#if defined(DEBUG) || defined(XFS_WARN)
        .keys_inorder           = xfs_allocbt_keys_inorder,
        .recs_inorder           = xfs_allocbt_recs_inorder,
 #endif
@@ -444,6 +482,9 @@ xfs_allocbt_init_cursor(
        cur->bc_private.a.agbp = agbp;
        cur->bc_private.a.agno = agno;
 
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
+
        return cur;
 }