diff options
author | David Howells <dhowells@redhat.com> | 2018-11-01 19:07:25 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-02-28 03:29:28 -0500 |
commit | 66f592e2ece0389c018d74d1bbb2d0b9738cfe48 (patch) | |
tree | 0974a99d09dc7e3c37ff9559baa016dce1b529e8 | |
parent | 60a3c3a58e2e01e19ed2b68b415adb12118ac349 (diff) |
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>
-rw-r--r-- | fs/proc/inode.c | 1 | ||||
-rw-r--r-- | fs/proc/internal.h | 1 | ||||
-rw-r--r-- | fs/proc/root.c | 195 |
3 files changed, 129 insertions, 68 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 17b5261206dd..fc7e38def174 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -127,7 +127,6 @@ const struct super_operations proc_sops = { | |||
127 | .drop_inode = generic_delete_inode, | 127 | .drop_inode = generic_delete_inode, |
128 | .evict_inode = proc_evict_inode, | 128 | .evict_inode = proc_evict_inode, |
129 | .statfs = simple_statfs, | 129 | .statfs = simple_statfs, |
130 | .remount_fs = proc_remount, | ||
131 | .show_options = proc_show_options, | 130 | .show_options = proc_show_options, |
132 | }; | 131 | }; |
133 | 132 | ||
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 97157c0410a2..40f905143d39 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -270,7 +270,6 @@ static inline void proc_tty_init(void) {} | |||
270 | extern struct proc_dir_entry proc_root; | 270 | extern struct proc_dir_entry proc_root; |
271 | 271 | ||
272 | extern void proc_self_init(void); | 272 | extern void proc_self_init(void); |
273 | extern int proc_remount(struct super_block *, int *, char *); | ||
274 | 273 | ||
275 | /* | 274 | /* |
276 | * task_[no]mmu.c | 275 | * task_[no]mmu.c |
diff --git a/fs/proc/root.c b/fs/proc/root.c index fe4f64b3250b..6927b29ece76 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -19,74 +19,89 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/bitops.h> | 20 | #include <linux/bitops.h> |
21 | #include <linux/user_namespace.h> | 21 | #include <linux/user_namespace.h> |
22 | #include <linux/fs_context.h> | ||
22 | #include <linux/mount.h> | 23 | #include <linux/mount.h> |
23 | #include <linux/pid_namespace.h> | 24 | #include <linux/pid_namespace.h> |
24 | #include <linux/parser.h> | 25 | #include <linux/fs_parser.h> |
25 | #include <linux/cred.h> | 26 | #include <linux/cred.h> |
26 | #include <linux/magic.h> | 27 | #include <linux/magic.h> |
28 | #include <linux/slab.h> | ||
27 | 29 | ||
28 | #include "internal.h" | 30 | #include "internal.h" |
29 | 31 | ||
30 | enum { | 32 | struct proc_fs_context { |
31 | Opt_gid, Opt_hidepid, Opt_err, | 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 = { | 39 | enum proc_param { |
35 | {Opt_hidepid, "hidepid=%u"}, | 40 | Opt_gid, |
36 | {Opt_gid, "gid=%u"}, | 41 | Opt_hidepid, |
37 | {Opt_err, NULL}, | ||
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; | 57 | struct proc_fs_context *ctx = fc->fs_private; |
43 | substring_t args[MAX_OPT_ARGS]; | 58 | struct fs_parse_result result; |
44 | int option; | 59 | int opt; |
45 | 60 | ||
46 | if (!options) | 61 | opt = fs_parse(fc, &proc_fs_parameters, param, &result); |
47 | return 1; | 62 | if (opt < 0) |
48 | 63 | return opt; | |
49 | while ((p = strsep(&options, ",")) != NULL) { | 64 | |
50 | int token; | 65 | switch (opt) { |
51 | if (!*p) | 66 | case Opt_gid: |
52 | continue; | 67 | ctx->gid = result.uint_32; |
53 | 68 | break; | |
54 | args[0].to = args[0].from = NULL; | 69 | |
55 | token = match_token(p, tokens, args); | 70 | case Opt_hidepid: |
56 | switch (token) { | 71 | ctx->hidepid = result.uint_32; |
57 | case Opt_gid: | 72 | if (ctx->hidepid < HIDEPID_OFF || |
58 | if (match_int(&args[0], &option)) | 73 | ctx->hidepid > HIDEPID_INVISIBLE) |
59 | return 0; | 74 | return invalf(fc, "proc: hidepid value must be between 0 and 2.\n"); |
60 | pid->pid_gid = make_kgid(current_user_ns(), option); | 75 | break; |
61 | break; | 76 | |
62 | case Opt_hidepid: | 77 | default: |
63 | if (match_int(&args[0], &option)) | 78 | return -EINVAL; |
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 | } | ||
77 | } | 79 | } |
78 | 80 | ||
79 | return 1; | 81 | ctx->mask |= 1 << opt; |
82 | return 0; | ||
83 | } | ||
84 | |||
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) | ||
89 | { | ||
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; | ||
80 | } | 96 | } |
81 | 97 | ||
82 | static int proc_fill_super(struct super_block *s, void *data, int silent) | 98 | static int proc_fill_super(struct super_block *s, struct fs_context *fc) |
83 | { | 99 | { |
84 | struct pid_namespace *ns = get_pid_ns(s->s_fs_info); | 100 | struct pid_namespace *pid_ns = get_pid_ns(s->s_fs_info); |
85 | struct inode *root_inode; | 101 | struct inode *root_inode; |
86 | int ret; | 102 | int ret; |
87 | 103 | ||
88 | if (!proc_parse_options(data, ns)) | 104 | proc_apply_options(s, fc, pid_ns, current_user_ns()); |
89 | return -EINVAL; | ||
90 | 105 | ||
91 | /* User space would break if executables or devices appear on proc */ | 106 | /* User space would break if executables or devices appear on proc */ |
92 | s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV; | 107 | s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV; |
@@ -127,27 +142,55 @@ static int proc_fill_super(struct super_block *s, void *data, int silent) | |||
127 | return proc_setup_thread_self(s); | 142 | return proc_setup_thread_self(s); |
128 | } | 143 | } |
129 | 144 | ||
130 | int proc_remount(struct super_block *sb, int *flags, char *data) | 145 | static int proc_reconfigure(struct fs_context *fc) |
131 | { | 146 | { |
147 | struct super_block *sb = fc->root->d_sb; | ||
132 | struct pid_namespace *pid = sb->s_fs_info; | 148 | struct pid_namespace *pid = sb->s_fs_info; |
133 | 149 | ||
134 | sync_filesystem(sb); | 150 | sync_filesystem(sb); |
135 | return !proc_parse_options(data, pid); | 151 | |
152 | proc_apply_options(sb, fc, pid, current_user_ns()); | ||
153 | return 0; | ||
136 | } | 154 | } |
137 | 155 | ||
138 | static struct dentry *proc_mount(struct file_system_type *fs_type, | 156 | static int proc_get_tree(struct fs_context *fc) |
139 | int flags, const char *dev_name, void *data) | ||
140 | { | 157 | { |
141 | struct pid_namespace *ns; | 158 | struct proc_fs_context *ctx = fc->fs_private; |
142 | 159 | ||
143 | if (flags & SB_KERNMOUNT) { | 160 | put_user_ns(fc->user_ns); |
144 | ns = data; | 161 | fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); |
145 | data = NULL; | 162 | fc->s_fs_info = ctx->pid_ns; |
146 | } else { | 163 | return vfs_get_super(fc, vfs_get_keyed_super, proc_fill_super); |
147 | ns = task_active_pid_ns(current); | 164 | } |
148 | } | 165 | |
166 | static void proc_fs_context_free(struct fs_context *fc) | ||
167 | { | ||
168 | struct proc_fs_context *ctx = fc->fs_private; | ||
169 | |||
170 | if (ctx->pid_ns) | ||
171 | put_pid_ns(ctx->pid_ns); | ||
172 | kfree(ctx); | ||
173 | } | ||
174 | |||
175 | static const struct fs_context_operations proc_fs_context_ops = { | ||
176 | .free = proc_fs_context_free, | ||
177 | .parse_param = proc_parse_param, | ||
178 | .get_tree = proc_get_tree, | ||
179 | .reconfigure = proc_reconfigure, | ||
180 | }; | ||
181 | |||
182 | static int proc_init_fs_context(struct fs_context *fc) | ||
183 | { | ||
184 | struct proc_fs_context *ctx; | ||
185 | |||
186 | ctx = kzalloc(sizeof(struct proc_fs_context), GFP_KERNEL); | ||
187 | if (!ctx) | ||
188 | return -ENOMEM; | ||
149 | 189 | ||
150 | return mount_ns(fs_type, flags, data, ns, ns->user_ns, proc_fill_super); | 190 | ctx->pid_ns = get_pid_ns(task_active_pid_ns(current)); |
191 | fc->fs_private = ctx; | ||
192 | fc->ops = &proc_fs_context_ops; | ||
193 | return 0; | ||
151 | } | 194 | } |
152 | 195 | ||
153 | static void proc_kill_sb(struct super_block *sb) | 196 | static void proc_kill_sb(struct super_block *sb) |
@@ -164,10 +207,11 @@ static void proc_kill_sb(struct super_block *sb) | |||
164 | } | 207 | } |
165 | 208 | ||
166 | static struct file_system_type proc_fs_type = { | 209 | static struct file_system_type proc_fs_type = { |
167 | .name = "proc", | 210 | .name = "proc", |
168 | .mount = proc_mount, | 211 | .init_fs_context = proc_init_fs_context, |
169 | .kill_sb = proc_kill_sb, | 212 | .parameters = &proc_fs_parameters, |
170 | .fs_flags = FS_USERNS_MOUNT, | 213 | .kill_sb = proc_kill_sb, |
214 | .fs_flags = FS_USERNS_MOUNT, | ||
171 | }; | 215 | }; |
172 | 216 | ||
173 | void __init proc_root_init(void) | 217 | void __init proc_root_init(void) |
@@ -205,7 +249,7 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr | |||
205 | { | 249 | { |
206 | if (!proc_pid_lookup(dir, dentry, flags)) | 250 | if (!proc_pid_lookup(dir, dentry, flags)) |
207 | return NULL; | 251 | return NULL; |
208 | 252 | ||
209 | return proc_lookup(dir, dentry, flags); | 253 | return proc_lookup(dir, dentry, flags); |
210 | } | 254 | } |
211 | 255 | ||
@@ -258,9 +302,28 @@ struct proc_dir_entry proc_root = { | |||
258 | 302 | ||
259 | int pid_ns_prepare_proc(struct pid_namespace *ns) | 303 | int pid_ns_prepare_proc(struct pid_namespace *ns) |
260 | { | 304 | { |
305 | struct proc_fs_context *ctx; | ||
306 | struct fs_context *fc; | ||
261 | struct vfsmount *mnt; | 307 | struct vfsmount *mnt; |
262 | 308 | ||
263 | mnt = kern_mount_data(&proc_fs_type, ns); | 309 | fc = fs_context_for_mount(&proc_fs_type, SB_KERNMOUNT); |
310 | if (IS_ERR(fc)) | ||
311 | return PTR_ERR(fc); | ||
312 | |||
313 | if (fc->user_ns != ns->user_ns) { | ||
314 | put_user_ns(fc->user_ns); | ||
315 | fc->user_ns = get_user_ns(ns->user_ns); | ||
316 | } | ||
317 | |||
318 | ctx = fc->fs_private; | ||
319 | if (ctx->pid_ns != ns) { | ||
320 | put_pid_ns(ctx->pid_ns); | ||
321 | get_pid_ns(ns); | ||
322 | ctx->pid_ns = ns; | ||
323 | } | ||
324 | |||
325 | mnt = fc_mount(fc); | ||
326 | put_fs_context(fc); | ||
264 | if (IS_ERR(mnt)) | 327 | if (IS_ERR(mnt)) |
265 | return PTR_ERR(mnt); | 328 | return PTR_ERR(mnt); |
266 | 329 | ||