Merge tag 'for_v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
[linux-2.6-microblaze.git] / fs / proc / root.c
index cdbe929..5e444d4 100644 (file)
 struct proc_fs_context {
        struct pid_namespace    *pid_ns;
        unsigned int            mask;
-       int                     hidepid;
+       enum proc_hidepid       hidepid;
        int                     gid;
+       enum proc_pidonly       pidonly;
 };
 
 enum proc_param {
        Opt_gid,
        Opt_hidepid,
+       Opt_subset,
 };
 
 static const struct fs_parameter_spec proc_fs_parameters[] = {
        fsparam_u32("gid",      Opt_gid),
-       fsparam_u32("hidepid",  Opt_hidepid),
+       fsparam_string("hidepid",       Opt_hidepid),
+       fsparam_string("subset",        Opt_subset),
        {}
 };
 
+static inline int valid_hidepid(unsigned int value)
+{
+       return (value == HIDEPID_OFF ||
+               value == HIDEPID_NO_ACCESS ||
+               value == HIDEPID_INVISIBLE ||
+               value == HIDEPID_NOT_PTRACEABLE);
+}
+
+static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param)
+{
+       struct proc_fs_context *ctx = fc->fs_private;
+       struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid);
+       struct fs_parse_result result;
+       int base = (unsigned long)hidepid_u32_spec.data;
+
+       if (param->type != fs_value_is_string)
+               return invalf(fc, "proc: unexpected type of hidepid value\n");
+
+       if (!kstrtouint(param->string, base, &result.uint_32)) {
+               if (!valid_hidepid(result.uint_32))
+                       return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
+               ctx->hidepid = result.uint_32;
+               return 0;
+       }
+
+       if (!strcmp(param->string, "off"))
+               ctx->hidepid = HIDEPID_OFF;
+       else if (!strcmp(param->string, "noaccess"))
+               ctx->hidepid = HIDEPID_NO_ACCESS;
+       else if (!strcmp(param->string, "invisible"))
+               ctx->hidepid = HIDEPID_INVISIBLE;
+       else if (!strcmp(param->string, "ptraceable"))
+               ctx->hidepid = HIDEPID_NOT_PTRACEABLE;
+       else
+               return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
+
+       return 0;
+}
+
+static int proc_parse_subset_param(struct fs_context *fc, char *value)
+{
+       struct proc_fs_context *ctx = fc->fs_private;
+
+       while (value) {
+               char *ptr = strchr(value, ',');
+
+               if (ptr != NULL)
+                       *ptr++ = '\0';
+
+               if (*value != '\0') {
+                       if (!strcmp(value, "pid")) {
+                               ctx->pidonly = PROC_PIDONLY_ON;
+                       } else {
+                               return invalf(fc, "proc: unsupported subset option - %s\n", value);
+                       }
+               }
+               value = ptr;
+       }
+
+       return 0;
+}
+
 static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
        struct proc_fs_context *ctx = fc->fs_private;
@@ -63,10 +128,13 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
                break;
 
        case Opt_hidepid:
-               ctx->hidepid = result.uint_32;
-               if (ctx->hidepid < HIDEPID_OFF ||
-                   ctx->hidepid > HIDEPID_INVISIBLE)
-                       return invalfc(fc, "hidepid value must be between 0 and 2.\n");
+               if (proc_parse_hidepid_param(fc, param))
+                       return -EINVAL;
+               break;
+
+       case Opt_subset:
+               if (proc_parse_subset_param(fc, param->string) < 0)
+                       return -EINVAL;
                break;
 
        default:
@@ -77,26 +145,33 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
        return 0;
 }
 
-static void proc_apply_options(struct super_block *s,
+static void proc_apply_options(struct proc_fs_info *fs_info,
                               struct fs_context *fc,
-                              struct pid_namespace *pid_ns,
                               struct user_namespace *user_ns)
 {
        struct proc_fs_context *ctx = fc->fs_private;
 
        if (ctx->mask & (1 << Opt_gid))
-               pid_ns->pid_gid = make_kgid(user_ns, ctx->gid);
+               fs_info->pid_gid = make_kgid(user_ns, ctx->gid);
        if (ctx->mask & (1 << Opt_hidepid))
-               pid_ns->hide_pid = ctx->hidepid;
+               fs_info->hide_pid = ctx->hidepid;
+       if (ctx->mask & (1 << Opt_subset))
+               fs_info->pidonly = ctx->pidonly;
 }
 
 static int proc_fill_super(struct super_block *s, struct fs_context *fc)
 {
-       struct pid_namespace *pid_ns = get_pid_ns(s->s_fs_info);
+       struct proc_fs_context *ctx = fc->fs_private;
        struct inode *root_inode;
+       struct proc_fs_info *fs_info;
        int ret;
 
-       proc_apply_options(s, fc, pid_ns, current_user_ns());
+       fs_info = kzalloc(sizeof(*fs_info), GFP_KERNEL);
+       if (!fs_info)
+               return -ENOMEM;
+
+       fs_info->pid_ns = get_pid_ns(ctx->pid_ns);
+       proc_apply_options(fs_info, fc, current_user_ns());
 
        /* User space would break if executables or devices appear on proc */
        s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
@@ -106,6 +181,7 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
        s->s_magic = PROC_SUPER_MAGIC;
        s->s_op = &proc_sops;
        s->s_time_gran = 1;
+       s->s_fs_info = fs_info;
 
        /*
         * procfs isn't actually a stacking filesystem; however, there is
@@ -113,7 +189,7 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
         * top of it
         */
        s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
-       
+
        /* procfs dentries and inodes don't require IO to create */
        s->s_shrink.seeks = 0;
 
@@ -140,19 +216,17 @@ static int proc_fill_super(struct super_block *s, struct fs_context *fc)
 static int proc_reconfigure(struct fs_context *fc)
 {
        struct super_block *sb = fc->root->d_sb;
-       struct pid_namespace *pid = sb->s_fs_info;
+       struct proc_fs_info *fs_info = proc_sb_info(sb);
 
        sync_filesystem(sb);
 
-       proc_apply_options(sb, fc, pid, current_user_ns());
+       proc_apply_options(fs_info, fc, current_user_ns());
        return 0;
 }
 
 static int proc_get_tree(struct fs_context *fc)
 {
-       struct proc_fs_context *ctx = fc->fs_private;
-
-       return get_tree_keyed(fc, proc_fill_super, ctx->pid_ns);
+       return get_tree_nodev(fc, proc_fill_super);
 }
 
 static void proc_fs_context_free(struct fs_context *fc)
@@ -188,22 +262,19 @@ static int proc_init_fs_context(struct fs_context *fc)
 
 static void proc_kill_sb(struct super_block *sb)
 {
-       struct pid_namespace *ns;
+       struct proc_fs_info *fs_info = proc_sb_info(sb);
 
-       ns = (struct pid_namespace *)sb->s_fs_info;
-       if (ns->proc_self)
-               dput(ns->proc_self);
-       if (ns->proc_thread_self)
-               dput(ns->proc_thread_self);
-       kill_anon_super(sb);
+       if (!fs_info) {
+               kill_anon_super(sb);
+               return;
+       }
 
-       /* Make the pid namespace safe for the next mount of proc */
-       ns->proc_self = NULL;
-       ns->proc_thread_self = NULL;
-       ns->pid_gid = GLOBAL_ROOT_GID;
-       ns->hide_pid = 0;
+       dput(fs_info->proc_self);
+       dput(fs_info->proc_thread_self);
 
-       put_pid_ns(ns);
+       kill_anon_super(sb);
+       put_pid_ns(fs_info->pid_ns);
+       kfree(fs_info);
 }
 
 static struct file_system_type proc_fs_type = {