diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index ea9120a..ce12dfe 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -325,8 +325,10 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) atomic_set(&sd->s_active, 0); sd->s_name = name; - sd->s_mode = mode; + sd->s_mode = mode & ~sysfs_perms.umask; sd->s_flags = type; + sd->s_uid = sysfs_perms.uid; + sd->s_gid = sysfs_perms.gid; return sd; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 0a12eb8..2764d07 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -217,8 +218,9 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd) static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) { struct sysfs_inode_attrs *iattrs = sd->s_iattr; + struct sysfs_super_info *sb_info = sysfs_info(inode->i_sb); - inode->i_mode = sd->s_mode; + inode->i_mode = sd->s_mode & (sb_info->mode | ~0777); if (iattrs) { /* sysfs_dirent has non-default attributes * get them from persistent copy in sysfs_dirent @@ -256,6 +258,8 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) inode->i_op = &sysfs_inode_operations; set_default_inode_attr(inode, sd->s_mode); + inode->i_uid = sd->s_uid; + inode->i_gid = sd->s_gid; sysfs_refresh_inode(sd, inode); /* initialize inode according to type */ diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 2668957..8ef7eb9 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -18,10 +18,16 @@ #include #include #include +#include #include +#include #include "sysfs.h" +struct sysfs_perms sysfs_perms; + +static int sysfs_remount_fs(struct super_block * sb, int *flags, char *data); +static int sysfs_show_options(struct seq_file *seq, struct vfsmount *vfs); static struct vfsmount *sysfs_mnt; struct kmem_cache *sysfs_dir_cachep; @@ -30,6 +36,8 @@ static const struct super_operations sysfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = sysfs_evict_inode, + .remount_fs = sysfs_remount_fs, + .show_options = sysfs_show_options, }; struct sysfs_dirent sysfs_root = { @@ -40,10 +48,77 @@ struct sysfs_dirent sysfs_root = { .s_ino = 1, }; +enum { + Opt_uid, Opt_gid, Opt_umask, Opt_mode, Opt_err +}; + +static const match_table_t sysfs_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_mode, "mode=%o"}, + {Opt_err, NULL}, +}; + +static int parse_options(char *options, struct sysfs_super_info *sb_info) +{ + char *p; + int option; + substring_t args[MAX_OPT_ARGS]; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sysfs_tokens, args); + + switch (token) { + case Opt_uid: + case Opt_gid: + case Opt_umask: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + default: + break; + } + + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return -EINVAL; + sysfs_perms.uid = option; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return -EINVAL; + sysfs_perms.gid = option; + break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return -EINVAL; + sb_info->mode = option; + break; + case Opt_umask: + if (match_octal(&args[0], &option)) + return -EINVAL; + sysfs_perms.umask = option; + break; + default: + pr_err("SYSFS: Unrecognized mount option \"%s\" " + "or missing value\n", p); + return -EINVAL; + } + } + + return 0; +} + static int sysfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct dentry *root; + int err; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; @@ -51,6 +126,10 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &sysfs_ops; sb->s_time_gran = 1; + err = parse_options(data, sysfs_info(sb)); + if (err) + return err; + /* get root inode, initialize and unlock it */ mutex_lock(&sysfs_mutex); inode = sysfs_get_inode(sb, &sysfs_root); @@ -109,6 +188,7 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) info->ns[type] = kobj_ns_current(type); + info->mode = 0777; sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); if (IS_ERR(sb) || sb->s_fs_info != info) @@ -128,6 +208,28 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, return dget(sb->s_root); } +static int sysfs_remount_fs(struct super_block * sb, int *flags, char *data) +{ + return parse_options(data, sysfs_info(sb)); +} + +static int sysfs_show_options(struct seq_file *seq, struct vfsmount *vfs) +{ + struct super_block *sb = vfs->mnt_sb; + struct sysfs_super_info *info = sysfs_info(sb); + + if (sysfs_perms.uid != 0) + seq_printf(seq, ",uid=%lu", (unsigned long)sysfs_perms.uid); + if (sysfs_perms.gid != 0) + seq_printf(seq, ",gid=%lu", (unsigned long)sysfs_perms.gid); + if (info->mode != 0777) + seq_printf(seq, ",mode=%o", info->mode); + if (sysfs_perms.umask != 0) + seq_printf(seq, ",umask=%o", sysfs_perms.umask); + + return 0; +} + static void sysfs_kill_sb(struct super_block *sb) { struct sysfs_super_info *info = sysfs_info(sb); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 3d28af3..d474f1e 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -69,6 +69,8 @@ struct sysfs_dirent { unsigned int s_flags; unsigned short s_mode; + uid_t s_uid; + gid_t s_gid; ino_t s_ino; struct sysfs_inode_attrs *s_iattr; }; @@ -130,6 +132,13 @@ struct sysfs_addrm_cxt { * mount.c */ +struct sysfs_perms { + unsigned int umask; + gid_t gid; + uid_t uid; +}; +extern struct sysfs_perms sysfs_perms; + /* * Each sb is associated with a set of namespace tags (i.e. * the network namespace of the task which mounted this sysfs @@ -137,6 +146,7 @@ struct sysfs_addrm_cxt { */ struct sysfs_super_info { const void *ns[KOBJ_NS_TYPES]; + unsigned int mode; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root;