From f9fc76f53bd95ab6fa74d6bee45ca88723ecdbb0 Mon Sep 17 00:00:00 2001
From: Spotlight <spotlight@joscomputing.space>
Date: Wed, 26 Apr 2023 13:31:30 -0500
Subject: [PATCH] sys-fs/zfs-kmod: Add Linux kernel 6.3.x patchset

---
 sys-fs/zfs-kmod/Manifest                      |    8 +-
 ....3-compat-idmapped-mount-api-changes.patch | 2573 +++++++++++++
 ....3-compat-writepage_t-typedef-change.patch |  170 +
 .../2.1.11-expose-additional-attributes.patch | 1272 +++++++
 .../files/2.1.11-optimize-access-checks.patch |  207 ++
 ...11-support-idmapped-mount-in-user-ns.patch | 2012 +++++++++++
 .../files/2.1.11-support-idmapped-mount.patch | 3203 +++++++++++++++++
 sys-fs/zfs-kmod/zfs-kmod-2.1.11.ebuild        |   21 +
 8 files changed, 9465 insertions(+), 1 deletion(-)
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-6.3-compat-idmapped-mount-api-changes.patch
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-6.3-compat-writepage_t-typedef-change.patch
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-expose-additional-attributes.patch
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-optimize-access-checks.patch
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount-in-user-ns.patch
 create mode 100644 sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount.patch

diff --git a/sys-fs/zfs-kmod/Manifest b/sys-fs/zfs-kmod/Manifest
index abdf0fd..6a83a98 100644
--- a/sys-fs/zfs-kmod/Manifest
+++ b/sys-fs/zfs-kmod/Manifest
@@ -1,8 +1,14 @@
 AUX 2.1.10-Revert-ZFS_IOC_COUNT_FILLED-does-unnecessary-txg_wai.patch 1932 BLAKE2B 761f1ed4a17c946fb08aa46c3b9defb0672897311dc61225f28298af7569f6350b264cb917696c091ebbf2a95aec8e2005385d27d3f996b99cc424ae0b503082 SHA512 ebdb7c6562eba403397b17dc0830f5c10a80b339ff62f52b569b4ddc28660e8ba353f2e54927e546812fe17539249ba6d1757d5cc150d3a2a75f41e702bcd239
+AUX 2.1.11-6.3-compat-idmapped-mount-api-changes.patch 95219 BLAKE2B bac74774b10ab4d0a06aa3e0d425c6e76566ff385ed3995a91bda3282ad62d80113df02f638fbfae24042b7bf8387d731554c7689f5c8280c68359cc6a21faf4 SHA512 47516410204e28540a8c29d3cf451fcfb6a7139561a036a46800ad7a402c20c6af7fa269302f8affa7b0e964b26a6b9bcdbd807fa648ed34d07a579ebeef951b
+AUX 2.1.11-6.3-compat-writepage_t-typedef-change.patch 5498 BLAKE2B 70a6a5589e14bd5de689d4d50956f3b2ae6820a36b8d84c872c36ce9042e067cea2022bba291c9711823fb8e6d2e1c3aa1d7935e3f4bf429c15bc7f2499ae23a SHA512 2b55930c10acb50f744362a9dcc29ce0132f55453258e907e5cb85f3205c25fafd4e76e986819650e6b7a8a64f442ba4dce78ff1d2d38e961aed9342bd9a7ddd
+AUX 2.1.11-expose-additional-attributes.patch 41613 BLAKE2B 495e656bffab4e282dd260f9d080d3543ebcb88eed79e3f1bb3dccdd303543dcb921e7cc5b5a3b2357ae7fd2343909e7184540df4b0b76a76530543610f7c5ba SHA512 17eaceeb490e0094b54d7c4e37bf653eb5cea2846316bdda0319a9c10c802084561a48aa7b1d72a4b6b08e09bf6ea688b93daa912a7c42feddad02c11749a399
+AUX 2.1.11-optimize-access-checks.patch 6850 BLAKE2B f4b5f05fd197668b23bcffcab661ab641cc9eedd84e23e39229e630fbc34441b2838f457739759df56e5ac80eb539b311f7e8f3f7abd4601a0ae169a1d464e9a SHA512 2876721ae59e918ced2ac439b32c350f3c6d4b9e23c6a830f81b69b63a05b55e0a15b774b03aeb85ded515825ccf29c783e19d7b52d6e09441ca3ba9e658427e
+AUX 2.1.11-support-idmapped-mount-in-user-ns.patch 69463 BLAKE2B e599d28bab727b800c5f654b1902c0001dd411ee24bbeb04957fce7eeb6d9d1f3e152058dc0d0a430e3422fb7bb4cadaf09460cd48fe1e7a2374954c8cfe66e7 SHA512 b8109a18c012f69be7cd5dfadf6fdf0d349ec5248316414acca92b40065c8d60d08fa8ed0c2214bf40ae0351443abf3897baef48065688fa408528877e2ae759
+AUX 2.1.11-support-idmapped-mount.patch 104745 BLAKE2B bc6611d64e5b296025aab9a42202f44ba6c14a629128dec6b5bc3eb5a244809f1c04a466d118cf5e41d5af8977bf84aca5edbb5930301ded060920796f8a7bb7 SHA512 fcf4380c49cf1066b68942de5ffac0e052a72bea4095af7dd27ab2b36e7629a245050f1c480c1267957b18f3429579f04f570cdd8209fcfd1a72b819a0b4534c
 AUX 2.1.7-ppc64-ieee128-compat.patch 6889 BLAKE2B 9214915ed5b1d149927d46f749903fbb9b4d243d993041b8c6db039dcb7a3968a315f211e010ef9373b721336d45daef264d951e7ea1e0aed01818ebccae9d02 SHA512 4e3edbe396d0c1fdb7b286417b2bf7fe114b392e09158b3f5d3e3d1c4844bf0d59263309883363175dacd63194fd0d6a120ace35c83989d0546dae825bbb91bd
 AUX zfs-kmod-2.1.6-fgrep.patch 2183 BLAKE2B b8c9b58cc6d94e9ddeacbe3c5eca9d040f1c6fba84b814c8c553d5c1e15334765d3190286a4ad2298762bc54f66a8b89ef69d115cfbd5a25d928170a1d46a770 SHA512 f65e7ffa8cfd00da7ad6d1fcbf5d61172c52e55ea2630f719b49750692c1c046e8baed0e04cadc841adbd804cf861ec712b0d07dbbcae7c1c3b475326045ae8c
 DIST zfs-2.1.11.tar.gz 35100716 BLAKE2B 991ac2347bcd452812e247358e2c44a04a88e700d25878b5b95f86939e6114e1205e7afabfd2a1ea9220947876511374d7224aa587d3d66184838d705f71a89a SHA512 335a543644d2dbba919213a28cc5922bf6a118fc19069db84562ce056449a2d6ca4ba827e54f304ab7d9be22260aa9b255134f1b12e2bc98890f757f35e48bd7
 DIST zfs-2.1.11.tar.gz.asc 836 BLAKE2B 0b904d8e1de2dd08a377efc94e32862192d6b9ccb8628af058a71b3ea51f5e483e0cf527906cd222fe9b41b28ca0b30b0efa07d97c480e5546f6e2bed8cbcb01 SHA512 7329e62012ba64288345d8959611de82502ef1da4020e215462fbb2ed209413ec8638d211a31dd6e70be71c998f1da1d8a0d19e5df1f2778782ebb988c94aa41
-EBUILD zfs-kmod-2.1.11.ebuild 5435 BLAKE2B 5cc4835c1cff1a4d78b8710cd21f557b5d7de99a135d1b60e96b5f738755e219c66092b686dc2299f94baa58b84abac4d8cabac02b14b5e2031e867cd2f405fb SHA512 929af77ae88751937a97dbeb53bd3f8ce8161df48b34ab82a96f888642328bd0fa3346ff0646b92c1835048725162e23f6d45d6ebffe012d4b69ba787ddf21d4
+EBUILD zfs-kmod-2.1.11.ebuild 6588 BLAKE2B 59815b0bc24c8f84e3a4362c3df66cc7e08b0e16e7ef7a7fdb098003c5465905cce095df7315b2ad0cadbb7f7a81c4bdbeeea4cd48a400caff1c7fa8e7507551 SHA512 241c3977cb69c6e5266690acc3ec73007a08ea27db44756dedfa20c32ff0f0942e964ad226b8e252eaba3b312f1a73e53500b2f97819d29d0b5e6d9502d8abe0
 EBUILD zfs-kmod-9999.ebuild 6569 BLAKE2B 2bf117650742e0044bf78d4838c0f8b1a2895d4bac31df742be78ce93aa321074840233d825b8844d01a2de0d1923d0f42bacb53109b7c034592babdb5c4dde9 SHA512 f0342de93072653647992b0b1c22c81f026697446b721fb44cde9808beea93651d58323ebdefb783adfc9eb3b2d8ff02fb267fdf6e4109e1b2355e1867ffedba
 MISC metadata.xml 651 BLAKE2B 477c5d768a2eddab7bc0c14d0845801e25bfd9298fe229a132d7ff11a8560988d0230ec5d4b1447df32f58b9754df0cbe989f2a5600cd3fa99124ea4edc45cdd SHA512 d9bf0598c87bcdaab7d81dd5502caf1400f3c8d3834a6770630d85ed365cd4ebb3beeebae72d2d4d49bcdd0aa6cd709aa57cb2af4195dfc04a9c0cef89cfd724
diff --git a/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-idmapped-mount-api-changes.patch b/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-idmapped-mount-api-changes.patch
new file mode 100644
index 0000000..83185f4
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-idmapped-mount-api-changes.patch
@@ -0,0 +1,2573 @@
+From ecd3ef2b3171e7ce507fd0ad653597d00eab0228 Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:56:38 -0500
+Subject: [PATCH] Backport "Support idmapped mount for kernel 6.3"
+
+---
+ config/kernel-acl.m4                         | 34 ++++++--
+ config/kernel-generic_fillattr.m4            | 33 +++++--
+ config/kernel-inode-create.m4                | 41 +++++++--
+ config/kernel-inode-getattr.m4               | 63 ++++++++++----
+ config/kernel-inode-permission.m4            | 35 ++++++--
+ config/kernel-inode-setattr.m4               | 87 +++++++++++++++++++
+ config/kernel-is_owner_or_cap.m4             | 25 +++++-
+ config/kernel-mkdir.m4                       | 55 +++++++++---
+ config/kernel-mknod.m4                       | 34 +++++++-
+ config/kernel-rename.m4                      | 59 ++++++++++---
+ config/kernel-setattr-prepare.m4             | 44 +++++++---
+ config/kernel-symlink.m4                     | 33 +++++--
+ config/kernel-tmpfile.m4                     | 33 +++++--
+ config/kernel-xattr-handler.m4               | 91 +++++++++++++-------
+ config/kernel.m4                             |  6 +-
+ include/os/freebsd/spl/sys/types.h           |  2 +-
+ include/os/freebsd/zfs/sys/zfs_vnops_os.h    | 10 +--
+ include/os/linux/kernel/linux/vfs_compat.h   | 21 ++++-
+ include/os/linux/kernel/linux/xattr_compat.h | 17 +++-
+ include/os/linux/spl/sys/cred.h              | 30 ++++---
+ include/os/linux/spl/sys/types.h             | 15 +++-
+ include/os/linux/zfs/sys/policy.h            |  6 +-
+ include/os/linux/zfs/sys/zfs_vnops_os.h      | 15 ++--
+ include/os/linux/zfs/sys/zpl.h               | 13 ++-
+ include/sys/zfs_acl.h                        | 12 +--
+ module/os/freebsd/zfs/zfs_acl.c              | 10 +--
+ module/os/freebsd/zfs/zfs_vnops_os.c         | 10 +--
+ module/os/linux/spl/spl-cred.c               | 12 +++
+ module/os/linux/zfs/policy.c                 | 13 +--
+ module/os/linux/zfs/zfs_acl.c                | 37 ++++----
+ module/os/linux/zfs/zfs_dir.c                |  4 +-
+ module/os/linux/zfs/zfs_ioctl_os.c           |  4 +
+ module/os/linux/zfs/zfs_vnops_os.c           | 35 ++++----
+ module/os/linux/zfs/zfs_znode.c              |  2 +-
+ module/os/linux/zfs/zpl_ctldir.c             | 61 ++++++++++---
+ module/os/linux/zfs/zpl_file.c               | 10 +--
+ module/os/linux/zfs/zpl_inode.c              | 79 ++++++++++++-----
+ module/os/linux/zfs/zpl_xattr.c              | 25 +++---
+ module/zfs/zfs_replay.c                      | 14 +--
+ module/zfs/zfs_vnops.c                       |  4 +-
+ 40 files changed, 848 insertions(+), 286 deletions(-)
+ create mode 100644 config/kernel-inode-setattr.m4
+
+diff --git a/config/kernel-acl.m4 b/config/kernel-acl.m4
+index 6e92da97d0f..be08c3c6072 100644
+--- a/config/kernel-acl.m4
++++ b/config/kernel-acl.m4
+@@ -236,7 +236,22 @@ dnl #
+ dnl # 6.2 API change,
+ dnl # set_acl() second paramter changed to a struct dentry *
+ dnl #
++dnl # 6.3 API change,
++dnl # set_acl() first parameter changed to struct mnt_idmap *
++dnl #
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_OPERATIONS_SET_ACL], [
++	ZFS_LINUX_TEST_SRC([inode_operations_set_acl_mnt_idmap_dentry], [
++		#include <linux/fs.h>
++
++		int set_acl_fn(struct mnt_idmap *idmap,
++		    struct dentry *dent, struct posix_acl *acl,
++		    int type) { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.set_acl = set_acl_fn,
++		};
++	],[])
+ 	ZFS_LINUX_TEST_SRC([inode_operations_set_acl_userns_dentry], [
+ 		#include <linux/fs.h>
+ 
+@@ -281,17 +296,24 @@ AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_SET_ACL], [
+ 		AC_DEFINE(HAVE_SET_ACL, 1, [iops->set_acl() exists])
+ 		AC_DEFINE(HAVE_SET_ACL_USERNS, 1, [iops->set_acl() takes 4 args])
+ 	],[
+-		ZFS_LINUX_TEST_RESULT([inode_operations_set_acl_userns_dentry], [
++		ZFS_LINUX_TEST_RESULT([inode_operations_set_acl_mnt_idmap_dentry], [
+ 			AC_MSG_RESULT(yes)
+ 			AC_DEFINE(HAVE_SET_ACL, 1, [iops->set_acl() exists])
+-			AC_DEFINE(HAVE_SET_ACL_USERNS_DENTRY_ARG2, 1,
+-			    [iops->set_acl() takes 4 args, arg2 is struct dentry *])
++			AC_DEFINE(HAVE_SET_ACL_IDMAP_DENTRY, 1,
++			    [iops->set_acl() takes 4 args, arg1 is struct mnt_idmap *])
+ 		],[
+-			ZFS_LINUX_TEST_RESULT([inode_operations_set_acl], [
++			ZFS_LINUX_TEST_RESULT([inode_operations_set_acl_userns_dentry], [
+ 				AC_MSG_RESULT(yes)
+-				AC_DEFINE(HAVE_SET_ACL, 1, [iops->set_acl() exists, takes 3 args])
++				AC_DEFINE(HAVE_SET_ACL, 1, [iops->set_acl() exists])
++				AC_DEFINE(HAVE_SET_ACL_USERNS_DENTRY_ARG2, 1,
++				    [iops->set_acl() takes 4 args, arg2 is struct dentry *])
+ 			],[
+-				ZFS_LINUX_REQUIRE_API([i_op->set_acl()], [3.14])
++				ZFS_LINUX_TEST_RESULT([inode_operations_set_acl], [
++					AC_MSG_RESULT(yes)
++					AC_DEFINE(HAVE_SET_ACL, 1, [iops->set_acl() exists, takes 3 args])
++				],[
++					ZFS_LINUX_REQUIRE_API([i_op->set_acl()], [3.14])
++				])
+ 			])
+ 		])
+ 	])
+diff --git a/config/kernel-generic_fillattr.m4 b/config/kernel-generic_fillattr.m4
+index 0acd5d53103..02dee4d4c00 100644
+--- a/config/kernel-generic_fillattr.m4
++++ b/config/kernel-generic_fillattr.m4
+@@ -4,7 +4,10 @@ dnl #
+ dnl # generic_fillattr in linux/fs.h now requires a struct user_namespace*
+ dnl # as the first arg, to support idmapped mounts.
+ dnl #
+-AC_DEFUN([ZFS_AC_KERNEL_SRC_GENERIC_FILLATTR_USERNS], [
++dnl # 6.3 API
++dnl # generic_fillattr() now takes struct mnt_idmap* as the first argument
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_SRC_GENERIC_FILLATTR], [
+ 	ZFS_LINUX_TEST_SRC([generic_fillattr_userns], [
+ 		#include <linux/fs.h>
+ 	],[
+@@ -13,16 +16,32 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_GENERIC_FILLATTR_USERNS], [
+ 		struct kstat *k = NULL;
+ 		generic_fillattr(userns, in, k);
+ 	])
++
++	ZFS_LINUX_TEST_SRC([generic_fillattr_mnt_idmap], [
++		#include <linux/fs.h>
++	],[
++		struct mnt_idmap *idmap = NULL;
++		struct inode *in = NULL;
++		struct kstat *k = NULL;
++		generic_fillattr(idmap, in, k);
++	])
+ ])
+ 
+-AC_DEFUN([ZFS_AC_KERNEL_GENERIC_FILLATTR_USERNS], [
+-	AC_MSG_CHECKING([whether generic_fillattr requires struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([generic_fillattr_userns], [
++AC_DEFUN([ZFS_AC_KERNEL_GENERIC_FILLATTR], [
++	AC_MSG_CHECKING([whether generic_fillattr requires struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([generic_fillattr_mnt_idmap], [
+ 		AC_MSG_RESULT([yes])
+-		AC_DEFINE(HAVE_GENERIC_FILLATTR_USERNS, 1,
+-		    [generic_fillattr requires struct user_namespace*])
++		AC_DEFINE(HAVE_GENERIC_FILLATTR_IDMAP, 1,
++		    [generic_fillattr requires struct mnt_idmap*])
+ 	],[
+-		AC_MSG_RESULT([no])
++		AC_MSG_CHECKING([whether generic_fillattr requires struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([generic_fillattr_userns], [
++			AC_MSG_RESULT([yes])
++			AC_DEFINE(HAVE_GENERIC_FILLATTR_USERNS, 1,
++			    [generic_fillattr requires struct user_namespace*])
++		],[
++			AC_MSG_RESULT([no])
++		])
+ 	])
+ ])
+ 
+diff --git a/config/kernel-inode-create.m4 b/config/kernel-inode-create.m4
+index a6ea11fb61b..9e9e4318097 100644
+--- a/config/kernel-inode-create.m4
++++ b/config/kernel-inode-create.m4
+@@ -1,4 +1,22 @@
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_CREATE], [
++	dnl #
++	dnl # 6.3 API change
++	dnl # The first arg is changed to struct mnt_idmap *
++	dnl #
++	ZFS_LINUX_TEST_SRC([create_mnt_idmap], [
++		#include <linux/fs.h>
++		#include <linux/sched.h>
++
++		int inode_create(struct mnt_idmap *idmap,
++		    struct inode *inode ,struct dentry *dentry,
++		    umode_t umode, bool flag) { return 0; }
++
++		static const struct inode_operations
++			iops __attribute__ ((unused)) = {
++			.create         = inode_create,
++		};
++	],[])
++
+ 	dnl #
+ 	dnl # 5.12 API change that added the struct user_namespace* arg
+ 	dnl # to the front of this function type's arg list.
+@@ -35,19 +53,28 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_CREATE], [
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_CREATE], [
+-	AC_MSG_CHECKING([whether iops->create() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([create_userns], [
++	AC_MSG_CHECKING([whether iops->create() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([create_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_CREATE_USERNS, 1,
+-		   [iops->create() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_CREATE_IDMAP, 1,
++		   [iops->create() takes struct mnt_idmap*])
+ 	],[
+ 		AC_MSG_RESULT(no)
+ 
+-		AC_MSG_CHECKING([whether iops->create() passes flags])
+-		ZFS_LINUX_TEST_RESULT([create_flags], [
++		AC_MSG_CHECKING([whether iops->create() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([create_userns], [
+ 			AC_MSG_RESULT(yes)
++			AC_DEFINE(HAVE_IOPS_CREATE_USERNS, 1,
++			   [iops->create() takes struct user_namespace*])
+ 		],[
+-			ZFS_LINUX_TEST_ERROR([iops->create()])
++			AC_MSG_RESULT(no)
++
++			AC_MSG_CHECKING([whether iops->create() passes flags])
++			ZFS_LINUX_TEST_RESULT([create_flags], [
++				AC_MSG_RESULT(yes)
++			],[
++				ZFS_LINUX_TEST_ERROR([iops->create()])
++			])
+ 		])
+ 	])
+ ])
+diff --git a/config/kernel-inode-getattr.m4 b/config/kernel-inode-getattr.m4
+index f62e82f5230..c8bfb07862a 100644
+--- a/config/kernel-inode-getattr.m4
++++ b/config/kernel-inode-getattr.m4
+@@ -1,4 +1,24 @@
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_GETATTR], [
++	dnl #
++	dnl # Linux 6.3 API
++	dnl # The first arg of getattr I/O operations handler type
++	dnl # is changed to struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([inode_operations_getattr_mnt_idmap], [
++		#include <linux/fs.h>
++
++		int test_getattr(
++		    struct mnt_idmap *idmap,
++		    const struct path *p, struct kstat *k,
++		    u32 request_mask, unsigned int query_flags)
++		    { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.getattr = test_getattr,
++		};
++	],[])
++
+ 	dnl #
+ 	dnl # Linux 5.12 API
+ 	dnl # The getattr I/O operations handler type was extended to require
+@@ -55,37 +75,48 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_GETATTR], [
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_INODE_GETATTR], [
+ 	dnl #
+-	dnl # Kernel 5.12 test
++	dnl # Kernel 6.3 test
+ 	dnl #
+-	AC_MSG_CHECKING([whether iops->getattr() takes user_namespace])
+-	ZFS_LINUX_TEST_RESULT([inode_operations_getattr_userns], [
++	AC_MSG_CHECKING([whether iops->getattr() takes mnt_idmap])
++	ZFS_LINUX_TEST_RESULT([inode_operations_getattr_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_USERNS_IOPS_GETATTR, 1,
+-		    [iops->getattr() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IDMAP_IOPS_GETATTR, 1,
++		    [iops->getattr() takes struct mnt_idmap*])
+ 	],[
+ 		AC_MSG_RESULT(no)
+-
+ 		dnl #
+-		dnl # Kernel 4.11 test
++		dnl # Kernel 5.12 test
+ 		dnl #
+-		AC_MSG_CHECKING([whether iops->getattr() takes a path])
+-		ZFS_LINUX_TEST_RESULT([inode_operations_getattr_path], [
++		AC_MSG_CHECKING([whether iops->getattr() takes user_namespace])
++		ZFS_LINUX_TEST_RESULT([inode_operations_getattr_userns], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_PATH_IOPS_GETATTR, 1,
+-				[iops->getattr() takes a path])
++			AC_DEFINE(HAVE_USERNS_IOPS_GETATTR, 1,
++			    [iops->getattr() takes struct user_namespace*])
+ 		],[
+ 			AC_MSG_RESULT(no)
+ 
+ 			dnl #
+-			dnl # Kernel < 4.11 test
++			dnl # Kernel 4.11 test
+ 			dnl #
+-			AC_MSG_CHECKING([whether iops->getattr() takes a vfsmount])
+-			ZFS_LINUX_TEST_RESULT([inode_operations_getattr_vfsmount], [
++			AC_MSG_CHECKING([whether iops->getattr() takes a path])
++			ZFS_LINUX_TEST_RESULT([inode_operations_getattr_path], [
+ 				AC_MSG_RESULT(yes)
+-				AC_DEFINE(HAVE_VFSMOUNT_IOPS_GETATTR, 1,
+-					[iops->getattr() takes a vfsmount])
++				AC_DEFINE(HAVE_PATH_IOPS_GETATTR, 1,
++					[iops->getattr() takes a path])
+ 			],[
+ 				AC_MSG_RESULT(no)
++
++				dnl #
++				dnl # Kernel < 4.11 test
++				dnl #
++				AC_MSG_CHECKING([whether iops->getattr() takes a vfsmount])
++				ZFS_LINUX_TEST_RESULT([inode_operations_getattr_vfsmount], [
++					AC_MSG_RESULT(yes)
++					AC_DEFINE(HAVE_VFSMOUNT_IOPS_GETATTR, 1,
++						[iops->getattr() takes a vfsmount])
++				],[
++					AC_MSG_RESULT(no)
++				])
+ 			])
+ 		])
+ 	])
+diff --git a/config/kernel-inode-permission.m4 b/config/kernel-inode-permission.m4
+index ba9ff5d43d4..01d23635b0c 100644
+--- a/config/kernel-inode-permission.m4
++++ b/config/kernel-inode-permission.m4
+@@ -1,4 +1,22 @@
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_PERMISSION], [
++	dnl #
++	dnl # 6.3 API change
++	dnl # iops->permission() now takes struct mnt_idmap*
++	dnl # as its first arg
++	dnl #
++	ZFS_LINUX_TEST_SRC([permission_mnt_idmap], [
++		#include <linux/fs.h>
++		#include <linux/sched.h>
++
++		int inode_permission(struct mnt_idmap *idmap,
++		    struct inode *inode, int mask) { return 0; }
++
++		static const struct inode_operations
++			iops __attribute__ ((unused)) = {
++			.permission             = inode_permission,
++		};
++	],[])
++
+ 	dnl #
+ 	dnl # 5.12 API change that added the struct user_namespace* arg
+ 	dnl # to the front of this function type's arg list.
+@@ -18,12 +36,19 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_PERMISSION], [
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_PERMISSION], [
+-	AC_MSG_CHECKING([whether iops->permission() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([permission_userns], [
++	AC_MSG_CHECKING([whether iops->permission() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([permission_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_PERMISSION_USERNS, 1,
+-		   [iops->permission() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_PERMISSION_IDMAP, 1,
++		   [iops->permission() takes struct mnt_idmap*])
+ 	],[
+-		AC_MSG_RESULT(no)
++		AC_MSG_CHECKING([whether iops->permission() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([permission_userns], [
++			AC_MSG_RESULT(yes)
++			AC_DEFINE(HAVE_IOPS_PERMISSION_USERNS, 1,
++			   [iops->permission() takes struct user_namespace*])
++		],[
++			AC_MSG_RESULT(no)
++		])
+ 	])
+ ])
+diff --git a/config/kernel-inode-setattr.m4 b/config/kernel-inode-setattr.m4
+new file mode 100644
+index 00000000000..45755b4eb27
+--- /dev/null
++++ b/config/kernel-inode-setattr.m4
+@@ -0,0 +1,87 @@
++AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_SETATTR], [
++	dnl #
++	dnl # Linux 6.3 API
++	dnl # The first arg of setattr I/O operations handler type
++	dnl # is changed to struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([inode_operations_setattr_mnt_idmap], [
++		#include <linux/fs.h>
++
++		int test_setattr(
++		    struct mnt_idmap *idmap,
++		    struct dentry *de, struct iattr *ia)
++		    { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.setattr = test_setattr,
++		};
++	],[])
++
++	dnl #
++	dnl # Linux 5.12 API
++	dnl # The setattr I/O operations handler type was extended to require
++	dnl # a struct user_namespace* as its first arg, to support idmapped
++	dnl # mounts.
++	dnl #
++	ZFS_LINUX_TEST_SRC([inode_operations_setattr_userns], [
++		#include <linux/fs.h>
++
++		int test_setattr(
++		    struct user_namespace *userns,
++		    struct dentry *de, struct iattr *ia)
++		    { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.setattr = test_setattr,
++		};
++	],[])
++
++	ZFS_LINUX_TEST_SRC([inode_operations_setattr], [
++		#include <linux/fs.h>
++
++		int test_setattr(
++		    struct dentry *de, struct iattr *ia)
++		    { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.setattr = test_setattr,
++		};
++	],[])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_INODE_SETATTR], [
++	dnl #
++	dnl # Kernel 6.3 test
++	dnl #
++	AC_MSG_CHECKING([whether iops->setattr() takes mnt_idmap])
++	ZFS_LINUX_TEST_RESULT([inode_operations_setattr_mnt_idmap], [
++		AC_MSG_RESULT(yes)
++		AC_DEFINE(HAVE_IDMAP_IOPS_SETATTR, 1,
++		    [iops->setattr() takes struct mnt_idmap*])
++	],[
++		AC_MSG_RESULT(no)
++		dnl #
++		dnl # Kernel 5.12 test
++		dnl #
++		AC_MSG_CHECKING([whether iops->setattr() takes user_namespace])
++		ZFS_LINUX_TEST_RESULT([inode_operations_setattr_userns], [
++			AC_MSG_RESULT(yes)
++			AC_DEFINE(HAVE_USERNS_IOPS_SETATTR, 1,
++			    [iops->setattr() takes struct user_namespace*])
++		],[
++			AC_MSG_RESULT(no)
++
++			AC_MSG_CHECKING([whether iops->setattr() exists])
++			ZFS_LINUX_TEST_RESULT([inode_operations_setattr], [
++				AC_MSG_RESULT(yes)
++				AC_DEFINE(HAVE_IOPS_SETATTR, 1,
++					[iops->setattr() exists])
++			],[
++				AC_MSG_RESULT(no)
++			])
++		])
++	])
++])
+diff --git a/config/kernel-is_owner_or_cap.m4 b/config/kernel-is_owner_or_cap.m4
+index a90cf3da641..4e9c002b77f 100644
+--- a/config/kernel-is_owner_or_cap.m4
++++ b/config/kernel-is_owner_or_cap.m4
+@@ -16,12 +16,20 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_OWNER_OR_CAPABLE], [
+ 		(void) inode_owner_or_capable(ip);
+ 	])
+ 
+-	ZFS_LINUX_TEST_SRC([inode_owner_or_capable_idmapped], [
++	ZFS_LINUX_TEST_SRC([inode_owner_or_capable_userns], [
+ 		#include <linux/fs.h>
+ 	],[
+ 		struct inode *ip = NULL;
+ 		(void) inode_owner_or_capable(&init_user_ns, ip);
+ 	])
++
++	ZFS_LINUX_TEST_SRC([inode_owner_or_capable_mnt_idmap], [
++		#include <linux/fs.h>
++		#include <linux/mnt_idmapping.h>
++	],[
++		struct inode *ip = NULL;
++		(void) inode_owner_or_capable(&nop_mnt_idmap, ip);
++	])
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_INODE_OWNER_OR_CAPABLE], [
+@@ -35,12 +43,21 @@ AC_DEFUN([ZFS_AC_KERNEL_INODE_OWNER_OR_CAPABLE], [
+ 
+ 		AC_MSG_CHECKING(
+ 		    [whether inode_owner_or_capable() takes user_ns])
+-		ZFS_LINUX_TEST_RESULT([inode_owner_or_capable_idmapped], [
++		ZFS_LINUX_TEST_RESULT([inode_owner_or_capable_userns], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_INODE_OWNER_OR_CAPABLE_IDMAPPED, 1,
++			AC_DEFINE(HAVE_INODE_OWNER_OR_CAPABLE_USERNS, 1,
+ 			    [inode_owner_or_capable() takes user_ns])
+ 		],[
+-			ZFS_LINUX_TEST_ERROR([capability])
++			AC_MSG_RESULT(no)
++			AC_MSG_CHECKING(
++			    [whether inode_owner_or_capable() takes mnt_idmap])
++			ZFS_LINUX_TEST_RESULT([inode_owner_or_capable_mnt_idmap], [
++				AC_MSG_RESULT(yes)
++				AC_DEFINE(HAVE_INODE_OWNER_OR_CAPABLE_IDMAP, 1,
++				    [inode_owner_or_capable() takes mnt_idmap])
++			], [
++				ZFS_LINUX_TEST_ERROR([capability])
++			])
+ 		])
+ 	])
+ ])
+diff --git a/config/kernel-mkdir.m4 b/config/kernel-mkdir.m4
+index 6667ed04fa4..7407a791b84 100644
+--- a/config/kernel-mkdir.m4
++++ b/config/kernel-mkdir.m4
+@@ -2,6 +2,22 @@ dnl #
+ dnl # Supported mkdir() interfaces checked newest to oldest.
+ dnl #
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_MKDIR], [
++	dnl #
++	dnl # 6.3 API change
++	dnl # mkdir() takes struct mnt_idmap * as the first arg
++	dnl #
++	ZFS_LINUX_TEST_SRC([mkdir_mnt_idmap], [
++		#include <linux/fs.h>
++
++		int mkdir(struct mnt_idmap *idmap,
++			struct inode *inode, struct dentry *dentry,
++			umode_t umode) { return 0; }
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.mkdir = mkdir,
++		};
++	],[])
++
+ 	dnl #
+ 	dnl # 5.12 API change
+ 	dnl # The struct user_namespace arg was added as the first argument to
+@@ -43,25 +59,36 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_MKDIR], [
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_MKDIR], [
+ 	dnl #
+-	dnl # 5.12 API change
+-	dnl # The struct user_namespace arg was added as the first argument to
+-	dnl # mkdir() of the iops structure.
++	dnl # 6.3 API change
++	dnl # mkdir() takes struct mnt_idmap * as the first arg
+ 	dnl #
+-	AC_MSG_CHECKING([whether iops->mkdir() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([mkdir_user_namespace], [
++	AC_MSG_CHECKING([whether iops->mkdir() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([mkdir_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_MKDIR_USERNS, 1,
+-		    [iops->mkdir() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_MKDIR_IDMAP, 1,
++		    [iops->mkdir() takes struct mnt_idmap*])
+ 	],[
+-		AC_MSG_RESULT(no)
+-
+-		AC_MSG_CHECKING([whether iops->mkdir() takes umode_t])
+-		ZFS_LINUX_TEST_RESULT([inode_operations_mkdir], [
++		dnl #
++		dnl # 5.12 API change
++		dnl # The struct user_namespace arg was added as the first argument to
++		dnl # mkdir() of the iops structure.
++		dnl #
++		AC_MSG_CHECKING([whether iops->mkdir() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([mkdir_user_namespace], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_MKDIR_UMODE_T, 1,
+-			    [iops->mkdir() takes umode_t])
++			AC_DEFINE(HAVE_IOPS_MKDIR_USERNS, 1,
++			    [iops->mkdir() takes struct user_namespace*])
+ 		],[
+-			ZFS_LINUX_TEST_ERROR([mkdir()])
++			AC_MSG_RESULT(no)
++
++			AC_MSG_CHECKING([whether iops->mkdir() takes umode_t])
++			ZFS_LINUX_TEST_RESULT([inode_operations_mkdir], [
++				AC_MSG_RESULT(yes)
++				AC_DEFINE(HAVE_MKDIR_UMODE_T, 1,
++				    [iops->mkdir() takes umode_t])
++			],[
++				ZFS_LINUX_TEST_ERROR([mkdir()])
++			])
+ 		])
+ 	])
+ ])
+diff --git a/config/kernel-mknod.m4 b/config/kernel-mknod.m4
+index ffe45106003..1494ec1ae4d 100644
+--- a/config/kernel-mknod.m4
++++ b/config/kernel-mknod.m4
+@@ -1,4 +1,22 @@
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_MKNOD], [
++	dnl #
++	dnl # 6.3 API change
++	dnl # The first arg is now struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([mknod_mnt_idmap], [
++		#include <linux/fs.h>
++		#include <linux/sched.h>
++
++		int tmp_mknod(struct mnt_idmap *idmap,
++		    struct inode *inode ,struct dentry *dentry,
++		    umode_t u, dev_t d) { return 0; }
++
++		static const struct inode_operations
++			iops __attribute__ ((unused)) = {
++			.mknod          = tmp_mknod,
++		};
++	],[])
++
+ 	dnl #
+ 	dnl # 5.12 API change that added the struct user_namespace* arg
+ 	dnl # to the front of this function type's arg list.
+@@ -19,12 +37,20 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_MKNOD], [
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_MKNOD], [
+-	AC_MSG_CHECKING([whether iops->mknod() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([mknod_userns], [
++	AC_MSG_CHECKING([whether iops->mknod() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([mknod_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_MKNOD_USERNS, 1,
+-		    [iops->mknod() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_MKNOD_IDMAP, 1,
++		    [iops->mknod() takes struct mnt_idmap*])
+ 	],[
+ 		AC_MSG_RESULT(no)
++		AC_MSG_CHECKING([whether iops->mknod() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([mknod_userns], [
++			AC_MSG_RESULT(yes)
++			AC_DEFINE(HAVE_IOPS_MKNOD_USERNS, 1,
++			    [iops->mknod() takes struct user_namespace*])
++		],[
++			AC_MSG_RESULT(no)
++		])
+ 	])
+ ])
+diff --git a/config/kernel-rename.m4 b/config/kernel-rename.m4
+index 302db43f574..06a9791bc8d 100644
+--- a/config/kernel-rename.m4
++++ b/config/kernel-rename.m4
+@@ -33,24 +33,63 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_RENAME], [
+ 			.rename = rename_fn,
+ 		};
+ 	],[])
++
++	dnl #
++	dnl # 6.3 API change - the first arg is now struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([inode_operations_rename_mnt_idmap], [
++		#include <linux/fs.h>
++		int rename_fn(struct mnt_idmap *idmap, struct inode *sip,
++			struct dentry *sdp, struct inode *tip, struct dentry *tdp,
++			unsigned int flags) { return 0; }
++
++		static const struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.rename = rename_fn,
++		};
++	],[])
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_RENAME], [
+-	AC_MSG_CHECKING([whether iops->rename() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([inode_operations_rename_userns], [
++	AC_MSG_CHECKING([whether iops->rename() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([inode_operations_rename_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_RENAME_USERNS, 1,
+-		    [iops->rename() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_RENAME_IDMAP, 1,
++		    [iops->rename() takes struct mnt_idmap*])
+ 	],[
+-		AC_MSG_RESULT(no)
+-
+-		AC_MSG_CHECKING([whether iop->rename() wants flags])
+-		ZFS_LINUX_TEST_RESULT([inode_operations_rename_flags], [
++		AC_MSG_CHECKING([whether iops->rename() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([inode_operations_rename_userns], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_RENAME_WANTS_FLAGS, 1,
+-				[iops->rename() wants flags])
++			AC_DEFINE(HAVE_IOPS_RENAME_USERNS, 1,
++			    [iops->rename() takes struct user_namespace*])
+ 		],[
+ 			AC_MSG_RESULT(no)
++
++			AC_MSG_CHECKING([whether iops->rename2() exists])
++			ZFS_LINUX_TEST_RESULT([inode_operations_rename2], [
++				AC_MSG_RESULT(yes)
++				AC_DEFINE(HAVE_RENAME2, 1, [iops->rename2() exists])
++			],[
++				AC_MSG_RESULT(no)
++
++				AC_MSG_CHECKING([whether iops->rename() wants flags])
++				ZFS_LINUX_TEST_RESULT([inode_operations_rename_flags], [
++					AC_MSG_RESULT(yes)
++					AC_DEFINE(HAVE_RENAME_WANTS_FLAGS, 1,
++						[iops->rename() wants flags])
++				],[
++					AC_MSG_RESULT(no)
++
++					AC_MSG_CHECKING([whether struct inode_operations_wrapper takes .rename2()])
++					ZFS_LINUX_TEST_RESULT([dir_inode_operations_wrapper_rename2], [
++						AC_MSG_RESULT(yes)
++						AC_DEFINE(HAVE_RENAME2_OPERATIONS_WRAPPER, 1,
++							[struct inode_operations_wrapper takes .rename2()])
++					],[
++						AC_MSG_RESULT(no)
++					])
++				])
++			])
+ 		])
+ 	])
+ ])
+diff --git a/config/kernel-setattr-prepare.m4 b/config/kernel-setattr-prepare.m4
+index 24245aa5344..e02d6263e9c 100644
+--- a/config/kernel-setattr-prepare.m4
++++ b/config/kernel-setattr-prepare.m4
+@@ -27,26 +27,48 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_SETATTR_PREPARE], [
+ 		int error __attribute__ ((unused)) =
+ 			setattr_prepare(userns, dentry, attr);
+ 	])
++
++	dnl #
++	dnl # 6.3 API change
++	dnl # The first arg of setattr_prepare() is changed to struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([setattr_prepare_mnt_idmap], [
++		#include <linux/fs.h>
++	], [
++		struct dentry *dentry = NULL;
++		struct iattr *attr = NULL;
++		struct mnt_idmap *idmap = NULL;
++		int error __attribute__ ((unused)) =
++			setattr_prepare(idmap, dentry, attr);
++	])
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_SETATTR_PREPARE], [
+-	AC_MSG_CHECKING([whether setattr_prepare() is available and accepts struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT_SYMBOL([setattr_prepare_userns],
++	AC_MSG_CHECKING([whether setattr_prepare() is available and accepts struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT_SYMBOL([setattr_prepare_mnt_idmap],
+ 	    [setattr_prepare], [fs/attr.c], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_SETATTR_PREPARE_USERNS, 1,
+-		    [setattr_prepare() accepts user_namespace])
++		AC_DEFINE(HAVE_SETATTR_PREPARE_IDMAP, 1,
++		    [setattr_prepare() accepts mnt_idmap])
+ 	], [
+-		AC_MSG_RESULT(no)
+-
+-		AC_MSG_CHECKING([whether setattr_prepare() is available, doesn't accept user_namespace])
+-		ZFS_LINUX_TEST_RESULT_SYMBOL([setattr_prepare],
+-			[setattr_prepare], [fs/attr.c], [
++		AC_MSG_CHECKING([whether setattr_prepare() is available and accepts struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT_SYMBOL([setattr_prepare_userns],
++		    [setattr_prepare], [fs/attr.c], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_SETATTR_PREPARE_NO_USERNS, 1,
+-				[setattr_prepare() is available, doesn't accept user_namespace])
++			AC_DEFINE(HAVE_SETATTR_PREPARE_USERNS, 1,
++			    [setattr_prepare() accepts user_namespace])
+ 		], [
+ 			AC_MSG_RESULT(no)
++
++			AC_MSG_CHECKING([whether setattr_prepare() is available, doesn't accept user_namespace])
++			ZFS_LINUX_TEST_RESULT_SYMBOL([setattr_prepare],
++				[setattr_prepare], [fs/attr.c], [
++				AC_MSG_RESULT(yes)
++				AC_DEFINE(HAVE_SETATTR_PREPARE_NO_USERNS, 1,
++					[setattr_prepare() is available, doesn't accept user_namespace])
++			], [
++				AC_MSG_RESULT(no)
++			])
+ 		])
+ 	])
+ ])
+diff --git a/config/kernel-symlink.m4 b/config/kernel-symlink.m4
+index d90366d04b7..a0333ed66a7 100644
+--- a/config/kernel-symlink.m4
++++ b/config/kernel-symlink.m4
+@@ -1,4 +1,20 @@
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_SYMLINK], [
++	dnl #
++	dnl # 6.3 API change that changed the first arg
++	dnl # to struct mnt_idmap*
++	dnl #
++	ZFS_LINUX_TEST_SRC([symlink_mnt_idmap], [
++		#include <linux/fs.h>
++		#include <linux/sched.h>
++		int tmp_symlink(struct mnt_idmap *idmap,
++		    struct inode *inode ,struct dentry *dentry,
++		    const char *path) { return 0; }
++
++		static const struct inode_operations
++			iops __attribute__ ((unused)) = {
++			.symlink                = tmp_symlink,
++		};
++	],[])
+ 	dnl #
+ 	dnl # 5.12 API change that added the struct user_namespace* arg
+ 	dnl # to the front of this function type's arg list.
+@@ -19,12 +35,19 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_SYMLINK], [
+ ])
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_SYMLINK], [
+-	AC_MSG_CHECKING([whether iops->symlink() takes struct user_namespace*])
+-	ZFS_LINUX_TEST_RESULT([symlink_userns], [
++	AC_MSG_CHECKING([whether iops->symlink() takes struct mnt_idmap*])
++	ZFS_LINUX_TEST_RESULT([symlink_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_IOPS_SYMLINK_USERNS, 1,
+-		    [iops->symlink() takes struct user_namespace*])
++		AC_DEFINE(HAVE_IOPS_SYMLINK_IDMAP, 1,
++		    [iops->symlink() takes struct mnt_idmap*])
+ 	],[
+-		AC_MSG_RESULT(no)
++		AC_MSG_CHECKING([whether iops->symlink() takes struct user_namespace*])
++		ZFS_LINUX_TEST_RESULT([symlink_userns], [
++			AC_MSG_RESULT(yes)
++			AC_DEFINE(HAVE_IOPS_SYMLINK_USERNS, 1,
++			    [iops->symlink() takes struct user_namespace*])
++		],[
++			AC_MSG_RESULT(no)
++		])
+ 	])
+ ])
+diff --git a/config/kernel-tmpfile.m4 b/config/kernel-tmpfile.m4
+index 0e1deb3612f..cc18b8f65a8 100644
+--- a/config/kernel-tmpfile.m4
++++ b/config/kernel-tmpfile.m4
+@@ -4,6 +4,19 @@ dnl # Add support for i_op->tmpfile
+ dnl #
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_TMPFILE], [
+ 	dnl #
++	dnl # 6.3 API change
++	dnl # The first arg is now struct mnt_idmap * 
++	dnl #
++	ZFS_LINUX_TEST_SRC([inode_operations_tmpfile_mnt_idmap], [
++		#include <linux/fs.h>
++		int tmpfile(struct mnt_idmap *idmap,
++		    struct inode *inode, struct file *file,
++		    umode_t mode) { return 0; }
++		static struct inode_operations
++		    iops __attribute__ ((unused)) = {
++			.tmpfile = tmpfile,
++		};
++	],[])
+ 	dnl # 6.1 API change
+ 	dnl # use struct file instead of struct dentry
+ 	dnl #
+@@ -44,23 +57,29 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_TMPFILE], [
+ 
+ AC_DEFUN([ZFS_AC_KERNEL_TMPFILE], [
+ 	AC_MSG_CHECKING([whether i_op->tmpfile() exists])
+-	ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile], [
++	ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+ 		AC_DEFINE(HAVE_TMPFILE, 1, [i_op->tmpfile() exists])
+-		AC_DEFINE(HAVE_TMPFILE_USERNS, 1, [i_op->tmpfile() has userns])
+-	],[
+-		ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile_dentry_userns], [
++		AC_DEFINE(HAVE_TMPFILE_IDMAP, 1, [i_op->tmpfile() has mnt_idmap])
++	], [
++		ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile], [
+ 			AC_MSG_RESULT(yes)
+ 			AC_DEFINE(HAVE_TMPFILE, 1, [i_op->tmpfile() exists])
+ 			AC_DEFINE(HAVE_TMPFILE_USERNS, 1, [i_op->tmpfile() has userns])
+-			AC_DEFINE(HAVE_TMPFILE_DENTRY, 1, [i_op->tmpfile() uses old dentry signature])
+ 		],[
+-			ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile_dentry], [
++			ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile_dentry_userns], [
+ 				AC_MSG_RESULT(yes)
+ 				AC_DEFINE(HAVE_TMPFILE, 1, [i_op->tmpfile() exists])
++				AC_DEFINE(HAVE_TMPFILE_USERNS, 1, [i_op->tmpfile() has userns])
+ 				AC_DEFINE(HAVE_TMPFILE_DENTRY, 1, [i_op->tmpfile() uses old dentry signature])
+ 			],[
+-				ZFS_LINUX_REQUIRE_API([i_op->tmpfile()], [3.11])
++				ZFS_LINUX_TEST_RESULT([inode_operations_tmpfile_dentry], [
++					AC_MSG_RESULT(yes)
++					AC_DEFINE(HAVE_TMPFILE, 1, [i_op->tmpfile() exists])
++					AC_DEFINE(HAVE_TMPFILE_DENTRY, 1, [i_op->tmpfile() uses old dentry signature])
++				],[
++					ZFS_LINUX_REQUIRE_API([i_op->tmpfile()], [3.11])
++				])
+ 			])
+ 		])
+ 	])
+diff --git a/config/kernel-xattr-handler.m4 b/config/kernel-xattr-handler.m4
+index b6cbfa15500..6b8a08dbcc8 100644
+--- a/config/kernel-xattr-handler.m4
++++ b/config/kernel-xattr-handler.m4
+@@ -179,6 +179,21 @@ dnl #
+ dnl # Supported xattr handler set() interfaces checked newest to oldest.
+ dnl #
+ AC_DEFUN([ZFS_AC_KERNEL_SRC_XATTR_HANDLER_SET], [
++	ZFS_LINUX_TEST_SRC([xattr_handler_set_mnt_idmap], [
++		#include <linux/xattr.h>
++
++		int set(const struct xattr_handler *handler,
++			struct mnt_idmap *idmap,
++			struct dentry *dentry, struct inode *inode,
++			const char *name, const void *buffer,
++			size_t size, int flags)
++			{ return 0; }
++		static const struct xattr_handler
++			xops __attribute__ ((unused)) = {
++			.set = set,
++		};
++	],[])
++
+ 	ZFS_LINUX_TEST_SRC([xattr_handler_set_userns], [
+ 		#include <linux/xattr.h>
+ 
+@@ -240,53 +255,63 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
+ 	dnl # The xattr_handler->set() callback was changed to 8 arguments, and
+ 	dnl # struct user_namespace* was inserted as arg #2
+ 	dnl #
+-	AC_MSG_CHECKING([whether xattr_handler->set() wants dentry, inode, and user_namespace])
+-	ZFS_LINUX_TEST_RESULT([xattr_handler_set_userns], [
++	dnl # 6.3 API change,
++	dnl # The xattr_handler->set() callback 2nd arg is now struct mnt_idmap *
++	dnl #
++	AC_MSG_CHECKING([whether xattr_handler->set() wants dentry, inode, and mnt_idmap])
++	ZFS_LINUX_TEST_RESULT([xattr_handler_set_mnt_idmap], [
+ 		AC_MSG_RESULT(yes)
+-		AC_DEFINE(HAVE_XATTR_SET_USERNS, 1,
+-		    [xattr_handler->set() takes user_namespace])
+-	],[
+-		dnl #
+-		dnl # 4.7 API change,
+-		dnl # The xattr_handler->set() callback was changed to take both
+-		dnl # dentry and inode.
+-		dnl #
+-		AC_MSG_RESULT(no)
+-		AC_MSG_CHECKING([whether xattr_handler->set() wants dentry and inode])
+-		ZFS_LINUX_TEST_RESULT([xattr_handler_set_dentry_inode], [
++		AC_DEFINE(HAVE_XATTR_SET_IDMAP, 1,
++		    [xattr_handler->set() takes mnt_idmap])
++	], [
++		AC_MSG_CHECKING([whether xattr_handler->set() wants dentry, inode, and user_namespace])
++		ZFS_LINUX_TEST_RESULT([xattr_handler_set_userns], [
+ 			AC_MSG_RESULT(yes)
+-			AC_DEFINE(HAVE_XATTR_SET_DENTRY_INODE, 1,
+-			    [xattr_handler->set() wants both dentry and inode])
++			AC_DEFINE(HAVE_XATTR_SET_USERNS, 1,
++			    [xattr_handler->set() takes user_namespace])
+ 		],[
+ 			dnl #
+-			dnl # 4.4 API change,
+-			dnl # The xattr_handler->set() callback was changed to take a
+-			dnl # xattr_handler, and handler_flags argument was removed and
+-			dnl # should be accessed by handler->flags.
++			dnl # 4.7 API change,
++			dnl # The xattr_handler->set() callback was changed to take both
++			dnl # dentry and inode.
+ 			dnl #
+ 			AC_MSG_RESULT(no)
+-			AC_MSG_CHECKING(
+-			    [whether xattr_handler->set() wants xattr_handler])
+-			ZFS_LINUX_TEST_RESULT([xattr_handler_set_xattr_handler], [
++			AC_MSG_CHECKING([whether xattr_handler->set() wants dentry and inode])
++			ZFS_LINUX_TEST_RESULT([xattr_handler_set_dentry_inode], [
+ 				AC_MSG_RESULT(yes)
+-				AC_DEFINE(HAVE_XATTR_SET_HANDLER, 1,
+-				    [xattr_handler->set() wants xattr_handler])
++				AC_DEFINE(HAVE_XATTR_SET_DENTRY_INODE, 1,
++				    [xattr_handler->set() wants both dentry and inode])
+ 			],[
+ 				dnl #
+-				dnl # 2.6.33 API change,
+-				dnl # The xattr_handler->set() callback was changed
+-				dnl # to take a dentry instead of an inode, and a
+-				dnl # handler_flags argument was added.
++				dnl # 4.4 API change,
++				dnl # The xattr_handler->set() callback was changed to take a
++				dnl # xattr_handler, and handler_flags argument was removed and
++				dnl # should be accessed by handler->flags.
+ 				dnl #
+ 				AC_MSG_RESULT(no)
+ 				AC_MSG_CHECKING(
+-				    [whether xattr_handler->set() wants dentry])
+-				ZFS_LINUX_TEST_RESULT([xattr_handler_set_dentry], [
++				    [whether xattr_handler->set() wants xattr_handler])
++				ZFS_LINUX_TEST_RESULT([xattr_handler_set_xattr_handler], [
+ 					AC_MSG_RESULT(yes)
+-					AC_DEFINE(HAVE_XATTR_SET_DENTRY, 1,
+-					    [xattr_handler->set() wants dentry])
++					AC_DEFINE(HAVE_XATTR_SET_HANDLER, 1,
++					    [xattr_handler->set() wants xattr_handler])
+ 				],[
+-					ZFS_LINUX_TEST_ERROR([xattr set()])
++					dnl #
++					dnl # 2.6.33 API change,
++					dnl # The xattr_handler->set() callback was changed
++					dnl # to take a dentry instead of an inode, and a
++					dnl # handler_flags argument was added.
++					dnl #
++					AC_MSG_RESULT(no)
++					AC_MSG_CHECKING(
++					    [whether xattr_handler->set() wants dentry])
++					ZFS_LINUX_TEST_RESULT([xattr_handler_set_dentry], [
++						AC_MSG_RESULT(yes)
++						AC_DEFINE(HAVE_XATTR_SET_DENTRY, 1,
++						    [xattr_handler->set() wants dentry])
++					],[
++						ZFS_LINUX_TEST_ERROR([xattr set()])
++					])
+ 				])
+ 			])
+ 		])
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index 1998b831e96..b376a151694 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -69,6 +69,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_INODE_OWNER_OR_CAPABLE
+ 	ZFS_AC_KERNEL_SRC_XATTR
+ 	ZFS_AC_KERNEL_SRC_ACL
++	ZFS_AC_KERNEL_SRC_INODE_SETATTR
+ 	ZFS_AC_KERNEL_SRC_INODE_GETATTR
+ 	ZFS_AC_KERNEL_SRC_INODE_SET_FLAGS
+ 	ZFS_AC_KERNEL_SRC_INODE_SET_IVERSION
+@@ -131,7 +132,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_KSTRTOUL
+ 	ZFS_AC_KERNEL_SRC_PERCPU
+ 	ZFS_AC_KERNEL_SRC_CPU_HOTPLUG
+-	ZFS_AC_KERNEL_SRC_GENERIC_FILLATTR_USERNS
++	ZFS_AC_KERNEL_SRC_GENERIC_FILLATTR
+ 	ZFS_AC_KERNEL_SRC_MKNOD
+ 	ZFS_AC_KERNEL_SRC_SYMLINK
+ 	ZFS_AC_KERNEL_SRC_BIO_MAX_SEGS
+@@ -189,6 +190,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_INODE_OWNER_OR_CAPABLE
+ 	ZFS_AC_KERNEL_XATTR
+ 	ZFS_AC_KERNEL_ACL
++	ZFS_AC_KERNEL_INODE_SETATTR
+ 	ZFS_AC_KERNEL_INODE_GETATTR
+ 	ZFS_AC_KERNEL_INODE_SET_FLAGS
+ 	ZFS_AC_KERNEL_INODE_SET_IVERSION
+@@ -251,7 +253,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_KSTRTOUL
+ 	ZFS_AC_KERNEL_PERCPU
+ 	ZFS_AC_KERNEL_CPU_HOTPLUG
+-	ZFS_AC_KERNEL_GENERIC_FILLATTR_USERNS
++	ZFS_AC_KERNEL_GENERIC_FILLATTR
+ 	ZFS_AC_KERNEL_MKNOD
+ 	ZFS_AC_KERNEL_SYMLINK
+ 	ZFS_AC_KERNEL_BIO_MAX_SEGS
+diff --git a/include/os/freebsd/spl/sys/types.h b/include/os/freebsd/spl/sys/types.h
+index 6557c840feb..bbb81f9f840 100644
+--- a/include/os/freebsd/spl/sys/types.h
++++ b/include/os/freebsd/spl/sys/types.h
+@@ -104,7 +104,7 @@ typedef	u_longlong_t	len_t;
+ 
+ typedef	longlong_t	diskaddr_t;
+ 
+-typedef void		zuserns_t;
++typedef void		zidmap_t;
+ 
+ #include <sys/debug.h>
+ #endif	/* !_OPENSOLARIS_SYS_TYPES_H_ */
+diff --git a/include/os/freebsd/zfs/sys/zfs_vnops_os.h b/include/os/freebsd/zfs/sys/zfs_vnops_os.h
+index 460aecd2e70..b79cccfcf35 100644
+--- a/include/os/freebsd/zfs/sys/zfs_vnops_os.h
++++ b/include/os/freebsd/zfs/sys/zfs_vnops_os.h
+@@ -35,22 +35,22 @@ int dmu_read_pages(objset_t *os, uint64_t object, vm_page_t *ma, int count,
+     int *rbehind, int *rahead, int last_size);
+ extern int zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags);
+ extern int zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap,
+-    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns);
++    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zidmap_t *mnt_ns);
+ extern int zfs_rmdir(znode_t *dzp, const char *name, znode_t *cwd,
+     cred_t *cr, int flags);
+ extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr,
+-    zuserns_t *mnt_ns);
++    zidmap_t *mnt_ns);
+ extern int zfs_rename(znode_t *sdzp, const char *snm, znode_t *tdzp,
+-    const char *tnm, cred_t *cr, int flags, zuserns_t *mnt_ns);
++    const char *tnm, cred_t *cr, int flags, zidmap_t *mnt_ns);
+ extern int zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap,
+-    const char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns);
++    const char *link, znode_t **zpp, cred_t *cr, int flags, zidmap_t *mnt_ns);
+ extern int zfs_link(znode_t *tdzp, znode_t *sp,
+     const char *name, cred_t *cr, int flags);
+ extern int zfs_space(znode_t *zp, int cmd, struct flock *bfp, int flag,
+     offset_t offset, cred_t *cr);
+ extern int zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl,
+     int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
+-    zuserns_t *mnt_ns);
++    zidmap_t *mnt_ns);
+ extern int zfs_setsecattr(znode_t *zp, vsecattr_t *vsecp, int flag,
+     cred_t *cr);
+ extern int zfs_write_simple(znode_t *zp, const void *data, size_t len,
+diff --git a/include/os/linux/kernel/linux/vfs_compat.h b/include/os/linux/kernel/linux/vfs_compat.h
+index 91e908598fb..e82bbf755d5 100644
+--- a/include/os/linux/kernel/linux/vfs_compat.h
++++ b/include/os/linux/kernel/linux/vfs_compat.h
+@@ -344,7 +344,8 @@ static inline void zfs_gid_write(struct inode *ip, gid_t gid)
+  * 4.9 API change
+  */
+ #if !(defined(HAVE_SETATTR_PREPARE_NO_USERNS) || \
+-    defined(HAVE_SETATTR_PREPARE_USERNS))
++    defined(HAVE_SETATTR_PREPARE_USERNS) || \
++    defined(HAVE_SETATTR_PREPARE_IDMAP))
+ static inline int
+ setattr_prepare(struct dentry *dentry, struct iattr *ia)
+ {
+@@ -399,6 +400,15 @@ func(struct user_namespace *user_ns, const struct path *path,	\
+ 	return (func##_impl(user_ns, path, stat, request_mask, \
+ 	    query_flags));	\
+ }
++#elif defined(HAVE_IDMAP_IOPS_GETATTR)
++#define	ZPL_GETATTR_WRAPPER(func)					\
++static int								\
++func(struct mnt_idmap *user_ns, const struct path *path,	\
++    struct kstat *stat, u32 request_mask, unsigned int query_flags)	\
++{									\
++	return (func##_impl(user_ns, path, stat, request_mask,	\
++	    query_flags));	\
++}
+ #else
+ #error
+ #endif
+@@ -450,8 +460,15 @@ zpl_is_32bit_api(void)
+  * 5.12 API change
+  * To support id-mapped mounts, generic_fillattr() was modified to
+  * accept a new struct user_namespace* as its first arg.
++ *
++ * 6.3 API change
++ * generic_fillattr() first arg is changed to struct mnt_idmap *
++ *
+  */
+-#ifdef HAVE_GENERIC_FILLATTR_USERNS
++#ifdef HAVE_GENERIC_FILLATTR_IDMAP
++#define	zpl_generic_fillattr(idmap, ip, sp)	\
++    generic_fillattr(idmap, ip, sp)
++#elif defined(HAVE_GENERIC_FILLATTR_USERNS)
+ #define	zpl_generic_fillattr(user_ns, ip, sp)	\
+     generic_fillattr(user_ns, ip, sp)
+ #else
+diff --git a/include/os/linux/kernel/linux/xattr_compat.h b/include/os/linux/kernel/linux/xattr_compat.h
+index 83763c64a14..700454c26de 100644
+--- a/include/os/linux/kernel/linux/xattr_compat.h
++++ b/include/os/linux/kernel/linux/xattr_compat.h
+@@ -133,13 +133,28 @@ fn(const struct xattr_handler *handler, struct dentry *dentry,		\
+ #error "Unsupported kernel"
+ #endif
+ 
++/*
++ * 6.3 API change,
++ * The xattr_handler->set() callback was changed to take the
++ * struct mnt_idmap* as the first arg, to support idmapped
++ * mounts.
++ */
++#if defined(HAVE_XATTR_SET_IDMAP)
++#define	ZPL_XATTR_SET_WRAPPER(fn)					\
++static int								\
++fn(const struct xattr_handler *handler, struct mnt_idmap *user_ns,	\
++    struct dentry *dentry, struct inode *inode, const char *name,	\
++    const void *buffer, size_t size, int flags)	\
++{									\
++	return (__ ## fn(user_ns, inode, name, buffer, size, flags));	\
++}
+ /*
+  * 5.12 API change,
+  * The xattr_handler->set() callback was changed to take the
+  * struct user_namespace* as the first arg, to support idmapped
+  * mounts.
+  */
+-#if defined(HAVE_XATTR_SET_USERNS)
++#elif defined(HAVE_XATTR_SET_USERNS)
+ #define	ZPL_XATTR_SET_WRAPPER(fn)					\
+ static int								\
+ fn(const struct xattr_handler *handler, struct user_namespace *user_ns, \
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index 75ad400d312..7fd5f644863 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -48,6 +48,8 @@ extern struct task_struct init_task;
+ #define	SGID_TO_KGID(x)		(KGIDT_INIT(x))
+ #define	KGIDP_TO_SGIDP(x)	(&(x)->val)
+ 
++extern zidmap_t *zfs_get_init_idmap(void);
++
+ /* Check if the user ns is the initial one */
+ static inline boolean_t
+ zfs_is_init_userns(struct user_namespace *user_ns)
+@@ -74,36 +76,39 @@ static inline boolean_t zfs_no_idmapping(struct user_namespace *mnt_userns,
+ 	return (zfs_is_init_userns(mnt_userns) || mnt_userns == fs_userns);
+ }
+ 
+-static inline uid_t zfs_uid_to_vfsuid(struct user_namespace *mnt_userns,
++static inline uid_t zfs_uid_to_vfsuid(zidmap_t *mnt_userns,
+     struct user_namespace *fs_userns, uid_t uid)
+ {
+-	if (zfs_no_idmapping(mnt_userns, fs_userns))
++	struct user_namespace *owner = idmap_owner(mnt_userns);
++	if (zfs_no_idmapping(owner, fs_userns))
+ 		return (uid);
+ 	if (!zfs_is_init_userns(fs_userns))
+ 		uid = from_kuid(fs_userns, KUIDT_INIT(uid));
+ 	if (uid == (uid_t)-1)
+ 		return (uid);
+-	return (__kuid_val(make_kuid(mnt_userns, uid)));
++	return (__kuid_val(make_kuid(owner, uid)));
+ }
+ 
+-static inline gid_t zfs_gid_to_vfsgid(struct user_namespace *mnt_userns,
++static inline gid_t zfs_gid_to_vfsgid(zidmap_t *mnt_userns,
+     struct user_namespace *fs_userns, gid_t gid)
+ {
+-	if (zfs_no_idmapping(mnt_userns, fs_userns))
++	struct user_namespace *owner = idmap_owner(mnt_userns);
++	if (zfs_no_idmapping(owner, fs_userns))
+ 		return (gid);
+ 	if (!zfs_is_init_userns(fs_userns))
+ 		gid = from_kgid(fs_userns, KGIDT_INIT(gid));
+ 	if (gid == (gid_t)-1)
+ 		return (gid);
+-	return (__kgid_val(make_kgid(mnt_userns, gid)));
++	return (__kgid_val(make_kgid(owner, gid)));
+ }
+ 
+-static inline uid_t zfs_vfsuid_to_uid(struct user_namespace *mnt_userns,
++static inline uid_t zfs_vfsuid_to_uid(zidmap_t *mnt_userns,
+     struct user_namespace *fs_userns, uid_t uid)
+ {
+-	if (zfs_no_idmapping(mnt_userns, fs_userns))
++	struct user_namespace *owner = idmap_owner(mnt_userns);
++	if (zfs_no_idmapping(owner, fs_userns))
+ 		return (uid);
+-	uid = from_kuid(mnt_userns, KUIDT_INIT(uid));
++	uid = from_kuid(owner, KUIDT_INIT(uid));
+ 	if (uid == (uid_t)-1)
+ 		return (uid);
+ 	if (zfs_is_init_userns(fs_userns))
+@@ -111,12 +116,13 @@ static inline uid_t zfs_vfsuid_to_uid(struct user_namespace *mnt_userns,
+ 	return (__kuid_val(make_kuid(fs_userns, uid)));
+ }
+ 
+-static inline gid_t zfs_vfsgid_to_gid(struct user_namespace *mnt_userns,
++static inline gid_t zfs_vfsgid_to_gid(zidmap_t *mnt_userns,
+     struct user_namespace *fs_userns, gid_t gid)
+ {
+-	if (zfs_no_idmapping(mnt_userns, fs_userns))
++	struct user_namespace *owner = idmap_owner(mnt_userns);
++	if (zfs_no_idmapping(owner, fs_userns))
+ 		return (gid);
+-	gid = from_kgid(mnt_userns, KGIDT_INIT(gid));
++	gid = from_kgid(owner, KGIDT_INIT(gid));
+ 	if (gid == (gid_t)-1)
+ 		return (gid);
+ 	if (zfs_is_init_userns(fs_userns))
+diff --git a/include/os/linux/spl/sys/types.h b/include/os/linux/spl/sys/types.h
+index cae1bbddf10..a7666187ec2 100644
+--- a/include/os/linux/spl/sys/types.h
++++ b/include/os/linux/spl/sys/types.h
+@@ -55,6 +55,19 @@ typedef int			major_t;
+ typedef int			minor_t;
+ 
+ struct user_namespace;
+-typedef struct user_namespace	zuserns_t;
++#ifdef HAVE_IOPS_CREATE_IDMAP
++#include <linux/refcount.h>
++struct mnt_idmap {
++	struct user_namespace *owner;
++	refcount_t count;
++};
++typedef struct mnt_idmap	zidmap_t;
++#define	idmap_owner(p)	(((struct mnt_idmap *)p)->owner)
++#else
++typedef struct user_namespace	zidmap_t;
++#define	idmap_owner(p)	((struct user_namespace *)p)
++#endif
++
++extern zidmap_t *zfs_init_idmap;
+ 
+ #endif	/* _SPL_TYPES_H */
+diff --git a/include/os/linux/zfs/sys/policy.h b/include/os/linux/zfs/sys/policy.h
+index 7548804b9ee..ab352fd30cb 100644
+--- a/include/os/linux/zfs/sys/policy.h
++++ b/include/os/linux/zfs/sys/policy.h
+@@ -47,14 +47,14 @@ int secpolicy_vnode_create_gid(const cred_t *);
+ int secpolicy_vnode_remove(const cred_t *);
+ int secpolicy_vnode_setdac(const cred_t *, uid_t);
+ int secpolicy_vnode_setid_retain(struct znode *, const cred_t *, boolean_t);
+-int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *,
+-    zuserns_t *);
++int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zidmap_t *,
++    struct user_namespace *);
+ int secpolicy_zinject(const cred_t *);
+ int secpolicy_zfs(const cred_t *);
+ int secpolicy_zfs_proc(const cred_t *, proc_t *);
+ void secpolicy_setid_clear(vattr_t *, cred_t *);
+ int secpolicy_setid_setsticky_clear(struct inode *, vattr_t *,
+-    const vattr_t *, cred_t *, zuserns_t *, zuserns_t *);
++    const vattr_t *, cred_t *, zidmap_t *, struct user_namespace *);
+ int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, mode_t);
+ int secpolicy_vnode_setattr(cred_t *, struct inode *, struct vattr *,
+     const struct vattr *, int, int (void *, int, cred_t *), void *);
+diff --git a/include/os/linux/zfs/sys/zfs_vnops_os.h b/include/os/linux/zfs/sys/zfs_vnops_os.h
+index fcb32308562..941eb93e97f 100644
+--- a/include/os/linux/zfs/sys/zfs_vnops_os.h
++++ b/include/os/linux/zfs/sys/zfs_vnops_os.h
+@@ -46,24 +46,23 @@ extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags,
+     cred_t *cr, int *direntflags, pathname_t *realpnp);
+ extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+     int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
+-    zuserns_t *mnt_ns);
++    zidmap_t *mnt_ns);
+ extern int zfs_tmpfile(struct inode *dip, vattr_t *vapzfs, int excl,
+     int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp,
+-    zuserns_t *mnt_ns);
++    zidmap_t *mnt_ns);
+ extern int zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags);
+ extern int zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap,
+-    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns);
++    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zidmap_t *mnt_ns);
+ extern int zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd,
+     cred_t *cr, int flags);
+ extern int zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr);
+-extern int zfs_getattr_fast(struct user_namespace *, struct inode *ip,
+-	struct kstat *sp);
++extern int zfs_getattr_fast(zidmap_t *, struct inode *ip, struct kstat *sp);
+ extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr,
+-    zuserns_t *mnt_ns);
++    zidmap_t *mnt_ns);
+ extern int zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp,
+-    char *tnm, cred_t *cr, int flags, zuserns_t *mnt_ns);
++    char *tnm, cred_t *cr, int flags, zidmap_t *mnt_ns);
+ extern int zfs_symlink(znode_t *dzp, char *name, vattr_t *vap,
+-    char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns);
++    char *link, znode_t **zpp, cred_t *cr, int flags, zidmap_t *mnt_ns);
+ extern int zfs_readlink(struct inode *ip, zfs_uio_t *uio, cred_t *cr);
+ extern int zfs_link(znode_t *tdzp, znode_t *szp,
+     char *name, cred_t *cr, int flags);
+diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h
+index 703e335c28a..795b9b4f4ce 100644
+--- a/include/os/linux/zfs/sys/zpl.h
++++ b/include/os/linux/zfs/sys/zpl.h
+@@ -39,7 +39,7 @@
+ 
+ /* zpl_inode.c */
+ extern void zpl_vap_init(vattr_t *vap, struct inode *dir,
+-    umode_t mode, cred_t *cr, zuserns_t *mnt_ns);
++    umode_t mode, cred_t *cr, zidmap_t *mnt_ns);
+ 
+ extern const struct inode_operations zpl_inode_operations;
+ extern const struct inode_operations zpl_dir_inode_operations;
+@@ -64,7 +64,10 @@ extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
+     const struct qstr *qstr);
+ #if defined(CONFIG_FS_POSIX_ACL)
+ #if defined(HAVE_SET_ACL)
+-#if defined(HAVE_SET_ACL_USERNS)
++#if defined(HAVE_SET_ACL_IDMAP_DENTRY)
++extern int zpl_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
++    struct posix_acl *acl, int type);
++#elif defined(HAVE_SET_ACL_USERNS)
+ extern int zpl_set_acl(struct user_namespace *userns, struct inode *ip,
+     struct posix_acl *acl, int type);
+ #elif defined(HAVE_SET_ACL_USERNS_DENTRY_ARG2)
+@@ -186,13 +189,15 @@ zpl_dir_emit_dots(struct file *file, zpl_dir_context_t *ctx)
+ 
+ #if defined(HAVE_INODE_OWNER_OR_CAPABLE)
+ #define	zpl_inode_owner_or_capable(ns, ip)	inode_owner_or_capable(ip)
+-#elif defined(HAVE_INODE_OWNER_OR_CAPABLE_IDMAPPED)
++#elif defined(HAVE_INODE_OWNER_OR_CAPABLE_USERNS)
+ #define	zpl_inode_owner_or_capable(ns, ip)	inode_owner_or_capable(ns, ip)
++#elif defined(HAVE_INODE_OWNER_OR_CAPABLE_IDMAP)
++#define	zpl_inode_owner_or_capable(idmap, ip) inode_owner_or_capable(idmap, ip)
+ #else
+ #error "Unsupported kernel"
+ #endif
+ 
+-#ifdef HAVE_SETATTR_PREPARE_USERNS
++#if defined(HAVE_SETATTR_PREPARE_USERNS) || defined(HAVE_SETATTR_PREPARE_IDMAP)
+ #define	zpl_setattr_prepare(ns, dentry, ia)	setattr_prepare(ns, dentry, ia)
+ #else
+ /*
+diff --git a/include/sys/zfs_acl.h b/include/sys/zfs_acl.h
+index 93a1f7f6172..55af29cc000 100644
+--- a/include/sys/zfs_acl.h
++++ b/include/sys/zfs_acl.h
+@@ -206,7 +206,7 @@ struct zfsvfs;
+ 
+ #ifdef _KERNEL
+ int zfs_acl_ids_create(struct znode *, int, vattr_t *,
+-    cred_t *, vsecattr_t *, zfs_acl_ids_t *, zuserns_t *);
++    cred_t *, vsecattr_t *, zfs_acl_ids_t *, zidmap_t *);
+ void zfs_acl_ids_free(zfs_acl_ids_t *);
+ boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *, uint64_t);
+ int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
+@@ -216,15 +216,15 @@ void zfs_oldace_byteswap(ace_t *, int);
+ void zfs_ace_byteswap(void *, size_t, boolean_t);
+ extern boolean_t zfs_has_access(struct znode *zp, cred_t *cr);
+ extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *,
+-    zuserns_t *);
++    zidmap_t *);
+ int zfs_fastaccesschk_execute(struct znode *, cred_t *);
+-extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *, zuserns_t *);
+-extern int zfs_zaccess_unix(struct znode *, mode_t, cred_t *);
++extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *, zidmap_t *);
++extern int zfs_zaccess_unix(void *, int, cred_t *);
+ extern int zfs_acl_access(struct znode *, int, cred_t *);
+ int zfs_acl_chmod_setattr(struct znode *, zfs_acl_t **, uint64_t);
+-int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *, zuserns_t *);
++int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *, zidmap_t *);
+ int zfs_zaccess_rename(struct znode *, struct znode *,
+-    struct znode *, struct znode *, cred_t *cr, zuserns_t *mnt_ns);
++    struct znode *, struct znode *, cred_t *cr, zidmap_t *mnt_ns);
+ void zfs_acl_free(zfs_acl_t *);
+ int zfs_vsec_2_aclp(struct zfsvfs *, umode_t, vsecattr_t *, cred_t *,
+     struct zfs_fuid_info **, zfs_acl_t **);
+diff --git a/module/os/freebsd/zfs/zfs_acl.c b/module/os/freebsd/zfs/zfs_acl.c
+index ca50f442a07..060bf143136 100644
+--- a/module/os/freebsd/zfs/zfs_acl.c
++++ b/module/os/freebsd/zfs/zfs_acl.c
+@@ -1618,7 +1618,7 @@ zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp,
+  */
+ int
+ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+-    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zuserns_t *mnt_ns)
++    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zidmap_t *mnt_ns)
+ {
+ 	int		error;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -2341,7 +2341,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+  */
+ int
+ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	uint32_t	working_mode;
+ 	int		error;
+@@ -2472,7 +2472,7 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+  */
+ int
+ zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr,
+ 	    mnt_ns));
+@@ -2542,7 +2542,7 @@ zfs_delete_final_check(znode_t *zp, znode_t *dzp,
+  *
+  */
+ int
+-zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
++zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	uint32_t dzp_working_mode = 0;
+ 	uint32_t zp_working_mode = 0;
+@@ -2629,7 +2629,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
+ 
+ int
+ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+-    znode_t *tzp, cred_t *cr, zuserns_t *mnt_ns)
++    znode_t *tzp, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	int add_perm;
+ 	int error;
+diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c
+index 0b7bbbdc52b..145ad4680b2 100644
+--- a/module/os/freebsd/zfs/zfs_vnops_os.c
++++ b/module/os/freebsd/zfs/zfs_vnops_os.c
+@@ -1062,7 +1062,7 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp,
+ /* ARGSUSED */
+ int
+ zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl, int mode,
+-    znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp, zuserns_t *mnt_ns)
++    znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -1413,7 +1413,7 @@ zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags)
+ /*ARGSUSED*/
+ int
+ zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap, znode_t **zpp,
+-    cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns)
++    cred_t *cr, int flags, vsecattr_t *vsecp, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -2224,7 +2224,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
+  */
+ /* ARGSUSED */
+ int
+-zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
++zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	vnode_t		*vp = ZTOV(zp);
+ 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
+@@ -3478,7 +3478,7 @@ zfs_do_rename_impl(vnode_t *sdvp, vnode_t **svpp, struct componentname *scnp,
+ 
+ int
+ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
+-    cred_t *cr, int flags, zuserns_t *mnt_ns)
++    cred_t *cr, int flags, zidmap_t *mnt_ns)
+ {
+ 	struct componentname scn, tcn;
+ 	vnode_t *sdvp, *tdvp;
+@@ -3533,7 +3533,7 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
+ /*ARGSUSED*/
+ int
+ zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap,
+-    const char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns)
++    const char *link, znode_t **zpp, cred_t *cr, int flags, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	dmu_tx_t	*tx;
+diff --git a/module/os/linux/spl/spl-cred.c b/module/os/linux/spl/spl-cred.c
+index f81b9540a63..d407fc66b2d 100644
+--- a/module/os/linux/spl/spl-cred.c
++++ b/module/os/linux/spl/spl-cred.c
+@@ -145,6 +145,18 @@ crgetgid(const cred_t *cr)
+ 	return (KGID_TO_SGID(cr->fsgid));
+ }
+ 
++/* Return the initial user ns or nop_mnt_idmap */
++zidmap_t *
++zfs_get_init_idmap(void)
++{
++#ifdef HAVE_IOPS_CREATE_IDMAP
++	return ((zidmap_t *)&nop_mnt_idmap);
++#else
++	return ((zidmap_t *)&init_user_ns);
++#endif
++}
++
++EXPORT_SYMBOL(zfs_get_init_idmap);
+ EXPORT_SYMBOL(crhold);
+ EXPORT_SYMBOL(crfree);
+ EXPORT_SYMBOL(crgetuid);
+diff --git a/module/os/linux/zfs/policy.c b/module/os/linux/zfs/policy.c
+index e879ec32c20..cc831bbb339 100644
+--- a/module/os/linux/zfs/policy.c
++++ b/module/os/linux/zfs/policy.c
+@@ -124,7 +124,7 @@ secpolicy_vnode_any_access(const cred_t *cr, struct inode *ip, uid_t owner)
+ 	if (crgetuid(cr) == owner)
+ 		return (0);
+ 
+-	if (zpl_inode_owner_or_capable(kcred->user_ns, ip))
++	if (zpl_inode_owner_or_capable(zfs_init_idmap, ip))
+ 		return (0);
+ 
+ #if defined(CONFIG_USER_NS)
+@@ -214,8 +214,8 @@ secpolicy_vnode_setid_retain(struct znode *zp __maybe_unused, const cred_t *cr,
+  * Determine that subject can set the file setgid flag.
+  */
+ int
+-secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns,
+-    zuserns_t *fs_ns)
++secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zidmap_t *mnt_ns,
++    struct user_namespace *fs_ns)
+ {
+ 	gid = zfs_gid_to_vfsgid(mnt_ns, fs_ns, gid);
+ #if defined(CONFIG_USER_NS)
+@@ -286,8 +286,8 @@ secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
+  * Determine that subject can set the file setid flags.
+  */
+ static int
+-secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns,
+-    zuserns_t *fs_ns)
++secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zidmap_t *mnt_ns,
++    struct user_namespace *fs_ns)
+ {
+ 	owner = zfs_uid_to_vfsuid(mnt_ns, fs_ns, owner);
+ 
+@@ -315,7 +315,8 @@ secpolicy_vnode_stky_modify(const cred_t *cr)
+ 
+ int
+ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+-    const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns, zuserns_t *fs_ns)
++    const vattr_t *ovap, cred_t *cr, zidmap_t *mnt_ns,
++    struct user_namespace *fs_ns)
+ {
+ 	int error;
+ 
+diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
+index 3d31da6e35b..7014ccd22a3 100644
+--- a/module/os/linux/zfs/zfs_acl.c
++++ b/module/os/linux/zfs/zfs_acl.c
+@@ -1801,7 +1801,7 @@ zfs_acl_inherit(zfsvfs_t *zfsvfs, umode_t va_mode, zfs_acl_t *paclp,
+  */
+ int
+ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+-    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zuserns_t *mnt_ns)
++    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zidmap_t *mnt_ns)
+ {
+ 	int		error;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -1980,7 +1980,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 		return (SET_ERROR(ENOSYS));
+ 
+ 	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr,
+-	    kcred->user_ns)))
++	    zfs_init_idmap)))
+ 		return (error);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+@@ -2140,7 +2140,7 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 		return (SET_ERROR(EPERM));
+ 
+ 	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
+-	    kcred->user_ns)))
++	    zfs_init_idmap)))
+ 		return (error);
+ 
+ 	error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
+@@ -2286,7 +2286,7 @@ zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
+  */
+ static int
+ zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+-    boolean_t anyaccess, cred_t *cr, zuserns_t *mnt_ns)
++    boolean_t anyaccess, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(zp);
+ 	zfs_acl_t	*aclp;
+@@ -2420,7 +2420,7 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+ 	uint32_t have = ACE_ALL_PERMS;
+ 
+ 	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr,
+-	    kcred->user_ns) != 0) {
++	    zfs_init_idmap) != 0) {
+ 		uid_t owner;
+ 
+ 		owner = zfs_fuid_map_id(ZTOZSB(zp),
+@@ -2451,9 +2451,9 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+  */
+ static int
+ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+-	int err, mask;
++	int err, mask = 0;
+ 	int unmapped = 0;
+ 
+ 	ASSERT(zp->z_pflags & ZFS_ACL_TRIVIAL);
+@@ -2464,11 +2464,10 @@ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
+ 		return (unmapped ? SET_ERROR(EPERM) : 0);
+ 	}
+ 
+-#if defined(HAVE_IOPS_PERMISSION_USERNS)
++#if (defined(HAVE_IOPS_PERMISSION_USERNS) || \
++	defined(HAVE_IOPS_PERMISSION_IDMAP))
+ 	if (mnt_ns)
+ 		err = generic_permission(mnt_ns, ZTOI(zp), mask);
+-	else
+-		err = generic_permission(cr->user_ns, ZTOI(zp), mask);
+ #else
+ 	err = generic_permission(ZTOI(zp), mask);
+ #endif
+@@ -2483,7 +2482,7 @@ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
+ 
+ static int
+ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+-    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr, zuserns_t *mnt_ns)
++    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	zfsvfs_t *zfsvfs = ZTOZSB(zp);
+ 	int err;
+@@ -2540,7 +2539,7 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+ 
+ static int
+ zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
+-    cred_t *cr, zuserns_t *mnt_ns)
++    cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	if (*working_mode != ACE_WRITE_DATA)
+ 		return (SET_ERROR(EACCES));
+@@ -2613,7 +2612,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+ 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
+ 	ZFS_ENTER(ZTOZSB(zdp));
+ 	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+-	    kcred->user_ns);
++	    zfs_init_idmap);
+ 	ZFS_EXIT(ZTOZSB(zdp));
+ 	return (error);
+ }
+@@ -2626,7 +2625,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+  */
+ int
+ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	uint32_t	working_mode;
+ 	int		error;
+@@ -2776,7 +2775,7 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+  */
+ int
+ zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr,
+ 	    mnt_ns));
+@@ -2786,11 +2785,11 @@ zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
+  * Access function for secpolicy_vnode_setattr
+  */
+ int
+-zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
++zfs_zaccess_unix(void *zp, int mode, cred_t *cr)
+ {
+ 	int v4_mode = zfs_unix_to_v4(mode >> 6);
+ 
+-	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, kcred->user_ns));
++	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, zfs_init_idmap));
+ }
+ 
+ /* See zfs_zaccess_delete() */
+@@ -2867,7 +2866,7 @@ int zfs_write_implies_delete_child = 1;
+  * zfs_write_implies_delete_child
+  */
+ int
+-zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
++zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	uint32_t wanted_dirperms;
+ 	uint32_t dzp_working_mode = 0;
+@@ -2998,7 +2997,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
+ 
+ int
+ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+-    znode_t *tzp, cred_t *cr, zuserns_t *mnt_ns)
++    znode_t *tzp, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	int add_perm;
+ 	int error;
+diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c
+index 8c65c33628d..353e8805a8c 100644
+--- a/module/os/linux/zfs/zfs_dir.c
++++ b/module/os/linux/zfs/zfs_dir.c
+@@ -1067,7 +1067,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr)
+ 	*xzpp = NULL;
+ 
+ 	if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
+-	    &acl_ids, kcred->user_ns)) != 0)
++	    &acl_ids, zfs_init_idmap)) != 0)
+ 		return (error);
+ 	if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) {
+ 		zfs_acl_ids_free(&acl_ids);
+@@ -1216,7 +1216,7 @@ zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
+ 
+ 	if ((uid = crgetuid(cr)) == downer || uid == fowner ||
+ 	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
+-	    kcred->user_ns) == 0)
++	    zfs_init_idmap) == 0)
+ 		return (0);
+ 	else
+ 		return (secpolicy_vnode_remove(cr));
+diff --git a/module/os/linux/zfs/zfs_ioctl_os.c b/module/os/linux/zfs/zfs_ioctl_os.c
+index d70b31f9f10..5634a2514ae 100644
+--- a/module/os/linux/zfs/zfs_ioctl_os.c
++++ b/module/os/linux/zfs/zfs_ioctl_os.c
+@@ -290,6 +290,8 @@ zfsdev_detach(void)
+ #define	ZFS_DEBUG_STR	""
+ #endif
+ 
++zidmap_t *zfs_init_idmap;
++
+ static int __init
+ openzfs_init(void)
+ {
+@@ -313,6 +315,8 @@ openzfs_init(void)
+ 	printk(KERN_NOTICE "ZFS: Posix ACLs disabled by kernel\n");
+ #endif /* CONFIG_FS_POSIX_ACL */
+ 
++	zfs_init_idmap = (zidmap_t *)zfs_get_init_idmap();
++
+ 	return (0);
+ }
+ 
+diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
+index 23f3d140cf5..f8240ab7682 100644
+--- a/module/os/linux/zfs/zfs_vnops_os.c
++++ b/module/os/linux/zfs/zfs_vnops_os.c
+@@ -501,7 +501,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 		 */
+ 
+ 		if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
+-		    B_TRUE, cr, kcred->user_ns))) {
++		    B_TRUE, cr, zfs_init_idmap))) {
+ 			zrele(*zpp);
+ 			*zpp = NULL;
+ 		}
+@@ -520,7 +520,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 	 */
+ 
+ 	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+-	    kcred->user_ns))) {
++	    zfs_init_idmap))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -567,7 +567,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ int
+ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+     int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -817,7 +817,7 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+ int
+ zfs_tmpfile(struct inode *dip, vattr_t *vap, int excl,
+     int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp = NULL, *dzp = ITOZ(dip);
+ 	zfsvfs_t	*zfsvfs = ITOZSB(dip);
+@@ -1002,7 +1002,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_idmap))) {
+ 		goto out;
+ 	}
+ 
+@@ -1196,7 +1196,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ /*ARGSUSED*/
+ int
+ zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp,
+-    cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns)
++    cred_t *cr, int flags, vsecattr_t *vsecp, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -1418,7 +1418,7 @@ zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_idmap))) {
+ 		goto out;
+ 	}
+ 
+@@ -1671,8 +1671,7 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr)
+  */
+ /* ARGSUSED */
+ int
+-zfs_getattr_fast(struct user_namespace *user_ns, struct inode *ip,
+-    struct kstat *sp)
++zfs_getattr_fast(zidmap_t *user_ns, struct inode *ip, struct kstat *sp)
+ {
+ 	znode_t *zp = ITOZ(ip);
+ 	zfsvfs_t *zfsvfs = ITOZSB(ip);
+@@ -1860,7 +1859,7 @@ zfs_setattr_dir(znode_t *dzp)
+  */
+ /* ARGSUSED */
+ int
+-zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
++zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zidmap_t *mnt_ns)
+ {
+ 	struct inode	*ip;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(zp);
+@@ -2057,10 +2056,10 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
+ 		 * Take ownership or chgrp to group we are a member of
+ 		 */
+ 
+-		uid = zfs_uid_to_vfsuid((struct user_namespace *)mnt_ns,
+-		    zfs_i_user_ns(ip), vap->va_uid);
+-		gid = zfs_gid_to_vfsgid((struct user_namespace *)mnt_ns,
+-		    zfs_i_user_ns(ip), vap->va_gid);
++		uid = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ip),
++		    vap->va_uid);
++		gid = zfs_gid_to_vfsgid(mnt_ns, zfs_i_user_ns(ip),
++		    vap->va_gid);
+ 		take_owner = (mask & ATTR_UID) && (uid == crgetuid(cr));
+ 		take_group = (mask & ATTR_GID) &&
+ 		    zfs_groupmember(zfsvfs, gid, cr);
+@@ -2698,7 +2697,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
+ /*ARGSUSED*/
+ int
+ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+-    cred_t *cr, int flags, zuserns_t *mnt_ns)
++    cred_t *cr, int flags, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*szp, *tzp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(sdzp);
+@@ -3067,7 +3066,7 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+ /*ARGSUSED*/
+ int
+ zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, char *link,
+-    znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns)
++    znode_t **zpp, cred_t *cr, int flags, zidmap_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfs_dirlock_t	*dl;
+@@ -3373,7 +3372,7 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
+ 	}
+ 
+ 	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr,
+-	    kcred->user_ns))) {
++	    zfs_init_idmap))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3962,7 +3961,7 @@ zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag,
+ 	 * operates directly on inodes, so we need to check access rights.
+ 	 */
+ 	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
+-	    kcred->user_ns))) {
++	    zfs_init_idmap))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
+index 5e820587388..4a1c6525aca 100644
+--- a/module/os/linux/zfs/zfs_znode.c
++++ b/module/os/linux/zfs/zfs_znode.c
+@@ -1947,7 +1947,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
+ 	}
+ 
+ 	VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
+-	    cr, NULL, &acl_ids, kcred->user_ns));
++	    cr, NULL, &acl_ids, zfs_init_idmap));
+ 	zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, rootzp);
+ 	error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
+diff --git a/module/os/linux/zfs/zpl_ctldir.c b/module/os/linux/zfs/zpl_ctldir.c
+index e4f191ba051..1e1c35ed157 100644
+--- a/module/os/linux/zfs/zpl_ctldir.c
++++ b/module/os/linux/zfs/zpl_ctldir.c
+@@ -101,7 +101,11 @@ zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+  */
+ /* ARGSUSED */
+ static int
+-#ifdef HAVE_USERNS_IOPS_GETATTR
++#ifdef HAVE_IDMAP_IOPS_GETATTR
++zpl_root_getattr_impl(struct mnt_idmap *user_ns,
++    const struct path *path, struct kstat *stat, u32 request_mask,
++    unsigned int query_flags)
++#elif defined(HAVE_USERNS_IOPS_GETATTR)
+ zpl_root_getattr_impl(struct user_namespace *user_ns,
+     const struct path *path, struct kstat *stat, u32 request_mask,
+     unsigned int query_flags)
+@@ -112,8 +116,14 @@ zpl_root_getattr_impl(const struct path *path, struct kstat *stat,
+ {
+ 	struct inode *ip = path->dentry->d_inode;
+ 
+-#if defined(HAVE_GENERIC_FILLATTR_USERNS) && defined(HAVE_USERNS_IOPS_GETATTR)
++#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
++#ifdef HAVE_GENERIC_FILLATTR_USERNS
+ 	generic_fillattr(user_ns, ip, stat);
++#elif defined(HAVE_GENERIC_FILLATTR_IDMAP)
++	generic_fillattr(user_ns, ip, stat);
++#else
++	(void) user_ns;
++#endif
+ #else
+ 	generic_fillattr(ip, stat);
+ #endif
+@@ -304,6 +314,10 @@ static int
+ zpl_snapdir_rename2(struct user_namespace *user_ns, struct inode *sdip,
+     struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry,
+     unsigned int flags)
++#elif defined(HAVE_IOPS_RENAME_IDMAP)
++zpl_snapdir_rename2(struct mnt_idmap *user_ns, struct inode *sdip,
++    struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry,
++    unsigned int flags)
+ #else
+ zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry,
+     struct inode *tdip, struct dentry *tdentry, unsigned int flags)
+@@ -325,7 +339,9 @@ zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	return (error);
+ }
+ 
+-#if !defined(HAVE_RENAME_WANTS_FLAGS) && !defined(HAVE_IOPS_RENAME_USERNS)
++#if (!defined(HAVE_RENAME_WANTS_FLAGS) && \
++	!defined(HAVE_IOPS_RENAME_USERNS) && \
++	!defined(HAVE_IOPS_RENAME_IDMAP))
+ static int
+ zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry,
+     struct inode *tdip, struct dentry *tdentry)
+@@ -352,6 +368,9 @@ static int
+ #ifdef HAVE_IOPS_MKDIR_USERNS
+ zpl_snapdir_mkdir(struct user_namespace *user_ns, struct inode *dip,
+     struct dentry *dentry, umode_t mode)
++#elif defined(HAVE_IOPS_MKDIR_IDMAP)
++zpl_snapdir_mkdir(struct mnt_idmap *user_ns, struct inode *dip,
++    struct dentry *dentry, umode_t mode)
+ #else
+ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ #endif
+@@ -363,10 +382,10 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-#ifdef HAVE_IOPS_MKDIR_USERNS
++#if (defined(HAVE_IOPS_MKDIR_USERNS) || defined(HAVE_IOPS_MKDIR_IDMAP))
+ 	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, user_ns);
+ #else
+-	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, kcred->user_ns);
++	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, zfs_init_idmap);
+ #endif
+ 
+ 	error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
+@@ -388,7 +407,11 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+  */
+ /* ARGSUSED */
+ static int
+-#ifdef HAVE_USERNS_IOPS_GETATTR
++#ifdef HAVE_IDMAP_IOPS_GETATTR
++zpl_snapdir_getattr_impl(struct mnt_idmap *user_ns,
++    const struct path *path, struct kstat *stat, u32 request_mask,
++    unsigned int query_flags)
++#elif defined(HAVE_USERNS_IOPS_GETATTR)
+ zpl_snapdir_getattr_impl(struct user_namespace *user_ns,
+     const struct path *path, struct kstat *stat, u32 request_mask,
+     unsigned int query_flags)
+@@ -401,8 +424,14 @@ zpl_snapdir_getattr_impl(const struct path *path, struct kstat *stat,
+ 	zfsvfs_t *zfsvfs = ITOZSB(ip);
+ 
+ 	ZPL_ENTER(zfsvfs);
+-#if defined(HAVE_GENERIC_FILLATTR_USERNS) && defined(HAVE_USERNS_IOPS_GETATTR)
++#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
++#ifdef HAVE_GENERIC_FILLATTR_USERNS
++	generic_fillattr(user_ns, ip, stat);
++#elif defined(HAVE_GENERIC_FILLATTR_IDMAP)
+ 	generic_fillattr(user_ns, ip, stat);
++#else
++	(void) user_ns;
++#endif
+ #else
+ 	generic_fillattr(ip, stat);
+ #endif
+@@ -443,7 +472,9 @@ const struct file_operations zpl_fops_snapdir = {
+ const struct inode_operations zpl_ops_snapdir = {
+ 	.lookup		= zpl_snapdir_lookup,
+ 	.getattr	= zpl_snapdir_getattr,
+-#if defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS)
++#if (defined(HAVE_RENAME_WANTS_FLAGS) || \
++	defined(HAVE_IOPS_RENAME_USERNS) || \
++	defined(HAVE_IOPS_RENAME_IDMAP))
+ 	.rename		= zpl_snapdir_rename2,
+ #else
+ 	.rename		= zpl_snapdir_rename,
+@@ -534,6 +565,10 @@ static int
+ zpl_shares_getattr_impl(struct user_namespace *user_ns,
+     const struct path *path, struct kstat *stat, u32 request_mask,
+     unsigned int query_flags)
++#elif defined(HAVE_IDMAP_IOPS_GETATTR)
++zpl_shares_getattr_impl(struct mnt_idmap *user_ns,
++    const struct path *path, struct kstat *stat, u32 request_mask,
++    unsigned int query_flags)
+ #else
+ zpl_shares_getattr_impl(const struct path *path, struct kstat *stat,
+     u32 request_mask, unsigned int query_flags)
+@@ -547,8 +582,14 @@ zpl_shares_getattr_impl(const struct path *path, struct kstat *stat,
+ 	ZPL_ENTER(zfsvfs);
+ 
+ 	if (zfsvfs->z_shares_dir == 0) {
+-#if defined(HAVE_GENERIC_FILLATTR_USERNS) && defined(HAVE_USERNS_IOPS_GETATTR)
++#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
++#ifdef HAVE_GENERIC_FILLATTR_USERNS
++		generic_fillattr(user_ns, path->dentry->d_inode, stat);
++#elif defined(HAVE_GENERIC_FILLATTR_IDMAP)
+ 		generic_fillattr(user_ns, path->dentry->d_inode, stat);
++#else
++		(void) user_ns;
++#endif
+ #else
+ 		generic_fillattr(path->dentry->d_inode, stat);
+ #endif
+@@ -560,7 +601,7 @@ zpl_shares_getattr_impl(const struct path *path, struct kstat *stat,
+ 
+ 	error = -zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp);
+ 	if (error == 0) {
+-#if defined(HAVE_GENERIC_FILLATTR_USERNS) && defined(HAVE_USERNS_IOPS_GETATTR)
++#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
+ 		error = -zfs_getattr_fast(user_ns, ZTOI(dzp), stat);
+ #else
+ 		error = -zfs_getattr_fast(kcred->user_ns, ZTOI(dzp), stat);
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index 4ad30efd3ec..f124599393f 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -924,7 +924,7 @@ __zpl_ioctl_setflags(struct inode *ip, uint32_t ioctl_flags, xvattr_t *xva)
+ 	    !capable(CAP_LINUX_IMMUTABLE))
+ 		return (-EPERM);
+ 
+-	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
++	if (!zpl_inode_owner_or_capable(zfs_init_idmap, ip))
+ 		return (-EACCES);
+ 
+ 	xva_init(xva);
+@@ -971,7 +971,7 @@ zpl_ioctl_setflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_idmap);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1019,7 +1019,7 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_idmap);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1054,7 +1054,7 @@ __zpl_ioctl_setdosflags(struct inode *ip, uint64_t ioctl_flags, xvattr_t *xva)
+ 	    !capable(CAP_LINUX_IMMUTABLE))
+ 		return (-EPERM);
+ 
+-	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
++	if (!zpl_inode_owner_or_capable(zfs_init_idmap, ip))
+ 		return (-EACCES);
+ 
+ 	xva_init(xva);
+@@ -1107,7 +1107,7 @@ zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_idmap);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c
+index ea659d9618a..01bcfc19f2b 100644
+--- a/module/os/linux/zfs/zpl_inode.c
++++ b/module/os/linux/zfs/zpl_inode.c
+@@ -112,12 +112,12 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+ 
+ void
+ zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
+-    zuserns_t *mnt_ns)
++    zidmap_t *mnt_ns)
+ {
+ 	vap->va_mask = ATTR_MODE;
+ 	vap->va_mode = mode;
+ 
+-	vap->va_uid = zfs_vfsuid_to_uid((struct user_namespace *)mnt_ns,
++	vap->va_uid = zfs_vfsuid_to_uid(mnt_ns,
+ 	    zfs_i_user_ns(dir), crgetuid(cr));
+ 
+ 	if (dir && dir->i_mode & S_ISGID) {
+@@ -125,7 +125,7 @@ zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
+ 		if (S_ISDIR(mode))
+ 			vap->va_mode |= S_ISGID;
+ 	} else {
+-		vap->va_gid = zfs_vfsgid_to_gid((struct user_namespace *)mnt_ns,
++		vap->va_gid = zfs_vfsgid_to_gid(mnt_ns,
+ 		    zfs_i_user_ns(dir), crgetgid(cr));
+ 	}
+ }
+@@ -134,6 +134,9 @@ static int
+ #ifdef HAVE_IOPS_CREATE_USERNS
+ zpl_create(struct user_namespace *user_ns, struct inode *dir,
+     struct dentry *dentry, umode_t mode, bool flag)
++#elif defined(HAVE_IOPS_CREATE_IDMAP)
++zpl_create(struct mnt_idmap *user_ns, struct inode *dir,
++    struct dentry *dentry, umode_t mode, bool flag)
+ #else
+ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
+ #endif
+@@ -143,8 +146,8 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_IOPS_CREATE_USERNS
+-	zuserns_t *user_ns = kcred->user_ns;
++#if !(defined(HAVE_IOPS_CREATE_USERNS) || defined(HAVE_IOPS_CREATE_IDMAP))
++	zidmap_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -180,6 +183,9 @@ static int
+ #ifdef HAVE_IOPS_MKNOD_USERNS
+ zpl_mknod(struct user_namespace *user_ns, struct inode *dir,
+     struct dentry *dentry, umode_t mode,
++#elif defined(HAVE_IOPS_MKNOD_IDMAP)
++zpl_mknod(struct mnt_idmap *user_ns, struct inode *dir,
++    struct dentry *dentry, umode_t mode,
+ #else
+ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ #endif
+@@ -190,8 +196,8 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_IOPS_MKNOD_USERNS
+-	zuserns_t *user_ns = kcred->user_ns;
++#if !(defined(HAVE_IOPS_MKNOD_USERNS) || defined(HAVE_IOPS_MKNOD_IDMAP))
++	zidmap_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	/*
+@@ -233,7 +239,10 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 
+ #ifdef HAVE_TMPFILE
+ static int
+-#ifndef HAVE_TMPFILE_DENTRY
++#ifdef HAVE_TMPFILE_IDMAP
++zpl_tmpfile(struct mnt_idmap *userns, struct inode *dir,
++    struct file *file, umode_t mode)
++#elif !defined(HAVE_TMPFILE_DENTRY)
+ zpl_tmpfile(struct user_namespace *userns, struct inode *dir,
+     struct file *file, umode_t mode)
+ #else
+@@ -250,8 +259,8 @@ zpl_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_TMPFILE_USERNS
+-	zuserns_t *userns = kcred->user_ns;
++#if !(defined(HAVE_TMPFILE_USERNS) || defined(HAVE_TMPFILE_IDMAP))
++	zidmap_t *userns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -329,6 +338,9 @@ static int
+ #ifdef HAVE_IOPS_MKDIR_USERNS
+ zpl_mkdir(struct user_namespace *user_ns, struct inode *dir,
+     struct dentry *dentry, umode_t mode)
++#elif defined(HAVE_IOPS_MKDIR_IDMAP)
++zpl_mkdir(struct mnt_idmap *user_ns, struct inode *dir,
++    struct dentry *dentry, umode_t mode)
+ #else
+ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+ #endif
+@@ -338,8 +350,8 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	znode_t *zp;
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_IOPS_MKDIR_USERNS
+-	zuserns_t *user_ns = kcred->user_ns;
++#if !(defined(HAVE_IOPS_MKDIR_USERNS) || defined(HAVE_IOPS_MKDIR_IDMAP))
++	zidmap_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -402,6 +414,10 @@ static int
+ zpl_getattr_impl(struct user_namespace *user_ns,
+     const struct path *path, struct kstat *stat, u32 request_mask,
+     unsigned int query_flags)
++#elif defined(HAVE_IDMAP_IOPS_GETATTR)
++zpl_getattr_impl(struct mnt_idmap *user_ns,
++    const struct path *path, struct kstat *stat, u32 request_mask,
++    unsigned int query_flags)
+ #else
+ zpl_getattr_impl(const struct path *path, struct kstat *stat, u32 request_mask,
+     unsigned int query_flags)
+@@ -418,7 +434,7 @@ zpl_getattr_impl(const struct path *path, struct kstat *stat, u32 request_mask,
+ 	 * XXX query_flags currently ignored.
+ 	 */
+ 
+-#ifdef HAVE_USERNS_IOPS_GETATTR
++#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
+ 	error = -zfs_getattr_fast(user_ns, ip, stat);
+ #else
+ 	error = -zfs_getattr_fast(kcred->user_ns, ip, stat);
+@@ -457,9 +473,12 @@ zpl_getattr_impl(const struct path *path, struct kstat *stat, u32 request_mask,
+ ZPL_GETATTR_WRAPPER(zpl_getattr);
+ 
+ static int
+-#ifdef HAVE_SETATTR_PREPARE_USERNS
++#ifdef HAVE_USERNS_IOPS_SETATTR
+ zpl_setattr(struct user_namespace *user_ns, struct dentry *dentry,
+     struct iattr *ia)
++#elif defined(HAVE_IDMAP_IOPS_SETATTR)
++zpl_setattr(struct mnt_idmap *user_ns, struct dentry *dentry,
++    struct iattr *ia)
+ #else
+ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ #endif
+@@ -472,8 +491,10 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ 
+ #ifdef HAVE_SETATTR_PREPARE_USERNS
+ 	error = zpl_setattr_prepare(user_ns, dentry, ia);
++#elif defined(HAVE_SETATTR_PREPARE_IDMAP)
++	error = zpl_setattr_prepare(user_ns, dentry, ia);
+ #else
+-	error = zpl_setattr_prepare(kcred->user_ns, dentry, ia);
++	error = zpl_setattr_prepare(zfs_init_idmap, dentry, ia);
+ #endif
+ 	if (error)
+ 		return (error);
+@@ -505,10 +526,12 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ 		ip->i_atime = zpl_inode_timestamp_truncate(ia->ia_atime, ip);
+ 
+ 	cookie = spl_fstrans_mark();
+-#ifdef HAVE_SETATTR_PREPARE_USERNS
++#ifdef HAVE_USERNS_IOPS_SETATTR
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, user_ns);
++#elif defined(HAVE_IDMAP_IOPS_SETATTR)
+ 	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, user_ns);
+ #else
+-	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, kcred->user_ns);
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, zfs_init_idmap);
+ #endif
+ 	if (!error && (ia->ia_valid & ATTR_MODE))
+ 		error = zpl_chmod_acl(ip);
+@@ -526,6 +549,10 @@ static int
+ zpl_rename2(struct user_namespace *user_ns, struct inode *sdip,
+     struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry,
+     unsigned int flags)
++#elif defined(HAVE_IOPS_RENAME_IDMAP)
++zpl_rename2(struct mnt_idmap *user_ns, struct inode *sdip,
++    struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry,
++    unsigned int flags)
+ #else
+ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+     struct inode *tdip, struct dentry *tdentry, unsigned int flags)
+@@ -534,8 +561,8 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	cred_t *cr = CRED();
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_IOPS_RENAME_USERNS
+-	zuserns_t *user_ns = kcred->user_ns;
++#if !(defined(HAVE_IOPS_RENAME_USERNS) || defined(HAVE_IOPS_RENAME_IDMAP))
++	zidmap_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	/* We don't have renameat2(2) support */
+@@ -553,7 +580,10 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	return (error);
+ }
+ 
+-#if !defined(HAVE_RENAME_WANTS_FLAGS) && !defined(HAVE_IOPS_RENAME_USERNS)
++#if !defined(HAVE_IOPS_RENAME_USERNS) && \
++	!defined(HAVE_RENAME_WANTS_FLAGS) && \
++	!defined(HAVE_RENAME2) && \
++	!defined(HAVE_IOPS_RENAME_IDMAP)
+ static int
+ zpl_rename(struct inode *sdip, struct dentry *sdentry,
+     struct inode *tdip, struct dentry *tdentry)
+@@ -566,6 +596,9 @@ static int
+ #ifdef HAVE_IOPS_SYMLINK_USERNS
+ zpl_symlink(struct user_namespace *user_ns, struct inode *dir,
+     struct dentry *dentry, const char *name)
++#elif defined(HAVE_IOPS_SYMLINK_IDMAP)
++zpl_symlink(struct mnt_idmap *user_ns, struct inode *dir,
++    struct dentry *dentry, const char *name)
+ #else
+ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+ #endif
+@@ -575,8 +608,8 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+ 	znode_t *zp;
+ 	int error;
+ 	fstrans_cookie_t cookie;
+-#ifndef HAVE_IOPS_SYMLINK_USERNS
+-	zuserns_t *user_ns = kcred->user_ns;
++#if !(defined(HAVE_IOPS_SYMLINK_USERNS) || defined(HAVE_IOPS_SYMLINK_IDMAP))
++	zidmap_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -787,6 +820,8 @@ const struct inode_operations zpl_dir_inode_operations = {
+ 	.mknod		= zpl_mknod,
+ #if defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS)
+ 	.rename		= zpl_rename2,
++#elif defined(HAVE_IOPS_RENAME_IDMAP)
++	.rename		= zpl_rename2,
+ #else
+ 	.rename		= zpl_rename,
+ #endif
+diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c
+index b377651bad4..bffc86b3006 100644
+--- a/module/os/linux/zfs/zpl_xattr.c
++++ b/module/os/linux/zfs/zpl_xattr.c
+@@ -496,7 +496,7 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
+ 		vap->va_gid = crgetgid(cr);
+ 
+ 		error = -zfs_create(dxzp, (char *)name, vap, 0, 0644, &xzp,
+-		    cr, 0, NULL, kcred->user_ns);
++		    cr, ATTR_NOACLCHECK, NULL, zfs_init_idmap);
+ 		if (error)
+ 			goto out;
+ 	}
+@@ -725,7 +725,7 @@ __zpl_xattr_user_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_user_get);
+ 
+ static int
+-__zpl_xattr_user_set(struct user_namespace *user_ns,
++__zpl_xattr_user_set(zidmap_t *user_ns,
+     struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+@@ -796,7 +796,7 @@ __zpl_xattr_trusted_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_trusted_get);
+ 
+ static int
+-__zpl_xattr_trusted_set(struct user_namespace *user_ns,
++__zpl_xattr_trusted_set(zidmap_t *user_ns,
+     struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+@@ -867,7 +867,7 @@ __zpl_xattr_security_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_security_get);
+ 
+ static int
+-__zpl_xattr_security_set(struct user_namespace *user_ns,
++__zpl_xattr_security_set(zidmap_t *user_ns,
+     struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+@@ -1010,6 +1010,9 @@ int
+ #ifdef HAVE_SET_ACL_USERNS
+ zpl_set_acl(struct user_namespace *userns, struct inode *ip,
+     struct posix_acl *acl, int type)
++#elif defined(HAVE_SET_ACL_IDMAP_DENTRY)
++zpl_set_acl(struct mnt_idmap *userns, struct dentry *dentry,
++    struct posix_acl *acl, int type)
+ #elif defined(HAVE_SET_ACL_USERNS_DENTRY_ARG2)
+ zpl_set_acl(struct user_namespace *userns, struct dentry *dentry,
+     struct posix_acl *acl, int type)
+@@ -1019,6 +1022,8 @@ zpl_set_acl(struct inode *ip, struct posix_acl *acl, int type)
+ {
+ #ifdef HAVE_SET_ACL_USERNS_DENTRY_ARG2
+ 	return (zpl_set_acl_impl(d_inode(dentry), acl, type));
++#elif defined(HAVE_SET_ACL_IDMAP_DENTRY)
++	return (zpl_set_acl_impl(d_inode(dentry), acl, type));
+ #else
+ 	return (zpl_set_acl_impl(ip, acl, type));
+ #endif /* HAVE_SET_ACL_USERNS_DENTRY_ARG2 */
+@@ -1262,7 +1267,7 @@ __zpl_xattr_acl_get_default(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_acl_get_default);
+ 
+ static int
+-__zpl_xattr_acl_set_access(struct user_namespace *mnt_ns,
++__zpl_xattr_acl_set_access(zidmap_t *mnt_ns,
+     struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+@@ -1277,12 +1282,12 @@ __zpl_xattr_acl_set_access(struct user_namespace *mnt_ns,
+ 	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIX)
+ 		return (-EOPNOTSUPP);
+ 
+-#if defined(HAVE_XATTR_SET_USERNS)
++#if defined(HAVE_XATTR_SET_USERNS) || defined(HAVE_XATTR_SET_IDMAP)
+ 	if (!zpl_inode_owner_or_capable(mnt_ns, ip))
+ 		return (-EPERM);
+ #else
+ 	(void) mnt_ns;
+-	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
++	if (!zpl_inode_owner_or_capable(zfs_init_idmap, ip))
+ 		return (-EPERM);
+ #endif
+ 
+@@ -1308,7 +1313,7 @@ __zpl_xattr_acl_set_access(struct user_namespace *mnt_ns,
+ ZPL_XATTR_SET_WRAPPER(zpl_xattr_acl_set_access);
+ 
+ static int
+-__zpl_xattr_acl_set_default(struct user_namespace *mnt_ns,
++__zpl_xattr_acl_set_default(zidmap_t *mnt_ns,
+     struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+@@ -1323,12 +1328,12 @@ __zpl_xattr_acl_set_default(struct user_namespace *mnt_ns,
+ 	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIX)
+ 		return (-EOPNOTSUPP);
+ 
+-#if defined(HAVE_XATTR_SET_USERNS)
++#if defined(HAVE_XATTR_SET_USERNS) || defined(HAVE_XATTR_SET_IDMAP)
+ 	if (!zpl_inode_owner_or_capable(mnt_ns, ip))
+ 		return (-EPERM);
+ #else
+ 	(void) mnt_ns;
+-	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
++	if (!zpl_inode_owner_or_capable(zfs_init_idmap, ip))
+ 		return (-EPERM);
+ #endif
+ 
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index 755bbc6cd98..db5aefbe104 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -386,7 +386,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 
+ #if defined(__linux__)
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, &vsec, kcred->user_ns);
++		    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);
+@@ -421,7 +421,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 		}
+ #if defined(__linux__)
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, &vsec, kcred->user_ns);
++		    &zp, kcred, vflg, &vsec, zfs_init_idmap);
+ #else
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, &vsec, NULL);
+@@ -536,7 +536,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 
+ #if defined(__linux__)
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, NULL, kcred->user_ns);
++		    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);
+@@ -558,7 +558,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 
+ #if defined(__linux__)
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, NULL, kcred->user_ns);
++		    &zp, kcred, vflg, NULL, zfs_init_idmap);
+ #else
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, NULL, NULL);
+@@ -573,7 +573,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		link = name + strlen(name) + 1;
+ #if defined(__linux__)
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+-		    link, &zp, kcred, vflg, kcred->user_ns);
++		    link, &zp, kcred, vflg, zfs_init_idmap);
+ #else
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+ 		    link, &zp, kcred, vflg, NULL);
+@@ -691,7 +691,7 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 
+ #if defined(__linux__)
+ 	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
+-	    kcred->user_ns);
++	    zfs_init_idmap);
+ #else
+ 	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
+ 	    NULL);
+@@ -890,7 +890,7 @@ zfs_replay_setattr(void *arg1, void *arg2, boolean_t byteswap)
+ 	    lr->lr_uid, lr->lr_gid);
+ 
+ #if defined(__linux__)
+-	error = zfs_setattr(zp, vap, 0, kcred, kcred->user_ns);
++	error = zfs_setattr(zp, vap, 0, kcred, zfs_init_idmap);
+ #else
+ 	error = zfs_setattr(zp, vap, 0, kcred, NULL);
+ #endif
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index 2b6d35282a0..7590a985266 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -167,14 +167,14 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 	if (flag & V_ACE_MASK)
+ #if defined(__linux__)
+ 		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+-		    kcred->user_ns);
++		    zfs_init_idmap);
+ #else
+ 		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+ 		    NULL);
+ #endif
+ 	else
+ #if defined(__linux__)
+-		error = zfs_zaccess_rwx(zp, mode, flag, cr, kcred->user_ns);
++		error = zfs_zaccess_rwx(zp, mode, flag, cr, zfs_init_idmap);
+ #else
+ 		error = zfs_zaccess_rwx(zp, mode, flag, cr, NULL);
+ #endif
diff --git a/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-writepage_t-typedef-change.patch b/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-writepage_t-typedef-change.patch
new file mode 100644
index 0000000..948dcfd
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-6.3-compat-writepage_t-typedef-change.patch
@@ -0,0 +1,170 @@
+From 87b91626330696dc57d3f1de6d6659b1b71fed1b Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 13:04:16 -0500
+Subject: [PATCH 1/2] Backport "Linux 6.3 compat: writepage_t first arg struct
+ folio*"
+
+---
+ config/kernel-writepage_t.m4   | 26 ++++++++++++++++++++++++++
+ config/kernel.m4               |  2 ++
+ module/os/linux/zfs/zpl_file.c | 17 +++++++++++++++++
+ 3 files changed, 45 insertions(+)
+ create mode 100644 config/kernel-writepage_t.m4
+
+diff --git a/config/kernel-writepage_t.m4 b/config/kernel-writepage_t.m4
+new file mode 100644
+index 00000000000..3a0cffd9857
+--- /dev/null
++++ b/config/kernel-writepage_t.m4
+@@ -0,0 +1,26 @@
++AC_DEFUN([ZFS_AC_KERNEL_SRC_WRITEPAGE_T], [
++	dnl #
++	dnl # 6.3 API change
++	dnl # The writepage_t function type now has its first argument as
++	dnl # struct folio* instead of struct page*
++	dnl #
++	ZFS_LINUX_TEST_SRC([writepage_t_folio], [
++		#include <linux/writeback.h>
++		int putpage(struct folio *folio,
++		    struct writeback_control *wbc, void *data)
++		{ return 0; }
++		writepage_t func = putpage;
++	],[])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_WRITEPAGE_T], [
++	AC_MSG_CHECKING([whether int (*writepage_t)() takes struct folio*])
++	ZFS_LINUX_TEST_RESULT([writepage_t_folio], [
++		AC_MSG_RESULT(yes)
++		AC_DEFINE(HAVE_WRITEPAGE_T_FOLIO, 1,
++		   [int (*writepage_t)() takes struct folio*])
++	],[
++		AC_MSG_RESULT(no)
++	])
++])
++
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index b376a151694..b64cca7a1f9 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -148,6 +148,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC
+ 	ZFS_AC_KERNEL_SRC_IDMAP_MNT_API
+ 	ZFS_AC_KERNEL_SRC_IATTR_VFSID
++	ZFS_AC_KERNEL_SRC_WRITEPAGE_T
+ 
+ 	AC_MSG_CHECKING([for available kernel interfaces])
+ 	ZFS_LINUX_TEST_COMPILE_ALL([kabi])
+@@ -269,6 +270,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC
+ 	ZFS_AC_KERNEL_IDMAP_MNT_API
+ 	ZFS_AC_KERNEL_IATTR_VFSID
++	ZFS_AC_KERNEL_WRITEPAGE_T
+ ])
+ 
+ dnl #
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index f124599393f..2bdb53579ae 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -701,6 +701,15 @@ zpl_putpage(struct page *pp, struct writeback_control *wbc, void *data)
+ 	return (0);
+ }
+ 
++#ifdef HAVE_WRITEPAGE_T_FOLIO
++static int
++zpl_putfolio(struct folio *pp, struct writeback_control *wbc, void *data)
++{
++	(void) zpl_putpage(&pp->page, wbc, data);
++	return (0);
++}
++#endif
++
+ static int
+ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ {
+@@ -723,7 +732,11 @@ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ 	 * and then we commit it all in one go.
+ 	 */
+ 	wbc->sync_mode = WB_SYNC_NONE;
++#ifdef HAVE_WRITEPAGE_T_FOLIO
++	result = write_cache_pages(mapping, wbc, zpl_putfolio, mapping);
++#else
+ 	result = write_cache_pages(mapping, wbc, zpl_putpage, mapping);
++#endif
+ 	if (sync_mode != wbc->sync_mode) {
+ 		ZPL_ENTER(zfsvfs);
+ 		ZPL_VERIFY_ZP(zp);
+@@ -739,7 +752,11 @@ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ 		 * details). That being said, this is a no-op in most cases.
+ 		 */
+ 		wbc->sync_mode = sync_mode;
++#ifdef HAVE_WRITEPAGE_T_FOLIO
++		result = write_cache_pages(mapping, wbc, zpl_putfolio, mapping);
++#else
+ 		result = write_cache_pages(mapping, wbc, zpl_putpage, mapping);
++#endif
+ 	}
+ 	return (result);
+ }
+
+From 04e8c37ff2490d1933fcb7f5c42da3e11b50bf7c Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 13:11:02 -0500
+Subject: [PATCH 2/2] Backport "Add zpl_write_cache_pages()"
+
+---
+ module/os/linux/zfs/zpl_file.c | 26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index 2bdb53579ae..30013c38d8e 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -710,6 +710,20 @@ zpl_putfolio(struct folio *pp, struct writeback_control *wbc, void *data)
+ }
+ #endif
+ 
++static inline int
++zpl_write_cache_pages(struct address_space *mapping,
++    struct writeback_control *wbc, void *data)
++{
++	int result;
++
++#ifdef HAVE_WRITEPAGE_T_FOLIO
++	result = write_cache_pages(mapping, wbc, zpl_putfolio, data);
++#else
++	result = write_cache_pages(mapping, wbc, zpl_putpage, data);
++#endif
++	return (result);
++}
++
+ static int
+ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ {
+@@ -732,11 +746,7 @@ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ 	 * and then we commit it all in one go.
+ 	 */
+ 	wbc->sync_mode = WB_SYNC_NONE;
+-#ifdef HAVE_WRITEPAGE_T_FOLIO
+-	result = write_cache_pages(mapping, wbc, zpl_putfolio, mapping);
+-#else
+-	result = write_cache_pages(mapping, wbc, zpl_putpage, mapping);
+-#endif
++	result = zpl_write_cache_pages(mapping, wbc, mapping);
+ 	if (sync_mode != wbc->sync_mode) {
+ 		ZPL_ENTER(zfsvfs);
+ 		ZPL_VERIFY_ZP(zp);
+@@ -752,11 +762,7 @@ zpl_writepages(struct address_space *mapping, struct writeback_control *wbc)
+ 		 * details). That being said, this is a no-op in most cases.
+ 		 */
+ 		wbc->sync_mode = sync_mode;
+-#ifdef HAVE_WRITEPAGE_T_FOLIO
+-		result = write_cache_pages(mapping, wbc, zpl_putfolio, mapping);
+-#else
+-		result = write_cache_pages(mapping, wbc, zpl_putpage, mapping);
+-#endif
++		result = zpl_write_cache_pages(mapping, wbc, mapping);
+ 	}
+ 	return (result);
+ }
diff --git a/sys-fs/zfs-kmod/files/2.1.11-expose-additional-attributes.patch b/sys-fs/zfs-kmod/files/2.1.11-expose-additional-attributes.patch
new file mode 100644
index 0000000..79838bc
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-expose-additional-attributes.patch
@@ -0,0 +1,1272 @@
+From 2f38cb2c0640a7d53062b57aab3bf8620fc49237 Mon Sep 17 00:00:00 2001
+From: Umer Saleem <usaleem@ixsystems.com>
+Date: Fri, 18 Feb 2022 21:21:20 +0500
+Subject: [PATCH] Expose additional file level attributes
+
+ZFS allows to update and retrieve additional file level attributes for
+FreeBSD. This commit allows additional file level attributes to be
+updated and retrieved for Linux. These include the flags stored in the
+upper half of z_pflags only.
+
+Two new IOCTLs have been added for this purpose. ZFS_IOC_GETDOSFLAGS
+can be used to retieve the attributes, while ZFS_IOC_SETDOSFLAGS can
+be used to update the attributes.
+
+Attributes that are allowed to be updated include ZFS_IMMUTABLE,
+ZFS_APPENDONLY, ZFS_NOUNLINK, ZFS_ARCHIVE, ZFS_NODUMP, ZFS_SYSTEM,
+ZFS_HIDDEN, ZFS_READONLY, ZFS_REPARSE, ZFS_OFFLINE and ZFS_SPARSE.
+Flags can be or'd together while calling ZFS_IOC_SETDOSFLAGS.
+
+Signed-off-by: Umer Saleem <usaleem@ixsystems.com>
+---
+ configure.ac                                  |   3 +
+ include/sys/fs/zfs.h                          |  35 +++
+ include/sys/zfs_znode.h                       |   2 +-
+ module/os/linux/zfs/zpl_file.c                | 125 +++++++++--
+ tests/runfiles/common.run                     |   2 +-
+ tests/runfiles/freebsd.run                    |   4 -
+ tests/runfiles/linux.run                      |   4 +
+ tests/zfs-tests/cmd/Makefile.am               |   2 +
+ .../cmd/read_dos_attributes/.gitignore        |   1 +
+ .../cmd/read_dos_attributes/Makefile.am       |   6 +
+ .../read_dos_attributes/read_dos_attributes.c | 167 +++++++++++++++
+ .../cmd/write_dos_attributes/.gitignore       |   1 +
+ .../cmd/write_dos_attributes/Makefile.am      |   6 +
+ .../write_dos_attributes.c                    | 201 ++++++++++++++++++
+ tests/zfs-tests/include/commands.cfg          |   2 +
+ tests/zfs-tests/tests/functional/Makefile.am  |   1 +
+ .../tests/functional/acl/off/Makefile.am      |   2 -
+ .../tests/functional/acl/off/dosmode.ksh      |  97 +++++----
+ .../acl/off/dosmode_readonly_write.c          |  11 +
+ .../functional/dos_attributes/Makefile.am     |   8 +
+ .../functional/dos_attributes/cleanup.ksh     |  34 +++
+ .../dos_attributes/read_dos_attrs_001.ksh     |  60 ++++++
+ .../tests/functional/dos_attributes/setup.ksh |  35 +++
+ .../dos_attributes/write_dos_attrs_001.ksh    |  61 ++++++
+ 24 files changed, 806 insertions(+), 64 deletions(-)
+ create mode 100644 tests/zfs-tests/cmd/read_dos_attributes/.gitignore
+ create mode 100644 tests/zfs-tests/cmd/read_dos_attributes/Makefile.am
+ create mode 100644 tests/zfs-tests/cmd/read_dos_attributes/read_dos_attributes.c
+ create mode 100644 tests/zfs-tests/cmd/write_dos_attributes/.gitignore
+ create mode 100644 tests/zfs-tests/cmd/write_dos_attributes/Makefile.am
+ create mode 100644 tests/zfs-tests/cmd/write_dos_attributes/write_dos_attributes.c
+ create mode 100644 tests/zfs-tests/tests/functional/dos_attributes/Makefile.am
+ create mode 100755 tests/zfs-tests/tests/functional/dos_attributes/cleanup.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/dos_attributes/read_dos_attrs_001.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/dos_attributes/setup.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/dos_attributes/write_dos_attrs_001.ksh
+
+diff --git a/configure.ac b/configure.ac
+index 7037c06b225..990958bafa1 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -226,12 +226,14 @@ AC_CONFIG_FILES([
+ 	tests/zfs-tests/cmd/randfree_file/Makefile
+ 	tests/zfs-tests/cmd/randwritecomp/Makefile
+ 	tests/zfs-tests/cmd/readmmap/Makefile
++	tests/zfs-tests/cmd/read_dos_attributes/Makefile
+ 	tests/zfs-tests/cmd/rename_dir/Makefile
+ 	tests/zfs-tests/cmd/rm_lnkcnt_zero_file/Makefile
+ 	tests/zfs-tests/cmd/send_doall/Makefile
+ 	tests/zfs-tests/cmd/stride_dd/Makefile
+ 	tests/zfs-tests/cmd/threadsappend/Makefile
+ 	tests/zfs-tests/cmd/user_ns_exec/Makefile
++	tests/zfs-tests/cmd/write_dos_attributes/Makefile
+ 	tests/zfs-tests/cmd/xattrtest/Makefile
+ 	tests/zfs-tests/include/Makefile
+ 	tests/zfs-tests/tests/Makefile
+@@ -333,6 +335,7 @@ AC_CONFIG_FILES([
+ 	tests/zfs-tests/tests/functional/deadman/Makefile
+ 	tests/zfs-tests/tests/functional/delegate/Makefile
+ 	tests/zfs-tests/tests/functional/devices/Makefile
++	tests/zfs-tests/tests/functional/dos_attributes/Makefile
+ 	tests/zfs-tests/tests/functional/events/Makefile
+ 	tests/zfs-tests/tests/functional/exec/Makefile
+ 	tests/zfs-tests/tests/functional/fallocate/Makefile
+diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
+index 6e85c4b7b39..ab29b4e2ef3 100644
+--- a/include/sys/fs/zfs.h
++++ b/include/sys/fs/zfs.h
+@@ -1459,6 +1459,41 @@ typedef enum zfs_ioc {
+  */
+ #define	BLKZNAME		_IOR(0x12, 125, char[ZFS_MAX_DATASET_NAME_LEN])
+ 
++#ifdef __linux__
++
++/*
++ * IOCTLs to update and retrieve additional file level attributes on
++ * Linux.
++ */
++#define	ZFS_IOC_GETDOSFLAGS	_IOR(0x83, 1, uint64_t)
++#define	ZFS_IOC_SETDOSFLAGS	_IOW(0x83, 2, uint64_t)
++
++/*
++ * Additional file level attributes, that are stored
++ * in the upper half of z_pflags
++ */
++#define	ZFS_READONLY		0x0000000100000000ull
++#define	ZFS_HIDDEN		0x0000000200000000ull
++#define	ZFS_SYSTEM		0x0000000400000000ull
++#define	ZFS_ARCHIVE		0x0000000800000000ull
++#define	ZFS_IMMUTABLE		0x0000001000000000ull
++#define	ZFS_NOUNLINK		0x0000002000000000ull
++#define	ZFS_APPENDONLY		0x0000004000000000ull
++#define	ZFS_NODUMP		0x0000008000000000ull
++#define	ZFS_OPAQUE		0x0000010000000000ull
++#define	ZFS_AV_QUARANTINED	0x0000020000000000ull
++#define	ZFS_AV_MODIFIED		0x0000040000000000ull
++#define	ZFS_REPARSE		0x0000080000000000ull
++#define	ZFS_OFFLINE		0x0000100000000000ull
++#define	ZFS_SPARSE		0x0000200000000000ull
++
++#define	ZFS_DOS_FL_USER_VISIBLE	(ZFS_IMMUTABLE | ZFS_APPENDONLY | \
++	    ZFS_NOUNLINK | ZFS_ARCHIVE | ZFS_NODUMP | ZFS_SYSTEM | \
++	    ZFS_HIDDEN | ZFS_READONLY | ZFS_REPARSE | ZFS_OFFLINE | \
++	    ZFS_SPARSE)
++
++#endif
++
+ /*
+  * ZFS-specific error codes used for returning descriptive errors
+  * to the userland through zfs ioctls.
+diff --git a/include/sys/zfs_znode.h b/include/sys/zfs_znode.h
+index e20c18cc2b6..127fd8736ff 100644
+--- a/include/sys/zfs_znode.h
++++ b/include/sys/zfs_znode.h
+@@ -37,7 +37,7 @@ extern "C" {
+ 
+ /*
+  * Additional file level attributes, that are stored
+- * in the upper half of zp_flags
++ * in the upper half of z_pflags
+  */
+ #define	ZFS_READONLY		0x0000000100000000ull
+ #define	ZFS_HIDDEN		0x0000000200000000ull
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index 982b424197e..f78e50262af 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -905,21 +905,24 @@ __zpl_ioctl_setflags(struct inode *ip, uint32_t ioctl_flags, xvattr_t *xva)
+ 	xva_init(xva);
+ 	xoap = xva_getxoptattr(xva);
+ 
+-	XVA_SET_REQ(xva, XAT_IMMUTABLE);
+-	if (ioctl_flags & FS_IMMUTABLE_FL)
+-		xoap->xoa_immutable = B_TRUE;
+-
+-	XVA_SET_REQ(xva, XAT_APPENDONLY);
+-	if (ioctl_flags & FS_APPEND_FL)
+-		xoap->xoa_appendonly = B_TRUE;
+-
+-	XVA_SET_REQ(xva, XAT_NODUMP);
+-	if (ioctl_flags & FS_NODUMP_FL)
+-		xoap->xoa_nodump = B_TRUE;
+-
+-	XVA_SET_REQ(xva, XAT_PROJINHERIT);
+-	if (ioctl_flags & ZFS_PROJINHERIT_FL)
+-		xoap->xoa_projinherit = B_TRUE;
++#define	FLAG_CHANGE(iflag, zflag, xflag, xfield)	do {	\
++	if (((ioctl_flags & (iflag)) && !(zfs_flags & (zflag))) ||	\
++	    ((zfs_flags & (zflag)) && !(ioctl_flags & (iflag)))) {	\
++		XVA_SET_REQ(xva, (xflag));	\
++		(xfield) = ((ioctl_flags & (iflag)) != 0);	\
++	}	\
++} while (0)
++
++	FLAG_CHANGE(FS_IMMUTABLE_FL, ZFS_IMMUTABLE, XAT_IMMUTABLE,
++	    xoap->xoa_immutable);
++	FLAG_CHANGE(FS_APPEND_FL, ZFS_APPENDONLY, XAT_APPENDONLY,
++	    xoap->xoa_appendonly);
++	FLAG_CHANGE(FS_NODUMP_FL, ZFS_NODUMP, XAT_NODUMP,
++	    xoap->xoa_nodump);
++	FLAG_CHANGE(ZFS_PROJINHERIT_FL, ZFS_PROJINHERIT, XAT_PROJINHERIT,
++	    xoap->xoa_projinherit);
++
++#undef	FLAG_CHANGE
+ 
+ 	return (0);
+ }
+@@ -998,6 +1001,94 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
+ 	return (err);
+ }
+ 
++/*
++ * Expose Additional File Level Attributes of ZFS.
++ */
++static int
++zpl_ioctl_getdosflags(struct file *filp, void __user *arg)
++{
++	struct inode *ip = file_inode(filp);
++	uint64_t dosflags = ITOZ(ip)->z_pflags;
++	dosflags &= ZFS_DOS_FL_USER_VISIBLE;
++	int err = copy_to_user(arg, &dosflags, sizeof (dosflags));
++
++	return (err);
++}
++
++static int
++__zpl_ioctl_setdosflags(struct inode *ip, uint64_t ioctl_flags, xvattr_t *xva)
++{
++	uint64_t zfs_flags = ITOZ(ip)->z_pflags;
++	xoptattr_t *xoap;
++
++	if (ioctl_flags & (~ZFS_DOS_FL_USER_VISIBLE))
++		return (-EOPNOTSUPP);
++
++	if ((fchange(ioctl_flags, zfs_flags, ZFS_IMMUTABLE, ZFS_IMMUTABLE) ||
++	    fchange(ioctl_flags, zfs_flags, ZFS_APPENDONLY, ZFS_APPENDONLY)) &&
++	    !capable(CAP_LINUX_IMMUTABLE))
++		return (-EPERM);
++
++	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
++		return (-EACCES);
++
++	xva_init(xva);
++	xoap = xva_getxoptattr(xva);
++
++#define	FLAG_CHANGE(iflag, xflag, xfield)	do {	\
++	if (((ioctl_flags & (iflag)) && !(zfs_flags & (iflag))) ||	\
++	    ((zfs_flags & (iflag)) && !(ioctl_flags & (iflag)))) {	\
++		XVA_SET_REQ(xva, (xflag));	\
++		(xfield) = ((ioctl_flags & (iflag)) != 0);	\
++	}	\
++} while (0)
++
++	FLAG_CHANGE(ZFS_IMMUTABLE, XAT_IMMUTABLE, xoap->xoa_immutable);
++	FLAG_CHANGE(ZFS_APPENDONLY, XAT_APPENDONLY, xoap->xoa_appendonly);
++	FLAG_CHANGE(ZFS_NODUMP, XAT_NODUMP, xoap->xoa_nodump);
++	FLAG_CHANGE(ZFS_READONLY, XAT_READONLY, xoap->xoa_readonly);
++	FLAG_CHANGE(ZFS_HIDDEN, XAT_HIDDEN, xoap->xoa_hidden);
++	FLAG_CHANGE(ZFS_SYSTEM, XAT_SYSTEM, xoap->xoa_system);
++	FLAG_CHANGE(ZFS_ARCHIVE, XAT_ARCHIVE, xoap->xoa_archive);
++	FLAG_CHANGE(ZFS_NOUNLINK, XAT_NOUNLINK, xoap->xoa_nounlink);
++	FLAG_CHANGE(ZFS_REPARSE, XAT_REPARSE, xoap->xoa_reparse);
++	FLAG_CHANGE(ZFS_OFFLINE, XAT_OFFLINE, xoap->xoa_offline);
++	FLAG_CHANGE(ZFS_SPARSE, XAT_SPARSE, xoap->xoa_sparse);
++
++#undef	FLAG_CHANGE
++
++	return (0);
++}
++
++/*
++ * Set Additional File Level Attributes of ZFS.
++ */
++static int
++zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
++{
++	struct inode *ip = file_inode(filp);
++	uint64_t dosflags;
++	cred_t *cr = CRED();
++	xvattr_t xva;
++	int err;
++	fstrans_cookie_t cookie;
++
++	if (copy_from_user(&dosflags, arg, sizeof (dosflags)))
++		return (-EFAULT);
++
++	err = __zpl_ioctl_setdosflags(ip, dosflags, &xva);
++	if (err)
++		return (err);
++
++	crhold(cr);
++	cookie = spl_fstrans_mark();
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr);
++	spl_fstrans_unmark(cookie);
++	crfree(cr);
++
++	return (err);
++}
++
+ static long
+ zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ {
+@@ -1012,6 +1103,10 @@ zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ 		return (zpl_ioctl_getxattr(filp, (void *)arg));
+ 	case ZFS_IOC_FSSETXATTR:
+ 		return (zpl_ioctl_setxattr(filp, (void *)arg));
++	case ZFS_IOC_GETDOSFLAGS:
++		return (zpl_ioctl_getdosflags(filp, (void *)arg));
++	case ZFS_IOC_SETDOSFLAGS:
++		return (zpl_ioctl_setdosflags(filp, (void *)arg));
+ 	default:
+ 		return (-ENOTTY);
+ 	}
+diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
+index 9f11284ad62..eb0d451351f 100644
+--- a/tests/runfiles/common.run
++++ b/tests/runfiles/common.run
+@@ -29,7 +29,7 @@ outputdir = /var/tmp/test_results
+ tags = ['functional']
+ 
+ [tests/functional/acl/off]
+-tests = ['posixmode']
++tests = ['dosmode', 'posixmode']
+ tags = ['functional', 'acl']
+ 
+ [tests/functional/alloc_class]
+diff --git a/tests/runfiles/freebsd.run b/tests/runfiles/freebsd.run
+index 153b204b493..c7ca1d769fc 100644
+--- a/tests/runfiles/freebsd.run
++++ b/tests/runfiles/freebsd.run
+@@ -22,10 +22,6 @@ failsafe = callbacks/zfs_failsafe
+ outputdir = /var/tmp/test_results
+ tags = ['functional']
+ 
+-[tests/functional/acl/off:FreeBSD]
+-tests = ['dosmode']
+-tags = ['functional', 'acl']
+-
+ [tests/functional/cli_root/zfs_jail:FreeBSD]
+ tests = ['zfs_jail_001_pos']
+ tags = ['functional', 'cli_root', 'zfs_jail']
+diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
+index 7186d5d67ac..8412a7ea5a9 100644
+--- a/tests/runfiles/linux.run
++++ b/tests/runfiles/linux.run
+@@ -149,6 +149,10 @@ tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
+     'projecttree_001_pos', 'projecttree_002_pos', 'projecttree_003_neg']
+ tags = ['functional', 'projectquota']
+ 
++[tests/functional/dos_attributes:Linux]
++tests = ['read_dos_attrs_001', 'write_dos_attrs_001']
++tags = ['functional', 'dos_attributes']
++
+ [tests/functional/rsend:Linux]
+ tests = ['send_realloc_dnode_size', 'send_encrypted_files']
+ tags = ['functional', 'rsend']
+diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am
+index 2470397a90a..88d8c8cf08b 100644
+--- a/tests/zfs-tests/cmd/Makefile.am
++++ b/tests/zfs-tests/cmd/Makefile.am
+@@ -34,6 +34,8 @@ if BUILD_LINUX
+ SUBDIRS += \
+ 	getversion \
+ 	randfree_file \
++	read_dos_attributes \
+ 	user_ns_exec \
++	write_dos_attributes \
+ 	xattrtest
+ endif
+diff --git a/tests/zfs-tests/cmd/read_dos_attributes/.gitignore b/tests/zfs-tests/cmd/read_dos_attributes/.gitignore
+new file mode 100644
+index 00000000000..52584e4a738
+--- /dev/null
++++ b/tests/zfs-tests/cmd/read_dos_attributes/.gitignore
+@@ -0,0 +1 @@
++/read_dos_attributes
+diff --git a/tests/zfs-tests/cmd/read_dos_attributes/Makefile.am b/tests/zfs-tests/cmd/read_dos_attributes/Makefile.am
+new file mode 100644
+index 00000000000..69412f05f65
+--- /dev/null
++++ b/tests/zfs-tests/cmd/read_dos_attributes/Makefile.am
+@@ -0,0 +1,6 @@
++include $(top_srcdir)/config/Rules.am
++
++pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
++
++pkgexec_PROGRAMS = read_dos_attributes
++read_dos_attributes_SOURCES = read_dos_attributes.c
+diff --git a/tests/zfs-tests/cmd/read_dos_attributes/read_dos_attributes.c b/tests/zfs-tests/cmd/read_dos_attributes/read_dos_attributes.c
+new file mode 100644
+index 00000000000..ed0906c36a6
+--- /dev/null
++++ b/tests/zfs-tests/cmd/read_dos_attributes/read_dos_attributes.c
+@@ -0,0 +1,167 @@
++/*
++ * This file and its contents are supplied under the terms of the
++ * Common Development and Distribution License ("CDDL"), version 1.0.
++ * You may only use this file in accordance with the terms of version
++ * 1.0 of the CDDL.
++ *
++ * A full copy of the text of the CDDL should have accompanied this
++ * source.  A copy of the CDDL is also available via the Internet at
++ * http://www.illumos.org/license/CDDL.
++ */
++
++/*
++ * Copyright 2022 iXsystems, Inc.
++ */
++
++/*
++ * FreeBSD allows to update and retreive additional file level attributes.
++ * For Linux, two IOCTLs have been added to update and retrieve additional
++ * level attributes.
++ *
++ * This application reads additional file level attributes on a given
++ * file and prints FreeBSD keywords that map to respective attributes.
++ *
++ * Usage: 'read_dos_attributes filepath'
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <err.h>
++#include <sys/fs/zfs.h>
++#include <string.h>
++
++#define	SU_ARCH_SHORT				"arch"
++#define	SU_ARCH_FULL				"archived"
++#define	SU_NODUMP				"nodump"
++#define	SU_APPEND_SHORT				"sappnd"
++#define	SU_APPEND_FULL				"sappend"
++#define	SU_IMMUTABLE				"schg"
++#define	SU_IMMUTABLE_SHORT			"schange"
++#define	SU_IMMUTABLE_FULL			"simmutable"
++#define	SU_UNLINK_SHORT				"sunlnk"
++#define	SU_UNLINK_FULL				"sunlink"
++#define	U_APPEND_SHORT				"uappnd"
++#define	U_APPEND_FULL				"uappend"
++#define	U_ARCH_SHORT				"uarch"
++#define	U_ARCH_FULL				"uarchive"
++#define	U_IMMUTABLE				"uchg"
++#define	U_IMMUTABLE_SHORT			"uchange"
++#define	U_IMMUTABLE_FULL			"uimmutable"
++#define	U_HIDDEN_SHORT				"hidden"
++#define	U_HIDDEN_FULL				"uhidden"
++#define	U_OFFLINE_SHORT				"offline"
++#define	U_OFFLINE_FULL				"uoffline"
++#define	U_RDONLY				"rdonly"
++#define	U_RDONLY_SHORT				"urdonly"
++#define	U_RDONLY_FULL				"readonly"
++#define	U_SPARSE_SHORT				"sparse"
++#define	U_SPARSE_FULL				"usparse"
++#define	U_SYSTEM_SHORT				"system"
++#define	U_SYSTEM_FULL				"usystem"
++#define	U_REPARSE_SHORT				"reparse"
++#define	U_REPARSE_FULL				"ureparse"
++#define	U_UNLINK_SHORT				"uunlnk"
++#define	U_UNLINK_FULL				"uunlink"
++#define	UNSET_NODUMP				"dump"
++
++#define	NO_ATTRIBUTE				"-"
++
++#define	SEPARATOR				","
++
++#define	BUFFER_SIZE				0x200
++
++void attribute_to_str(uint64_t attributes, char *buff);
++
++void
++attribute_to_str(uint64_t attributes, char *buff)
++{
++	if (attributes & ZFS_ARCHIVE) {
++		strcat(buff, U_ARCH_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_APPENDONLY) {
++		strcat(buff, U_APPEND_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_IMMUTABLE) {
++		strcat(buff, U_IMMUTABLE_FULL);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_NOUNLINK) {
++		strcat(buff, U_UNLINK_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_NODUMP) {
++		strcat(buff, SU_NODUMP);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_HIDDEN) {
++		strcat(buff, U_HIDDEN_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_OFFLINE) {
++		strcat(buff, U_OFFLINE_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_READONLY) {
++		strcat(buff, U_RDONLY);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_SPARSE) {
++		strcat(buff, U_SPARSE_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_SYSTEM) {
++		strcat(buff, U_SYSTEM_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (attributes & ZFS_REPARSE) {
++		strcat(buff, U_REPARSE_SHORT);
++		strcat(buff, SEPARATOR);
++	}
++
++	if (buff[0] == '\0')
++		strcat(buff, NO_ATTRIBUTE);
++	else
++		buff[strlen(buff) - 1] = '\0';
++}
++
++int
++main(int argc, const char * const argv[])
++{
++	if (argc != 2)
++		errx(EXIT_FAILURE, "Usage: %s filepath", argv[0]);
++
++	int fd = open(argv[1], O_RDWR | O_APPEND);
++	if (fd < 0)
++		err(EXIT_FAILURE, "Failed to open %s", argv[1]);
++
++	uint64_t dosflags = 0;
++	if (ioctl(fd, ZFS_IOC_GETDOSFLAGS, &dosflags) == -1)
++		err(EXIT_FAILURE, "ZFS_IOC_GETDOSFLAGS failed");
++
++	(void) close(fd);
++
++	char buffer[BUFFER_SIZE];
++	memset(buffer, 0, BUFFER_SIZE);
++
++	(void) attribute_to_str(dosflags, buffer);
++
++	(void) printf("%s\n", buffer);
++
++	return (EXIT_SUCCESS);
++}
+diff --git a/tests/zfs-tests/cmd/write_dos_attributes/.gitignore b/tests/zfs-tests/cmd/write_dos_attributes/.gitignore
+new file mode 100644
+index 00000000000..f3949ac8282
+--- /dev/null
++++ b/tests/zfs-tests/cmd/write_dos_attributes/.gitignore
+@@ -0,0 +1 @@
++/write_dos_attributes
+diff --git a/tests/zfs-tests/cmd/write_dos_attributes/Makefile.am b/tests/zfs-tests/cmd/write_dos_attributes/Makefile.am
+new file mode 100644
+index 00000000000..c297fd49e0e
+--- /dev/null
++++ b/tests/zfs-tests/cmd/write_dos_attributes/Makefile.am
+@@ -0,0 +1,6 @@
++include $(top_srcdir)/config/Rules.am
++
++pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
++
++pkgexec_PROGRAMS = write_dos_attributes
++write_dos_attributes_SOURCES = write_dos_attributes.c
+diff --git a/tests/zfs-tests/cmd/write_dos_attributes/write_dos_attributes.c b/tests/zfs-tests/cmd/write_dos_attributes/write_dos_attributes.c
+new file mode 100644
+index 00000000000..c373d3b1531
+--- /dev/null
++++ b/tests/zfs-tests/cmd/write_dos_attributes/write_dos_attributes.c
+@@ -0,0 +1,201 @@
++/*
++ * This file and its contents are supplied under the terms of the
++ * Common Development and Distribution License ("CDDL"), version 1.0.
++ * You may only use this file in accordance with the terms of version
++ * 1.0 of the CDDL.
++ *
++ * A full copy of the text of the CDDL should have accompanied this
++ * source.  A copy of the CDDL is also available via the Internet at
++ * http://www.illumos.org/license/CDDL.
++ */
++
++/*
++ * Copyright 2022 iXsystems, Inc.
++ */
++
++/*
++ * FreeBSD allows to update and retreive additional file level attributes.
++ * For Linux, two IOCTLs have been added to update and retrieve additional
++ * level attributes.
++ *
++ * This application updates additional file level attributes on a given
++ * file. FreeBSD keywords can be used to specify the flag.
++ *
++ * Usage: 'write_dos_attributes flag filepath'
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <err.h>
++#include <sys/fs/zfs.h>
++#include <string.h>
++#include <ctype.h>
++
++#define	SU_ARCH_SHORT				"arch"
++#define	SU_ARCH_FULL				"archived"
++#define	SU_NODUMP				"nodump"
++#define	SU_APPEND_SHORT				"sappnd"
++#define	SU_APPEND_FULL				"sappend"
++#define	SU_IMMUTABLE				"schg"
++#define	SU_IMMUTABLE_SHORT			"schange"
++#define	SU_IMMUTABLE_FULL			"simmutable"
++#define	SU_UNLINK_SHORT				"sunlnk"
++#define	SU_UNLINK_FULL				"sunlink"
++#define	U_APPEND_SHORT				"uappnd"
++#define	U_APPEND_FULL				"uappend"
++#define	U_ARCH_SHORT				"uarch"
++#define	U_ARCH_FULL				"uarchive"
++#define	U_IMMUTABLE				"uchg"
++#define	U_IMMUTABLE_SHORT			"uchange"
++#define	U_IMMUTABLE_FULL			"uimmutable"
++#define	U_HIDDEN_SHORT				"hidden"
++#define	U_HIDDEN_FULL				"uhidden"
++#define	U_OFFLINE_SHORT				"offline"
++#define	U_OFFLINE_FULL				"uoffline"
++#define	U_RDONLY				"rdonly"
++#define	U_RDONLY_SHORT				"urdonly"
++#define	U_RDONLY_FULL				"readonly"
++#define	U_SPARSE_SHORT				"sparse"
++#define	U_SPARSE_FULL				"usparse"
++#define	U_SYSTEM_SHORT				"system"
++#define	U_SYSTEM_FULL				"usystem"
++#define	U_REPARSE_SHORT				"reparse"
++#define	U_REPARSE_FULL				"ureparse"
++#define	U_UNLINK_SHORT				"uunlnk"
++#define	U_UNLINK_FULL				"uunlink"
++#define	UNSET_NODUMP				"dump"
++
++#define	IS_NO(s)	(s[0] == 'n' && s[1] == 'o')
++
++uint64_t str_to_attribute(char *str);
++
++uint64_t
++str_to_attribute(char *str)
++{
++	if ((strcmp(str, SU_ARCH_SHORT) == 0) ||
++	    (strcmp(str, SU_ARCH_FULL) == 0) ||
++	    (strcmp(str, U_ARCH_SHORT) == 0) ||
++	    (strcmp(str, U_ARCH_FULL) == 0))
++		return (ZFS_ARCHIVE);
++
++	else if ((strcmp(str, SU_APPEND_SHORT) == 0) ||
++	    (strcmp(str, SU_APPEND_FULL) == 0) ||
++	    (strcmp(str, U_APPEND_SHORT) == 0) ||
++	    (strcmp(str, U_APPEND_FULL) == 0))
++		return (ZFS_APPENDONLY);
++
++	else if ((strcmp(str, SU_IMMUTABLE) == 0) ||
++	    (strcmp(str, SU_IMMUTABLE_SHORT) == 0) ||
++	    (strcmp(str, SU_IMMUTABLE_FULL) == 0))
++		return (ZFS_IMMUTABLE);
++
++	else if ((strcmp(str, SU_UNLINK_SHORT) == 0) ||
++	    (strcmp(str, SU_UNLINK_FULL) == 0) ||
++	    (strcmp(str, U_UNLINK_SHORT) == 0) ||
++	    (strcmp(str, SU_UNLINK_FULL) == 0))
++		return (ZFS_NOUNLINK);
++
++	else if ((strcmp(str, U_HIDDEN_SHORT) == 0) ||
++	    (strcmp(str, U_HIDDEN_FULL) == 0))
++		return (ZFS_HIDDEN);
++
++	else if ((strcmp(str, U_OFFLINE_SHORT) == 0) ||
++	    (strcmp(str, U_OFFLINE_FULL) == 0))
++		return (ZFS_OFFLINE);
++
++	else if ((strcmp(str, U_RDONLY) == 0) ||
++	    (strcmp(str, U_RDONLY_SHORT) == 0) ||
++	    (strcmp(str, U_RDONLY_FULL) == 0))
++		return (ZFS_READONLY);
++
++	else if ((strcmp(str, U_SPARSE_SHORT) == 0) ||
++	    (strcmp(str, U_SPARSE_FULL) == 0))
++		return (ZFS_SPARSE);
++
++	else if ((strcmp(str, U_SYSTEM_SHORT) == 0) ||
++	    (strcmp(str, U_SYSTEM_FULL) == 0))
++		return (ZFS_SYSTEM);
++
++	else if ((strcmp(str, U_REPARSE_SHORT) == 0) ||
++	    (strcmp(str, U_REPARSE_FULL) == 0))
++		return (ZFS_REPARSE);
++
++	return (-1);
++}
++
++int
++main(int argc, const char * const argv[])
++{
++	if (argc != 3)
++		errx(EXIT_FAILURE, "Usage: %s flag filepath", argv[0]);
++
++	uint8_t unset, unset_all;
++	uint64_t attribute, dosflags;
++	char *flag = strdup(argv[1]);
++	unset = unset_all = 0;
++	attribute = dosflags = 0;
++
++	// convert the flag to lower case
++	for (int i = 0; i < strlen(argv[1]); ++i)
++		flag[i] = tolower((unsigned char) flag[i]);
++
++	// check if flag starts with 'no'
++	if (IS_NO(flag)) {
++		if (strcmp(flag, SU_NODUMP) == 0) {
++			attribute = ZFS_NODUMP;
++		} else {
++			attribute = str_to_attribute(flag + 2);
++			unset = 1;
++		}
++	}
++	// check if '0' was passed
++	else if (strcmp(flag, "0") == 0) {
++		unset_all = 1;
++	}
++	// check if the flag is 'dump'
++	else if (strcmp(flag, UNSET_NODUMP) == 0) {
++		attribute = ZFS_NODUMP;
++		unset = 1;
++	} else {
++		attribute = str_to_attribute(flag);
++	}
++
++	if (attribute == -1)
++		errx(EXIT_FAILURE, "Invalid Flag %s", argv[1]);
++
++	int fd = open(argv[2], O_RDWR | O_APPEND);
++	if (fd < 0)
++		err(EXIT_FAILURE, "Failed to open %s", argv[2]);
++
++	if (ioctl(fd, ZFS_IOC_GETDOSFLAGS, &dosflags) == -1)
++		err(EXIT_FAILURE, "ZFS_IOC_GETDOSFLAGS failed");
++
++	if (unset == 0 && attribute != 0)
++		attribute |= dosflags;
++	else if (unset == 1 && attribute != 0)
++		attribute = dosflags & (~attribute);
++	else if (unset_all == 1)
++		attribute = 0;
++
++	// set the attribute/s
++	if (ioctl(fd, ZFS_IOC_SETDOSFLAGS, &attribute) == -1)
++		err(EXIT_FAILURE, "ZFS_IOC_SETDOSFLAGS failed");
++
++	// get the attributes to confirm
++	dosflags = -1;
++	if (ioctl(fd, ZFS_IOC_GETDOSFLAGS, &dosflags) == -1)
++		err(EXIT_FAILURE, "ZFS_IOC_GETDOSFLAGS failed");
++
++	(void) close(fd);
++
++	if (dosflags != attribute)
++		errx(EXIT_FAILURE, "Could not set %s attribute", argv[1]);
++
++	(void) printf("New Dos Flags: 0x%llx\n", (u_longlong_t)dosflags);
++
++	return (EXIT_SUCCESS);
++}
+diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg
+index fa3d12d669f..b247a67ff66 100644
+--- a/tests/zfs-tests/include/commands.cfg
++++ b/tests/zfs-tests/include/commands.cfg
+@@ -215,10 +215,12 @@ export ZFSTEST_FILES='badsend
+     randfree_file
+     randwritecomp
+     readmmap
++    read_dos_attributes
+     rename_dir
+     rm_lnkcnt_zero_file
+     send_doall
+     threadsappend
+     user_ns_exec
++    write_dos_attributes
+     xattrtest
+     stride_dd'
+diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am
+index e71172b8e86..9164650e341 100644
+--- a/tests/zfs-tests/tests/functional/Makefile.am
++++ b/tests/zfs-tests/tests/functional/Makefile.am
+@@ -21,6 +21,7 @@ SUBDIRS = \
+ 	deadman \
+ 	delegate \
+ 	devices \
++	dos_attributes \
+ 	events \
+ 	exec \
+ 	fallocate \
+diff --git a/tests/zfs-tests/tests/functional/acl/off/Makefile.am b/tests/zfs-tests/tests/functional/acl/off/Makefile.am
+index 36aa13dd03f..ae6a9c69d93 100644
+--- a/tests/zfs-tests/tests/functional/acl/off/Makefile.am
++++ b/tests/zfs-tests/tests/functional/acl/off/Makefile.am
+@@ -10,7 +10,5 @@ dist_pkgdata_SCRIPTS = \
+ 
+ pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/acl/off
+ 
+-if BUILD_FREEBSD
+ pkgexec_PROGRAMS = dosmode_readonly_write
+ dosmode_readonly_write_SOURCES = dosmode_readonly_write.c
+-endif
+diff --git a/tests/zfs-tests/tests/functional/acl/off/dosmode.ksh b/tests/zfs-tests/tests/functional/acl/off/dosmode.ksh
+index e232dfd525c..585aa025390 100755
+--- a/tests/zfs-tests/tests/functional/acl/off/dosmode.ksh
++++ b/tests/zfs-tests/tests/functional/acl/off/dosmode.ksh
+@@ -31,8 +31,6 @@
+ # DESCRIPTION:
+ #	Verify that DOS mode flags function correctly.
+ #
+-#	These flags are not currently exposed on Linux, so the test is
+-#	only useful on FreeBSD.
+ #
+ # STRATEGY:
+ #	1. ARCHIVE
+@@ -56,7 +54,13 @@ function hasflag
+ 	typeset flag=$1
+ 	typeset path=$2
+ 
+-	ls -lo $path | awk '{ gsub(",", "\n", $5); print $5 }' | grep -qxF $flag
++	if is_linux; then
++		read_dos_attributes $path | awk \
++		'{ gsub(",", "\n", $1); print $1 }' | grep -qxF $flag
++	else
++		ls -lo $path | awk '{ gsub(",", "\n", $5); print $5 }' | \
++		grep -qxF $flag
++	fi
+ }
+ 
+ log_assert "Verify DOS mode flags function correctly"
+@@ -67,6 +71,12 @@ testfile=$TESTDIR/testfile
+ owner=$ZFS_ACL_STAFF1
+ other=$ZFS_ACL_STAFF2
+ 
++if is_linux; then
++	changeflags=write_dos_attributes
++else
++	changeflags=chflags
++fi
++
+ #
+ # ARCHIVE
+ #
+@@ -75,36 +85,40 @@ other=$ZFS_ACL_STAFF2
+ #
+ log_must touch $testfile
+ log_must hasflag uarch $testfile
+-log_must chflags nouarch $testfile
++log_must $changeflags nouarch $testfile
+ log_must hasflag - $testfile
+ log_must touch $testfile
+-log_must hasflag uarch $testfile
++if ! is_linux; then
++	log_must hasflag uarch $testfile
++fi
+ log_must rm $testfile
+ log_must user_run $owner touch $testfile
+ log_must hasflag uarch $testfile
+-log_must user_run $owner chflags nouarch $testfile
+-log_mustnot user_run $other chflags uarch $testfile
++log_must user_run $owner $changeflags nouarch $testfile
++log_mustnot user_run $other $changeflags uarch $testfile
+ log_must hasflag - $testfile
+ log_must user_run $owner touch $testfile
+-log_mustnot user_run $other chflags nouarch $testfile
+-log_must hasflag uarch $testfile
++log_mustnot user_run $other $changeflags nouarch $testfile
++if ! is_linux; then
++	log_must hasflag uarch $testfile
++fi
+ log_must user_run $owner rm $testfile
+ 
+ #
+ # HIDDEN
+ #
+ log_must touch $testfile
+-log_must chflags hidden $testfile
++log_must $changeflags hidden $testfile
+ log_must hasflag hidden $testfile
+-log_must chflags 0 $testfile
++log_must $changeflags 0 $testfile
+ log_must hasflag - $testfile
+ log_must rm $testfile
+ log_must user_run $owner touch $testfile
+-log_must user_run $owner chflags hidden $testfile
+-log_mustnot user_run $other chflags nohidden $testfile
++log_must user_run $owner $changeflags hidden $testfile
++log_mustnot user_run $other $changeflags nohidden $testfile
+ log_must hasflag hidden $testfile
+-log_must user_run $owner chflags 0 $testfile
+-log_mustnot user_run $other chflags hidden $testfile
++log_must user_run $owner $changeflags 0 $testfile
++log_mustnot user_run $other $changeflags hidden $testfile
+ log_must hasflag - $testfile
+ log_must user_run $owner rm $testfile
+ 
+@@ -113,17 +127,17 @@ log_must user_run $owner rm $testfile
+ # OFFLINE
+ #
+ log_must touch $testfile
+-log_must chflags offline $testfile
++log_must $changeflags offline $testfile
+ log_must hasflag offline $testfile
+-log_must chflags 0 $testfile
++log_must $changeflags 0 $testfile
+ log_must hasflag - $testfile
+ log_must rm $testfile
+ log_must user_run $owner touch $testfile
+-log_must user_run $owner chflags offline $testfile
+-log_mustnot user_run $other chflags nooffline $testfile
++log_must user_run $owner $changeflags offline $testfile
++log_mustnot user_run $other $changeflags nooffline $testfile
+ log_must hasflag offline $testfile
+-log_must user_run $owner chflags 0 $testfile
+-log_mustnot user_run $other chflags offline $testfile
++log_must user_run $owner $changeflags 0 $testfile
++log_mustnot user_run $other $changeflags offline $testfile
+ log_must hasflag - $testfile
+ log_must user_run $owner rm $testfile
+ 
+@@ -134,21 +148,23 @@ log_must user_run $owner rm $testfile
+ # but root is always allowed the operation.
+ #
+ log_must touch $testfile
+-log_must chflags rdonly $testfile
++log_must $changeflags rdonly $testfile
+ log_must hasflag rdonly $testfile
+ log_must eval "echo 'root write allowed' >> $testfile"
+ log_must cat $testfile
+-log_must chflags 0 $testfile
+-log_must hasflag - $tesfile
++log_must $changeflags 0 $testfile
++log_must hasflag - $testfile
+ log_must rm $testfile
+ # It is required to still be able to write to an fd that was opened RW before
+ # READONLY is set.  We have a special test program for that.
+ log_must user_run $owner touch $testfile
+-log_mustnot user_run $other chflags rdonly $testfile
++log_mustnot user_run $other $changeflags rdonly $testfile
+ log_must user_run $owner $tests_base/dosmode_readonly_write $testfile
+-log_mustnot user_run $other chflags nordonly $testfile
++log_mustnot user_run $other $changeflags nordonly $testfile
+ log_must hasflag rdonly $testfile
+-log_mustnot user_run $owner "echo 'user write forbidden' >> $testfile"
++if ! is_linux; then
++	log_mustnot user_run $owner "echo 'user write forbidden' >> $testfile"
++fi
+ log_must eval "echo 'root write allowed' >> $testfile"
+ # We are still allowed to read and remove the file when READONLY is set.
+ log_must user_run $owner cat $testfile
+@@ -157,24 +173,23 @@ log_must user_run $owner rm $testfile
+ #
+ # REPARSE
+ #
+-# FIXME: does not work, not sure if broken or testing wrong
+-#
++# not allowed to be changed
+ 
+ #
+ # SPARSE
+ #
+ log_must truncate -s 1m $testfile
+-log_must chflags sparse $testfile
++log_must $changeflags sparse $testfile
+ log_must hasflag sparse $testfile
+-log_must chflags 0 $testfile
++log_must $changeflags 0 $testfile
+ log_must hasflag - $testfile
+ log_must rm $testfile
+ log_must user_run $owner truncate -s 1m $testfile
+-log_must user_run $owner chflags sparse $testfile
+-log_mustnot user_run $other chflags nosparse $testfile
++log_must user_run $owner $changeflags sparse $testfile
++log_mustnot user_run $other $changeflags nosparse $testfile
+ log_must hasflag sparse $testfile
+-log_must user_run $owner chflags 0 $testfile
+-log_mustnot user_run $other chflags sparse $testfile
++log_must user_run $owner $changeflags 0 $testfile
++log_mustnot user_run $other $changeflags sparse $testfile
+ log_must hasflag - $testfile
+ log_must user_run $owner rm $testfile
+ 
+@@ -182,17 +197,17 @@ log_must user_run $owner rm $testfile
+ # SYSTEM
+ #
+ log_must touch $testfile
+-log_must chflags system $testfile
++log_must $changeflags system $testfile
+ log_must hasflag system $testfile
+-log_must chflags 0 $testfile
++log_must $changeflags 0 $testfile
+ log_must hasflag - $testfile
+ log_must rm $testfile
+ log_must user_run $owner touch $testfile
+-log_must user_run $owner chflags system $testfile
+-log_mustnot user_run $other chflags nosystem $testfile
++log_must user_run $owner $changeflags system $testfile
++log_mustnot user_run $other $changeflags nosystem $testfile
+ log_must hasflag system $testfile
+-log_must user_run $owner chflags 0 $testfile
+-log_mustnot user_run $other chflags system $testfile
++log_must user_run $owner $changeflags 0 $testfile
++log_mustnot user_run $other $changeflags system $testfile
+ log_must hasflag - $testfile
+ log_must user_run $owner rm $testfile
+ 
+diff --git a/tests/zfs-tests/tests/functional/acl/off/dosmode_readonly_write.c b/tests/zfs-tests/tests/functional/acl/off/dosmode_readonly_write.c
+index 372c3f7f64d..0441d1c7b47 100644
+--- a/tests/zfs-tests/tests/functional/acl/off/dosmode_readonly_write.c
++++ b/tests/zfs-tests/tests/functional/acl/off/dosmode_readonly_write.c
+@@ -36,6 +36,11 @@
+ #include <string.h>
+ #include <unistd.h>
+ 
++#ifdef __linux__
++#include <stdint.h>
++#include <sys/fs/zfs.h>
++#endif
++
+ int
+ main(int argc, const char *argv[])
+ {
+@@ -51,8 +56,14 @@ main(int argc, const char *argv[])
+ 	fd = open(path, O_CREAT|O_RDWR, 0777);
+ 	if (fd == -1)
+ 		err(EXIT_FAILURE, "%s: open failed", path);
++#ifdef __linux__
++	uint64_t dosflags = ZFS_READONLY;
++	if (ioctl(fd, ZFS_IOC_SETDOSFLAGS, &dosflags) == -1)
++		err(EXIT_FAILURE, "%s: ZFS_IOC_SETDOSFLAGS failed", path);
++#else
+ 	if (chflags(path, UF_READONLY) == -1)
+ 		err(EXIT_FAILURE, "%s: chflags failed", path);
++#endif
+ 	if (write(fd, buf, strlen(buf)) == -1)
+ 		err(EXIT_FAILURE, "%s: write failed", path);
+ 	if (close(fd) == -1)
+diff --git a/tests/zfs-tests/tests/functional/dos_attributes/Makefile.am b/tests/zfs-tests/tests/functional/dos_attributes/Makefile.am
+new file mode 100644
+index 00000000000..436bcdb1f31
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/dos_attributes/Makefile.am
+@@ -0,0 +1,8 @@
++include $(top_srcdir)/config/Rules.am
++
++pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/dos_attributes
++dist_pkgdata_SCRIPTS = \
++    cleanup.ksh \
++    read_dos_attrs_001.ksh \
++    write_dos_attrs_001.ksh \
++    setup.ksh
+diff --git a/tests/zfs-tests/tests/functional/dos_attributes/cleanup.ksh b/tests/zfs-tests/tests/functional/dos_attributes/cleanup.ksh
+new file mode 100755
+index 00000000000..3166bd6ec16
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/dos_attributes/cleanup.ksh
+@@ -0,0 +1,34 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++#
++# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++
++#
++# Copyright (c) 2013 by Delphix. All rights reserved.
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++default_cleanup
+diff --git a/tests/zfs-tests/tests/functional/dos_attributes/read_dos_attrs_001.ksh b/tests/zfs-tests/tests/functional/dos_attributes/read_dos_attrs_001.ksh
+new file mode 100755
+index 00000000000..c36c0183315
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/dos_attributes/read_dos_attrs_001.ksh
+@@ -0,0 +1,60 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++#
++# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++
++#
++# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++#
++# DESCRIPTION:
++#
++# Read additional file level attributes stored in upper half of z_pflags
++#
++# STARTEGY:
++#		1) Create a file
++#		2) Execute read_dos_attributes on the file we created
++#		3) Verify that read_dos_attributes exited successfully
++#
++
++verify_runnable "global"
++
++FILETOTEST="$TESTDIR/test_read_dos_attrs.txt"
++
++function cleanup
++{
++	rm -f $FILETOTEST
++}
++
++log_onexit cleanup
++
++log_must chmod 777 $TESTDIR
++log_must eval "echo 'This is a test file.' > $FILETOTEST"
++log_must read_dos_attributes $FILETOTEST
++
++log_pass "reading DOS attributes succeeded."
+diff --git a/tests/zfs-tests/tests/functional/dos_attributes/setup.ksh b/tests/zfs-tests/tests/functional/dos_attributes/setup.ksh
+new file mode 100755
+index 00000000000..fc5cec3063a
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/dos_attributes/setup.ksh
+@@ -0,0 +1,35 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++#
++# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++
++#
++# Copyright (c) 2013 by Delphix. All rights reserved.
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++DISK=${DISKS%% *}
++default_setup $DISK
+diff --git a/tests/zfs-tests/tests/functional/dos_attributes/write_dos_attrs_001.ksh b/tests/zfs-tests/tests/functional/dos_attributes/write_dos_attrs_001.ksh
+new file mode 100755
+index 00000000000..9d66cd35759
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/dos_attributes/write_dos_attrs_001.ksh
+@@ -0,0 +1,61 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++#
++# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++
++#
++# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++#
++# DESCRIPTION:
++#
++# Write additional file level attributes stored in upper half of z_pflags
++#
++# STARTEGY:
++#		1) Create a file
++#		2) Execute write_dos_attributes on the file we created
++#		3) Verify that write_dos_attributes exited successfully
++#
++
++verify_runnable "global"
++
++FILETOTEST="$TESTDIR/test_write_dos_attrs.txt"
++
++function cleanup
++{
++	rm -f $FILETOTEST
++}
++
++log_onexit cleanup
++
++log_must chmod 777 $TESTDIR
++log_must eval "echo 'This is a test file.' > $FILETOTEST"
++log_must write_dos_attributes offline $FILETOTEST
++log_must write_dos_attributes nooffline $FILETOTEST
++
++log_pass "writing DOS attributes succeeded."
diff --git a/sys-fs/zfs-kmod/files/2.1.11-optimize-access-checks.patch b/sys-fs/zfs-kmod/files/2.1.11-optimize-access-checks.patch
new file mode 100644
index 0000000..a59a986
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-optimize-access-checks.patch
@@ -0,0 +1,207 @@
+From 9e7724f9011c0b42b982ed9d67ce3a5d75e33441 Mon Sep 17 00:00:00 2001
+From: Andrew Walker <awalker@ixsystems.com>
+Date: Tue, 15 Mar 2022 11:03:29 -0400
+Subject: [PATCH 1/2] Linux optimize access checks when ACL is trivial
+
+Bypass check of ZFS aces if the ACL is trivial. When an ACL is
+trivial its permissions are represented by the mode without any
+loss of information. In this case, it is safe to convert the
+access request into equivalent mode and then pass desired mask
+and inode to generic_permission(). This has the added benefit
+of also checking whether entries in a POSIX ACL on the file grant
+the desired access.
+
+This commit also skips the ACL check on looking up the xattr dir
+since such restrictions don't exist in Linux kernel and it makes
+xattr lookup behavior inconsistent between SA and file-based
+xattrs. We also don't want to perform a POSIX ACL check while
+looking up the POSIX ACL if for some reason it is located in
+the xattr dir rather than an SA.
+
+Signed-off-by: Andrew Walker <awalker@ixsystems.com>
+---
+ module/os/linux/zfs/zfs_acl.c      | 70 ++++++++++++++++++++++++++++++
+ module/os/linux/zfs/zfs_vnops_os.c |  2 +-
+ 2 files changed, 71 insertions(+), 1 deletion(-)
+
+diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
+index 351e4dad799..b70691ab31c 100644
+--- a/module/os/linux/zfs/zfs_acl.c
++++ b/module/os/linux/zfs/zfs_acl.c
+@@ -863,6 +863,26 @@ zfs_unix_to_v4(uint32_t access_mask)
+ 	return (new_mask);
+ }
+ 
++
++static int
++zfs_v4_to_unix(uint32_t access_mask, int *unmapped)
++{
++	int new_mask = 0;
++
++	*unmapped = access_mask &
++	    (ACE_WRITE_OWNER | ACE_WRITE_ACL | ACE_DELETE);
++
++	if (access_mask & WRITE_MASK)
++		new_mask |= S_IWOTH;
++	if (access_mask & ACE_READ_DATA)
++		new_mask |= S_IROTH;
++	if (access_mask & ACE_EXECUTE)
++		new_mask |= S_IXOTH;
++
++	return (new_mask);
++}
++
++
+ static void
+ zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
+     uint16_t access_type, uint64_t fuid, uint16_t entry_type)
+@@ -2399,6 +2419,53 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+ 	return (B_TRUE);
+ }
+ 
++/*
++ * Simplified access check for case where ACL is known to not contain
++ * information beyond what is defined in the mode. In this case, we
++ * can pass along to the kernel / vfs generic_permission() check, which
++ * evaluates the mode and POSIX ACL.
++ *
++ * NFSv4 ACLs allow granting permissions that are usually relegated only
++ * to the file owner or superuser. Examples are ACE_WRITE_OWNER (chown),
++ * ACE_WRITE_ACL(chmod), and ACE_DELETE. ACE_DELETE requests must fail
++ * because with conventional posix permissions, right to delete file
++ * is determined by write bit on the parent dir.
++ *
++ * If unmappable perms are requested, then we must return EPERM
++ * and include those bits in the working_mode so that the caller of
++ * zfs_zaccess_common() can decide whether to perform additional
++ * policy / capability checks. EACCES is used in zfs_zaccess_aces_check()
++ * to indicate access check failed due to explicit DENY entry, and so
++ * we want to avoid that here.
++ */
++static int
++zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr)
++{
++	int err, mask;
++	int unmapped = 0;
++
++	ASSERT(zp->z_pflags & ZFS_ACL_TRIVIAL);
++
++	mask = zfs_v4_to_unix(*working_mode, &unmapped);
++	if (mask == 0 || unmapped) {
++		*working_mode = unmapped;
++		return (unmapped ? SET_ERROR(EPERM) : 0);
++	}
++
++#if defined(HAVE_IOPS_PERMISSION_USERNS)
++	err = generic_permission(cr->user_ns, ZTOI(zp), mask);
++#else
++	err = generic_permission(ZTOI(zp), mask);
++#endif
++	if (err != 0) {
++		return (SET_ERROR(EPERM));
++	}
++
++	*working_mode = unmapped;
++
++	return (0);
++}
++
+ static int
+ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+     boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+@@ -2450,6 +2517,9 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+ 		return (SET_ERROR(EPERM));
+ 	}
+ 
++	if (zp->z_pflags & ZFS_ACL_TRIVIAL)
++		return (zfs_zaccess_trivial(zp, working_mode, cr));
++
+ 	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
+ }
+ 
+diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
+index ece7c373e85..b65728f0d4c 100644
+--- a/module/os/linux/zfs/zfs_vnops_os.c
++++ b/module/os/linux/zfs/zfs_vnops_os.c
+@@ -474,7 +474,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 		 */
+ 
+ 		if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
+-		    B_FALSE, cr))) {
++		    B_TRUE, cr))) {
+ 			zrele(*zpp);
+ 			*zpp = NULL;
+ 		}
+
+From 6947cd4d94945b1014f69801efbd92fc526ab373 Mon Sep 17 00:00:00 2001
+From: Ryan Moeller <ryan@iXsystems.com>
+Date: Wed, 3 Nov 2021 08:03:08 -0400
+Subject: [PATCH 2/2] Add configure check for "permission" inode operation
+
+The "permission" inode operation takes a new `struct user_namespace *`
+parameter starting in Linux 5.12.
+
+Add a configure check and adapt accordingly.
+
+Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
+---
+ config/kernel-inode-permission.m4 | 29 +++++++++++++++++++++++++++++
+ config/kernel.m4                  |  2 ++
+ 2 files changed, 31 insertions(+)
+ create mode 100644 config/kernel-inode-permission.m4
+
+diff --git a/config/kernel-inode-permission.m4 b/config/kernel-inode-permission.m4
+new file mode 100644
+index 00000000000..ba9ff5d43d4
+--- /dev/null
++++ b/config/kernel-inode-permission.m4
+@@ -0,0 +1,29 @@
++AC_DEFUN([ZFS_AC_KERNEL_SRC_PERMISSION], [
++	dnl #
++	dnl # 5.12 API change that added the struct user_namespace* arg
++	dnl # to the front of this function type's arg list.
++	dnl #
++	ZFS_LINUX_TEST_SRC([permission_userns], [
++		#include <linux/fs.h>
++		#include <linux/sched.h>
++
++		int inode_permission(struct user_namespace *userns,
++		    struct inode *inode, int mask) { return 0; }
++
++		static const struct inode_operations
++			iops __attribute__ ((unused)) = {
++			.permission		= inode_permission,
++		};
++	],[])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_PERMISSION], [
++	AC_MSG_CHECKING([whether iops->permission() takes struct user_namespace*])
++	ZFS_LINUX_TEST_RESULT([permission_userns], [
++		AC_MSG_RESULT(yes)
++		AC_DEFINE(HAVE_IOPS_PERMISSION_USERNS, 1,
++		   [iops->permission() takes struct user_namespace*])
++	],[
++		AC_MSG_RESULT(no)
++	])
++])
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index d1d3dede175..639d1837712 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -82,6 +82,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_MKDIR
+ 	ZFS_AC_KERNEL_SRC_LOOKUP_FLAGS
+ 	ZFS_AC_KERNEL_SRC_CREATE
++	ZFS_AC_KERNEL_SRC_PERMISSION
+ 	ZFS_AC_KERNEL_SRC_GET_LINK
+ 	ZFS_AC_KERNEL_SRC_PUT_LINK
+ 	ZFS_AC_KERNEL_SRC_TMPFILE
+@@ -193,6 +194,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_MKDIR
+ 	ZFS_AC_KERNEL_LOOKUP_FLAGS
+ 	ZFS_AC_KERNEL_CREATE
++	ZFS_AC_KERNEL_PERMISSION
+ 	ZFS_AC_KERNEL_GET_LINK
+ 	ZFS_AC_KERNEL_PUT_LINK
+ 	ZFS_AC_KERNEL_TMPFILE
diff --git a/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount-in-user-ns.patch b/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount-in-user-ns.patch
new file mode 100644
index 0000000..de3cede
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount-in-user-ns.patch
@@ -0,0 +1,2012 @@
+From de47f78acbfd3fc19eadc72801cdd963df1eb41d Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:24:30 -0500
+Subject: [PATCH 1/8] Backport "Support idmapped mount in user namespace"
+
+---
+ include/os/linux/kernel/linux/xattr_compat.h  |  10 +-
+ include/os/linux/spl/sys/cred.h               |  83 ++++++++---
+ include/os/linux/spl/sys/types.h              |   1 +
+ include/os/linux/zfs/sys/policy.h             |   5 +-
+ module/os/linux/spl/spl-cred.c                |   8 +
+ module/os/linux/zfs/policy.c                  |  17 ++-
+ module/os/linux/zfs/zfs_acl.c                 |  24 +--
+ module/os/linux/zfs/zfs_dir.c                 |   5 +-
+ module/os/linux/zfs/zfs_ioctl_os.c            |   7 +
+ module/os/linux/zfs/zfs_vnops_os.c            |  25 ++--
+ module/os/linux/zfs/zfs_znode.c               |   2 +-
+ module/os/linux/zfs/zpl_ctldir.c              |   2 +-
+ module/os/linux/zfs/zpl_file.c                |   6 +-
+ module/os/linux/zfs/zpl_inode.c               |  22 +--
+ module/os/linux/zfs/zpl_xattr.c               |  36 ++++-
+ module/zfs/zfs_replay.c                       |  14 +-
+ module/zfs/zfs_vnops.c                        |   5 +-
+ tests/runfiles/linux.run                      |   2 +-
+ tests/test-runner/bin/zts-report.py.in        |   1 +
+ tests/zfs-tests/cmd/idmap_util/idmap_util.c   |  49 +++++--
+ .../idmap_mount/idmap_mount_005.ksh           | 138 ++++++++++++++++++
+ 21 files changed, 361 insertions(+), 101 deletions(-)
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh
+
+diff --git a/include/os/linux/kernel/linux/xattr_compat.h b/include/os/linux/kernel/linux/xattr_compat.h
+index 30403fe8739..2c33a58d5c7 100644
+--- a/include/os/linux/kernel/linux/xattr_compat.h
++++ b/include/os/linux/kernel/linux/xattr_compat.h
+@@ -146,7 +146,7 @@ fn(const struct xattr_handler *handler, struct user_namespace *user_ns, \
+     struct dentry *dentry, struct inode *inode, const char *name,	\
+     const void *buffer, size_t size, int flags)	\
+ {									\
+-	return (__ ## fn(inode, name, buffer, size, flags));		\
++	return (__ ## fn(user_ns, inode, name, buffer, size, flags));	\
+ }
+ /*
+  * 4.7 API change,
+@@ -160,7 +160,7 @@ fn(const struct xattr_handler *handler, struct dentry *dentry,		\
+     struct inode *inode, const char *name, const void *buffer,		\
+     size_t size, int flags)						\
+ {									\
+-	return (__ ## fn(inode, name, buffer, size, flags));		\
++	return (__ ## fn(zfs_init_user_ns, inode, name, buffer, size, flags));\
+ }
+ /*
+  * 4.4 API change,
+@@ -174,7 +174,8 @@ static int								\
+ fn(const struct xattr_handler *handler, struct dentry *dentry,		\
+     const char *name, const void *buffer, size_t size, int flags)	\
+ {									\
+-	return (__ ## fn(dentry->d_inode, name, buffer, size, flags));	\
++	return (__ ## fn(zfs_init_user_ns, dentry->d_inode, name,	\
++	    buffer, size, flags));					\
+ }
+ /*
+  * 2.6.33 API change,
+@@ -187,7 +188,8 @@ static int								\
+ fn(struct dentry *dentry, const char *name, const void *buffer,		\
+     size_t size, int flags, int unused_handler_flags)			\
+ {									\
+-	return (__ ## fn(dentry->d_inode, name, buffer, size, flags));	\
++	return (__ ## fn(zfs_init_user_ns, dentry->d_inode, name,	\
++	    buffer, size, flags));					\
+ }
+ #else
+ #error "Unsupported kernel"
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index dc3c260dbba..13e420e8de2 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -45,32 +45,82 @@ typedef struct cred cred_t;
+ #define	SGID_TO_KGID(x)		(KGIDT_INIT(x))
+ #define	KGIDP_TO_SGIDP(x)	(&(x)->val)
+ 
+-static inline uid_t zfs_uid_into_mnt(struct user_namespace *mnt_ns, uid_t uid)
++extern struct user_namespace *zfs_get_init_userns(void);
++
++/* Check if the user ns is the initial one */
++static inline boolean_t
++zfs_is_init_userns(struct user_namespace *user_ns)
+ {
+-	if (mnt_ns)
+-		return (__kuid_val(make_kuid(mnt_ns, uid)));
+-	return (uid);
++#if defined(CONFIG_USER_NS)
++	return (user_ns == zfs_init_user_ns);
++#else
++	return (B_FALSE);
++#endif
+ }
+ 
+-static inline gid_t zfs_gid_into_mnt(struct user_namespace *mnt_ns, gid_t gid)
++static inline struct user_namespace *zfs_i_user_ns(struct inode *inode)
+ {
+-	if (mnt_ns)
+-		return (__kgid_val(make_kgid(mnt_ns, gid)));
+-	return (gid);
++#ifdef HAVE_SUPER_USER_NS
++	return (inode->i_sb->s_user_ns);
++#else
++	return (zfs_init_user_ns);
++#endif
+ }
+ 
+-static inline uid_t zfs_uid_from_mnt(struct user_namespace *mnt_ns, uid_t uid)
++static inline boolean_t zfs_no_idmapping(struct user_namespace *mnt_userns,
++    struct user_namespace *fs_userns)
+ {
+-	if (mnt_ns)
+-		return (from_kuid(mnt_ns, KUIDT_INIT(uid)));
+-	return (uid);
++	return (zfs_is_init_userns(mnt_userns) || mnt_userns == fs_userns);
+ }
+ 
+-static inline gid_t zfs_gid_from_mnt(struct user_namespace *mnt_ns, gid_t gid)
++static inline uid_t zfs_uid_to_vfsuid(struct user_namespace *mnt_userns,
++    struct user_namespace *fs_userns, uid_t uid)
+ {
+-	if (mnt_ns)
+-		return (from_kgid(mnt_ns, KGIDT_INIT(gid)));
+-	return (gid);
++	if (zfs_no_idmapping(mnt_userns, fs_userns))
++		return (uid);
++	if (!zfs_is_init_userns(fs_userns))
++		uid = from_kuid(fs_userns, KUIDT_INIT(uid));
++	if (uid == (uid_t)-1)
++		return (uid);
++	return (__kuid_val(make_kuid(mnt_userns, uid)));
++}
++
++static inline gid_t zfs_gid_to_vfsgid(struct user_namespace *mnt_userns,
++    struct user_namespace *fs_userns, gid_t gid)
++{
++	if (zfs_no_idmapping(mnt_userns, fs_userns))
++		return (gid);
++	if (!zfs_is_init_userns(fs_userns))
++		gid = from_kgid(fs_userns, KGIDT_INIT(gid));
++	if (gid == (gid_t)-1)
++		return (gid);
++	return (__kgid_val(make_kgid(mnt_userns, gid)));
++}
++
++static inline uid_t zfs_vfsuid_to_uid(struct user_namespace *mnt_userns,
++    struct user_namespace *fs_userns, uid_t uid)
++{
++	if (zfs_no_idmapping(mnt_userns, fs_userns))
++		return (uid);
++	uid = from_kuid(mnt_userns, KUIDT_INIT(uid));
++	if (uid == (uid_t)-1)
++		return (uid);
++	if (zfs_is_init_userns(fs_userns))
++		return (uid);
++	return (__kuid_val(make_kuid(fs_userns, uid)));
++}
++
++static inline gid_t zfs_vfsgid_to_gid(struct user_namespace *mnt_userns,
++    struct user_namespace *fs_userns, gid_t gid)
++{
++	if (zfs_no_idmapping(mnt_userns, fs_userns))
++		return (gid);
++	gid = from_kgid(mnt_userns, KGIDT_INIT(gid));
++	if (gid == (gid_t)-1)
++		return (gid);
++	if (zfs_is_init_userns(fs_userns))
++		return (gid);
++	return (__kgid_val(make_kgid(fs_userns, gid)));
+ }
+ 
+ extern void crhold(cred_t *cr);
+@@ -81,5 +131,4 @@ extern gid_t crgetgid(const cred_t *cr);
+ extern int crgetngroups(const cred_t *cr);
+ extern gid_t *crgetgroups(const cred_t *cr);
+ extern int groupmember(gid_t gid, const cred_t *cr);
+-
+ #endif  /* _SPL_CRED_H */
+diff --git a/include/os/linux/spl/sys/types.h b/include/os/linux/spl/sys/types.h
+index cae1bbddf10..e682c0560d5 100644
+--- a/include/os/linux/spl/sys/types.h
++++ b/include/os/linux/spl/sys/types.h
+@@ -56,5 +56,6 @@ typedef int			minor_t;
+ 
+ struct user_namespace;
+ typedef struct user_namespace	zuserns_t;
++extern zuserns_t *zfs_init_user_ns;
+ 
+ #endif	/* _SPL_TYPES_H */
+diff --git a/include/os/linux/zfs/sys/policy.h b/include/os/linux/zfs/sys/policy.h
+index 5ec51392f17..7548804b9ee 100644
+--- a/include/os/linux/zfs/sys/policy.h
++++ b/include/os/linux/zfs/sys/policy.h
+@@ -47,13 +47,14 @@ int secpolicy_vnode_create_gid(const cred_t *);
+ int secpolicy_vnode_remove(const cred_t *);
+ int secpolicy_vnode_setdac(const cred_t *, uid_t);
+ int secpolicy_vnode_setid_retain(struct znode *, const cred_t *, boolean_t);
+-int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *);
++int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *,
++    zuserns_t *);
+ int secpolicy_zinject(const cred_t *);
+ int secpolicy_zfs(const cred_t *);
+ int secpolicy_zfs_proc(const cred_t *, proc_t *);
+ void secpolicy_setid_clear(vattr_t *, cred_t *);
+ int secpolicy_setid_setsticky_clear(struct inode *, vattr_t *,
+-    const vattr_t *, cred_t *, zuserns_t *);
++    const vattr_t *, cred_t *, zuserns_t *, zuserns_t *);
+ int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, mode_t);
+ int secpolicy_vnode_setattr(cred_t *, struct inode *, struct vattr *,
+     const struct vattr *, int, int (void *, int, cred_t *), void *);
+diff --git a/module/os/linux/spl/spl-cred.c b/module/os/linux/spl/spl-cred.c
+index f81b9540a63..1f406ec540b 100644
+--- a/module/os/linux/spl/spl-cred.c
++++ b/module/os/linux/spl/spl-cred.c
+@@ -145,6 +145,14 @@ crgetgid(const cred_t *cr)
+ 	return (KGID_TO_SGID(cr->fsgid));
+ }
+ 
++/* Return the initial user ns */
++struct user_namespace *
++zfs_get_init_userns(void)
++{
++	return (&init_user_ns);
++}
++
++EXPORT_SYMBOL(zfs_get_init_userns);
+ EXPORT_SYMBOL(crhold);
+ EXPORT_SYMBOL(crfree);
+ EXPORT_SYMBOL(crgetuid);
+diff --git a/module/os/linux/zfs/policy.c b/module/os/linux/zfs/policy.c
+index d68f3561210..e879ec32c20 100644
+--- a/module/os/linux/zfs/policy.c
++++ b/module/os/linux/zfs/policy.c
+@@ -214,9 +214,10 @@ secpolicy_vnode_setid_retain(struct znode *zp __maybe_unused, const cred_t *cr,
+  * Determine that subject can set the file setgid flag.
+  */
+ int
+-secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns)
++secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns,
++    zuserns_t *fs_ns)
+ {
+-	gid = zfs_gid_into_mnt(mnt_ns, gid);
++	gid = zfs_gid_to_vfsgid(mnt_ns, fs_ns, gid);
+ #if defined(CONFIG_USER_NS)
+ 	if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid)))
+ 		return (EPERM);
+@@ -285,9 +286,10 @@ secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
+  * Determine that subject can set the file setid flags.
+  */
+ static int
+-secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns)
++secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns,
++    zuserns_t *fs_ns)
+ {
+-	owner = zfs_uid_into_mnt(mnt_ns, owner);
++	owner = zfs_uid_to_vfsuid(mnt_ns, fs_ns, owner);
+ 
+ 	if (crgetuid(cr) == owner)
+ 		return (0);
+@@ -313,13 +315,13 @@ secpolicy_vnode_stky_modify(const cred_t *cr)
+ 
+ int
+ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+-    const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns)
++    const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns, zuserns_t *fs_ns)
+ {
+ 	int error;
+ 
+ 	if ((vap->va_mode & S_ISUID) != 0 &&
+ 	    (error = secpolicy_vnode_setid_modify(cr,
+-	    ovap->va_uid, mnt_ns)) != 0) {
++	    ovap->va_uid, mnt_ns, fs_ns)) != 0) {
+ 		return (error);
+ 	}
+ 
+@@ -337,7 +339,8 @@ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+ 	 * group-id bit.
+ 	 */
+ 	if ((vap->va_mode & S_ISGID) != 0 &&
+-	    secpolicy_vnode_setids_setgids(cr, ovap->va_gid, mnt_ns) != 0) {
++	    secpolicy_vnode_setids_setgids(cr, ovap->va_gid,
++	    mnt_ns, fs_ns) != 0) {
+ 		vap->va_mode &= ~S_ISGID;
+ 	}
+ 
+diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
+index fcb767e9e4a..52978c1133e 100644
+--- a/module/os/linux/zfs/zfs_acl.c
++++ b/module/os/linux/zfs/zfs_acl.c
+@@ -1888,7 +1888,8 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+ 		acl_ids->z_mode |= S_ISGID;
+ 	} else {
+ 		if ((acl_ids->z_mode & S_ISGID) &&
+-		    secpolicy_vnode_setids_setgids(cr, gid, mnt_ns) != 0) {
++		    secpolicy_vnode_setids_setgids(cr, gid, mnt_ns,
++		    zfs_i_user_ns(ZTOI(dzp))) != 0) {
+ 			acl_ids->z_mode &= ~S_ISGID;
+ 		}
+ 	}
+@@ -1978,7 +1979,8 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (mask == 0)
+ 		return (SET_ERROR(ENOSYS));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr, NULL)))
++	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr,
++	    zfs_init_user_ns)))
+ 		return (error);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+@@ -2137,7 +2139,8 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (zp->z_pflags & ZFS_IMMUTABLE)
+ 		return (SET_ERROR(EPERM));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr, NULL)))
++	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
++	    zfs_init_user_ns)))
+ 		return (error);
+ 
+ 	error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
+@@ -2300,9 +2303,9 @@ zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+ 	uid_t		fowner;
+ 
+ 	if (mnt_ns) {
+-		fowner = zfs_uid_into_mnt(mnt_ns,
++		fowner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
+ 		    KUID_TO_SUID(ZTOI(zp)->i_uid));
+-		gowner = zfs_gid_into_mnt(mnt_ns,
++		gowner = zfs_gid_to_vfsgid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
+ 		    KGID_TO_SGID(ZTOI(zp)->i_gid));
+ 	} else
+ 		zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
+@@ -2416,7 +2419,8 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+ {
+ 	uint32_t have = ACE_ALL_PERMS;
+ 
+-	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr, NULL) != 0) {
++	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr,
++	    zfs_init_user_ns) != 0) {
+ 		uid_t owner;
+ 
+ 		owner = zfs_fuid_map_id(ZTOZSB(zp),
+@@ -2608,7 +2612,8 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+ slow:
+ 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
+ 	ZFS_ENTER(ZTOZSB(zdp));
+-	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL);
++	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
++	    zfs_init_user_ns);
+ 	ZFS_EXIT(ZTOZSB(zdp));
+ 	return (error);
+ }
+@@ -2660,7 +2665,8 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
+ 		}
+ 	}
+ 
+-	owner = zfs_uid_into_mnt(mnt_ns, KUID_TO_SUID(ZTOI(zp)->i_uid));
++	owner = zfs_uid_to_vfsuid(mnt_ns, zfs_i_user_ns(ZTOI(zp)),
++	    KUID_TO_SUID(ZTOI(zp)->i_uid));
+ 	owner = zfs_fuid_map_id(ZTOZSB(zp), owner, cr, ZFS_OWNER);
+ 
+ 	/*
+@@ -2784,7 +2790,7 @@ zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
+ {
+ 	int v4_mode = zfs_unix_to_v4(mode >> 6);
+ 
+-	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, NULL));
++	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, zfs_init_user_ns));
+ }
+ 
+ /* See zfs_zaccess_delete() */
+diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c
+index 9bb11d6d4c2..4a04cc37d5c 100644
+--- a/module/os/linux/zfs/zfs_dir.c
++++ b/module/os/linux/zfs/zfs_dir.c
+@@ -1067,7 +1067,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr)
+ 	*xzpp = NULL;
+ 
+ 	if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
+-	    &acl_ids, NULL)) != 0)
++	    &acl_ids, zfs_init_user_ns)) != 0)
+ 		return (error);
+ 	if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) {
+ 		zfs_acl_ids_free(&acl_ids);
+@@ -1215,7 +1215,8 @@ zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
+ 	    cr, ZFS_OWNER);
+ 
+ 	if ((uid = crgetuid(cr)) == downer || uid == fowner ||
+-	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL) == 0)
++	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
++	    zfs_init_user_ns) == 0)
+ 		return (0);
+ 	else
+ 		return (secpolicy_vnode_remove(cr));
+diff --git a/module/os/linux/zfs/zfs_ioctl_os.c b/module/os/linux/zfs/zfs_ioctl_os.c
+index 79b9d777dc8..ad17973e7ad 100644
+--- a/module/os/linux/zfs/zfs_ioctl_os.c
++++ b/module/os/linux/zfs/zfs_ioctl_os.c
+@@ -58,6 +58,9 @@
+ #include <sys/zvol.h>
+ #include <sys/fm/util.h>
+ #include <sys/dsl_crypt.h>
++#include <sys/crypto/icp.h>
++#include <sys/zstd/zstd.h>
++#include <sys/cred.h>
+ 
+ #include <sys/zfs_ioctl_impl.h>
+ 
+@@ -288,6 +291,8 @@ zfsdev_detach(void)
+ #define	ZFS_DEBUG_STR	""
+ #endif
+ 
++zuserns_t *zfs_init_user_ns;
++
+ static int __init
+ openzfs_init(void)
+ {
+@@ -311,6 +316,8 @@ openzfs_init(void)
+ 	printk(KERN_NOTICE "ZFS: Posix ACLs disabled by kernel\n");
+ #endif /* CONFIG_FS_POSIX_ACL */
+ 
++	zfs_init_user_ns = (zuserns_t *)zfs_get_init_userns();
++
+ 	return (0);
+ }
+ 
+diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
+index b81b4298620..d7abe558cd0 100644
+--- a/module/os/linux/zfs/zfs_vnops_os.c
++++ b/module/os/linux/zfs/zfs_vnops_os.c
+@@ -501,7 +501,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 		 */
+ 
+ 		if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
+-		    B_TRUE, cr, NULL))) {
++		    B_TRUE, cr, zfs_init_user_ns))) {
+ 			zrele(*zpp);
+ 			*zpp = NULL;
+ 		}
+@@ -519,7 +519,8 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 	 * Check accessibility of directory.
+ 	 */
+ 
+-	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL))) {
++	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
++	    zfs_init_user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -1001,7 +1002,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_user_ns))) {
+ 		goto out;
+ 	}
+ 
+@@ -1417,7 +1418,7 @@ zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_user_ns))) {
+ 		goto out;
+ 	}
+ 
+@@ -2056,10 +2057,10 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
+ 		 * Take ownership or chgrp to group we are a member of
+ 		 */
+ 
+-		uid = zfs_uid_into_mnt((struct user_namespace *)mnt_ns,
+-		    vap->va_uid);
+-		gid = zfs_gid_into_mnt((struct user_namespace *)mnt_ns,
+-		    vap->va_gid);
++		uid = zfs_uid_to_vfsuid((struct user_namespace *)mnt_ns,
++		    zfs_i_user_ns(ip), vap->va_uid);
++		gid = zfs_gid_to_vfsgid((struct user_namespace *)mnt_ns,
++		    zfs_i_user_ns(ip), vap->va_gid);
+ 		take_owner = (mask & ATTR_UID) && (uid == crgetuid(cr));
+ 		take_group = (mask & ATTR_GID) &&
+ 		    zfs_groupmember(zfsvfs, gid, cr);
+@@ -2194,7 +2195,7 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
+ 		if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
+ 		    mnt_ns) == 0) {
+ 			err = secpolicy_setid_setsticky_clear(ip, vap,
+-			    &oldva, cr, mnt_ns);
++			    &oldva, cr, mnt_ns, zfs_i_user_ns(ip));
+ 			if (err)
+ 				goto out3;
+ 			trim_mask |= ATTR_MODE;
+@@ -3371,7 +3372,8 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
+ 		return (SET_ERROR(EPERM));
+ 	}
+ 
+-	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr, NULL))) {
++	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr,
++	    zfs_init_user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3959,7 +3961,8 @@ zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag,
+ 	 * On Linux we can get here through truncate_range() which
+ 	 * operates directly on inodes, so we need to check access rights.
+ 	 */
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL))) {
++	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
++	    zfs_init_user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
+index 619ef68a947..956346e0afd 100644
+--- a/module/os/linux/zfs/zfs_znode.c
++++ b/module/os/linux/zfs/zfs_znode.c
+@@ -1947,7 +1947,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
+ 	}
+ 
+ 	VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
+-	    cr, NULL, &acl_ids, NULL));
++	    cr, NULL, &acl_ids, zfs_init_user_ns));
+ 	zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, rootzp);
+ 	error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
+diff --git a/module/os/linux/zfs/zpl_ctldir.c b/module/os/linux/zfs/zpl_ctldir.c
+index 9e7e1806f8c..6cc99d6f37a 100644
+--- a/module/os/linux/zfs/zpl_ctldir.c
++++ b/module/os/linux/zfs/zpl_ctldir.c
+@@ -366,7 +366,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ #ifdef HAVE_IOPS_MKDIR_USERNS
+ 	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, user_ns);
+ #else
+-	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, NULL);
++	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, zfs_init_user_ns);
+ #endif
+ 
+ 	error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index 92babf04a92..8d320101ee3 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -971,7 +971,7 @@ zpl_ioctl_setflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1019,7 +1019,7 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1107,7 +1107,7 @@ zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c
+index d218045d11c..5eb95676e88 100644
+--- a/module/os/linux/zfs/zpl_inode.c
++++ b/module/os/linux/zfs/zpl_inode.c
+@@ -117,16 +117,16 @@ zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
+ 	vap->va_mask = ATTR_MODE;
+ 	vap->va_mode = mode;
+ 
+-	vap->va_uid = zfs_uid_from_mnt((struct user_namespace *)mnt_ns,
+-	    crgetuid(cr));
++	vap->va_uid = zfs_vfsuid_to_uid((struct user_namespace *)mnt_ns,
++	    zfs_i_user_ns(dir), crgetuid(cr));
+ 
+ 	if (dir && dir->i_mode & S_ISGID) {
+ 		vap->va_gid = KGID_TO_SGID(dir->i_gid);
+ 		if (S_ISDIR(mode))
+ 			vap->va_mode |= S_ISGID;
+ 	} else {
+-		vap->va_gid = zfs_gid_from_mnt((struct user_namespace *)mnt_ns,
+-		    crgetgid(cr));
++		vap->va_gid = zfs_vfsgid_to_gid((struct user_namespace *)mnt_ns,
++		    zfs_i_user_ns(dir), crgetgid(cr));
+ 	}
+ }
+ 
+@@ -144,7 +144,7 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_CREATE_USERNS
+-	zuserns_t *user_ns = NULL;
++	zuserns_t *user_ns = zfs_init_user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -191,7 +191,7 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_MKNOD_USERNS
+-	zuserns_t *user_ns = NULL;
++	zuserns_t *user_ns = zfs_init_user_ns;
+ #endif
+ 
+ 	/*
+@@ -251,7 +251,7 @@ zpl_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_TMPFILE_USERNS
+-	zuserns_t *userns = NULL;
++	zuserns_t *userns = zfs_init_user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -339,7 +339,7 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_MKDIR_USERNS
+-	zuserns_t *user_ns = NULL;
++	zuserns_t *user_ns = zfs_init_user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -496,7 +496,7 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ #ifdef HAVE_SETATTR_PREPARE_USERNS
+ 	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, user_ns);
+ #else
+-	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, NULL);
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, zfs_init_user_ns);
+ #endif
+ 	if (!error && (ia->ia_valid & ATTR_MODE))
+ 		error = zpl_chmod_acl(ip);
+@@ -523,7 +523,7 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_RENAME_USERNS
+-	zuserns_t *user_ns = NULL;
++	zuserns_t *user_ns = zfs_init_user_ns;
+ #endif
+ 
+ 	/* We don't have renameat2(2) support */
+@@ -564,7 +564,7 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_SYMLINK_USERNS
+-	zuserns_t *user_ns = NULL;
++	zuserns_t *user_ns = zfs_init_user_ns;
+ #endif
+ 
+ 	crhold(cr);
+diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c
+index 5f0be5f6418..c2be9352ae1 100644
+--- a/module/os/linux/zfs/zpl_xattr.c
++++ b/module/os/linux/zfs/zpl_xattr.c
+@@ -496,7 +496,7 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
+ 		vap->va_gid = crgetgid(cr);
+ 
+ 		error = -zfs_create(dxzp, (char *)name, vap, 0, 0644, &xzp,
+-		    cr, 0, NULL, NULL);
++		    cr, 0, NULL, zfs_init_user_ns);
+ 		if (error)
+ 			goto out;
+ 	}
+@@ -725,11 +725,13 @@ __zpl_xattr_user_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_user_get);
+ 
+ static int
+-__zpl_xattr_user_set(struct inode *ip, const char *name,
++__zpl_xattr_user_set(struct user_namespace *user_ns,
++    struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+ 	char *xattr_name;
+-	int error;
++	(void) user_ns;
++	int error = 0;
+ 	/* xattr_resolve_name will do this for us if this is defined */
+ #ifndef HAVE_XATTR_HANDLER_NAME
+ 	if (strcmp(name, "") == 0)
+@@ -794,9 +796,11 @@ __zpl_xattr_trusted_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_trusted_get);
+ 
+ static int
+-__zpl_xattr_trusted_set(struct inode *ip, const char *name,
++__zpl_xattr_trusted_set(struct user_namespace *user_ns,
++    struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
++	(void) user_ns;
+ 	char *xattr_name;
+ 	int error;
+ 
+@@ -863,9 +867,11 @@ __zpl_xattr_security_get(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_security_get);
+ 
+ static int
+-__zpl_xattr_security_set(struct inode *ip, const char *name,
++__zpl_xattr_security_set(struct user_namespace *user_ns,
++    struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
++	(void) user_ns;
+ 	char *xattr_name;
+ 	int error;
+ 	/* xattr_resolve_name will do this for us if this is defined */
+@@ -889,7 +895,7 @@ zpl_xattr_security_init_impl(struct inode *ip, const struct xattr *xattrs,
+ 	int error = 0;
+ 
+ 	for (xattr = xattrs; xattr->name != NULL; xattr++) {
+-		error = __zpl_xattr_security_set(ip,
++		error = __zpl_xattr_security_set(NULL, ip,
+ 		    xattr->name, xattr->value, xattr->value_len, 0);
+ 
+ 		if (error < 0)
+@@ -1256,7 +1262,8 @@ __zpl_xattr_acl_get_default(struct inode *ip, const char *name,
+ ZPL_XATTR_GET_WRAPPER(zpl_xattr_acl_get_default);
+ 
+ static int
+-__zpl_xattr_acl_set_access(struct inode *ip, const char *name,
++__zpl_xattr_acl_set_access(struct user_namespace *mnt_ns,
++    struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+ 	struct posix_acl *acl;
+@@ -1270,8 +1277,14 @@ __zpl_xattr_acl_set_access(struct inode *ip, const char *name,
+ 	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIX)
+ 		return (-EOPNOTSUPP);
+ 
++#if defined(HAVE_XATTR_SET_USERNS)
++	if (!zpl_inode_owner_or_capable(mnt_ns, ip))
++		return (-EPERM);
++#else
++	(void) mnt_ns;
+ 	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
+ 		return (-EPERM);
++#endif
+ 
+ 	if (value) {
+ 		acl = zpl_acl_from_xattr(value, size);
+@@ -1295,7 +1308,8 @@ __zpl_xattr_acl_set_access(struct inode *ip, const char *name,
+ ZPL_XATTR_SET_WRAPPER(zpl_xattr_acl_set_access);
+ 
+ static int
+-__zpl_xattr_acl_set_default(struct inode *ip, const char *name,
++__zpl_xattr_acl_set_default(struct user_namespace *mnt_ns,
++    struct inode *ip, const char *name,
+     const void *value, size_t size, int flags)
+ {
+ 	struct posix_acl *acl;
+@@ -1309,8 +1323,14 @@ __zpl_xattr_acl_set_default(struct inode *ip, const char *name,
+ 	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIX)
+ 		return (-EOPNOTSUPP);
+ 
++#if defined(HAVE_XATTR_SET_USERNS)
++	if (!zpl_inode_owner_or_capable(mnt_ns, ip))
++		return (-EPERM);
++#else
++	(void) mnt_ns;
+ 	if (!zpl_inode_owner_or_capable(kcred->user_ns, ip))
+ 		return (-EPERM);
++#endif
+ 
+ 	if (value) {
+ 		acl = zpl_acl_from_xattr(value, size);
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index c7a3486cd36..950339ceade 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -385,7 +385,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 		}
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, &vsec, NULL);
++		    0, 0, &zp, kcred, vflg, &vsec, zfs_init_user_ns);
+ 		break;
+ 	case TX_MKDIR_ACL:
+ 		aclstart = (caddr_t)(lracl + 1);
+@@ -415,7 +415,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 			    lr->lr_uid, lr->lr_gid);
+ 		}
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, &vsec, NULL);
++		    &zp, kcred, vflg, &vsec, zfs_init_user_ns);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -525,7 +525,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)start;
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, NULL, NULL);
++		    0, 0, &zp, kcred, vflg, NULL, zfs_init_user_ns);
+ 		break;
+ 	case TX_MKDIR_ATTR:
+ 		lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+@@ -542,7 +542,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)(lr + 1);
+ 
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, NULL, NULL);
++		    &zp, kcred, vflg, NULL, zfs_init_user_ns);
+ 		break;
+ 	case TX_MKXATTR:
+ 		error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
+@@ -551,7 +551,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		name = (char *)(lr + 1);
+ 		link = name + strlen(name) + 1;
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+-		    link, &zp, kcred, vflg, NULL);
++		    link, &zp, kcred, vflg, zfs_init_user_ns);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -663,7 +663,7 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 	if (lr->lr_common.lrc_txtype & TX_CI)
+ 		vflg |= FIGNORECASE;
+ 
+-	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, NULL);
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, zfs_init_user_ns);
+ 
+ 	zrele(tdzp);
+ 	zrele(sdzp);
+@@ -857,7 +857,7 @@ 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(zp, vap, 0, kcred, NULL);
++	error = zfs_setattr(zp, vap, 0, kcred, zfs_init_user_ns);
+ 
+ 	zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ 	zfsvfs->z_fuid_replay = NULL;
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index 6d74ffb633c..7ef1fcf99ed 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -165,9 +165,10 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 	ZFS_VERIFY_ZP(zp);
+ 
+ 	if (flag & V_ACE_MASK)
+-		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr, NULL);
++		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
++		    zfs_init_user_ns);
+ 	else
+-		error = zfs_zaccess_rwx(zp, mode, flag, cr, NULL);
++		error = zfs_zaccess_rwx(zp, mode, flag, cr, zfs_init_user_ns);
+ 
+ 	ZFS_EXIT(zfsvfs);
+ 	return (error);
+diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
+index 211e6a1dd41..56029b44677 100644
+--- a/tests/runfiles/linux.run
++++ b/tests/runfiles/linux.run
+@@ -191,5 +191,5 @@ tags = ['functional', 'userquota']
+ 
+ [tests/functional/idmap_mount:Linux]
+ tests = ['idmap_mount_001', 'idmap_mount_002', 'idmap_mount_003',
+-    'idmap_mount_004']
++    'idmap_mount_004', 'idmap_mount_005']
+ tags = ['functional', 'idmap_mount']
+diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in
+index a98e8175b06..2bbd765dd73 100755
+--- a/tests/test-runner/bin/zts-report.py.in
++++ b/tests/test-runner/bin/zts-report.py.in
+@@ -298,6 +298,7 @@ elif sys.platform.startswith('linux'):
+         'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason],
+         'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
+         'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
++        'idmap_mount/idmap_mount_005': ['SKIP', idmap_reason],
+     })
+ 
+ 
+diff --git a/tests/zfs-tests/cmd/idmap_util/idmap_util.c b/tests/zfs-tests/cmd/idmap_util/idmap_util.c
+index a9731f00dbc..39a2a147701 100644
+--- a/tests/zfs-tests/cmd/idmap_util/idmap_util.c
++++ b/tests/zfs-tests/cmd/idmap_util/idmap_util.c
+@@ -36,6 +36,7 @@
+ #include <errno.h>
+ #include <sched.h>
+ #include <syscall.h>
++#include <sys/socket.h>
+ 
+ #include <sys/list.h>
+ 
+@@ -460,43 +461,56 @@ userns_fd_from_idmap(list_t *head)
+ {
+ 	pid_t pid;
+ 	int ret, fd;
+-	int pipe_fd[2];
++	int fds[2];
+ 	char c;
+ 	int saved_errno = 0;
+ 
+-	/* pipe for bidirectional communication */
+-	ret = pipe(pipe_fd);
++	/* socketpair for bidirectional communication */
++	ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
+ 	if (ret) {
+-		log_errno("pipe");
++		log_errno("socketpair");
+ 		return (-errno);
+ 	}
+ 
+ 	pid = fork();
+ 	if (pid < 0) {
+ 		log_errno("fork");
+-		return (-errno);
++		fd = -errno;
++		goto out;
+ 	}
+ 
+ 	if (pid == 0) {
+ 		/* child process */
+-		close(pipe_fd[0]);
+ 		ret = unshare(CLONE_NEWUSER);
+ 		if (ret == 0) {
+ 			/* notify the parent of success */
+-			ret = write_buf(pipe_fd[1], "1", 1);
++			ret = write_buf(fds[1], "1", 1);
++			if (ret < 0)
++				saved_errno = errno;
++			else {
++				/*
++				 * Until the parent has written to idmap,
++				 * we cannot exit, otherwise the defunct
++				 * process is owned by the real root, writing
++				 * to its idmap ends up with EPERM in the
++				 * context of a user ns
++				 */
++				ret = read_buf(fds[1], &c, 1);
++				if (ret < 0)
++					saved_errno = errno;
++			}
+ 		} else {
+ 			saved_errno = errno;
+ 			log_errno("unshare");
+-			ret = write_buf(pipe_fd[1], "0", 1);
++			ret = write_buf(fds[1], "0", 1);
++			if (ret < 0)
++				saved_errno = errno;
+ 		}
+-		if (ret < 0)
+-			saved_errno = errno;
+-		close(pipe_fd[1]);
+ 		exit(saved_errno);
+ 	}
++
+ 	/* parent process */
+-	close(pipe_fd[1]);
+-	ret = read_buf(pipe_fd[0], &c, 1);
++	ret = read_buf(fds[0], &c, 1);
+ 	if (ret == 1 && c == '1') {
+ 		ret = write_pid_idmaps(pid, head);
+ 		if (!ret) {
+@@ -506,11 +520,15 @@ userns_fd_from_idmap(list_t *head)
+ 		} else {
+ 			fd = -ret;
+ 		}
++		/* Let child know it can exit */
++		(void) write_buf(fds[0], "1", 1);
+ 	} else {
+ 		fd = -EBADF;
+ 	}
+-	close(pipe_fd[0]);
+ 	(void) wait_for_pid(pid);
++out:
++	close(fds[0]);
++	close(fds[1]);
+ 	return (fd);
+ }
+ 
+@@ -534,7 +552,8 @@ is_idmap_supported(char *path)
+ 	};
+ 
+ 	/* strtok_r() won't be happy with a const string */
+-	char *input = strdup("b:0:1000000:1000000");
++	/* To check if idmapped mount can be done in a user ns, map 0 to 0 */
++	char *input = strdup("b:0:0:1");
+ 
+ 	if (!input) {
+ 		errno = ENOMEM;
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh
+new file mode 100755
+index 00000000000..a4ecea92c12
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh
+@@ -0,0 +1,138 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib
++
++#
++#
++# DESCRIPTION:
++#       Test idmapped mount in a user namespace
++#
++# STRATEGY:
++#	1. Create a zoned dataset
++#	2. Create a user namespace and designate the dataset to the zone
++#	3. In the zone, mount the dataset to "idmap_test"
++#	4. In the zone, idmap mount the dataset mountpoint to "idmap_dest"
++#	5. Do some file operations in the idmapped mountpoint "idmap_dest"
++#	6. Check the owner of files/folder in the mount point "idmap_test"
++#	7. unmount the mountpoints in the zone
++#	8. Remount the dataset in global zone to "idmap_test"
++#	9. Check the owenr of filers/folder in the mountpoint "idmap_test"
++#
++
++verify_runnable "global"
++
++export WORKDIR=$TESTDIR/idmap_test
++export IDMAPDIR=$TESTDIR/idmap_dest
++
++function cleanup
++{
++	if [[ -v unshared_pid ]]; then
++		zfs unzone /proc/$unshared_pid/ns/user "$TESTPOOL/userns"
++		kill -TERM ${unshared_pid}
++	fi
++	if mountpoint $WORKDIR; then
++		log_must umount $WORKDIR
++	fi
++	log_must rm -rf $WORKDIR
++}
++
++log_onexit cleanup
++
++if ! idmap_util -c $TESTDIR; then
++	log_unsupported "Idmap mount not supported."
++fi
++
++unshare -Urm echo test
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++
++log_must zfs create -o zoned=off -o mountpoint=$WORKDIR "$TESTPOOL/userns"
++
++# "root" user and group in the user ns
++log_must chown 1000000:1000000 $WORKDIR
++log_must zfs set zoned=on "$TESTPOOL/userns"
++
++log_must mkdir -p $IDMAPDIR
++
++unshare -Um /usr/bin/sleep 2h &
++unshared_pid=$!
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++# wait for userns to be ready
++sleep 1
++echo "0 1000000 1000000" > /proc/$unshared_pid/uid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to uid_map"
++fi
++echo "0 1000000 1000000" > /proc/$unshared_pid/gid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to gid_map"
++fi
++
++NSENTER="nsenter -t $unshared_pid --all -S 0 -G 0"
++
++log_must zfs zone /proc/$unshared_pid/ns/user "$TESTPOOL/userns"
++log_must $NSENTER zfs mount "$TESTPOOL/userns"
++log_must $NSENTER chmod 777 $WORKDIR
++
++$NSENTER idmap_util -c $WORKDIR
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Idmapped mount not supported in a user namespace"
++fi
++
++log_must $NSENTER idmap_util -m b:0:10000:100000 $WORKDIR $IDMAPDIR
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups touch $IDMAPDIR/file
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups mkdir $IDMAPDIR/folder
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups ln -s file $IDMAPDIR/file-soft
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups ln $IDMAPDIR/file $IDMAPDIR/file-hard
++
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups cp -p $IDMAPDIR/file $IDMAPDIR/folder/file-p
++log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups cp $IDMAPDIR/file $IDMAPDIR/folder/file
++
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file)"
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder)"
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file-soft)"
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file-hard)"
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder/file-p)"
++log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder/file)"
++
++log_must $NSENTER umount $IDMAPDIR
++log_must $NSENTER umount $WORKDIR
++
++log_must zfs unzone /proc/$unshared_pid/ns/user "$TESTPOOL/userns"
++log_must kill -TERM $unshared_pid
++unset unshared_pid
++log_must zfs set zoned=off "$TESTPOOL/userns"
++log_must zfs mount "$TESTPOOL/userns"
++
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file)"
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder)"
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file-soft)"
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file-hard)"
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder/file-p)"
++log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder/file)"
++
++log_pass "Testing idmapped mount in a user ns is successful."
++
+
+From bff61819cd1f0cd1e44b06dca4e8fb24506340f0 Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:27:12 -0500
+Subject: [PATCH 2/8] Backport "Use kcred->user_ns to reference &init_user_ns"
+
+---
+ include/os/linux/kernel/linux/xattr_compat.h |  6 +++---
+ include/os/linux/spl/sys/cred.h              |  6 ++----
+ include/os/linux/spl/sys/types.h             |  1 -
+ module/os/linux/spl/spl-cred.c               |  8 --------
+ module/os/linux/zfs/zfs_acl.c                | 10 +++++-----
+ module/os/linux/zfs/zfs_dir.c                |  4 ++--
+ module/os/linux/zfs/zfs_ioctl_os.c           |  4 ----
+ module/os/linux/zfs/zfs_vnops_os.c           | 12 ++++++------
+ module/os/linux/zfs/zfs_znode.c              |  2 +-
+ module/os/linux/zfs/zpl_ctldir.c             |  2 +-
+ module/os/linux/zfs/zpl_file.c               |  6 +++---
+ module/os/linux/zfs/zpl_inode.c              | 14 +++++++-------
+ module/os/linux/zfs/zpl_xattr.c              |  2 +-
+ module/zfs/zfs_replay.c                      | 14 +++++++-------
+ module/zfs/zfs_vnops.c                       |  4 ++--
+ 15 files changed, 40 insertions(+), 55 deletions(-)
+
+diff --git a/include/os/linux/kernel/linux/xattr_compat.h b/include/os/linux/kernel/linux/xattr_compat.h
+index 2c33a58d5c7..83763c64a14 100644
+--- a/include/os/linux/kernel/linux/xattr_compat.h
++++ b/include/os/linux/kernel/linux/xattr_compat.h
+@@ -160,7 +160,7 @@ fn(const struct xattr_handler *handler, struct dentry *dentry,		\
+     struct inode *inode, const char *name, const void *buffer,		\
+     size_t size, int flags)						\
+ {									\
+-	return (__ ## fn(zfs_init_user_ns, inode, name, buffer, size, flags));\
++	return (__ ## fn(kcred->user_ns, inode, name, buffer, size, flags));\
+ }
+ /*
+  * 4.4 API change,
+@@ -174,7 +174,7 @@ static int								\
+ fn(const struct xattr_handler *handler, struct dentry *dentry,		\
+     const char *name, const void *buffer, size_t size, int flags)	\
+ {									\
+-	return (__ ## fn(zfs_init_user_ns, dentry->d_inode, name,	\
++	return (__ ## fn(kcred->user_ns, dentry->d_inode, name,	\
+ 	    buffer, size, flags));					\
+ }
+ /*
+@@ -188,7 +188,7 @@ static int								\
+ fn(struct dentry *dentry, const char *name, const void *buffer,		\
+     size_t size, int flags, int unused_handler_flags)			\
+ {									\
+-	return (__ ## fn(zfs_init_user_ns, dentry->d_inode, name,	\
++	return (__ ## fn(kcred->user_ns, dentry->d_inode, name,	\
+ 	    buffer, size, flags));					\
+ }
+ #else
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index 13e420e8de2..6c891164b08 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -45,14 +45,12 @@ typedef struct cred cred_t;
+ #define	SGID_TO_KGID(x)		(KGIDT_INIT(x))
+ #define	KGIDP_TO_SGIDP(x)	(&(x)->val)
+ 
+-extern struct user_namespace *zfs_get_init_userns(void);
+-
+ /* Check if the user ns is the initial one */
+ static inline boolean_t
+ zfs_is_init_userns(struct user_namespace *user_ns)
+ {
+ #if defined(CONFIG_USER_NS)
+-	return (user_ns == zfs_init_user_ns);
++	return (user_ns == kcred->user_ns);
+ #else
+ 	return (B_FALSE);
+ #endif
+@@ -63,7 +61,7 @@ static inline struct user_namespace *zfs_i_user_ns(struct inode *inode)
+ #ifdef HAVE_SUPER_USER_NS
+ 	return (inode->i_sb->s_user_ns);
+ #else
+-	return (zfs_init_user_ns);
++	return (kcred->user_ns);
+ #endif
+ }
+ 
+diff --git a/include/os/linux/spl/sys/types.h b/include/os/linux/spl/sys/types.h
+index e682c0560d5..cae1bbddf10 100644
+--- a/include/os/linux/spl/sys/types.h
++++ b/include/os/linux/spl/sys/types.h
+@@ -56,6 +56,5 @@ typedef int			minor_t;
+ 
+ struct user_namespace;
+ typedef struct user_namespace	zuserns_t;
+-extern zuserns_t *zfs_init_user_ns;
+ 
+ #endif	/* _SPL_TYPES_H */
+diff --git a/module/os/linux/spl/spl-cred.c b/module/os/linux/spl/spl-cred.c
+index 1f406ec540b..f81b9540a63 100644
+--- a/module/os/linux/spl/spl-cred.c
++++ b/module/os/linux/spl/spl-cred.c
+@@ -145,14 +145,6 @@ crgetgid(const cred_t *cr)
+ 	return (KGID_TO_SGID(cr->fsgid));
+ }
+ 
+-/* Return the initial user ns */
+-struct user_namespace *
+-zfs_get_init_userns(void)
+-{
+-	return (&init_user_ns);
+-}
+-
+-EXPORT_SYMBOL(zfs_get_init_userns);
+ EXPORT_SYMBOL(crhold);
+ EXPORT_SYMBOL(crfree);
+ EXPORT_SYMBOL(crgetuid);
+diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
+index 52978c1133e..3d31da6e35b 100644
+--- a/module/os/linux/zfs/zfs_acl.c
++++ b/module/os/linux/zfs/zfs_acl.c
+@@ -1980,7 +1980,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 		return (SET_ERROR(ENOSYS));
+ 
+ 	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr,
+-	    zfs_init_user_ns)))
++	    kcred->user_ns)))
+ 		return (error);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+@@ -2140,7 +2140,7 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 		return (SET_ERROR(EPERM));
+ 
+ 	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
+-	    zfs_init_user_ns)))
++	    kcred->user_ns)))
+ 		return (error);
+ 
+ 	error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
+@@ -2420,7 +2420,7 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+ 	uint32_t have = ACE_ALL_PERMS;
+ 
+ 	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr,
+-	    zfs_init_user_ns) != 0) {
++	    kcred->user_ns) != 0) {
+ 		uid_t owner;
+ 
+ 		owner = zfs_fuid_map_id(ZTOZSB(zp),
+@@ -2613,7 +2613,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+ 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
+ 	ZFS_ENTER(ZTOZSB(zdp));
+ 	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+-	    zfs_init_user_ns);
++	    kcred->user_ns);
+ 	ZFS_EXIT(ZTOZSB(zdp));
+ 	return (error);
+ }
+@@ -2790,7 +2790,7 @@ zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
+ {
+ 	int v4_mode = zfs_unix_to_v4(mode >> 6);
+ 
+-	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, zfs_init_user_ns));
++	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, kcred->user_ns));
+ }
+ 
+ /* See zfs_zaccess_delete() */
+diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c
+index 4a04cc37d5c..8c65c33628d 100644
+--- a/module/os/linux/zfs/zfs_dir.c
++++ b/module/os/linux/zfs/zfs_dir.c
+@@ -1067,7 +1067,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr)
+ 	*xzpp = NULL;
+ 
+ 	if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
+-	    &acl_ids, zfs_init_user_ns)) != 0)
++	    &acl_ids, kcred->user_ns)) != 0)
+ 		return (error);
+ 	if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) {
+ 		zfs_acl_ids_free(&acl_ids);
+@@ -1216,7 +1216,7 @@ zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
+ 
+ 	if ((uid = crgetuid(cr)) == downer || uid == fowner ||
+ 	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
+-	    zfs_init_user_ns) == 0)
++	    kcred->user_ns) == 0)
+ 		return (0);
+ 	else
+ 		return (secpolicy_vnode_remove(cr));
+diff --git a/module/os/linux/zfs/zfs_ioctl_os.c b/module/os/linux/zfs/zfs_ioctl_os.c
+index ad17973e7ad..29d968d22a4 100644
+--- a/module/os/linux/zfs/zfs_ioctl_os.c
++++ b/module/os/linux/zfs/zfs_ioctl_os.c
+@@ -291,8 +291,6 @@ zfsdev_detach(void)
+ #define	ZFS_DEBUG_STR	""
+ #endif
+ 
+-zuserns_t *zfs_init_user_ns;
+-
+ static int __init
+ openzfs_init(void)
+ {
+@@ -316,8 +314,6 @@ openzfs_init(void)
+ 	printk(KERN_NOTICE "ZFS: Posix ACLs disabled by kernel\n");
+ #endif /* CONFIG_FS_POSIX_ACL */
+ 
+-	zfs_init_user_ns = (zuserns_t *)zfs_get_init_userns();
+-
+ 	return (0);
+ }
+ 
+diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
+index d7abe558cd0..23f3d140cf5 100644
+--- a/module/os/linux/zfs/zfs_vnops_os.c
++++ b/module/os/linux/zfs/zfs_vnops_os.c
+@@ -501,7 +501,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 		 */
+ 
+ 		if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
+-		    B_TRUE, cr, zfs_init_user_ns))) {
++		    B_TRUE, cr, kcred->user_ns))) {
+ 			zrele(*zpp);
+ 			*zpp = NULL;
+ 		}
+@@ -520,7 +520,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 	 */
+ 
+ 	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+-	    zfs_init_user_ns))) {
++	    kcred->user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -1002,7 +1002,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_user_ns))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
+ 		goto out;
+ 	}
+ 
+@@ -1418,7 +1418,7 @@ zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr, zfs_init_user_ns))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
+ 		goto out;
+ 	}
+ 
+@@ -3373,7 +3373,7 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
+ 	}
+ 
+ 	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr,
+-	    zfs_init_user_ns))) {
++	    kcred->user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3962,7 +3962,7 @@ zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag,
+ 	 * operates directly on inodes, so we need to check access rights.
+ 	 */
+ 	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr,
+-	    zfs_init_user_ns))) {
++	    kcred->user_ns))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
+index 956346e0afd..5e820587388 100644
+--- a/module/os/linux/zfs/zfs_znode.c
++++ b/module/os/linux/zfs/zfs_znode.c
+@@ -1947,7 +1947,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
+ 	}
+ 
+ 	VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
+-	    cr, NULL, &acl_ids, zfs_init_user_ns));
++	    cr, NULL, &acl_ids, kcred->user_ns));
+ 	zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, rootzp);
+ 	error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
+diff --git a/module/os/linux/zfs/zpl_ctldir.c b/module/os/linux/zfs/zpl_ctldir.c
+index 6cc99d6f37a..e4f191ba051 100644
+--- a/module/os/linux/zfs/zpl_ctldir.c
++++ b/module/os/linux/zfs/zpl_ctldir.c
+@@ -366,7 +366,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ #ifdef HAVE_IOPS_MKDIR_USERNS
+ 	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, user_ns);
+ #else
+-	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, zfs_init_user_ns);
++	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, kcred->user_ns);
+ #endif
+ 
+ 	error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index 8d320101ee3..4ad30efd3ec 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -971,7 +971,7 @@ zpl_ioctl_setflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1019,7 +1019,7 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1107,7 +1107,7 @@ zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, zfs_init_user_ns);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, kcred->user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c
+index 5eb95676e88..62461841248 100644
+--- a/module/os/linux/zfs/zpl_inode.c
++++ b/module/os/linux/zfs/zpl_inode.c
+@@ -144,7 +144,7 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_CREATE_USERNS
+-	zuserns_t *user_ns = zfs_init_user_ns;
++	zuserns_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -191,7 +191,7 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_MKNOD_USERNS
+-	zuserns_t *user_ns = zfs_init_user_ns;
++	zuserns_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	/*
+@@ -251,7 +251,7 @@ zpl_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_TMPFILE_USERNS
+-	zuserns_t *userns = zfs_init_user_ns;
++	zuserns_t *userns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -339,7 +339,7 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_MKDIR_USERNS
+-	zuserns_t *user_ns = zfs_init_user_ns;
++	zuserns_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+@@ -496,7 +496,7 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ #ifdef HAVE_SETATTR_PREPARE_USERNS
+ 	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, user_ns);
+ #else
+-	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, zfs_init_user_ns);
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, kcred->user_ns);
+ #endif
+ 	if (!error && (ia->ia_valid & ATTR_MODE))
+ 		error = zpl_chmod_acl(ip);
+@@ -523,7 +523,7 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_RENAME_USERNS
+-	zuserns_t *user_ns = zfs_init_user_ns;
++	zuserns_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	/* We don't have renameat2(2) support */
+@@ -564,7 +564,7 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ #ifndef HAVE_IOPS_SYMLINK_USERNS
+-	zuserns_t *user_ns = zfs_init_user_ns;
++	zuserns_t *user_ns = kcred->user_ns;
+ #endif
+ 
+ 	crhold(cr);
+diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c
+index c2be9352ae1..b377651bad4 100644
+--- a/module/os/linux/zfs/zpl_xattr.c
++++ b/module/os/linux/zfs/zpl_xattr.c
+@@ -496,7 +496,7 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
+ 		vap->va_gid = crgetgid(cr);
+ 
+ 		error = -zfs_create(dxzp, (char *)name, vap, 0, 0644, &xzp,
+-		    cr, 0, NULL, zfs_init_user_ns);
++		    cr, 0, NULL, kcred->user_ns);
+ 		if (error)
+ 			goto out;
+ 	}
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index 950339ceade..c83867fe535 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -385,7 +385,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 		}
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, &vsec, zfs_init_user_ns);
++		    0, 0, &zp, kcred, vflg, &vsec, kcred->user_ns);
+ 		break;
+ 	case TX_MKDIR_ACL:
+ 		aclstart = (caddr_t)(lracl + 1);
+@@ -415,7 +415,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 			    lr->lr_uid, lr->lr_gid);
+ 		}
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, &vsec, zfs_init_user_ns);
++		    &zp, kcred, vflg, &vsec, kcred->user_ns);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -525,7 +525,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)start;
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, NULL, zfs_init_user_ns);
++		    0, 0, &zp, kcred, vflg, NULL, kcred->user_ns);
+ 		break;
+ 	case TX_MKDIR_ATTR:
+ 		lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+@@ -542,7 +542,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)(lr + 1);
+ 
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, NULL, zfs_init_user_ns);
++		    &zp, kcred, vflg, NULL, kcred->user_ns);
+ 		break;
+ 	case TX_MKXATTR:
+ 		error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
+@@ -551,7 +551,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		name = (char *)(lr + 1);
+ 		link = name + strlen(name) + 1;
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+-		    link, &zp, kcred, vflg, zfs_init_user_ns);
++		    link, &zp, kcred, vflg, kcred->user_ns);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -663,7 +663,7 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 	if (lr->lr_common.lrc_txtype & TX_CI)
+ 		vflg |= FIGNORECASE;
+ 
+-	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, zfs_init_user_ns);
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, kcred->user_ns);
+ 
+ 	zrele(tdzp);
+ 	zrele(sdzp);
+@@ -857,7 +857,7 @@ 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(zp, vap, 0, kcred, zfs_init_user_ns);
++	error = zfs_setattr(zp, vap, 0, kcred, kcred->user_ns);
+ 
+ 	zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ 	zfsvfs->z_fuid_replay = NULL;
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index 7ef1fcf99ed..f8fa53bd615 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -166,9 +166,9 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 
+ 	if (flag & V_ACE_MASK)
+ 		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+-		    zfs_init_user_ns);
++		    kcred->user_ns);
+ 	else
+-		error = zfs_zaccess_rwx(zp, mode, flag, cr, zfs_init_user_ns);
++		error = zfs_zaccess_rwx(zp, mode, flag, cr, kcred->user_ns);
+ 
+ 	ZFS_EXIT(zfsvfs);
+ 	return (error);
+
+From 1ba71ae1eccf1057d89253f94ac638d493c5486f Mon Sep 17 00:00:00 2001
+From: Youzhong Yang <yyang@mathworks.com>
+Date: Tue, 1 Nov 2022 19:18:59 -0400
+Subject: [PATCH 3/8] No need to include sys/cred.h
+
+Signed-off-by: Youzhong Yang <yyang@mathworks.com>
+---
+ module/os/linux/zfs/zfs_ioctl_os.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/module/os/linux/zfs/zfs_ioctl_os.c b/module/os/linux/zfs/zfs_ioctl_os.c
+index 29d968d22a4..d70b31f9f10 100644
+--- a/module/os/linux/zfs/zfs_ioctl_os.c
++++ b/module/os/linux/zfs/zfs_ioctl_os.c
+@@ -60,7 +60,6 @@
+ #include <sys/dsl_crypt.h>
+ #include <sys/crypto/icp.h>
+ #include <sys/zstd/zstd.h>
+-#include <sys/cred.h>
+ 
+ #include <sys/zfs_ioctl_impl.h>
+ 
+
+From 132f2bfb9ed8f7544415d0ef5e921a556b8ed613 Mon Sep 17 00:00:00 2001
+From: Youzhong Yang <yyang@mathworks.com>
+Date: Thu, 3 Nov 2022 13:49:46 +0000
+Subject: [PATCH 4/8] Declare init_task in cred.h, fix build error on 18.04
+
+Signed-off-by: Youzhong Yang <yyang@mathworks.com>
+---
+ include/os/linux/spl/sys/cred.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index 6c891164b08..3bed5fa013e 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -31,6 +31,8 @@
+ 
+ typedef struct cred cred_t;
+ 
++extern struct task_struct init_task;
++
+ #define	kcred		((cred_t *)(init_task.cred))
+ #define	CRED()		((cred_t *)current_cred())
+ 
+
+From 61d77dcd1a284c64c533c34c11c688f5003b5ca8 Mon Sep 17 00:00:00 2001
+From: Youzhong Yang <yyang@mathworks.com>
+Date: Thu, 3 Nov 2022 10:35:25 -0400
+Subject: [PATCH 5/8] Include sched.h for task_struct
+
+Signed-off-by: Youzhong Yang <yyang@mathworks.com>
+---
+ include/os/linux/spl/sys/cred.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index 3bed5fa013e..75ad400d312 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -26,6 +26,7 @@
+ 
+ #include <linux/module.h>
+ #include <linux/cred.h>
++#include <linux/sched.h>
+ #include <sys/types.h>
+ #include <sys/vfs.h>
+ 
+
+From 9919d64fe026ab3361bdea443957d95fcc928c49 Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:27:44 -0500
+Subject: [PATCH 6/8] Backport "Fix FreeBSD build errors"
+
+---
+ module/zfs/zfs_replay.c | 32 +++++++++++++++++++++++++++++++-
+ module/zfs/zfs_vnops.c  |  8 ++++++++
+ 2 files changed, 39 insertions(+), 1 deletion(-)
+
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index c83867fe535..491314716fa 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -385,7 +385,11 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 		}
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
++#if defined(__linux__)
+ 		    0, 0, &zp, kcred, vflg, &vsec, kcred->user_ns);
++#else
++		    0, 0, &zp, kcred, vflg, &vsec, NULL);
++#endif
+ 		break;
+ 	case TX_MKDIR_ACL:
+ 		aclstart = (caddr_t)(lracl + 1);
+@@ -415,7 +419,11 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 			    lr->lr_uid, lr->lr_gid);
+ 		}
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
++#if defined(__linux__)
+ 		    &zp, kcred, vflg, &vsec, kcred->user_ns);
++#else
++		    &zp, kcred, vflg, &vsec, NULL);
++#endif
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -525,7 +533,11 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)start;
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
++#if defined(__linux__)
+ 		    0, 0, &zp, kcred, vflg, NULL, kcred->user_ns);
++#else
++		    0, 0, &zp, kcred, vflg, NULL, NULL);
++#endif
+ 		break;
+ 	case TX_MKDIR_ATTR:
+ 		lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+@@ -542,7 +554,12 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)(lr + 1);
+ 
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
++#if defined(__linux__)
+ 		    &zp, kcred, vflg, NULL, kcred->user_ns);
++#else
++		    &zp, kcred, vflg, NULL, NULL);
++#endif
++
+ 		break;
+ 	case TX_MKXATTR:
+ 		error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
+@@ -551,7 +568,11 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		name = (char *)(lr + 1);
+ 		link = name + strlen(name) + 1;
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
++#if defined(__linux__)
+ 		    link, &zp, kcred, vflg, kcred->user_ns);
++#else
++		    link, &zp, kcred, vflg, NULL);
++#endif
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -663,7 +684,12 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 	if (lr->lr_common.lrc_txtype & TX_CI)
+ 		vflg |= FIGNORECASE;
+ 
+-	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, kcred->user_ns);
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
++#if defined(__linux__)
++	    kcred->user_ns);
++#else
++	    NULL);
++#endif
+ 
+ 	zrele(tdzp);
+ 	zrele(sdzp);
+@@ -857,7 +883,11 @@ 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);
+ 
++#if defined(__linux__)
+ 	error = zfs_setattr(zp, vap, 0, kcred, kcred->user_ns);
++#else
++	error = zfs_setattr(zp, vap, 0, kcred, NULL);
++#endif
+ 
+ 	zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ 	zfsvfs->z_fuid_replay = NULL;
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index f8fa53bd615..3a46e1d0686 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -166,9 +166,17 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 
+ 	if (flag & V_ACE_MASK)
+ 		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
++#if defined(__linux__)
+ 		    kcred->user_ns);
++#else
++		    NULL);
++#endif
+ 	else
++#if defined(__linux__)
+ 		error = zfs_zaccess_rwx(zp, mode, flag, cr, kcred->user_ns);
++#else
++		error = zfs_zaccess_rwx(zp, mode, flag, cr, NULL);
++#endif
+ 
+ 	ZFS_EXIT(zfsvfs);
+ 	return (error);
+
+From 0eac99fb03f77c4dc19a11850f62993ae6340c59 Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:28:39 -0500
+Subject: [PATCH 7/8] Backport "Fix style errors"
+
+---
+ module/zfs/zfs_replay.c | 18 ++++++++++++------
+ module/zfs/zfs_vnops.c  |  3 ++-
+ 2 files changed, 14 insertions(+), 7 deletions(-)
+
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index 491314716fa..755bbc6cd98 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -384,10 +384,11 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 			    lr->lr_uid, lr->lr_gid);
+ 		}
+ 
+-		error = zfs_create(dzp, name, &xva.xva_vattr,
+ #if defined(__linux__)
++		error = zfs_create(dzp, name, &xva.xva_vattr,
+ 		    0, 0, &zp, kcred, vflg, &vsec, kcred->user_ns);
+ #else
++		error = zfs_create(dzp, name, &xva.xva_vattr,
+ 		    0, 0, &zp, kcred, vflg, &vsec, NULL);
+ #endif
+ 		break;
+@@ -418,10 +419,11 @@ 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(dzp, name, &xva.xva_vattr,
+ #if defined(__linux__)
++		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, &vsec, kcred->user_ns);
+ #else
++		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, &vsec, NULL);
+ #endif
+ 		break;
+@@ -532,10 +534,11 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		if (name == NULL)
+ 			name = (char *)start;
+ 
+-		error = zfs_create(dzp, name, &xva.xva_vattr,
+ #if defined(__linux__)
++		error = zfs_create(dzp, name, &xva.xva_vattr,
+ 		    0, 0, &zp, kcred, vflg, NULL, kcred->user_ns);
+ #else
++		error = zfs_create(dzp, name, &xva.xva_vattr,
+ 		    0, 0, &zp, kcred, vflg, NULL, NULL);
+ #endif
+ 		break;
+@@ -553,10 +556,11 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		if (name == NULL)
+ 			name = (char *)(lr + 1);
+ 
+-		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ #if defined(__linux__)
++		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, NULL, kcred->user_ns);
+ #else
++		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+ 		    &zp, kcred, vflg, NULL, NULL);
+ #endif
+ 
+@@ -567,10 +571,11 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 	case TX_SYMLINK:
+ 		name = (char *)(lr + 1);
+ 		link = name + strlen(name) + 1;
+-		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+ #if defined(__linux__)
++		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+ 		    link, &zp, kcred, vflg, kcred->user_ns);
+ #else
++		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+ 		    link, &zp, kcred, vflg, NULL);
+ #endif
+ 		break;
+@@ -684,10 +689,11 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 	if (lr->lr_common.lrc_txtype & TX_CI)
+ 		vflg |= FIGNORECASE;
+ 
+-	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
+ #if defined(__linux__)
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
+ 	    kcred->user_ns);
+ #else
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg,
+ 	    NULL);
+ #endif
+ 
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index 3a46e1d0686..2b6d35282a0 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -165,10 +165,11 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 	ZFS_VERIFY_ZP(zp);
+ 
+ 	if (flag & V_ACE_MASK)
+-		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+ #if defined(__linux__)
++		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+ 		    kcred->user_ns);
+ #else
++		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr,
+ 		    NULL);
+ #endif
+ 	else
+
+From d628d7ff4325a034afbf23460f667ece64781bbf Mon Sep 17 00:00:00 2001
+From: Youzhong Yang <yyang@mathworks.com>
+Date: Sat, 5 Nov 2022 00:56:25 +0000
+Subject: [PATCH 8/8] zpl_setattr() needs a fix to work on kernel 6
+
+Signed-off-by: Youzhong Yang <yyang@mathworks.com>
+---
+ config/kernel-iattr-vfsid.m4    | 24 ++++++++++++++++++++++++
+ config/kernel.m4                |  2 ++
+ module/os/linux/zfs/zpl_inode.c | 16 ++++++++++++++--
+ 3 files changed, 40 insertions(+), 2 deletions(-)
+ create mode 100644 config/kernel-iattr-vfsid.m4
+
+diff --git a/config/kernel-iattr-vfsid.m4 b/config/kernel-iattr-vfsid.m4
+new file mode 100644
+index 00000000000..75bc4613b83
+--- /dev/null
++++ b/config/kernel-iattr-vfsid.m4
+@@ -0,0 +1,24 @@
++dnl #
++dnl # 6.0 API change
++dnl # struct iattr has two unions for the uid and gid
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_SRC_IATTR_VFSID], [
++	ZFS_LINUX_TEST_SRC([iattr_vfsid], [
++		#include <linux/fs.h>
++	], [
++		struct iattr ia;
++		ia.ia_vfsuid = (vfsuid_t){0};
++		ia.ia_vfsgid = (vfsgid_t){0};
++	])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_IATTR_VFSID], [
++	AC_MSG_CHECKING([whether iattr->ia_vfsuid and iattr->ia_vfsgid exist])
++	ZFS_LINUX_TEST_RESULT([iattr_vfsid], [
++		AC_MSG_RESULT(yes)
++		AC_DEFINE(HAVE_IATTR_VFSID, 1,
++		    [iattr->ia_vfsuid and iattr->ia_vfsgid exist])
++	],[
++		AC_MSG_RESULT(no)
++	])
++])
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index 44f4d072501..1998b831e96 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -146,6 +146,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_ZERO_PAGE
+ 	ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC
+ 	ZFS_AC_KERNEL_SRC_IDMAP_MNT_API
++	ZFS_AC_KERNEL_SRC_IATTR_VFSID
+ 
+ 	AC_MSG_CHECKING([for available kernel interfaces])
+ 	ZFS_LINUX_TEST_COMPILE_ALL([kabi])
+@@ -265,6 +266,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_ZERO_PAGE
+ 	ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC
+ 	ZFS_AC_KERNEL_IDMAP_MNT_API
++	ZFS_AC_KERNEL_IATTR_VFSID
+ ])
+ 
+ dnl #
+diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c
+index 62461841248..ea659d9618a 100644
+--- a/module/os/linux/zfs/zpl_inode.c
++++ b/module/os/linux/zfs/zpl_inode.c
+@@ -482,8 +482,20 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+ 	vap->va_mask = ia->ia_valid & ATTR_IATTR_MASK;
+ 	vap->va_mode = ia->ia_mode;
+-	vap->va_uid = KUID_TO_SUID(ia->ia_uid);
+-	vap->va_gid = KGID_TO_SGID(ia->ia_gid);
++	if (ia->ia_valid & ATTR_UID)
++#ifdef HAVE_IATTR_VFSID
++		vap->va_uid = zfs_vfsuid_to_uid(user_ns, zfs_i_user_ns(ip),
++		    __vfsuid_val(ia->ia_vfsuid));
++#else
++		vap->va_uid = KUID_TO_SUID(ia->ia_uid);
++#endif
++	if (ia->ia_valid & ATTR_GID)
++#ifdef HAVE_IATTR_VFSID
++		vap->va_gid = zfs_vfsgid_to_gid(user_ns, zfs_i_user_ns(ip),
++		    __vfsgid_val(ia->ia_vfsgid));
++#else
++		vap->va_gid = KGID_TO_SGID(ia->ia_gid);
++#endif
+ 	vap->va_size = ia->ia_size;
+ 	vap->va_atime = ia->ia_atime;
+ 	vap->va_mtime = ia->ia_mtime;
diff --git a/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount.patch b/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount.patch
new file mode 100644
index 0000000..ae05264
--- /dev/null
+++ b/sys-fs/zfs-kmod/files/2.1.11-support-idmapped-mount.patch
@@ -0,0 +1,3203 @@
+From 259662bc5e8b234dd8f253acc8cff9083927f560 Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:04:02 -0500
+Subject: [PATCH 1/3] Backport "Support idmapped mount"
+
+---
+ config/kernel-idmap_mnt_api.m4            | 25 ++++++++
+ config/kernel.m4                          |  2 +
+ include/os/freebsd/spl/sys/types.h        |  2 +
+ include/os/freebsd/zfs/sys/zfs_vnops_os.h | 12 ++--
+ include/os/linux/spl/sys/cred.h           | 28 +++++++++
+ include/os/linux/spl/sys/types.h          |  3 +
+ include/os/linux/zfs/sys/policy.h         |  4 +-
+ include/os/linux/zfs/sys/zfs_vnops_os.h   | 15 +++--
+ include/os/linux/zfs/sys/zpl.h            |  2 +-
+ include/sys/zfs_acl.h                     | 11 ++--
+ module/os/freebsd/zfs/zfs_acl.c           | 30 +++++----
+ module/os/freebsd/zfs/zfs_dir.c           |  4 +-
+ module/os/freebsd/zfs/zfs_vnops_os.c      | 60 ++++++++++--------
+ module/os/freebsd/zfs/zfs_znode.c         |  4 +-
+ module/os/linux/zfs/policy.c              | 13 ++--
+ module/os/linux/zfs/zfs_acl.c             | 76 +++++++++++++---------
+ module/os/linux/zfs/zfs_dir.c             |  4 +-
+ module/os/linux/zfs/zfs_vnops_os.c        | 77 ++++++++++++++---------
+ module/os/linux/zfs/zfs_znode.c           |  2 +-
+ module/os/linux/zfs/zpl_ctldir.c          |  6 +-
+ module/os/linux/zfs/zpl_file.c            |  6 +-
+ module/os/linux/zfs/zpl_inode.c           | 62 +++++++++++++-----
+ module/os/linux/zfs/zpl_super.c           |  5 ++
+ module/os/linux/zfs/zpl_xattr.c           |  2 +-
+ module/zfs/zfs_replay.c                   | 14 ++---
+ module/zfs/zfs_vnops.c                    |  4 +-
+ 26 files changed, 312 insertions(+), 161 deletions(-)
+ create mode 100644 config/kernel-idmap_mnt_api.m4
+
+diff --git a/config/kernel-idmap_mnt_api.m4 b/config/kernel-idmap_mnt_api.m4
+new file mode 100644
+index 00000000000..47ddc5702fb
+--- /dev/null
++++ b/config/kernel-idmap_mnt_api.m4
+@@ -0,0 +1,25 @@
++dnl #
++dnl # 5.12 API
++dnl #
++dnl # Check if APIs for idmapped mount are available
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_SRC_IDMAP_MNT_API], [
++        ZFS_LINUX_TEST_SRC([idmap_mnt_api], [
++                #include <linux/fs.h>
++        ],[
++		int fs_flags = 0;
++		fs_flags |= FS_ALLOW_IDMAP;
++        ])
++])
++
++AC_DEFUN([ZFS_AC_KERNEL_IDMAP_MNT_API], [
++        AC_MSG_CHECKING([whether APIs for idmapped mount are present])
++        ZFS_LINUX_TEST_RESULT([idmap_mnt_api], [
++                AC_MSG_RESULT([yes])
++                AC_DEFINE(HAVE_IDMAP_MNT_API, 1,
++                    [APIs for idmapped mount are present])
++        ],[
++                AC_MSG_RESULT([no])
++        ])
++])
++
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index 7b0c59d26d4..44f4d072501 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -145,6 +145,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
+ 	ZFS_AC_KERNEL_SRC_KTHREAD
+ 	ZFS_AC_KERNEL_SRC_ZERO_PAGE
+ 	ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC
++	ZFS_AC_KERNEL_SRC_IDMAP_MNT_API
+ 
+ 	AC_MSG_CHECKING([for available kernel interfaces])
+ 	ZFS_LINUX_TEST_COMPILE_ALL([kabi])
+@@ -263,6 +264,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
+ 	ZFS_AC_KERNEL_KTHREAD
+ 	ZFS_AC_KERNEL_ZERO_PAGE
+ 	ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC
++	ZFS_AC_KERNEL_IDMAP_MNT_API
+ ])
+ 
+ dnl #
+diff --git a/include/os/freebsd/spl/sys/types.h b/include/os/freebsd/spl/sys/types.h
+index ecb91fd1bb8..6557c840feb 100644
+--- a/include/os/freebsd/spl/sys/types.h
++++ b/include/os/freebsd/spl/sys/types.h
+@@ -104,5 +104,7 @@ typedef	u_longlong_t	len_t;
+ 
+ typedef	longlong_t	diskaddr_t;
+ 
++typedef void		zuserns_t;
++
+ #include <sys/debug.h>
+ #endif	/* !_OPENSOLARIS_SYS_TYPES_H_ */
+diff --git a/include/os/freebsd/zfs/sys/zfs_vnops_os.h b/include/os/freebsd/zfs/sys/zfs_vnops_os.h
+index bf5e03b24c0..460aecd2e70 100644
+--- a/include/os/freebsd/zfs/sys/zfs_vnops_os.h
++++ b/include/os/freebsd/zfs/sys/zfs_vnops_os.h
+@@ -35,20 +35,22 @@ int dmu_read_pages(objset_t *os, uint64_t object, vm_page_t *ma, int count,
+     int *rbehind, int *rahead, int last_size);
+ extern int zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags);
+ extern int zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap,
+-    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp);
++    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns);
+ extern int zfs_rmdir(znode_t *dzp, const char *name, znode_t *cwd,
+     cred_t *cr, int flags);
+-extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr);
++extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr,
++    zuserns_t *mnt_ns);
+ extern int zfs_rename(znode_t *sdzp, const char *snm, znode_t *tdzp,
+-    const char *tnm, cred_t *cr, int flags);
++    const char *tnm, cred_t *cr, int flags, zuserns_t *mnt_ns);
+ extern int zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap,
+-    const char *link, znode_t **zpp, cred_t *cr, int flags);
++    const char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns);
+ extern int zfs_link(znode_t *tdzp, znode_t *sp,
+     const char *name, cred_t *cr, int flags);
+ extern int zfs_space(znode_t *zp, int cmd, struct flock *bfp, int flag,
+     offset_t offset, cred_t *cr);
+ extern int zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl,
+-    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp);
++    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
++    zuserns_t *mnt_ns);
+ extern int zfs_setsecattr(znode_t *zp, vsecattr_t *vsecp, int flag,
+     cred_t *cr);
+ extern int zfs_write_simple(znode_t *zp, const void *data, size_t len,
+diff --git a/include/os/linux/spl/sys/cred.h b/include/os/linux/spl/sys/cred.h
+index b7d3f38d70b..dc3c260dbba 100644
+--- a/include/os/linux/spl/sys/cred.h
++++ b/include/os/linux/spl/sys/cred.h
+@@ -45,6 +45,34 @@ typedef struct cred cred_t;
+ #define	SGID_TO_KGID(x)		(KGIDT_INIT(x))
+ #define	KGIDP_TO_SGIDP(x)	(&(x)->val)
+ 
++static inline uid_t zfs_uid_into_mnt(struct user_namespace *mnt_ns, uid_t uid)
++{
++	if (mnt_ns)
++		return (__kuid_val(make_kuid(mnt_ns, uid)));
++	return (uid);
++}
++
++static inline gid_t zfs_gid_into_mnt(struct user_namespace *mnt_ns, gid_t gid)
++{
++	if (mnt_ns)
++		return (__kgid_val(make_kgid(mnt_ns, gid)));
++	return (gid);
++}
++
++static inline uid_t zfs_uid_from_mnt(struct user_namespace *mnt_ns, uid_t uid)
++{
++	if (mnt_ns)
++		return (from_kuid(mnt_ns, KUIDT_INIT(uid)));
++	return (uid);
++}
++
++static inline gid_t zfs_gid_from_mnt(struct user_namespace *mnt_ns, gid_t gid)
++{
++	if (mnt_ns)
++		return (from_kgid(mnt_ns, KGIDT_INIT(gid)));
++	return (gid);
++}
++
+ extern void crhold(cred_t *cr);
+ extern void crfree(cred_t *cr);
+ extern uid_t crgetuid(const cred_t *cr);
+diff --git a/include/os/linux/spl/sys/types.h b/include/os/linux/spl/sys/types.h
+index b44c9451875..cae1bbddf10 100644
+--- a/include/os/linux/spl/sys/types.h
++++ b/include/os/linux/spl/sys/types.h
+@@ -54,4 +54,7 @@ typedef ulong_t			pgcnt_t;
+ typedef int			major_t;
+ typedef int			minor_t;
+ 
++struct user_namespace;
++typedef struct user_namespace	zuserns_t;
++
+ #endif	/* _SPL_TYPES_H */
+diff --git a/include/os/linux/zfs/sys/policy.h b/include/os/linux/zfs/sys/policy.h
+index 61afc376550..5ec51392f17 100644
+--- a/include/os/linux/zfs/sys/policy.h
++++ b/include/os/linux/zfs/sys/policy.h
+@@ -47,13 +47,13 @@ int secpolicy_vnode_create_gid(const cred_t *);
+ int secpolicy_vnode_remove(const cred_t *);
+ int secpolicy_vnode_setdac(const cred_t *, uid_t);
+ int secpolicy_vnode_setid_retain(struct znode *, const cred_t *, boolean_t);
+-int secpolicy_vnode_setids_setgids(const cred_t *, gid_t);
++int secpolicy_vnode_setids_setgids(const cred_t *, gid_t, zuserns_t *);
+ int secpolicy_zinject(const cred_t *);
+ int secpolicy_zfs(const cred_t *);
+ int secpolicy_zfs_proc(const cred_t *, proc_t *);
+ void secpolicy_setid_clear(vattr_t *, cred_t *);
+ int secpolicy_setid_setsticky_clear(struct inode *, vattr_t *,
+-    const vattr_t *, cred_t *);
++    const vattr_t *, cred_t *, zuserns_t *);
+ int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, mode_t);
+ int secpolicy_vnode_setattr(cred_t *, struct inode *, struct vattr *,
+     const struct vattr *, int, int (void *, int, cred_t *), void *);
+diff --git a/include/os/linux/zfs/sys/zfs_vnops_os.h b/include/os/linux/zfs/sys/zfs_vnops_os.h
+index 47f91e4a6cf..fcb32308562 100644
+--- a/include/os/linux/zfs/sys/zfs_vnops_os.h
++++ b/include/os/linux/zfs/sys/zfs_vnops_os.h
+@@ -45,22 +45,25 @@ extern int zfs_write_simple(znode_t *zp, const void *data, size_t len,
+ extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags,
+     cred_t *cr, int *direntflags, pathname_t *realpnp);
+ extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+-    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp);
++    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
++    zuserns_t *mnt_ns);
+ extern int zfs_tmpfile(struct inode *dip, vattr_t *vapzfs, int excl,
+-    int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp);
++    int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp,
++    zuserns_t *mnt_ns);
+ extern int zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags);
+ extern int zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap,
+-    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp);
++    znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns);
+ extern int zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd,
+     cred_t *cr, int flags);
+ extern int zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr);
+ extern int zfs_getattr_fast(struct user_namespace *, struct inode *ip,
+ 	struct kstat *sp);
+-extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr);
++extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr,
++    zuserns_t *mnt_ns);
+ extern int zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp,
+-    char *tnm, cred_t *cr, int flags);
++    char *tnm, cred_t *cr, int flags, zuserns_t *mnt_ns);
+ extern int zfs_symlink(znode_t *dzp, char *name, vattr_t *vap,
+-    char *link, znode_t **zpp, cred_t *cr, int flags);
++    char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns);
+ extern int zfs_readlink(struct inode *ip, zfs_uio_t *uio, cred_t *cr);
+ extern int zfs_link(znode_t *tdzp, znode_t *szp,
+     char *name, cred_t *cr, int flags);
+diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h
+index ac9815d4ee0..703e335c28a 100644
+--- a/include/os/linux/zfs/sys/zpl.h
++++ b/include/os/linux/zfs/sys/zpl.h
+@@ -39,7 +39,7 @@
+ 
+ /* zpl_inode.c */
+ extern void zpl_vap_init(vattr_t *vap, struct inode *dir,
+-    umode_t mode, cred_t *cr);
++    umode_t mode, cred_t *cr, zuserns_t *mnt_ns);
+ 
+ extern const struct inode_operations zpl_inode_operations;
+ extern const struct inode_operations zpl_dir_inode_operations;
+diff --git a/include/sys/zfs_acl.h b/include/sys/zfs_acl.h
+index 010686a9121..93a1f7f6172 100644
+--- a/include/sys/zfs_acl.h
++++ b/include/sys/zfs_acl.h
+@@ -206,7 +206,7 @@ struct zfsvfs;
+ 
+ #ifdef _KERNEL
+ int zfs_acl_ids_create(struct znode *, int, vattr_t *,
+-    cred_t *, vsecattr_t *, zfs_acl_ids_t *);
++    cred_t *, vsecattr_t *, zfs_acl_ids_t *, zuserns_t *);
+ void zfs_acl_ids_free(zfs_acl_ids_t *);
+ boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *, uint64_t);
+ int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
+@@ -215,15 +215,16 @@ void zfs_acl_rele(void *);
+ void zfs_oldace_byteswap(ace_t *, int);
+ void zfs_ace_byteswap(void *, size_t, boolean_t);
+ extern boolean_t zfs_has_access(struct znode *zp, cred_t *cr);
+-extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *);
++extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *,
++    zuserns_t *);
+ int zfs_fastaccesschk_execute(struct znode *, cred_t *);
+-extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *);
++extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *, zuserns_t *);
+ extern int zfs_zaccess_unix(struct znode *, mode_t, cred_t *);
+ extern int zfs_acl_access(struct znode *, int, cred_t *);
+ int zfs_acl_chmod_setattr(struct znode *, zfs_acl_t **, uint64_t);
+-int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *);
++int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *, zuserns_t *);
+ int zfs_zaccess_rename(struct znode *, struct znode *,
+-    struct znode *, struct znode *, cred_t *cr);
++    struct znode *, struct znode *, cred_t *cr, zuserns_t *mnt_ns);
+ void zfs_acl_free(zfs_acl_t *);
+ int zfs_vsec_2_aclp(struct zfsvfs *, umode_t, vsecattr_t *, cred_t *,
+     struct zfs_fuid_info **, zfs_acl_t **);
+diff --git a/module/os/freebsd/zfs/zfs_acl.c b/module/os/freebsd/zfs/zfs_acl.c
+index fe0f6913232..ca50f442a07 100644
+--- a/module/os/freebsd/zfs/zfs_acl.c
++++ b/module/os/freebsd/zfs/zfs_acl.c
+@@ -1618,7 +1618,7 @@ zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp,
+  */
+ int
+ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+-    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
++    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zuserns_t *mnt_ns)
+ {
+ 	int		error;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -1788,7 +1788,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (mask == 0)
+ 		return (SET_ERROR(ENOSYS));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr)))
++	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr, NULL)))
+ 		return (error);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+@@ -1951,7 +1951,7 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (zp->z_pflags & ZFS_IMMUTABLE)
+ 		return (SET_ERROR(EPERM));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)))
++	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr, NULL)))
+ 		return (error);
+ 
+ 	error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, cr, &fuidp,
+@@ -2340,7 +2340,8 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+  * can define any form of access.
+  */
+ int
+-zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
++zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+ 	uint32_t	working_mode;
+ 	int		error;
+@@ -2470,9 +2471,11 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+  * NFSv4-style ZFS ACL format and call zfs_zaccess()
+  */
+ int
+-zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
++zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+-	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
++	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr,
++	    mnt_ns));
+ }
+ 
+ /*
+@@ -2483,7 +2486,7 @@ zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
+ {
+ 	int v4_mode = zfs_unix_to_v4(mode >> 6);
+ 
+-	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
++	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, NULL));
+ }
+ 
+ static int
+@@ -2539,7 +2542,7 @@ zfs_delete_final_check(znode_t *zp, znode_t *dzp,
+  *
+  */
+ int
+-zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
++zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	uint32_t dzp_working_mode = 0;
+ 	uint32_t zp_working_mode = 0;
+@@ -2626,7 +2629,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
+ 
+ int
+ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+-    znode_t *tzp, cred_t *cr)
++    znode_t *tzp, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	int add_perm;
+ 	int error;
+@@ -2646,7 +2649,8 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+ 	 * to another.
+ 	 */
+ 	if (ZTOV(szp)->v_type == VDIR && ZTOV(sdzp) != ZTOV(tdzp)) {
+-		if ((error = zfs_zaccess(szp, ACE_WRITE_DATA, 0, B_FALSE, cr)))
++		if ((error = zfs_zaccess(szp, ACE_WRITE_DATA, 0, B_FALSE, cr,
++		    mnt_ns)))
+ 			return (error);
+ 	}
+ 
+@@ -2656,19 +2660,19 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+ 	 * If that succeeds then check for add_file/add_subdir permissions
+ 	 */
+ 
+-	if ((error = zfs_zaccess_delete(sdzp, szp, cr)))
++	if ((error = zfs_zaccess_delete(sdzp, szp, cr, mnt_ns)))
+ 		return (error);
+ 
+ 	/*
+ 	 * If we have a tzp, see if we can delete it?
+ 	 */
+-	if (tzp && (error = zfs_zaccess_delete(tdzp, tzp, cr)))
++	if (tzp && (error = zfs_zaccess_delete(tdzp, tzp, cr, mnt_ns)))
+ 		return (error);
+ 
+ 	/*
+ 	 * Now check for add permissions
+ 	 */
+-	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
++	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr, mnt_ns);
+ 
+ 	return (error);
+ }
+diff --git a/module/os/freebsd/zfs/zfs_dir.c b/module/os/freebsd/zfs/zfs_dir.c
+index 7fff329a939..eebe34228e7 100644
+--- a/module/os/freebsd/zfs/zfs_dir.c
++++ b/module/os/freebsd/zfs/zfs_dir.c
+@@ -810,7 +810,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xvpp, cred_t *cr)
+ 	*xvpp = NULL;
+ 
+ 	if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
+-	    &acl_ids)) != 0)
++	    &acl_ids, NULL)) != 0)
+ 		return (error);
+ 	if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, 0)) {
+ 		zfs_acl_ids_free(&acl_ids);
+@@ -956,7 +956,7 @@ zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
+ 
+ 	if ((uid = crgetuid(cr)) == downer || uid == fowner ||
+ 	    (ZTOV(zp)->v_type == VREG &&
+-	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr) == 0))
++	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL) == 0))
+ 		return (0);
+ 	else
+ 		return (secpolicy_vnode_remove(ZTOV(zp), cr));
+diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c
+index ea6388dd515..0b7bbbdc52b 100644
+--- a/module/os/freebsd/zfs/zfs_vnops_os.c
++++ b/module/os/freebsd/zfs/zfs_vnops_os.c
+@@ -852,7 +852,7 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp,
+ 		/*
+ 		 * Do we have permission to get into attribute directory?
+ 		 */
+-		error = zfs_zaccess(zp, ACE_EXECUTE, 0, B_FALSE, cr);
++		error = zfs_zaccess(zp, ACE_EXECUTE, 0, B_FALSE, cr, NULL);
+ 		if (error) {
+ 			vrele(ZTOV(zp));
+ 		}
+@@ -871,7 +871,8 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp,
+ 			cnp->cn_flags &= ~NOEXECCHECK;
+ 		} else
+ #endif
+-		if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr))) {
++		if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
++		    NULL))) {
+ 			ZFS_EXIT(zfsvfs);
+ 			return (error);
+ 		}
+@@ -1047,6 +1048,7 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp,
+  *		flag	- large file flag [UNUSED].
+  *		ct	- caller context
+  *		vsecp	- ACL to be set
++ *		mnt_ns	- Unused on FreeBSD
+  *
+  *	OUT:	vpp	- vnode of created or trunc'd entry.
+  *
+@@ -1060,7 +1062,7 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp,
+ /* ARGSUSED */
+ int
+ zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl, int mode,
+-    znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp)
++    znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -1122,7 +1124,7 @@ zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl, int mode,
+ 	 * Create a new file object and update the directory
+ 	 * to reference it.
+ 	 */
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr, mnt_ns))) {
+ 		goto out;
+ 	}
+ 
+@@ -1138,7 +1140,7 @@ zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl, int mode,
+ 	}
+ 
+ 	if ((error = zfs_acl_ids_create(dzp, 0, vap,
+-	    cr, vsecp, &acl_ids)) != 0)
++	    cr, vsecp, &acl_ids, NULL)) != 0)
+ 		goto out;
+ 
+ 	if (S_ISREG(vap->va_mode) || S_ISDIR(vap->va_mode))
+@@ -1242,7 +1244,7 @@ zfs_remove_(vnode_t *dvp, vnode_t *vp, const char *name, cred_t *cr)
+ 	xattr_obj = 0;
+ 	xzp = NULL;
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ 		goto out;
+ 	}
+ 
+@@ -1398,6 +1400,7 @@ zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags)
+  *		ct	- caller context
+  *		flags	- case flags
+  *		vsecp	- ACL to be set
++ *		mnt_ns	- Unused on FreeBSD
+  *
+  *	OUT:	vpp	- vnode of created directory.
+  *
+@@ -1410,7 +1413,7 @@ zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags)
+ /*ARGSUSED*/
+ int
+ zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap, znode_t **zpp,
+-    cred_t *cr, int flags, vsecattr_t *vsecp)
++    cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
+@@ -1458,7 +1461,7 @@ zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap, znode_t **zpp,
+ 	}
+ 
+ 	if ((error = zfs_acl_ids_create(dzp, 0, vap, cr,
+-	    NULL, &acl_ids)) != 0) {
++	    NULL, &acl_ids, NULL)) != 0) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -1479,7 +1482,8 @@ zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap, znode_t **zpp,
+ 	}
+ 	ASSERT3P(zp, ==, NULL);
+ 
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr,
++	    mnt_ns))) {
+ 		zfs_acl_ids_free(&acl_ids);
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+@@ -1594,7 +1598,7 @@ zfs_rmdir_(vnode_t *dvp, vnode_t *vp, const char *name, cred_t *cr)
+ 	zilog = zfsvfs->z_log;
+ 
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ 		goto out;
+ 	}
+ 
+@@ -2045,7 +2049,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
+ 	if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) &&
+ 	    (vap->va_uid != crgetuid(cr))) {
+ 		if ((error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
+-		    skipaclchk, cr))) {
++		    skipaclchk, cr, NULL))) {
+ 			ZFS_EXIT(zfsvfs);
+ 			return (error);
+ 		}
+@@ -2211,7 +2215,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
+  *		flags	- ATTR_UTIME set if non-default time values provided.
+  *			- ATTR_NOACLCHECK (CIFS context only).
+  *		cr	- credentials of caller.
+- *		ct	- caller context
++ *		mnt_ns	- Unused on FreeBSD
+  *
+  *	RETURN:	0 on success, error code on failure.
+  *
+@@ -2220,7 +2224,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
+  */
+ /* ARGSUSED */
+ int
+-zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
++zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	vnode_t		*vp = ZTOV(zp);
+ 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
+@@ -2392,7 +2396,7 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 	    XVA_ISSET_REQ(xvap, XAT_CREATETIME) ||
+ 	    XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) {
+ 		need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0,
+-		    skipaclchk, cr);
++		    skipaclchk, cr, mnt_ns);
+ 	}
+ 
+ 	if (mask & (AT_UID|AT_GID)) {
+@@ -2429,7 +2433,7 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 		    ((idmask == AT_UID) && take_owner) ||
+ 		    ((idmask == AT_GID) && take_group)) {
+ 			if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0,
+-			    skipaclchk, cr) == 0) {
++			    skipaclchk, cr, mnt_ns) == 0) {
+ 				/*
+ 				 * Remove setuid/setgid for non-privileged users
+ 				 */
+@@ -2538,7 +2542,8 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 	}
+ 
+ 	if (mask & AT_MODE) {
+-		if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) {
++		if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
++		    mnt_ns) == 0) {
+ 			err = secpolicy_setid_setsticky_clear(vp, vap,
+ 			    &oldva, cr);
+ 			if (err) {
+@@ -3322,7 +3327,7 @@ zfs_do_rename_impl(vnode_t *sdvp, vnode_t **svpp, struct componentname *scnp,
+ 	 * Note that if target and source are the same, this can be
+ 	 * done in a single check.
+ 	 */
+-	if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)))
++	if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr, NULL)))
+ 		goto out;
+ 
+ 	if ((*svpp)->v_type == VDIR) {
+@@ -3473,7 +3478,7 @@ zfs_do_rename_impl(vnode_t *sdvp, vnode_t **svpp, struct componentname *scnp,
+ 
+ int
+ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
+-    cred_t *cr, int flags)
++    cred_t *cr, int flags, zuserns_t *mnt_ns)
+ {
+ 	struct componentname scn, tcn;
+ 	vnode_t *sdvp, *tdvp;
+@@ -3518,6 +3523,7 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
+  *		cr	- credentials of caller.
+  *		ct	- caller context
+  *		flags	- case flags
++ *		mnt_ns	- Unused on FreeBSD
+  *
+  *	RETURN:	0 on success, error code on failure.
+  *
+@@ -3527,7 +3533,7 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
+ /*ARGSUSED*/
+ int
+ zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap,
+-    const char *link, znode_t **zpp, cred_t *cr, int flags)
++    const char *link, znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	dmu_tx_t	*tx;
+@@ -3572,7 +3578,7 @@ zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr, mnt_ns))) {
+ 		zfs_acl_ids_free(&acl_ids);
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+@@ -3785,7 +3791,7 @@ zfs_link(znode_t *tdzp, znode_t *szp, const char *name, cred_t *cr,
+ 		return (SET_ERROR(EPERM));
+ 	}
+ 
+-	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr, NULL))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3886,7 +3892,7 @@ zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag,
+ 	 * On Linux we can get here through truncate_range() which
+ 	 * operates directly on inodes, so we need to check access rights.
+ 	 */
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -4658,8 +4664,8 @@ zfs_freebsd_create(struct vop_create_args *ap)
+ 	zfsvfs = ap->a_dvp->v_mount->mnt_data;
+ 	*ap->a_vpp = NULL;
+ 
+-	rc = zfs_create(VTOZ(ap->a_dvp), cnp->cn_nameptr, vap, !EXCL, mode,
+-	    &zp, cnp->cn_cred, 0 /* flag */, NULL /* vsecattr */);
++	rc = zfs_create(VTOZ(ap->a_dvp), cnp->cn_nameptr, vap, 0, mode,
++	    &zp, cnp->cn_cred, 0 /* flag */, NULL /* vsecattr */, NULL);
+ 	if (rc == 0)
+ 		*ap->a_vpp = ZTOV(zp);
+ 	if (zfsvfs->z_use_namecache &&
+@@ -4713,7 +4719,7 @@ zfs_freebsd_mkdir(struct vop_mkdir_args *ap)
+ 	*ap->a_vpp = NULL;
+ 
+ 	rc = zfs_mkdir(VTOZ(ap->a_dvp), ap->a_cnp->cn_nameptr, vap, &zp,
+-	    ap->a_cnp->cn_cred, 0, NULL);
++	    ap->a_cnp->cn_cred, 0, NULL, NULL);
+ 
+ 	if (rc == 0)
+ 		*ap->a_vpp = ZTOV(zp);
+@@ -4966,7 +4972,7 @@ zfs_freebsd_setattr(struct vop_setattr_args *ap)
+ 		xvap.xva_vattr.va_mask |= AT_XVATTR;
+ 		XVA_SET_REQ(&xvap, XAT_CREATETIME);
+ 	}
+-	return (zfs_setattr(VTOZ(vp), (vattr_t *)&xvap, 0, cred));
++	return (zfs_setattr(VTOZ(vp), (vattr_t *)&xvap, 0, cred, NULL));
+ }
+ 
+ #ifndef _SYS_SYSPROTO_H_
+@@ -5037,7 +5043,7 @@ zfs_freebsd_symlink(struct vop_symlink_args *ap)
+ 	*ap->a_vpp = NULL;
+ 
+ 	rc = zfs_symlink(VTOZ(ap->a_dvp), cnp->cn_nameptr, vap,
+-	    ap->a_target, &zp, cnp->cn_cred, 0 /* flags */);
++	    ap->a_target, &zp, cnp->cn_cred, 0 /* flags */, NULL);
+ 	if (rc == 0) {
+ 		*ap->a_vpp = ZTOV(zp);
+ 		ASSERT_VOP_ELOCKED(ZTOV(zp), __func__);
+diff --git a/module/os/freebsd/zfs/zfs_znode.c b/module/os/freebsd/zfs/zfs_znode.c
+index 1debc3ec3df..76c040d3797 100644
+--- a/module/os/freebsd/zfs/zfs_znode.c
++++ b/module/os/freebsd/zfs/zfs_znode.c
+@@ -296,7 +296,7 @@ zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
+ 	sharezp->z_is_sa = zfsvfs->z_use_sa;
+ 
+ 	VERIFY0(zfs_acl_ids_create(sharezp, IS_ROOT_NODE, &vattr,
+-	    kcred, NULL, &acl_ids));
++	    kcred, NULL, &acl_ids, NULL));
+ 	zfs_mknode(sharezp, &vattr, tx, kcred, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, sharezp);
+ 	POINTER_INVALIDATE(&sharezp->z_zfsvfs);
+@@ -1772,7 +1772,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
+ 
+ 	rootzp->z_zfsvfs = zfsvfs;
+ 	VERIFY0(zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
+-	    cr, NULL, &acl_ids));
++	    cr, NULL, &acl_ids, NULL));
+ 	zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, rootzp);
+ 	error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
+diff --git a/module/os/linux/zfs/policy.c b/module/os/linux/zfs/policy.c
+index 5a52092bb90..d68f3561210 100644
+--- a/module/os/linux/zfs/policy.c
++++ b/module/os/linux/zfs/policy.c
+@@ -214,8 +214,9 @@ secpolicy_vnode_setid_retain(struct znode *zp __maybe_unused, const cred_t *cr,
+  * Determine that subject can set the file setgid flag.
+  */
+ int
+-secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid)
++secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid, zuserns_t *mnt_ns)
+ {
++	gid = zfs_gid_into_mnt(mnt_ns, gid);
+ #if defined(CONFIG_USER_NS)
+ 	if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid)))
+ 		return (EPERM);
+@@ -284,8 +285,10 @@ secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
+  * Determine that subject can set the file setid flags.
+  */
+ static int
+-secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner)
++secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner, zuserns_t *mnt_ns)
+ {
++	owner = zfs_uid_into_mnt(mnt_ns, owner);
++
+ 	if (crgetuid(cr) == owner)
+ 		return (0);
+ 
+@@ -310,13 +313,13 @@ secpolicy_vnode_stky_modify(const cred_t *cr)
+ 
+ int
+ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+-    const vattr_t *ovap, cred_t *cr)
++    const vattr_t *ovap, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	int error;
+ 
+ 	if ((vap->va_mode & S_ISUID) != 0 &&
+ 	    (error = secpolicy_vnode_setid_modify(cr,
+-	    ovap->va_uid)) != 0) {
++	    ovap->va_uid, mnt_ns)) != 0) {
+ 		return (error);
+ 	}
+ 
+@@ -334,7 +337,7 @@ secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap,
+ 	 * group-id bit.
+ 	 */
+ 	if ((vap->va_mode & S_ISGID) != 0 &&
+-	    secpolicy_vnode_setids_setgids(cr, ovap->va_gid) != 0) {
++	    secpolicy_vnode_setids_setgids(cr, ovap->va_gid, mnt_ns) != 0) {
+ 		vap->va_mode &= ~S_ISGID;
+ 	}
+ 
+diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
+index 8e96cc8e35a..fcb767e9e4a 100644
+--- a/module/os/linux/zfs/zfs_acl.c
++++ b/module/os/linux/zfs/zfs_acl.c
+@@ -1801,7 +1801,7 @@ zfs_acl_inherit(zfsvfs_t *zfsvfs, umode_t va_mode, zfs_acl_t *paclp,
+  */
+ int
+ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+-    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
++    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids, zuserns_t *mnt_ns)
+ {
+ 	int		error;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -1888,8 +1888,9 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+ 		acl_ids->z_mode |= S_ISGID;
+ 	} else {
+ 		if ((acl_ids->z_mode & S_ISGID) &&
+-		    secpolicy_vnode_setids_setgids(cr, gid) != 0)
++		    secpolicy_vnode_setids_setgids(cr, gid, mnt_ns) != 0) {
+ 			acl_ids->z_mode &= ~S_ISGID;
++		}
+ 	}
+ 
+ 	if (acl_ids->z_aclp == NULL) {
+@@ -1977,7 +1978,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (mask == 0)
+ 		return (SET_ERROR(ENOSYS));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr)))
++	if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr, NULL)))
+ 		return (error);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+@@ -2136,7 +2137,7 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
+ 	if (zp->z_pflags & ZFS_IMMUTABLE)
+ 		return (SET_ERROR(EPERM));
+ 
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)))
++	if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr, NULL)))
+ 		return (error);
+ 
+ 	error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
+@@ -2282,7 +2283,7 @@ zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
+  */
+ static int
+ zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+-    boolean_t anyaccess, cred_t *cr)
++    boolean_t anyaccess, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(zp);
+ 	zfs_acl_t	*aclp;
+@@ -2298,7 +2299,13 @@ zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+ 	uid_t		gowner;
+ 	uid_t		fowner;
+ 
+-	zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
++	if (mnt_ns) {
++		fowner = zfs_uid_into_mnt(mnt_ns,
++		    KUID_TO_SUID(ZTOI(zp)->i_uid));
++		gowner = zfs_gid_into_mnt(mnt_ns,
++		    KGID_TO_SGID(ZTOI(zp)->i_gid));
++	} else
++		zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
+ 
+ 	mutex_enter(&zp->z_acl_lock);
+ 
+@@ -2409,7 +2416,7 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+ {
+ 	uint32_t have = ACE_ALL_PERMS;
+ 
+-	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
++	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr, NULL) != 0) {
+ 		uid_t owner;
+ 
+ 		owner = zfs_fuid_map_id(ZTOZSB(zp),
+@@ -2439,7 +2446,8 @@ zfs_has_access(znode_t *zp, cred_t *cr)
+  * we want to avoid that here.
+  */
+ static int
+-zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr)
++zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+ 	int err, mask;
+ 	int unmapped = 0;
+@@ -2453,7 +2461,10 @@ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr)
+ 	}
+ 
+ #if defined(HAVE_IOPS_PERMISSION_USERNS)
+-	err = generic_permission(cr->user_ns, ZTOI(zp), mask);
++	if (mnt_ns)
++		err = generic_permission(mnt_ns, ZTOI(zp), mask);
++	else
++		err = generic_permission(cr->user_ns, ZTOI(zp), mask);
+ #else
+ 	err = generic_permission(ZTOI(zp), mask);
+ #endif
+@@ -2468,7 +2479,7 @@ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr)
+ 
+ static int
+ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+-    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
++    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	zfsvfs_t *zfsvfs = ZTOZSB(zp);
+ 	int err;
+@@ -2518,20 +2529,20 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+ 	}
+ 
+ 	if (zp->z_pflags & ZFS_ACL_TRIVIAL)
+-		return (zfs_zaccess_trivial(zp, working_mode, cr));
++		return (zfs_zaccess_trivial(zp, working_mode, cr, mnt_ns));
+ 
+-	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
++	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr, mnt_ns));
+ }
+ 
+ static int
+ zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
+-    cred_t *cr)
++    cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	if (*working_mode != ACE_WRITE_DATA)
+ 		return (SET_ERROR(EACCES));
+ 
+ 	return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
+-	    check_privs, B_FALSE, cr));
++	    check_privs, B_FALSE, cr, mnt_ns));
+ }
+ 
+ int
+@@ -2597,7 +2608,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+ slow:
+ 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
+ 	ZFS_ENTER(ZTOZSB(zdp));
+-	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr);
++	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL);
+ 	ZFS_EXIT(ZTOZSB(zdp));
+ 	return (error);
+ }
+@@ -2609,7 +2620,8 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
+  * can define any form of access.
+  */
+ int
+-zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
++zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+ 	uint32_t	working_mode;
+ 	int		error;
+@@ -2648,8 +2660,9 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+ 		}
+ 	}
+ 
+-	owner = zfs_fuid_map_id(ZTOZSB(zp), KUID_TO_SUID(ZTOI(zp)->i_uid),
+-	    cr, ZFS_OWNER);
++	owner = zfs_uid_into_mnt(mnt_ns, KUID_TO_SUID(ZTOI(zp)->i_uid));
++	owner = zfs_fuid_map_id(ZTOZSB(zp), owner, cr, ZFS_OWNER);
++
+ 	/*
+ 	 * Map the bits required to the standard inode flags
+ 	 * S_IRUSR|S_IWUSR|S_IXUSR in the needed_bits.  Map the bits
+@@ -2674,7 +2687,7 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+ 		needed_bits |= S_IXUSR;
+ 
+ 	if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
+-	    &check_privs, skipaclchk, cr)) == 0) {
++	    &check_privs, skipaclchk, cr, mnt_ns)) == 0) {
+ 		if (is_attr)
+ 			zrele(xzp);
+ 		return (secpolicy_vnode_access2(cr, ZTOI(zp), owner,
+@@ -2688,7 +2701,8 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+ 	}
+ 
+ 	if (error && (flags & V_APPEND)) {
+-		error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
++		error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr,
++		    mnt_ns);
+ 	}
+ 
+ 	if (error && check_privs) {
+@@ -2755,9 +2769,11 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
+  * NFSv4-style ZFS ACL format and call zfs_zaccess()
+  */
+ int
+-zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
++zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+-	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
++	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr,
++	    mnt_ns));
+ }
+ 
+ /*
+@@ -2768,7 +2784,7 @@ zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
+ {
+ 	int v4_mode = zfs_unix_to_v4(mode >> 6);
+ 
+-	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
++	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr, NULL));
+ }
+ 
+ /* See zfs_zaccess_delete() */
+@@ -2845,7 +2861,7 @@ int zfs_write_implies_delete_child = 1;
+  * zfs_write_implies_delete_child
+  */
+ int
+-zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
++zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	uint32_t wanted_dirperms;
+ 	uint32_t dzp_working_mode = 0;
+@@ -2872,7 +2888,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
+ 	 * (This is part of why we're checking the target first.)
+ 	 */
+ 	zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
+-	    &zpcheck_privs, B_FALSE, cr);
++	    &zpcheck_privs, B_FALSE, cr, mnt_ns);
+ 	if (zp_error == EACCES) {
+ 		/* We hit a DENY ACE. */
+ 		if (!zpcheck_privs)
+@@ -2894,7 +2910,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
+ 	if (zfs_write_implies_delete_child)
+ 		wanted_dirperms |= ACE_WRITE_DATA;
+ 	dzp_error = zfs_zaccess_common(dzp, wanted_dirperms,
+-	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
++	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr, mnt_ns);
+ 	if (dzp_error == EACCES) {
+ 		/* We hit a DENY ACE. */
+ 		if (!dzpcheck_privs)
+@@ -2976,7 +2992,7 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
+ 
+ int
+ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+-    znode_t *tzp, cred_t *cr)
++    znode_t *tzp, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	int add_perm;
+ 	int error;
+@@ -2998,21 +3014,21 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
+ 	 * If that succeeds then check for add_file/add_subdir permissions
+ 	 */
+ 
+-	if ((error = zfs_zaccess_delete(sdzp, szp, cr)))
++	if ((error = zfs_zaccess_delete(sdzp, szp, cr, mnt_ns)))
+ 		return (error);
+ 
+ 	/*
+ 	 * If we have a tzp, see if we can delete it?
+ 	 */
+ 	if (tzp) {
+-		if ((error = zfs_zaccess_delete(tdzp, tzp, cr)))
++		if ((error = zfs_zaccess_delete(tdzp, tzp, cr, mnt_ns)))
+ 			return (error);
+ 	}
+ 
+ 	/*
+ 	 * Now check for add permissions
+ 	 */
+-	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
++	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr, mnt_ns);
+ 
+ 	return (error);
+ }
+diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c
+index 8ad5454b5a7..9bb11d6d4c2 100644
+--- a/module/os/linux/zfs/zfs_dir.c
++++ b/module/os/linux/zfs/zfs_dir.c
+@@ -1067,7 +1067,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr)
+ 	*xzpp = NULL;
+ 
+ 	if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
+-	    &acl_ids)) != 0)
++	    &acl_ids, NULL)) != 0)
+ 		return (error);
+ 	if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) {
+ 		zfs_acl_ids_free(&acl_ids);
+@@ -1215,7 +1215,7 @@ zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
+ 	    cr, ZFS_OWNER);
+ 
+ 	if ((uid = crgetuid(cr)) == downer || uid == fowner ||
+-	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr) == 0)
++	    zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL) == 0)
+ 		return (0);
+ 	else
+ 		return (secpolicy_vnode_remove(cr));
+diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c
+index 025768fb4e7..b81b4298620 100644
+--- a/module/os/linux/zfs/zfs_vnops_os.c
++++ b/module/os/linux/zfs/zfs_vnops_os.c
+@@ -501,7 +501,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 		 */
+ 
+ 		if ((error = zfs_zaccess(*zpp, ACE_EXECUTE, 0,
+-		    B_TRUE, cr))) {
++		    B_TRUE, cr, NULL))) {
+ 			zrele(*zpp);
+ 			*zpp = NULL;
+ 		}
+@@ -519,7 +519,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ 	 * Check accessibility of directory.
+ 	 */
+ 
+-	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -551,6 +551,7 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+  *		cr	- credentials of caller.
+  *		flag	- file flag.
+  *		vsecp	- ACL to be set
++ *		mnt_ns	- user namespace of the mount
+  *
+  *	OUT:	zpp	- znode of created or trunc'd entry.
+  *
+@@ -564,7 +565,8 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
+ /* ARGSUSED */
+ int
+ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+-    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp)
++    int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
++    zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -652,7 +654,8 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+ 		 * Create a new file object and update the directory
+ 		 * to reference it.
+ 		 */
+-		if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, skip_acl, cr))) {
++		if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr,
++		    mnt_ns))) {
+ 			if (have_acl)
+ 				zfs_acl_ids_free(&acl_ids);
+ 			goto out;
+@@ -671,7 +674,7 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+ 		}
+ 
+ 		if (!have_acl && (error = zfs_acl_ids_create(dzp, 0, vap,
+-		    cr, vsecp, &acl_ids)) != 0)
++		    cr, vsecp, &acl_ids, mnt_ns)) != 0)
+ 			goto out;
+ 		have_acl = B_TRUE;
+ 
+@@ -766,7 +769,8 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+ 		/*
+ 		 * Verify requested access to file.
+ 		 */
+-		if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) {
++		if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr,
++		    mnt_ns))) {
+ 			goto out;
+ 		}
+ 
+@@ -811,7 +815,8 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
+ /* ARGSUSED */
+ int
+ zfs_tmpfile(struct inode *dip, vattr_t *vap, int excl,
+-    int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp)
++    int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp,
++    zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp = NULL, *dzp = ITOZ(dip);
+ 	zfsvfs_t	*zfsvfs = ITOZSB(dip);
+@@ -857,14 +862,14 @@ zfs_tmpfile(struct inode *dip, vattr_t *vap, int excl,
+ 	 * Create a new file object and update the directory
+ 	 * to reference it.
+ 	 */
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr, mnt_ns))) {
+ 		if (have_acl)
+ 			zfs_acl_ids_free(&acl_ids);
+ 		goto out;
+ 	}
+ 
+ 	if (!have_acl && (error = zfs_acl_ids_create(dzp, 0, vap,
+-	    cr, vsecp, &acl_ids)) != 0)
++	    cr, vsecp, &acl_ids, mnt_ns)) != 0)
+ 		goto out;
+ 	have_acl = B_TRUE;
+ 
+@@ -996,7 +1001,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ 		goto out;
+ 	}
+ 
+@@ -1176,6 +1181,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+  *		cr	- credentials of caller.
+  *		flags	- case flags.
+  *		vsecp	- ACL to be set
++ *		mnt_ns	- user namespace of the mount
+  *
+  *	OUT:	zpp	- znode of created directory.
+  *
+@@ -1189,7 +1195,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags)
+ /*ARGSUSED*/
+ int
+ zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp,
+-    cred_t *cr, int flags, vsecattr_t *vsecp)
++    cred_t *cr, int flags, vsecattr_t *vsecp, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(dzp);
+@@ -1246,7 +1252,7 @@ zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp,
+ 	}
+ 
+ 	if ((error = zfs_acl_ids_create(dzp, 0, vap, cr,
+-	    vsecp, &acl_ids)) != 0) {
++	    vsecp, &acl_ids, mnt_ns)) != 0) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -1267,7 +1273,8 @@ zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr,
++	    mnt_ns))) {
+ 		zfs_acl_ids_free(&acl_ids);
+ 		zfs_dirent_unlock(dl);
+ 		ZFS_EXIT(zfsvfs);
+@@ -1410,7 +1417,7 @@ zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess_delete(dzp, zp, cr))) {
++	if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ 		goto out;
+ 	}
+ 
+@@ -1842,6 +1849,7 @@ zfs_setattr_dir(znode_t *dzp)
+  *		flags	- ATTR_UTIME set if non-default time values provided.
+  *			- ATTR_NOACLCHECK (CIFS context only).
+  *		cr	- credentials of caller.
++ *		mnt_ns	- user namespace of the mount
+  *
+  *	RETURN:	0 if success
+  *		error code if failure
+@@ -1851,7 +1859,7 @@ zfs_setattr_dir(znode_t *dzp)
+  */
+ /* ARGSUSED */
+ int
+-zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
++zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zuserns_t *mnt_ns)
+ {
+ 	struct inode	*ip;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(zp);
+@@ -2000,7 +2008,8 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 	 */
+ 
+ 	if (mask & ATTR_SIZE) {
+-		err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr);
++		err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr,
++		    mnt_ns);
+ 		if (err)
+ 			goto out3;
+ 
+@@ -2025,13 +2034,15 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 	    XVA_ISSET_REQ(xvap, XAT_CREATETIME) ||
+ 	    XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) {
+ 		need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0,
+-		    skipaclchk, cr);
++		    skipaclchk, cr, mnt_ns);
+ 	}
+ 
+ 	if (mask & (ATTR_UID|ATTR_GID)) {
+ 		int	idmask = (mask & (ATTR_UID|ATTR_GID));
+ 		int	take_owner;
+ 		int	take_group;
++		uid_t	uid;
++		gid_t	gid;
+ 
+ 		/*
+ 		 * NOTE: even if a new mode is being set,
+@@ -2045,9 +2056,13 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 		 * Take ownership or chgrp to group we are a member of
+ 		 */
+ 
+-		take_owner = (mask & ATTR_UID) && (vap->va_uid == crgetuid(cr));
++		uid = zfs_uid_into_mnt((struct user_namespace *)mnt_ns,
++		    vap->va_uid);
++		gid = zfs_gid_into_mnt((struct user_namespace *)mnt_ns,
++		    vap->va_gid);
++		take_owner = (mask & ATTR_UID) && (uid == crgetuid(cr));
+ 		take_group = (mask & ATTR_GID) &&
+-		    zfs_groupmember(zfsvfs, vap->va_gid, cr);
++		    zfs_groupmember(zfsvfs, gid, cr);
+ 
+ 		/*
+ 		 * If both ATTR_UID and ATTR_GID are set then take_owner and
+@@ -2063,7 +2078,7 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 		    ((idmask == ATTR_UID) && take_owner) ||
+ 		    ((idmask == ATTR_GID) && take_group)) {
+ 			if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0,
+-			    skipaclchk, cr) == 0) {
++			    skipaclchk, cr, mnt_ns) == 0) {
+ 				/*
+ 				 * Remove setuid/setgid for non-privileged users
+ 				 */
+@@ -2176,12 +2191,12 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr)
+ 	mutex_exit(&zp->z_lock);
+ 
+ 	if (mask & ATTR_MODE) {
+-		if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) {
++		if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr,
++		    mnt_ns) == 0) {
+ 			err = secpolicy_setid_setsticky_clear(ip, vap,
+-			    &oldva, cr);
++			    &oldva, cr, mnt_ns);
+ 			if (err)
+ 				goto out3;
+-
+ 			trim_mask |= ATTR_MODE;
+ 		} else {
+ 			need_policy = TRUE;
+@@ -2672,6 +2687,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
+  *		tnm	- New entry name.
+  *		cr	- credentials of caller.
+  *		flags	- case flags
++ *		mnt_ns	- user namespace of the mount
+  *
+  *	RETURN:	0 on success, error code on failure.
+  *
+@@ -2681,7 +2697,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
+ /*ARGSUSED*/
+ int
+ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+-    cred_t *cr, int flags)
++    cred_t *cr, int flags, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*szp, *tzp;
+ 	zfsvfs_t	*zfsvfs = ZTOZSB(sdzp);
+@@ -2871,7 +2887,7 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+ 	 * done in a single check.
+ 	 */
+ 
+-	if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)))
++	if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr, mnt_ns)))
+ 		goto out;
+ 
+ 	if (S_ISDIR(ZTOI(szp)->i_mode)) {
+@@ -3038,6 +3054,7 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+  *		link	- Name for new symlink entry.
+  *		cr	- credentials of caller.
+  *		flags	- case flags
++ *		mnt_ns	- user namespace of the mount
+  *
+  *	OUT:	zpp	- Znode for new symbolic link.
+  *
+@@ -3049,7 +3066,7 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
+ /*ARGSUSED*/
+ int
+ zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, char *link,
+-    znode_t **zpp, cred_t *cr, int flags)
++    znode_t **zpp, cred_t *cr, int flags, zuserns_t *mnt_ns)
+ {
+ 	znode_t		*zp;
+ 	zfs_dirlock_t	*dl;
+@@ -3087,7 +3104,7 @@ zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, char *link,
+ 	}
+ 
+ 	if ((error = zfs_acl_ids_create(dzp, 0,
+-	    vap, cr, NULL, &acl_ids)) != 0) {
++	    vap, cr, NULL, &acl_ids, mnt_ns)) != 0) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3104,7 +3121,7 @@ zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, char *link,
+ 		return (error);
+ 	}
+ 
+-	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr, mnt_ns))) {
+ 		zfs_acl_ids_free(&acl_ids);
+ 		zfs_dirent_unlock(dl);
+ 		ZFS_EXIT(zfsvfs);
+@@ -3354,7 +3371,7 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
+ 		return (SET_ERROR(EPERM));
+ 	}
+ 
+-	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr, NULL))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+@@ -3942,7 +3959,7 @@ zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag,
+ 	 * On Linux we can get here through truncate_range() which
+ 	 * operates directly on inodes, so we need to check access rights.
+ 	 */
+-	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr))) {
++	if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr, NULL))) {
+ 		ZFS_EXIT(zfsvfs);
+ 		return (error);
+ 	}
+diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
+index f3475b4d979..619ef68a947 100644
+--- a/module/os/linux/zfs/zfs_znode.c
++++ b/module/os/linux/zfs/zfs_znode.c
+@@ -1947,7 +1947,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
+ 	}
+ 
+ 	VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
+-	    cr, NULL, &acl_ids));
++	    cr, NULL, &acl_ids, NULL));
+ 	zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
+ 	ASSERT3P(zp, ==, rootzp);
+ 	error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
+diff --git a/module/os/linux/zfs/zpl_ctldir.c b/module/os/linux/zfs/zpl_ctldir.c
+index 9b526afd000..9e7e1806f8c 100644
+--- a/module/os/linux/zfs/zpl_ctldir.c
++++ b/module/os/linux/zfs/zpl_ctldir.c
+@@ -363,7 +363,11 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-	zpl_vap_init(vap, dip, mode | S_IFDIR, cr);
++#ifdef HAVE_IOPS_MKDIR_USERNS
++	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, user_ns);
++#else
++	zpl_vap_init(vap, dip, mode | S_IFDIR, cr, NULL);
++#endif
+ 
+ 	error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
+ 	if (error == 0) {
+diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c
+index b045786f203..92babf04a92 100644
+--- a/module/os/linux/zfs/zpl_file.c
++++ b/module/os/linux/zfs/zpl_file.c
+@@ -971,7 +971,7 @@ zpl_ioctl_setflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1019,7 +1019,7 @@ zpl_ioctl_setxattr(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+@@ -1107,7 +1107,7 @@ zpl_ioctl_setdosflags(struct file *filp, void __user *arg)
+ 
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+-	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr);
++	err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 
+diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c
+index dd634f70eb7..d218045d11c 100644
+--- a/module/os/linux/zfs/zpl_inode.c
++++ b/module/os/linux/zfs/zpl_inode.c
+@@ -33,7 +33,6 @@
+ #include <sys/zpl.h>
+ #include <sys/file.h>
+ 
+-
+ static struct dentry *
+ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+ {
+@@ -112,18 +111,22 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+ }
+ 
+ void
+-zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr)
++zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
++    zuserns_t *mnt_ns)
+ {
+ 	vap->va_mask = ATTR_MODE;
+ 	vap->va_mode = mode;
+-	vap->va_uid = crgetuid(cr);
++
++	vap->va_uid = zfs_uid_from_mnt((struct user_namespace *)mnt_ns,
++	    crgetuid(cr));
+ 
+ 	if (dir && dir->i_mode & S_ISGID) {
+ 		vap->va_gid = KGID_TO_SGID(dir->i_gid);
+ 		if (S_ISDIR(mode))
+ 			vap->va_mode |= S_ISGID;
+ 	} else {
+-		vap->va_gid = crgetgid(cr);
++		vap->va_gid = zfs_gid_from_mnt((struct user_namespace *)mnt_ns,
++		    crgetgid(cr));
+ 	}
+ }
+ 
+@@ -140,14 +143,17 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_IOPS_CREATE_USERNS
++	zuserns_t *user_ns = NULL;
++#endif
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-	zpl_vap_init(vap, dir, mode, cr);
++	zpl_vap_init(vap, dir, mode, cr, user_ns);
+ 
+ 	cookie = spl_fstrans_mark();
+ 	error = -zfs_create(ITOZ(dir), dname(dentry), vap, 0,
+-	    mode, &zp, cr, 0, NULL);
++	    mode, &zp, cr, 0, NULL, user_ns);
+ 	if (error == 0) {
+ 		error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
+ 		if (error == 0)
+@@ -184,6 +190,9 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_IOPS_MKNOD_USERNS
++	zuserns_t *user_ns = NULL;
++#endif
+ 
+ 	/*
+ 	 * We currently expect Linux to supply rdev=0 for all sockets
+@@ -194,12 +203,12 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-	zpl_vap_init(vap, dir, mode, cr);
++	zpl_vap_init(vap, dir, mode, cr, user_ns);
+ 	vap->va_rdev = rdev;
+ 
+ 	cookie = spl_fstrans_mark();
+ 	error = -zfs_create(ITOZ(dir), dname(dentry), vap, 0,
+-	    mode, &zp, cr, 0, NULL);
++	    mode, &zp, cr, 0, NULL, user_ns);
+ 	if (error == 0) {
+ 		error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
+ 		if (error == 0)
+@@ -241,6 +250,9 @@ zpl_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	vattr_t *vap;
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_TMPFILE_USERNS
++	zuserns_t *userns = NULL;
++#endif
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+@@ -250,10 +262,10 @@ zpl_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	 */
+ 	if (!IS_POSIXACL(dir))
+ 		mode &= ~current_umask();
+-	zpl_vap_init(vap, dir, mode, cr);
++	zpl_vap_init(vap, dir, mode, cr, userns);
+ 
+ 	cookie = spl_fstrans_mark();
+-	error = -zfs_tmpfile(dir, vap, 0, mode, &ip, cr, 0, NULL);
++	error = -zfs_tmpfile(dir, vap, 0, mode, &ip, cr, 0, NULL, userns);
+ 	if (error == 0) {
+ 		/* d_tmpfile will do drop_nlink, so we should set it first */
+ 		set_nlink(ip, 1);
+@@ -326,13 +338,17 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+ 	znode_t *zp;
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_IOPS_MKDIR_USERNS
++	zuserns_t *user_ns = NULL;
++#endif
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-	zpl_vap_init(vap, dir, mode | S_IFDIR, cr);
++	zpl_vap_init(vap, dir, mode | S_IFDIR, cr, user_ns);
+ 
+ 	cookie = spl_fstrans_mark();
+-	error = -zfs_mkdir(ITOZ(dir), dname(dentry), vap, &zp, cr, 0, NULL);
++	error = -zfs_mkdir(ITOZ(dir), dname(dentry), vap, &zp, cr, 0, NULL,
++	    user_ns);
+ 	if (error == 0) {
+ 		error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
+ 		if (error == 0)
+@@ -454,7 +470,11 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ 	int error;
+ 	fstrans_cookie_t cookie;
+ 
++#ifdef HAVE_SETATTR_PREPARE_USERNS
++	error = zpl_setattr_prepare(user_ns, dentry, ia);
++#else
+ 	error = zpl_setattr_prepare(kcred->user_ns, dentry, ia);
++#endif
+ 	if (error)
+ 		return (error);
+ 
+@@ -473,7 +493,11 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia)
+ 		ip->i_atime = zpl_inode_timestamp_truncate(ia->ia_atime, ip);
+ 
+ 	cookie = spl_fstrans_mark();
+-	error = -zfs_setattr(ITOZ(ip), vap, 0, cr);
++#ifdef HAVE_SETATTR_PREPARE_USERNS
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, user_ns);
++#else
++	error = -zfs_setattr(ITOZ(ip), vap, 0, cr, NULL);
++#endif
+ 	if (!error && (ia->ia_valid & ATTR_MODE))
+ 		error = zpl_chmod_acl(ip);
+ 
+@@ -498,6 +522,9 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	cred_t *cr = CRED();
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_IOPS_RENAME_USERNS
++	zuserns_t *user_ns = NULL;
++#endif
+ 
+ 	/* We don't have renameat2(2) support */
+ 	if (flags)
+@@ -506,7 +533,7 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry,
+ 	crhold(cr);
+ 	cookie = spl_fstrans_mark();
+ 	error = -zfs_rename(ITOZ(sdip), dname(sdentry), ITOZ(tdip),
+-	    dname(tdentry), cr, 0);
++	    dname(tdentry), cr, 0, user_ns);
+ 	spl_fstrans_unmark(cookie);
+ 	crfree(cr);
+ 	ASSERT3S(error, <=, 0);
+@@ -536,14 +563,17 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+ 	znode_t *zp;
+ 	int error;
+ 	fstrans_cookie_t cookie;
++#ifndef HAVE_IOPS_SYMLINK_USERNS
++	zuserns_t *user_ns = NULL;
++#endif
+ 
+ 	crhold(cr);
+ 	vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
+-	zpl_vap_init(vap, dir, S_IFLNK | S_IRWXUGO, cr);
++	zpl_vap_init(vap, dir, S_IFLNK | S_IRWXUGO, cr, user_ns);
+ 
+ 	cookie = spl_fstrans_mark();
+ 	error = -zfs_symlink(ITOZ(dir), dname(dentry), vap,
+-	    (char *)name, &zp, cr, 0);
++	    (char *)name, &zp, cr, 0, user_ns);
+ 	if (error == 0) {
+ 		error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
+ 		if (error) {
+diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c
+index c2fd3fee140..21db088942b 100644
+--- a/module/os/linux/zfs/zpl_super.c
++++ b/module/os/linux/zfs/zpl_super.c
+@@ -360,6 +360,11 @@ const struct super_operations zpl_super_operations = {
+ struct file_system_type zpl_fs_type = {
+ 	.owner			= THIS_MODULE,
+ 	.name			= ZFS_DRIVER,
++#if defined(HAVE_IDMAP_MNT_API)
++	.fs_flags		= FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
++#else
++	.fs_flags		= FS_USERNS_MOUNT,
++#endif
+ 	.mount			= zpl_mount,
+ 	.kill_sb		= zpl_kill_sb,
+ };
+diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c
+index 364cd34c163..5f0be5f6418 100644
+--- a/module/os/linux/zfs/zpl_xattr.c
++++ b/module/os/linux/zfs/zpl_xattr.c
+@@ -496,7 +496,7 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
+ 		vap->va_gid = crgetgid(cr);
+ 
+ 		error = -zfs_create(dxzp, (char *)name, vap, 0, 0644, &xzp,
+-		    cr, ATTR_NOACLCHECK, NULL);
++		    cr, 0, NULL, NULL);
+ 		if (error)
+ 			goto out;
+ 	}
+diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c
+index f3d209f1fbd..c7a3486cd36 100644
+--- a/module/zfs/zfs_replay.c
++++ b/module/zfs/zfs_replay.c
+@@ -385,7 +385,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 		}
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, &vsec);
++		    0, 0, &zp, kcred, vflg, &vsec, NULL);
+ 		break;
+ 	case TX_MKDIR_ACL:
+ 		aclstart = (caddr_t)(lracl + 1);
+@@ -415,7 +415,7 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
+ 			    lr->lr_uid, lr->lr_gid);
+ 		}
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, &vsec);
++		    &zp, kcred, vflg, &vsec, NULL);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -525,7 +525,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)start;
+ 
+ 		error = zfs_create(dzp, name, &xva.xva_vattr,
+-		    0, 0, &zp, kcred, vflg, NULL);
++		    0, 0, &zp, kcred, vflg, NULL, NULL);
+ 		break;
+ 	case TX_MKDIR_ATTR:
+ 		lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+@@ -542,7 +542,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 			name = (char *)(lr + 1);
+ 
+ 		error = zfs_mkdir(dzp, name, &xva.xva_vattr,
+-		    &zp, kcred, vflg, NULL);
++		    &zp, kcred, vflg, NULL, NULL);
+ 		break;
+ 	case TX_MKXATTR:
+ 		error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
+@@ -551,7 +551,7 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
+ 		name = (char *)(lr + 1);
+ 		link = name + strlen(name) + 1;
+ 		error = zfs_symlink(dzp, name, &xva.xva_vattr,
+-		    link, &zp, kcred, vflg);
++		    link, &zp, kcred, vflg, NULL);
+ 		break;
+ 	default:
+ 		error = SET_ERROR(ENOTSUP);
+@@ -663,7 +663,7 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
+ 	if (lr->lr_common.lrc_txtype & TX_CI)
+ 		vflg |= FIGNORECASE;
+ 
+-	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg);
++	error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, NULL);
+ 
+ 	zrele(tdzp);
+ 	zrele(sdzp);
+@@ -857,7 +857,7 @@ 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(zp, vap, 0, kcred);
++	error = zfs_setattr(zp, vap, 0, kcred, NULL);
+ 
+ 	zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ 	zfsvfs->z_fuid_replay = NULL;
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index b9498d17ee2..6d74ffb633c 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -165,9 +165,9 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
+ 	ZFS_VERIFY_ZP(zp);
+ 
+ 	if (flag & V_ACE_MASK)
+-		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr);
++		error = zfs_zaccess(zp, mode, flag, B_FALSE, cr, NULL);
+ 	else
+-		error = zfs_zaccess_rwx(zp, mode, flag, cr);
++		error = zfs_zaccess_rwx(zp, mode, flag, cr, NULL);
+ 
+ 	ZFS_EXIT(zfsvfs);
+ 	return (error);
+
+From 99435ad33fdffdcf710255c7ed25eea3a3e21a4b Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:11:02 -0500
+Subject: [PATCH 2/3] Backport "Add tests for idmapped mount functionality"
+
+---
+ tests/runfiles/linux.run                      |   5 +
+ tests/zfs-tests/cmd/Makefile.am               |   1 +
+ tests/zfs-tests/cmd/idmap_util/.gitignore     |   1 +
+ tests/zfs-tests/cmd/idmap_util/Makefile.am    |   9 +
+ tests/zfs-tests/cmd/idmap_util/idmap_util.c   | 791 ++++++++++++++++++
+ tests/zfs-tests/include/commands.cfg          |   2 +-
+ .../tests/functional/idmap_mount/cleanup.ksh  |  25 +
+ .../functional/idmap_mount/idmap_mount.cfg    |  25 +
+ .../idmap_mount/idmap_mount_001.ksh           |  76 ++
+ .../idmap_mount/idmap_mount_002.ksh           |  97 +++
+ .../idmap_mount/idmap_mount_003.ksh           | 121 +++
+ .../idmap_mount/idmap_mount_004.ksh           | 106 +++
+ .../idmap_mount/idmap_mount_common.kshlib     |  23 +
+ .../tests/functional/idmap_mount/setup.ksh    |  30 +
+ 14 files changed, 1311 insertions(+), 1 deletion(-)
+ create mode 100644 tests/zfs-tests/cmd/idmap_util/.gitignore
+ create mode 100644 tests/zfs-tests/cmd/idmap_util/Makefile.am
+ create mode 100644 tests/zfs-tests/cmd/idmap_util/idmap_util.c
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh
+ create mode 100644 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh
+ create mode 100644 tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_common.kshlib
+ create mode 100755 tests/zfs-tests/tests/functional/idmap_mount/setup.ksh
+
+diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
+index a94472d1261..211e6a1dd41 100644
+--- a/tests/runfiles/linux.run
++++ b/tests/runfiles/linux.run
+@@ -188,3 +188,8 @@ tags = ['functional', 'user_namespace']
+ tests = ['groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos',
+     'userquota_013_pos', 'userspace_003_pos']
+ tags = ['functional', 'userquota']
++
++[tests/functional/idmap_mount:Linux]
++tests = ['idmap_mount_001', 'idmap_mount_002', 'idmap_mount_003',
++    'idmap_mount_004']
++tags = ['functional', 'idmap_mount']
+diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am
+index 7202e00711d..4bd6747a81d 100644
+--- a/tests/zfs-tests/cmd/Makefile.am
++++ b/tests/zfs-tests/cmd/Makefile.am
+@@ -32,6 +32,7 @@ SUBDIRS = \
+ 
+ if BUILD_LINUX
+ SUBDIRS += \
++	idmap_util \
+ 	randfree_file \
+ 	read_dos_attributes \
+ 	user_ns_exec \
+diff --git a/tests/zfs-tests/cmd/idmap_util/.gitignore b/tests/zfs-tests/cmd/idmap_util/.gitignore
+new file mode 100644
+index 00000000000..37982f07a5e
+--- /dev/null
++++ b/tests/zfs-tests/cmd/idmap_util/.gitignore
+@@ -0,0 +1 @@
++/idmap_util
+diff --git a/tests/zfs-tests/cmd/idmap_util/Makefile.am b/tests/zfs-tests/cmd/idmap_util/Makefile.am
+new file mode 100644
+index 00000000000..38b62eb0272
+--- /dev/null
++++ b/tests/zfs-tests/cmd/idmap_util/Makefile.am
+@@ -0,0 +1,9 @@
++include $(top_srcdir)/config/Rules.am
++
++pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
++
++pkgexec_PROGRAMS = idmap_util
++
++idmap_util_SOURCES = idmap_util.c
++idmap_util_LDADD = \
++	$(abs_top_builddir)/lib/libspl/libspl.la
+diff --git a/tests/zfs-tests/cmd/idmap_util/idmap_util.c b/tests/zfs-tests/cmd/idmap_util/idmap_util.c
+new file mode 100644
+index 00000000000..a9731f00dbc
+--- /dev/null
++++ b/tests/zfs-tests/cmd/idmap_util/idmap_util.c
+@@ -0,0 +1,791 @@
++/*
++ * CDDL HEADER START
++ *
++ * The contents of this file are subject to the terms of the
++ * Common Development and Distribution License (the "License").
++ * 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 https://opensource.org/licenses/CDDL-1.0.
++ * See the License for the specific language governing permissions
++ * and limitations under the License.
++ *
++ * When distributing Covered Code, include this CDDL HEADER in each
++ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++ * If applicable, add the following below this CDDL HEADER, with the
++ * fields enclosed by brackets "[]" replaced with your own identifying
++ * information: Portions Copyright [yyyy] [name of copyright owner]
++ *
++ * CDDL HEADER END
++ */
++
++#ifndef _GNU_SOURCE
++#define	_GNU_SOURCE
++#endif
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdbool.h>
++#include <stddef.h>
++#include <string.h>
++#include <linux/types.h>
++#include <sys/wait.h>
++#include <sys/stat.h>
++#include <sys/mount.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <sched.h>
++#include <syscall.h>
++
++#include <sys/list.h>
++
++#ifndef UINT_MAX
++#define	UINT_MAX	4294967295U
++#endif
++
++#ifndef __NR_Linux
++#if defined __alpha__
++#define	__NR_Linux 110
++#elif defined _MIPS_SIM
++#if _MIPS_SIM == _MIPS_SIM_ABI32
++#define	__NR_Linux 4000
++#endif
++#if _MIPS_SIM == _MIPS_SIM_NABI32
++#define	__NR_Linux 6000
++#endif
++#if _MIPS_SIM == _MIPS_SIM_ABI64
++#define	__NR_Linux 5000
++#endif
++#elif defined __ia64__
++#define	__NR_Linux 1024
++#else
++#define	__NR_Linux 0
++#endif
++#endif
++
++#ifndef __NR_mount_setattr
++#define	__NR_mount_setattr (442 + __NR_Linux)
++#endif
++
++#ifndef __NR_open_tree
++#define	__NR_open_tree (428 + __NR_Linux)
++#endif
++
++#ifndef __NR_move_mount
++#define	__NR_move_mount (429 + __NR_Linux)
++#endif
++
++#ifndef MNT_DETACH
++#define	MNT_DETACH 2
++#endif
++
++#ifndef MOVE_MOUNT_F_EMPTY_PATH
++#define	MOVE_MOUNT_F_EMPTY_PATH 0x00000004
++#endif
++
++#ifndef MOUNT_ATTR_IDMAP
++#define	MOUNT_ATTR_IDMAP 0x00100000
++#endif
++
++#ifndef OPEN_TREE_CLONE
++#define	OPEN_TREE_CLONE 1
++#endif
++
++#ifndef OPEN_TREE_CLOEXEC
++#define	OPEN_TREE_CLOEXEC O_CLOEXEC
++#endif
++
++#ifndef AT_RECURSIVE
++#define	AT_RECURSIVE 0x8000
++#endif
++
++#ifndef mount_attr
++struct mount_attr {
++	__u64 attr_set;
++	__u64 attr_clr;
++	__u64 propagation;
++	__u64 userns_fd;
++};
++#endif
++
++static inline int
++sys_mount_setattr(int dfd, const char *path, unsigned int flags,
++    struct mount_attr *attr, size_t size)
++{
++	return (syscall(__NR_mount_setattr, dfd, path, flags, attr, size));
++}
++
++static inline int
++sys_open_tree(int dfd, const char *filename, unsigned int flags)
++{
++	return (syscall(__NR_open_tree, dfd, filename, flags));
++}
++
++static inline int sys_move_mount(int from_dfd, const char *from_pathname,
++    int to_dfd, const char *to_pathname, unsigned int flags)
++{
++	return (syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
++	    to_pathname, flags));
++}
++
++typedef enum idmap_type_t {
++	TYPE_UID,
++	TYPE_GID,
++	TYPE_BOTH
++} idmap_type_t;
++
++struct idmap_entry {
++	__u32 first;
++	__u32 lower_first;
++	__u32 count;
++	idmap_type_t type;
++	list_node_t node;
++};
++
++static void
++log_msg(const char *msg, ...)
++{
++	va_list ap;
++
++	va_start(ap, msg);
++	vfprintf(stderr, msg, ap);
++	fputc('\n', stderr);
++	va_end(ap);
++}
++
++#define	log_errno(msg, args...) \
++	do { \
++		log_msg("%s:%d:%s: [%m] " msg, __FILE__, __LINE__,\
++		    __FUNCTION__, ##args); \
++	} while (0)
++
++/*
++ * Parse the idmapping in the following format
++ * and add to the list:
++ *
++ *   u:nsid_first:hostid_first:count
++ *   g:nsid_first:hostid_first:count
++ *   b:nsid_first:hostid_first:count
++ *
++ * The delimiter can be : or space character.
++ *
++ * Return:
++ *   0      if success
++ *   ENOMEM if out of memory
++ *   EINVAL if wrong arg or input
++ */
++static int
++parse_idmap_entry(list_t *head, char *input)
++{
++	char *token, *savedptr = NULL;
++	struct idmap_entry *entry;
++	unsigned long ul;
++	char *delimiter = (char *)": ";
++	char c;
++
++	if (!input || !head)
++		return (EINVAL);
++	entry = malloc(sizeof (*entry));
++	if (!entry)
++		return (ENOMEM);
++
++	token = strtok_r(input, delimiter, &savedptr);
++	if (token)
++		c = token[0];
++	if (!token || (c != 'b' && c != 'u' && c != 'g'))
++		goto errout;
++	entry->type = (c == 'b') ? TYPE_BOTH :
++	    ((c == 'u') ? TYPE_UID : TYPE_GID);
++
++	token = strtok_r(NULL, delimiter, &savedptr);
++	if (!token)
++		goto errout;
++	ul = strtoul(token, NULL, 10);
++	if (ul > UINT_MAX || errno != 0)
++		goto errout;
++	entry->first = (__u32)ul;
++
++	token = strtok_r(NULL, delimiter, &savedptr);
++	if (!token)
++		goto errout;
++	ul = strtoul(token, NULL, 10);
++	if (ul > UINT_MAX || errno != 0)
++		goto errout;
++	entry->lower_first = (__u32)ul;
++
++	token = strtok_r(NULL, delimiter, &savedptr);
++	if (!token)
++		goto errout;
++	ul = strtoul(token, NULL, 10);
++	if (ul > UINT_MAX || errno != 0)
++		goto errout;
++	entry->count = (__u32)ul;
++
++	list_insert_tail(head, entry);
++
++	return (0);
++
++errout:
++	free(entry);
++	return (EINVAL);
++}
++
++/*
++ * Release all the entries in the list
++ */
++static void
++free_idmap(list_t *head)
++{
++	struct idmap_entry *entry;
++
++	while ((entry = list_remove_head(head)) != NULL)
++		free(entry);
++	/* list_destroy() to be done by the caller */
++}
++
++/*
++ * Write all bytes in the buffer to fd
++ */
++static ssize_t
++write_buf(int fd, const char *buf, size_t buf_size)
++{
++	ssize_t written, total_written = 0;
++	size_t remaining = buf_size;
++	char *position = (char *)buf;
++
++	for (;;) {
++		written = write(fd, position, remaining);
++		if (written < 0 && errno == EINTR)
++			continue;
++		if (written < 0) {
++			log_errno("write");
++			return (written);
++		}
++		total_written += written;
++		if (total_written == buf_size)
++			break;
++		remaining -= written;
++		position += written;
++	}
++
++	return (total_written);
++}
++
++/*
++ * Read data from file into buffer
++ */
++static ssize_t
++read_buf(int fd, char *buf, size_t buf_size)
++{
++	int ret;
++	for (;;) {
++		ret = read(fd, buf, buf_size);
++		if (ret < 0 && errno == EINTR)
++			continue;
++		break;
++	}
++	if (ret < 0)
++		log_errno("read");
++	return (ret);
++}
++
++/*
++ * Write idmap of the given type in the buffer to the
++ * process' uid_map or gid_map proc file.
++ *
++ * Return:
++ *   0     if success
++ *   errno if there's any error
++ */
++static int
++write_idmap(pid_t pid, char *buf, size_t buf_size, idmap_type_t type)
++{
++	char path[PATH_MAX];
++	int fd = -EBADF;
++	int ret;
++
++	(void) snprintf(path, sizeof (path), "/proc/%d/%cid_map",
++	    pid, type == TYPE_UID ? 'u' : 'g');
++	fd = open(path, O_WRONLY | O_CLOEXEC);
++	if (fd < 0) {
++		ret = errno;
++		log_errno("open(%s)", path);
++		goto out;
++	}
++	ret = write_buf(fd, buf, buf_size);
++	if (ret < 0)
++		ret = errno;
++	else
++		ret = 0;
++out:
++	if (fd > 0)
++		close(fd);
++	return (ret);
++}
++
++/*
++ * Write idmap info in the list to the process
++ * user namespace, i.e. its /proc/<pid>/uid_map
++ * and /proc/<pid>/gid_map file.
++ *
++ * Return:
++ *   0     if success
++ *   errno if it fails
++ */
++static int
++write_pid_idmaps(pid_t pid, list_t *head)
++{
++	char *buf_uids, *buf_gids;
++	char *curr_bufu, *curr_bufg;
++	/* max 4k to be allowed for each map */
++	int size_buf_uids = 4096, size_buf_gids = 4096;
++	struct idmap_entry *entry;
++	int uid_filled, gid_filled;
++	int ret;
++	int has_uids = 0, has_gids = 0;
++	size_t buf_size;
++
++	buf_uids = malloc(size_buf_uids);
++	if (!buf_uids)
++		return (ENOMEM);
++	buf_gids = malloc(size_buf_gids);
++	if (!buf_gids) {
++		free(buf_uids);
++		return (ENOMEM);
++	}
++	curr_bufu = buf_uids;
++	curr_bufg = buf_gids;
++
++	for (entry = list_head(head); entry; entry = list_next(head, entry)) {
++		if (entry->type == TYPE_UID || entry->type == TYPE_BOTH) {
++			uid_filled = snprintf(curr_bufu, size_buf_uids,
++			    "%u %u %u\n", entry->first, entry->lower_first,
++			    entry->count);
++			if (uid_filled <= 0 || uid_filled >= size_buf_uids) {
++				ret = E2BIG;
++				goto out;
++			}
++			curr_bufu += uid_filled;
++			size_buf_uids -= uid_filled;
++			has_uids = 1;
++		}
++		if (entry->type == TYPE_GID || entry->type == TYPE_BOTH) {
++			gid_filled = snprintf(curr_bufg, size_buf_gids,
++			    "%u %u %u\n", entry->first, entry->lower_first,
++			    entry->count);
++			if (gid_filled <= 0 || gid_filled >= size_buf_gids) {
++				ret = E2BIG;
++				goto out;
++			}
++			curr_bufg += gid_filled;
++			size_buf_gids -= gid_filled;
++			has_gids = 1;
++		}
++	}
++	if (has_uids) {
++		buf_size = curr_bufu - buf_uids;
++		ret = write_idmap(pid, buf_uids, buf_size, TYPE_UID);
++		if (ret)
++			goto out;
++	}
++	if (has_gids) {
++		buf_size = curr_bufg - buf_gids;
++		ret = write_idmap(pid, buf_gids, buf_size, TYPE_GID);
++	}
++
++out:
++	free(buf_uids);
++	free(buf_gids);
++	return (ret);
++}
++
++/*
++ * Wait for the child process to exit
++ * and reap it.
++ *
++ * Return:
++ *   process exit code if available
++ */
++static int
++wait_for_pid(pid_t pid)
++{
++	int status;
++	int ret;
++
++	for (;;) {
++		ret = waitpid(pid, &status, 0);
++		if (ret < 0) {
++			if (errno == EINTR)
++				continue;
++			return (EXIT_FAILURE);
++		}
++		break;
++	}
++	if (!WIFEXITED(status))
++		return (EXIT_FAILURE);
++	return (WEXITSTATUS(status));
++}
++
++/*
++ * Get the file descriptor of the process user namespace
++ * given its pid.
++ *
++ * Return:
++ *   fd  if success
++ *   -1  if it fails
++ */
++static int
++userns_fd_from_pid(pid_t pid)
++{
++	int fd;
++	char path[PATH_MAX];
++
++	(void) snprintf(path, sizeof (path), "/proc/%d/ns/user", pid);
++	fd = open(path, O_RDONLY | O_CLOEXEC);
++	if (fd < 0)
++		log_errno("open(%s)", path);
++	return (fd);
++}
++
++/*
++ * Get the user namespace file descriptor given a list
++ * of idmap info.
++ *
++ * Return:
++ *   fd     if success
++ *   -errno if it fails
++ */
++static int
++userns_fd_from_idmap(list_t *head)
++{
++	pid_t pid;
++	int ret, fd;
++	int pipe_fd[2];
++	char c;
++	int saved_errno = 0;
++
++	/* pipe for bidirectional communication */
++	ret = pipe(pipe_fd);
++	if (ret) {
++		log_errno("pipe");
++		return (-errno);
++	}
++
++	pid = fork();
++	if (pid < 0) {
++		log_errno("fork");
++		return (-errno);
++	}
++
++	if (pid == 0) {
++		/* child process */
++		close(pipe_fd[0]);
++		ret = unshare(CLONE_NEWUSER);
++		if (ret == 0) {
++			/* notify the parent of success */
++			ret = write_buf(pipe_fd[1], "1", 1);
++		} else {
++			saved_errno = errno;
++			log_errno("unshare");
++			ret = write_buf(pipe_fd[1], "0", 1);
++		}
++		if (ret < 0)
++			saved_errno = errno;
++		close(pipe_fd[1]);
++		exit(saved_errno);
++	}
++	/* parent process */
++	close(pipe_fd[1]);
++	ret = read_buf(pipe_fd[0], &c, 1);
++	if (ret == 1 && c == '1') {
++		ret = write_pid_idmaps(pid, head);
++		if (!ret) {
++			fd = userns_fd_from_pid(pid);
++			if (fd < 0)
++				fd = -errno;
++		} else {
++			fd = -ret;
++		}
++	} else {
++		fd = -EBADF;
++	}
++	close(pipe_fd[0]);
++	(void) wait_for_pid(pid);
++	return (fd);
++}
++
++/*
++ * Check if the operating system supports idmapped mount on the
++ * given path or not.
++ *
++ * Return:
++ *   true  if supported
++ *   false if not supported
++ */
++static bool
++is_idmap_supported(char *path)
++{
++	list_t head;
++	int ret;
++	int tree_fd = -EBADF, path_fd = -EBADF;
++	struct mount_attr attr = {
++	    .attr_set	= MOUNT_ATTR_IDMAP,
++	    .userns_fd  = -EBADF,
++	};
++
++	/* strtok_r() won't be happy with a const string */
++	char *input = strdup("b:0:1000000:1000000");
++
++	if (!input) {
++		errno = ENOMEM;
++		log_errno("strdup");
++		return (false);
++	}
++
++	list_create(&head, sizeof (struct idmap_entry),
++	    offsetof(struct idmap_entry, node));
++	ret = parse_idmap_entry(&head, input);
++	if (ret) {
++		errno = ret;
++		log_errno("parse_idmap_entry(%s)", input);
++		goto out;
++	}
++	ret = userns_fd_from_idmap(&head);
++	if (ret < 0)
++		goto out;
++	attr.userns_fd = ret;
++	ret = openat(-EBADF, path, O_DIRECTORY | O_CLOEXEC);
++	if (ret < 0) {
++		log_errno("openat(%s)", path);
++		goto out;
++	}
++	path_fd = ret;
++	ret = sys_open_tree(path_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
++	    AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
++	if (ret < 0) {
++		log_errno("sys_open_tree");
++		goto out;
++	}
++	tree_fd = ret;
++	ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH, &attr,
++	    sizeof (attr));
++	if (ret < 0) {
++		log_errno("sys_mount_setattr");
++	}
++out:
++	free_idmap(&head);
++	list_destroy(&head);
++	if (tree_fd > 0)
++		close(tree_fd);
++	if (path_fd > 0)
++		close(path_fd);
++	if (attr.userns_fd > 0)
++		close(attr.userns_fd);
++	free(input);
++	return (ret == 0);
++}
++
++/*
++ * Check if the given path is a mount point or not.
++ *
++ * Return:
++ *   true  if it is
++ *   false otherwise
++ */
++static bool
++is_mountpoint(char *path)
++{
++	char *parent;
++	struct stat st_me, st_parent;
++	bool ret;
++
++	parent = malloc(strlen(path)+4);
++	if (!parent) {
++		errno = ENOMEM;
++		log_errno("malloc");
++		return (false);
++	}
++	strcat(strcpy(parent, path), "/..");
++	if (lstat(path, &st_me) != 0 ||
++	    lstat(parent, &st_parent) != 0)
++		ret = false;
++	else
++		if (st_me.st_dev != st_parent.st_dev ||
++		    st_me.st_ino == st_parent.st_ino)
++			ret = true;
++		else
++			ret = false;
++	free(parent);
++	return (ret);
++}
++
++/*
++ * Remount the source on the new target folder with the given
++ * list of idmap info. If target is NULL, the source will be
++ * unmounted and then remounted if it is a mountpoint, otherwise
++ * no unmount is done, the source is simply idmap remounted.
++ *
++ * Return:
++ *   0      if success
++ *   -errno otherwise
++ */
++static int
++do_idmap_mount(list_t *idmap, char *source, char *target, int flags)
++{
++	int ret;
++	int tree_fd = -EBADF, source_fd = -EBADF;
++	struct mount_attr attr = {
++	    .attr_set   = MOUNT_ATTR_IDMAP,
++	    .userns_fd  = -EBADF,
++	};
++
++	ret = userns_fd_from_idmap(idmap);
++	if (ret < 0)
++		goto out;
++	attr.userns_fd = ret;
++	ret = openat(-EBADF, source, O_DIRECTORY | O_CLOEXEC);
++	if (ret < 0) {
++		ret = -errno;
++		log_errno("openat(%s)", source);
++		goto out;
++	}
++	source_fd = ret;
++	ret = sys_open_tree(source_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
++	    AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE | flags);
++	if (ret < 0) {
++		ret = -errno;
++		log_errno("sys_open_tree");
++		goto out;
++	}
++	tree_fd = ret;
++	ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH | flags, &attr,
++	    sizeof (attr));
++	if (ret < 0) {
++		ret = -errno;
++		log_errno("sys_mount_setattr");
++		goto out;
++	}
++	if (source != NULL && target == NULL && is_mountpoint(source)) {
++		ret = umount2(source, MNT_DETACH);
++		if (ret < 0) {
++			ret = -errno;
++			log_errno("umount2(%s)", source);
++			goto out;
++		}
++	}
++	ret = sys_move_mount(tree_fd, "", -EBADF, target == NULL ?
++	    source : target, MOVE_MOUNT_F_EMPTY_PATH);
++	if (ret < 0) {
++		ret = -errno;
++		log_errno("sys_move_mount(%s)", target == NULL ?
++		    source : target);
++	}
++out:
++	if (tree_fd > 0)
++		close(tree_fd);
++	if (source_fd > 0)
++		close(source_fd);
++	if (attr.userns_fd > 0)
++		close(attr.userns_fd);
++	return (ret);
++}
++
++static void
++print_usage(char *argv[])
++{
++	fprintf(stderr, "Usage: %s [-r] [-c] [-m <idmap1>] [-m <idmap2>]" \
++	    " ... [<source>] [<target>]\n", argv[0]);
++	fprintf(stderr, "\n");
++	fprintf(stderr, "  -r Recursively do idmapped mount.\n");
++	fprintf(stderr, "\n");
++	fprintf(stderr, "  -c Checks if idmapped mount is supported " \
++	    "on the <source> by the operating system or not.\n");
++	fprintf(stderr, "\n");
++	fprintf(stderr, "  -m <idmap> to specify the idmap info, " \
++	    "in the following format:\n");
++	fprintf(stderr, "     <id_type>:<nsid_first>:<hostid_first>:<count>\n");
++	fprintf(stderr, "\n");
++	fprintf(stderr, "  <id_type> can be either of 'b', 'u', and 'g'.\n");
++	fprintf(stderr, "\n");
++	fprintf(stderr, "The <source> folder will be mounted at <target> " \
++	    "with the provided idmap information.\nIf no <target> is " \
++	    "specified, and <source> is a mount point, " \
++	    "then <source> will be unmounted and then remounted.\n");
++}
++
++int
++main(int argc, char *argv[])
++{
++	int opt;
++	list_t idmap_head;
++	int check_supported = 0;
++	int ret = EXIT_SUCCESS;
++	char *source = NULL, *target = NULL;
++	int flags = 0;
++
++	list_create(&idmap_head, sizeof (struct idmap_entry),
++	    offsetof(struct idmap_entry, node));
++
++	while ((opt = getopt(argc, argv, "rcm:")) != -1) {
++		switch (opt) {
++		case 'r':
++			flags |= AT_RECURSIVE;
++			break;
++		case 'c':
++			check_supported = 1;
++			break;
++		case 'm':
++			ret = parse_idmap_entry(&idmap_head, optarg);
++			if (ret) {
++				errno = ret;
++				log_errno("parse_idmap_entry(%s)", optarg);
++				ret = EXIT_FAILURE;
++				goto out;
++			}
++			break;
++		default:
++			print_usage(argv);
++			exit(EXIT_FAILURE);
++		}
++	}
++
++	if (check_supported == 0 && list_is_empty(&idmap_head))	{
++		print_usage(argv);
++		ret = EXIT_FAILURE;
++		goto out;
++	}
++
++	if (optind >= argc) {
++		fprintf(stderr, "Expected to have <source>, <target>.\n");
++		print_usage(argv);
++		ret = EXIT_FAILURE;
++		goto out;
++	}
++
++	source = argv[optind];
++	if (optind < (argc - 1)) {
++		target = argv[optind + 1];
++	}
++
++	if (check_supported) {
++		free_idmap(&idmap_head);
++		list_destroy(&idmap_head);
++		if (is_idmap_supported(source)) {
++			printf("idmapped mount is supported on [%s].\n",
++			    source);
++			return (EXIT_SUCCESS);
++		} else {
++			printf("idmapped mount is NOT supported.\n");
++			return (EXIT_FAILURE);
++		}
++	}
++
++	ret = do_idmap_mount(&idmap_head, source, target, flags);
++	if (ret)
++		ret = EXIT_FAILURE;
++out:
++	free_idmap(&idmap_head);
++	list_destroy(&idmap_head);
++
++	exit(ret);
++}
+diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg
+index 9953c94952e..db6ee24417c 100644
+--- a/tests/zfs-tests/include/commands.cfg
++++ b/tests/zfs-tests/include/commands.cfg
+@@ -220,4 +220,4 @@ export ZFSTEST_FILES='badsend
+     user_ns_exec
+     write_dos_attributes
+     xattrtest
+-    stride_dd'
++    idmap_util'
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh b/tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh
+new file mode 100755
+index 00000000000..4895aa23ee4
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh
+@@ -0,0 +1,25 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++default_cleanup
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg
+new file mode 100644
+index 00000000000..51998945d0d
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg
+@@ -0,0 +1,25 @@
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++export UID1=1000000
++export GID1=1000000
++export UID2=2000000
++export GID2=2000000
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh
+new file mode 100755
+index 00000000000..e7187935e53
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh
+@@ -0,0 +1,76 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib
++
++#
++#
++# DESCRIPTION:
++#       Test uid and gid of files in idmapped folder are mapped correctly
++#
++#
++# STRATEGY:
++#       1. Create files/folder owned by $UID1 and $GID1 under "idmap_test"
++#       2. Idmap the folder to "idmap_dest"
++#       3. Verify the owner of files/folder under "idmap_dest"
++#
++
++verify_runnable "global"
++
++export WORKDIR=$TESTDIR/idmap_test
++export IDMAPDIR=$TESTDIR/idmap_dest
++
++function cleanup
++{
++	log_must rm -rf $WORKDIR
++	if mountpoint $IDMAPDIR; then
++		log_must umount $IDMAPDIR
++	fi
++	log_must rm -rf $IDMAPDIR
++}
++
++log_onexit cleanup
++
++if ! idmap_util -c $TESTDIR; then
++	log_unsupported "Idmap mount not supported."
++fi
++
++log_must mkdir -p $WORKDIR
++log_must mkdir -p $IDMAPDIR
++log_must touch $WORKDIR/file1
++log_must mkdir $WORKDIR/subdir
++log_must ln -s $WORKDIR/file1 $WORKDIR/file1_sym
++log_must ln $WORKDIR/file1 $WORKDIR/subdir/file1_hard
++log_must touch $WORKDIR/subdir/file2
++log_must chown -R $UID1:$GID1 $WORKDIR
++log_must chown $UID2:$GID2 $WORKDIR/subdir/file2
++
++log_must idmap_util -m "u:${UID1}:${UID2}:1" -m "g:${GID1}:${GID2}:1" $WORKDIR $IDMAPDIR
++
++log_must test "$UID2 $GID2" = "$(stat -c '%u %g' $IDMAPDIR/file1)"
++log_must test "$UID2 $GID2" = "$(stat -c '%u %g' $IDMAPDIR/file1_sym)"
++log_must test "$UID2 $GID2" = "$(stat -c '%u %g' $IDMAPDIR/subdir)"
++log_must test "$UID2 $GID2" = "$(stat -c '%u %g' $IDMAPDIR/subdir/file1_hard)"
++log_mustnot test "$UID2 $GID2" = "$(stat -c '%u %g' $IDMAPDIR/subdir/file2)"
++
++log_pass "Owner verification of entries under idmapped folder is successful."
++
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh
+new file mode 100755
+index 00000000000..8cba90ea58b
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh
+@@ -0,0 +1,97 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib
++
++#
++#
++# DESCRIPTION:
++#       Perform file operations in idmapped folder, check owner in its base.
++#
++#
++# STRATEGY:
++#       1. Create folder "idmap_test"
++#       2. Idmap the folder to "idmap_dest"
++#       3. Do basic file operations in "idmap_dest" folder, verify the owner in
++#          the base folder "idmap_test"
++#
++
++verify_runnable "global"
++
++export WORKDIR=$TESTDIR/idmap_test
++export IDMAPDIR=$TESTDIR/idmap_dest
++
++function cleanup
++{
++	log_must rm -rf $IDMAPDIR/*
++	if mountpoint $IDMAPDIR; then
++		log_must umount $IDMAPDIR
++	fi
++	log_must rm -rf $IDMAPDIR $WORKDIR
++}
++
++log_onexit cleanup
++
++if ! idmap_util -c $TESTDIR; then
++	log_unsupported "Idmap mount not supported."
++fi
++
++log_must mkdir -p $WORKDIR
++log_must mkdir -p $IDMAPDIR
++
++log_must chown $UID1:$GID1 $WORKDIR
++log_must idmap_util -m "u:${UID1}:${UID2}:1" -m "g:${GID1}:${GID2}:1" $WORKDIR $IDMAPDIR
++
++SETPRIV="setpriv --reuid $UID2 --regid $GID2 --clear-groups"
++
++log_must $SETPRIV touch $IDMAPDIR/file1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1)"
++
++log_must $SETPRIV mv $IDMAPDIR/file1 $IDMAPDIR/file1_renamed
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1_renamed)"
++
++log_must $SETPRIV mv $IDMAPDIR/file1_renamed $IDMAPDIR/file1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1)"
++
++log_must $SETPRIV mkdir $IDMAPDIR/subdir
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir)"
++
++log_must $SETPRIV ln -s $IDMAPDIR/file1 $IDMAPDIR/file1_sym
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1_sym)"
++
++log_must $SETPRIV ln $IDMAPDIR/file1 $IDMAPDIR/subdir/file1_hard
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir/file1_hard)"
++
++log_must $SETPRIV touch $IDMAPDIR/subdir/file2
++log_must $SETPRIV chown $UID2:$GID2 $IDMAPDIR/subdir/file2
++log_mustnot $SETPRIV chown $UID1 $IDMAPDIR/subdir/file2
++
++log_must $SETPRIV cp -r $IDMAPDIR/subdir $IDMAPDIR/subdir1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir1/file2)"
++log_must $SETPRIV rm -rf $IDMAPDIR/subdir1
++
++log_must $SETPRIV cp -rp $IDMAPDIR/subdir $IDMAPDIR/subdir1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir1/file1_hard)"
++log_must $SETPRIV rm -rf $IDMAPDIR/subdir1
++
++log_pass "Owner verification of entries under base folder is successful."
++
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh
+new file mode 100755
+index 00000000000..1f1a2aec655
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh
+@@ -0,0 +1,121 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib
++
++#
++#
++# DESCRIPTION:
++#       Perform file operations in idmapped folder in user namespace,
++#       then check the owner in its base.
++#
++#
++# STRATEGY:
++#       1. Create folder "idmap_test"
++#       2. Idmap the folder to "idmap_dest"
++#       3. Perform file operations in the idmapped folder in the user
++#          namespace having the same idmap info as the idmapped mount
++#       4. Verify the owner of entries under the base folder "idmap_test"
++#
++
++verify_runnable "global"
++
++export WORKDIR=$TESTDIR/idmap_test
++export IDMAPDIR=$TESTDIR/idmap_dest
++
++function cleanup
++{
++	kill -TERM ${unshared_pid}
++	log_must rm -rf $IDMAPDIR/*
++	if mountpoint $IDMAPDIR; then
++		log_must umount $IDMAPDIR
++	fi
++	log_must rm -rf $IDMAPDIR $WORKDIR
++}
++
++log_onexit cleanup
++
++if ! idmap_util -c $TESTDIR; then
++	log_unsupported "Idmap mount not supported."
++fi
++
++log_must mkdir -p $WORKDIR
++log_must mkdir -p $IDMAPDIR
++
++log_must chown $UID1:$GID1 $WORKDIR
++log_must idmap_util -m "u:${UID1}:${UID2}:1" -m "g:${GID1}:${GID2}:1" $WORKDIR $IDMAPDIR
++
++# Create a user namespace with the same idmapping
++unshare -Urm echo test
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++unshare -Um /usr/bin/sleep 2h &
++unshared_pid=$!
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++# wait for userns to be ready
++sleep 1
++echo "${UID1} ${UID2} 1" > /proc/$unshared_pid/uid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to uid_map"
++fi
++echo "${GID1} ${GID2} 1" > /proc/$unshared_pid/gid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to gid_map"
++fi
++
++NSENTER="nsenter -t $unshared_pid --all -S ${UID1} -G ${GID1}"
++
++log_must $NSENTER touch $IDMAPDIR/file1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1)"
++
++log_must $NSENTER mv $IDMAPDIR/file1 $IDMAPDIR/file1_renamed
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1_renamed)"
++
++log_must $NSENTER mv $IDMAPDIR/file1_renamed $IDMAPDIR/file1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1)"
++
++log_must $NSENTER mkdir $IDMAPDIR/subdir
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir)"
++
++log_must $NSENTER ln -s $IDMAPDIR/file1 $IDMAPDIR/file1_sym
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/file1_sym)"
++
++log_must $NSENTER ln $IDMAPDIR/file1 $IDMAPDIR/subdir/file1_hard
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir/file1_hard)"
++
++log_must $NSENTER touch $IDMAPDIR/subdir/file2
++log_must $NSENTER chown $UID1:$GID1 $IDMAPDIR/subdir/file2
++log_mustnot $NSENTER chown $UID2 $IDMAPDIR/subdir/file2
++
++log_must $NSENTER cp -r $IDMAPDIR/subdir $IDMAPDIR/subdir1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir1/file2)"
++log_must $NSENTER rm -rf $IDMAPDIR/subdir1
++
++log_must $NSENTER cp -rp $IDMAPDIR/subdir $IDMAPDIR/subdir1
++log_must test "$UID1 $GID1" = "$(stat -c '%u %g' $WORKDIR/subdir1/file1_hard)"
++log_must $NSENTER rm -rf $IDMAPDIR/subdir1
++
++log_pass "Owner verification of entries under the base folder is successful."
++
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh
+new file mode 100755
+index 00000000000..89f2f750d23
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh
+@@ -0,0 +1,106 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib
++
++#
++#
++# DESCRIPTION:
++#       Test setgid bit is set properly on the idmapped mount
++#       in a user namespace.
++#
++# STRATEGY:
++#       1. Create folder "idmap_test", set gid bit on it
++#       2. Idmap the folder to "idmap_dest"
++#       3. Create file and folder in the idmapped folder in the user
++#          namespace having the same idmap info 
++#       4. Verify the gid bit of the file and folder is set
++#
++
++verify_runnable "global"
++
++export WORKDIR=$TESTDIR/idmap_test
++export IDMAPDIR=$TESTDIR/idmap_dest
++
++function cleanup
++{
++	kill -TERM ${unshared_pid}
++	log_must rm -rf $IDMAPDIR/*
++	if mountpoint $IDMAPDIR; then
++		log_must umount $IDMAPDIR
++	fi
++	log_must rm -rf $IDMAPDIR $WORKDIR
++}
++
++log_onexit cleanup
++
++if ! idmap_util -c $TESTDIR; then
++	log_unsupported "Idmap mount not supported."
++fi
++
++log_must mkdir -p $WORKDIR
++log_must mkdir -p $IDMAPDIR
++
++log_must chown $UID1:$GID1 $WORKDIR
++# set gid bit
++log_must chmod 2755 $WORKDIR
++log_must idmap_util -m "u:${UID1}:${UID2}:1" -m "g:${GID1}:${GID2}:1" $WORKDIR $IDMAPDIR
++log_must test -g $IDMAPDIR
++
++# Create a user namespace with the same idmapping
++unshare -Urm echo test
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++unshare -Um /usr/bin/sleep 2h &
++unshared_pid=$!
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to create user namespace"
++fi
++# wait for userns to be ready
++sleep 1
++echo "${UID1} ${UID2} 1" > /proc/$unshared_pid/uid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to uid_map"
++fi
++echo "${GID1} ${GID2} 1" > /proc/$unshared_pid/gid_map
++if [ "$?" -ne "0" ]; then
++	log_unsupported "Failed to write to gid_map"
++fi
++
++NSENTER="nsenter -t $unshared_pid --all -S ${UID1} -G ${GID1}"
++
++# gid bit can be set on the file
++log_must $NSENTER touch $IDMAPDIR/file1
++log_must $NSENTER chmod 2654 $IDMAPDIR/file1
++log_must test -g $WORKDIR/file1
++log_must test -g $IDMAPDIR/file1
++log_must test "$UID1 $GID1" = "$($NSENTER stat -c '%u %g' $IDMAPDIR/file1)"
++
++# gid bit is carried over to new folder
++log_must $NSENTER mkdir $IDMAPDIR/subdir
++log_must test -g $WORKDIR/subdir
++log_must test -g $IDMAPDIR/subdir
++log_must test "$UID1 $GID1" = "$($NSENTER stat -c '%u %g' $IDMAPDIR/subdir)"
++
++log_pass "Verification of setting gid bit in userns is successful."
++
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_common.kshlib b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_common.kshlib
+new file mode 100644
+index 00000000000..980845ca209
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_common.kshlib
+@@ -0,0 +1,23 @@
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/include/libtest.shlib
++. $STF_SUITE/tests/functional/idmap_mount/idmap_mount.cfg
+diff --git a/tests/zfs-tests/tests/functional/idmap_mount/setup.ksh b/tests/zfs-tests/tests/functional/idmap_mount/setup.ksh
+new file mode 100755
+index 00000000000..90a14f12058
+--- /dev/null
++++ b/tests/zfs-tests/tests/functional/idmap_mount/setup.ksh
+@@ -0,0 +1,30 @@
++#!/bin/ksh -p
++#
++# CDDL HEADER START
++#
++# The contents of this file are subject to the terms of the
++# Common Development and Distribution License (the "License").
++# 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 https://opensource.org/licenses/CDDL-1.0.
++# See the License for the specific language governing permissions
++# and limitations under the License.
++#
++# When distributing Covered Code, include this CDDL HEADER in each
++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++# If applicable, add the following below this CDDL HEADER, with the
++# fields enclosed by brackets "[]" replaced with your own identifying
++# information: Portions Copyright [yyyy] [name of copyright owner]
++#
++# CDDL HEADER END
++#
++
++. $STF_SUITE/include/libtest.shlib
++
++# unable to do idmapped mount in a local zone
++verify_runnable "global"
++
++DISK=${DISKS%% *}
++default_setup $DISK
++
+
+From d6b868c52df987a992908763b253172112f895ec Mon Sep 17 00:00:00 2001
+From: Spotlight <spotlight@joscomputing.space>
+Date: Wed, 26 Apr 2023 12:13:04 -0500
+Subject: [PATCH 3/3] Backport "idmap_mount tests can be skipped on older
+ kernel"
+
+---
+ tests/test-runner/bin/zts-report.py.in | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in
+index 432899c21f4..a98e8175b06 100755
+--- a/tests/test-runner/bin/zts-report.py.in
++++ b/tests/test-runner/bin/zts-report.py.in
+@@ -146,6 +146,11 @@ na_reason = "Not applicable"
+ #
+ ci_reason = 'CI runner doesn\'t have all requirements'
+ 
++#
++# Idmapped mount is only supported in kernel version >= 5.12
++#
++idmap_reason = 'Idmapped mount needs kernel 5.12+'
++
+ summary = {
+     'total': float(0),
+     'passed': float(0),
+@@ -289,6 +294,10 @@ elif sys.platform.startswith('linux'):
+         'mmp/mmp_inactive_import': ['FAIL', known_reason],
+         'zvol/zvol_misc/zvol_misc_snapdev': ['FAIL', '12621'],
+         'zvol/zvol_misc/zvol_misc_volmode': ['FAIL', known_reason],
++        'idmap_mount/idmap_mount_001': ['SKIP', idmap_reason],
++        'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason],
++        'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
++        'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
+     })
+ 
+ 
diff --git a/sys-fs/zfs-kmod/zfs-kmod-2.1.11.ebuild b/sys-fs/zfs-kmod/zfs-kmod-2.1.11.ebuild
index 0308647..2ea9af3 100644
--- a/sys-fs/zfs-kmod/zfs-kmod-2.1.11.ebuild
+++ b/sys-fs/zfs-kmod/zfs-kmod-2.1.11.ebuild
@@ -60,6 +60,27 @@ RESTRICT="debug? ( strip ) test"
 
 DOCS=( AUTHORS COPYRIGHT META README.md )
 
+PATCHES=(
+       # Prerequisite to idmap patch: "Use generic_permission() on Linux if ACL is trivial"
+       # https://github.com/openzfs/zfs/pull/13237
+       "${FILESDIR}"/2.1.11-optimize-access-checks.patch
+       # Prerequesite to idmap patch: "Expose additional file level attributes"
+       # https://github.com/openzfs/zfs/pull/13118
+       "${FILESDIR}"/2.1.11-expose-additional-attributes.patch
+       # Backported version of "Support idmapped mount"
+       # https://github.com/openzfs/zfs/pull/13671
+       "${FILESDIR}"/2.1.11-support-idmapped-mount.patch
+       # Backported version of "Support idmapped mount in user namespace"
+       # https://github.com/openzfs/zfs/pull/14097
+       "${FILESDIR}"/2.1.11-support-idmapped-mount-in-user-ns.patch
+       # Backported version of "Linux 6.3 compat: idmapped mount API changes"
+       # https://github.com/openzfs/zfs/pull/14682
+       "${FILESDIR}"/2.1.11-6.3-compat-idmapped-mount-api-changes.patch
+       # "Linux 6.3 compat: writepage_t first arg struct folio*"
+       # https://github.com/openzfs/zfs/pull/14699
+       "${FILESDIR}"/2.1.11-6.3-compat-writepage_t-typedef-change.patch
+)
+
 pkg_pretend() {
 	use rootfs || return 0
 
-- 
GitLab