Hello,
while trying to add a umask option to the proc filesystem I stumbled
over a somewhat related problem: the existing mount options uid and
gid were non-functional. The patch below is an attempt to fix them
and prepares the ground for my evil umask plans. :)
The first half of the reason why the uid and gid options are not
working is that proc has only a single superblock, no matter how many
times it is mounted. This is achieved with the help of get_single_sb().
Only the first mount calls fill_super(), all following mounts are
like remounts.
The second half is that proc is mounted during boot with kern_mount().
This function passes NULL as mount parameter string to
proc_fill_super().
So the single mount call where we could provide some options provides --
nothing. I don't know why parse_options() is not called from
proc_remount(), but it looks like this has been done on purpose, to
avoid changing ownership of the root inode while proc is mounted.
Is that observation correct or was it just an accident?
Anyway, the following patch changes proc to not appear like accepting
regular mount options and instead makes it parse kernel parameters. I
hope this is not too evil. They provide the semantics which I hope is
correct: changable at boot time and only at boot time.
Patch is against 2.6.11-rc2-bk10 (2.6.10 should be OK, too), compiles,
boots and works for me.
Comments welcome.
Thanks,
Rene
diff -pur linux-2.6.11-rc2-bk10/fs/proc/inode.c ll/fs/proc/inode.c
--- linux-2.6.11-rc2-bk10/fs/proc/inode.c 2005-02-01 04:17:25.000000000 +0100
+++ ll/fs/proc/inode.c 2005-02-01 03:23:51.000000000 +0100
@@ -148,22 +148,24 @@ enum {
};
static match_table_t tokens = {
- {Opt_uid, "uid=%u"},
- {Opt_gid, "gid=%u"},
+ {Opt_uid, "proc.uid=%u"},
+ {Opt_gid, "proc.gid=%u"},
{Opt_err, NULL}
};
+/*
+ * This parse_options function is different. It parses kernel parameters
+ * instead of filesystem mount options.
+ */
static int parse_options(char *options,uid_t *uid,gid_t *gid)
{
char *p;
int option;
- *uid = current->uid;
- *gid = current->gid;
if (!options)
return 1;
- while ((p = strsep(&options, ",")) != NULL) {
+ while ((p = strsep(&options, " \t")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
@@ -173,16 +175,14 @@ static int parse_options(char *options,u
switch (token) {
case Opt_uid:
if (match_int(args, &option))
- return 0;
+ continue;
*uid = option;
break;
case Opt_gid:
if (match_int(args, &option))
- return 0;
+ continue;
*gid = option;
break;
- default:
- return 0;
}
}
return 1;
@@ -231,6 +231,11 @@ out_fail:
goto out;
}
+/*
+ * Because proc is a single-superblock filesystem, proc_fill_super() is
+ * only called at boot time and never during sys_mount(). That means
+ * filesystem options can only be specified as kernel parameters.
+ */
int proc_fill_super(struct super_block *s, void *data, int silent)
{
struct inode * root_inode;
@@ -249,6 +254,8 @@ int proc_fill_super(struct super_block *
* Fixup the root inode's nlink value
*/
root_inode->i_nlink += nr_processes();
+ root_inode->i_uid = 0;
+ root_inode->i_gid = 0;
s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_no_root;
diff -pur linux-2.6.11-rc2-bk10/fs/proc/root.c ll/fs/proc/root.c
--- linux-2.6.11-rc2-bk10/fs/proc/root.c 2004-12-24 22:35:24.000000000 +0100
+++ ll/fs/proc/root.c 2005-02-01 02:25:04.000000000 +0100
@@ -7,6 +7,7 @@
*/
#include <asm/uaccess.h>
+#include <asm/setup.h>
#include <linux/errno.h>
#include <linux/time.h>
@@ -17,6 +18,8 @@
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/smp_lock.h>
+#include <linux/string.h>
+#include <linux/mount.h>
struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver;
@@ -39,13 +42,15 @@ static struct file_system_type proc_fs_t
extern int __init proc_init_inodecache(void);
void __init proc_root_init(void)
{
+ char tmp_cmdline[COMMAND_LINE_SIZE];
int err = proc_init_inodecache();
if (err)
return;
err = register_filesystem(&proc_fs_type);
if (err)
return;
- proc_mnt = kern_mount(&proc_fs_type);
+ strlcpy(tmp_cmdline, saved_command_line, COMMAND_LINE_SIZE);
+ proc_mnt = do_kern_mount("proc", 0, "proc", tmp_cmdline);
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc_fs_type);
This patch adds the umask option to the proc filesystem. It's
essentially unchanged from the previous version, except that the mount
option has to be specified as a kernel parameter now. This way we avoid
dealing with pre-existing inodes.
The umask can be used to restrict the permissions of process information
in /proc (the numerical directories and the files within them). No other
file or directory should be affected.
Al, there was a big IF in your comment regarding the previous version.
Do you have general objections against the umask feature or its
usefulness?
Thanks,
Rene
diff -pur l1/fs/proc/base.c l2/fs/proc/base.c
--- l1/fs/proc/base.c 2005-02-01 04:17:25.000000000 +0100
+++ l2/fs/proc/base.c 2005-02-01 04:26:03.000000000 +0100
@@ -180,6 +180,8 @@ static struct pid_entry tid_attr_stuff[]
#undef E
+umode_t proc_umask;
+
static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
{
struct task_struct *task = proc_task(inode);
@@ -1222,7 +1224,7 @@ static struct dentry *proc_pident_lookup
goto out;
ei = PROC_I(inode);
- inode->i_mode = p->mode;
+ inode->i_mode = p->mode & ~proc_umask;
/*
* Yes, it does not scale. And it should not. Don't add
* new entries into /proc/<tgid>/ without very good reasons.
@@ -1537,7 +1539,7 @@ struct dentry *proc_pid_lookup(struct in
put_task_struct(task);
goto out;
}
- inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+ inode->i_mode = (S_IFDIR|S_IRUGO|S_IXUGO) & ~proc_umask;
inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations;
inode->i_nlink = 3;
@@ -1592,7 +1594,7 @@ static struct dentry *proc_task_lookup(s
if (!inode)
goto out_drop_task;
- inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+ inode->i_mode = (S_IFDIR|S_IRUGO|S_IXUGO) & ~proc_umask;
inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations;
inode->i_nlink = 3;
diff -pur l1/fs/proc/inode.c l2/fs/proc/inode.c
--- l1/fs/proc/inode.c 2005-02-01 04:29:33.000000000 +0100
+++ l2/fs/proc/inode.c 2005-02-01 04:24:08.000000000 +0100
@@ -20,6 +20,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+extern umode_t proc_umask;
extern void free_proc_entry(struct proc_dir_entry *);
static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de)
@@ -144,12 +145,13 @@ static struct super_operations proc_sops
};
enum {
- Opt_uid, Opt_gid, Opt_err
+ Opt_uid, Opt_gid, Opt_umask, Opt_err
};
static match_table_t tokens = {
{Opt_uid, "proc.uid=%u"},
{Opt_gid, "proc.gid=%u"},
+ {Opt_umask, "proc.umask=%o"},
{Opt_err, NULL}
};
@@ -183,6 +185,11 @@ static int parse_options(char *options,u
continue;
*gid = option;
break;
+ case Opt_umask:
+ if (match_octal(args, &option))
+ continue;
+ proc_umask = option & 0777;
+ break;
}
}
return 1;
@@ -240,6 +247,7 @@ int proc_fill_super(struct super_block *
{
struct inode * root_inode;
+ proc_umask = 0;
s->s_flags |= MS_NODIRATIME;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
This patch adds a show_options function to the proc filesystem.
diff -pur l2/fs/proc/inode.c l3/fs/proc/inode.c
--- l2/fs/proc/inode.c 2005-02-01 04:51:23.000000000 +0100
+++ l3/fs/proc/inode.c 2005-02-01 04:51:07.000000000 +0100
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/parser.h>
#include <linux/smp_lock.h>
+#include <linux/seq_file.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -23,6 +24,9 @@
extern umode_t proc_umask;
extern void free_proc_entry(struct proc_dir_entry *);
+static int proc_root_inode_uid;
+static int proc_root_inode_gid;
+
static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de)
{
if (de)
@@ -134,6 +138,17 @@ static int proc_remount(struct super_blo
return 0;
}
+static int proc_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ if (proc_root_inode_uid != 0)
+ seq_printf(m, ",uid=%i", proc_root_inode_uid);
+ if (proc_root_inode_gid != 0)
+ seq_printf(m, ",gid=%i", proc_root_inode_gid);
+ if (proc_umask != 0)
+ seq_printf(m, ",umask=%04o", proc_umask);
+ return 0;
+}
+
static struct super_operations proc_sops = {
.alloc_inode = proc_alloc_inode,
.destroy_inode = proc_destroy_inode,
@@ -142,6 +157,7 @@ static struct super_operations proc_sops
.delete_inode = proc_delete_inode,
.statfs = simple_statfs,
.remount_fs = proc_remount,
+ .show_options = proc_show_options,
};
enum {
@@ -248,6 +264,8 @@ int proc_fill_super(struct super_block *
struct inode * root_inode;
proc_umask = 0;
+ proc_root_inode_uid = 0;
+ proc_root_inode_gid = 0;
s->s_flags |= MS_NODIRATIME;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
@@ -262,12 +280,12 @@ int proc_fill_super(struct super_block *
* Fixup the root inode's nlink value
*/
root_inode->i_nlink += nr_processes();
- root_inode->i_uid = 0;
- root_inode->i_gid = 0;
s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_no_root;
- parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
+ parse_options(data, &proc_root_inode_uid, &proc_root_inode_gid);
+ root_inode->i_uid = proc_root_inode_uid;
+ root_inode->i_gid = proc_root_inode_gid;
return 0;
out_no_root: