Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

proc: Add fs_context support to procfs

Add fs_context support to procfs.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

David Howells and committed by
Al Viro
66f592e2 60a3c3a5

+126 -65
-1
fs/proc/inode.c
··· 127 127 .drop_inode = generic_delete_inode, 128 128 .evict_inode = proc_evict_inode, 129 129 .statfs = simple_statfs, 130 - .remount_fs = proc_remount, 131 130 .show_options = proc_show_options, 132 131 }; 133 132
-1
fs/proc/internal.h
··· 270 270 extern struct proc_dir_entry proc_root; 271 271 272 272 extern void proc_self_init(void); 273 - extern int proc_remount(struct super_block *, int *, char *); 274 273 275 274 /* 276 275 * task_[no]mmu.c
+126 -63
fs/proc/root.c
··· 19 19 #include <linux/module.h> 20 20 #include <linux/bitops.h> 21 21 #include <linux/user_namespace.h> 22 + #include <linux/fs_context.h> 22 23 #include <linux/mount.h> 23 24 #include <linux/pid_namespace.h> 24 - #include <linux/parser.h> 25 + #include <linux/fs_parser.h> 25 26 #include <linux/cred.h> 26 27 #include <linux/magic.h> 28 + #include <linux/slab.h> 27 29 28 30 #include "internal.h" 29 31 30 - enum { 31 - Opt_gid, Opt_hidepid, Opt_err, 32 + struct proc_fs_context { 33 + struct pid_namespace *pid_ns; 34 + unsigned int mask; 35 + int hidepid; 36 + int gid; 32 37 }; 33 38 34 - static const match_table_t tokens = { 35 - {Opt_hidepid, "hidepid=%u"}, 36 - {Opt_gid, "gid=%u"}, 37 - {Opt_err, NULL}, 39 + enum proc_param { 40 + Opt_gid, 41 + Opt_hidepid, 38 42 }; 39 43 40 - static int proc_parse_options(char *options, struct pid_namespace *pid) 44 + static const struct fs_parameter_spec proc_param_specs[] = { 45 + fsparam_u32("gid", Opt_gid), 46 + fsparam_u32("hidepid", Opt_hidepid), 47 + {} 48 + }; 49 + 50 + static const struct fs_parameter_description proc_fs_parameters = { 51 + .name = "proc", 52 + .specs = proc_param_specs, 53 + }; 54 + 55 + static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param) 41 56 { 42 - char *p; 43 - substring_t args[MAX_OPT_ARGS]; 44 - int option; 57 + struct proc_fs_context *ctx = fc->fs_private; 58 + struct fs_parse_result result; 59 + int opt; 45 60 46 - if (!options) 47 - return 1; 61 + opt = fs_parse(fc, &proc_fs_parameters, param, &result); 62 + if (opt < 0) 63 + return opt; 48 64 49 - while ((p = strsep(&options, ",")) != NULL) { 50 - int token; 51 - if (!*p) 52 - continue; 65 + switch (opt) { 66 + case Opt_gid: 67 + ctx->gid = result.uint_32; 68 + break; 53 69 54 - args[0].to = args[0].from = NULL; 55 - token = match_token(p, tokens, args); 56 - switch (token) { 57 - case Opt_gid: 58 - if (match_int(&args[0], &option)) 59 - return 0; 60 - pid->pid_gid = make_kgid(current_user_ns(), option); 61 - break; 62 - case Opt_hidepid: 63 - if (match_int(&args[0], &option)) 64 - return 0; 65 - if (option < HIDEPID_OFF || 66 - option > HIDEPID_INVISIBLE) { 67 - pr_err("proc: hidepid value must be between 0 and 2.\n"); 68 - return 0; 69 - } 70 - pid->hide_pid = option; 71 - break; 72 - default: 73 - pr_err("proc: unrecognized mount option \"%s\" " 74 - "or missing value\n", p); 75 - return 0; 76 - } 70 + case Opt_hidepid: 71 + ctx->hidepid = result.uint_32; 72 + if (ctx->hidepid < HIDEPID_OFF || 73 + ctx->hidepid > HIDEPID_INVISIBLE) 74 + return invalf(fc, "proc: hidepid value must be between 0 and 2.\n"); 75 + break; 76 + 77 + default: 78 + return -EINVAL; 77 79 } 78 80 79 - return 1; 81 + ctx->mask |= 1 << opt; 82 + return 0; 80 83 } 81 84 82 - static int proc_fill_super(struct super_block *s, void *data, int silent) 85 + static void proc_apply_options(struct super_block *s, 86 + struct fs_context *fc, 87 + struct pid_namespace *pid_ns, 88 + struct user_namespace *user_ns) 83 89 { 84 - struct pid_namespace *ns = get_pid_ns(s->s_fs_info); 90 + struct proc_fs_context *ctx = fc->fs_private; 91 + 92 + if (ctx->mask & (1 << Opt_gid)) 93 + pid_ns->pid_gid = make_kgid(user_ns, ctx->gid); 94 + if (ctx->mask & (1 << Opt_hidepid)) 95 + pid_ns->hide_pid = ctx->hidepid; 96 + } 97 + 98 + static int proc_fill_super(struct super_block *s, struct fs_context *fc) 99 + { 100 + struct pid_namespace *pid_ns = get_pid_ns(s->s_fs_info); 85 101 struct inode *root_inode; 86 102 int ret; 87 103 88 - if (!proc_parse_options(data, ns)) 89 - return -EINVAL; 104 + proc_apply_options(s, fc, pid_ns, current_user_ns()); 90 105 91 106 /* User space would break if executables or devices appear on proc */ 92 107 s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV; ··· 142 127 return proc_setup_thread_self(s); 143 128 } 144 129 145 - int proc_remount(struct super_block *sb, int *flags, char *data) 130 + static int proc_reconfigure(struct fs_context *fc) 146 131 { 132 + struct super_block *sb = fc->root->d_sb; 147 133 struct pid_namespace *pid = sb->s_fs_info; 148 134 149 135 sync_filesystem(sb); 150 - return !proc_parse_options(data, pid); 136 + 137 + proc_apply_options(sb, fc, pid, current_user_ns()); 138 + return 0; 151 139 } 152 140 153 - static struct dentry *proc_mount(struct file_system_type *fs_type, 154 - int flags, const char *dev_name, void *data) 141 + static int proc_get_tree(struct fs_context *fc) 155 142 { 156 - struct pid_namespace *ns; 143 + struct proc_fs_context *ctx = fc->fs_private; 157 144 158 - if (flags & SB_KERNMOUNT) { 159 - ns = data; 160 - data = NULL; 161 - } else { 162 - ns = task_active_pid_ns(current); 163 - } 145 + put_user_ns(fc->user_ns); 146 + fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); 147 + fc->s_fs_info = ctx->pid_ns; 148 + return vfs_get_super(fc, vfs_get_keyed_super, proc_fill_super); 149 + } 164 150 165 - return mount_ns(fs_type, flags, data, ns, ns->user_ns, proc_fill_super); 151 + static void proc_fs_context_free(struct fs_context *fc) 152 + { 153 + struct proc_fs_context *ctx = fc->fs_private; 154 + 155 + if (ctx->pid_ns) 156 + put_pid_ns(ctx->pid_ns); 157 + kfree(ctx); 158 + } 159 + 160 + static const struct fs_context_operations proc_fs_context_ops = { 161 + .free = proc_fs_context_free, 162 + .parse_param = proc_parse_param, 163 + .get_tree = proc_get_tree, 164 + .reconfigure = proc_reconfigure, 165 + }; 166 + 167 + static int proc_init_fs_context(struct fs_context *fc) 168 + { 169 + struct proc_fs_context *ctx; 170 + 171 + ctx = kzalloc(sizeof(struct proc_fs_context), GFP_KERNEL); 172 + if (!ctx) 173 + return -ENOMEM; 174 + 175 + ctx->pid_ns = get_pid_ns(task_active_pid_ns(current)); 176 + fc->fs_private = ctx; 177 + fc->ops = &proc_fs_context_ops; 178 + return 0; 166 179 } 167 180 168 181 static void proc_kill_sb(struct super_block *sb) ··· 207 164 } 208 165 209 166 static struct file_system_type proc_fs_type = { 210 - .name = "proc", 211 - .mount = proc_mount, 212 - .kill_sb = proc_kill_sb, 213 - .fs_flags = FS_USERNS_MOUNT, 167 + .name = "proc", 168 + .init_fs_context = proc_init_fs_context, 169 + .parameters = &proc_fs_parameters, 170 + .kill_sb = proc_kill_sb, 171 + .fs_flags = FS_USERNS_MOUNT, 214 172 }; 215 173 216 174 void __init proc_root_init(void) ··· 249 205 { 250 206 if (!proc_pid_lookup(dir, dentry, flags)) 251 207 return NULL; 252 - 208 + 253 209 return proc_lookup(dir, dentry, flags); 254 210 } 255 211 ··· 302 258 303 259 int pid_ns_prepare_proc(struct pid_namespace *ns) 304 260 { 261 + struct proc_fs_context *ctx; 262 + struct fs_context *fc; 305 263 struct vfsmount *mnt; 306 264 307 - mnt = kern_mount_data(&proc_fs_type, ns); 265 + fc = fs_context_for_mount(&proc_fs_type, SB_KERNMOUNT); 266 + if (IS_ERR(fc)) 267 + return PTR_ERR(fc); 268 + 269 + if (fc->user_ns != ns->user_ns) { 270 + put_user_ns(fc->user_ns); 271 + fc->user_ns = get_user_ns(ns->user_ns); 272 + } 273 + 274 + ctx = fc->fs_private; 275 + if (ctx->pid_ns != ns) { 276 + put_pid_ns(ctx->pid_ns); 277 + get_pid_ns(ns); 278 + ctx->pid_ns = ns; 279 + } 280 + 281 + mnt = fc_mount(fc); 282 + put_fs_context(fc); 308 283 if (IS_ERR(mnt)) 309 284 return PTR_ERR(mnt); 310 285