]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/rdwr.c
xfs: fix transaction leak on remote attr set/remove failure
[thirdparty/xfsprogs-dev.git] / libxfs / rdwr.c
index ab5c91664402369e971d81bd2b9ce632c491348b..14a4633e9fa60d3a722d11b257b879ed74e67adf 100644 (file)
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2006 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
  */
 
 
@@ -118,9 +106,9 @@ static void unmount_record(void *p)
        xlog_op_header_t        *op = (xlog_op_header_t *)p;
        /* the data section must be 32 bit size aligned */
        struct {
-           __uint16_t magic;
-           __uint16_t pad1;
-           __uint32_t pad2; /* may as well make it 64 bits */
+           uint16_t magic;
+           uint16_t pad1;
+           uint32_t pad2; /* may as well make it 64 bits */
        } magic = { XLOG_UNMOUNT_TYPE, 0, 0 };
 
        memset(p, 0, BBSIZE);
@@ -143,7 +131,7 @@ static char *next(
        struct xfs_buf  *buf = (struct xfs_buf *)private;
 
        if (buf &&
-           (XFS_BUF_COUNT(buf) < (int)(ptr - XFS_BUF_PTR(buf)) + offset))
+           (buf->b_bcount < (int)(ptr - (char *)buf->b_addr) + offset))
                abort();
 
        return ptr + offset;
@@ -193,7 +181,7 @@ libxfs_log_clear(
         * previous cycle.
         */
        len = ((version == 2) && sunit) ? BTOBB(sunit) : 2;
-       len = MAX(len, 2);
+       len = max(len, 2);
        lsn = xlog_assign_lsn(cycle, 0);
        if (cycle == XLOG_INIT_CYCLE)
                tail_lsn = lsn;
@@ -204,7 +192,7 @@ libxfs_log_clear(
        ptr = dptr;
        if (btp) {
                bp = libxfs_getbufr(btp, start, len);
-               ptr = XFS_BUF_PTR(bp);
+               ptr = bp->b_addr;
        }
        libxfs_log_header(ptr, fs_uuid, version, sunit, fmt, lsn, tail_lsn,
                          next, bp);
@@ -252,7 +240,7 @@ libxfs_log_clear(
                ptr = dptr;
                if (btp) {
                        bp = libxfs_getbufr(btp, blk, len);
-                       ptr = XFS_BUF_PTR(bp);
+                       ptr = bp->b_addr;
                }
                /*
                 * Note: pass the full buffer length as the sunit to initialize
@@ -307,7 +295,7 @@ libxfs_log_header(
        head->h_prev_block = cpu_to_be32(-1);
        head->h_num_logops = cpu_to_be32(1);
        head->h_fmt = cpu_to_be32(fmt);
-       head->h_size = cpu_to_be32(MAX(sunit, XLOG_BIG_RECORD_BSIZE));
+       head->h_size = cpu_to_be32(max(sunit, XLOG_BIG_RECORD_BSIZE));
 
        head->h_lsn = cpu_to_be64(lsn);
        head->h_tail_lsn = cpu_to_be64(tail_lsn);
@@ -358,7 +346,7 @@ libxfs_log_header(
         * minimum (1 hdr blk + 1 data blk). The record length is the total
         * minus however many header blocks are required.
         */
-       head->h_len = cpu_to_be32(MAX(BBTOB(2), sunit) - hdrs * BBSIZE);
+       head->h_len = cpu_to_be32(max(BBTOB(2), sunit) - hdrs * BBSIZE);
 
        /*
         * Write out the unmount record, pack the first word into the record
@@ -375,7 +363,7 @@ libxfs_log_header(
         * the cycle. We don't need to pack any of these blocks because the
         * cycle data in the headers has already been zeroed.
         */
-       len = MAX(len, hdrs + 1);
+       len = max(len, hdrs + 1);
        for (i = hdrs + 1; i < len; i++) {
                p = nextfunc(p, BBSIZE, private);
                memset(p, 0, BBSIZE);
@@ -559,7 +547,7 @@ libxfs_bcompare(struct cache_node *node, cache_key_t key)
 void
 libxfs_bprint(xfs_buf_t *bp)
 {
-       fprintf(stderr, "Buffer 0x%p blkno=%llu bytes=%u flags=0x%x count=%u\n",
+       fprintf(stderr, "Buffer %p blkno=%llu bytes=%u flags=0x%x count=%u\n",
                bp, (unsigned long long)bp->b_bn, (unsigned)bp->b_bcount,
                bp->b_flags, bp->b_node.cn_count);
 }
@@ -591,6 +579,13 @@ __initbuf(xfs_buf_t *bp, struct xfs_buftarg *btp, xfs_daddr_t bno,
        bp->b_holder = 0;
        bp->b_recur = 0;
        bp->b_ops = NULL;
+
+       if (!bp->b_maps) {
+               bp->b_nmaps = 1;
+               bp->b_maps = &bp->__b_map;
+               bp->b_maps[0].bm_bn = bp->b_bn;
+               bp->b_maps[0].bm_len = bp->b_length;
+       }
 }
 
 static void
@@ -654,7 +649,8 @@ __libxfs_getbufr(int blen)
                        list_del_init(&bp->b_node.cn_mru);
                        free(bp->b_addr);
                        bp->b_addr = NULL;
-                       free(bp->b_maps);
+                       if (bp->b_maps != &bp->__b_map)
+                               free(bp->b_maps);
                        bp->b_maps = NULL;
                }
        } else
@@ -915,7 +911,7 @@ __read_buf(int fd, void *buf, int len, off64_t offset, int flags)
 {
        int     sts;
 
-       sts = pread64(fd, buf, len, offset);
+       sts = pread(fd, buf, len, offset);
        if (sts < 0) {
                int error = errno;
                fprintf(stderr, _("%s: read failed: %s\n"),
@@ -1018,7 +1014,7 @@ libxfs_readbufr_map(struct xfs_buftarg *btp, struct xfs_buf *bp, int flags)
 {
        int     fd;
        int     error = 0;
-       char    *buf;
+       void    *buf;
        int     i;
 
        fd = libxfs_device_to_fd(btp->dev);
@@ -1083,16 +1079,16 @@ __write_buf(int fd, void *buf, int len, off64_t offset, int flags)
 {
        int     sts;
 
-       sts = pwrite64(fd, buf, len, offset);
+       sts = pwrite(fd, buf, len, offset);
        if (sts < 0) {
                int error = errno;
-               fprintf(stderr, _("%s: pwrite64 failed: %s\n"),
+               fprintf(stderr, _("%s: pwrite failed: %s\n"),
                        progname, strerror(error));
                if (flags & LIBXFS_B_EXIT)
                        exit(1);
                return -error;
        } else if (sts != len) {
-               fprintf(stderr, _("%s: error - pwrite64 only %d of %d bytes\n"),
+               fprintf(stderr, _("%s: error - pwrite only %d of %d bytes\n"),
                        progname, sts, len);
                if (flags & LIBXFS_B_EXIT)
                        exit(1);
@@ -1139,7 +1135,7 @@ libxfs_writebufr(xfs_buf_t *bp)
                                    LIBXFS_BBTOOFF64(bp->b_bn), bp->b_flags);
        } else {
                int     i;
-               char    *buf = bp->b_addr;
+               void    *buf = bp->b_addr;
 
                for (i = 0; i < bp->b_nmaps; i++) {
                        off64_t offset = LIBXFS_BBTOOFF64(bp->b_maps[i].bm_bn);
@@ -1266,6 +1262,24 @@ libxfs_bulkrelse(
        return count;
 }
 
+/*
+ * Free everything from the xfs_buf_freelist MRU, used at final teardown
+ */
+void
+libxfs_bcache_free(void)
+{
+       struct list_head        *cm_list;
+       xfs_buf_t               *bp, *next;
+
+       cm_list = &xfs_buf_freelist.cm_list;
+       list_for_each_entry_safe(bp, next, cm_list, b_node.cn_mru) {
+               free(bp->b_addr);
+               if (bp->b_maps != &bp->__b_map)
+                       free(bp->b_maps);
+               kmem_zone_free(xfs_buf_zone, bp);
+       }
+}
+
 /*
  * When a buffer is marked dirty, the error is cleared. Hence if we are trying
  * to flush a buffer prior to cache reclaim that has an error on it it means
@@ -1325,15 +1339,54 @@ struct cache_operations libxfs_bcache_operations = {
  * Inode cache stubs.
  */
 
+kmem_zone_t            *xfs_inode_zone;
 extern kmem_zone_t     *xfs_ili_zone;
-extern kmem_zone_t     *xfs_inode_zone;
+
+/*
+ * If there are inline format data / attr forks attached to this inode,
+ * make sure they're not corrupt.
+ */
+bool
+libxfs_inode_verify_forks(
+       struct xfs_inode        *ip,
+       struct xfs_ifork_ops    *ops)
+{
+       struct xfs_ifork        *ifp;
+       xfs_failaddr_t          fa;
+
+       if (!ops)
+               return true;
+
+       fa = xfs_ifork_verify_data(ip, ops);
+       if (fa) {
+               ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+               xfs_inode_verifier_error(ip, -EFSCORRUPTED, "data fork",
+                               ifp->if_u1.if_data, ifp->if_bytes, fa);
+               return false;
+       }
+
+       fa = xfs_ifork_verify_attr(ip, ops);
+       if (fa) {
+               ifp = XFS_IFORK_PTR(ip, XFS_ATTR_FORK);
+               xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork",
+                               ifp ? ifp->if_u1.if_data : NULL,
+                               ifp ? ifp->if_bytes : 0, fa);
+               return false;
+       }
+       return true;
+}
 
 int
-libxfs_iget(xfs_mount_t *mp, xfs_trans_t *tp, xfs_ino_t ino, uint lock_flags,
-               xfs_inode_t **ipp)
+libxfs_iget(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_ino_t               ino,
+       uint                    lock_flags,
+       struct xfs_inode        **ipp,
+       struct xfs_ifork_ops    *ifork_ops)
 {
-       xfs_inode_t     *ip;
-       int             error = 0;
+       struct xfs_inode        *ip;
+       int                     error = 0;
 
        ip = kmem_zone_zalloc(xfs_inode_zone, 0);
        if (!ip)
@@ -1348,6 +1401,11 @@ libxfs_iget(xfs_mount_t *mp, xfs_trans_t *tp, xfs_ino_t ino, uint lock_flags,
                return error;
        }
 
+       if (!libxfs_inode_verify_forks(ip, ifork_ops)) {
+               libxfs_iput(ip);
+               return -EFSCORRUPTED;
+       }
+
        /*
         * set up the inode ops structure that the libxfs code relies on
         */