Skip to content

Commit 14f3db5

Browse files
author
Christian Brauner
committed
ext4: support idmapped mounts
Enable idmapped mounts for ext4. All dedicated helpers we need for this exist. So this basically just means we're passing down the user_namespace argument from the VFS methods to the relevant helpers. Let's create simple example where we idmap an ext4 filesystem: root@f2-vm:~# truncate -s 5G ext4.img root@f2-vm:~# mkfs.ext4 ./ext4.img mke2fs 1.45.5 (07-Jan-2020) Discarding device blocks: done Creating filesystem with 1310720 4k blocks and 327680 inodes Filesystem UUID: 3fd91794-c6ca-4b0f-9964-289a000919cf Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736 Allocating group tables: done Writing inode tables: done Creating journal (16384 blocks): done Writing superblocks and filesystem accounting information: done root@f2-vm:~# losetup -f --show ./ext4.img /dev/loop0 root@f2-vm:~# mount /dev/loop0 /mnt root@f2-vm:~# ls -al /mnt/ total 24 drwxr-xr-x 3 root root 4096 Oct 28 13:34 . drwxr-xr-x 30 root root 4096 Oct 28 13:22 .. drwx------ 2 root root 16384 Oct 28 13:34 lost+found # Let's create an idmapped mount at /idmapped1 where we map uid and gid # 0 to uid and gid 1000 root@f2-vm:/# ./mount-idmapped --map-mount b:0:1000:1 /mnt/ /idmapped1/ root@f2-vm:/# ls -al /idmapped1/ total 24 drwxr-xr-x 3 ubuntu ubuntu 4096 Oct 28 13:34 . drwxr-xr-x 30 root root 4096 Oct 28 13:22 .. drwx------ 2 ubuntu ubuntu 16384 Oct 28 13:34 lost+found # Let's create an idmapped mount at /idmapped2 where we map uid and gid # 0 to uid and gid 2000 root@f2-vm:/# ./mount-idmapped --map-mount b:0:2000:1 /mnt/ /idmapped2/ root@f2-vm:/# ls -al /idmapped2/ total 24 drwxr-xr-x 3 2000 2000 4096 Oct 28 13:34 . drwxr-xr-x 31 root root 4096 Oct 28 13:39 .. drwx------ 2 2000 2000 16384 Oct 28 13:34 lost+found Let's create another example where we idmap the rootfs filesystem without a mapping for uid 0 and gid 0: # Create an idmapped mount of for a full POSIX range of rootfs under # /mnt but without a mapping for uid 0 to reduce attack surface root@f2-vm:/# ./mount-idmapped --map-mount b:1:1:65536 / /mnt/ # Since we don't have a mapping for uid and gid 0 all files owned by # uid and gid 0 should show up as uid and gid 65534: root@f2-vm:/# ls -al /mnt/ total 664 drwxr-xr-x 31 nobody nogroup 4096 Oct 28 13:39 . drwxr-xr-x 31 root root 4096 Oct 28 13:39 .. lrwxrwxrwx 1 nobody nogroup 7 Aug 25 07:44 bin -> usr/bin drwxr-xr-x 4 nobody nogroup 4096 Oct 28 13:17 boot drwxr-xr-x 2 nobody nogroup 4096 Aug 25 07:48 dev drwxr-xr-x 81 nobody nogroup 4096 Oct 28 04:00 etc drwxr-xr-x 4 nobody nogroup 4096 Oct 28 04:00 home lrwxrwxrwx 1 nobody nogroup 7 Aug 25 07:44 lib -> usr/lib lrwxrwxrwx 1 nobody nogroup 9 Aug 25 07:44 lib32 -> usr/lib32 lrwxrwxrwx 1 nobody nogroup 9 Aug 25 07:44 lib64 -> usr/lib64 lrwxrwxrwx 1 nobody nogroup 10 Aug 25 07:44 libx32 -> usr/libx32 drwx------ 2 nobody nogroup 16384 Aug 25 07:47 lost+found drwxr-xr-x 2 nobody nogroup 4096 Aug 25 07:44 media drwxr-xr-x 31 nobody nogroup 4096 Oct 28 13:39 mnt drwxr-xr-x 2 nobody nogroup 4096 Aug 25 07:44 opt drwxr-xr-x 2 nobody nogroup 4096 Apr 15 2020 proc drwx--x--x 6 nobody nogroup 4096 Oct 28 13:34 root drwxr-xr-x 2 nobody nogroup 4096 Aug 25 07:46 run lrwxrwxrwx 1 nobody nogroup 8 Aug 25 07:44 sbin -> usr/sbin drwxr-xr-x 2 nobody nogroup 4096 Aug 25 07:44 srv drwxr-xr-x 2 nobody nogroup 4096 Apr 15 2020 sys drwxrwxrwt 10 nobody nogroup 4096 Oct 28 13:19 tmp drwxr-xr-x 14 nobody nogroup 4096 Oct 20 13:00 usr drwxr-xr-x 12 nobody nogroup 4096 Aug 25 07:45 var # Since we do have a mapping for uid and gid 1000 all files owned by # uid and gid 1000 should simply show up as uid and gid 1000: root@f2-vm:/# ls -al /mnt/home/ubuntu/ total 40 drwxr-xr-x 3 ubuntu ubuntu 4096 Oct 28 00:43 . drwxr-xr-x 4 nobody nogroup 4096 Oct 28 04:00 .. -rw------- 1 ubuntu ubuntu 2936 Oct 28 12:26 .bash_history -rw-r--r-- 1 ubuntu ubuntu 220 Feb 25 2020 .bash_logout -rw-r--r-- 1 ubuntu ubuntu 3771 Feb 25 2020 .bashrc -rw-r--r-- 1 ubuntu ubuntu 807 Feb 25 2020 .profile -rw-r--r-- 1 ubuntu ubuntu 0 Oct 16 16:11 .sudo_as_admin_successful -rw------- 1 ubuntu ubuntu 1144 Oct 28 00:43 .viminfo Link: https://lore.kernel.org/r/20210121131959.646623-39-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-ext4@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
1 parent 4b78993 commit 14f3db5

7 files changed

Lines changed: 46 additions & 39 deletions

File tree

fs/ext4/acl.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,7 @@ ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
246246
ext4_fc_start_update(inode);
247247

248248
if ((type == ACL_TYPE_ACCESS) && acl) {
249-
error = posix_acl_update_mode(&init_user_ns, inode, &mode,
250-
&acl);
249+
error = posix_acl_update_mode(mnt_userns, inode, &mode, &acl);
251250
if (error)
252251
goto out_stop;
253252
if (mode != inode->i_mode)

fs/ext4/ext4.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2755,18 +2755,19 @@ extern int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
27552755

27562756
/* ialloc.c */
27572757
extern int ext4_mark_inode_used(struct super_block *sb, int ino);
2758-
extern struct inode *__ext4_new_inode(handle_t *, struct inode *, umode_t,
2758+
extern struct inode *__ext4_new_inode(struct user_namespace *, handle_t *,
2759+
struct inode *, umode_t,
27592760
const struct qstr *qstr, __u32 goal,
27602761
uid_t *owner, __u32 i_flags,
27612762
int handle_type, unsigned int line_no,
27622763
int nblocks);
27632764

2764-
#define ext4_new_inode(handle, dir, mode, qstr, goal, owner, i_flags) \
2765-
__ext4_new_inode((handle), (dir), (mode), (qstr), (goal), (owner), \
2766-
i_flags, 0, 0, 0)
2767-
#define ext4_new_inode_start_handle(dir, mode, qstr, goal, owner, \
2765+
#define ext4_new_inode(handle, dir, mode, qstr, goal, owner, i_flags) \
2766+
__ext4_new_inode(&init_user_ns, (handle), (dir), (mode), (qstr), \
2767+
(goal), (owner), i_flags, 0, 0, 0)
2768+
#define ext4_new_inode_start_handle(mnt_userns, dir, mode, qstr, goal, owner, \
27682769
type, nblocks) \
2769-
__ext4_new_inode(NULL, (dir), (mode), (qstr), (goal), (owner), \
2770+
__ext4_new_inode((mnt_userns), NULL, (dir), (mode), (qstr), (goal), (owner), \
27702771
0, (type), __LINE__, (nblocks))
27712772

27722773

fs/ext4/ialloc.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,8 @@ static int ext4_xattr_credits_for_new_inode(struct inode *dir, mode_t mode,
919919
* For other inodes, search forward from the parent directory's block
920920
* group to find a free inode.
921921
*/
922-
struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
922+
struct inode *__ext4_new_inode(struct user_namespace *mnt_userns,
923+
handle_t *handle, struct inode *dir,
923924
umode_t mode, const struct qstr *qstr,
924925
__u32 goal, uid_t *owner, __u32 i_flags,
925926
int handle_type, unsigned int line_no,
@@ -969,10 +970,10 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
969970
i_gid_write(inode, owner[1]);
970971
} else if (test_opt(sb, GRPID)) {
971972
inode->i_mode = mode;
972-
inode->i_uid = current_fsuid();
973+
inode->i_uid = fsuid_into_mnt(mnt_userns);
973974
inode->i_gid = dir->i_gid;
974975
} else
975-
inode_init_owner(&init_user_ns, inode, dir, mode);
976+
inode_init_owner(mnt_userns, inode, dir, mode);
976977

977978
if (ext4_has_feature_project(sb) &&
978979
ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT))

fs/ext4/inode.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
#include <linux/fs.h>
23+
#include <linux/mount.h>
2324
#include <linux/time.h>
2425
#include <linux/highuid.h>
2526
#include <linux/pagemap.h>
@@ -5338,7 +5339,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
53385339
ATTR_GID | ATTR_TIMES_SET))))
53395340
return -EPERM;
53405341

5341-
error = setattr_prepare(&init_user_ns, dentry, attr);
5342+
error = setattr_prepare(mnt_userns, dentry, attr);
53425343
if (error)
53435344
return error;
53445345

@@ -5513,7 +5514,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
55135514
}
55145515

55155516
if (!error) {
5516-
setattr_copy(&init_user_ns, inode, attr);
5517+
setattr_copy(mnt_userns, inode, attr);
55175518
mark_inode_dirty(inode);
55185519
}
55195520

@@ -5525,7 +5526,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
55255526
ext4_orphan_del(NULL, inode);
55265527

55275528
if (!error && (ia_valid & ATTR_MODE))
5528-
rc = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
5529+
rc = posix_acl_chmod(mnt_userns, inode, inode->i_mode);
55295530

55305531
err_out:
55315532
if (error)
@@ -5572,7 +5573,7 @@ int ext4_getattr(struct user_namespace *mnt_userns, const struct path *path,
55725573
STATX_ATTR_NODUMP |
55735574
STATX_ATTR_VERITY);
55745575

5575-
generic_fillattr(&init_user_ns, inode, stat);
5576+
generic_fillattr(mnt_userns, inode, stat);
55765577
return 0;
55775578
}
55785579

@@ -5583,7 +5584,7 @@ int ext4_file_getattr(struct user_namespace *mnt_userns,
55835584
struct inode *inode = d_inode(path->dentry);
55845585
u64 delalloc_blocks;
55855586

5586-
ext4_getattr(&init_user_ns, path, stat, request_mask, query_flags);
5587+
ext4_getattr(mnt_userns, path, stat, request_mask, query_flags);
55875588

55885589
/*
55895590
* If there is inline data in the inode, the inode will normally not

fs/ext4/ioctl.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,12 @@ void ext4_reset_inode_seed(struct inode *inode)
107107
* important fields of the inodes.
108108
*
109109
* @sb: the super block of the filesystem
110+
* @mnt_userns: user namespace of the mount the inode was found from
110111
* @inode: the inode to swap with EXT4_BOOT_LOADER_INO
111112
*
112113
*/
113114
static long swap_inode_boot_loader(struct super_block *sb,
115+
struct user_namespace *mnt_userns,
114116
struct inode *inode)
115117
{
116118
handle_t *handle;
@@ -139,7 +141,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
139141
}
140142

141143
if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) ||
142-
!inode_owner_or_capable(&init_user_ns, inode) ||
144+
!inode_owner_or_capable(mnt_userns, inode) ||
143145
!capable(CAP_SYS_ADMIN)) {
144146
err = -EPERM;
145147
goto journal_err_out;
@@ -815,6 +817,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
815817
struct inode *inode = file_inode(filp);
816818
struct super_block *sb = inode->i_sb;
817819
struct ext4_inode_info *ei = EXT4_I(inode);
820+
struct user_namespace *mnt_userns = file_mnt_user_ns(filp);
818821
unsigned int flags;
819822

820823
ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
@@ -830,7 +833,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
830833
case FS_IOC_SETFLAGS: {
831834
int err;
832835

833-
if (!inode_owner_or_capable(&init_user_ns, inode))
836+
if (!inode_owner_or_capable(mnt_userns, inode))
834837
return -EACCES;
835838

836839
if (get_user(flags, (int __user *) arg))
@@ -872,7 +875,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
872875
__u32 generation;
873876
int err;
874877

875-
if (!inode_owner_or_capable(&init_user_ns, inode))
878+
if (!inode_owner_or_capable(mnt_userns, inode))
876879
return -EPERM;
877880

878881
if (ext4_has_metadata_csum(inode->i_sb)) {
@@ -1011,7 +1014,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
10111014
case EXT4_IOC_MIGRATE:
10121015
{
10131016
int err;
1014-
if (!inode_owner_or_capable(&init_user_ns, inode))
1017+
if (!inode_owner_or_capable(mnt_userns, inode))
10151018
return -EACCES;
10161019

10171020
err = mnt_want_write_file(filp);
@@ -1033,7 +1036,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
10331036
case EXT4_IOC_ALLOC_DA_BLKS:
10341037
{
10351038
int err;
1036-
if (!inode_owner_or_capable(&init_user_ns, inode))
1039+
if (!inode_owner_or_capable(mnt_userns, inode))
10371040
return -EACCES;
10381041

10391042
err = mnt_want_write_file(filp);
@@ -1052,7 +1055,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
10521055
err = mnt_want_write_file(filp);
10531056
if (err)
10541057
return err;
1055-
err = swap_inode_boot_loader(sb, inode);
1058+
err = swap_inode_boot_loader(sb, mnt_userns, inode);
10561059
mnt_drop_write_file(filp);
10571060
return err;
10581061
}
@@ -1218,7 +1221,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
12181221

12191222
case EXT4_IOC_CLEAR_ES_CACHE:
12201223
{
1221-
if (!inode_owner_or_capable(&init_user_ns, inode))
1224+
if (!inode_owner_or_capable(mnt_userns, inode))
12221225
return -EACCES;
12231226
ext4_clear_inode_es(inode);
12241227
return 0;
@@ -1264,7 +1267,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
12641267
return -EFAULT;
12651268

12661269
/* Make sure caller has proper permission */
1267-
if (!inode_owner_or_capable(&init_user_ns, inode))
1270+
if (!inode_owner_or_capable(mnt_userns, inode))
12681271
return -EACCES;
12691272

12701273
if (fa.fsx_xflags & ~EXT4_SUPPORTED_FS_XFLAGS)

fs/ext4/namei.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,8 +2610,8 @@ static int ext4_create(struct user_namespace *mnt_userns, struct inode *dir,
26102610
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
26112611
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
26122612
retry:
2613-
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
2614-
NULL, EXT4_HT_DIR, credits);
2613+
inode = ext4_new_inode_start_handle(mnt_userns, dir, mode, &dentry->d_name,
2614+
0, NULL, EXT4_HT_DIR, credits);
26152615
handle = ext4_journal_current_handle();
26162616
err = PTR_ERR(inode);
26172617
if (!IS_ERR(inode)) {
@@ -2645,8 +2645,8 @@ static int ext4_mknod(struct user_namespace *mnt_userns, struct inode *dir,
26452645
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
26462646
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
26472647
retry:
2648-
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
2649-
NULL, EXT4_HT_DIR, credits);
2648+
inode = ext4_new_inode_start_handle(mnt_userns, dir, mode, &dentry->d_name,
2649+
0, NULL, EXT4_HT_DIR, credits);
26502650
handle = ext4_journal_current_handle();
26512651
err = PTR_ERR(inode);
26522652
if (!IS_ERR(inode)) {
@@ -2677,7 +2677,7 @@ static int ext4_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
26772677
return err;
26782678

26792679
retry:
2680-
inode = ext4_new_inode_start_handle(dir, mode,
2680+
inode = ext4_new_inode_start_handle(mnt_userns, dir, mode,
26812681
NULL, 0, NULL,
26822682
EXT4_HT_DIR,
26832683
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
@@ -2792,7 +2792,7 @@ static int ext4_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
27922792
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
27932793
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
27942794
retry:
2795-
inode = ext4_new_inode_start_handle(dir, S_IFDIR | mode,
2795+
inode = ext4_new_inode_start_handle(mnt_userns, dir, S_IFDIR | mode,
27962796
&dentry->d_name,
27972797
0, NULL, EXT4_HT_DIR, credits);
27982798
handle = ext4_journal_current_handle();
@@ -3335,7 +3335,7 @@ static int ext4_symlink(struct user_namespace *mnt_userns, struct inode *dir,
33353335
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
33363336
}
33373337

3338-
inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO,
3338+
inode = ext4_new_inode_start_handle(mnt_userns, dir, S_IFLNK|S_IRWXUGO,
33393339
&dentry->d_name, 0, NULL,
33403340
EXT4_HT_DIR, credits);
33413341
handle = ext4_journal_current_handle();
@@ -3664,7 +3664,8 @@ static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
36643664
}
36653665
}
36663666

3667-
static struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent,
3667+
static struct inode *ext4_whiteout_for_rename(struct user_namespace *mnt_userns,
3668+
struct ext4_renament *ent,
36683669
int credits, handle_t **h)
36693670
{
36703671
struct inode *wh;
@@ -3678,7 +3679,8 @@ static struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent,
36783679
credits += (EXT4_MAXQUOTAS_TRANS_BLOCKS(ent->dir->i_sb) +
36793680
EXT4_XATTR_TRANS_BLOCKS + 4);
36803681
retry:
3681-
wh = ext4_new_inode_start_handle(ent->dir, S_IFCHR | WHITEOUT_MODE,
3682+
wh = ext4_new_inode_start_handle(mnt_userns, ent->dir,
3683+
S_IFCHR | WHITEOUT_MODE,
36823684
&ent->dentry->d_name, 0, NULL,
36833685
EXT4_HT_DIR, credits);
36843686

@@ -3705,9 +3707,9 @@ static struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent,
37053707
* while new_{dentry,inode) refers to the destination dentry/inode
37063708
* This comes from rename(const char *oldpath, const char *newpath)
37073709
*/
3708-
static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3709-
struct inode *new_dir, struct dentry *new_dentry,
3710-
unsigned int flags)
3710+
static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
3711+
struct dentry *old_dentry, struct inode *new_dir,
3712+
struct dentry *new_dentry, unsigned int flags)
37113713
{
37123714
handle_t *handle = NULL;
37133715
struct ext4_renament old = {
@@ -3791,7 +3793,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
37913793
goto end_rename;
37923794
}
37933795
} else {
3794-
whiteout = ext4_whiteout_for_rename(&old, credits, &handle);
3796+
whiteout = ext4_whiteout_for_rename(mnt_userns, &old, credits, &handle);
37953797
if (IS_ERR(whiteout)) {
37963798
retval = PTR_ERR(whiteout);
37973799
whiteout = NULL;
@@ -4110,7 +4112,7 @@ static int ext4_rename2(struct user_namespace *mnt_userns,
41104112
new_dir, new_dentry);
41114113
}
41124114

4113-
return ext4_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
4115+
return ext4_rename(mnt_userns, old_dir, old_dentry, new_dir, new_dentry, flags);
41144116
}
41154117

41164118
/*

fs/ext4/super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6654,7 +6654,7 @@ static struct file_system_type ext4_fs_type = {
66546654
.name = "ext4",
66556655
.mount = ext4_mount,
66566656
.kill_sb = kill_block_super,
6657-
.fs_flags = FS_REQUIRES_DEV,
6657+
.fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
66586658
};
66596659
MODULE_ALIAS_FS("ext4");
66606660

0 commit comments

Comments
 (0)