Commit b27c82e1 authored by Christian Brauner's avatar Christian Brauner Committed by Christian Brauner (Microsoft)

attr: port attribute changes to new types

Now that we introduced new infrastructure to increase the type safety
for filesystems supporting idmapped mounts port the first part of the
vfs over to them.

This ports the attribute changes codepaths to rely on the new better
helpers using a dedicated type.

Before this change we used to take a shortcut and place the actual
values that would be written to inode->i_{g,u}id into struct iattr. This
had the advantage that we moved idmappings mostly out of the picture
early on but it made reasoning about changes more difficult than it
should be.

The filesystem was never explicitly told that it dealt with an idmapped
mount. The transition to the value that needed to be stored in
inode->i_{g,u}id appeared way too early and increased the probability of
bugs in various codepaths.

We know place the same value in struct iattr no matter if this is an
idmapped mount or not. The vfs will only deal with type safe
vfs{g,u}id_t. This makes it massively safer to perform permission checks
as the type will tell us what checks we need to perform and what helpers
we need to use.

Fileystems raising FS_ALLOW_IDMAP can't simply write ia_vfs{g,u}id to
inode->i_{g,u}id since they are different types. Instead they need to
use the dedicated vfs{g,u}id_to_k{g,u}id() helpers that map the
vfs{g,u}id into the filesystem.

The other nice effect is that filesystems like overlayfs don't need to
care about idmappings explicitly anymore and can simply set up struct
iattr accordingly directly.

Link: https://lore.kernel.org/lkml/CAHk-=win6+ahs1EwLkcq8apqLi_1wXFWbrPf340zYEhObpz4jA@mail.gmail.com [1]
Link: https://lore.kernel.org/r/20220621141454.2914719-9-brauner@kernel.org
Cc: Seth Forshee <sforshee@digitalocean.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
CC: linux-fsdevel@vger.kernel.org
Reviewed-by: default avatarSeth Forshee <sforshee@digitalocean.com>
Signed-off-by: default avatarChristian Brauner (Microsoft) <brauner@kernel.org>
parent 0e363cf3
...@@ -31,15 +31,15 @@ ...@@ -31,15 +31,15 @@
* performed on the raw inode simply passs init_user_ns. * performed on the raw inode simply passs init_user_ns.
*/ */
static bool chown_ok(struct user_namespace *mnt_userns, static bool chown_ok(struct user_namespace *mnt_userns,
const struct inode *inode, const struct inode *inode, vfsuid_t ia_vfsuid)
kuid_t uid)
{ {
kuid_t kuid = i_uid_into_mnt(mnt_userns, inode); vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
if (uid_eq(current_fsuid(), kuid) && uid_eq(uid, inode->i_uid)) if (vfsuid_eq_kuid(vfsuid, current_fsuid()) &&
vfsuid_eq(ia_vfsuid, vfsuid))
return true; return true;
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN)) if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
return true; return true;
if (uid_eq(kuid, INVALID_UID) && if (!vfsuid_valid(vfsuid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true; return true;
return false; return false;
...@@ -58,21 +58,19 @@ static bool chown_ok(struct user_namespace *mnt_userns, ...@@ -58,21 +58,19 @@ static bool chown_ok(struct user_namespace *mnt_userns,
* performed on the raw inode simply passs init_user_ns. * performed on the raw inode simply passs init_user_ns.
*/ */
static bool chgrp_ok(struct user_namespace *mnt_userns, static bool chgrp_ok(struct user_namespace *mnt_userns,
const struct inode *inode, kgid_t gid) const struct inode *inode, vfsgid_t ia_vfsgid)
{ {
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode); vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) { vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
kgid_t mapped_gid; if (vfsuid_eq_kuid(vfsuid, current_fsuid())) {
if (vfsgid_eq(ia_vfsgid, vfsgid))
if (gid_eq(gid, inode->i_gid))
return true; return true;
mapped_gid = mapped_kgid_fs(mnt_userns, i_user_ns(inode), gid); if (vfsgid_in_group_p(ia_vfsgid))
if (in_group_p(mapped_gid))
return true; return true;
} }
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN)) if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
return true; return true;
if (gid_eq(kgid, INVALID_GID) && if (!vfsgid_valid(vfsgid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
return true; return true;
return false; return false;
...@@ -120,28 +118,29 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -120,28 +118,29 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
goto kill_priv; goto kill_priv;
/* Make sure a caller can chown. */ /* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) && !chown_ok(mnt_userns, inode, attr->ia_uid)) if ((ia_valid & ATTR_UID) &&
!chown_ok(mnt_userns, inode, attr->ia_vfsuid))
return -EPERM; return -EPERM;
/* Make sure caller can chgrp. */ /* Make sure caller can chgrp. */
if ((ia_valid & ATTR_GID) && !chgrp_ok(mnt_userns, inode, attr->ia_gid)) if ((ia_valid & ATTR_GID) &&
!chgrp_ok(mnt_userns, inode, attr->ia_vfsgid))
return -EPERM; return -EPERM;
/* Make sure a caller can chmod. */ /* Make sure a caller can chmod. */
if (ia_valid & ATTR_MODE) { if (ia_valid & ATTR_MODE) {
kgid_t mapped_gid; vfsgid_t vfsgid;
if (!inode_owner_or_capable(mnt_userns, inode)) if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM; return -EPERM;
if (ia_valid & ATTR_GID) if (ia_valid & ATTR_GID)
mapped_gid = mapped_kgid_fs(mnt_userns, vfsgid = attr->ia_vfsgid;
i_user_ns(inode), attr->ia_gid);
else else
mapped_gid = i_gid_into_mnt(mnt_userns, inode); vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
/* Also check the setgid bit! */ /* Also check the setgid bit! */
if (!in_group_p(mapped_gid) && if (!vfsgid_in_group_p(vfsgid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID)) !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
attr->ia_mode &= ~S_ISGID; attr->ia_mode &= ~S_ISGID;
} }
...@@ -219,9 +218,7 @@ EXPORT_SYMBOL(inode_newsize_ok); ...@@ -219,9 +218,7 @@ EXPORT_SYMBOL(inode_newsize_ok);
* setattr_copy must be called with i_mutex held. * setattr_copy must be called with i_mutex held.
* *
* setattr_copy updates the inode's metadata with that specified * setattr_copy updates the inode's metadata with that specified
* in attr on idmapped mounts. If file ownership is changed setattr_copy * in attr on idmapped mounts. Necessary permission checks to determine
* doesn't map ia_uid and ia_gid. It will asssume the caller has already
* provided the intended values. Necessary permission checks to determine
* whether or not the S_ISGID property needs to be removed are performed with * whether or not the S_ISGID property needs to be removed are performed with
* the correct idmapped mount permission helpers. * the correct idmapped mount permission helpers.
* Noticeably missing is inode size update, which is more complex * Noticeably missing is inode size update, which is more complex
...@@ -242,8 +239,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -242,8 +239,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
{ {
unsigned int ia_valid = attr->ia_valid; unsigned int ia_valid = attr->ia_valid;
i_uid_update(&init_user_ns, attr, inode); i_uid_update(mnt_userns, attr, inode);
i_gid_update(&init_user_ns, attr, inode); i_gid_update(mnt_userns, attr, inode);
if (ia_valid & ATTR_ATIME) if (ia_valid & ATTR_ATIME)
inode->i_atime = attr->ia_atime; inode->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME) if (ia_valid & ATTR_MTIME)
...@@ -252,8 +249,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -252,8 +249,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
inode->i_ctime = attr->ia_ctime; inode->i_ctime = attr->ia_ctime;
if (ia_valid & ATTR_MODE) { if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode; umode_t mode = attr->ia_mode;
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode); vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
if (!in_group_p(kgid) && if (!vfsgid_in_group_p(vfsgid) &&
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID)) !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
mode &= ~S_ISGID; mode &= ~S_ISGID;
inode->i_mode = mode; inode->i_mode = mode;
...@@ -304,9 +301,6 @@ EXPORT_SYMBOL(may_setattr); ...@@ -304,9 +301,6 @@ EXPORT_SYMBOL(may_setattr);
* retry. Because breaking a delegation may take a long time, the * retry. Because breaking a delegation may take a long time, the
* caller should drop the i_mutex before doing so. * caller should drop the i_mutex before doing so.
* *
* If file ownership is changed notify_change() doesn't map ia_uid and
* ia_gid. It will asssume the caller has already provided the intended values.
*
* Alternatively, a caller may pass NULL for delegated_inode. This may * Alternatively, a caller may pass NULL for delegated_inode. This may
* be appropriate for callers that expect the underlying filesystem not * be appropriate for callers that expect the underlying filesystem not
* to be NFS exported. Also, passing NULL is fine for callers holding * to be NFS exported. Also, passing NULL is fine for callers holding
...@@ -395,23 +389,25 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -395,23 +389,25 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
* namespace of the superblock. * namespace of the superblock.
*/ */
if (ia_valid & ATTR_UID && if (ia_valid & ATTR_UID &&
!kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid)) !vfsuid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
attr->ia_vfsuid))
return -EOVERFLOW; return -EOVERFLOW;
if (ia_valid & ATTR_GID && if (ia_valid & ATTR_GID &&
!kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid)) !vfsgid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
attr->ia_vfsgid))
return -EOVERFLOW; return -EOVERFLOW;
/* Don't allow modifications of files with invalid uids or /* Don't allow modifications of files with invalid uids or
* gids unless those uids & gids are being made valid. * gids unless those uids & gids are being made valid.
*/ */
if (!(ia_valid & ATTR_UID) && if (!(ia_valid & ATTR_UID) &&
!uid_valid(i_uid_into_mnt(mnt_userns, inode))) !vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)))
return -EOVERFLOW; return -EOVERFLOW;
if (!(ia_valid & ATTR_GID) && if (!(ia_valid & ATTR_GID) &&
!gid_valid(i_gid_into_mnt(mnt_userns, inode))) !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode)))
return -EOVERFLOW; return -EOVERFLOW;
error = security_inode_setattr(&init_user_ns, dentry, attr); error = security_inode_setattr(mnt_userns, dentry, attr);
if (error) if (error)
return error; return error;
error = try_break_deleg(inode, delegated_inode); error = try_break_deleg(inode, delegated_inode);
......
...@@ -1679,14 +1679,14 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -1679,14 +1679,14 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (error) if (error)
return error; return error;
if (is_quota_modification(&init_user_ns, inode, iattr)) { if (is_quota_modification(mnt_userns, inode, iattr)) {
error = dquot_initialize(inode); error = dquot_initialize(inode);
if (error) if (error)
return error; return error;
} }
if (i_uid_needs_update(&init_user_ns, iattr, inode) || if (i_uid_needs_update(mnt_userns, iattr, inode) ||
i_gid_needs_update(&init_user_ns, iattr, inode)) { i_gid_needs_update(mnt_userns, iattr, inode)) {
error = dquot_transfer(&init_user_ns, inode, iattr); error = dquot_transfer(mnt_userns, inode, iattr);
if (error) if (error)
return error; return error;
} }
......
...@@ -5350,14 +5350,14 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -5350,14 +5350,14 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (error) if (error)
return error; return error;
if (is_quota_modification(&init_user_ns, inode, attr)) { if (is_quota_modification(mnt_userns, inode, attr)) {
error = dquot_initialize(inode); error = dquot_initialize(inode);
if (error) if (error)
return error; return error;
} }
if (i_uid_needs_update(&init_user_ns, attr, inode) || if (i_uid_needs_update(mnt_userns, attr, inode) ||
i_gid_needs_update(&init_user_ns, attr, inode)) { i_gid_needs_update(mnt_userns, attr, inode)) {
handle_t *handle; handle_t *handle;
/* (user+group)*(old+new) structure, inode write (sb, /* (user+group)*(old+new) structure, inode write (sb,
...@@ -5374,7 +5374,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -5374,7 +5374,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
* counts xattr inode references. * counts xattr inode references.
*/ */
down_read(&EXT4_I(inode)->xattr_sem); down_read(&EXT4_I(inode)->xattr_sem);
error = dquot_transfer(&init_user_ns, inode, attr); error = dquot_transfer(mnt_userns, inode, attr);
up_read(&EXT4_I(inode)->xattr_sem); up_read(&EXT4_I(inode)->xattr_sem);
if (error) { if (error) {
...@@ -5383,8 +5383,8 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -5383,8 +5383,8 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
} }
/* Update corresponding info in inode so that everything is in /* Update corresponding info in inode so that everything is in
* one transaction */ * one transaction */
i_uid_update(&init_user_ns, attr, inode); i_uid_update(mnt_userns, attr, inode);
i_gid_update(&init_user_ns, attr, inode); i_gid_update(mnt_userns, attr, inode);
error = ext4_mark_inode_dirty(handle, inode); error = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle); ext4_journal_stop(handle);
if (unlikely(error)) { if (unlikely(error)) {
......
...@@ -861,8 +861,8 @@ static void __setattr_copy(struct user_namespace *mnt_userns, ...@@ -861,8 +861,8 @@ static void __setattr_copy(struct user_namespace *mnt_userns,
{ {
unsigned int ia_valid = attr->ia_valid; unsigned int ia_valid = attr->ia_valid;
i_uid_update(&init_user_ns, attr, inode); i_uid_update(mnt_userns, attr, inode);
i_gid_update(&init_user_ns, attr, inode); i_gid_update(mnt_userns, attr, inode);
if (ia_valid & ATTR_ATIME) if (ia_valid & ATTR_ATIME)
inode->i_atime = attr->ia_atime; inode->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME) if (ia_valid & ATTR_MTIME)
...@@ -915,15 +915,15 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -915,15 +915,15 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (err) if (err)
return err; return err;
if (is_quota_modification(&init_user_ns, inode, attr)) { if (is_quota_modification(mnt_userns, inode, attr)) {
err = f2fs_dquot_initialize(inode); err = f2fs_dquot_initialize(inode);
if (err) if (err)
return err; return err;
} }
if (i_uid_needs_update(&init_user_ns, attr, inode) || if (i_uid_needs_update(mnt_userns, attr, inode) ||
i_gid_needs_update(&init_user_ns, attr, inode)) { i_gid_needs_update(mnt_userns, attr, inode)) {
f2fs_lock_op(F2FS_I_SB(inode)); f2fs_lock_op(F2FS_I_SB(inode));
err = dquot_transfer(&init_user_ns, inode, attr); err = dquot_transfer(mnt_userns, inode, attr);
if (err) { if (err) {
set_sbi_flag(F2FS_I_SB(inode), set_sbi_flag(F2FS_I_SB(inode),
SBI_QUOTA_NEED_REPAIR); SBI_QUOTA_NEED_REPAIR);
...@@ -934,8 +934,8 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -934,8 +934,8 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
* update uid/gid under lock_op(), so that dquot and inode can * update uid/gid under lock_op(), so that dquot and inode can
* be updated atomically. * be updated atomically.
*/ */
i_uid_update(&init_user_ns, attr, inode); i_uid_update(mnt_userns, attr, inode);
i_gid_update(&init_user_ns, attr, inode); i_gid_update(mnt_userns, attr, inode);
f2fs_mark_inode_dirty_sync(inode, true); f2fs_mark_inode_dirty_sync(inode, true);
f2fs_unlock_op(F2FS_I_SB(inode)); f2fs_unlock_op(F2FS_I_SB(inode));
} }
......
...@@ -255,12 +255,12 @@ static int recover_quota_data(struct inode *inode, struct page *page) ...@@ -255,12 +255,12 @@ static int recover_quota_data(struct inode *inode, struct page *page)
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
attr.ia_uid = make_kuid(inode->i_sb->s_user_ns, i_uid); attr.ia_vfsuid = VFSUIDT_INIT(make_kuid(inode->i_sb->s_user_ns, i_uid));
attr.ia_gid = make_kgid(inode->i_sb->s_user_ns, i_gid); attr.ia_vfsgid = VFSGIDT_INIT(make_kgid(inode->i_sb->s_user_ns, i_gid));
if (!uid_eq(attr.ia_uid, inode->i_uid)) if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&init_user_ns, inode)))
attr.ia_valid |= ATTR_UID; attr.ia_valid |= ATTR_UID;
if (!gid_eq(attr.ia_gid, inode->i_gid)) if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&init_user_ns, inode)))
attr.ia_valid |= ATTR_GID; attr.ia_valid |= ATTR_GID;
if (!attr.ia_valid) if (!attr.ia_valid)
......
...@@ -90,7 +90,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) ...@@ -90,7 +90,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
* out the RO attribute for checking by the security * out the RO attribute for checking by the security
* module, just because it maps to a file mode. * module, just because it maps to a file mode.
*/ */
err = security_inode_setattr(&init_user_ns, err = security_inode_setattr(file_mnt_user_ns(file),
file->f_path.dentry, &ia); file->f_path.dentry, &ia);
if (err) if (err)
goto out_unlock_inode; goto out_unlock_inode;
...@@ -517,9 +517,11 @@ int fat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -517,9 +517,11 @@ int fat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
} }
if (((attr->ia_valid & ATTR_UID) && if (((attr->ia_valid & ATTR_UID) &&
(!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || (!uid_eq(from_vfsuid(mnt_userns, i_user_ns(inode), attr->ia_vfsuid),
sbi->options.fs_uid))) ||
((attr->ia_valid & ATTR_GID) && ((attr->ia_valid & ATTR_GID) &&
(!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || (!gid_eq(from_vfsgid(mnt_userns, i_user_ns(inode), attr->ia_vfsgid),
sbi->options.fs_gid))) ||
((attr->ia_valid & ATTR_MODE) && ((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~FAT_VALID_MODE))) (attr->ia_mode & ~FAT_VALID_MODE)))
error = -EPERM; error = -EPERM;
......
...@@ -95,14 +95,14 @@ int jfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -95,14 +95,14 @@ int jfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (rc) if (rc)
return rc; return rc;
if (is_quota_modification(&init_user_ns, inode, iattr)) { if (is_quota_modification(mnt_userns, inode, iattr)) {
rc = dquot_initialize(inode); rc = dquot_initialize(inode);
if (rc) if (rc)
return rc; return rc;
} }
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) || if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) { (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
rc = dquot_transfer(&init_user_ns, inode, iattr); rc = dquot_transfer(mnt_userns, inode, iattr);
if (rc) if (rc)
return rc; return rc;
} }
......
...@@ -1146,7 +1146,7 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -1146,7 +1146,7 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
if (status) if (status)
return status; return status;
if (is_quota_modification(&init_user_ns, inode, attr)) { if (is_quota_modification(mnt_userns, inode, attr)) {
status = dquot_initialize(inode); status = dquot_initialize(inode);
if (status) if (status)
return status; return status;
......
...@@ -663,6 +663,42 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode) ...@@ -663,6 +663,42 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
return do_fchmodat(AT_FDCWD, filename, mode); return do_fchmodat(AT_FDCWD, filename, mode);
} }
/**
* setattr_vfsuid - check and set ia_fsuid attribute
* @kuid: new inode owner
*
* Check whether @kuid is valid and if so generate and set vfsuid_t in
* ia_vfsuid.
*
* Return: true if @kuid is valid, false if not.
*/
static inline bool setattr_vfsuid(struct iattr *attr, kuid_t kuid)
{
if (!uid_valid(kuid))
return false;
attr->ia_valid |= ATTR_UID;
attr->ia_vfsuid = VFSUIDT_INIT(kuid);
return true;
}
/**
* setattr_vfsgid - check and set ia_fsgid attribute
* @kgid: new inode owner
*
* Check whether @kgid is valid and if so generate and set vfsgid_t in
* ia_vfsgid.
*
* Return: true if @kgid is valid, false if not.
*/
static inline bool setattr_vfsgid(struct iattr *attr, kgid_t kgid)
{
if (!gid_valid(kgid))
return false;
attr->ia_valid |= ATTR_GID;
attr->ia_vfsgid = VFSGIDT_INIT(kgid);
return true;
}
int chown_common(const struct path *path, uid_t user, gid_t group) int chown_common(const struct path *path, uid_t user, gid_t group)
{ {
struct user_namespace *mnt_userns, *fs_userns; struct user_namespace *mnt_userns, *fs_userns;
...@@ -678,28 +714,22 @@ int chown_common(const struct path *path, uid_t user, gid_t group) ...@@ -678,28 +714,22 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
mnt_userns = mnt_user_ns(path->mnt); mnt_userns = mnt_user_ns(path->mnt);
fs_userns = i_user_ns(inode); fs_userns = i_user_ns(inode);
uid = mapped_kuid_user(mnt_userns, fs_userns, uid);
gid = mapped_kgid_user(mnt_userns, fs_userns, gid);
retry_deleg: retry_deleg:
newattrs.ia_valid = ATTR_CTIME; newattrs.ia_valid = ATTR_CTIME;
if (user != (uid_t) -1) { if ((user != (uid_t)-1) && !setattr_vfsuid(&newattrs, uid))
if (!uid_valid(uid))
return -EINVAL; return -EINVAL;
newattrs.ia_valid |= ATTR_UID; if ((group != (gid_t)-1) && !setattr_vfsgid(&newattrs, gid))
newattrs.ia_uid = uid;
}
if (group != (gid_t) -1) {
if (!gid_valid(gid))
return -EINVAL; return -EINVAL;
newattrs.ia_valid |= ATTR_GID;
newattrs.ia_gid = gid;
}
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
newattrs.ia_valid |= newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
inode_lock(inode); inode_lock(inode);
error = security_path_chown(path, uid, gid); /* Continue to send actual fs values, not the mount values. */
error = security_path_chown(
path,
from_vfsuid(mnt_userns, fs_userns, newattrs.ia_vfsuid),
from_vfsgid(mnt_userns, fs_userns, newattrs.ia_vfsgid));
if (!error) if (!error)
error = notify_change(mnt_userns, path->dentry, &newattrs, error = notify_change(mnt_userns, path->dentry, &newattrs,
&delegated_inode); &delegated_inode);
......
...@@ -331,8 +331,8 @@ int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry, ...@@ -331,8 +331,8 @@ int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry,
if (!err) { if (!err) {
struct iattr attr = { struct iattr attr = {
.ia_valid = ATTR_UID | ATTR_GID, .ia_valid = ATTR_UID | ATTR_GID,
.ia_uid = stat->uid, .ia_vfsuid = VFSUIDT_INIT(stat->uid),
.ia_gid = stat->gid, .ia_vfsgid = VFSGIDT_INIT(stat->gid),
}; };
err = ovl_do_notify_change(ofs, upperdentry, &attr); err = ovl_do_notify_change(ofs, upperdentry, &attr);
} }
......
...@@ -139,17 +139,7 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs, ...@@ -139,17 +139,7 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs,
struct dentry *upperdentry, struct dentry *upperdentry,
struct iattr *attr) struct iattr *attr)
{ {
struct user_namespace *upper_mnt_userns = ovl_upper_mnt_userns(ofs); return notify_change(ovl_upper_mnt_userns(ofs), upperdentry, attr, NULL);
struct user_namespace *fs_userns = i_user_ns(d_inode(upperdentry));
if (attr->ia_valid & ATTR_UID)
attr->ia_uid = mapped_kuid_user(upper_mnt_userns,
fs_userns, attr->ia_uid);
if (attr->ia_valid & ATTR_GID)
attr->ia_gid = mapped_kgid_user(upper_mnt_userns,
fs_userns, attr->ia_gid);
return notify_change(upper_mnt_userns, upperdentry, attr, NULL);
} }
static inline int ovl_do_rmdir(struct ovl_fs *ofs, static inline int ovl_do_rmdir(struct ovl_fs *ofs,
......
...@@ -2096,8 +2096,11 @@ int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -2096,8 +2096,11 @@ int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode,
if (!dquot_active(inode)) if (!dquot_active(inode))
return 0; return 0;
if (i_uid_needs_update(&init_user_ns, iattr, inode)) { if (i_uid_needs_update(mnt_userns, iattr, inode)) {
dquot = dqget(sb, make_kqid_uid(iattr->ia_uid)); kuid_t kuid = from_vfsuid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsuid);
dquot = dqget(sb, make_kqid_uid(kuid));
if (IS_ERR(dquot)) { if (IS_ERR(dquot)) {
if (PTR_ERR(dquot) != -ESRCH) { if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot); ret = PTR_ERR(dquot);
...@@ -2107,8 +2110,11 @@ int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -2107,8 +2110,11 @@ int dquot_transfer(struct user_namespace *mnt_userns, struct inode *inode,
} }
transfer_to[USRQUOTA] = dquot; transfer_to[USRQUOTA] = dquot;
} }
if (i_gid_needs_update(&init_user_ns, iattr, inode)) { if (i_gid_needs_update(mnt_userns, iattr, inode)) {
dquot = dqget(sb, make_kqid_gid(iattr->ia_gid)); kgid_t kgid = from_vfsgid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsgid);
dquot = dqget(sb, make_kqid_gid(kgid));
if (IS_ERR(dquot)) { if (IS_ERR(dquot)) {
if (PTR_ERR(dquot) != -ESRCH) { if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot); ret = PTR_ERR(dquot);
......
...@@ -3284,7 +3284,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -3284,7 +3284,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
/* must be turned off for recursive notify_change calls */ /* must be turned off for recursive notify_change calls */
ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID); ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID);
if (is_quota_modification(&init_user_ns, inode, attr)) { if (is_quota_modification(mnt_userns, inode, attr)) {
error = dquot_initialize(inode); error = dquot_initialize(inode);
if (error) if (error)
return error; return error;
...@@ -3367,7 +3367,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -3367,7 +3367,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
reiserfs_write_unlock(inode->i_sb); reiserfs_write_unlock(inode->i_sb);
if (error) if (error)
goto out; goto out;
error = dquot_transfer(&init_user_ns, inode, attr); error = dquot_transfer(mnt_userns, inode, attr);
reiserfs_write_lock(inode->i_sb); reiserfs_write_lock(inode->i_sb);
if (error) { if (error) {
journal_end(&th); journal_end(&th);
......
...@@ -667,13 +667,15 @@ xfs_setattr_nonsize( ...@@ -667,13 +667,15 @@ xfs_setattr_nonsize(
uint qflags = 0; uint qflags = 0;
if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) { if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) {
uid = iattr->ia_uid; uid = from_vfsuid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsuid);
qflags |= XFS_QMOPT_UQUOTA; qflags |= XFS_QMOPT_UQUOTA;
} else { } else {
uid = inode->i_uid; uid = inode->i_uid;
} }
if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) { if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) {
gid = iattr->ia_gid; gid = from_vfsgid(mnt_userns, i_user_ns(inode),
iattr->ia_vfsgid);
qflags |= XFS_QMOPT_GQUOTA; qflags |= XFS_QMOPT_GQUOTA;
} else { } else {
gid = inode->i_gid; gid = inode->i_gid;
...@@ -705,12 +707,12 @@ xfs_setattr_nonsize( ...@@ -705,12 +707,12 @@ xfs_setattr_nonsize(
* also. * also.
*/ */
if (XFS_IS_UQUOTA_ON(mp) && if (XFS_IS_UQUOTA_ON(mp) &&
i_uid_needs_update(&init_user_ns, iattr, inode)) { i_uid_needs_update(mnt_userns, iattr, inode)) {
ASSERT(udqp); ASSERT(udqp);
old_udqp = xfs_qm_vop_chown(tp, ip, &ip->i_udquot, udqp); old_udqp = xfs_qm_vop_chown(tp, ip, &ip->i_udquot, udqp);
} }
if (XFS_IS_GQUOTA_ON(mp) && if (XFS_IS_GQUOTA_ON(mp) &&
i_gid_needs_update(&init_user_ns, iattr, inode)) { i_gid_needs_update(mnt_userns, iattr, inode)) {
ASSERT(xfs_has_pquotino(mp) || !XFS_IS_PQUOTA_ON(mp)); ASSERT(xfs_has_pquotino(mp) || !XFS_IS_PQUOTA_ON(mp));
ASSERT(gdqp); ASSERT(gdqp);
old_gdqp = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp); old_gdqp = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp);
......
...@@ -616,7 +616,7 @@ static int zonefs_inode_setattr(struct user_namespace *mnt_userns, ...@@ -616,7 +616,7 @@ static int zonefs_inode_setattr(struct user_namespace *mnt_userns,
!uid_eq(iattr->ia_uid, inode->i_uid)) || !uid_eq(iattr->ia_uid, inode->i_uid)) ||
((iattr->ia_valid & ATTR_GID) && ((iattr->ia_valid & ATTR_GID) &&
!gid_eq(iattr->ia_gid, inode->i_gid))) { !gid_eq(iattr->ia_gid, inode->i_gid))) {
ret = dquot_transfer(&init_user_ns, inode, iattr); ret = dquot_transfer(mnt_userns, inode, iattr);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -228,6 +228,10 @@ struct iattr { ...@@ -228,6 +228,10 @@ struct iattr {
* are a dedicated type requiring the filesystem to use the dedicated * are a dedicated type requiring the filesystem to use the dedicated
* helpers. Other filesystem can continue to use ia_{g,u}id until they * helpers. Other filesystem can continue to use ia_{g,u}id until they
* have been ported. * have been ported.
*
* They always contain the same value. In other words FS_ALLOW_IDMAP
* pass down the same value on idmapped mounts as they would on regular
* mounts.
*/ */
union { union {
kuid_t ia_uid; kuid_t ia_uid;
......
...@@ -24,8 +24,8 @@ static inline bool is_quota_modification(struct user_namespace *mnt_userns, ...@@ -24,8 +24,8 @@ static inline bool is_quota_modification(struct user_namespace *mnt_userns,
struct inode *inode, struct iattr *ia) struct inode *inode, struct iattr *ia)
{ {
return ((ia->ia_valid & ATTR_SIZE) || return ((ia->ia_valid & ATTR_SIZE) ||
i_uid_needs_update(&init_user_ns, ia, inode) || i_uid_needs_update(mnt_userns, ia, inode) ||
i_gid_needs_update(&init_user_ns, ia, inode)); i_gid_needs_update(mnt_userns, ia, inode));
} }
#if defined(CONFIG_QUOTA) #if defined(CONFIG_QUOTA)
......
...@@ -761,8 +761,8 @@ static int evm_attr_change(struct user_namespace *mnt_userns, ...@@ -761,8 +761,8 @@ static int evm_attr_change(struct user_namespace *mnt_userns,
struct inode *inode = d_backing_inode(dentry); struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = attr->ia_valid; unsigned int ia_valid = attr->ia_valid;
if (!i_uid_needs_update(&init_user_ns, attr, inode) && if (!i_uid_needs_update(mnt_userns, attr, inode) &&
!i_gid_needs_update(&init_user_ns, attr, inode) && !i_gid_needs_update(mnt_userns, attr, inode) &&
(!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode)) (!(ia_valid & ATTR_MODE) || attr->ia_mode == inode->i_mode))
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment