mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-24 18:39:34 +03:00
Support idmapped mount
Adds support for idmapped mounts. Supported as of Linux 5.12 this functionality allows user and group IDs to be remapped without changing their state on disk. This can be useful for portable home directories and a variety of container related use cases. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Ryan Moeller <ryan@iXsystems.com> Signed-off-by: Youzhong Yang <yyang@mathworks.com> Closes #12923 Closes #13671
This commit is contained in:
parent
eaaed26ffb
commit
2a068a1394
25
config/kernel-idmap_mnt_api.m4
Normal file
25
config/kernel-idmap_mnt_api.m4
Normal file
@ -0,0 +1,25 @@
|
||||
dnl #
|
||||
dnl # 5.12 API
|
||||
dnl #
|
||||
dnl # Check if APIs for idmapped mount are available
|
||||
dnl #
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_IDMAP_MNT_API], [
|
||||
ZFS_LINUX_TEST_SRC([idmap_mnt_api], [
|
||||
#include <linux/fs.h>
|
||||
],[
|
||||
int fs_flags = 0;
|
||||
fs_flags |= FS_ALLOW_IDMAP;
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_IDMAP_MNT_API], [
|
||||
AC_MSG_CHECKING([whether APIs for idmapped mount are present])
|
||||
ZFS_LINUX_TEST_RESULT([idmap_mnt_api], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_IDMAP_MNT_API, 1,
|
||||
[APIs for idmapped mount are present])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
||||
|
@ -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 #
|
||||
|
@ -105,5 +105,7 @@ typedef u_longlong_t len_t;
|
||||
|
||||
typedef longlong_t diskaddr_t;
|
||||
|
||||
typedef void zuserns_t;
|
||||
|
||||
#include <sys/debug.h>
|
||||
#endif /* !_OPENSOLARIS_SYS_TYPES_H_ */
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 **);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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 @@ out:
|
||||
|
||||
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 @@ fail:
|
||||
* 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 @@ fail:
|
||||
*/
|
||||
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__);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 @@ slow:
|
||||
DTRACE_PROBE(zfs__fastpath__execute__access__miss);
|
||||
if ((error = zfs_enter(ZTOZSB(zdp), FTAG)) != 0)
|
||||
return (error);
|
||||
error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr);
|
||||
error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr, NULL);
|
||||
zfs_exit(ZTOZSB(zdp), FTAG);
|
||||
return (error);
|
||||
}
|
||||
@ -2611,7 +2622,8 @@ slow:
|
||||
* 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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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 @@ top:
|
||||
* 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 @@ top:
|
||||
}
|
||||
|
||||
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 @@ top:
|
||||
/*
|
||||
* 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 @@ out:
|
||||
|
||||
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 @@ top:
|
||||
* 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 @@ top:
|
||||
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 @@ out:
|
||||
* 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 @@ out:
|
||||
*/
|
||||
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 @@ top:
|
||||
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 @@ top:
|
||||
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 @@ next:
|
||||
* 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 @@ next:
|
||||
* 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 @@ top:
|
||||
*/
|
||||
|
||||
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 @@ top:
|
||||
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 @@ top:
|
||||
* 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 @@ top:
|
||||
((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 @@ top:
|
||||
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 @@ top:
|
||||
* 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 @@ out:
|
||||
* 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 @@ out:
|
||||
*/
|
||||
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 @@ top:
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include <sys/zpl.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
|
||||
static struct dentry *
|
||||
zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
@ -112,18 +111,22 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
}
|
||||
|
||||
void
|
||||
zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr)
|
||||
zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr,
|
||||
zuserns_t *mnt_ns)
|
||||
{
|
||||
vap->va_mask = ATTR_MODE;
|
||||
vap->va_mode = mode;
|
||||
vap->va_uid = crgetuid(cr);
|
||||
|
||||
vap->va_uid = zfs_uid_from_mnt((struct user_namespace *)mnt_ns,
|
||||
crgetuid(cr));
|
||||
|
||||
if (dir && dir->i_mode & S_ISGID) {
|
||||
vap->va_gid = KGID_TO_SGID(dir->i_gid);
|
||||
if (S_ISDIR(mode))
|
||||
vap->va_mode |= S_ISGID;
|
||||
} else {
|
||||
vap->va_gid = crgetgid(cr);
|
||||
vap->va_gid = zfs_gid_from_mnt((struct user_namespace *)mnt_ns,
|
||||
crgetgid(cr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,14 +143,17 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag)
|
||||
vattr_t *vap;
|
||||
int error;
|
||||
fstrans_cookie_t cookie;
|
||||
#ifndef HAVE_IOPS_CREATE_USERNS
|
||||
zuserns_t *user_ns = NULL;
|
||||
#endif
|
||||
|
||||
crhold(cr);
|
||||
vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
|
||||
zpl_vap_init(vap, dir, mode, cr);
|
||||
zpl_vap_init(vap, dir, mode, cr, user_ns);
|
||||
|
||||
cookie = spl_fstrans_mark();
|
||||
error = -zfs_create(ITOZ(dir), dname(dentry), vap, 0,
|
||||
mode, &zp, cr, 0, NULL);
|
||||
mode, &zp, cr, 0, NULL, user_ns);
|
||||
if (error == 0) {
|
||||
error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
|
||||
if (error == 0)
|
||||
@ -184,6 +190,9 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
vattr_t *vap;
|
||||
int error;
|
||||
fstrans_cookie_t cookie;
|
||||
#ifndef HAVE_IOPS_MKNOD_USERNS
|
||||
zuserns_t *user_ns = NULL;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We currently expect Linux to supply rdev=0 for all sockets
|
||||
@ -194,12 +203,12 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
|
||||
crhold(cr);
|
||||
vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
|
||||
zpl_vap_init(vap, dir, mode, cr);
|
||||
zpl_vap_init(vap, dir, mode, cr, user_ns);
|
||||
vap->va_rdev = rdev;
|
||||
|
||||
cookie = spl_fstrans_mark();
|
||||
error = -zfs_create(ITOZ(dir), dname(dentry), vap, 0,
|
||||
mode, &zp, cr, 0, NULL);
|
||||
mode, &zp, cr, 0, NULL, user_ns);
|
||||
if (error == 0) {
|
||||
error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name);
|
||||
if (error == 0)
|
||||
@ -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) {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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']
|
||||
|
@ -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],
|
||||
})
|
||||
|
||||
|
||||
|
1
tests/zfs-tests/cmd/.gitignore
vendored
1
tests/zfs-tests/cmd/.gitignore
vendored
@ -47,3 +47,4 @@
|
||||
/edonr_test
|
||||
/skein_test
|
||||
/sha2_test
|
||||
/idmap_util
|
||||
|
@ -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
|
||||
|
791
tests/zfs-tests/cmd/idmap_util.c
Normal file
791
tests/zfs-tests/cmd/idmap_util.c
Normal file
@ -0,0 +1,791 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <linux/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#include <sys/list.h>
|
||||
|
||||
#ifndef UINT_MAX
|
||||
#define UINT_MAX 4294967295U
|
||||
#endif
|
||||
|
||||
#ifndef __NR_Linux
|
||||
#if defined __alpha__
|
||||
#define __NR_Linux 110
|
||||
#elif defined _MIPS_SIM
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
#define __NR_Linux 4000
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
#define __NR_Linux 6000
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
#define __NR_Linux 5000
|
||||
#endif
|
||||
#elif defined __ia64__
|
||||
#define __NR_Linux 1024
|
||||
#else
|
||||
#define __NR_Linux 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __NR_mount_setattr
|
||||
#define __NR_mount_setattr (442 + __NR_Linux)
|
||||
#endif
|
||||
|
||||
#ifndef __NR_open_tree
|
||||
#define __NR_open_tree (428 + __NR_Linux)
|
||||
#endif
|
||||
|
||||
#ifndef __NR_move_mount
|
||||
#define __NR_move_mount (429 + __NR_Linux)
|
||||
#endif
|
||||
|
||||
#ifndef MNT_DETACH
|
||||
#define MNT_DETACH 2
|
||||
#endif
|
||||
|
||||
#ifndef MOVE_MOUNT_F_EMPTY_PATH
|
||||
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_IDMAP
|
||||
#define MOUNT_ATTR_IDMAP 0x00100000
|
||||
#endif
|
||||
|
||||
#ifndef OPEN_TREE_CLONE
|
||||
#define OPEN_TREE_CLONE 1
|
||||
#endif
|
||||
|
||||
#ifndef OPEN_TREE_CLOEXEC
|
||||
#define OPEN_TREE_CLOEXEC O_CLOEXEC
|
||||
#endif
|
||||
|
||||
#ifndef AT_RECURSIVE
|
||||
#define AT_RECURSIVE 0x8000
|
||||
#endif
|
||||
|
||||
#ifndef mount_attr
|
||||
struct mount_attr {
|
||||
__u64 attr_set;
|
||||
__u64 attr_clr;
|
||||
__u64 propagation;
|
||||
__u64 userns_fd;
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
sys_mount_setattr(int dfd, const char *path, unsigned int flags,
|
||||
struct mount_attr *attr, size_t size)
|
||||
{
|
||||
return (syscall(__NR_mount_setattr, dfd, path, flags, attr, size));
|
||||
}
|
||||
|
||||
static inline int
|
||||
sys_open_tree(int dfd, const char *filename, unsigned int flags)
|
||||
{
|
||||
return (syscall(__NR_open_tree, dfd, filename, flags));
|
||||
}
|
||||
|
||||
static inline int sys_move_mount(int from_dfd, const char *from_pathname,
|
||||
int to_dfd, const char *to_pathname, unsigned int flags)
|
||||
{
|
||||
return (syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
|
||||
to_pathname, flags));
|
||||
}
|
||||
|
||||
typedef enum idmap_type_t {
|
||||
TYPE_UID,
|
||||
TYPE_GID,
|
||||
TYPE_BOTH
|
||||
} idmap_type_t;
|
||||
|
||||
struct idmap_entry {
|
||||
__u32 first;
|
||||
__u32 lower_first;
|
||||
__u32 count;
|
||||
idmap_type_t type;
|
||||
list_node_t node;
|
||||
};
|
||||
|
||||
static void
|
||||
log_msg(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
vfprintf(stderr, msg, ap);
|
||||
fputc('\n', stderr);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#define log_errno(msg, args...) \
|
||||
do { \
|
||||
log_msg("%s:%d:%s: [%m] " msg, __FILE__, __LINE__,\
|
||||
__FUNCTION__, ##args); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Parse the idmapping in the following format
|
||||
* and add to the list:
|
||||
*
|
||||
* u:nsid_first:hostid_first:count
|
||||
* g:nsid_first:hostid_first:count
|
||||
* b:nsid_first:hostid_first:count
|
||||
*
|
||||
* The delimiter can be : or space character.
|
||||
*
|
||||
* Return:
|
||||
* 0 if success
|
||||
* ENOMEM if out of memory
|
||||
* EINVAL if wrong arg or input
|
||||
*/
|
||||
static int
|
||||
parse_idmap_entry(list_t *head, char *input)
|
||||
{
|
||||
char *token, *savedptr = NULL;
|
||||
struct idmap_entry *entry;
|
||||
unsigned long ul;
|
||||
char *delimiter = (char *)": ";
|
||||
char c;
|
||||
|
||||
if (!input || !head)
|
||||
return (EINVAL);
|
||||
entry = malloc(sizeof (*entry));
|
||||
if (!entry)
|
||||
return (ENOMEM);
|
||||
|
||||
token = strtok_r(input, delimiter, &savedptr);
|
||||
if (token)
|
||||
c = token[0];
|
||||
if (!token || (c != 'b' && c != 'u' && c != 'g'))
|
||||
goto errout;
|
||||
entry->type = (c == 'b') ? TYPE_BOTH :
|
||||
((c == 'u') ? TYPE_UID : TYPE_GID);
|
||||
|
||||
token = strtok_r(NULL, delimiter, &savedptr);
|
||||
if (!token)
|
||||
goto errout;
|
||||
ul = strtoul(token, NULL, 10);
|
||||
if (ul > UINT_MAX || errno != 0)
|
||||
goto errout;
|
||||
entry->first = (__u32)ul;
|
||||
|
||||
token = strtok_r(NULL, delimiter, &savedptr);
|
||||
if (!token)
|
||||
goto errout;
|
||||
ul = strtoul(token, NULL, 10);
|
||||
if (ul > UINT_MAX || errno != 0)
|
||||
goto errout;
|
||||
entry->lower_first = (__u32)ul;
|
||||
|
||||
token = strtok_r(NULL, delimiter, &savedptr);
|
||||
if (!token)
|
||||
goto errout;
|
||||
ul = strtoul(token, NULL, 10);
|
||||
if (ul > UINT_MAX || errno != 0)
|
||||
goto errout;
|
||||
entry->count = (__u32)ul;
|
||||
|
||||
list_insert_tail(head, entry);
|
||||
|
||||
return (0);
|
||||
|
||||
errout:
|
||||
free(entry);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release all the entries in the list
|
||||
*/
|
||||
static void
|
||||
free_idmap(list_t *head)
|
||||
{
|
||||
struct idmap_entry *entry;
|
||||
|
||||
while ((entry = list_remove_head(head)) != NULL)
|
||||
free(entry);
|
||||
/* list_destroy() to be done by the caller */
|
||||
}
|
||||
|
||||
/*
|
||||
* Write all bytes in the buffer to fd
|
||||
*/
|
||||
static ssize_t
|
||||
write_buf(int fd, const char *buf, size_t buf_size)
|
||||
{
|
||||
ssize_t written, total_written = 0;
|
||||
size_t remaining = buf_size;
|
||||
char *position = (char *)buf;
|
||||
|
||||
for (;;) {
|
||||
written = write(fd, position, remaining);
|
||||
if (written < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (written < 0) {
|
||||
log_errno("write");
|
||||
return (written);
|
||||
}
|
||||
total_written += written;
|
||||
if (total_written == buf_size)
|
||||
break;
|
||||
remaining -= written;
|
||||
position += written;
|
||||
}
|
||||
|
||||
return (total_written);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from file into buffer
|
||||
*/
|
||||
static ssize_t
|
||||
read_buf(int fd, char *buf, size_t buf_size)
|
||||
{
|
||||
int ret;
|
||||
for (;;) {
|
||||
ret = read(fd, buf, buf_size);
|
||||
if (ret < 0 && errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ret < 0)
|
||||
log_errno("read");
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write idmap of the given type in the buffer to the
|
||||
* process' uid_map or gid_map proc file.
|
||||
*
|
||||
* Return:
|
||||
* 0 if success
|
||||
* errno if there's any error
|
||||
*/
|
||||
static int
|
||||
write_idmap(pid_t pid, char *buf, size_t buf_size, idmap_type_t type)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int fd = -EBADF;
|
||||
int ret;
|
||||
|
||||
(void) snprintf(path, sizeof (path), "/proc/%d/%cid_map",
|
||||
pid, type == TYPE_UID ? 'u' : 'g');
|
||||
fd = open(path, O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = errno;
|
||||
log_errno("open(%s)", path);
|
||||
goto out;
|
||||
}
|
||||
ret = write_buf(fd, buf, buf_size);
|
||||
if (ret < 0)
|
||||
ret = errno;
|
||||
else
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd > 0)
|
||||
close(fd);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write idmap info in the list to the process
|
||||
* user namespace, i.e. its /proc/<pid>/uid_map
|
||||
* and /proc/<pid>/gid_map file.
|
||||
*
|
||||
* Return:
|
||||
* 0 if success
|
||||
* errno if it fails
|
||||
*/
|
||||
static int
|
||||
write_pid_idmaps(pid_t pid, list_t *head)
|
||||
{
|
||||
char *buf_uids, *buf_gids;
|
||||
char *curr_bufu, *curr_bufg;
|
||||
/* max 4k to be allowed for each map */
|
||||
int size_buf_uids = 4096, size_buf_gids = 4096;
|
||||
struct idmap_entry *entry;
|
||||
int uid_filled, gid_filled;
|
||||
int ret;
|
||||
int has_uids = 0, has_gids = 0;
|
||||
size_t buf_size;
|
||||
|
||||
buf_uids = malloc(size_buf_uids);
|
||||
if (!buf_uids)
|
||||
return (ENOMEM);
|
||||
buf_gids = malloc(size_buf_gids);
|
||||
if (!buf_gids) {
|
||||
free(buf_uids);
|
||||
return (ENOMEM);
|
||||
}
|
||||
curr_bufu = buf_uids;
|
||||
curr_bufg = buf_gids;
|
||||
|
||||
for (entry = list_head(head); entry; entry = list_next(head, entry)) {
|
||||
if (entry->type == TYPE_UID || entry->type == TYPE_BOTH) {
|
||||
uid_filled = snprintf(curr_bufu, size_buf_uids,
|
||||
"%u %u %u\n", entry->first, entry->lower_first,
|
||||
entry->count);
|
||||
if (uid_filled <= 0 || uid_filled >= size_buf_uids) {
|
||||
ret = E2BIG;
|
||||
goto out;
|
||||
}
|
||||
curr_bufu += uid_filled;
|
||||
size_buf_uids -= uid_filled;
|
||||
has_uids = 1;
|
||||
}
|
||||
if (entry->type == TYPE_GID || entry->type == TYPE_BOTH) {
|
||||
gid_filled = snprintf(curr_bufg, size_buf_gids,
|
||||
"%u %u %u\n", entry->first, entry->lower_first,
|
||||
entry->count);
|
||||
if (gid_filled <= 0 || gid_filled >= size_buf_gids) {
|
||||
ret = E2BIG;
|
||||
goto out;
|
||||
}
|
||||
curr_bufg += gid_filled;
|
||||
size_buf_gids -= gid_filled;
|
||||
has_gids = 1;
|
||||
}
|
||||
}
|
||||
if (has_uids) {
|
||||
buf_size = curr_bufu - buf_uids;
|
||||
ret = write_idmap(pid, buf_uids, buf_size, TYPE_UID);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
if (has_gids) {
|
||||
buf_size = curr_bufg - buf_gids;
|
||||
ret = write_idmap(pid, buf_gids, buf_size, TYPE_GID);
|
||||
}
|
||||
|
||||
out:
|
||||
free(buf_uids);
|
||||
free(buf_gids);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the child process to exit
|
||||
* and reap it.
|
||||
*
|
||||
* Return:
|
||||
* process exit code if available
|
||||
*/
|
||||
static int
|
||||
wait_for_pid(pid_t pid)
|
||||
{
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
ret = waitpid(pid, &status, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!WIFEXITED(status))
|
||||
return (EXIT_FAILURE);
|
||||
return (WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the file descriptor of the process user namespace
|
||||
* given its pid.
|
||||
*
|
||||
* Return:
|
||||
* fd if success
|
||||
* -1 if it fails
|
||||
*/
|
||||
static int
|
||||
userns_fd_from_pid(pid_t pid)
|
||||
{
|
||||
int fd;
|
||||
char path[PATH_MAX];
|
||||
|
||||
(void) snprintf(path, sizeof (path), "/proc/%d/ns/user", pid);
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
log_errno("open(%s)", path);
|
||||
return (fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the user namespace file descriptor given a list
|
||||
* of idmap info.
|
||||
*
|
||||
* Return:
|
||||
* fd if success
|
||||
* -errno if it fails
|
||||
*/
|
||||
static int
|
||||
userns_fd_from_idmap(list_t *head)
|
||||
{
|
||||
pid_t pid;
|
||||
int ret, fd;
|
||||
int pipe_fd[2];
|
||||
char c;
|
||||
int saved_errno = 0;
|
||||
|
||||
/* pipe for bidirectional communication */
|
||||
ret = pipe(pipe_fd);
|
||||
if (ret) {
|
||||
log_errno("pipe");
|
||||
return (-errno);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
log_errno("fork");
|
||||
return (-errno);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* child process */
|
||||
close(pipe_fd[0]);
|
||||
ret = unshare(CLONE_NEWUSER);
|
||||
if (ret == 0) {
|
||||
/* notify the parent of success */
|
||||
ret = write_buf(pipe_fd[1], "1", 1);
|
||||
} else {
|
||||
saved_errno = errno;
|
||||
log_errno("unshare");
|
||||
ret = write_buf(pipe_fd[1], "0", 1);
|
||||
}
|
||||
if (ret < 0)
|
||||
saved_errno = errno;
|
||||
close(pipe_fd[1]);
|
||||
exit(saved_errno);
|
||||
}
|
||||
/* parent process */
|
||||
close(pipe_fd[1]);
|
||||
ret = read_buf(pipe_fd[0], &c, 1);
|
||||
if (ret == 1 && c == '1') {
|
||||
ret = write_pid_idmaps(pid, head);
|
||||
if (!ret) {
|
||||
fd = userns_fd_from_pid(pid);
|
||||
if (fd < 0)
|
||||
fd = -errno;
|
||||
} else {
|
||||
fd = -ret;
|
||||
}
|
||||
} else {
|
||||
fd = -EBADF;
|
||||
}
|
||||
close(pipe_fd[0]);
|
||||
(void) wait_for_pid(pid);
|
||||
return (fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the operating system supports idmapped mount on the
|
||||
* given path or not.
|
||||
*
|
||||
* Return:
|
||||
* true if supported
|
||||
* false if not supported
|
||||
*/
|
||||
static bool
|
||||
is_idmap_supported(char *path)
|
||||
{
|
||||
list_t head;
|
||||
int ret;
|
||||
int tree_fd = -EBADF, path_fd = -EBADF;
|
||||
struct mount_attr attr = {
|
||||
.attr_set = MOUNT_ATTR_IDMAP,
|
||||
.userns_fd = -EBADF,
|
||||
};
|
||||
|
||||
/* strtok_r() won't be happy with a const string */
|
||||
char *input = strdup("b:0:1000000:1000000");
|
||||
|
||||
if (!input) {
|
||||
errno = ENOMEM;
|
||||
log_errno("strdup");
|
||||
return (false);
|
||||
}
|
||||
|
||||
list_create(&head, sizeof (struct idmap_entry),
|
||||
offsetof(struct idmap_entry, node));
|
||||
ret = parse_idmap_entry(&head, input);
|
||||
if (ret) {
|
||||
errno = ret;
|
||||
log_errno("parse_idmap_entry(%s)", input);
|
||||
goto out;
|
||||
}
|
||||
ret = userns_fd_from_idmap(&head);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
attr.userns_fd = ret;
|
||||
ret = openat(-EBADF, path, O_DIRECTORY | O_CLOEXEC);
|
||||
if (ret < 0) {
|
||||
log_errno("openat(%s)", path);
|
||||
goto out;
|
||||
}
|
||||
path_fd = ret;
|
||||
ret = sys_open_tree(path_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
|
||||
AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
|
||||
if (ret < 0) {
|
||||
log_errno("sys_open_tree");
|
||||
goto out;
|
||||
}
|
||||
tree_fd = ret;
|
||||
ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH, &attr,
|
||||
sizeof (attr));
|
||||
if (ret < 0) {
|
||||
log_errno("sys_mount_setattr");
|
||||
}
|
||||
out:
|
||||
free_idmap(&head);
|
||||
list_destroy(&head);
|
||||
if (tree_fd > 0)
|
||||
close(tree_fd);
|
||||
if (path_fd > 0)
|
||||
close(path_fd);
|
||||
if (attr.userns_fd > 0)
|
||||
close(attr.userns_fd);
|
||||
free(input);
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the given path is a mount point or not.
|
||||
*
|
||||
* Return:
|
||||
* true if it is
|
||||
* false otherwise
|
||||
*/
|
||||
static bool
|
||||
is_mountpoint(char *path)
|
||||
{
|
||||
char *parent;
|
||||
struct stat st_me, st_parent;
|
||||
bool ret;
|
||||
|
||||
parent = malloc(strlen(path)+4);
|
||||
if (!parent) {
|
||||
errno = ENOMEM;
|
||||
log_errno("malloc");
|
||||
return (false);
|
||||
}
|
||||
strcat(strcpy(parent, path), "/..");
|
||||
if (lstat(path, &st_me) != 0 ||
|
||||
lstat(parent, &st_parent) != 0)
|
||||
ret = false;
|
||||
else
|
||||
if (st_me.st_dev != st_parent.st_dev ||
|
||||
st_me.st_ino == st_parent.st_ino)
|
||||
ret = true;
|
||||
else
|
||||
ret = false;
|
||||
free(parent);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remount the source on the new target folder with the given
|
||||
* list of idmap info. If target is NULL, the source will be
|
||||
* unmounted and then remounted if it is a mountpoint, otherwise
|
||||
* no unmount is done, the source is simply idmap remounted.
|
||||
*
|
||||
* Return:
|
||||
* 0 if success
|
||||
* -errno otherwise
|
||||
*/
|
||||
static int
|
||||
do_idmap_mount(list_t *idmap, char *source, char *target, int flags)
|
||||
{
|
||||
int ret;
|
||||
int tree_fd = -EBADF, source_fd = -EBADF;
|
||||
struct mount_attr attr = {
|
||||
.attr_set = MOUNT_ATTR_IDMAP,
|
||||
.userns_fd = -EBADF,
|
||||
};
|
||||
|
||||
ret = userns_fd_from_idmap(idmap);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
attr.userns_fd = ret;
|
||||
ret = openat(-EBADF, source, O_DIRECTORY | O_CLOEXEC);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
log_errno("openat(%s)", source);
|
||||
goto out;
|
||||
}
|
||||
source_fd = ret;
|
||||
ret = sys_open_tree(source_fd, "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT |
|
||||
AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE | flags);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
log_errno("sys_open_tree");
|
||||
goto out;
|
||||
}
|
||||
tree_fd = ret;
|
||||
ret = sys_mount_setattr(tree_fd, "", AT_EMPTY_PATH | flags, &attr,
|
||||
sizeof (attr));
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
log_errno("sys_mount_setattr");
|
||||
goto out;
|
||||
}
|
||||
if (source != NULL && target == NULL && is_mountpoint(source)) {
|
||||
ret = umount2(source, MNT_DETACH);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
log_errno("umount2(%s)", source);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = sys_move_mount(tree_fd, "", -EBADF, target == NULL ?
|
||||
source : target, MOVE_MOUNT_F_EMPTY_PATH);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
log_errno("sys_move_mount(%s)", target == NULL ?
|
||||
source : target);
|
||||
}
|
||||
out:
|
||||
if (tree_fd > 0)
|
||||
close(tree_fd);
|
||||
if (source_fd > 0)
|
||||
close(source_fd);
|
||||
if (attr.userns_fd > 0)
|
||||
close(attr.userns_fd);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(char *argv[])
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-r] [-c] [-m <idmap1>] [-m <idmap2>]" \
|
||||
" ... [<source>] [<target>]\n", argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " -r Recursively do idmapped mount.\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " -c Checks if idmapped mount is supported " \
|
||||
"on the <source> by the operating system or not.\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " -m <idmap> to specify the idmap info, " \
|
||||
"in the following format:\n");
|
||||
fprintf(stderr, " <id_type>:<nsid_first>:<hostid_first>:<count>\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " <id_type> can be either of 'b', 'u', and 'g'.\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "The <source> folder will be mounted at <target> " \
|
||||
"with the provided idmap information.\nIf no <target> is " \
|
||||
"specified, and <source> is a mount point, " \
|
||||
"then <source> will be unmounted and then remounted.\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
list_t idmap_head;
|
||||
int check_supported = 0;
|
||||
int ret = EXIT_SUCCESS;
|
||||
char *source = NULL, *target = NULL;
|
||||
int flags = 0;
|
||||
|
||||
list_create(&idmap_head, sizeof (struct idmap_entry),
|
||||
offsetof(struct idmap_entry, node));
|
||||
|
||||
while ((opt = getopt(argc, argv, "rcm:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'r':
|
||||
flags |= AT_RECURSIVE;
|
||||
break;
|
||||
case 'c':
|
||||
check_supported = 1;
|
||||
break;
|
||||
case 'm':
|
||||
ret = parse_idmap_entry(&idmap_head, optarg);
|
||||
if (ret) {
|
||||
errno = ret;
|
||||
log_errno("parse_idmap_entry(%s)", optarg);
|
||||
ret = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_usage(argv);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (check_supported == 0 && list_is_empty(&idmap_head)) {
|
||||
print_usage(argv);
|
||||
ret = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "Expected to have <source>, <target>.\n");
|
||||
print_usage(argv);
|
||||
ret = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
source = argv[optind];
|
||||
if (optind < (argc - 1)) {
|
||||
target = argv[optind + 1];
|
||||
}
|
||||
|
||||
if (check_supported) {
|
||||
free_idmap(&idmap_head);
|
||||
list_destroy(&idmap_head);
|
||||
if (is_idmap_supported(source)) {
|
||||
printf("idmapped mount is supported on [%s].\n",
|
||||
source);
|
||||
return (EXIT_SUCCESS);
|
||||
} else {
|
||||
printf("idmapped mount is NOT supported.\n");
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
ret = do_idmap_mount(&idmap_head, source, target, flags);
|
||||
if (ret)
|
||||
ret = EXIT_FAILURE;
|
||||
out:
|
||||
free_idmap(&idmap_head);
|
||||
list_destroy(&idmap_head);
|
||||
|
||||
exit(ret);
|
||||
}
|
@ -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'
|
||||
|
@ -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
|
||||
|
25
tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh
Executable file
25
tests/zfs-tests/tests/functional/idmap_mount/cleanup.ksh
Executable file
@ -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
|
25
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg
Normal file
25
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount.cfg
Normal file
@ -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
|
76
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh
Executable file
76
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_001.ksh
Executable file
@ -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."
|
||||
|
97
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh
Executable file
97
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_002.ksh
Executable file
@ -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."
|
||||
|
121
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh
Executable file
121
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_003.ksh
Executable file
@ -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."
|
||||
|
106
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh
Executable file
106
tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_004.ksh
Executable file
@ -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."
|
||||
|
@ -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
|
30
tests/zfs-tests/tests/functional/idmap_mount/setup.ksh
Executable file
30
tests/zfs-tests/tests/functional/idmap_mount/setup.ksh
Executable file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user