diff --git a/config/kernel-idmap_mnt_api.m4 b/config/kernel-idmap_mnt_api.m4 new file mode 100644 index 000000000000..47ddc5702fb7 --- /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 + ],[ + 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 f12b0da48d4a..d4d13ddd1d1a 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -147,6 +147,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_USER_NS_COMMON_INUM + ZFS_AC_KERNEL_SRC_IDMAP_MNT_API AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -267,6 +268,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_ZERO_PAGE ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC ZFS_AC_KERNEL_USER_NS_COMMON_INUM + 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 b1308df29503..558843dcaa74 100644 --- a/include/os/freebsd/spl/sys/types.h +++ b/include/os/freebsd/spl/sys/types.h @@ -105,5 +105,7 @@ typedef u_longlong_t len_t; typedef longlong_t diskaddr_t; +typedef void zuserns_t; + #include #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 bf5e03b24c06..460aecd2e708 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 b7d3f38d70bb..dc3c260dbbab 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 b44c94518750..cae1bbddf105 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 3bd7ce36b85d..ee7fda761a3b 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 22ca625023b0..787d258e1388 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 95f08f5416d0..30d73db6b9e8 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 c4d2dddd7b1f..82fb98c9fb89 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 a85d4c24178b..c075e180a860 100644 --- a/module/os/freebsd/zfs/zfs_acl.c +++ b/module/os/freebsd/zfs/zfs_acl.c @@ -1619,7 +1619,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; @@ -1789,7 +1789,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); @@ -1952,7 +1952,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, @@ -2341,7 +2341,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; @@ -2471,9 +2472,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)); } /* @@ -2484,7 +2487,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 @@ -2540,7 +2543,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; @@ -2627,7 +2630,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; @@ -2647,7 +2650,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); } @@ -2657,19 +2661,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 778e4151656d..07232086d52b 100644 --- a/module/os/freebsd/zfs/zfs_dir.c +++ b/module/os/freebsd/zfs/zfs_dir.c @@ -809,7 +809,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); @@ -955,7 +955,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 fae390a148d6..362e02751ee4 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -837,7 +837,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)); } @@ -856,7 +856,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, FTAG); return (error); } @@ -1036,6 +1037,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. * @@ -1047,7 +1049,7 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, */ 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) { (void) excl, (void) mode, (void) flag; znode_t *zp; @@ -1110,7 +1112,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; } @@ -1126,7 +1128,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)) @@ -1231,7 +1233,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; } @@ -1387,6 +1389,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. * @@ -1398,7 +1401,7 @@ zfs_remove(znode_t *dzp, const char *name, cred_t *cr, int flags) */ 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) { (void) flags, (void) vsecp; znode_t *zp; @@ -1447,7 +1450,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, FTAG); return (error); } @@ -1468,7 +1471,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, FTAG); return (error); @@ -1585,7 +1589,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; } @@ -1976,7 +1980,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, FTAG); return (error); } @@ -2142,7 +2146,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. * @@ -2150,7 +2154,7 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) * vp - ctime updated, mtime updated if size changed. */ 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; @@ -2322,7 +2326,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)) { @@ -2359,7 +2363,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 */ @@ -2468,7 +2472,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) { @@ -3264,7 +3269,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) { @@ -3415,7 +3420,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; @@ -3460,6 +3465,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. * @@ -3468,7 +3474,7 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname, */ 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) { (void) flags; znode_t *zp; @@ -3499,7 +3505,7 @@ zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap, } if ((error = zfs_acl_ids_create(dzp, 0, - vap, cr, NULL, &acl_ids)) != 0) { + vap, cr, NULL, &acl_ids, NULL)) != 0) { zfs_exit(zfsvfs, FTAG); return (error); } @@ -3514,7 +3520,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, FTAG); return (error); @@ -3730,7 +3736,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, FTAG); return (error); } @@ -3831,7 +3837,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, FTAG); return (error); } @@ -4607,7 +4613,7 @@ zfs_freebsd_create(struct vop_create_args *ap) *ap->a_vpp = NULL; rc = zfs_create(VTOZ(ap->a_dvp), cnp->cn_nameptr, vap, 0, mode, - &zp, cnp->cn_cred, 0 /* flag */, NULL /* vsecattr */); + &zp, cnp->cn_cred, 0 /* flag */, NULL /* vsecattr */, NULL); if (rc == 0) *ap->a_vpp = ZTOV(zp); if (zfsvfs->z_use_namecache && @@ -4661,7 +4667,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); @@ -4914,7 +4920,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_ @@ -4985,7 +4991,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 192aa748fc13..6c269480cb4b 100644 --- a/module/os/freebsd/zfs/zfs_znode.c +++ b/module/os/freebsd/zfs/zfs_znode.c @@ -298,7 +298,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); @@ -1773,7 +1773,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 a69618978622..50eb7cfaa61c 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 d5cd5de890ab..d04034490758 100644 --- a/module/os/linux/zfs/zfs_acl.c +++ b/module/os/linux/zfs/zfs_acl.c @@ -1802,7 +1802,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); @@ -1889,8 +1889,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) { @@ -1978,7 +1979,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); @@ -2137,7 +2138,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, @@ -2283,7 +2284,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; @@ -2299,7 +2300,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); @@ -2410,7 +2417,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), @@ -2440,7 +2447,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; @@ -2454,7 +2462,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 @@ -2469,7 +2480,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; @@ -2519,20 +2530,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 @@ -2599,7 +2610,7 @@ zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr) 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); + error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL); zfs_exit(ZTOZSB(zdp), FTAG); return (error); } @@ -2611,7 +2622,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; @@ -2650,8 +2662,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 @@ -2676,7 +2689,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, @@ -2690,7 +2703,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) { @@ -2757,9 +2771,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)); } /* @@ -2770,7 +2786,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() */ @@ -2847,7 +2863,7 @@ static const boolean_t zfs_write_implies_delete_child = B_TRUE; * 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; @@ -2874,7 +2890,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) @@ -2896,7 +2912,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) @@ -2978,7 +2994,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; @@ -3000,21 +3016,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 6738d237b923..611a2471dd94 100644 --- a/module/os/linux/zfs/zfs_dir.c +++ b/module/os/linux/zfs/zfs_dir.c @@ -1066,11 +1066,12 @@ 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))) + if ((error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr, + NULL))) return (error); 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); @@ -1218,7 +1219,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 1ff88c121a79..9160f3e77390 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))) { + B_TRUE, cr, NULL))) { zrele(*zpp); *zpp = NULL; } @@ -494,7 +494,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, FTAG); return (error); } @@ -526,6 +526,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. * @@ -537,7 +538,8 @@ 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) + 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); @@ -624,7 +626,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, 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; @@ -643,7 +646,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; @@ -738,7 +741,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; } @@ -782,7 +786,8 @@ 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) + int mode, struct inode **ipp, cred_t *cr, int flag, vsecattr_t *vsecp, + zuserns_t *mnt_ns) { (void) excl, (void) mode, (void) flag; znode_t *zp = NULL, *dzp = ITOZ(dip); @@ -829,14 +834,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; @@ -967,7 +972,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; } @@ -1147,6 +1152,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. * @@ -1159,7 +1165,7 @@ zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags) */ 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); @@ -1216,7 +1222,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, FTAG); return (error); } @@ -1237,7 +1243,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, FTAG); @@ -1379,7 +1386,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; } @@ -1811,6 +1818,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 @@ -1819,7 +1827,7 @@ zfs_setattr_dir(znode_t *dzp) * ip - ctime updated, mtime updated if size changed. */ 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); @@ -1968,7 +1976,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; @@ -1993,13 +2002,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, @@ -2013,9 +2024,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 @@ -2031,7 +2046,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 */ @@ -2144,12 +2159,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; @@ -2640,6 +2655,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. * @@ -2648,7 +2664,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) */ 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); @@ -2841,7 +2857,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)) { @@ -3008,6 +3024,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. * @@ -3018,7 +3035,7 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm, */ 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; @@ -3056,7 +3073,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, FTAG); return (error); } @@ -3073,7 +3090,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, FTAG); @@ -3325,7 +3342,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, FTAG); return (error); } @@ -3951,7 +3968,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, FTAG); return (error); } diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c index a97955f4020a..9aeffba86150 100644 --- a/module/os/linux/zfs/zfs_znode.c +++ b/module/os/linux/zfs/zfs_znode.c @@ -1960,7 +1960,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 837629e4a5e0..8bc4a9b39f22 100644 --- a/module/os/linux/zfs/zpl_ctldir.c +++ b/module/os/linux/zfs/zpl_ctldir.c @@ -371,7 +371,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 25fc6b223297..cfa03e571f78 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); + err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL); 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); + err = -zfs_setattr(ITOZ(ip), (vattr_t *)&xva, 0, cr, NULL); 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); + 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 7578753ed8ce..8d073ff8cbd3 100644 --- a/module/os/linux/zfs/zpl_inode.c +++ b/module/os/linux/zfs/zpl_inode.c @@ -33,7 +33,6 @@ #include #include - 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) @@ -236,6 +245,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); @@ -245,10 +257,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); @@ -311,13 +323,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) @@ -439,7 +455,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); @@ -458,7 +478,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); @@ -483,6 +507,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) @@ -491,7 +518,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); @@ -521,14 +548,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 e3945a2a05fe..63ba731dd804 100644 --- a/module/os/linux/zfs/zpl_super.c +++ b/module/os/linux/zfs/zpl_super.c @@ -374,7 +374,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 a010667adfa8..97b6e048c8a8 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); + cr, 0, NULL, NULL); if (error) goto out; } diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c index 379e1d1a7b57..45c2fa3720cf 100644 --- a/module/zfs/zfs_replay.c +++ b/module/zfs/zfs_replay.c @@ -387,7 +387,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); @@ -417,7 +417,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); @@ -528,7 +528,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); @@ -546,7 +546,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); @@ -555,7 +555,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); @@ -667,7 +667,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); @@ -860,7 +860,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 a2c15bc807a8..c63076f90c18 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -168,9 +168,9 @@ 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); + 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, FTAG); return (error); diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 09dfb5eb1e1d..21e0f882dc40 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -194,3 +194,7 @@ tags = ['functional', 'userquota'] tests = ['zvol_misc_fua'] tags = ['functional', 'zvol', 'zvol_misc'] +[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/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in index bf7cf22b61fc..e7d338fcf8a9 100755 --- a/tests/test-runner/bin/zts-report.py.in +++ b/tests/test-runner/bin/zts-report.py.in @@ -132,6 +132,10 @@ 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+' # # These tests are known to fail, thus we use this list to prevent these @@ -270,6 +274,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/tests/zfs-tests/cmd/.gitignore b/tests/zfs-tests/cmd/.gitignore index 1fd54c1dd510..0ec450e248db 100644 --- a/tests/zfs-tests/cmd/.gitignore +++ b/tests/zfs-tests/cmd/.gitignore @@ -47,3 +47,4 @@ /edonr_test /skein_test /sha2_test +/idmap_util diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am index c19c870cf698..673a18b4c083 100644 --- a/tests/zfs-tests/cmd/Makefile.am +++ b/tests/zfs-tests/cmd/Makefile.am @@ -118,7 +118,9 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/getversion scripts_zfs_tests_bin_PROGRAMS += %D%/user_ns_exec scripts_zfs_tests_bin_PROGRAMS += %D%/xattrtest scripts_zfs_tests_bin_PROGRAMS += %D%/zed_fd_spill-zedlet +scripts_zfs_tests_bin_PROGRAMS += %D%/idmap_util +%C%_idmap_util_LDADD = libspl.la dist_noinst_DATA += %D%/linux_dos_attributes/dos_attributes.h scripts_zfs_tests_bin_PROGRAMS += %D%/read_dos_attributes %D%/write_dos_attributes diff --git a/tests/zfs-tests/cmd/idmap_util.c b/tests/zfs-tests/cmd/idmap_util.c new file mode 100644 index 000000000000..a9731f00dbc0 --- /dev/null +++ b/tests/zfs-tests/cmd/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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//uid_map + * and /proc//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 ] [-m ]" \ + " ... [] []\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 by the operating system or not.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -m to specify the idmap info, " \ + "in the following format:\n"); + fprintf(stderr, " :::\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " can be either of 'b', 'u', and 'g'.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "The folder will be mounted at " \ + "with the provided idmap information.\nIf no is " \ + "specified, and is a mount point, " \ + "then 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 , .\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 c05b918325b7..30514361ad57 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -156,7 +156,8 @@ export SYSTEM_FILES_LINUX='attr useradd userdel usermod - + setpriv + mountpoint flock logger' @@ -226,4 +227,5 @@ export ZFSTEST_FILES='badsend truncate_test ereports zfs_diff-socket - dosmode_readonly_write' + dosmode_readonly_write + idmap_util' diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 6aeb862fbb85..2d99027754c4 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -378,7 +378,9 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/zvol/zvol_common.shlib \ functional/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg \ functional/zvol/zvol_misc/zvol_misc_common.kshlib \ - functional/zvol/zvol_swap/zvol_swap.cfg + functional/zvol/zvol_swap/zvol_swap.cfg \ + functional/idmap_mount/idmap_mount.cfg \ + functional/idmap_mount/idmap_mount_common.kshlib nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/acl/off/cleanup.ksh \ @@ -1998,4 +2000,10 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/zvol/zvol_swap/zvol_swap_003_pos.ksh \ functional/zvol/zvol_swap/zvol_swap_004_pos.ksh \ functional/zvol/zvol_swap/zvol_swap_005_pos.ksh \ - functional/zvol/zvol_swap/zvol_swap_006_pos.ksh + functional/zvol/zvol_swap/zvol_swap_006_pos.ksh \ + functional/idmap_mount/cleanup.ksh \ + functional/idmap_mount/setup.ksh \ + 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 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 000000000000..4895aa23ee4a --- /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 000000000000..51998945d0d1 --- /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 000000000000..e7187935e532 --- /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 000000000000..8cba90ea58b7 --- /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 000000000000..1f1a2aec655e --- /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 000000000000..89f2f750d23c --- /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 000000000000..980845ca209c --- /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 000000000000..90a14f12058f --- /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 +