Merge branch 'linaro-android-3.10-lsk' of git://git.linaro.org/people/john.stultz...
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_dir2_block.c
index 12afe07a91d71ddc003a763674d7d1ee45a992d3..e59f5fc816fe9a98a99ee11f712987fcd6c5b39a 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2013 Red Hat, Inc.
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or
 #include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_inode_item.h"
+#include "xfs_buf_item.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_format.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_cksum.h"
 
 /*
  * Local function prototypes.
@@ -56,52 +59,110 @@ xfs_dir_startup(void)
        xfs_dir_hash_dotdot = xfs_da_hashname((unsigned char *)"..", 2);
 }
 
-static void
-xfs_dir2_block_verify(
+static bool
+xfs_dir3_block_verify(
        struct xfs_buf          *bp)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
-       struct xfs_dir2_data_hdr *hdr = bp->b_addr;
-       int                     block_ok = 0;
-
-       block_ok = hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
-       block_ok = block_ok && __xfs_dir2_data_check(NULL, bp) == 0;
-
-       if (!block_ok) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, hdr);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               if (hdr3->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+                       return false;
+               if (!uuid_equal(&hdr3->uuid, &mp->m_sb.sb_uuid))
+                       return false;
+               if (be64_to_cpu(hdr3->blkno) != bp->b_bn)
+                       return false;
+       } else {
+               if (hdr3->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC))
+                       return false;
        }
+       if (__xfs_dir3_data_check(NULL, bp))
+               return false;
+       return true;
 }
 
 static void
-xfs_dir2_block_read_verify(
+xfs_dir3_block_read_verify(
        struct xfs_buf  *bp)
 {
-       xfs_dir2_block_verify(bp);
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+
+       if ((xfs_sb_version_hascrc(&mp->m_sb) &&
+            !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+                                         XFS_DIR3_DATA_CRC_OFF)) ||
+           !xfs_dir3_block_verify(bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
 }
 
 static void
-xfs_dir2_block_write_verify(
+xfs_dir3_block_write_verify(
        struct xfs_buf  *bp)
 {
-       xfs_dir2_block_verify(bp);
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_buf_log_item *bip = bp->b_fspriv;
+       struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+
+       if (!xfs_dir3_block_verify(bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+               return;
+       }
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb))
+               return;
+
+       if (bip)
+               hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
+
+       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_DATA_CRC_OFF);
 }
 
-const struct xfs_buf_ops xfs_dir2_block_buf_ops = {
-       .verify_read = xfs_dir2_block_read_verify,
-       .verify_write = xfs_dir2_block_write_verify,
+const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
+       .verify_read = xfs_dir3_block_read_verify,
+       .verify_write = xfs_dir3_block_write_verify,
 };
 
 static int
-xfs_dir2_block_read(
+xfs_dir3_block_read(
        struct xfs_trans        *tp,
        struct xfs_inode        *dp,
        struct xfs_buf          **bpp)
 {
        struct xfs_mount        *mp = dp->i_mount;
+       int                     err;
 
-       return xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, bpp,
-                               XFS_DATA_FORK, &xfs_dir2_block_buf_ops);
+       err = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, bpp,
+                               XFS_DATA_FORK, &xfs_dir3_block_buf_ops);
+       if (!err && tp)
+               xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_BLOCK_BUF);
+       return err;
+}
+
+static void
+xfs_dir3_block_init(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
+       struct xfs_inode        *dp)
+{
+       struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+
+       bp->b_ops = &xfs_dir3_block_buf_ops;
+       xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_BLOCK_BUF);
+
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               memset(hdr3, 0, sizeof(*hdr3));
+               hdr3->magic = cpu_to_be32(XFS_DIR3_BLOCK_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);
+               return;
+
+       }
+       hdr3->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
 }
 
 static void
@@ -121,7 +182,7 @@ xfs_dir2_block_need_space(
        struct xfs_dir2_data_unused     *enddup = NULL;
 
        *compact = 0;
-       bf = hdr->bestfree;
+       bf = xfs_dir3_data_bestfree_p(hdr);
 
        /*
         * If there are stale entries we'll use one for the leaf.
@@ -303,7 +364,7 @@ xfs_dir2_block_addname(
        mp = dp->i_mount;
 
        /* Read the (one and only) directory block into bp. */
-       error = xfs_dir2_block_read(tp, dp, &bp);
+       error = xfs_dir3_block_read(tp, dp, &bp);
        if (error)
                return error;
 
@@ -498,7 +559,7 @@ xfs_dir2_block_addname(
                xfs_dir2_data_log_header(tp, bp);
        xfs_dir2_block_log_tail(tp, bp);
        xfs_dir2_data_log_entry(tp, bp, dep);
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        return 0;
 }
 
@@ -531,7 +592,7 @@ xfs_dir2_block_getdents(
        if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk)
                return 0;
 
-       error = xfs_dir2_block_read(NULL, dp, &bp);
+       error = xfs_dir3_block_read(NULL, dp, &bp);
        if (error)
                return error;
 
@@ -541,12 +602,12 @@ xfs_dir2_block_getdents(
         */
        wantoff = xfs_dir2_dataptr_to_off(mp, *offset);
        hdr = bp->b_addr;
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        /*
         * Set up values for the loop.
         */
        btp = xfs_dir2_block_tail_p(mp, hdr);
-       ptr = (char *)(hdr + 1);
+       ptr = (char *)xfs_dir3_data_entry_p(hdr);
        endptr = (char *)xfs_dir2_block_leaf_p(btp);
 
        /*
@@ -665,7 +726,7 @@ xfs_dir2_block_lookup(
        dp = args->dp;
        mp = dp->i_mount;
        hdr = bp->b_addr;
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
        /*
@@ -711,12 +772,12 @@ xfs_dir2_block_lookup_int(
        tp = args->trans;
        mp = dp->i_mount;
 
-       error = xfs_dir2_block_read(tp, dp, &bp);
+       error = xfs_dir3_block_read(tp, dp, &bp);
        if (error)
                return error;
 
        hdr = bp->b_addr;
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
        /*
@@ -853,7 +914,7 @@ xfs_dir2_block_removename(
                xfs_dir2_data_freescan(mp, hdr, &needlog);
        if (needlog)
                xfs_dir2_data_log_header(tp, bp);
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        /*
         * See if the size as a shortform is good enough.
         */
@@ -910,7 +971,7 @@ xfs_dir2_block_replace(
         */
        dep->inumber = cpu_to_be64(args->inumber);
        xfs_dir2_data_log_entry(args->trans, bp, dep);
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        return 0;
 }
 
@@ -958,6 +1019,8 @@ xfs_dir2_leaf_to_block(
        __be16                  *tagp;          /* end of entry (tag) */
        int                     to;             /* block/leaf to index */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir2_leaf_entry *ents;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
        trace_xfs_dir2_leaf_to_block(args);
 
@@ -965,8 +1028,12 @@ xfs_dir2_leaf_to_block(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = lbp->b_addr;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC));
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
+
+       ASSERT(leafhdr.magic == XFS_DIR2_LEAF1_MAGIC ||
+              leafhdr.magic == XFS_DIR3_LEAF1_MAGIC);
        /*
         * If there are data blocks other than the first one, take this
         * opportunity to remove trailing empty data blocks that may have
@@ -974,9 +1041,12 @@ xfs_dir2_leaf_to_block(
         * These will show up in the leaf bests table.
         */
        while (dp->i_d.di_size > mp->m_dirblksize) {
+               int hdrsz;
+
+               hdrsz = xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
                bestsp = xfs_dir2_leaf_bests_p(ltp);
                if (be16_to_cpu(bestsp[be32_to_cpu(ltp->bestcount) - 1]) ==
-                   mp->m_dirblksize - (uint)sizeof(*hdr)) {
+                                           mp->m_dirblksize - hdrsz) {
                        if ((error =
                            xfs_dir2_leaf_trim_data(args, lbp,
                                    (xfs_dir2_db_t)(be32_to_cpu(ltp->bestcount) - 1))))
@@ -988,17 +1058,19 @@ xfs_dir2_leaf_to_block(
         * Read the data block if we don't already have it, give up if it fails.
         */
        if (!dbp) {
-               error = xfs_dir2_data_read(tp, dp, mp->m_dirdatablk, -1, &dbp);
+               error = xfs_dir3_data_read(tp, dp, mp->m_dirdatablk, -1, &dbp);
                if (error)
                        return error;
        }
        hdr = dbp->b_addr;
-       ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC));
+       ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
+              hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC));
+
        /*
         * Size of the "leaf" area in the block.
         */
        size = (uint)sizeof(xfs_dir2_block_tail_t) +
-              (uint)sizeof(*lep) * (be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale));
+              (uint)sizeof(*lep) * (leafhdr.count - leafhdr.stale);
        /*
         * Look at the last data entry.
         */
@@ -1014,8 +1086,8 @@ xfs_dir2_leaf_to_block(
        /*
         * Start converting it to block form.
         */
-       dbp->b_ops = &xfs_dir2_block_buf_ops;
-       hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
+       xfs_dir3_block_init(mp, tp, dbp, dp);
+
        needlog = 1;
        needscan = 0;
        /*
@@ -1027,18 +1099,17 @@ xfs_dir2_leaf_to_block(
         * Initialize the block tail.
         */
        btp = xfs_dir2_block_tail_p(mp, hdr);
-       btp->count = cpu_to_be32(be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale));
+       btp->count = cpu_to_be32(leafhdr.count - leafhdr.stale);
        btp->stale = 0;
        xfs_dir2_block_log_tail(tp, dbp);
        /*
         * Initialize the block leaf area.  We compact out stale entries.
         */
        lep = xfs_dir2_block_leaf_p(btp);
-       for (from = to = 0; from < be16_to_cpu(leaf->hdr.count); from++) {
-               if (leaf->ents[from].address ==
-                   cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
+       for (from = to = 0; from < leafhdr.count; from++) {
+               if (ents[from].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
                        continue;
-               lep[to++] = leaf->ents[from];
+               lep[to++] = ents[from];
        }
        ASSERT(to == be32_to_cpu(btp->count));
        xfs_dir2_block_log_leaf(tp, dbp, 0, be32_to_cpu(btp->count) - 1);
@@ -1137,16 +1208,16 @@ xfs_dir2_sf_to_block(
                return error;
        }
        /*
-        * Initialize the data block.
+        * Initialize the data block, then convert it to block format.
         */
-       error = xfs_dir2_data_init(args, blkno, &bp);
+       error = xfs_dir3_data_init(args, blkno, &bp);
        if (error) {
                kmem_free(sfp);
                return error;
        }
-       bp->b_ops = &xfs_dir2_block_buf_ops;
+       xfs_dir3_block_init(mp, tp, bp, dp);
        hdr = bp->b_addr;
-       hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
+
        /*
         * Compute size of block "tail" area.
         */
@@ -1156,7 +1227,7 @@ xfs_dir2_sf_to_block(
         * The whole thing is initialized to free by the init routine.
         * Say we're using the leaf and tail area.
         */
-       dup = (xfs_dir2_data_unused_t *)(hdr + 1);
+       dup = xfs_dir3_data_unused_p(hdr);
        needlog = needscan = 0;
        xfs_dir2_data_use_free(tp, bp, dup, mp->m_dirblksize - i, i, &needlog,
                &needscan);
@@ -1178,8 +1249,7 @@ xfs_dir2_sf_to_block(
        /*
         * Create entry for .
         */
-       dep = (xfs_dir2_data_entry_t *)
-             ((char *)hdr + XFS_DIR2_DATA_DOT_OFFSET);
+       dep = xfs_dir3_data_dot_entry_p(hdr);
        dep->inumber = cpu_to_be64(dp->i_ino);
        dep->namelen = 1;
        dep->name[0] = '.';
@@ -1192,8 +1262,7 @@ xfs_dir2_sf_to_block(
        /*
         * Create entry for ..
         */
-       dep = (xfs_dir2_data_entry_t *)
-               ((char *)hdr + XFS_DIR2_DATA_DOTDOT_OFFSET);
+       dep = xfs_dir3_data_dotdot_entry_p(hdr);
        dep->inumber = cpu_to_be64(xfs_dir2_sf_get_parent_ino(sfp));
        dep->namelen = 2;
        dep->name[0] = dep->name[1] = '.';
@@ -1203,7 +1272,7 @@ xfs_dir2_sf_to_block(
        blp[1].hashval = cpu_to_be32(xfs_dir_hash_dotdot);
        blp[1].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
                                (char *)dep - (char *)hdr));
-       offset = XFS_DIR2_DATA_FIRST_OFFSET;
+       offset = xfs_dir3_data_first_offset(hdr);
        /*
         * Loop over existing entries, stuff them in.
         */
@@ -1273,6 +1342,6 @@ xfs_dir2_sf_to_block(
        ASSERT(needscan == 0);
        xfs_dir2_block_log_leaf(tp, bp, 0, be32_to_cpu(btp->count) - 1);
        xfs_dir2_block_log_tail(tp, bp);
-       xfs_dir2_data_check(dp, bp);
+       xfs_dir3_data_check(dp, bp);
        return 0;
 }