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