diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 633ae7becd61..755a68bb7e22 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -970,7 +970,7 @@ struct inode *__ext4_new_inode(struct user_namespace *mnt_userns, i_gid_write(inode, owner[1]); } else if (test_opt(sb, GRPID)) { inode->i_mode = mode; - inode->i_uid = fsuid_into_mnt(mnt_userns); + inode_fsuid_set(inode, mnt_userns); inode->i_gid = dir->i_gid; } else inode_init_owner(mnt_userns, inode, dir, mode); diff --git a/fs/inode.c b/fs/inode.c index ae526fd9c0a4..9e192bea0630 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2147,7 +2147,7 @@ EXPORT_SYMBOL(init_special_inode); void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode, const struct inode *dir, umode_t mode) { - inode->i_uid = fsuid_into_mnt(mnt_userns); + inode_fsuid_set(inode, mnt_userns); if (dir && dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; @@ -2159,7 +2159,7 @@ void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode, !capable_wrt_inode_uidgid(mnt_userns, dir, CAP_FSETID)) mode &= ~S_ISGID; } else - inode->i_gid = fsgid_into_mnt(mnt_userns); + inode_fsgid_set(inode, mnt_userns); inode->i_mode = mode; } EXPORT_SYMBOL(inode_init_owner); diff --git a/fs/namei.c b/fs/namei.c index 0fa000ad7e9b..79b0ff9b151e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2824,16 +2824,14 @@ static int may_delete(struct user_namespace *mnt_userns, struct inode *dir, static inline int may_create(struct user_namespace *mnt_userns, struct inode *dir, struct dentry *child) { - struct user_namespace *s_user_ns; audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - s_user_ns = dir->i_sb->s_user_ns; - if (!kuid_has_mapping(s_user_ns, fsuid_into_mnt(mnt_userns)) || - !kgid_has_mapping(s_user_ns, fsgid_into_mnt(mnt_userns))) + if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns)) return -EOVERFLOW; + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); } @@ -3035,14 +3033,11 @@ static int may_o_create(struct user_namespace *mnt_userns, const struct path *dir, struct dentry *dentry, umode_t mode) { - struct user_namespace *s_user_ns; int error = security_path_mknod(dir, dentry, mode, 0); if (error) return error; - s_user_ns = dir->dentry->d_sb->s_user_ns; - if (!kuid_has_mapping(s_user_ns, fsuid_into_mnt(mnt_userns)) || - !kgid_has_mapping(s_user_ns, fsgid_into_mnt(mnt_userns))) + if (!fsuidgid_has_mapping(dir->dentry->d_sb, mnt_userns)) return -EOVERFLOW; error = inode_permission(mnt_userns, dir->dentry->d_inode, diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index f93370bd7b1e..2a8bdf33e6c4 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -812,7 +812,7 @@ xfs_init_new_inode( if (dir && !(dir->i_mode & S_ISGID) && (mp->m_flags & XFS_MOUNT_GRPID)) { - inode->i_uid = fsuid_into_mnt(mnt_userns); + inode_fsuid_set(inode, mnt_userns); inode->i_gid = dir->i_gid; inode->i_mode = mode; } else { @@ -1007,8 +1007,8 @@ xfs_create( /* * Make sure that we have allocated dquot(s) on disk. */ - error = xfs_qm_vop_dqalloc(dp, fsuid_into_mnt(mnt_userns), - fsgid_into_mnt(mnt_userns), prid, + error = xfs_qm_vop_dqalloc(dp, mapped_fsuid(mnt_userns), + mapped_fsgid(mnt_userns), prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp); if (error) @@ -1158,8 +1158,8 @@ xfs_create_tmpfile( /* * Make sure that we have allocated dquot(s) on disk. */ - error = xfs_qm_vop_dqalloc(dp, fsuid_into_mnt(mnt_userns), - fsgid_into_mnt(mnt_userns), prid, + error = xfs_qm_vop_dqalloc(dp, mapped_fsuid(mnt_userns), + mapped_fsgid(mnt_userns), prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp); if (error) diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 7f368b10ded1..63edb4dbed4a 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -182,8 +182,8 @@ xfs_symlink( /* * Make sure that we have allocated dquot(s) on disk. */ - error = xfs_qm_vop_dqalloc(dp, fsuid_into_mnt(mnt_userns), - fsgid_into_mnt(mnt_userns), prid, + error = xfs_qm_vop_dqalloc(dp, mapped_fsuid(mnt_userns), + mapped_fsgid(mnt_userns), prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp); if (error) diff --git a/include/linux/fs.h b/include/linux/fs.h index 2e80a1e79fb5..5ac7bad3f0de 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1575,52 +1575,172 @@ static inline void i_gid_write(struct inode *inode, gid_t gid) inode->i_gid = make_kgid(inode->i_sb->s_user_ns, gid); } +/** + * kuid_into_mnt - map a kuid down into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * @kuid: kuid to be mapped + * + * Return: @kuid mapped according to @mnt_userns. + * If @kuid has no mapping INVALID_UID is returned. + */ static inline kuid_t kuid_into_mnt(struct user_namespace *mnt_userns, kuid_t kuid) { return make_kuid(mnt_userns, __kuid_val(kuid)); } +/** + * kgid_into_mnt - map a kgid down into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * @kgid: kgid to be mapped + * + * Return: @kgid mapped according to @mnt_userns. + * If @kgid has no mapping INVALID_GID is returned. + */ static inline kgid_t kgid_into_mnt(struct user_namespace *mnt_userns, kgid_t kgid) { return make_kgid(mnt_userns, __kgid_val(kgid)); } +/** + * i_uid_into_mnt - map an inode's i_uid down into a mnt_userns + * @mnt_userns: user namespace of the mount the inode was found from + * @inode: inode to map + * + * Return: the inode's i_uid mapped down according to @mnt_userns. + * If the inode's i_uid has no mapping INVALID_UID is returned. + */ static inline kuid_t i_uid_into_mnt(struct user_namespace *mnt_userns, const struct inode *inode) { return kuid_into_mnt(mnt_userns, inode->i_uid); } +/** + * i_gid_into_mnt - map an inode's i_gid down into a mnt_userns + * @mnt_userns: user namespace of the mount the inode was found from + * @inode: inode to map + * + * Return: the inode's i_gid mapped down according to @mnt_userns. + * If the inode's i_gid has no mapping INVALID_GID is returned. + */ static inline kgid_t i_gid_into_mnt(struct user_namespace *mnt_userns, const struct inode *inode) { return kgid_into_mnt(mnt_userns, inode->i_gid); } +/** + * kuid_from_mnt - map a kuid up into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * @kuid: kuid to be mapped + * + * Return: @kuid mapped up according to @mnt_userns. + * If @kuid has no mapping INVALID_UID is returned. + */ static inline kuid_t kuid_from_mnt(struct user_namespace *mnt_userns, kuid_t kuid) { return KUIDT_INIT(from_kuid(mnt_userns, kuid)); } +/** + * kgid_from_mnt - map a kgid up into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * @kgid: kgid to be mapped + * + * Return: @kgid mapped up according to @mnt_userns. + * If @kgid has no mapping INVALID_GID is returned. + */ static inline kgid_t kgid_from_mnt(struct user_namespace *mnt_userns, kgid_t kgid) { return KGIDT_INIT(from_kgid(mnt_userns, kgid)); } -static inline kuid_t fsuid_into_mnt(struct user_namespace *mnt_userns) +/** + * mapped_fsuid - return caller's fsuid mapped up into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * + * Use this helper to initialize a new vfs or filesystem object based on + * the caller's fsuid. A common example is initializing the i_uid field of + * a newly allocated inode triggered by a creation event such as mkdir or + * O_CREAT. Other examples include the allocation of quotas for a specific + * user. + * + * Return: the caller's current fsuid mapped up according to @mnt_userns. + */ +static inline kuid_t mapped_fsuid(struct user_namespace *mnt_userns) { return kuid_from_mnt(mnt_userns, current_fsuid()); } -static inline kgid_t fsgid_into_mnt(struct user_namespace *mnt_userns) +/** + * mapped_fsgid - return caller's fsgid mapped up into a mnt_userns + * @mnt_userns: user namespace of the relevant mount + * + * Use this helper to initialize a new vfs or filesystem object based on + * the caller's fsgid. A common example is initializing the i_gid field of + * a newly allocated inode triggered by a creation event such as mkdir or + * O_CREAT. Other examples include the allocation of quotas for a specific + * user. + * + * Return: the caller's current fsgid mapped up according to @mnt_userns. + */ +static inline kgid_t mapped_fsgid(struct user_namespace *mnt_userns) { return kgid_from_mnt(mnt_userns, current_fsgid()); } +/** + * inode_fsuid_set - initialize inode's i_uid field with callers fsuid + * @inode: inode to initialize + * @mnt_userns: user namespace of the mount the inode was found from + * + * Initialize the i_uid field of @inode. If the inode was found/created via + * an idmapped mount map the caller's fsuid according to @mnt_users. + */ +static inline void inode_fsuid_set(struct inode *inode, + struct user_namespace *mnt_userns) +{ + inode->i_uid = mapped_fsuid(mnt_userns); +} + +/** + * inode_fsgid_set - initialize inode's i_gid field with callers fsgid + * @inode: inode to initialize + * @mnt_userns: user namespace of the mount the inode was found from + * + * Initialize the i_gid field of @inode. If the inode was found/created via + * an idmapped mount map the caller's fsgid according to @mnt_users. + */ +static inline void inode_fsgid_set(struct inode *inode, + struct user_namespace *mnt_userns) +{ + inode->i_gid = mapped_fsgid(mnt_userns); +} + +/** + * fsuidgid_has_mapping() - check whether caller's fsuid/fsgid is mapped + * @sb: the superblock we want a mapping in + * @mnt_userns: user namespace of the relevant mount + * + * Check whether the caller's fsuid and fsgid have a valid mapping in the + * s_user_ns of the superblock @sb. If the caller is on an idmapped mount map + * the caller's fsuid and fsgid according to the @mnt_userns first. + * + * Return: true if fsuid and fsgid is mapped, false if not. + */ +static inline bool fsuidgid_has_mapping(struct super_block *sb, + struct user_namespace *mnt_userns) +{ + struct user_namespace *s_user_ns = sb->s_user_ns; + + return kuid_has_mapping(s_user_ns, mapped_fsuid(mnt_userns)) && + kgid_has_mapping(s_user_ns, mapped_fsgid(mnt_userns)); +} + extern struct timespec64 current_time(struct inode *inode); /*