]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_dir2_leaf.c
xfs: fix transaction leak on remote attr set/remove failure
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_dir2_leaf.c
index 80d03b3bd328e5dab4d12be2956d1263eb7d0177..dbe4d2efb069a6ea6f03c21bf71b723ef2e87b3a 100644 (file)
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * 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
- * 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"
@@ -47,13 +35,7 @@ static void xfs_dir3_leaf_log_tail(struct xfs_da_args *args,
  * Pop an assert if something is wrong.
  */
 #ifdef DEBUG
-#define        xfs_dir3_leaf_check(dp, bp) \
-do { \
-       if (!xfs_dir3_leaf1_check((dp), (bp))) \
-               ASSERT(0); \
-} while (0);
-
-STATIC bool
+static xfs_failaddr_t
 xfs_dir3_leaf1_check(
        struct xfs_inode        *dp,
        struct xfs_buf          *bp)
@@ -66,17 +48,33 @@ xfs_dir3_leaf1_check(
        if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) {
                struct xfs_dir3_leaf_hdr *leaf3 = bp->b_addr;
                if (be64_to_cpu(leaf3->info.blkno) != bp->b_bn)
-                       return false;
+                       return __this_address;
        } else if (leafhdr.magic != XFS_DIR2_LEAF1_MAGIC)
-               return false;
+               return __this_address;
 
        return xfs_dir3_leaf_check_int(dp->i_mount, dp, &leafhdr, leaf);
 }
+
+static inline void
+xfs_dir3_leaf_check(
+       struct xfs_inode        *dp,
+       struct xfs_buf          *bp)
+{
+       xfs_failaddr_t          fa;
+
+       fa = xfs_dir3_leaf1_check(dp, bp);
+       if (!fa)
+               return;
+       xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, dp->i_mount,
+                       bp->b_addr, BBTOB(bp->b_length), __FILE__, __LINE__,
+                       fa);
+       ASSERT(0);
+}
 #else
 #define        xfs_dir3_leaf_check(dp, bp)
 #endif
 
-bool
+xfs_failaddr_t
 xfs_dir3_leaf_check_int(
        struct xfs_mount        *mp,
        struct xfs_inode        *dp,
@@ -111,27 +109,27 @@ xfs_dir3_leaf_check_int(
         * We can deduce a value for that from di_size.
         */
        if (hdr->count > ops->leaf_max_ents(geo))
-               return false;
+               return __this_address;
 
        /* Leaves and bests don't overlap in leaf format. */
        if ((hdr->magic == XFS_DIR2_LEAF1_MAGIC ||
             hdr->magic == XFS_DIR3_LEAF1_MAGIC) &&
            (char *)&ents[hdr->count] > (char *)xfs_dir2_leaf_bests_p(ltp))
-               return false;
+               return __this_address;
 
        /* Check hash value order, count stale entries.  */
        for (i = stale = 0; i < hdr->count; i++) {
                if (i + 1 < hdr->count) {
                        if (be32_to_cpu(ents[i].hashval) >
                                        be32_to_cpu(ents[i + 1].hashval))
-                               return false;
+                               return __this_address;
                }
                if (ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
                        stale++;
        }
        if (hdr->stale != stale)
-               return false;
-       return true;
+               return __this_address;
+       return NULL;
 }
 
 /*
@@ -139,10 +137,10 @@ xfs_dir3_leaf_check_int(
  * kernels we don't get assertion failures in xfs_dir3_leaf_hdr_from_disk() due
  * to incorrect magic numbers.
  */
-static bool
+static xfs_failaddr_t
 xfs_dir3_leaf_verify(
        struct xfs_buf          *bp,
-       __uint16_t              magic)
+       uint16_t                magic)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
@@ -151,20 +149,22 @@ xfs_dir3_leaf_verify(
 
        if (xfs_sb_version_hascrc(&mp->m_sb)) {
                struct xfs_dir3_leaf_hdr *leaf3 = bp->b_addr;
-               __uint16_t              magic3;
+               uint16_t                magic3;
 
                magic3 = (magic == XFS_DIR2_LEAF1_MAGIC) ? XFS_DIR3_LEAF1_MAGIC
                                                         : XFS_DIR3_LEAFN_MAGIC;
 
                if (leaf3->info.hdr.magic != cpu_to_be16(magic3))
-                       return false;
+                       return __this_address;
                if (!uuid_equal(&leaf3->info.uuid, &mp->m_sb.sb_meta_uuid))
-                       return false;
+                       return __this_address;
                if (be64_to_cpu(leaf3->info.blkno) != bp->b_bn)
-                       return false;
+                       return __this_address;
+               if (!xfs_log_check_lsn(mp, be64_to_cpu(leaf3->info.lsn)))
+                       return __this_address;
        } else {
                if (leaf->hdr.info.magic != cpu_to_be16(magic))
-                       return false;
+                       return __this_address;
        }
 
        return xfs_dir3_leaf_check_int(mp, NULL, NULL, leaf);
@@ -173,32 +173,34 @@ xfs_dir3_leaf_verify(
 static void
 __read_verify(
        struct xfs_buf  *bp,
-       __uint16_t      magic)
+       uint16_t        magic)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
+       xfs_failaddr_t          fa;
 
        if (xfs_sb_version_hascrc(&mp->m_sb) &&
             !xfs_buf_verify_cksum(bp, XFS_DIR3_LEAF_CRC_OFF))
-               xfs_buf_ioerror(bp, -EFSBADCRC);
-       else if (!xfs_dir3_leaf_verify(bp, magic))
-               xfs_buf_ioerror(bp, -EFSCORRUPTED);
-
-       if (bp->b_error)
-               xfs_verifier_error(bp);
+               xfs_verifier_error(bp, -EFSBADCRC, __this_address);
+       else {
+               fa = xfs_dir3_leaf_verify(bp, magic);
+               if (fa)
+                       xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+       }
 }
 
 static void
 __write_verify(
        struct xfs_buf  *bp,
-       __uint16_t      magic)
+       uint16_t        magic)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
-       struct xfs_buf_log_item *bip = bp->b_fspriv;
+       struct xfs_buf_log_item *bip = bp->b_log_item;
        struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr;
+       xfs_failaddr_t          fa;
 
-       if (!xfs_dir3_leaf_verify(bp, magic)) {
-               xfs_buf_ioerror(bp, -EFSCORRUPTED);
-               xfs_verifier_error(bp);
+       fa = xfs_dir3_leaf_verify(bp, magic);
+       if (fa) {
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
                return;
        }
 
@@ -211,6 +213,13 @@ __write_verify(
        xfs_buf_update_cksum(bp, XFS_DIR3_LEAF_CRC_OFF);
 }
 
+static xfs_failaddr_t
+xfs_dir3_leaf1_verify(
+       struct xfs_buf  *bp)
+{
+       return xfs_dir3_leaf_verify(bp, XFS_DIR2_LEAF1_MAGIC);
+}
+
 static void
 xfs_dir3_leaf1_read_verify(
        struct xfs_buf  *bp)
@@ -225,6 +234,13 @@ xfs_dir3_leaf1_write_verify(
        __write_verify(bp, XFS_DIR2_LEAF1_MAGIC);
 }
 
+static xfs_failaddr_t
+xfs_dir3_leafn_verify(
+       struct xfs_buf  *bp)
+{
+       return xfs_dir3_leaf_verify(bp, XFS_DIR2_LEAFN_MAGIC);
+}
+
 static void
 xfs_dir3_leafn_read_verify(
        struct xfs_buf  *bp)
@@ -240,16 +256,20 @@ xfs_dir3_leafn_write_verify(
 }
 
 const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops = {
+       .name = "xfs_dir3_leaf1",
        .verify_read = xfs_dir3_leaf1_read_verify,
        .verify_write = xfs_dir3_leaf1_write_verify,
+       .verify_struct = xfs_dir3_leaf1_verify,
 };
 
 const struct xfs_buf_ops xfs_dir3_leafn_buf_ops = {
+       .name = "xfs_dir3_leafn",
        .verify_read = xfs_dir3_leafn_read_verify,
        .verify_write = xfs_dir3_leafn_write_verify,
+       .verify_struct = xfs_dir3_leafn_verify,
 };
 
-static int
+int
 xfs_dir3_leaf_read(
        struct xfs_trans        *tp,
        struct xfs_inode        *dp,
@@ -261,7 +281,7 @@ xfs_dir3_leaf_read(
 
        err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
                                XFS_DATA_FORK, &xfs_dir3_leaf1_buf_ops);
-       if (!err && tp)
+       if (!err && tp && *bpp)
                xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
        return err;
 }
@@ -278,7 +298,7 @@ xfs_dir3_leafn_read(
 
        err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
                                XFS_DATA_FORK, &xfs_dir3_leafn_buf_ops);
-       if (!err && tp)
+       if (!err && tp && *bpp)
                xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
        return err;
 }
@@ -292,7 +312,7 @@ xfs_dir3_leaf_init(
        struct xfs_trans        *tp,
        struct xfs_buf          *bp,
        xfs_ino_t               owner,
-       __uint16_t              type)
+       uint16_t                type)
 {
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
 
@@ -336,7 +356,7 @@ xfs_dir3_leaf_get_buf(
        xfs_da_args_t           *args,
        xfs_dir2_db_t           bno,
        struct xfs_buf          **bpp,
-       __uint16_t              magic)
+       uint16_t                magic)
 {
        struct xfs_inode        *dp = args->dp;
        struct xfs_trans        *tp = args->trans;
@@ -567,8 +587,8 @@ xfs_dir3_leaf_find_entry(
                                (index - lowstale - 1) *
                                        sizeof(xfs_dir2_leaf_entry_t));
                }
-               *lfloglow = MIN(lowstale, *lfloglow);
-               *lfloghigh = MAX(index - 1, *lfloghigh);
+               *lfloglow = min(lowstale, *lfloglow);
+               *lfloghigh = max(index - 1, *lfloghigh);
                leafhdr->stale--;
                return &ents[index - 1];
        }
@@ -587,8 +607,8 @@ xfs_dir3_leaf_find_entry(
                memmove(&ents[index + 1], &ents[index],
                        (highstale - index) * sizeof(xfs_dir2_leaf_entry_t));
        }
-       *lfloglow = MIN(index, *lfloglow);
-       *lfloghigh = MAX(highstale, *lfloghigh);
+       *lfloglow = min(index, *lfloglow);
+       *lfloghigh = max(highstale, *lfloghigh);
        leafhdr->stale--;
        return &ents[index];
 }
@@ -838,14 +858,17 @@ xfs_dir2_leaf_addname(
         */
        dup = (xfs_dir2_data_unused_t *)
              ((char *)hdr + be16_to_cpu(bf[0].offset));
-       ASSERT(be16_to_cpu(dup->length) >= length);
        needscan = needlog = 0;
        /*
         * Mark the initial part of our freespace in use for the new entry.
         */
-       xfs_dir2_data_use_free(args, dbp, dup,
-               (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
-               &needlog, &needscan);
+       error = xfs_dir2_data_use_free(args, dbp, dup,
+                       (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+                       length, &needlog, &needscan);
+       if (error) {
+               xfs_trans_brelse(tp, lbp);
+               return error;
+       }
        /*
         * Initialize our new entry (at last).
         */
@@ -1381,7 +1404,8 @@ xfs_dir2_leaf_removename(
        oldbest = be16_to_cpu(bf[0].length);
        ltp = xfs_dir2_leaf_tail_p(args->geo, leaf);
        bestsp = xfs_dir2_leaf_bests_p(ltp);
-       ASSERT(be16_to_cpu(bestsp[db]) == oldbest);
+       if (be16_to_cpu(bestsp[db]) != oldbest)
+               return -EFSCORRUPTED;
        /*
         * Mark the former data entry unused.
         */