Patchwork [3/3] implement uid and gid mount options for ext4

login
register
mail settings
Submitter Ludwig Nussel
Date Aug. 2, 2012, 11:54 a.m.
Message ID <1343908443-17369-4-git-send-email-ludwig.nussel@suse.de>
Download mbox | patch
Permalink /patch/174740/
State New
Headers show

Comments

Ludwig Nussel - Aug. 2, 2012, 11:54 a.m.
Signed-off-by: Ludwig Nussel <ludwig.nussel@suse.de>
---
 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(-)

Patch

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;