2012-08-02 11:54:00

by Ludwig Nussel

[permalink] [raw]
Subject: [PATCH 0/3] implement uid and gid mount options for ext2, ext3 and ext4

When using 'real' file systems on removable storage devices such as
hard disks or usb sticks people quickly face the problem that their
Linux users have different uids on different machines. Therefore one
cannot modify or even read files created on a different machine
without running chown as root or storing everything with mode 777.
Simple file systems such as vfat don't have that problem as they
don't store file ownership information and one can pass the uid
files should belong to as mount option.

The following three patches implement the uid (and gid) mount option
for ext2, ext3 and ext4 to make them actually useful on removable
media. If a file system is mounted with the uid option all files
appear to be owned by the specified uid. Only newly created files
actually end up with that uid as owner on disk though. Ownership of
existing files cannot be changed permanently if the uid option was
specified.
Optionally a second uid (diskuid) can be specified. This one is actually
written to the file system then. Useful if the filesytem is also used on a
system that does not support the uid option.

The feature can be used in two ways. Firstly via fstab by having the
admin add 'uid=useruid' in the fs_mntops column in addition to the
'user' or 'users' option. Secondly via e.g. udisks which would
automatically pass the uid of the calling user as option.

Ludwig Nussel (3):
implement uid and gid mount options for ext2
implement uid and gid mount options for ext3
implement uid and gid mount options for ext4

Documentation/filesystems/ext2.txt | 9 ++++
Documentation/filesystems/ext3.txt | 9 ++++
Documentation/filesystems/ext4.txt | 9 ++++
fs/ext2/ext2.h | 8 +++
fs/ext2/inode.c | 48 +++++++++++++-----
fs/ext2/super.c | 95 +++++++++++++++++++++++++++++++++++-
fs/ext3/ext3.h | 8 +++
fs/ext3/inode.c | 54 ++++++++++++++------
fs/ext3/super.c | 95 +++++++++++++++++++++++++++++++++++-
fs/ext4/ext4.h | 4 ++
fs/ext4/inode.c | 52 ++++++++++++++------
fs/ext4/super.c | 87 ++++++++++++++++++++++++++++++++-
12 Dateien geändert, 433 Zeilen hinzugefügt(+), 45 Zeilen entfernt(-)

--
1.7.10.4



2012-08-02 11:54:02

by Ludwig Nussel

[permalink] [raw]
Subject: [PATCH 2/3] implement uid and gid mount options for ext3

Signed-off-by: Ludwig Nussel <[email protected]>
---
Documentation/filesystems/ext3.txt | 9 ++++
fs/ext3/ext3.h | 8 +++
fs/ext3/inode.c | 54 ++++++++++++++------
fs/ext3/super.c | 95 +++++++++++++++++++++++++++++++++++-
4 Dateien geändert, 149 Zeilen hinzugefügt(+), 17 Zeilen entfernt(-)

diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt
index 293855e..bd586d9 100644
--- a/Documentation/filesystems/ext3.txt
+++ b/Documentation/filesystems/ext3.txt
@@ -124,6 +124,15 @@ resgid=n The group ID which may use the reserved blocks.

resuid=n The user ID which may use the reserved blocks.

+uid=n[:m] Make all files appear to belong to uid n.
+ Useful for e.g. removable media with fstab
+ options 'user,uid=useruid'. The optional second
+ uid m is actually written to the file system.
+
+gid=n[:m] Make all files appear to belong to gid n.
+ The optional second gid m is actually written to
+ the file system.
+
sb=n Use alternate superblock at this location.

quota These options are ignored by the filesystem. They
diff --git a/fs/ext3/ext3.h b/fs/ext3/ext3.h
index e85ff15..401114d 100644
--- a/fs/ext3/ext3.h
+++ b/fs/ext3/ext3.h
@@ -245,6 +245,10 @@ struct ext3_mount_options {
unsigned long s_mount_opt;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid;
+ kuid_t s_diskuid;
+ kgid_t s_gid;
+ kgid_t s_diskgid;
unsigned long s_commit_interval;
#ifdef CONFIG_QUOTA
int s_jquota_fmt;
@@ -639,6 +643,10 @@ struct ext3_sb_info {
ext3_fsblk_t s_sb_block;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid; /* make all files appear to belong to this uid */
+ kuid_t s_diskuid; /* write this uid to disk (if s_uid != 0) */
+ kgid_t s_gid; /* make all files appear to belong to this gid */
+ kgid_t s_diskgid; /* write this gid to disk (if s_gid != 0) */
unsigned short s_mount_state;
unsigned short s_pad;
int s_addr_per_block_bits;
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 9a4a5c4..c1176ae 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -2915,8 +2915,14 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
- i_uid_write(inode, i_uid);
- i_gid_write(inode, i_gid);
+ if (uid_valid(EXT3_SB(sb)->s_uid))
+ inode->i_uid = EXT3_SB(sb)->s_uid;
+ else
+ i_uid_write(inode, i_uid);
+ if (gid_valid(EXT3_SB(sb)->s_gid))
+ inode->i_gid = EXT3_SB(sb)->s_gid;
+ else
+ i_gid_write(inode, i_gid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
inode->i_size = le32_to_cpu(raw_inode->i_size);
inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);
@@ -3074,6 +3080,10 @@ static int ext3_do_update_inode(handle_t *handle,
int err = 0, rc, block;
uid_t i_uid;
gid_t i_gid;
+ __le16 uid_low;
+ __le16 gid_low;
+ __le16 uid_high;
+ __le16 gid_high;

again:
/* we can't allow multiple procs in here at once, its a bit racey */
@@ -3088,30 +3098,42 @@ again:
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
i_uid = i_uid_read(inode);
i_gid = i_gid_read(inode);
+ if (uid_valid(EXT3_SB(inode->i_sb)->s_uid))
+ i_uid = from_kuid(&init_user_ns, EXT3_SB(inode->i_sb)->s_diskuid);
+ if (gid_valid(EXT3_SB(inode->i_sb)->s_gid))
+ i_gid = from_kgid(&init_user_ns, EXT3_SB(inode->i_sb)->s_diskgid);
if(!(test_opt(inode->i_sb, NO_UID32))) {
- raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
- raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
+ uid_low = cpu_to_le16(low_16_bits(i_uid));
+ gid_low = cpu_to_le16(low_16_bits(i_gid));
/*
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
if(!ei->i_dtime) {
- raw_inode->i_uid_high =
- cpu_to_le16(high_16_bits(i_uid));
- raw_inode->i_gid_high =
- cpu_to_le16(high_16_bits(i_gid));
+ uid_high = cpu_to_le16(high_16_bits(i_uid));
+ gid_high = cpu_to_le16(high_16_bits(i_gid));
} else {
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_high = 0;
+ gid_high = 0;
}
} else {
- raw_inode->i_uid_low =
- cpu_to_le16(fs_high2lowuid(i_uid));
- raw_inode->i_gid_low =
- cpu_to_le16(fs_high2lowgid(i_gid));
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
+ gid_low = cpu_to_le16(fs_high2lowgid(i_gid));
+ uid_high = 0;
+ gid_high = 0;
+ }
+ /* don't mangle uid/gid of existing files if override is active */
+ if (!uid_valid(EXT3_SB(inode->i_sb)->s_uid) ||
+ ext3_test_inode_state(inode, EXT3_STATE_NEW)) {
+ raw_inode->i_uid_high = uid_high;
+ raw_inode->i_uid_low = uid_low;
}
+ if (!gid_valid(EXT3_SB(inode->i_sb)->s_gid) ||
+ ext3_test_inode_state(inode, EXT3_STATE_NEW)) {
+ raw_inode->i_gid_high = gid_high;
+ raw_inode->i_gid_low = gid_low;
+ }
+
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
raw_inode->i_size = cpu_to_le32(ei->i_disksize);
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc..730c532 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -627,6 +627,24 @@ static int ext3_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",resgid=%u",
from_kgid_munged(&init_user_ns, sbi->s_resgid));
}
+ if (uid_valid(sbi->s_uid)) {
+ if (!uid_eq(sbi->s_uid, sbi->s_diskuid))
+ seq_printf(seq, ",uid=%u:%u",
+ from_kuid_munged(&init_user_ns, sbi->s_uid),
+ from_kuid_munged(&init_user_ns, sbi->s_diskuid));
+ else
+ seq_printf(seq, ",uid=%u",
+ from_kuid_munged(&init_user_ns, sbi->s_uid));
+ }
+ if (gid_valid(sbi->s_gid)) {
+ if (!gid_eq(sbi->s_gid, sbi->s_diskgid))
+ seq_printf(seq, ",gid=%u:%u",
+ from_kgid_munged(&init_user_ns, sbi->s_gid),
+ from_kgid_munged(&init_user_ns, sbi->s_diskgid));
+ else
+ seq_printf(seq, ",gid=%u",
+ from_kgid_munged(&init_user_ns, sbi->s_gid));
+ }
if (test_opt(sb, ERRORS_RO)) {
int def_errors = le16_to_cpu(es->s_errors);

@@ -822,7 +840,8 @@ enum {
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
Opt_noquota, Opt_ignore, Opt_barrier, Opt_nobarrier, Opt_err,
- Opt_resize, Opt_usrquota, Opt_grpquota
+ Opt_resize, Opt_usrquota, Opt_grpquota,
+ Opt_uid, Opt_diskuid, Opt_gid, Opt_diskgid
};

static const match_table_t tokens = {
@@ -879,6 +898,10 @@ static const match_table_t tokens = {
{Opt_barrier, "barrier"},
{Opt_nobarrier, "nobarrier"},
{Opt_resize, "resize"},
+ {Opt_uid, "uid=%u"},
+ {Opt_diskuid, "uid=%u:%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_diskgid, "gid=%u:%u"},
{Opt_err, NULL},
};

@@ -1282,6 +1305,64 @@ set_qf_format:
ext3_msg(sb, KERN_WARNING,
"warning: ignoring deprecated bh option");
break;
+ case Opt_uid:
+ if (match_int(&args[0], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_uid = sbi->s_diskuid = uid;
+ break;
+ case Opt_diskuid:
+ if (match_int(&args[0], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_uid = uid;
+
+ if (match_int(&args[1], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_diskuid = uid;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_gid = sbi->s_diskgid = gid;
+ break;
+ case Opt_diskgid:
+ if (match_int(&args[0], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_gid = gid;
+
+ if (match_int(&args[1], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext3_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_diskgid = gid;
+ break;
default:
ext3_msg(sb, KERN_ERR,
"error: unrecognized mount option \"%s\" "
@@ -1668,6 +1749,10 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
sbi->s_mount_opt = 0;
sbi->s_resuid = make_kuid(&init_user_ns, EXT3_DEF_RESUID);
sbi->s_resgid = make_kgid(&init_user_ns, EXT3_DEF_RESGID);
+ sbi->s_uid = INVALID_UID;
+ sbi->s_gid = INVALID_GID;
+ sbi->s_diskuid = INVALID_UID;
+ sbi->s_diskgid = INVALID_GID;
sbi->s_sb_block = sb_block;

blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE);
@@ -2611,6 +2696,10 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
old_opts.s_mount_opt = sbi->s_mount_opt;
old_opts.s_resuid = sbi->s_resuid;
old_opts.s_resgid = sbi->s_resgid;
+ old_opts.s_uid = sbi->s_uid;
+ old_opts.s_diskuid = sbi->s_diskuid;
+ old_opts.s_gid = sbi->s_gid;
+ old_opts.s_diskgid = sbi->s_diskgid;
old_opts.s_commit_interval = sbi->s_commit_interval;
#ifdef CONFIG_QUOTA
old_opts.s_jquota_fmt = sbi->s_jquota_fmt;
@@ -2722,6 +2811,10 @@ restore_opts:
sbi->s_mount_opt = old_opts.s_mount_opt;
sbi->s_resuid = old_opts.s_resuid;
sbi->s_resgid = old_opts.s_resgid;
+ sbi->s_uid = old_opts.s_uid;
+ sbi->s_diskuid = old_opts.s_diskuid;
+ sbi->s_gid = old_opts.s_gid;
+ sbi->s_diskgid = old_opts.s_diskgid;
sbi->s_commit_interval = old_opts.s_commit_interval;
#ifdef CONFIG_QUOTA
sbi->s_jquota_fmt = old_opts.s_jquota_fmt;
--
1.7.10.4


2012-08-02 11:54:08

by Ludwig Nussel

[permalink] [raw]
Subject: [PATCH 1/3] implement uid and gid mount options for ext2

Signed-off-by: Ludwig Nussel <[email protected]>
---
Documentation/filesystems/ext2.txt | 9 ++++
fs/ext2/ext2.h | 8 +++
fs/ext2/inode.c | 48 +++++++++++++-----
fs/ext2/super.c | 95 +++++++++++++++++++++++++++++++++++-
4 Dateien geändert, 147 Zeilen hinzugefügt(+), 13 Zeilen entfernt(-)

diff --git a/Documentation/filesystems/ext2.txt b/Documentation/filesystems/ext2.txt
index 67639f9..fcc1002 100644
--- a/Documentation/filesystems/ext2.txt
+++ b/Documentation/filesystems/ext2.txt
@@ -42,6 +42,15 @@ orlov (*) Use the Orlov block allocator.
resuid=n The user ID which may use the reserved blocks.
resgid=n The group ID which may use the reserved blocks.

+uid=n[:m] Make all files appear to belong to uid n.
+ Useful for e.g. removable media with fstab
+ options 'user,uid=useruid'. The optional second
+ uid m is actually written to the file system.
+
+gid=n[:m] Make all files appear to belong to gid n.
+ The optional second gid m is actually written to
+ the file system.
+
sb=n Use alternate superblock at this location.

user_xattr Enable "user." POSIX Extended Attributes
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index d9a17d0..160d5d8 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -84,6 +84,10 @@ struct ext2_sb_info {
unsigned long s_sb_block;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid; /* make all files appear to belong to this uid */
+ kuid_t s_diskuid; /* write this uid to disk (if s_uid != 0) */
+ kgid_t s_gid; /* make all files appear to belong to this gid */
+ kgid_t s_diskgid; /* write this gid to disk (if s_gid != 0) */
unsigned short s_mount_state;
unsigned short s_pad;
int s_addr_per_block_bits;
@@ -639,6 +643,10 @@ struct ext2_mount_options {
unsigned long s_mount_opt;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid;
+ kuid_t s_diskuid;
+ kgid_t s_gid;
+ kgid_t s_diskgid;
};

/*
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 6363ac6..b2320e4 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1321,8 +1321,14 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
- i_uid_write(inode, i_uid);
- i_gid_write(inode, i_gid);
+ if (uid_valid(EXT2_SB(sb)->s_uid))
+ inode->i_uid = EXT2_SB(sb)->s_uid;
+ else
+ i_uid_write(inode, i_uid);
+ if (gid_valid(EXT2_SB(sb)->s_gid))
+ inode->i_gid = EXT2_SB(sb)->s_gid;
+ else
+ i_gid_write(inode, i_gid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
inode->i_size = le32_to_cpu(raw_inode->i_size);
inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);
@@ -1426,6 +1432,10 @@ static int __ext2_write_inode(struct inode *inode, int do_sync)
struct ext2_inode * raw_inode = ext2_get_inode(sb, ino, &bh);
int n;
int err = 0;
+ __le16 uid_low;
+ __le16 gid_low;
+ __le16 uid_high;
+ __le16 gid_high;

if (IS_ERR(raw_inode))
return -EIO;
@@ -1437,26 +1447,40 @@ static int __ext2_write_inode(struct inode *inode, int do_sync)

ext2_get_inode_flags(ei);
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
+ if (uid_valid(EXT2_SB(sb)->s_uid))
+ uid = from_kuid(&init_user_ns, EXT2_SB(sb)->s_diskuid);
+ if (gid_valid(EXT2_SB(sb)->s_gid))
+ gid = from_kgid(&init_user_ns, EXT2_SB(sb)->s_diskgid);
if (!(test_opt(sb, NO_UID32))) {
- raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid));
- raw_inode->i_gid_low = cpu_to_le16(low_16_bits(gid));
+ uid_low = cpu_to_le16(low_16_bits(uid));
+ gid_low = cpu_to_le16(low_16_bits(gid));
/*
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
if (!ei->i_dtime) {
- raw_inode->i_uid_high = cpu_to_le16(high_16_bits(uid));
- raw_inode->i_gid_high = cpu_to_le16(high_16_bits(gid));
+ uid_high = cpu_to_le16(high_16_bits(uid));
+ gid_high = cpu_to_le16(high_16_bits(gid));
} else {
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_high = 0;
+ gid_high = 0;
}
} else {
- raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(uid));
- raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(gid));
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_low = cpu_to_le16(fs_high2lowuid(uid));
+ gid_low = cpu_to_le16(fs_high2lowgid(gid));
+ uid_high = 0;
+ gid_high = 0;
+ }
+ /* don't mangle uid/gid of existing files if override is active */
+ if (!uid_valid(EXT2_SB(sb)->s_uid) || ei->i_state & EXT2_STATE_NEW) {
+ raw_inode->i_uid_high = uid_high;
+ raw_inode->i_uid_low = uid_low;
}
+ if (!gid_valid(EXT2_SB(sb)->s_gid) || ei->i_state & EXT2_STATE_NEW) {
+ raw_inode->i_gid_high = gid_high;
+ raw_inode->i_gid_low = gid_low;
+ }
+
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
raw_inode->i_size = cpu_to_le32(inode->i_size);
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index af74d9e..0f91dfb 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -237,6 +237,24 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",resgid=%u",
from_kgid_munged(&init_user_ns, sbi->s_resgid));
}
+ if (uid_valid(sbi->s_uid)) {
+ if (!uid_eq(sbi->s_uid, sbi->s_diskuid))
+ seq_printf(seq, ",uid=%u:%u",
+ from_kuid_munged(&init_user_ns, sbi->s_uid),
+ from_kuid_munged(&init_user_ns, sbi->s_diskuid));
+ else
+ seq_printf(seq, ",uid=%u",
+ from_kuid_munged(&init_user_ns, sbi->s_uid));
+ }
+ if (gid_valid(sbi->s_gid)) {
+ if (!gid_eq(sbi->s_gid, sbi->s_diskgid))
+ seq_printf(seq, ",gid=%u:%u",
+ from_kgid_munged(&init_user_ns, sbi->s_gid),
+ from_kgid_munged(&init_user_ns, sbi->s_diskgid));
+ else
+ seq_printf(seq, ",gid=%u",
+ from_kgid_munged(&init_user_ns, sbi->s_gid));
+ }
if (test_opt(sb, ERRORS_RO)) {
int def_errors = le16_to_cpu(es->s_errors);

@@ -390,7 +408,8 @@ enum {
Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,
Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota,
- Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation
+ Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation,
+ Opt_uid, Opt_diskuid, Opt_gid, Opt_diskgid
};

static const match_table_t tokens = {
@@ -424,6 +443,10 @@ static const match_table_t tokens = {
{Opt_usrquota, "usrquota"},
{Opt_reservation, "reservation"},
{Opt_noreservation, "noreservation"},
+ {Opt_uid, "uid=%u"},
+ {Opt_diskuid, "uid=%u:%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_diskgid, "gid=%u:%u"},
{Opt_err, NULL}
};

@@ -578,6 +601,64 @@ static int parse_options(char *options, struct super_block *sb)
clear_opt(sbi->s_mount_opt, RESERVATION);
ext2_msg(sb, KERN_INFO, "reservations OFF");
break;
+ case Opt_uid:
+ if (match_int(&args[0], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_uid = sbi->s_diskuid = uid;
+ break;
+ case Opt_diskuid:
+ if (match_int(&args[0], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_uid = uid;
+
+ if (match_int(&args[1], &option))
+ return 0;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid uid value %d", option);
+ return -1;
+ }
+ sbi->s_diskuid = uid;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_gid = sbi->s_diskgid = gid;
+ break;
+ case Opt_diskgid:
+ if (match_int(&args[0], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_gid = gid;
+
+ if (match_int(&args[1], &option))
+ return 0;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid)) {
+ ext2_msg(sb, KERN_ERR, "Invalid gid value %d", option);
+ return -1;
+ }
+ sbi->s_diskgid = gid;
+ break;
case Opt_ignore:
break;
default:
@@ -853,6 +934,10 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)

sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));
sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));
+ sbi->s_uid = INVALID_UID;
+ sbi->s_gid = INVALID_GID;
+ sbi->s_diskuid = INVALID_UID;
+ sbi->s_diskgid = INVALID_GID;

set_opt(sbi->s_mount_opt, RESERVATION);

@@ -1256,6 +1341,10 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
old_opts.s_mount_opt = sbi->s_mount_opt;
old_opts.s_resuid = sbi->s_resuid;
old_opts.s_resgid = sbi->s_resgid;
+ old_opts.s_uid = sbi->s_uid;
+ old_opts.s_diskuid = sbi->s_diskuid;
+ old_opts.s_gid = sbi->s_gid;
+ old_opts.s_diskgid = sbi->s_diskgid;

/*
* Allow the "check" option to be passed as a remount option.
@@ -1342,6 +1431,10 @@ restore_opts:
sbi->s_mount_opt = old_opts.s_mount_opt;
sbi->s_resuid = old_opts.s_resuid;
sbi->s_resgid = old_opts.s_resgid;
+ sbi->s_uid = old_opts.s_uid;
+ sbi->s_diskuid = old_opts.s_diskuid;
+ sbi->s_gid = old_opts.s_gid;
+ sbi->s_diskgid = old_opts.s_diskgid;
sb->s_flags = old_sb_flags;
spin_unlock(&sbi->s_lock);
return err;
--
1.7.10.4

2012-08-02 11:54:09

by Ludwig Nussel

[permalink] [raw]
Subject: [PATCH 3/3] implement uid and gid mount options for ext4

Signed-off-by: Ludwig Nussel <[email protected]>
---
Documentation/filesystems/ext4.txt | 9 ++++
fs/ext4/ext4.h | 4 ++
fs/ext4/inode.c | 52 +++++++++++++++------
fs/ext4/super.c | 87 +++++++++++++++++++++++++++++++++++-
4 Dateien geändert, 137 Zeilen hinzugefügt(+), 15 Zeilen entfernt(-)

diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index 1b7f9ac..b388ab5 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -245,6 +245,15 @@ resgid=n The group ID which may use the reserved blocks.

resuid=n The user ID which may use the reserved blocks.

+uid=n[:m] Make all files appear to belong to uid n.
+ Useful for e.g. removable media with fstab
+ options 'user,uid=useruid'. The optional second
+ uid m is actually written to the file system.
+
+gid=n[:m] Make all files appear to belong to gid n.
+ The optional second gid m is actually written to
+ the file system.
+
sb=n Use alternate superblock at this location.

quota These options are ignored by the filesystem. They
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index c3411d4..070e3ad 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1177,6 +1177,10 @@ struct ext4_sb_info {
ext4_fsblk_t s_sb_block;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid; /* make all files appear to belong to this uid */
+ kuid_t s_diskuid; /* write this uid to disk (if s_uid != 0) */
+ kgid_t s_gid; /* make all files appear to belong to this gid */
+ kgid_t s_diskgid; /* write this gid to disk (if s_gid != 0) */
unsigned short s_mount_state;
unsigned short s_pad;
int s_addr_per_block_bits;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 6324f74..b02ec15 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3838,8 +3838,14 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
- i_uid_write(inode, i_uid);
- i_gid_write(inode, i_gid);
+ if (uid_valid(EXT4_SB(sb)->s_uid))
+ inode->i_uid = EXT4_SB(sb)->s_uid;
+ else
+ i_uid_write(inode, i_uid);
+ if (gid_valid(EXT4_SB(sb)->s_gid))
+ inode->i_gid = EXT4_SB(sb)->s_gid;
+ else
+ i_gid_write(inode, i_gid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));

ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
@@ -4054,6 +4060,10 @@ static int ext4_do_update_inode(handle_t *handle,
int err = 0, rc, block;
uid_t i_uid;
gid_t i_gid;
+ __le16 uid_low;
+ __le16 gid_low;
+ __le16 uid_high;
+ __le16 gid_high;

/* For fields not not tracking in the in-memory inode,
* initialise them to zero for new inodes. */
@@ -4064,28 +4074,42 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
i_uid = i_uid_read(inode);
i_gid = i_gid_read(inode);
+ if (uid_valid(EXT4_SB(inode->i_sb)->s_uid))
+ i_uid = from_kuid(&init_user_ns, EXT4_SB(inode->i_sb)->s_diskuid);
+ if (gid_valid(EXT4_SB(inode->i_sb)->s_gid))
+ i_gid = from_kgid(&init_user_ns, EXT4_SB(inode->i_sb)->s_diskgid);
if (!(test_opt(inode->i_sb, NO_UID32))) {
- raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
- raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
+ uid_low = cpu_to_le16(low_16_bits(i_uid));
+ gid_low = cpu_to_le16(low_16_bits(i_gid));
/*
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
if (!ei->i_dtime) {
- raw_inode->i_uid_high =
- cpu_to_le16(high_16_bits(i_uid));
- raw_inode->i_gid_high =
- cpu_to_le16(high_16_bits(i_gid));
+ uid_high = cpu_to_le16(high_16_bits(i_uid));
+ gid_high = cpu_to_le16(high_16_bits(i_gid));
} else {
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_high = 0;
+ gid_high = 0;
}
} else {
- raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
- raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(i_gid));
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
+ uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
+ gid_low = cpu_to_le16(fs_high2lowgid(i_gid));
+ uid_high = 0;
+ gid_high = 0;
+ }
+ /* don't mangle uid/gid of existing files if override is active */
+ if (!uid_valid(EXT4_SB(inode->i_sb)->s_uid) ||
+ ext4_test_inode_state(inode, EXT4_STATE_NEW)) {
+ raw_inode->i_uid_high = uid_high;
+ raw_inode->i_uid_low = uid_low;
}
+ if (!gid_valid(EXT4_SB(inode->i_sb)->s_gid) ||
+ ext4_test_inode_state(inode, EXT4_STATE_NEW)) {
+ raw_inode->i_gid_high = gid_high;
+ raw_inode->i_gid_low = gid_low;
+ }
+
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);

EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d76ec82..927c020 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1229,6 +1229,7 @@ enum {
Opt_inode_readahead_blks, Opt_journal_ioprio,
Opt_dioread_nolock, Opt_dioread_lock,
Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
+ Opt_uid, Opt_diskuid, Opt_gid, Opt_diskgid,
};

static const match_table_t tokens = {
@@ -1307,6 +1308,10 @@ static const match_table_t tokens = {
{Opt_removed, "reservation"}, /* mount option from ext2/3 */
{Opt_removed, "noreservation"}, /* mount option from ext2/3 */
{Opt_removed, "journal=%u"}, /* mount option from ext2/3 */
+ {Opt_uid, "uid=%u"},
+ {Opt_diskuid, "uid=%u:%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_diskgid, "gid=%u:%u"},
{Opt_err, NULL},
};

@@ -1553,6 +1558,54 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
return -1;
*journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
return 1;
+ case Opt_uid:
+ uid = make_kuid(current_user_ns(), arg);
+ if (!uid_valid(uid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
+ return -1;
+ }
+ sbi->s_uid = sbi->s_diskuid = uid;
+ return 1;
+ case Opt_diskuid:
+ uid = make_kuid(current_user_ns(), arg);
+ if (!uid_valid(uid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
+ return -1;
+ }
+ sbi->s_uid = uid;
+ if (match_int(&args[1], &arg))
+ return -1;
+ uid = make_kuid(current_user_ns(), arg);
+ if (!uid_valid(uid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
+ return -1;
+ }
+ sbi->s_diskuid = uid;
+ return 1;
+ case Opt_gid:
+ gid = make_kgid(current_user_ns(), arg);
+ if (!gid_valid(gid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+ return -1;
+ }
+ sbi->s_gid = sbi->s_diskgid = gid;
+ return 1;
+ case Opt_diskgid:
+ gid = make_kgid(current_user_ns(), arg);
+ if (!gid_valid(gid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+ return -1;
+ }
+ sbi->s_gid = gid;
+ if (match_int(&args[1], &arg))
+ return -1;
+ gid = make_kgid(current_user_ns(), arg);
+ if (!gid_valid(gid)) {
+ ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+ return -1;
+ }
+ sbi->s_diskgid = gid;
+ return 1;
}

for (m = ext4_mount_opts; m->token != Opt_err; m++) {
@@ -1768,7 +1821,7 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
char sep = nodefs ? '\n' : ',';

#define SEQ_OPTS_PUTS(str) seq_printf(seq, "%c" str, sep)
-#define SEQ_OPTS_PRINT(str, arg) seq_printf(seq, "%c" str, sep, arg)
+#define SEQ_OPTS_PRINT(str, args...) seq_printf(seq, "%c" str, sep, ##args)

if (sbi->s_sb_block != 1)
SEQ_OPTS_PRINT("sb=%llu", sbi->s_sb_block);
@@ -1795,6 +1848,22 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID)
SEQ_OPTS_PRINT("resgid=%u",
from_kgid_munged(&init_user_ns, sbi->s_resgid));
+ if (uid_valid(sbi->s_uid)) {
+ if (!uid_eq(sbi->s_uid, sbi->s_diskuid))
+ SEQ_OPTS_PRINT("uid=%u:%u",
+ from_kuid_munged(&init_user_ns, sbi->s_uid),
+ from_kuid_munged(&init_user_ns, sbi->s_diskuid));
+ else
+ SEQ_OPTS_PRINT("uid=%u", from_kuid_munged(&init_user_ns, sbi->s_uid));
+ }
+ if (gid_valid(sbi->s_gid)) {
+ if (!gid_eq(sbi->s_gid, sbi->s_diskgid))
+ SEQ_OPTS_PRINT("gid=%u:%u",
+ from_kgid_munged(&init_user_ns, sbi->s_gid),
+ from_kgid_munged(&init_user_ns, sbi->s_diskgid));
+ else
+ SEQ_OPTS_PRINT("gid=%u", from_kgid_munged(&init_user_ns, sbi->s_gid));
+ }
def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors);
if (test_opt(sb, ERRORS_RO) && def_errors != EXT4_ERRORS_RO)
SEQ_OPTS_PUTS("errors=remount-ro");
@@ -3243,6 +3312,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_mount_opt = 0;
sbi->s_resuid = make_kuid(&init_user_ns, EXT4_DEF_RESUID);
sbi->s_resgid = make_kgid(&init_user_ns, EXT4_DEF_RESGID);
+ sbi->s_uid = INVALID_UID;
+ sbi->s_gid = INVALID_GID;
+ sbi->s_diskuid = INVALID_UID;
+ sbi->s_diskgid = INVALID_GID;
sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
sbi->s_sb_block = sb_block;
if (sb->s_bdev->bd_part)
@@ -4535,6 +4608,10 @@ struct ext4_mount_options {
unsigned long s_mount_opt2;
kuid_t s_resuid;
kgid_t s_resgid;
+ kuid_t s_uid;
+ kuid_t s_diskuid;
+ kgid_t s_gid;
+ kgid_t s_diskgid;
unsigned long s_commit_interval;
u32 s_min_batch_time, s_max_batch_time;
#ifdef CONFIG_QUOTA
@@ -4565,6 +4642,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
old_opts.s_mount_opt2 = sbi->s_mount_opt2;
old_opts.s_resuid = sbi->s_resuid;
old_opts.s_resgid = sbi->s_resgid;
+ old_opts.s_uid = sbi->s_uid;
+ old_opts.s_diskuid = sbi->s_diskuid;
+ old_opts.s_gid = sbi->s_gid;
+ old_opts.s_diskgid = sbi->s_diskgid;
old_opts.s_commit_interval = sbi->s_commit_interval;
old_opts.s_min_batch_time = sbi->s_min_batch_time;
old_opts.s_max_batch_time = sbi->s_max_batch_time;
@@ -4732,6 +4813,10 @@ restore_opts:
sbi->s_mount_opt2 = old_opts.s_mount_opt2;
sbi->s_resuid = old_opts.s_resuid;
sbi->s_resgid = old_opts.s_resgid;
+ sbi->s_uid = old_opts.s_uid;
+ sbi->s_diskuid = old_opts.s_diskuid;
+ sbi->s_gid = old_opts.s_gid;
+ sbi->s_diskgid = old_opts.s_diskgid;
sbi->s_commit_interval = old_opts.s_commit_interval;
sbi->s_min_batch_time = old_opts.s_min_batch_time;
sbi->s_max_batch_time = old_opts.s_max_batch_time;
--
1.7.10.4

2012-08-02 12:02:27

by Alan

[permalink] [raw]
Subject: Re: [PATCH 1/3] implement uid and gid mount options for ext2

> +uid=n[:m] Make all files appear to belong to uid n.
> + Useful for e.g. removable media with fstab
> + options 'user,uid=useruid'. The optional second
> + uid m is actually written to the file system.

So what happens if I insert a USB key containing a device node to the
hard disk ? Why is this functionality useful in the current form ?

(ie I think you need an actual rational security model first)


Also why is this at the per fs level duplicating stuff each time rather
than at the vfs level - this seems to be vfs level functionality.

> +
> +gid=n[:m] Make all files appear to belong to gid n.
> + The optional second gid m is actually written to
> + the file system.

Your documentation seems only half completed ?

Alan

2012-08-02 13:00:55

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH 1/3] implement uid and gid mount options for ext2

Alan Cox <[email protected]> writes:

>> +uid=n[:m] Make all files appear to belong to uid n.
>> + Useful for e.g. removable media with fstab
>> + options 'user,uid=useruid'. The optional second
>> + uid m is actually written to the file system.
>
> So what happens if I insert a USB key containing a device node to the
> hard disk ? Why is this functionality useful in the current form ?
>
> (ie I think you need an actual rational security model first)

> Also why is this at the per fs level duplicating stuff each time rather
> than at the vfs level - this seems to be vfs level functionality.

Even more fun there is essentially a generic implementation in the user
namespaces. What needs to be implemented to support this is support for
mounting in a non-default user namespace, and then all of the mapping
functionality is generic. Which I presume will imply MS_NODEV.

Eric