]> Pileus Git - ~andy/linux/blobdiff - fs/xfs/xfs_dir2_block.c
Merge branch 'i2c-embedded/for-next' of git://git.pengutronix.de/git/wsa/linux
[~andy/linux] / fs / xfs / xfs_dir2_block.c
index e93ca8f054f404245b2927b93cdf486387b432c1..7536faaa61e7852175f7b7c20f1e4b0c566c24db 100644 (file)
@@ -56,6 +56,214 @@ xfs_dir_startup(void)
        xfs_dir_hash_dotdot = xfs_da_hashname((unsigned char *)"..", 2);
 }
 
+static void
+xfs_dir2_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);
+       }
+}
+
+static void
+xfs_dir2_block_read_verify(
+       struct xfs_buf  *bp)
+{
+       xfs_dir2_block_verify(bp);
+}
+
+static void
+xfs_dir2_block_write_verify(
+       struct xfs_buf  *bp)
+{
+       xfs_dir2_block_verify(bp);
+}
+
+const struct xfs_buf_ops xfs_dir2_block_buf_ops = {
+       .verify_read = xfs_dir2_block_read_verify,
+       .verify_write = xfs_dir2_block_write_verify,
+};
+
+static int
+xfs_dir2_block_read(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+
+       return xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, bpp,
+                               XFS_DATA_FORK, &xfs_dir2_block_buf_ops);
+}
+
+static void
+xfs_dir2_block_need_space(
+       struct xfs_dir2_data_hdr        *hdr,
+       struct xfs_dir2_block_tail      *btp,
+       struct xfs_dir2_leaf_entry      *blp,
+       __be16                          **tagpp,
+       struct xfs_dir2_data_unused     **dupp,
+       struct xfs_dir2_data_unused     **enddupp,
+       int                             *compact,
+       int                             len)
+{
+       struct xfs_dir2_data_free       *bf;
+       __be16                          *tagp = NULL;
+       struct xfs_dir2_data_unused     *dup = NULL;
+       struct xfs_dir2_data_unused     *enddup = NULL;
+
+       *compact = 0;
+       bf = hdr->bestfree;
+
+       /*
+        * If there are stale entries we'll use one for the leaf.
+        */
+       if (btp->stale) {
+               if (be16_to_cpu(bf[0].length) >= len) {
+                       /*
+                        * The biggest entry enough to avoid compaction.
+                        */
+                       dup = (xfs_dir2_data_unused_t *)
+                             ((char *)hdr + be16_to_cpu(bf[0].offset));
+                       goto out;
+               }
+
+               /*
+                * Will need to compact to make this work.
+                * Tag just before the first leaf entry.
+                */
+               *compact = 1;
+               tagp = (__be16 *)blp - 1;
+
+               /* Data object just before the first leaf entry.  */
+               dup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
+
+               /*
+                * If it's not free then the data will go where the
+                * leaf data starts now, if it works at all.
+                */
+               if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+                       if (be16_to_cpu(dup->length) + (be32_to_cpu(btp->stale) - 1) *
+                           (uint)sizeof(*blp) < len)
+                               dup = NULL;
+               } else if ((be32_to_cpu(btp->stale) - 1) * (uint)sizeof(*blp) < len)
+                       dup = NULL;
+               else
+                       dup = (xfs_dir2_data_unused_t *)blp;
+               goto out;
+       }
+
+       /*
+        * no stale entries, so just use free space.
+        * Tag just before the first leaf entry.
+        */
+       tagp = (__be16 *)blp - 1;
+
+       /* Data object just before the first leaf entry.  */
+       enddup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
+
+       /*
+        * If it's not free then can't do this add without cleaning up:
+        * the space before the first leaf entry needs to be free so it
+        * can be expanded to hold the pointer to the new entry.
+        */
+       if (be16_to_cpu(enddup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+               /*
+                * Check out the biggest freespace and see if it's the same one.
+                */
+               dup = (xfs_dir2_data_unused_t *)
+                     ((char *)hdr + be16_to_cpu(bf[0].offset));
+               if (dup != enddup) {
+                       /*
+                        * Not the same free entry, just check its length.
+                        */
+                       if (be16_to_cpu(dup->length) < len)
+                               dup = NULL;
+                       goto out;
+               }
+
+               /*
+                * It is the biggest freespace, can it hold the leaf too?
+                */
+               if (be16_to_cpu(dup->length) < len + (uint)sizeof(*blp)) {
+                       /*
+                        * Yes, use the second-largest entry instead if it works.
+                        */
+                       if (be16_to_cpu(bf[1].length) >= len)
+                               dup = (xfs_dir2_data_unused_t *)
+                                     ((char *)hdr + be16_to_cpu(bf[1].offset));
+                       else
+                               dup = NULL;
+               }
+       }
+out:
+       *tagpp = tagp;
+       *dupp = dup;
+       *enddupp = enddup;
+}
+
+/*
+ * compact the leaf entries.
+ * Leave the highest-numbered stale entry stale.
+ * XXX should be the one closest to mid but mid is not yet computed.
+ */
+static void
+xfs_dir2_block_compact(
+       struct xfs_trans                *tp,
+       struct xfs_buf                  *bp,
+       struct xfs_dir2_data_hdr        *hdr,
+       struct xfs_dir2_block_tail      *btp,
+       struct xfs_dir2_leaf_entry      *blp,
+       int                             *needlog,
+       int                             *lfloghigh,
+       int                             *lfloglow)
+{
+       int                     fromidx;        /* source leaf index */
+       int                     toidx;          /* target leaf index */
+       int                     needscan = 0;
+       int                     highstale;      /* high stale index */
+
+       fromidx = toidx = be32_to_cpu(btp->count) - 1;
+       highstale = *lfloghigh = -1;
+       for (; fromidx >= 0; fromidx--) {
+               if (blp[fromidx].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) {
+                       if (highstale == -1)
+                               highstale = toidx;
+                       else {
+                               if (*lfloghigh == -1)
+                                       *lfloghigh = toidx;
+                               continue;
+                       }
+               }
+               if (fromidx < toidx)
+                       blp[toidx] = blp[fromidx];
+               toidx--;
+       }
+       *lfloglow = toidx + 1 - (be32_to_cpu(btp->stale) - 1);
+       *lfloghigh -= be32_to_cpu(btp->stale) - 1;
+       be32_add_cpu(&btp->count, -(be32_to_cpu(btp->stale) - 1));
+       xfs_dir2_data_make_free(tp, bp,
+               (xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
+               (xfs_dir2_data_aoff_t)((be32_to_cpu(btp->stale) - 1) * sizeof(*blp)),
+               needlog, &needscan);
+       blp += be32_to_cpu(btp->stale) - 1;
+       btp->stale = cpu_to_be32(1);
+       /*
+        * If we now need to rebuild the bestfree map, do so.
+        * This needs to happen before the next call to use_free.
+        */
+       if (needscan)
+               xfs_dir2_data_freescan(tp->t_mountp, hdr, needlog);
+}
+
 /*
  * Add an entry to a block directory.
  */
@@ -63,7 +271,6 @@ int                                          /* error */
 xfs_dir2_block_addname(
        xfs_da_args_t           *args)          /* directory op arguments */
 {
-       xfs_dir2_data_free_t    *bf;            /* bestfree table in block */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        xfs_dir2_leaf_entry_t   *blp;           /* block leaf entries */
        struct xfs_buf          *bp;            /* buffer for block */
@@ -94,134 +301,44 @@ xfs_dir2_block_addname(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       /*
-        * Read the (one and only) directory block into dabuf bp.
-        */
-       if ((error =
-           xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
+
+       /* Read the (one and only) directory block into bp. */
+       error = xfs_dir2_block_read(tp, dp, &bp);
+       if (error)
                return error;
-       }
-       ASSERT(bp != NULL);
-       hdr = bp->b_addr;
-       /*
-        * Check the magic number, corrupted if wrong.
-        */
-       if (unlikely(hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC))) {
-               XFS_CORRUPTION_ERROR("xfs_dir2_block_addname",
-                                    XFS_ERRLEVEL_LOW, mp, hdr);
-               xfs_trans_brelse(tp, bp);
-               return XFS_ERROR(EFSCORRUPTED);
-       }
+
        len = xfs_dir2_data_entsize(args->namelen);
+
        /*
         * Set up pointers to parts of the block.
         */
-       bf = hdr->bestfree;
+       hdr = bp->b_addr;
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
+
        /*
-        * No stale entries?  Need space for entry and new leaf.
-        */
-       if (!btp->stale) {
-               /*
-                * Tag just before the first leaf entry.
-                */
-               tagp = (__be16 *)blp - 1;
-               /*
-                * Data object just before the first leaf entry.
-                */
-               enddup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
-               /*
-                * If it's not free then can't do this add without cleaning up:
-                * the space before the first leaf entry needs to be free so it
-                * can be expanded to hold the pointer to the new entry.
-                */
-               if (be16_to_cpu(enddup->freetag) != XFS_DIR2_DATA_FREE_TAG)
-                       dup = enddup = NULL;
-               /*
-                * Check out the biggest freespace and see if it's the same one.
-                */
-               else {
-                       dup = (xfs_dir2_data_unused_t *)
-                             ((char *)hdr + be16_to_cpu(bf[0].offset));
-                       if (dup == enddup) {
-                               /*
-                                * It is the biggest freespace, is it too small
-                                * to hold the new leaf too?
-                                */
-                               if (be16_to_cpu(dup->length) < len + (uint)sizeof(*blp)) {
-                                       /*
-                                        * Yes, we use the second-largest
-                                        * entry instead if it works.
-                                        */
-                                       if (be16_to_cpu(bf[1].length) >= len)
-                                               dup = (xfs_dir2_data_unused_t *)
-                                                     ((char *)hdr +
-                                                      be16_to_cpu(bf[1].offset));
-                                       else
-                                               dup = NULL;
-                               }
-                       } else {
-                               /*
-                                * Not the same free entry,
-                                * just check its length.
-                                */
-                               if (be16_to_cpu(dup->length) < len) {
-                                       dup = NULL;
-                               }
-                       }
-               }
-               compact = 0;
-       }
-       /*
-        * If there are stale entries we'll use one for the leaf.
-        * Is the biggest entry enough to avoid compaction?
-        */
-       else if (be16_to_cpu(bf[0].length) >= len) {
-               dup = (xfs_dir2_data_unused_t *)
-                     ((char *)hdr + be16_to_cpu(bf[0].offset));
-               compact = 0;
-       }
-       /*
-        * Will need to compact to make this work.
+        * Find out if we can reuse stale entries or whether we need extra
+        * space for entry and new leaf.
         */
-       else {
-               /*
-                * Tag just before the first leaf entry.
-                */
-               tagp = (__be16 *)blp - 1;
-               /*
-                * Data object just before the first leaf entry.
-                */
-               dup = (xfs_dir2_data_unused_t *)((char *)hdr + be16_to_cpu(*tagp));
-               /*
-                * If it's not free then the data will go where the
-                * leaf data starts now, if it works at all.
-                */
-               if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
-                       if (be16_to_cpu(dup->length) + (be32_to_cpu(btp->stale) - 1) *
-                           (uint)sizeof(*blp) < len)
-                               dup = NULL;
-               } else if ((be32_to_cpu(btp->stale) - 1) * (uint)sizeof(*blp) < len)
-                       dup = NULL;
-               else
-                       dup = (xfs_dir2_data_unused_t *)blp;
-               compact = 1;
-       }
+       xfs_dir2_block_need_space(hdr, btp, blp, &tagp, &dup,
+                                 &enddup, &compact, len);
+
        /*
-        * If this isn't a real add, we're done with the buffer.
+        * Done everything we need for a space check now.
         */
-       if (args->op_flags & XFS_DA_OP_JUSTCHECK)
+       if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
                xfs_trans_brelse(tp, bp);
+               if (!dup)
+                       return XFS_ERROR(ENOSPC);
+               return 0;
+       }
+
        /*
         * If we don't have space for the new entry & leaf ...
         */
        if (!dup) {
-               /*
-                * Not trying to actually do anything, or don't have
-                * a space reservation: return no-space.
-                */
-               if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
+               /* Don't have a space reservation: return no-space.  */
+               if (args->total == 0)
                        return XFS_ERROR(ENOSPC);
                /*
                 * Convert to the next larger format.
@@ -232,65 +349,24 @@ xfs_dir2_block_addname(
                        return error;
                return xfs_dir2_leaf_addname(args);
        }
-       /*
-        * Just checking, and it would work, so say so.
-        */
-       if (args->op_flags & XFS_DA_OP_JUSTCHECK)
-               return 0;
+
        needlog = needscan = 0;
+
        /*
         * If need to compact the leaf entries, do it now.
-        * Leave the highest-numbered stale entry stale.
-        * XXX should be the one closest to mid but mid is not yet computed.
-        */
-       if (compact) {
-               int     fromidx;                /* source leaf index */
-               int     toidx;                  /* target leaf index */
-
-               for (fromidx = toidx = be32_to_cpu(btp->count) - 1,
-                       highstale = lfloghigh = -1;
-                    fromidx >= 0;
-                    fromidx--) {
-                       if (blp[fromidx].address ==
-                           cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) {
-                               if (highstale == -1)
-                                       highstale = toidx;
-                               else {
-                                       if (lfloghigh == -1)
-                                               lfloghigh = toidx;
-                                       continue;
-                               }
-                       }
-                       if (fromidx < toidx)
-                               blp[toidx] = blp[fromidx];
-                       toidx--;
-               }
-               lfloglow = toidx + 1 - (be32_to_cpu(btp->stale) - 1);
-               lfloghigh -= be32_to_cpu(btp->stale) - 1;
-               be32_add_cpu(&btp->count, -(be32_to_cpu(btp->stale) - 1));
-               xfs_dir2_data_make_free(tp, bp,
-                       (xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
-                       (xfs_dir2_data_aoff_t)((be32_to_cpu(btp->stale) - 1) * sizeof(*blp)),
-                       &needlog, &needscan);
-               blp += be32_to_cpu(btp->stale) - 1;
-               btp->stale = cpu_to_be32(1);
-               /*
-                * If we now need to rebuild the bestfree map, do so.
-                * This needs to happen before the next call to use_free.
-                */
-               if (needscan) {
-                       xfs_dir2_data_freescan(mp, hdr, &needlog);
-                       needscan = 0;
-               }
-       }
-       /*
-        * Set leaf logging boundaries to impossible state.
-        * For the no-stale case they're set explicitly.
         */
+       if (compact)
+               xfs_dir2_block_compact(tp, bp, hdr, btp, blp, &needlog,
+                                     &lfloghigh, &lfloglow);
        else if (btp->stale) {
+               /*
+                * Set leaf logging boundaries to impossible state.
+                * For the no-stale case they're set explicitly.
+                */
                lfloglow = be32_to_cpu(btp->count);
                lfloghigh = -1;
        }
+
        /*
         * Find the slot that's first lower than our hash value, -1 if none.
         */
@@ -450,18 +526,13 @@ xfs_dir2_block_getdents(
        /*
         * If the block number in the offset is out of range, we're done.
         */
-       if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk) {
+       if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk)
                return 0;
-       }
-       /*
-        * Can't read the block, give up, else get dabuf in bp.
-        */
-       error = xfs_da_read_buf(NULL, dp, mp->m_dirdatablk, -1,
-                               &bp, XFS_DATA_FORK);
+
+       error = xfs_dir2_block_read(NULL, dp, &bp);
        if (error)
                return error;
 
-       ASSERT(bp != NULL);
        /*
         * Extract the byte offset we start at from the seek pointer.
         * We'll skip entries before this.
@@ -637,14 +708,11 @@ xfs_dir2_block_lookup_int(
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
-       /*
-        * Read the buffer, return error if we can't get it.
-        */
-       if ((error =
-           xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
+
+       error = xfs_dir2_block_read(tp, dp, &bp);
+       if (error)
                return error;
-       }
-       ASSERT(bp != NULL);
+
        hdr = bp->b_addr;
        xfs_dir2_data_check(dp, bp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
@@ -917,10 +985,10 @@ xfs_dir2_leaf_to_block(
        /*
         * Read the data block if we don't already have it, give up if it fails.
         */
-       if (dbp == NULL &&
-           (error = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &dbp,
-                   XFS_DATA_FORK))) {
-               return error;
+       if (!dbp) {
+               error = xfs_dir2_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));
@@ -944,6 +1012,7 @@ 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);
        needlog = 1;
        needscan = 0;
@@ -1073,6 +1142,7 @@ xfs_dir2_sf_to_block(
                kmem_free(sfp);
                return error;
        }
+       bp->b_ops = &xfs_dir2_block_buf_ops;
        hdr = bp->b_addr;
        hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
        /*