Blame view

fs/proc_namespace.c 8.07 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
0226f4923   Al Viro   vfs: take /proc/*...
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * fs/proc_namespace.c - handling of /proc/<pid>/{mounts,mountinfo,mountstats}
   *
   * In fact, that's a piece of procfs; it's *almost* isolated from
   * the rest of fs/proc, but has rather close relationships with
   * fs/namespace.c, thus here instead of fs/proc
   *
   */
  #include <linux/mnt_namespace.h>
  #include <linux/nsproxy.h>
  #include <linux/security.h>
  #include <linux/fs_struct.h>
f719ff9bc   Ingo Molnar   sched/headers: Pr...
14
  #include <linux/sched/task.h>
0226f4923   Al Viro   vfs: take /proc/*...
15
16
17
18
  #include "proc/internal.h" /* only for get_proc_task() in ->open() */
  
  #include "pnode.h"
  #include "internal.h"
076ccb76e   Al Viro   fs: annotate ->po...
19
  static __poll_t mounts_poll(struct file *file, poll_table *wait)
0226f4923   Al Viro   vfs: take /proc/*...
20
  {
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
21
22
  	struct seq_file *m = file->private_data;
  	struct proc_mounts *p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
23
  	struct mnt_namespace *ns = p->ns;
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
24
  	__poll_t res = EPOLLIN | EPOLLRDNORM;
aab407fc5   Al Viro   don't bother with...
25
  	int event;
0226f4923   Al Viro   vfs: take /proc/*...
26
27
  
  	poll_wait(file, &p->ns->poll, wait);
6aa7de059   Mark Rutland   locking/atomics: ...
28
  	event = READ_ONCE(ns->event);
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
29
30
  	if (m->poll_event != event) {
  		m->poll_event = event;
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
31
  		res |= EPOLLERR | EPOLLPRI;
0226f4923   Al Viro   vfs: take /proc/*...
32
  	}
0226f4923   Al Viro   vfs: take /proc/*...
33
34
35
  
  	return res;
  }
1e88c4201   Alexey Gladkov   proc: rename stru...
36
  struct proc_fs_opts {
0226f4923   Al Viro   vfs: take /proc/*...
37
38
39
40
41
42
  	int flag;
  	const char *str;
  };
  
  static int show_sb_opts(struct seq_file *m, struct super_block *sb)
  {
1e88c4201   Alexey Gladkov   proc: rename stru...
43
  	static const struct proc_fs_opts fs_opts[] = {
1751e8a6c   Linus Torvalds   Rename superblock...
44
45
46
47
  		{ SB_SYNCHRONOUS, ",sync" },
  		{ SB_DIRSYNC, ",dirsync" },
  		{ SB_MANDLOCK, ",mand" },
  		{ SB_LAZYTIME, ",lazytime" },
0226f4923   Al Viro   vfs: take /proc/*...
48
49
  		{ 0, NULL }
  	};
1e88c4201   Alexey Gladkov   proc: rename stru...
50
  	const struct proc_fs_opts *fs_infop;
0226f4923   Al Viro   vfs: take /proc/*...
51

1e88c4201   Alexey Gladkov   proc: rename stru...
52
  	for (fs_infop = fs_opts; fs_infop->flag; fs_infop++) {
0226f4923   Al Viro   vfs: take /proc/*...
53
54
55
56
57
58
59
60
61
  		if (sb->s_flags & fs_infop->flag)
  			seq_puts(m, fs_infop->str);
  	}
  
  	return security_sb_show_options(m, sb);
  }
  
  static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
  {
1e88c4201   Alexey Gladkov   proc: rename stru...
62
  	static const struct proc_fs_opts mnt_opts[] = {
0226f4923   Al Viro   vfs: take /proc/*...
63
64
65
66
67
68
  		{ MNT_NOSUID, ",nosuid" },
  		{ MNT_NODEV, ",nodev" },
  		{ MNT_NOEXEC, ",noexec" },
  		{ MNT_NOATIME, ",noatime" },
  		{ MNT_NODIRATIME, ",nodiratime" },
  		{ MNT_RELATIME, ",relatime" },
dab741e0e   Mattias Nissler   Add a "nosymfollo...
69
  		{ MNT_NOSYMFOLLOW, ",nosymfollow" },
0226f4923   Al Viro   vfs: take /proc/*...
70
71
  		{ 0, NULL }
  	};
1e88c4201   Alexey Gladkov   proc: rename stru...
72
  	const struct proc_fs_opts *fs_infop;
0226f4923   Al Viro   vfs: take /proc/*...
73

1e88c4201   Alexey Gladkov   proc: rename stru...
74
  	for (fs_infop = mnt_opts; fs_infop->flag; fs_infop++) {
0226f4923   Al Viro   vfs: take /proc/*...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  		if (mnt->mnt_flags & fs_infop->flag)
  			seq_puts(m, fs_infop->str);
  	}
  }
  
  static inline void mangle(struct seq_file *m, const char *s)
  {
  	seq_escape(m, s, " \t
  \\");
  }
  
  static void show_type(struct seq_file *m, struct super_block *sb)
  {
  	mangle(m, sb->s_type->name);
c7eb68696   David Howells   vfs: subtype hand...
89
  	if (sb->s_subtype) {
0226f4923   Al Viro   vfs: take /proc/*...
90
91
92
93
94
95
96
  		seq_putc(m, '.');
  		mangle(m, sb->s_subtype);
  	}
  }
  
  static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
  {
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
97
  	struct proc_mounts *p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
98
  	struct mount *r = real_mount(mnt);
0226f4923   Al Viro   vfs: take /proc/*...
99
  	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
d861c630e   Al Viro   vfs: switch ->sho...
100
  	struct super_block *sb = mnt_path.dentry->d_sb;
5d9f3c7b6   Dmitry V. Levin   vfs: show_vfsmnt:...
101
  	int err;
0226f4923   Al Viro   vfs: take /proc/*...
102

d861c630e   Al Viro   vfs: switch ->sho...
103
104
  	if (sb->s_op->show_devname) {
  		err = sb->s_op->show_devname(m, mnt_path.dentry);
0226f4923   Al Viro   vfs: take /proc/*...
105
106
107
108
109
110
  		if (err)
  			goto out;
  	} else {
  		mangle(m, r->mnt_devname ? r->mnt_devname : "none");
  	}
  	seq_putc(m, ' ');
9d4d65748   Dmitry V. Levin   vfs: make mounts ...
111
112
113
114
115
  	/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
  	err = seq_path_root(m, &mnt_path, &p->root, " \t
  \\");
  	if (err)
  		goto out;
0226f4923   Al Viro   vfs: take /proc/*...
116
  	seq_putc(m, ' ');
d861c630e   Al Viro   vfs: switch ->sho...
117
  	show_type(m, sb);
0226f4923   Al Viro   vfs: take /proc/*...
118
  	seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
d861c630e   Al Viro   vfs: switch ->sho...
119
  	err = show_sb_opts(m, sb);
0226f4923   Al Viro   vfs: take /proc/*...
120
121
122
  	if (err)
  		goto out;
  	show_mnt_opts(m, mnt);
d861c630e   Al Viro   vfs: switch ->sho...
123
  	if (sb->s_op->show_options)
34c80b1d9   Al Viro   vfs: switch ->sho...
124
  		err = sb->s_op->show_options(m, mnt_path.dentry);
0226f4923   Al Viro   vfs: take /proc/*...
125
126
127
128
129
130
131
132
  	seq_puts(m, " 0 0
  ");
  out:
  	return err;
  }
  
  static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
  {
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
133
  	struct proc_mounts *p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
134
135
136
  	struct mount *r = real_mount(mnt);
  	struct super_block *sb = mnt->mnt_sb;
  	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
137
  	int err;
0226f4923   Al Viro   vfs: take /proc/*...
138
139
140
  
  	seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
  		   MAJOR(sb->s_dev), MINOR(sb->s_dev));
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
141
  	if (sb->s_op->show_path) {
a6322de67   Al Viro   vfs: switch ->sho...
142
  		err = sb->s_op->show_path(m, mnt->mnt_root);
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
143
144
145
  		if (err)
  			goto out;
  	} else {
0226f4923   Al Viro   vfs: take /proc/*...
146
147
  		seq_dentry(m, mnt->mnt_root, " \t
  \\");
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
148
  	}
0226f4923   Al Viro   vfs: take /proc/*...
149
150
151
  	seq_putc(m, ' ');
  
  	/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
9ad4dc4f7   Dmitry V. Levin   vfs: cleanup show...
152
153
  	err = seq_path_root(m, &mnt_path, &p->root, " \t
  \\");
0226f4923   Al Viro   vfs: take /proc/*...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  	if (err)
  		goto out;
  
  	seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
  	show_mnt_opts(m, mnt);
  
  	/* Tagged fields ("foo:X" or "bar") */
  	if (IS_MNT_SHARED(r))
  		seq_printf(m, " shared:%i", r->mnt_group_id);
  	if (IS_MNT_SLAVE(r)) {
  		int master = r->mnt_master->mnt_group_id;
  		int dom = get_dominating_id(r, &p->root);
  		seq_printf(m, " master:%i", master);
  		if (dom && dom != master)
  			seq_printf(m, " propagate_from:%i", dom);
  	}
  	if (IS_MNT_UNBINDABLE(r))
  		seq_puts(m, " unbindable");
  
  	/* Filesystem specific data */
  	seq_puts(m, " - ");
  	show_type(m, sb);
  	seq_putc(m, ' ');
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
177
  	if (sb->s_op->show_devname) {
d861c630e   Al Viro   vfs: switch ->sho...
178
  		err = sb->s_op->show_devname(m, mnt->mnt_root);
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
179
180
181
  		if (err)
  			goto out;
  	} else {
0226f4923   Al Viro   vfs: take /proc/*...
182
  		mangle(m, r->mnt_devname ? r->mnt_devname : "none");
6ce4bca0a   Dmitry V. Levin   vfs: show_mountin...
183
  	}
bc98a42c1   David Howells   VFS: Convert sb->...
184
  	seq_puts(m, sb_rdonly(sb) ? " ro" : " rw");
0226f4923   Al Viro   vfs: take /proc/*...
185
186
187
188
  	err = show_sb_opts(m, sb);
  	if (err)
  		goto out;
  	if (sb->s_op->show_options)
34c80b1d9   Al Viro   vfs: switch ->sho...
189
  		err = sb->s_op->show_options(m, mnt->mnt_root);
0226f4923   Al Viro   vfs: take /proc/*...
190
191
192
193
194
195
196
197
  	seq_putc(m, '
  ');
  out:
  	return err;
  }
  
  static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
  {
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
198
  	struct proc_mounts *p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
199
200
  	struct mount *r = real_mount(mnt);
  	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
64132379d   Al Viro   vfs: switch ->sho...
201
  	struct super_block *sb = mnt_path.dentry->d_sb;
b896fb35c   Dmitry V. Levin   vfs: show_vfsstat...
202
  	int err;
0226f4923   Al Viro   vfs: take /proc/*...
203
204
  
  	/* device */
64132379d   Al Viro   vfs: switch ->sho...
205
  	if (sb->s_op->show_devname) {
0226f4923   Al Viro   vfs: take /proc/*...
206
  		seq_puts(m, "device ");
d861c630e   Al Viro   vfs: switch ->sho...
207
  		err = sb->s_op->show_devname(m, mnt_path.dentry);
5f8d498d4   Dmitry V. Levin   vfs: show_vfsstat...
208
209
  		if (err)
  			goto out;
0226f4923   Al Viro   vfs: take /proc/*...
210
211
212
213
214
215
216
217
218
219
  	} else {
  		if (r->mnt_devname) {
  			seq_puts(m, "device ");
  			mangle(m, r->mnt_devname);
  		} else
  			seq_puts(m, "no device");
  	}
  
  	/* mount point */
  	seq_puts(m, " mounted on ");
9d4d65748   Dmitry V. Levin   vfs: make mounts ...
220
221
222
223
224
  	/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
  	err = seq_path_root(m, &mnt_path, &p->root, " \t
  \\");
  	if (err)
  		goto out;
0226f4923   Al Viro   vfs: take /proc/*...
225
226
227
228
  	seq_putc(m, ' ');
  
  	/* file system type */
  	seq_puts(m, "with fstype ");
64132379d   Al Viro   vfs: switch ->sho...
229
  	show_type(m, sb);
0226f4923   Al Viro   vfs: take /proc/*...
230
231
  
  	/* optional statistics */
64132379d   Al Viro   vfs: switch ->sho...
232
  	if (sb->s_op->show_stats) {
0226f4923   Al Viro   vfs: take /proc/*...
233
  		seq_putc(m, ' ');
b896fb35c   Dmitry V. Levin   vfs: show_vfsstat...
234
  		err = sb->s_op->show_stats(m, mnt_path.dentry);
0226f4923   Al Viro   vfs: take /proc/*...
235
236
237
238
  	}
  
  	seq_putc(m, '
  ');
9d4d65748   Dmitry V. Levin   vfs: make mounts ...
239
  out:
0226f4923   Al Viro   vfs: take /proc/*...
240
241
242
243
244
245
246
247
248
249
250
  	return err;
  }
  
  static int mounts_open_common(struct inode *inode, struct file *file,
  			      int (*show)(struct seq_file *, struct vfsmount *))
  {
  	struct task_struct *task = get_proc_task(inode);
  	struct nsproxy *nsp;
  	struct mnt_namespace *ns = NULL;
  	struct path root;
  	struct proc_mounts *p;
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
251
  	struct seq_file *m;
0226f4923   Al Viro   vfs: take /proc/*...
252
253
254
255
  	int ret = -EINVAL;
  
  	if (!task)
  		goto err;
728dba3a3   Eric W. Biederman   namespaces: Use t...
256
257
  	task_lock(task);
  	nsp = task->nsproxy;
3d93116ce   Axel Lin   fs/proc_namespace...
258
  	if (!nsp || !nsp->mnt_ns) {
728dba3a3   Eric W. Biederman   namespaces: Use t...
259
  		task_unlock(task);
0226f4923   Al Viro   vfs: take /proc/*...
260
261
262
263
  		put_task_struct(task);
  		goto err;
  	}
  	ns = nsp->mnt_ns;
0226f4923   Al Viro   vfs: take /proc/*...
264
  	get_mnt_ns(ns);
0226f4923   Al Viro   vfs: take /proc/*...
265
266
267
268
269
270
271
272
273
  	if (!task->fs) {
  		task_unlock(task);
  		put_task_struct(task);
  		ret = -ENOENT;
  		goto err_put_ns;
  	}
  	get_fs_root(task->fs, &root);
  	task_unlock(task);
  	put_task_struct(task);
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
274
275
  	ret = seq_open_private(file, &mounts_op, sizeof(struct proc_mounts));
  	if (ret)
0226f4923   Al Viro   vfs: take /proc/*...
276
  		goto err_put_path;
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
277
278
  	m = file->private_data;
  	m->poll_event = ns->event;
0226f4923   Al Viro   vfs: take /proc/*...
279

ede1bf0dc   Yann Droneaud   fs: use seq_open_...
280
  	p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
281
282
  	p->ns = ns;
  	p->root = root;
0226f4923   Al Viro   vfs: take /proc/*...
283
  	p->show = show;
9f6c61f96   Miklos Szeredi   proc/mounts: add ...
284
285
  	INIT_LIST_HEAD(&p->cursor.mnt_list);
  	p->cursor.mnt.mnt_flags = MNT_CURSOR;
0226f4923   Al Viro   vfs: take /proc/*...
286
287
  
  	return 0;
0226f4923   Al Viro   vfs: take /proc/*...
288
289
290
291
292
293
294
295
296
297
   err_put_path:
  	path_put(&root);
   err_put_ns:
  	put_mnt_ns(ns);
   err:
  	return ret;
  }
  
  static int mounts_release(struct inode *inode, struct file *file)
  {
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
298
299
  	struct seq_file *m = file->private_data;
  	struct proc_mounts *p = m->private;
0226f4923   Al Viro   vfs: take /proc/*...
300
  	path_put(&p->root);
9f6c61f96   Miklos Szeredi   proc/mounts: add ...
301
  	mnt_cursor_del(p->ns, &p->cursor);
0226f4923   Al Viro   vfs: take /proc/*...
302
  	put_mnt_ns(p->ns);
ede1bf0dc   Yann Droneaud   fs: use seq_open_...
303
  	return seq_release_private(inode, file);
0226f4923   Al Viro   vfs: take /proc/*...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  }
  
  static int mounts_open(struct inode *inode, struct file *file)
  {
  	return mounts_open_common(inode, file, show_vfsmnt);
  }
  
  static int mountinfo_open(struct inode *inode, struct file *file)
  {
  	return mounts_open_common(inode, file, show_mountinfo);
  }
  
  static int mountstats_open(struct inode *inode, struct file *file)
  {
  	return mounts_open_common(inode, file, show_vfsstat);
  }
  
  const struct file_operations proc_mounts_operations = {
  	.open		= mounts_open,
54677c80b   Linus Torvalds   proc mountinfo: m...
323
324
  	.read_iter	= seq_read_iter,
  	.splice_read	= generic_file_splice_read,
0226f4923   Al Viro   vfs: take /proc/*...
325
326
327
328
329
330
331
  	.llseek		= seq_lseek,
  	.release	= mounts_release,
  	.poll		= mounts_poll,
  };
  
  const struct file_operations proc_mountinfo_operations = {
  	.open		= mountinfo_open,
54677c80b   Linus Torvalds   proc mountinfo: m...
332
333
  	.read_iter	= seq_read_iter,
  	.splice_read	= generic_file_splice_read,
0226f4923   Al Viro   vfs: take /proc/*...
334
335
336
337
338
339
340
  	.llseek		= seq_lseek,
  	.release	= mounts_release,
  	.poll		= mounts_poll,
  };
  
  const struct file_operations proc_mountstats_operations = {
  	.open		= mountstats_open,
54677c80b   Linus Torvalds   proc mountinfo: m...
341
342
  	.read_iter	= seq_read_iter,
  	.splice_read	= generic_file_splice_read,
0226f4923   Al Viro   vfs: take /proc/*...
343
344
345
  	.llseek		= seq_lseek,
  	.release	= mounts_release,
  };