Skip to content

Commit bdfae5c

Browse files
committed
Merge tag 'fs.idmapped.vfsuid.v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull fs idmapping updates from Christian Brauner: "This introduces the new vfs{g,u}id_t types we agreed on. Similar to k{g,u}id_t the new types are just simple wrapper structs around regular {g,u}id_t types. They allow to establish a type safety boundary in the VFS for idmapped mounts preventing confusion betwen {g,u}ids mapped into an idmapped mount and {g,u}ids mapped into the caller's or the filesystem's idmapping. An initial set of helpers is introduced that allows to operate on vfs{g,u}id_t types. We will remove all references to non-type safe idmapped mounts helpers in the very near future. The patches do already exist. This converts the core attribute changing codepaths which become significantly easier to reason about because of this change. Just a few highlights here as the patches give detailed overviews of what is happening in the commit messages: - The kernel internal struct iattr contains type safe vfs{g,u}id_t values clearly communicating that these values have to take a given mount's idmapping into account. - The ownership values placed in struct iattr to change ownership are identical for idmapped and non-idmapped mounts going forward. This also allows to simplify stacking filesystems such as overlayfs that change attributes In other words, they always represent the values. - Instead of open coding checks for whether ownership changes have been requested and an actual update of the inode is required we now have small static inline wrappers that abstract this logic away removing a lot of code duplication from individual filesystems that all open-coded the same checks" * tag 'fs.idmapped.vfsuid.v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: mnt_idmapping: align kernel doc and parameter order mnt_idmapping: use new helpers in mapped_fs{g,u}id() fs: port HAS_UNMAPPED_ID() to vfs{g,u}id_t mnt_idmapping: return false when comparing two invalid ids attr: fix kernel doc attr: port attribute changes to new types security: pass down mount idmapping to setattr hook quota: port quota helpers mount ids fs: port to iattr ownership update helpers fs: introduce tiny iattr ownership update helpers fs: use mount types in iattr fs: add two type safe mapping helpers mnt_idmapping: add vfs{g,u}id_t
2 parents e6a7cf7 + 77940f0 commit bdfae5c

22 files changed

Lines changed: 546 additions & 175 deletions

File tree

fs/attr.c

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* chown_ok - verify permissions to chown inode
2323
* @mnt_userns: user namespace of the mount @inode was found from
2424
* @inode: inode to check permissions on
25-
* @uid: uid to chown @inode to
25+
* @ia_vfsuid: uid to chown @inode to
2626
*
2727
* If the inode has been found through an idmapped mount the user namespace of
2828
* the vfsmount must be passed through @mnt_userns. This function will then
@@ -31,15 +31,15 @@
3131
* performed on the raw inode simply passs init_user_ns.
3232
*/
3333
static bool chown_ok(struct user_namespace *mnt_userns,
34-
const struct inode *inode,
35-
kuid_t uid)
34+
const struct inode *inode, vfsuid_t ia_vfsuid)
3635
{
37-
kuid_t kuid = i_uid_into_mnt(mnt_userns, inode);
38-
if (uid_eq(current_fsuid(), kuid) && uid_eq(uid, inode->i_uid))
36+
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
37+
if (vfsuid_eq_kuid(vfsuid, current_fsuid()) &&
38+
vfsuid_eq(ia_vfsuid, vfsuid))
3939
return true;
4040
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
4141
return true;
42-
if (uid_eq(kuid, INVALID_UID) &&
42+
if (!vfsuid_valid(vfsuid) &&
4343
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
4444
return true;
4545
return false;
@@ -49,7 +49,7 @@ static bool chown_ok(struct user_namespace *mnt_userns,
4949
* chgrp_ok - verify permissions to chgrp inode
5050
* @mnt_userns: user namespace of the mount @inode was found from
5151
* @inode: inode to check permissions on
52-
* @gid: gid to chown @inode to
52+
* @ia_vfsgid: gid to chown @inode to
5353
*
5454
* If the inode has been found through an idmapped mount the user namespace of
5555
* the vfsmount must be passed through @mnt_userns. This function will then
@@ -58,21 +58,19 @@ static bool chown_ok(struct user_namespace *mnt_userns,
5858
* performed on the raw inode simply passs init_user_ns.
5959
*/
6060
static bool chgrp_ok(struct user_namespace *mnt_userns,
61-
const struct inode *inode, kgid_t gid)
61+
const struct inode *inode, vfsgid_t ia_vfsgid)
6262
{
63-
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
64-
if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode))) {
65-
kgid_t mapped_gid;
66-
67-
if (gid_eq(gid, inode->i_gid))
63+
vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
64+
vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
65+
if (vfsuid_eq_kuid(vfsuid, current_fsuid())) {
66+
if (vfsgid_eq(ia_vfsgid, vfsgid))
6867
return true;
69-
mapped_gid = mapped_kgid_fs(mnt_userns, i_user_ns(inode), gid);
70-
if (in_group_p(mapped_gid))
68+
if (vfsgid_in_group_p(ia_vfsgid))
7169
return true;
7270
}
7371
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
7472
return true;
75-
if (gid_eq(kgid, INVALID_GID) &&
73+
if (!vfsgid_valid(vfsgid) &&
7674
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
7775
return true;
7876
return false;
@@ -120,28 +118,29 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
120118
goto kill_priv;
121119

122120
/* Make sure a caller can chown. */
123-
if ((ia_valid & ATTR_UID) && !chown_ok(mnt_userns, inode, attr->ia_uid))
121+
if ((ia_valid & ATTR_UID) &&
122+
!chown_ok(mnt_userns, inode, attr->ia_vfsuid))
124123
return -EPERM;
125124

126125
/* Make sure caller can chgrp. */
127-
if ((ia_valid & ATTR_GID) && !chgrp_ok(mnt_userns, inode, attr->ia_gid))
126+
if ((ia_valid & ATTR_GID) &&
127+
!chgrp_ok(mnt_userns, inode, attr->ia_vfsgid))
128128
return -EPERM;
129129

130130
/* Make sure a caller can chmod. */
131131
if (ia_valid & ATTR_MODE) {
132-
kgid_t mapped_gid;
132+
vfsgid_t vfsgid;
133133

134134
if (!inode_owner_or_capable(mnt_userns, inode))
135135
return -EPERM;
136136

137137
if (ia_valid & ATTR_GID)
138-
mapped_gid = mapped_kgid_fs(mnt_userns,
139-
i_user_ns(inode), attr->ia_gid);
138+
vfsgid = attr->ia_vfsgid;
140139
else
141-
mapped_gid = i_gid_into_mnt(mnt_userns, inode);
140+
vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
142141

143142
/* Also check the setgid bit! */
144-
if (!in_group_p(mapped_gid) &&
143+
if (!vfsgid_in_group_p(vfsgid) &&
145144
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
146145
attr->ia_mode &= ~S_ISGID;
147146
}
@@ -219,9 +218,7 @@ EXPORT_SYMBOL(inode_newsize_ok);
219218
* setattr_copy must be called with i_mutex held.
220219
*
221220
* setattr_copy updates the inode's metadata with that specified
222-
* in attr on idmapped mounts. If file ownership is changed setattr_copy
223-
* doesn't map ia_uid and ia_gid. It will asssume the caller has already
224-
* provided the intended values. Necessary permission checks to determine
221+
* in attr on idmapped mounts. Necessary permission checks to determine
225222
* whether or not the S_ISGID property needs to be removed are performed with
226223
* the correct idmapped mount permission helpers.
227224
* Noticeably missing is inode size update, which is more complex
@@ -242,10 +239,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
242239
{
243240
unsigned int ia_valid = attr->ia_valid;
244241

245-
if (ia_valid & ATTR_UID)
246-
inode->i_uid = attr->ia_uid;
247-
if (ia_valid & ATTR_GID)
248-
inode->i_gid = attr->ia_gid;
242+
i_uid_update(mnt_userns, attr, inode);
243+
i_gid_update(mnt_userns, attr, inode);
249244
if (ia_valid & ATTR_ATIME)
250245
inode->i_atime = attr->ia_atime;
251246
if (ia_valid & ATTR_MTIME)
@@ -254,8 +249,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
254249
inode->i_ctime = attr->ia_ctime;
255250
if (ia_valid & ATTR_MODE) {
256251
umode_t mode = attr->ia_mode;
257-
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
258-
if (!in_group_p(kgid) &&
252+
vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
253+
if (!vfsgid_in_group_p(vfsgid) &&
259254
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
260255
mode &= ~S_ISGID;
261256
inode->i_mode = mode;
@@ -306,9 +301,6 @@ EXPORT_SYMBOL(may_setattr);
306301
* retry. Because breaking a delegation may take a long time, the
307302
* caller should drop the i_mutex before doing so.
308303
*
309-
* If file ownership is changed notify_change() doesn't map ia_uid and
310-
* ia_gid. It will asssume the caller has already provided the intended values.
311-
*
312304
* Alternatively, a caller may pass NULL for delegated_inode. This may
313305
* be appropriate for callers that expect the underlying filesystem not
314306
* to be NFS exported. Also, passing NULL is fine for callers holding
@@ -397,23 +389,25 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
397389
* namespace of the superblock.
398390
*/
399391
if (ia_valid & ATTR_UID &&
400-
!kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid))
392+
!vfsuid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
393+
attr->ia_vfsuid))
401394
return -EOVERFLOW;
402395
if (ia_valid & ATTR_GID &&
403-
!kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid))
396+
!vfsgid_has_fsmapping(mnt_userns, inode->i_sb->s_user_ns,
397+
attr->ia_vfsgid))
404398
return -EOVERFLOW;
405399

406400
/* Don't allow modifications of files with invalid uids or
407401
* gids unless those uids & gids are being made valid.
408402
*/
409403
if (!(ia_valid & ATTR_UID) &&
410-
!uid_valid(i_uid_into_mnt(mnt_userns, inode)))
404+
!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)))
411405
return -EOVERFLOW;
412406
if (!(ia_valid & ATTR_GID) &&
413-
!gid_valid(i_gid_into_mnt(mnt_userns, inode)))
407+
!vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode)))
414408
return -EOVERFLOW;
415409

416-
error = security_inode_setattr(dentry, attr);
410+
error = security_inode_setattr(mnt_userns, dentry, attr);
417411
if (error)
418412
return error;
419413
error = try_break_deleg(inode, delegated_inode);

fs/ext2/inode.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,14 +1679,14 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
16791679
if (error)
16801680
return error;
16811681

1682-
if (is_quota_modification(inode, iattr)) {
1682+
if (is_quota_modification(mnt_userns, inode, iattr)) {
16831683
error = dquot_initialize(inode);
16841684
if (error)
16851685
return error;
16861686
}
1687-
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
1688-
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
1689-
error = dquot_transfer(inode, iattr);
1687+
if (i_uid_needs_update(mnt_userns, iattr, inode) ||
1688+
i_gid_needs_update(mnt_userns, iattr, inode)) {
1689+
error = dquot_transfer(mnt_userns, inode, iattr);
16901690
if (error)
16911691
return error;
16921692
}

fs/ext4/inode.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5350,14 +5350,14 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
53505350
if (error)
53515351
return error;
53525352

5353-
if (is_quota_modification(inode, attr)) {
5353+
if (is_quota_modification(mnt_userns, inode, attr)) {
53545354
error = dquot_initialize(inode);
53555355
if (error)
53565356
return error;
53575357
}
53585358

5359-
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
5360-
(ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
5359+
if (i_uid_needs_update(mnt_userns, attr, inode) ||
5360+
i_gid_needs_update(mnt_userns, attr, inode)) {
53615361
handle_t *handle;
53625362

53635363
/* (user+group)*(old+new) structure, inode write (sb,
@@ -5374,7 +5374,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
53745374
* counts xattr inode references.
53755375
*/
53765376
down_read(&EXT4_I(inode)->xattr_sem);
5377-
error = dquot_transfer(inode, attr);
5377+
error = dquot_transfer(mnt_userns, inode, attr);
53785378
up_read(&EXT4_I(inode)->xattr_sem);
53795379

53805380
if (error) {
@@ -5383,10 +5383,8 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
53835383
}
53845384
/* Update corresponding info in inode so that everything is in
53855385
* one transaction */
5386-
if (attr->ia_valid & ATTR_UID)
5387-
inode->i_uid = attr->ia_uid;
5388-
if (attr->ia_valid & ATTR_GID)
5389-
inode->i_gid = attr->ia_gid;
5386+
i_uid_update(mnt_userns, attr, inode);
5387+
i_gid_update(mnt_userns, attr, inode);
53905388
error = ext4_mark_inode_dirty(handle, inode);
53915389
ext4_journal_stop(handle);
53925390
if (unlikely(error)) {

fs/f2fs/file.c

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,8 @@ static void __setattr_copy(struct user_namespace *mnt_userns,
861861
{
862862
unsigned int ia_valid = attr->ia_valid;
863863

864-
if (ia_valid & ATTR_UID)
865-
inode->i_uid = attr->ia_uid;
866-
if (ia_valid & ATTR_GID)
867-
inode->i_gid = attr->ia_gid;
864+
i_uid_update(mnt_userns, attr, inode);
865+
i_gid_update(mnt_userns, attr, inode);
868866
if (ia_valid & ATTR_ATIME)
869867
inode->i_atime = attr->ia_atime;
870868
if (ia_valid & ATTR_MTIME)
@@ -917,17 +915,15 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
917915
if (err)
918916
return err;
919917

920-
if (is_quota_modification(inode, attr)) {
918+
if (is_quota_modification(mnt_userns, inode, attr)) {
921919
err = f2fs_dquot_initialize(inode);
922920
if (err)
923921
return err;
924922
}
925-
if ((attr->ia_valid & ATTR_UID &&
926-
!uid_eq(attr->ia_uid, inode->i_uid)) ||
927-
(attr->ia_valid & ATTR_GID &&
928-
!gid_eq(attr->ia_gid, inode->i_gid))) {
923+
if (i_uid_needs_update(mnt_userns, attr, inode) ||
924+
i_gid_needs_update(mnt_userns, attr, inode)) {
929925
f2fs_lock_op(F2FS_I_SB(inode));
930-
err = dquot_transfer(inode, attr);
926+
err = dquot_transfer(mnt_userns, inode, attr);
931927
if (err) {
932928
set_sbi_flag(F2FS_I_SB(inode),
933929
SBI_QUOTA_NEED_REPAIR);
@@ -938,10 +934,8 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
938934
* update uid/gid under lock_op(), so that dquot and inode can
939935
* be updated atomically.
940936
*/
941-
if (attr->ia_valid & ATTR_UID)
942-
inode->i_uid = attr->ia_uid;
943-
if (attr->ia_valid & ATTR_GID)
944-
inode->i_gid = attr->ia_gid;
937+
i_uid_update(mnt_userns, attr, inode);
938+
i_gid_update(mnt_userns, attr, inode);
945939
f2fs_mark_inode_dirty_sync(inode, true);
946940
f2fs_unlock_op(F2FS_I_SB(inode));
947941
}

fs/f2fs/recovery.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,18 +255,18 @@ static int recover_quota_data(struct inode *inode, struct page *page)
255255

256256
memset(&attr, 0, sizeof(attr));
257257

258-
attr.ia_uid = make_kuid(inode->i_sb->s_user_ns, i_uid);
259-
attr.ia_gid = make_kgid(inode->i_sb->s_user_ns, i_gid);
258+
attr.ia_vfsuid = VFSUIDT_INIT(make_kuid(inode->i_sb->s_user_ns, i_uid));
259+
attr.ia_vfsgid = VFSGIDT_INIT(make_kgid(inode->i_sb->s_user_ns, i_gid));
260260

261-
if (!uid_eq(attr.ia_uid, inode->i_uid))
261+
if (!vfsuid_eq(attr.ia_vfsuid, i_uid_into_vfsuid(&init_user_ns, inode)))
262262
attr.ia_valid |= ATTR_UID;
263-
if (!gid_eq(attr.ia_gid, inode->i_gid))
263+
if (!vfsgid_eq(attr.ia_vfsgid, i_gid_into_vfsgid(&init_user_ns, inode)))
264264
attr.ia_valid |= ATTR_GID;
265265

266266
if (!attr.ia_valid)
267267
return 0;
268268

269-
err = dquot_transfer(inode, &attr);
269+
err = dquot_transfer(&init_user_ns, inode, &attr);
270270
if (err)
271271
set_sbi_flag(F2FS_I_SB(inode), SBI_QUOTA_NEED_REPAIR);
272272
return err;

fs/fat/file.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
9090
* out the RO attribute for checking by the security
9191
* module, just because it maps to a file mode.
9292
*/
93-
err = security_inode_setattr(file->f_path.dentry, &ia);
93+
err = security_inode_setattr(file_mnt_user_ns(file),
94+
file->f_path.dentry, &ia);
9495
if (err)
9596
goto out_unlock_inode;
9697

@@ -516,9 +517,11 @@ int fat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
516517
}
517518

518519
if (((attr->ia_valid & ATTR_UID) &&
519-
(!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
520+
(!uid_eq(from_vfsuid(mnt_userns, i_user_ns(inode), attr->ia_vfsuid),
521+
sbi->options.fs_uid))) ||
520522
((attr->ia_valid & ATTR_GID) &&
521-
(!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
523+
(!gid_eq(from_vfsgid(mnt_userns, i_user_ns(inode), attr->ia_vfsgid),
524+
sbi->options.fs_gid))) ||
522525
((attr->ia_valid & ATTR_MODE) &&
523526
(attr->ia_mode & ~FAT_VALID_MODE)))
524527
error = -EPERM;

fs/jfs/file.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,14 @@ int jfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
9595
if (rc)
9696
return rc;
9797

98-
if (is_quota_modification(inode, iattr)) {
98+
if (is_quota_modification(mnt_userns, inode, iattr)) {
9999
rc = dquot_initialize(inode);
100100
if (rc)
101101
return rc;
102102
}
103103
if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) ||
104104
(iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))) {
105-
rc = dquot_transfer(inode, iattr);
105+
rc = dquot_transfer(mnt_userns, inode, iattr);
106106
if (rc)
107107
return rc;
108108
}

fs/ocfs2/file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,7 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
11461146
if (status)
11471147
return status;
11481148

1149-
if (is_quota_modification(inode, attr)) {
1149+
if (is_quota_modification(mnt_userns, inode, attr)) {
11501150
status = dquot_initialize(inode);
11511151
if (status)
11521152
return status;

0 commit comments

Comments
 (0)