Blame view

fs/devpts/inode.c 16.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /* -*- linux-c -*- --------------------------------------------------------- *
   *
   * linux/fs/devpts/inode.c
   *
   *  Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
   *
   * This file is part of the Linux kernel and is made available under
   * the terms of the GNU General Public License, version 2, or at your
   * option, any later version, incorporated herein by reference.
   *
   * ------------------------------------------------------------------------- */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/fs.h>
  #include <linux/sched.h>
  #include <linux/namei.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  #include <linux/mount.h>
  #include <linux/tty.h>
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
21
  #include <linux/mutex.h>
1fd7317d0   Nick Black   Move magic number...
22
  #include <linux/magic.h>
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
23
  #include <linux/idr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <linux/devpts_fs.h>
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
25
  #include <linux/parser.h>
3972b7f67   Florin Malita   devpts: add fsnot...
26
  #include <linux/fsnotify.h>
b87a267eb   Miklos Szeredi   mount options: fi...
27
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

b87a267eb   Miklos Szeredi   mount options: fi...
29
  #define DEVPTS_DEFAULT_MODE 0600
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
30
31
32
33
34
35
36
  /*
   * ptmx is a new node in /dev/pts and will be unused in legacy (single-
   * instance) mode. To prevent surprises in user space, set permissions of
   * ptmx to 0. Use 'chmod' or remount with '-o ptmxmode' to set meaningful
   * permissions.
   */
  #define DEVPTS_DEFAULT_PTMX_MODE 0000
527b3e477   Sukadev Bhattiprolu   Simplify devpts_g...
37
  #define PTMX_MINOR	2
b87a267eb   Miklos Szeredi   mount options: fi...
38

a4834c102   Konstantin Khlebnikov   tty: move pty cou...
39
40
41
42
43
  /*
   * sysctl support for setting limits on the number of Unix98 ptys allocated.
   * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
   */
  static int pty_limit = NR_UNIX98_PTY_DEFAULT;
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
44
  static int pty_reserve = NR_UNIX98_PTY_RESERVE;
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
45
  static int pty_limit_min;
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
46
  static int pty_limit_max = INT_MAX;
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
47
48
49
50
51
52
53
54
55
56
57
58
  static int pty_count;
  
  static struct ctl_table pty_table[] = {
  	{
  		.procname	= "max",
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
  		.data		= &pty_limit,
  		.proc_handler	= proc_dointvec_minmax,
  		.extra1		= &pty_limit_min,
  		.extra2		= &pty_limit_max,
  	}, {
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
59
60
61
62
63
64
65
66
  		.procname	= "reserve",
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
  		.data		= &pty_reserve,
  		.proc_handler	= proc_dointvec_minmax,
  		.extra1		= &pty_limit_min,
  		.extra2		= &pty_limit_max,
  	}, {
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  		.procname	= "nr",
  		.maxlen		= sizeof(int),
  		.mode		= 0444,
  		.data		= &pty_count,
  		.proc_handler	= proc_dointvec,
  	},
  	{}
  };
  
  static struct ctl_table pty_kern_table[] = {
  	{
  		.procname	= "pty",
  		.mode		= 0555,
  		.child		= pty_table,
  	},
  	{}
  };
  
  static struct ctl_table pty_root_table[] = {
  	{
  		.procname	= "kernel",
  		.mode		= 0555,
  		.child		= pty_kern_table,
  	},
  	{}
  };
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
93
  static DEFINE_MUTEX(allocated_ptys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  static struct vfsmount *devpts_mnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95

31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
96
  struct pts_mount_opts {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  	int setuid;
  	int setgid;
f04c6ce2c   Eric W. Biederman   userns: Convert d...
99
100
  	kuid_t   uid;
  	kgid_t   gid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
  	umode_t mode;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
102
  	umode_t ptmxmode;
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
103
  	int newinstance;
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
104
  	int max;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
105
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106

7a673c6b8   Domen Puncer   [PATCH] devpts: u...
107
  enum {
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
108
  	Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance,  Opt_max,
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
109
110
  	Opt_err
  };
a447c0932   Steven Whitehouse   vfs: Use const fo...
111
  static const match_table_t tokens = {
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
112
113
114
  	{Opt_uid, "uid=%u"},
  	{Opt_gid, "gid=%u"},
  	{Opt_mode, "mode=%o"},
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
115
116
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  	{Opt_ptmxmode, "ptmxmode=%o"},
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
117
  	{Opt_newinstance, "newinstance"},
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
118
  	{Opt_max, "max=%d"},
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
119
  #endif
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
120
121
  	{Opt_err, NULL}
  };
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
122
123
  struct pts_fs_info {
  	struct ida allocated_ptys;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
124
  	struct pts_mount_opts mount_opts;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
125
  	struct dentry *ptmx_dentry;
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
126
127
128
129
130
131
  };
  
  static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
  {
  	return sb->s_fs_info;
  }
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
132
133
  static inline struct super_block *pts_sb_from_inode(struct inode *inode)
  {
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
134
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
135
136
  	if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
  		return inode->i_sb;
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
137
  #endif
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
138
139
  	return devpts_mnt->mnt_sb;
  }
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
140
141
  #define PARSE_MOUNT	0
  #define PARSE_REMOUNT	1
1f71ebedb   Sukadev Bhattiprolu   devpts: correctly...
142
143
144
145
146
147
148
149
150
  /*
   * parse_mount_options():
   * 	Set @opts to mount options specified in @data. If an option is not
   * 	specified in @data, set it to its default value. The exception is
   * 	'newinstance' option which can only be set/cleared on a mount (i.e.
   * 	cannot be changed during remount).
   *
   * Note: @data may be NULL (in which case all options are set to default).
   */
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
151
  static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  {
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
153
  	char *p;
f04c6ce2c   Eric W. Biederman   userns: Convert d...
154
155
  	kuid_t uid;
  	kgid_t gid;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
156

31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
157
158
  	opts->setuid  = 0;
  	opts->setgid  = 0;
f04c6ce2c   Eric W. Biederman   userns: Convert d...
159
160
  	opts->uid     = GLOBAL_ROOT_UID;
  	opts->gid     = GLOBAL_ROOT_GID;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
161
  	opts->mode    = DEVPTS_DEFAULT_MODE;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
162
  	opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
163
  	opts->max     = NR_UNIX98_PTY_MAX;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
164

2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
165
166
167
  	/* newinstance makes sense only on initial mount */
  	if (op == PARSE_MOUNT)
  		opts->newinstance = 0;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
168
169
170
171
172
173
  	while ((p = strsep(&data, ",")) != NULL) {
  		substring_t args[MAX_OPT_ARGS];
  		int token;
  		int option;
  
  		if (!*p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  			continue;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
175
176
177
178
179
180
  
  		token = match_token(p, tokens, args);
  		switch (token) {
  		case Opt_uid:
  			if (match_int(&args[0], &option))
  				return -EINVAL;
f04c6ce2c   Eric W. Biederman   userns: Convert d...
181
182
183
184
  			uid = make_kuid(current_user_ns(), option);
  			if (!uid_valid(uid))
  				return -EINVAL;
  			opts->uid = uid;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
185
  			opts->setuid = 1;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
186
187
188
189
  			break;
  		case Opt_gid:
  			if (match_int(&args[0], &option))
  				return -EINVAL;
f04c6ce2c   Eric W. Biederman   userns: Convert d...
190
191
192
193
  			gid = make_kgid(current_user_ns(), option);
  			if (!gid_valid(gid))
  				return -EINVAL;
  			opts->gid = gid;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
194
  			opts->setgid = 1;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
195
196
197
198
  			break;
  		case Opt_mode:
  			if (match_octal(&args[0], &option))
  				return -EINVAL;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
199
  			opts->mode = option & S_IALLUGO;
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
200
  			break;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
201
202
203
204
205
206
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  		case Opt_ptmxmode:
  			if (match_octal(&args[0], &option))
  				return -EINVAL;
  			opts->ptmxmode = option & S_IALLUGO;
  			break;
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
207
208
209
210
211
  		case Opt_newinstance:
  			/* newinstance makes sense only on initial mount */
  			if (op == PARSE_MOUNT)
  				opts->newinstance = 1;
  			break;
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
212
213
214
215
216
217
  		case Opt_max:
  			if (match_int(&args[0], &option) ||
  			    option < 0 || option > NR_UNIX98_PTY_MAX)
  				return -EINVAL;
  			opts->max = option;
  			break;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
218
  #endif
7a673c6b8   Domen Puncer   [PATCH] devpts: u...
219
220
221
  		default:
  			printk(KERN_ERR "devpts: called with bogus options
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  			return -EINVAL;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
  
  	return 0;
  }
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
228
229
230
231
232
233
234
235
236
237
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  static int mknod_ptmx(struct super_block *sb)
  {
  	int mode;
  	int rc = -ENOMEM;
  	struct dentry *dentry;
  	struct inode *inode;
  	struct dentry *root = sb->s_root;
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
  	struct pts_mount_opts *opts = &fsi->mount_opts;
ec2aa8e8d   Eric W. Biederman   userns: Allow the...
238
239
240
241
242
243
244
  	kuid_t root_uid;
  	kgid_t root_gid;
  
  	root_uid = make_kuid(current_user_ns(), 0);
  	root_gid = make_kgid(current_user_ns(), 0);
  	if (!uid_valid(root_uid) || !gid_valid(root_gid))
  		return -EINVAL;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  
  	mutex_lock(&root->d_inode->i_mutex);
  
  	/* If we have already created ptmx node, return */
  	if (fsi->ptmx_dentry) {
  		rc = 0;
  		goto out;
  	}
  
  	dentry = d_alloc_name(root, "ptmx");
  	if (!dentry) {
  		printk(KERN_NOTICE "Unable to alloc dentry for ptmx node
  ");
  		goto out;
  	}
  
  	/*
  	 * Create a new 'ptmx' node in this mount of devpts.
  	 */
  	inode = new_inode(sb);
  	if (!inode) {
  		printk(KERN_ERR "Unable to alloc inode for ptmx node
  ");
  		dput(dentry);
  		goto out;
  	}
  
  	inode->i_ino = 2;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
273
274
275
276
  	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
  
  	mode = S_IFCHR|opts->ptmxmode;
  	init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2));
ec2aa8e8d   Eric W. Biederman   userns: Allow the...
277
278
  	inode->i_uid = root_uid;
  	inode->i_gid = root_gid;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
279
280
281
282
283
  
  	d_add(dentry, inode);
  
  	fsi->ptmx_dentry = dentry;
  	rc = 0;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  out:
  	mutex_unlock(&root->d_inode->i_mutex);
  	return rc;
  }
  
  static void update_ptmx_mode(struct pts_fs_info *fsi)
  {
  	struct inode *inode;
  	if (fsi->ptmx_dentry) {
  		inode = fsi->ptmx_dentry->d_inode;
  		inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode;
  	}
  }
  #else
  static inline void update_ptmx_mode(struct pts_fs_info *fsi)
  {
         return;
  }
  #endif
53af8ee40   Sukadev Bhattiprolu   Extract option pa...
303
304
  static int devpts_remount(struct super_block *sb, int *flags, char *data)
  {
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
305
  	int err;
53af8ee40   Sukadev Bhattiprolu   Extract option pa...
306
307
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
  	struct pts_mount_opts *opts = &fsi->mount_opts;
02b9984d6   Theodore Ts'o   fs: push sync_fil...
308
  	sync_filesystem(sb);
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
309
  	err = parse_mount_options(data, PARSE_REMOUNT, opts);
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
310
311
312
313
314
315
316
317
318
319
  
  	/*
  	 * parse_mount_options() restores options to default values
  	 * before parsing and may have changed ptmxmode. So, update the
  	 * mode in the inode too. Bogus options don't fail the remount,
  	 * so do this even on error return.
  	 */
  	update_ptmx_mode(fsi);
  
  	return err;
53af8ee40   Sukadev Bhattiprolu   Extract option pa...
320
  }
34c80b1d9   Al Viro   vfs: switch ->sho...
321
  static int devpts_show_options(struct seq_file *seq, struct dentry *root)
b87a267eb   Miklos Szeredi   mount options: fi...
322
  {
34c80b1d9   Al Viro   vfs: switch ->sho...
323
  	struct pts_fs_info *fsi = DEVPTS_SB(root->d_sb);
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
324
325
326
  	struct pts_mount_opts *opts = &fsi->mount_opts;
  
  	if (opts->setuid)
f04c6ce2c   Eric W. Biederman   userns: Convert d...
327
  		seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, opts->uid));
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
328
  	if (opts->setgid)
f04c6ce2c   Eric W. Biederman   userns: Convert d...
329
  		seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, opts->gid));
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
330
  	seq_printf(seq, ",mode=%03o", opts->mode);
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
331
332
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  	seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode);
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
333
334
  	if (opts->max < NR_UNIX98_PTY_MAX)
  		seq_printf(seq, ",max=%d", opts->max);
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
335
  #endif
b87a267eb   Miklos Szeredi   mount options: fi...
336
337
338
  
  	return 0;
  }
ee9b6d61a   Josef 'Jeff' Sipek   [PATCH] Mark stru...
339
  static const struct super_operations devpts_sops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  	.statfs		= simple_statfs,
  	.remount_fs	= devpts_remount,
b87a267eb   Miklos Szeredi   mount options: fi...
342
  	.show_options	= devpts_show_options,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  };
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
344
345
346
347
348
349
350
351
352
  static void *new_pts_fs_info(void)
  {
  	struct pts_fs_info *fsi;
  
  	fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
  	if (!fsi)
  		return NULL;
  
  	ida_init(&fsi->allocated_ptys);
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
353
  	fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
354
  	fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
355
356
357
  
  	return fsi;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
  static int
  devpts_fill_super(struct super_block *s, void *data, int silent)
  {
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
361
  	struct inode *inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
364
365
366
  
  	s->s_blocksize = 1024;
  	s->s_blocksize_bits = 10;
  	s->s_magic = DEVPTS_SUPER_MAGIC;
  	s->s_op = &devpts_sops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  	s->s_time_gran = 1;
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
368
369
370
  	s->s_fs_info = new_pts_fs_info();
  	if (!s->s_fs_info)
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
  	inode = new_inode(s);
  	if (!inode)
3850aba74   Al Viro   devpts: fix doubl...
373
  		goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
  	inode->i_ino = 1;
  	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
  	inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
  	inode->i_op = &simple_dir_inode_operations;
  	inode->i_fop = &simple_dir_operations;
bfe868486   Miklos Szeredi   filesystems: add ...
379
  	set_nlink(inode, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380

48fde701a   Al Viro   switch open-coded...
381
  	s->s_root = d_make_root(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  	if (s->s_root)
  		return 0;
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
384

835aa440f   Alan Cox   devpts: Coding st...
385
386
  	printk(KERN_ERR "devpts: get root dentry failed
  ");
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
387

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
390
  fail:
  	return -ENOMEM;
  }
8c056e5b1   Andrew Morton   devpts: fix unuse...
391
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
392
393
394
395
  static int compare_init_pts_sb(struct super_block *s, void *p)
  {
  	if (devpts_mnt)
  		return devpts_mnt->mnt_sb == s;
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
396
397
  	return 0;
  }
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
398
  /*
fc14f2fef   Al Viro   convert get_sb_si...
399
   * devpts_mount()
1bd790356   Sukadev Bhattiprolu   Merge code for si...
400
401
402
403
404
405
406
407
   *
   *     If the '-o newinstance' mount option was specified, mount a new
   *     (private) instance of devpts.  PTYs created in this instance are
   *     independent of the PTYs in other devpts instances.
   *
   *     If the '-o newinstance' option was not specified, mount/remount the
   *     initial kernel mount of devpts.  This type of mount gives the
   *     legacy, single-instance semantics.
289f00e22   Sukadev Bhattiprolu   Remove get_init_p...
408
   *
1bd790356   Sukadev Bhattiprolu   Merge code for si...
409
410
411
412
   *     The 'newinstance' option is needed to support multiple namespace
   *     semantics in devpts while preserving backward compatibility of the
   *     current 'single-namespace' semantics. i.e all mounts of devpts
   *     without the 'newinstance' mount option should bind to the initial
fc14f2fef   Al Viro   convert get_sb_si...
413
   *     kernel mount, like mount_single().
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
414
   *
1bd790356   Sukadev Bhattiprolu   Merge code for si...
415
   *     Mounts with 'newinstance' option create a new, private namespace.
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
416
   *
1bd790356   Sukadev Bhattiprolu   Merge code for si...
417
   *     NOTE:
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
418
   *
fc14f2fef   Al Viro   convert get_sb_si...
419
420
   *     For single-mount semantics, devpts cannot use mount_single(),
   *     because mount_single()/sget() find and use the super-block from
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
421
   *     the most recent mount of devpts. But that recent mount may be a
fc14f2fef   Al Viro   convert get_sb_si...
422
   *     'newinstance' mount and mount_single() would pick the newinstance
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
423
   *     super-block instead of the initial super-block.
d4076ac55   Sukadev Bhattiprolu   Define get_init_p...
424
   */
fc14f2fef   Al Viro   convert get_sb_si...
425
426
  static struct dentry *devpts_mount(struct file_system_type *fs_type,
  	int flags, const char *dev_name, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  {
482984f06   Sukadev Bhattiprolu   Parse mount optio...
428
429
  	int error;
  	struct pts_mount_opts opts;
1bd790356   Sukadev Bhattiprolu   Merge code for si...
430
  	struct super_block *s;
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
431

1f71ebedb   Sukadev Bhattiprolu   devpts: correctly...
432
433
  	error = parse_mount_options(data, PARSE_MOUNT, &opts);
  	if (error)
fc14f2fef   Al Viro   convert get_sb_si...
434
  		return ERR_PTR(error);
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
435

ec2aa8e8d   Eric W. Biederman   userns: Allow the...
436
437
438
439
440
  	/* Require newinstance for all user namespace mounts to ensure
  	 * the mount options are not changed.
  	 */
  	if ((current_user_ns() != &init_user_ns) && !opts.newinstance)
  		return ERR_PTR(-EINVAL);
482984f06   Sukadev Bhattiprolu   Parse mount optio...
441
  	if (opts.newinstance)
9249e17fe   David Howells   VFS: Pass mount f...
442
  		s = sget(fs_type, NULL, set_anon_super, flags, NULL);
482984f06   Sukadev Bhattiprolu   Parse mount optio...
443
  	else
9249e17fe   David Howells   VFS: Pass mount f...
444
445
  		s = sget(fs_type, compare_init_pts_sb, set_anon_super, flags,
  			 NULL);
945cf2c79   Sukadev Bhattiprolu   Move common mknod...
446

1bd790356   Sukadev Bhattiprolu   Merge code for si...
447
  	if (IS_ERR(s))
fc14f2fef   Al Viro   convert get_sb_si...
448
  		return ERR_CAST(s);
1bd790356   Sukadev Bhattiprolu   Merge code for si...
449
450
  
  	if (!s->s_root) {
1bd790356   Sukadev Bhattiprolu   Merge code for si...
451
452
453
454
455
  		error = devpts_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
  		if (error)
  			goto out_undo_sget;
  		s->s_flags |= MS_ACTIVE;
  	}
1bd790356   Sukadev Bhattiprolu   Merge code for si...
456
  	memcpy(&(DEVPTS_SB(s))->mount_opts, &opts, sizeof(opts));
945cf2c79   Sukadev Bhattiprolu   Move common mknod...
457

1bd790356   Sukadev Bhattiprolu   Merge code for si...
458
  	error = mknod_ptmx(s);
945cf2c79   Sukadev Bhattiprolu   Move common mknod...
459
  	if (error)
894680710   Al Viro   Simplify devpts_g...
460
  		goto out_undo_sget;
945cf2c79   Sukadev Bhattiprolu   Move common mknod...
461

fc14f2fef   Al Viro   convert get_sb_si...
462
  	return dget(s->s_root);
1bd790356   Sukadev Bhattiprolu   Merge code for si...
463
464
  
  out_undo_sget:
6f5bbff9a   Al Viro   Convert obvious p...
465
  	deactivate_locked_super(s);
fc14f2fef   Al Viro   convert get_sb_si...
466
  	return ERR_PTR(error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
  }
482984f06   Sukadev Bhattiprolu   Parse mount optio...
468

2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
469
470
471
472
473
  #else
  /*
   * This supports only the legacy single-instance semantics (no
   * multiple-instance semantics)
   */
fc14f2fef   Al Viro   convert get_sb_si...
474
475
  static struct dentry *devpts_mount(struct file_system_type *fs_type, int flags,
  		const char *dev_name, void *data)
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
476
  {
fc14f2fef   Al Viro   convert get_sb_si...
477
  	return mount_single(fs_type, flags, data, devpts_fill_super);
2a1b2dc0c   Sukadev Bhattiprolu   Enable multiple i...
478
479
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480

e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
481
482
483
  static void devpts_kill_sb(struct super_block *sb)
  {
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
66da0e1f9   Ilija Hadzic   devpts: plug the ...
484
  	ida_destroy(&fsi->allocated_ptys);
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
485
  	kfree(fsi);
1f8f1e296   Sukadev Bhattiprolu   Define mknod_ptmx()
486
  	kill_litter_super(sb);
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
487
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
  static struct file_system_type devpts_fs_type = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  	.name		= "devpts",
fc14f2fef   Al Viro   convert get_sb_si...
490
  	.mount		= devpts_mount,
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
491
  	.kill_sb	= devpts_kill_sb,
ec2aa8e8d   Eric W. Biederman   userns: Allow the...
492
493
494
  #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  	.fs_flags	= FS_USERNS_MOUNT | FS_USERNS_DEV_MOUNT,
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
  };
  
  /*
   * The normal naming convention is simply /dev/pts/<number>; this conforms
   * to the System V naming convention
   */
15f1a6338   Sukadev Bhattiprolu   Add an instance p...
501
  int devpts_new_index(struct inode *ptmx_inode)
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
502
  {
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
503
504
  	struct super_block *sb = pts_sb_from_inode(ptmx_inode);
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
505
  	int index;
7ee7c12b7   Alexey Dobriyan   [PATCH] devpts: s...
506
  	int ida_ret;
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
507
508
  
  retry:
835aa440f   Alan Cox   devpts: Coding st...
509
  	if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL))
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
510
  		return -ENOMEM;
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
511
512
  
  	mutex_lock(&allocated_ptys_lock);
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
513
514
515
516
517
  	if (pty_count >= pty_limit -
  			(fsi->mount_opts.newinstance ? pty_reserve : 0)) {
  		mutex_unlock(&allocated_ptys_lock);
  		return -ENOSPC;
  	}
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
518
  	ida_ret = ida_get_new(&fsi->allocated_ptys, &index);
7ee7c12b7   Alexey Dobriyan   [PATCH] devpts: s...
519
  	if (ida_ret < 0) {
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
520
  		mutex_unlock(&allocated_ptys_lock);
7ee7c12b7   Alexey Dobriyan   [PATCH] devpts: s...
521
  		if (ida_ret == -EAGAIN)
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
522
523
524
  			goto retry;
  		return -EIO;
  	}
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
525
  	if (index >= fsi->mount_opts.max) {
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
526
  		ida_remove(&fsi->allocated_ptys, index);
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
527
  		mutex_unlock(&allocated_ptys_lock);
e9aba5158   Konstantin Khlebnikov   tty: rework pty c...
528
  		return -ENOSPC;
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
529
  	}
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
530
  	pty_count++;
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
531
532
533
  	mutex_unlock(&allocated_ptys_lock);
  	return index;
  }
15f1a6338   Sukadev Bhattiprolu   Add an instance p...
534
  void devpts_kill_index(struct inode *ptmx_inode, int idx)
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
535
  {
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
536
537
  	struct super_block *sb = pts_sb_from_inode(ptmx_inode);
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
538
  	mutex_lock(&allocated_ptys_lock);
e76b7c01e   Sukadev Bhattiprolu   Per-mount allocat...
539
  	ida_remove(&fsi->allocated_ptys, idx);
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
540
  	pty_count--;
718a91633   Sukadev Bhattiprolu   devpts: factor ou...
541
542
  	mutex_unlock(&allocated_ptys_lock);
  }
1dcb8e6d1   Jiri Slaby   TTY: devpts, docu...
543
544
545
546
547
548
549
550
551
  /**
   * devpts_pty_new -- create a new inode in /dev/pts/
   * @ptmx_inode: inode of the master
   * @device: major+minor of the node to be created
   * @index: used as a name of the node
   * @priv: what's given back by devpts_get_priv
   *
   * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill.
   */
f11afb612   Jiri Slaby   TTY: devpts, do n...
552
553
  struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index,
  		void *priv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	struct dentry *dentry;
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
556
  	struct super_block *sb = pts_sb_from_inode(ptmx_inode);
162b97cfa   Jiri Slaby   TTY: devpts, retu...
557
  	struct inode *inode;
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
558
  	struct dentry *root = sb->s_root;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
559
560
  	struct pts_fs_info *fsi = DEVPTS_SB(sb);
  	struct pts_mount_opts *opts = &fsi->mount_opts;
89a52e109   Sukadev Bhattiprolu   Simplify devpts_p...
561
  	char s[12];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562

162b97cfa   Jiri Slaby   TTY: devpts, retu...
563
  	inode = new_inode(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
  	if (!inode)
162b97cfa   Jiri Slaby   TTY: devpts, retu...
565
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566

f11afb612   Jiri Slaby   TTY: devpts, do n...
567
  	inode->i_ino = index + 3;
d0eafc7db   David Howells   CRED: Wrap task c...
568
569
  	inode->i_uid = opts->setuid ? opts->uid : current_fsuid();
  	inode->i_gid = opts->setgid ? opts->gid : current_fsgid();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
  	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
31af0abbd   Sukadev Bhattiprolu   Per-mount 'config...
571
  	init_special_inode(inode, S_IFCHR|opts->mode, device);
f11afb612   Jiri Slaby   TTY: devpts, do n...
572
  	inode->i_private = priv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573

f11afb612   Jiri Slaby   TTY: devpts, do n...
574
  	sprintf(s, "%d", index);
89a52e109   Sukadev Bhattiprolu   Simplify devpts_p...
575

59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
576
  	mutex_lock(&root->d_inode->i_mutex);
89a52e109   Sukadev Bhattiprolu   Simplify devpts_p...
577

59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
578
  	dentry = d_alloc_name(root, s);
b12d12596   Andrey Vagin   fs/devpts/inode.c...
579
  	if (dentry) {
89a52e109   Sukadev Bhattiprolu   Simplify devpts_p...
580
  		d_add(dentry, inode);
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
581
  		fsnotify_create(root->d_inode, dentry);
aa597bc1f   Andrey Vagin   fs: devpts_pty_ne...
582
583
  	} else {
  		iput(inode);
162b97cfa   Jiri Slaby   TTY: devpts, retu...
584
  		inode = ERR_PTR(-ENOMEM);
3972b7f67   Florin Malita   devpts: add fsnot...
585
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
587
  	mutex_unlock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588

162b97cfa   Jiri Slaby   TTY: devpts, retu...
589
  	return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
  }
1dcb8e6d1   Jiri Slaby   TTY: devpts, docu...
591
592
593
594
595
596
  /**
   * devpts_get_priv -- get private data for a slave
   * @pts_inode: inode of the slave
   *
   * Returns whatever was passed as priv in devpts_pty_new for a given inode.
   */
8fcbaa2b7   Jiri Slaby   TTY: devpts, don'...
597
  void *devpts_get_priv(struct inode *pts_inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  {
edfacdd6f   Sukadev Bhattiprolu   devpts_get_tty() ...
599
  	struct dentry *dentry;
8fcbaa2b7   Jiri Slaby   TTY: devpts, don'...
600
  	void *priv = NULL;
edfacdd6f   Sukadev Bhattiprolu   devpts_get_tty() ...
601

527b3e477   Sukadev Bhattiprolu   Simplify devpts_g...
602
  	BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603

edfacdd6f   Sukadev Bhattiprolu   devpts_get_tty() ...
604
605
606
607
  	/* Ensure dentry has not been deleted by devpts_pty_kill() */
  	dentry = d_find_alias(pts_inode);
  	if (!dentry)
  		return NULL;
527b3e477   Sukadev Bhattiprolu   Simplify devpts_g...
608
  	if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
8fcbaa2b7   Jiri Slaby   TTY: devpts, don'...
609
  		priv = pts_inode->i_private;
edfacdd6f   Sukadev Bhattiprolu   devpts_get_tty() ...
610
611
  
  	dput(dentry);
8fcbaa2b7   Jiri Slaby   TTY: devpts, don'...
612
  	return priv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  }
1dcb8e6d1   Jiri Slaby   TTY: devpts, docu...
614
615
616
617
618
619
  /**
   * devpts_pty_kill -- remove inode form /dev/pts/
   * @inode: inode of the slave to be removed
   *
   * This is an inverse operation of devpts_pty_new.
   */
f11afb612   Jiri Slaby   TTY: devpts, do n...
620
  void devpts_pty_kill(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
  {
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
622
623
  	struct super_block *sb = pts_sb_from_inode(inode);
  	struct dentry *root = sb->s_root;
a6f37daa8   Sukadev Bhattiprolu   Simplify devpts_p...
624
  	struct dentry *dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625

a6f37daa8   Sukadev Bhattiprolu   Simplify devpts_p...
626
  	BUG_ON(inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
627
  	mutex_lock(&root->d_inode->i_mutex);
a6f37daa8   Sukadev Bhattiprolu   Simplify devpts_p...
628
629
  
  	dentry = d_find_alias(inode);
a6f37daa8   Sukadev Bhattiprolu   Simplify devpts_p...
630

6d6b77f16   Miklos Szeredi   filesystems: add ...
631
  	drop_nlink(inode);
aa597bc1f   Andrey Vagin   fs: devpts_pty_ne...
632
633
  	d_delete(dentry);
  	dput(dentry);	/* d_alloc_name() in devpts_pty_new() */
835aa440f   Alan Cox   devpts: Coding st...
634
  	dput(dentry);		/* d_find_alias above */
aa597bc1f   Andrey Vagin   fs: devpts_pty_ne...
635

59e55e6cf   Sukadev Bhattiprolu   Remove devpts_roo...
636
  	mutex_unlock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
641
  }
  
  static int __init init_devpts_fs(void)
  {
  	int err = register_filesystem(&devpts_fs_type);
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
642
  	struct ctl_table_header *table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
  	if (!err) {
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
644
  		table = register_sysctl_table(pty_root_table);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
  		devpts_mnt = kern_mount(&devpts_fs_type);
93d5581e2   Alan Cox   devpts: unregiste...
646
  		if (IS_ERR(devpts_mnt)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  			err = PTR_ERR(devpts_mnt);
93d5581e2   Alan Cox   devpts: unregiste...
648
  			unregister_filesystem(&devpts_fs_type);
a4834c102   Konstantin Khlebnikov   tty: move pty cou...
649
  			unregister_sysctl_table(table);
93d5581e2   Alan Cox   devpts: unregiste...
650
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
  	}
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
  module_init(init_devpts_fs)