aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 20:42:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 20:42:39 -0400
commit644473e9c60c1ff4f6351fed637a6e5551e3dce7 (patch)
tree10316518bedc735a2c6552886658d69dfd9f1eb0 /fs/ext4
parentfb827ec68446c83e9e8754fa9b55aed27ecc4661 (diff)
parent4b06a81f1daee668fbd6de85557bfb36dd36078f (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull user namespace enhancements from Eric Biederman: "This is a course correction for the user namespace, so that we can reach an inexpensive, maintainable, and reasonably complete implementation. Highlights: - Config guards make it impossible to enable the user namespace and code that has not been converted to be user namespace safe. - Use of the new kuid_t type ensures the if you somehow get past the config guards the kernel will encounter type errors if you enable user namespaces and attempt to compile in code whose permission checks have not been updated to be user namespace safe. - All uids from child user namespaces are mapped into the initial user namespace before they are processed. Removing the need to add an additional check to see if the user namespace of the compared uids remains the same. - With the user namespaces compiled out the performance is as good or better than it is today. - For most operations absolutely nothing changes performance or operationally with the user namespace enabled. - The worst case performance I could come up with was timing 1 billion cache cold stat operations with the user namespace code enabled. This went from 156s to 164s on my laptop (or 156ns to 164ns per stat operation). - (uid_t)-1 and (gid_t)-1 are reserved as an internal error value. Most uid/gid setting system calls treat these value specially anyway so attempting to use -1 as a uid would likely cause entertaining failures in userspace. - If setuid is called with a uid that can not be mapped setuid fails. I have looked at sendmail, login, ssh and every other program I could think of that would call setuid and they all check for and handle the case where setuid fails. - If stat or a similar system call is called from a context in which we can not map a uid we lie and return overflowuid. The LFS experience suggests not lying and returning an error code might be better, but the historical precedent with uids is different and I can not think of anything that would break by lying about a uid we can't map. - Capabilities are localized to the current user namespace making it safe to give the initial user in a user namespace all capabilities. My git tree covers all of the modifications needed to convert the core kernel and enough changes to make a system bootable to runlevel 1." Fix up trivial conflicts due to nearby independent changes in fs/stat.c * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: (46 commits) userns: Silence silly gcc warning. cred: use correct cred accessor with regards to rcu read lock userns: Convert the move_pages, and migrate_pages permission checks to use uid_eq userns: Convert cgroup permission checks to use uid_eq userns: Convert tmpfs to use kuid and kgid where appropriate userns: Convert sysfs to use kgid/kuid where appropriate userns: Convert sysctl permission checks to use kuid and kgids. userns: Convert proc to use kuid/kgid where appropriate userns: Convert ext4 to user kuid/kgid where appropriate userns: Convert ext3 to use kuid/kgid where appropriate userns: Convert ext2 to use kuid/kgid where appropriate. userns: Convert devpts to use kuid/kgid where appropriate userns: Convert binary formats to use kuid/kgid where appropriate userns: Add negative depends on entries to avoid building code that is userns unsafe userns: signal remove unnecessary map_cred_ns userns: Teach inode_capable to understand inodes whose uids map to other namespaces. userns: Fail exec for suid and sgid binaries with ids outside our user namespace. userns: Convert stat to return values mapped from kuids and kgids userns: Convert user specfied uids and gids in chown into kuids and kgid userns: Use uid_eq gid_eq helpers when comparing kuids and kgids in the vfs ...
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/balloc.c4
-rw-r--r--fs/ext4/ext4.h4
-rw-r--r--fs/ext4/ialloc.c4
-rw-r--r--fs/ext4/inode.c34
-rw-r--r--fs/ext4/migrate.c4
-rw-r--r--fs/ext4/super.c38
6 files changed, 54 insertions, 34 deletions
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 4bbd07a6fa18..c45c41129a35 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -461,8 +461,8 @@ static int ext4_has_free_clusters(struct ext4_sb_info *sbi,
461 return 1; 461 return 1;
462 462
463 /* Hm, nope. Are (enough) root reserved clusters available? */ 463 /* Hm, nope. Are (enough) root reserved clusters available? */
464 if (sbi->s_resuid == current_fsuid() || 464 if (uid_eq(sbi->s_resuid, current_fsuid()) ||
465 ((sbi->s_resgid != 0) && in_group_p(sbi->s_resgid)) || 465 (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) && in_group_p(sbi->s_resgid)) ||
466 capable(CAP_SYS_RESOURCE) || 466 capable(CAP_SYS_RESOURCE) ||
467 (flags & EXT4_MB_USE_ROOT_BLOCKS)) { 467 (flags & EXT4_MB_USE_ROOT_BLOCKS)) {
468 468
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 0e01e90add8b..c21b1de51afb 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1153,8 +1153,8 @@ struct ext4_sb_info {
1153 unsigned int s_mount_flags; 1153 unsigned int s_mount_flags;
1154 unsigned int s_def_mount_opt; 1154 unsigned int s_def_mount_opt;
1155 ext4_fsblk_t s_sb_block; 1155 ext4_fsblk_t s_sb_block;
1156 uid_t s_resuid; 1156 kuid_t s_resuid;
1157 gid_t s_resgid; 1157 kgid_t s_resgid;
1158 unsigned short s_mount_state; 1158 unsigned short s_mount_state;
1159 unsigned short s_pad; 1159 unsigned short s_pad;
1160 int s_addr_per_block_bits; 1160 int s_addr_per_block_bits;
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 409c2ee7750a..9f9acac6c43f 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -808,8 +808,8 @@ got:
808 } 808 }
809 if (owner) { 809 if (owner) {
810 inode->i_mode = mode; 810 inode->i_mode = mode;
811 inode->i_uid = owner[0]; 811 i_uid_write(inode, owner[0]);
812 inode->i_gid = owner[1]; 812 i_gid_write(inode, owner[1]);
813 } else if (test_opt(sb, GRPID)) { 813 } else if (test_opt(sb, GRPID)) {
814 inode->i_mode = mode; 814 inode->i_mode = mode;
815 inode->i_uid = current_fsuid(); 815 inode->i_uid = current_fsuid();
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c77b0bd2c711..07eaf565fdcb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3630,6 +3630,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
3630 journal_t *journal = EXT4_SB(sb)->s_journal; 3630 journal_t *journal = EXT4_SB(sb)->s_journal;
3631 long ret; 3631 long ret;
3632 int block; 3632 int block;
3633 uid_t i_uid;
3634 gid_t i_gid;
3633 3635
3634 inode = iget_locked(sb, ino); 3636 inode = iget_locked(sb, ino);
3635 if (!inode) 3637 if (!inode)
@@ -3645,12 +3647,14 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
3645 goto bad_inode; 3647 goto bad_inode;
3646 raw_inode = ext4_raw_inode(&iloc); 3648 raw_inode = ext4_raw_inode(&iloc);
3647 inode->i_mode = le16_to_cpu(raw_inode->i_mode); 3649 inode->i_mode = le16_to_cpu(raw_inode->i_mode);
3648 inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); 3650 i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
3649 inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); 3651 i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
3650 if (!(test_opt(inode->i_sb, NO_UID32))) { 3652 if (!(test_opt(inode->i_sb, NO_UID32))) {
3651 inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; 3653 i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
3652 inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; 3654 i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
3653 } 3655 }
3656 i_uid_write(inode, i_uid);
3657 i_gid_write(inode, i_gid);
3654 set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); 3658 set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
3655 3659
3656 ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ 3660 ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
@@ -3870,6 +3874,8 @@ static int ext4_do_update_inode(handle_t *handle,
3870 struct ext4_inode_info *ei = EXT4_I(inode); 3874 struct ext4_inode_info *ei = EXT4_I(inode);
3871 struct buffer_head *bh = iloc->bh; 3875 struct buffer_head *bh = iloc->bh;
3872 int err = 0, rc, block; 3876 int err = 0, rc, block;
3877 uid_t i_uid;
3878 gid_t i_gid;
3873 3879
3874 /* For fields not not tracking in the in-memory inode, 3880 /* For fields not not tracking in the in-memory inode,
3875 * initialise them to zero for new inodes. */ 3881 * initialise them to zero for new inodes. */
@@ -3878,27 +3884,27 @@ static int ext4_do_update_inode(handle_t *handle,
3878 3884
3879 ext4_get_inode_flags(ei); 3885 ext4_get_inode_flags(ei);
3880 raw_inode->i_mode = cpu_to_le16(inode->i_mode); 3886 raw_inode->i_mode = cpu_to_le16(inode->i_mode);
3887 i_uid = i_uid_read(inode);
3888 i_gid = i_gid_read(inode);
3881 if (!(test_opt(inode->i_sb, NO_UID32))) { 3889 if (!(test_opt(inode->i_sb, NO_UID32))) {
3882 raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); 3890 raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
3883 raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid)); 3891 raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
3884/* 3892/*
3885 * Fix up interoperability with old kernels. Otherwise, old inodes get 3893 * Fix up interoperability with old kernels. Otherwise, old inodes get
3886 * re-used with the upper 16 bits of the uid/gid intact 3894 * re-used with the upper 16 bits of the uid/gid intact
3887 */ 3895 */
3888 if (!ei->i_dtime) { 3896 if (!ei->i_dtime) {
3889 raw_inode->i_uid_high = 3897 raw_inode->i_uid_high =
3890 cpu_to_le16(high_16_bits(inode->i_uid)); 3898 cpu_to_le16(high_16_bits(i_uid));
3891 raw_inode->i_gid_high = 3899 raw_inode->i_gid_high =
3892 cpu_to_le16(high_16_bits(inode->i_gid)); 3900 cpu_to_le16(high_16_bits(i_gid));
3893 } else { 3901 } else {
3894 raw_inode->i_uid_high = 0; 3902 raw_inode->i_uid_high = 0;
3895 raw_inode->i_gid_high = 0; 3903 raw_inode->i_gid_high = 0;
3896 } 3904 }
3897 } else { 3905 } else {
3898 raw_inode->i_uid_low = 3906 raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
3899 cpu_to_le16(fs_high2lowuid(inode->i_uid)); 3907 raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(i_gid));
3900 raw_inode->i_gid_low =
3901 cpu_to_le16(fs_high2lowgid(inode->i_gid));
3902 raw_inode->i_uid_high = 0; 3908 raw_inode->i_uid_high = 0;
3903 raw_inode->i_gid_high = 0; 3909 raw_inode->i_gid_high = 0;
3904 } 3910 }
@@ -4084,8 +4090,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
4084 4090
4085 if (is_quota_modification(inode, attr)) 4091 if (is_quota_modification(inode, attr))
4086 dquot_initialize(inode); 4092 dquot_initialize(inode);
4087 if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || 4093 if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
4088 (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { 4094 (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
4089 handle_t *handle; 4095 handle_t *handle;
4090 4096
4091 /* (user+group)*(old+new) structure, inode write (sb, 4097 /* (user+group)*(old+new) structure, inode write (sb,
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index f39f80f8f2c5..f1bb32ec0169 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -466,8 +466,8 @@ int ext4_ext_migrate(struct inode *inode)
466 } 466 }
467 goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) * 467 goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) *
468 EXT4_INODES_PER_GROUP(inode->i_sb)) + 1; 468 EXT4_INODES_PER_GROUP(inode->i_sb)) + 1;
469 owner[0] = inode->i_uid; 469 owner[0] = i_uid_read(inode);
470 owner[1] = inode->i_gid; 470 owner[1] = i_gid_read(inode);
471 tmp_inode = ext4_new_inode(handle, inode->i_sb->s_root->d_inode, 471 tmp_inode = ext4_new_inode(handle, inode->i_sb->s_root->d_inode,
472 S_IFREG, NULL, goal, owner); 472 S_IFREG, NULL, goal, owner);
473 if (IS_ERR(tmp_inode)) { 473 if (IS_ERR(tmp_inode)) {
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e1fb1d5de58e..436b4223df66 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1448,6 +1448,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
1448{ 1448{
1449 struct ext4_sb_info *sbi = EXT4_SB(sb); 1449 struct ext4_sb_info *sbi = EXT4_SB(sb);
1450 const struct mount_opts *m; 1450 const struct mount_opts *m;
1451 kuid_t uid;
1452 kgid_t gid;
1451 int arg = 0; 1453 int arg = 0;
1452 1454
1453#ifdef CONFIG_QUOTA 1455#ifdef CONFIG_QUOTA
@@ -1474,10 +1476,20 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
1474 "Ignoring removed %s option", opt); 1476 "Ignoring removed %s option", opt);
1475 return 1; 1477 return 1;
1476 case Opt_resuid: 1478 case Opt_resuid:
1477 sbi->s_resuid = arg; 1479 uid = make_kuid(current_user_ns(), arg);
1480 if (!uid_valid(uid)) {
1481 ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
1482 return -1;
1483 }
1484 sbi->s_resuid = uid;
1478 return 1; 1485 return 1;
1479 case Opt_resgid: 1486 case Opt_resgid:
1480 sbi->s_resgid = arg; 1487 gid = make_kgid(current_user_ns(), arg);
1488 if (!gid_valid(gid)) {
1489 ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
1490 return -1;
1491 }
1492 sbi->s_resgid = gid;
1481 return 1; 1493 return 1;
1482 case Opt_abort: 1494 case Opt_abort:
1483 sbi->s_mount_flags |= EXT4_MF_FS_ABORTED; 1495 sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
@@ -1732,12 +1744,14 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
1732 SEQ_OPTS_PRINT("%s", token2str(m->token)); 1744 SEQ_OPTS_PRINT("%s", token2str(m->token));
1733 } 1745 }
1734 1746
1735 if (nodefs || sbi->s_resuid != EXT4_DEF_RESUID || 1747 if (nodefs || !uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT4_DEF_RESUID)) ||
1736 le16_to_cpu(es->s_def_resuid) != EXT4_DEF_RESUID) 1748 le16_to_cpu(es->s_def_resuid) != EXT4_DEF_RESUID)
1737 SEQ_OPTS_PRINT("resuid=%u", sbi->s_resuid); 1749 SEQ_OPTS_PRINT("resuid=%u",
1738 if (nodefs || sbi->s_resgid != EXT4_DEF_RESGID || 1750 from_kuid_munged(&init_user_ns, sbi->s_resuid));
1751 if (nodefs || !gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT4_DEF_RESGID)) ||
1739 le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID) 1752 le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID)
1740 SEQ_OPTS_PRINT("resgid=%u", sbi->s_resgid); 1753 SEQ_OPTS_PRINT("resgid=%u",
1754 from_kgid_munged(&init_user_ns, sbi->s_resgid));
1741 def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors); 1755 def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors);
1742 if (test_opt(sb, ERRORS_RO) && def_errors != EXT4_ERRORS_RO) 1756 if (test_opt(sb, ERRORS_RO) && def_errors != EXT4_ERRORS_RO)
1743 SEQ_OPTS_PUTS("errors=remount-ro"); 1757 SEQ_OPTS_PUTS("errors=remount-ro");
@@ -2980,8 +2994,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
2980 } 2994 }
2981 sb->s_fs_info = sbi; 2995 sb->s_fs_info = sbi;
2982 sbi->s_mount_opt = 0; 2996 sbi->s_mount_opt = 0;
2983 sbi->s_resuid = EXT4_DEF_RESUID; 2997 sbi->s_resuid = make_kuid(&init_user_ns, EXT4_DEF_RESUID);
2984 sbi->s_resgid = EXT4_DEF_RESGID; 2998 sbi->s_resgid = make_kgid(&init_user_ns, EXT4_DEF_RESGID);
2985 sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS; 2999 sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
2986 sbi->s_sb_block = sb_block; 3000 sbi->s_sb_block = sb_block;
2987 if (sb->s_bdev->bd_part) 3001 if (sb->s_bdev->bd_part)
@@ -3060,8 +3074,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
3060 if (def_mount_opts & EXT4_DEFM_DISCARD) 3074 if (def_mount_opts & EXT4_DEFM_DISCARD)
3061 set_opt(sb, DISCARD); 3075 set_opt(sb, DISCARD);
3062 3076
3063 sbi->s_resuid = le16_to_cpu(es->s_def_resuid); 3077 sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));
3064 sbi->s_resgid = le16_to_cpu(es->s_def_resgid); 3078 sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));
3065 sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ; 3079 sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ;
3066 sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME; 3080 sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;
3067 sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME; 3081 sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;
@@ -4213,8 +4227,8 @@ static int ext4_unfreeze(struct super_block *sb)
4213struct ext4_mount_options { 4227struct ext4_mount_options {
4214 unsigned long s_mount_opt; 4228 unsigned long s_mount_opt;
4215 unsigned long s_mount_opt2; 4229 unsigned long s_mount_opt2;
4216 uid_t s_resuid; 4230 kuid_t s_resuid;
4217 gid_t s_resgid; 4231 kgid_t s_resgid;
4218 unsigned long s_commit_interval; 4232 unsigned long s_commit_interval;
4219 u32 s_min_batch_time, s_max_batch_time; 4233 u32 s_min_batch_time, s_max_batch_time;
4220#ifdef CONFIG_QUOTA 4234#ifdef CONFIG_QUOTA