summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-iattr-vfsid.m424
-rw-r--r--config/kernel.m42
-rw-r--r--include/os/linux/kernel/linux/xattr_compat.h10
-rw-r--r--include/os/linux/spl/sys/cred.h84
-rw-r--r--include/os/linux/zfs/sys/policy.h5
-rw-r--r--module/os/linux/zfs/policy.c17
-rw-r--r--module/os/linux/zfs/zfs_acl.c24
-rw-r--r--module/os/linux/zfs/zfs_dir.c7
-rw-r--r--module/os/linux/zfs/zfs_vnops_os.c25
-rw-r--r--module/os/linux/zfs/zfs_znode.c2
-rw-r--r--module/os/linux/zfs/zpl_ctldir.c2
-rw-r--r--module/os/linux/zfs/zpl_file.c6
-rw-r--r--module/os/linux/zfs/zpl_inode.c38
-rw-r--r--module/os/linux/zfs/zpl_xattr.c34
-rw-r--r--module/zfs/zfs_replay.c35
-rw-r--r--module/zfs/zfs_vnops.c12
-rw-r--r--tests/runfiles/linux.run2
-rwxr-xr-xtests/test-runner/bin/zts-report.py.in1
-rw-r--r--tests/zfs-tests/cmd/idmap_util.c49
-rw-r--r--tests/zfs-tests/tests/Makefile.am3
-rwxr-xr-xtests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh138
21 files changed, 424 insertions, 96 deletions
diff --git a/config/kernel-iattr-vfsid.m4 b/config/kernel-iattr-vfsid.m4
new file mode 100644
index 000000000..75bc4613b
--- /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 b573881c4..c71d576f4 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -149,6 +149,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC
ZFS_AC_KERNEL_SRC_USER_NS_COMMON_INUM
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])
@@ -271,6 +272,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC
ZFS_AC_KERNEL_USER_NS_COMMON_INUM
ZFS_AC_KERNEL_IDMAP_MNT_API
+ ZFS_AC_KERNEL_IATTR_VFSID
])
dnl #
diff --git a/include/os/linux/kernel/linux/xattr_compat.h b/include/os/linux/kernel/linux/xattr_compat.h
index 9b83813db..ff80fbb06 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(kcred->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(kcred->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(kcred->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 dc3c260db..75ad400d3 100644
--- a/include/os/linux/spl/sys/cred.h
+++ b/include/os/linux/spl/sys/cred.h
@@ -26,11 +26,14 @@
#include <linux/module.h>
#include <linux/cred.h>
+#include <linux/sched.h>
#include <sys/types.h>
#include <sys/vfs.h>
typedef struct cred cred_t;
+extern struct task_struct init_task;
+
#define kcred ((cred_t *)(init_task.cred))
#define CRED() ((cred_t *)current_cred())
@@ -45,32 +48,80 @@ 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)
+/* 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 == kcred->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 (kcred->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 +132,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/zfs/sys/policy.h b/include/os/linux/zfs/sys/policy.h
index ee7fda761..b182da95b 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/zfs/policy.c b/module/os/linux/zfs/policy.c
index 50eb7cfaa..eaf38df86 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 437169990..7d14863c5 100644
--- a/module/os/linux/zfs/zfs_acl.c
+++ b/module/os/linux/zfs/zfs_acl.c
@@ -1889,7 +1889,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;
}
}
@@ -1979,7 +1980,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,
+ kcred->user_ns)))
return (error);
mutex_enter(&zp->z_acl_lock);
@@ -2138,7 +2140,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,
+ kcred->user_ns)))
return (error);
error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp,
@@ -2301,9 +2304,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);
@@ -2417,7 +2420,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,
+ kcred->user_ns) != 0) {
uid_t owner;
owner = zfs_fuid_map_id(ZTOZSB(zp),
@@ -2610,7 +2614,8 @@ slow:
DTRACE_PROBE(zfs__fastpath__execute__access__miss);
if ((error = zfs_enter(ZTOZSB(zdp), FTAG)) != 0)
return (error);
- error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL);
+ error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr,
+ kcred->user_ns);
zfs_exit(ZTOZSB(zdp), FTAG);
return (error);
}
@@ -2662,7 +2667,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);
/*
@@ -2786,7 +2792,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, 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 b4e4146b0..85aa94d8d 100644
--- a/module/os/linux/zfs/zfs_dir.c
+++ b/module/os/linux/zfs/zfs_dir.c
@@ -1113,11 +1113,11 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr)
*xzpp = NULL;
if ((error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr,
- NULL)))
+ kcred->user_ns)))
return (error);
if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
- &acl_ids, NULL)) != 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);
@@ -1265,7 +1265,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,
+ kcred->user_ns) == 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 545d8ad8d..29d62837a 100644
--- a/module/os/linux/zfs/zfs_vnops_os.c
+++ b/module/os/linux/zfs/zfs_vnops_os.c
@@ -476,7 +476,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, kcred->user_ns))) {
zrele(*zpp);
*zpp = NULL;
}
@@ -494,7 +494,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,
+ kcred->user_ns))) {
zfs_exit(zfsvfs, FTAG);
return (error);
}
@@ -972,7 +973,7 @@ top:
return (error);
}
- if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
goto out;
}
@@ -1386,7 +1387,7 @@ top:
return (error);
}
- if ((error = zfs_zaccess_delete(dzp, zp, cr, NULL))) {
+ if ((error = zfs_zaccess_delete(dzp, zp, cr, kcred->user_ns))) {
goto out;
}
@@ -2024,10 +2025,10 @@ top:
* 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);
@@ -2162,7 +2163,7 @@ top:
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;
@@ -3506,7 +3507,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,
+ kcred->user_ns))) {
zfs_exit(zfsvfs, FTAG);
return (error);
}
@@ -4132,7 +4134,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,
+ kcred->user_ns))) {
zfs_exit(zfsvfs, FTAG);
return (error);
}
diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
index c8f6e02bd..662147ab4 100644
--- a/module/os/linux/zfs/zfs_znode.c
+++ b/module/os/linux/zfs/zfs_znode.c
@@ -1964,7 +1964,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, 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 8bc4a9b39..f0779c81d 100644
--- a/module/os/linux/zfs/zpl_ctldir.c
+++ b/module/os/linux/zfs/zpl_ctldir.c
@@ -374,7 +374,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, 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 cfa03e571..c56e3691e 100644
--- a/module/os/linux/zfs/zpl_file.c
+++ b/module/os/linux/zfs/zpl_file.c
@@ -1085,7 +1085,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, kcred->user_ns);
spl_fstrans_unmark(cookie);
crfree(cr);
@@ -1133,7 +1133,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, kcred->user_ns);
spl_fstrans_unmark(cookie);
crfree(cr);
@@ -1221,7 +1221,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, 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 64016f9ac..6f4718f7d 100644
--- a/module/os/linux/zfs/zpl_inode.c
+++ b/module/os/linux/zfs/zpl_inode.c
@@ -118,16 +118,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));
}
}
@@ -145,7 +145,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 = kcred->user_ns;
#endif
crhold(cr);
@@ -192,7 +192,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 = kcred->user_ns;
#endif
/*
@@ -247,7 +247,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 = kcred->user_ns;
#endif
crhold(cr);
@@ -325,7 +325,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 = kcred->user_ns;
#endif
crhold(cr);
@@ -468,8 +468,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;
@@ -482,7 +494,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, kcred->user_ns);
#endif
if (!error && (ia->ia_valid & ATTR_MODE))
error = zpl_chmod_acl(ip);
@@ -510,7 +522,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 = kcred->user_ns;
#endif
crhold(cr);
@@ -557,7 +569,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 = 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 97b6e048c..99d9b3793 100644
--- a/module/os/linux/zfs/zpl_xattr.c
+++ b/module/os/linux/zfs/zpl_xattr.c
@@ -499,7 +499,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, kcred->user_ns);
if (error)
goto out;
}
@@ -738,9 +738,11 @@ __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)
{
+ (void) user_ns;
int error = 0;
/* xattr_resolve_name will do this for us if this is defined */
#ifndef HAVE_XATTR_HANDLER_NAME
@@ -846,9 +848,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;
@@ -914,9 +918,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 */
@@ -940,7 +946,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)
@@ -1300,7 +1306,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;
@@ -1314,8 +1321,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);
@@ -1339,7 +1352,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;
@@ -1353,8 +1367,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 5e20ce331..0293e46d5 100644
--- a/module/zfs/zfs_replay.c
+++ b/module/zfs/zfs_replay.c
@@ -386,8 +386,13 @@ zfs_replay_create_acl(void *arg1, void *arg2, boolean_t byteswap)
lr->lr_uid, lr->lr_gid);
}
+#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;
case TX_MKDIR_ACL:
aclstart = (caddr_t)(lracl + 1);
@@ -416,8 +421,13 @@ 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);
}
+#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;
default:
error = SET_ERROR(ENOTSUP);
@@ -527,8 +537,13 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
if (name == NULL)
name = (char *)start;
+#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;
case TX_MKDIR_ATTR:
lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
@@ -545,8 +560,14 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
if (name == NULL)
name = (char *)(lr + 1);
+#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
+
break;
case TX_MKXATTR:
error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &zp, kcred);
@@ -554,8 +575,13 @@ zfs_replay_create(void *arg1, void *arg2, boolean_t byteswap)
case TX_SYMLINK:
name = (char *)(lr + 1);
link = name + strlen(name) + 1;
+#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;
default:
error = SET_ERROR(ENOTSUP);
@@ -670,8 +696,13 @@ do_zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, char *sname,
if (lr->lr_common.lrc_txtype & TX_CI)
vflg |= FIGNORECASE;
+#if defined(__linux__)
+ error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, rflags,
+ wo_vap, kcred->user_ns);
+#else
error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, rflags,
wo_vap, NULL);
+#endif
zrele(tdzp);
zrele(sdzp);
@@ -944,7 +975,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 593249e12..45ecb0773 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -168,9 +168,19 @@ zfs_access(znode_t *zp, int mode, int flag, cred_t *cr)
return (error);
if (flag & V_ACE_MASK)
- error = zfs_zaccess(zp, mode, flag, B_FALSE, cr, NULL);
+#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
+#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, FTAG);
return (error);
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 13f7efd96..52e824a88 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -200,5 +200,5 @@ tags = ['functional', 'zvol', 'zvol_misc']
[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 1cebf5082..66b041b6e 100755
--- a/tests/test-runner/bin/zts-report.py.in
+++ b/tests/test-runner/bin/zts-report.py.in
@@ -284,6 +284,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.c b/tests/zfs-tests/cmd/idmap_util.c
index b6ca111ad..49483cbaa 100644
--- a/tests/zfs-tests/cmd/idmap_util.c
+++ b/tests/zfs-tests/cmd/idmap_util.c
@@ -36,6 +36,7 @@
#include <errno.h>
#include <sched.h>
#include <syscall.h>
+#include <sys/socket.h>
#include <sys/list.h>
@@ -458,43 +459,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) {
@@ -504,11 +518,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);
}
@@ -532,7 +550,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/Makefile.am b/tests/zfs-tests/tests/Makefile.am
index 2d9902775..5b8458b73 100644
--- a/tests/zfs-tests/tests/Makefile.am
+++ b/tests/zfs-tests/tests/Makefile.am
@@ -2006,4 +2006,5 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/idmap_mount/idmap_mount_001.ksh \
functional/idmap_mount/idmap_mount_002.ksh \
functional/idmap_mount/idmap_mount_003.ksh \
- functional/idmap_mount/idmap_mount_004.ksh
+ functional/idmap_mount/idmap_mount_004.ksh \
+ functional/idmap_mount/idmap_mount_005.ksh
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 000000000..a4ecea92c
--- /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."
+