]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/zfs_replay.c
ZIL: Assert record sizes in different places
[mirror_zfs.git] / module / zfs / zfs_replay.c
index 1443817690590ec0bf0573b7cbce0f3d1d8bba46..2e0af60f6db42f707d8066a3c7cd6e9247b08fb9 100644 (file)
@@ -6,7 +6,7 @@
  * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
@@ -22,6 +22,7 @@
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 Cyril Plisko. All rights reserved.
  * Copyright (c) 2013, 2017 by Delphix. All rights reserved.
+ * Copyright (c) 2021, 2022 by Pawel Jakub Dawidek
  */
 
 #include <sys/types.h>
 #include <sys/zil.h>
 #include <sys/byteorder.h>
 #include <sys/stat.h>
-#include <sys/mode.h>
 #include <sys/acl.h>
 #include <sys/atomic.h>
 #include <sys/cred.h>
 #include <sys/zpl.h>
+#include <sys/dmu_objset.h>
+#include <sys/zfeature.h>
+
+/*
+ * NB: FreeBSD expects to be able to do vnode locking in lookup and
+ * hold the locks across all subsequent VOPs until vput is called.
+ * This means that its zfs vnops routines can't do any internal locking.
+ * In order to have the same contract as the Linux vnops there would
+ * needed to be duplicate locked vnops. If the vnops were used more widely
+ * in common code this would likely be preferable. However, currently
+ * this is the only file where this is the case.
+ */
 
 /*
  * Functions to replay ZFS intent log (ZIL) records
@@ -59,20 +71,22 @@ static void
 zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode,
     uint64_t uid, uint64_t gid, uint64_t rdev, uint64_t nodeid)
 {
-       bzero(vap, sizeof (*vap));
+       memset(vap, 0, sizeof (*vap));
        vap->va_mask = (uint_t)mask;
-       vap->va_type = IFTOVT(mode);
        vap->va_mode = mode;
+#if defined(__FreeBSD__) || defined(__APPLE__)
+       vap->va_type = IFTOVT(mode);
+#endif
        vap->va_uid = (uid_t)(IS_EPHEMERAL(uid)) ? -1 : uid;
        vap->va_gid = (gid_t)(IS_EPHEMERAL(gid)) ? -1 : gid;
-       vap->va_rdev = rdev;
+       vap->va_rdev = zfs_cmpldev(rdev);
        vap->va_nodeid = nodeid;
 }
 
-/* ARGSUSED */
 static int
 zfs_replay_error(void *arg1, void *arg2, boolean_t byteswap)
 {
+       (void) arg1, (void) arg2, (void) byteswap;
        return (SET_ERROR(ENOTSUP));
 }
 
@@ -130,13 +144,13 @@ zfs_replay_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
        if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) {
                ASSERT(!XVA_ISSET_REQ(xvap, XAT_PROJID));
 
-               bcopy(scanstamp, xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
+               memcpy(xoap->xoa_av_scanstamp, scanstamp, AV_SCANSTAMP_SZ);
        } else if (XVA_ISSET_REQ(xvap, XAT_PROJID)) {
                /*
                 * XAT_PROJID and XAT_AV_SCANSTAMP will never be valid
                 * at the same time, so we can share the same space.
                 */
-               bcopy(scanstamp, &xoap->xoa_projid, sizeof (uint64_t));
+               memcpy(&xoap->xoa_projid, scanstamp, sizeof (uint64_t));
        }
        if (XVA_ISSET_REQ(xvap, XAT_REPARSE))
                xoap->xoa_reparse = ((*attrs & XAT0_REPARSE) != 0);
@@ -282,7 +296,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
        char *name = NULL;              /* location determined later */
        lr_create_t *lr = (lr_create_t *)lracl;
        znode_t *dzp;
-       struct inode *ip = NULL;
+       znode_t *zp;
        xvattr_t xva;
        int vflg = 0;
        vsecattr_t vsec = { 0 };
@@ -295,6 +309,8 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
        uint64_t dnodesize;
        int error;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lracl));
+
        txtype = (lr->lr_common.lrc_txtype & ~TX_CI);
        if (byteswap) {
                byteswap_uint64_array(lracl, sizeof (*lracl));
@@ -337,8 +353,8 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
        xva.xva_vattr.va_nblocks = lr->lr_gen;
        xva.xva_vattr.va_fsid = dnodesize;
 
-       error = dmu_object_info(zfsvfs->z_os, lr->lr_foid, NULL);
-       if (error != ENOENT)
+       error = dnode_try_claim(zfsvfs->z_os, objid, dnodesize >> DNODE_SHIFT);
+       if (error)
                goto bail;
 
        if (lr->lr_common.lrc_txtype & TX_CI)
@@ -351,7 +367,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
                zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart,
                    (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
                    lr->lr_uid, lr->lr_gid);
-               /*FALLTHROUGH*/
+               zfs_fallthrough;
        case TX_CREATE_ACL_ATTR:
                if (name == NULL) {
                        lrattr = (lr_attr_t *)(caddr_t)(lracl + 1);
@@ -373,8 +389,13 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
                            lr->lr_uid, lr->lr_gid);
                }
 
-               error = zfs_create(ZTOI(dzp), name, &xva.xva_vattr,
-                   0, 0, &ip, kcred, vflg, &vsec);
+#if defined(__linux__)
+               error = zfs_create(dzp, name, &xva.xva_vattr,
+                   0, 0, &zp, kcred, vflg, &vsec, zfs_init_idmap);
+#else
+               error = zfs_create(dzp, name, &xva.xva_vattr,
+                   0, 0, &zp, kcred, vflg, &vsec, NULL);
+#endif
                break;
        case TX_MKDIR_ACL:
                aclstart = (caddr_t)(lracl + 1);
@@ -383,7 +404,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
                zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart,
                    (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
                    lr->lr_uid, lr->lr_gid);
-               /*FALLTHROUGH*/
+               zfs_fallthrough;
        case TX_MKDIR_ACL_ATTR:
                if (name == NULL) {
                        lrattr = (lr_attr_t *)(caddr_t)(lracl + 1);
@@ -403,18 +424,26 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
                            (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
                            lr->lr_uid, lr->lr_gid);
                }
-               error = zfs_mkdir(ZTOI(dzp), name, &xva.xva_vattr,
-                   &ip, kcred, vflg, &vsec);
+#if defined(__linux__)
+               error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+                   &zp, kcred, vflg, &vsec, zfs_init_idmap);
+#else
+               error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+                   &zp, kcred, vflg, &vsec, NULL);
+#endif
                break;
        default:
                error = SET_ERROR(ENOTSUP);
        }
 
 bail:
-       if (error == 0 && ip != NULL)
-               iput(ip);
-
-       iput(ZTOI(dzp));
+       if (error == 0 && zp != NULL) {
+#ifdef __FreeBSD__
+               VOP_UNLOCK1(ZTOV(zp));
+#endif
+               zrele(zp);
+       }
+       zrele(dzp);
 
        if (zfsvfs->z_fuid_replay)
                zfs_fuid_info_free(zfsvfs->z_fuid_replay);
@@ -431,7 +460,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
        char *name = NULL;              /* location determined later */
        char *link;                     /* symlink content follows name */
        znode_t *dzp;
-       struct inode *ip = NULL;
+       znode_t *zp = NULL;
        xvattr_t xva;
        int vflg = 0;
        size_t lrsize = sizeof (lr_create_t);
@@ -443,6 +472,8 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
        uint64_t dnodesize;
        int error;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr));
+
        txtype = (lr->lr_common.lrc_txtype & ~TX_CI);
        if (byteswap) {
                byteswap_uint64_array(lr, sizeof (*lr));
@@ -473,8 +504,8 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
        xva.xva_vattr.va_nblocks = lr->lr_gen;
        xva.xva_vattr.va_fsid = dnodesize;
 
-       error = dmu_object_info(zfsvfs->z_os, objid, NULL);
-       if (error != ENOENT)
+       error = dnode_try_claim(zfsvfs->z_os, objid, dnodesize >> DNODE_SHIFT);
+       if (error)
                goto out;
 
        if (lr->lr_common.lrc_txtype & TX_CI)
@@ -486,9 +517,9 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
         *
         * The _ATTR versions will grab the fuid info in their subcases.
         */
-       if ((int)lr->lr_common.lrc_txtype != TX_SYMLINK &&
-           (int)lr->lr_common.lrc_txtype != TX_MKDIR_ATTR &&
-           (int)lr->lr_common.lrc_txtype != TX_CREATE_ATTR) {
+       if (txtype != TX_SYMLINK &&
+           txtype != TX_MKDIR_ATTR &&
+           txtype != TX_CREATE_ATTR) {
                start = (lr + 1);
                zfsvfs->z_fuid_replay =
                    zfs_replay_fuid_domain(start, &start,
@@ -505,14 +536,19 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
                    zfs_replay_fuid_domain(start, &start,
                    lr->lr_uid, lr->lr_gid);
                name = (char *)start;
+               zfs_fallthrough;
 
-               /*FALLTHROUGH*/
        case TX_CREATE:
                if (name == NULL)
                        name = (char *)start;
 
-               error = zfs_create(ZTOI(dzp), name, &xva.xva_vattr,
-                   0, 0, &ip, kcred, vflg, NULL);
+#if defined(__linux__)
+               error = zfs_create(dzp, name, &xva.xva_vattr,
+                   0, 0, &zp, kcred, vflg, NULL, zfs_init_idmap);
+#else
+               error = zfs_create(dzp, name, &xva.xva_vattr,
+                   0, 0, &zp, kcred, vflg, NULL, NULL);
+#endif
                break;
        case TX_MKDIR_ATTR:
                lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
@@ -523,33 +559,47 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
                    zfs_replay_fuid_domain(start, &start,
                    lr->lr_uid, lr->lr_gid);
                name = (char *)start;
+               zfs_fallthrough;
 
-               /*FALLTHROUGH*/
        case TX_MKDIR:
                if (name == NULL)
                        name = (char *)(lr + 1);
 
-               error = zfs_mkdir(ZTOI(dzp), name, &xva.xva_vattr,
-                   &ip, kcred, vflg, NULL);
+#if defined(__linux__)
+               error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+                   &zp, kcred, vflg, NULL, zfs_init_idmap);
+#else
+               error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+                   &zp, kcred, vflg, NULL, NULL);
+#endif
+
                break;
        case TX_MKXATTR:
-               error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &ip, kcred);
+               error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
                break;
        case TX_SYMLINK:
                name = (char *)(lr + 1);
                link = name + strlen(name) + 1;
-               error = zfs_symlink(ZTOI(dzp), name, &xva.xva_vattr,
-                   link, &ip, kcred, vflg);
+#if defined(__linux__)
+               error = zfs_symlink(dzp, name, &xva.xva_vattr,
+                   link, &zp, kcred, vflg, zfs_init_idmap);
+#else
+               error = zfs_symlink(dzp, name, &xva.xva_vattr,
+                   link, &zp, kcred, vflg, NULL);
+#endif
                break;
        default:
                error = SET_ERROR(ENOTSUP);
        }
 
 out:
-       if (error == 0 && ip != NULL)
-               iput(ip);
-
-       iput(ZTOI(dzp));
+       if (error == 0 && zp != NULL) {
+#ifdef __FreeBSD__
+               VOP_UNLOCK1(ZTOV(zp));
+#endif
+               zrele(zp);
+       }
+       zrele(dzp);
 
        if (zfsvfs->z_fuid_replay)
                zfs_fuid_info_free(zfsvfs->z_fuid_replay);
@@ -567,6 +617,8 @@ zfs_replay_remove(void *arg1, void *arg2, boolean_t byteswap)
        int error;
        int vflg = 0;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr));
+
        if (byteswap)
                byteswap_uint64_array(lr, sizeof (*lr));
 
@@ -578,16 +630,16 @@ zfs_replay_remove(void *arg1, void *arg2, boolean_t byteswap)
 
        switch ((int)lr->lr_common.lrc_txtype) {
        case TX_REMOVE:
-               error = zfs_remove(ZTOI(dzp), name, kcred, vflg);
+               error = zfs_remove(dzp, name, kcred, vflg);
                break;
        case TX_RMDIR:
-               error = zfs_rmdir(ZTOI(dzp), name, NULL, kcred, vflg);
+               error = zfs_rmdir(dzp, name, NULL, kcred, vflg);
                break;
        default:
                error = SET_ERROR(ENOTSUP);
        }
 
-       iput(ZTOI(dzp));
+       zrele(dzp);
 
        return (error);
 }
@@ -602,6 +654,8 @@ zfs_replay_link(void *arg1, void *arg2, boolean_t byteswap)
        int error;
        int vflg = 0;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr));
+
        if (byteswap)
                byteswap_uint64_array(lr, sizeof (*lr));
 
@@ -609,54 +663,147 @@ zfs_replay_link(void *arg1, void *arg2, boolean_t byteswap)
                return (error);
 
        if ((error = zfs_zget(zfsvfs, lr->lr_link_obj, &zp)) != 0) {
-               iput(ZTOI(dzp));
+               zrele(dzp);
                return (error);
        }
 
        if (lr->lr_common.lrc_txtype & TX_CI)
                vflg |= FIGNORECASE;
 
-       error = zfs_link(ZTOI(dzp), ZTOI(zp), name, kcred, vflg);
-
-       iput(ZTOI(zp));
-       iput(ZTOI(dzp));
+       error = zfs_link(dzp, zp, name, kcred, vflg);
+       zrele(zp);
+       zrele(dzp);
 
        return (error);
 }
 
 static int
-zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+do_zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, char *sname,
+    char *tname, uint64_t rflags, vattr_t *wo_vap)
 {
-       zfsvfs_t *zfsvfs = arg1;
-       lr_rename_t *lr = arg2;
-       char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
-       char *tname = sname + strlen(sname) + 1;
        znode_t *sdzp, *tdzp;
-       int error;
-       int vflg = 0;
+       int error, vflg = 0;
 
-       if (byteswap)
-               byteswap_uint64_array(lr, sizeof (*lr));
+       /* Only Linux currently supports RENAME_* flags. */
+#ifdef __linux__
+       VERIFY0(rflags & ~(RENAME_EXCHANGE | RENAME_WHITEOUT));
+
+       /* wo_vap must be non-NULL iff. we're doing RENAME_WHITEOUT */
+       VERIFY_EQUIV(rflags & RENAME_WHITEOUT, wo_vap != NULL);
+#else
+       VERIFY0(rflags);
+#endif
 
        if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0)
                return (error);
 
        if ((error = zfs_zget(zfsvfs, lr->lr_tdoid, &tdzp)) != 0) {
-               iput(ZTOI(sdzp));
+               zrele(sdzp);
                return (error);
        }
 
        if (lr->lr_common.lrc_txtype & TX_CI)
                vflg |= FIGNORECASE;
 
-       error = zfs_rename(ZTOI(sdzp), sname, ZTOI(tdzp), tname, kcred, vflg);
-
-       iput(ZTOI(tdzp));
-       iput(ZTOI(sdzp));
+#if defined(__linux__)
+       error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, rflags,
+           wo_vap, zfs_init_idmap);
+#else
+       error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, rflags,
+           wo_vap, NULL);
+#endif
 
+       zrele(tdzp);
+       zrele(sdzp);
        return (error);
 }
 
+static int
+zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+{
+       zfsvfs_t *zfsvfs = arg1;
+       lr_rename_t *lr = arg2;
+
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr));
+
+       if (byteswap)
+               byteswap_uint64_array(lr, sizeof (*lr));
+
+       char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
+       char *tname = sname + strlen(sname) + 1;
+       return (do_zfs_replay_rename(zfsvfs, lr, sname, tname, 0, NULL));
+}
+
+static int
+zfs_replay_rename_exchange(void *arg1, void *arg2, boolean_t byteswap)
+{
+#ifdef __linux__
+       zfsvfs_t *zfsvfs = arg1;
+       lr_rename_t *lr = arg2;
+
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr));
+
+       if (byteswap)
+               byteswap_uint64_array(lr, sizeof (*lr));
+
+       char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
+       char *tname = sname + strlen(sname) + 1;
+       return (do_zfs_replay_rename(zfsvfs, lr, sname, tname, RENAME_EXCHANGE,
+           NULL));
+#else
+       return (SET_ERROR(ENOTSUP));
+#endif
+}
+
+static int
+zfs_replay_rename_whiteout(void *arg1, void *arg2, boolean_t byteswap)
+{
+#ifdef __linux__
+       zfsvfs_t *zfsvfs = arg1;
+       lr_rename_whiteout_t *lr = arg2;
+       int error;
+       /* For the whiteout file. */
+       xvattr_t xva;
+       uint64_t objid;
+       uint64_t dnodesize;
+
+       ASSERT3U(lr->lr_rename.lr_common.lrc_reclen, >, sizeof (*lr));
+
+       if (byteswap)
+               byteswap_uint64_array(lr, sizeof (*lr));
+
+       objid = LR_FOID_GET_OBJ(lr->lr_wfoid);
+       dnodesize = LR_FOID_GET_SLOTS(lr->lr_wfoid) << DNODE_SHIFT;
+
+       xva_init(&xva);
+       zfs_init_vattr(&xva.xva_vattr, ATTR_MODE | ATTR_UID | ATTR_GID,
+           lr->lr_wmode, lr->lr_wuid, lr->lr_wgid, lr->lr_wrdev, objid);
+
+       /*
+        * As with TX_CREATE, RENAME_WHITEOUT ends up in zfs_mknode(), which
+        * assigns the object's creation time, generation number, and dnode
+        * slot count. The generic zfs_rename() has no concept of these
+        * attributes, so we smuggle the values inside the vattr's otherwise
+        * unused va_ctime, va_nblocks, and va_fsid fields.
+        */
+       ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_wcrtime);
+       xva.xva_vattr.va_nblocks = lr->lr_wgen;
+       xva.xva_vattr.va_fsid = dnodesize;
+
+       error = dnode_try_claim(zfsvfs->z_os, objid, dnodesize >> DNODE_SHIFT);
+       if (error)
+               return (error);
+
+       /* sname and tname follow lr_rename_whiteout_t */
+       char *sname = (char *)(lr + 1);
+       char *tname = sname + strlen(sname) + 1;
+       return (do_zfs_replay_rename(zfsvfs, &lr->lr_rename, sname, tname,
+           RENAME_WHITEOUT, &xva.xva_vattr));
+#else
+       return (SET_ERROR(ENOTSUP));
+#endif
+}
+
 static int
 zfs_replay_write(void *arg1, void *arg2, boolean_t byteswap)
 {
@@ -664,9 +811,11 @@ zfs_replay_write(void *arg1, void *arg2, boolean_t byteswap)
        lr_write_t *lr = arg2;
        char *data = (char *)(lr + 1);  /* data follows lr_write_t */
        znode_t *zp;
-       int error, written;
+       int error;
        uint64_t eod, offset, length;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+
        if (byteswap)
                byteswap_uint64_array(lr, sizeof (*lr));
 
@@ -708,15 +857,8 @@ zfs_replay_write(void *arg1, void *arg2, boolean_t byteswap)
                if (zp->z_size < eod)
                        zfsvfs->z_replay_eof = eod;
        }
-
-       written = zpl_write_common(ZTOI(zp), data, length, &offset,
-           UIO_SYSSPACE, 0, kcred);
-       if (written < 0)
-               error = -written;
-       else if (written < length)
-               error = SET_ERROR(EIO); /* short write */
-
-       iput(ZTOI(zp));
+       error = zfs_write_simple(zp, data, length, offset, NULL);
+       zrele(zp);
        zfsvfs->z_replay_eof = 0;       /* safety */
 
        return (error);
@@ -737,6 +879,8 @@ zfs_replay_write2(void *arg1, void *arg2, boolean_t byteswap)
        int error;
        uint64_t end;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+
        if (byteswap)
                byteswap_uint64_array(lr, sizeof (*lr));
 
@@ -752,7 +896,7 @@ top:
                dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
                error = dmu_tx_assign(tx, TXG_WAIT);
                if (error) {
-                       iput(ZTOI(zp));
+                       zrele(zp);
                        if (error == ERESTART) {
                                dmu_tx_wait(tx);
                                dmu_tx_abort(tx);
@@ -770,7 +914,7 @@ top:
                dmu_tx_commit(tx);
        }
 
-       iput(ZTOI(zp));
+       zrele(zp);
 
        return (error);
 }
@@ -781,25 +925,26 @@ zfs_replay_truncate(void *arg1, void *arg2, boolean_t byteswap)
        zfsvfs_t *zfsvfs = arg1;
        lr_truncate_t *lr = arg2;
        znode_t *zp;
-       flock64_t fl;
+       flock64_t fl = {0};
        int error;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+
        if (byteswap)
                byteswap_uint64_array(lr, sizeof (*lr));
 
        if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0)
                return (error);
 
-       bzero(&fl, sizeof (fl));
        fl.l_type = F_WRLCK;
        fl.l_whence = SEEK_SET;
        fl.l_start = lr->lr_offset;
        fl.l_len = lr->lr_length;
 
-       error = zfs_space(ZTOI(zp), F_FREESP, &fl, FWRITE | FOFFMAX,
+       error = zfs_space(zp, F_FREESP, &fl, O_RDWR | O_LARGEFILE,
            lr->lr_offset, kcred);
 
-       iput(ZTOI(zp));
+       zrele(zp);
 
        return (error);
 }
@@ -815,6 +960,8 @@ zfs_replay_setattr(void *arg1, void *arg2, boolean_t byteswap)
        int error;
        void *start;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+
        xva_init(&xva);
        if (byteswap) {
                byteswap_uint64_array(lr, sizeof (*lr));
@@ -851,12 +998,99 @@ zfs_replay_setattr(void *arg1, void *arg2, boolean_t byteswap)
        zfsvfs->z_fuid_replay = zfs_replay_fuid_domain(start, &start,
            lr->lr_uid, lr->lr_gid);
 
-       error = zfs_setattr(ZTOI(zp), vap, 0, kcred);
+#if defined(__linux__)
+       error = zfs_setattr(zp, vap, 0, kcred, zfs_init_idmap);
+#else
+       error = zfs_setattr(zp, vap, 0, kcred, NULL);
+#endif
 
        zfs_fuid_info_free(zfsvfs->z_fuid_replay);
        zfsvfs->z_fuid_replay = NULL;
-       iput(ZTOI(zp));
+       zrele(zp);
+
+       return (error);
+}
+
+static int
+zfs_replay_setsaxattr(void *arg1, void *arg2, boolean_t byteswap)
+{
+       zfsvfs_t *zfsvfs = arg1;
+       lr_setsaxattr_t *lr = arg2;
+       znode_t *zp;
+       nvlist_t *nvl;
+       size_t sa_size;
+       char *name;
+       char *value;
+       size_t size;
+       int error = 0;
+
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+       ASSERT3U(lr->lr_common.lrc_reclen, >, sizeof (*lr) + lr->lr_size);
+
+       ASSERT(spa_feature_is_active(zfsvfs->z_os->os_spa,
+           SPA_FEATURE_ZILSAXATTR));
+       if (byteswap)
+               byteswap_uint64_array(lr, sizeof (*lr));
+
+       if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0)
+               return (error);
+
+       rw_enter(&zp->z_xattr_lock, RW_WRITER);
+       mutex_enter(&zp->z_lock);
+       if (zp->z_xattr_cached == NULL)
+               error = zfs_sa_get_xattr(zp);
+       mutex_exit(&zp->z_lock);
+
+       if (error)
+               goto out;
+
+       ASSERT(zp->z_xattr_cached);
+       nvl = zp->z_xattr_cached;
+
+       /* Get xattr name, value and size from log record */
+       size = lr->lr_size;
+       name = (char *)(lr + 1);
+       if (size == 0) {
+               value = NULL;
+               error = nvlist_remove(nvl, name, DATA_TYPE_BYTE_ARRAY);
+       } else {
+               value = name + strlen(name) + 1;
+               /* Limited to 32k to keep nvpair memory allocations small */
+               if (size > DXATTR_MAX_ENTRY_SIZE) {
+                       error = SET_ERROR(EFBIG);
+                       goto out;
+               }
 
+               /* Prevent the DXATTR SA from consuming the entire SA region */
+               error = nvlist_size(nvl, &sa_size, NV_ENCODE_XDR);
+               if (error)
+                       goto out;
+
+               if (sa_size > DXATTR_MAX_SA_SIZE) {
+                       error = SET_ERROR(EFBIG);
+                       goto out;
+               }
+
+               error = nvlist_add_byte_array(nvl, name, (uchar_t *)value,
+                   size);
+       }
+
+       /*
+        * Update the SA for additions, modifications, and removals. On
+        * error drop the inconsistent cached version of the nvlist, it
+        * will be reconstructed from the ARC when next accessed.
+        */
+       if (error == 0)
+               error = zfs_sa_set_xattr(zp, name, value, size);
+
+       if (error) {
+               nvlist_free(nvl);
+               zp->z_xattr_cached = NULL;
+       }
+
+out:
+       rw_exit(&zp->z_xattr_lock);
+       zrele(zp);
        return (error);
 }
 
@@ -866,10 +1100,14 @@ zfs_replay_acl_v0(void *arg1, void *arg2, boolean_t byteswap)
        zfsvfs_t *zfsvfs = arg1;
        lr_acl_v0_t *lr = arg2;
        ace_t *ace = (ace_t *)(lr + 1); /* ace array follows lr_acl_t */
-       vsecattr_t vsa;
+       vsecattr_t vsa = {0};
        znode_t *zp;
        int error;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr) +
+           sizeof (ace_t) * lr->lr_aclcnt);
+
        if (byteswap) {
                byteswap_uint64_array(lr, sizeof (*lr));
                zfs_oldace_byteswap(ace, lr->lr_aclcnt);
@@ -878,16 +1116,15 @@ zfs_replay_acl_v0(void *arg1, void *arg2, boolean_t byteswap)
        if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0)
                return (error);
 
-       bzero(&vsa, sizeof (vsa));
        vsa.vsa_mask = VSA_ACE | VSA_ACECNT;
        vsa.vsa_aclcnt = lr->lr_aclcnt;
        vsa.vsa_aclentsz = sizeof (ace_t) * vsa.vsa_aclcnt;
        vsa.vsa_aclflags = 0;
        vsa.vsa_aclentp = ace;
 
-       error = zfs_setsecattr(ZTOI(zp), &vsa, 0, kcred);
+       error = zfs_setsecattr(zp, &vsa, 0, kcred);
 
-       iput(ZTOI(zp));
+       zrele(zp);
 
        return (error);
 }
@@ -912,10 +1149,13 @@ zfs_replay_acl(void *arg1, void *arg2, boolean_t byteswap)
        zfsvfs_t *zfsvfs = arg1;
        lr_acl_t *lr = arg2;
        ace_t *ace = (ace_t *)(lr + 1);
-       vsecattr_t vsa;
+       vsecattr_t vsa = {0};
        znode_t *zp;
        int error;
 
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr) + lr->lr_acl_bytes);
+
        if (byteswap) {
                byteswap_uint64_array(lr, sizeof (*lr));
                zfs_ace_byteswap(ace, lr->lr_acl_bytes, B_FALSE);
@@ -929,7 +1169,6 @@ zfs_replay_acl(void *arg1, void *arg2, boolean_t byteswap)
        if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0)
                return (error);
 
-       bzero(&vsa, sizeof (vsa));
        vsa.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS;
        vsa.vsa_aclcnt = lr->lr_aclcnt;
        vsa.vsa_aclentp = ace;
@@ -945,21 +1184,53 @@ zfs_replay_acl(void *arg1, void *arg2, boolean_t byteswap)
                    lr->lr_fuidcnt, lr->lr_domcnt, 0, 0);
        }
 
-       error = zfs_setsecattr(ZTOI(zp), &vsa, 0, kcred);
+       error = zfs_setsecattr(zp, &vsa, 0, kcred);
 
        if (zfsvfs->z_fuid_replay)
                zfs_fuid_info_free(zfsvfs->z_fuid_replay);
 
        zfsvfs->z_fuid_replay = NULL;
-       iput(ZTOI(zp));
+       zrele(zp);
+
+       return (error);
+}
+
+static int
+zfs_replay_clone_range(void *arg1, void *arg2, boolean_t byteswap)
+{
+       zfsvfs_t *zfsvfs = arg1;
+       lr_clone_range_t *lr = arg2;
+       znode_t *zp;
+       int error;
+
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, sizeof (*lr));
+       ASSERT3U(lr->lr_common.lrc_reclen, >=, offsetof(lr_clone_range_t,
+           lr_bps[lr->lr_nbps]));
+
+       if (byteswap)
+               byteswap_uint64_array(lr, sizeof (*lr));
+
+       if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) {
+               /*
+                * Clones can be logged out of order, so don't be surprised if
+                * the file is gone - just return success.
+                */
+               if (error == ENOENT)
+                       error = 0;
+               return (error);
+       }
+
+       error = zfs_clone_range_replay(zp, lr->lr_offset, lr->lr_length,
+           lr->lr_blksz, lr->lr_bps, lr->lr_nbps);
 
+       zrele(zp);
        return (error);
 }
 
 /*
  * Callback vectors for replaying records
  */
-zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = {
+zil_replay_func_t *const zfs_replay_vector[TX_MAX_TYPE] = {
        zfs_replay_error,       /* no such type */
        zfs_replay_create,      /* TX_CREATE */
        zfs_replay_create,      /* TX_MKDIR */
@@ -981,4 +1252,8 @@ zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = {
        zfs_replay_create,      /* TX_MKDIR_ATTR */
        zfs_replay_create_acl,  /* TX_MKDIR_ACL_ATTR */
        zfs_replay_write2,      /* TX_WRITE2 */
+       zfs_replay_setsaxattr,  /* TX_SETSAXATTR */
+       zfs_replay_rename_exchange,     /* TX_RENAME_EXCHANGE */
+       zfs_replay_rename_whiteout,     /* TX_RENAME_WHITEOUT */
+       zfs_replay_clone_range, /* TX_CLONE_RANGE */
 };