aboutsummaryrefslogtreecommitdiffstats
path: root/fs/devpts
diff options
context:
space:
mode:
authorSukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>2009-01-02 08:42:27 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-02 13:19:36 -0500
commit2a1b2dc0c83bbfc24d72cafd5e69810a149b44e4 (patch)
tree4ed7b892b7a1e03bc085d1f78e0a46fd2ee13342 /fs/devpts
parentd4076ac55bf8755ce6c5706478631c1726cf0179 (diff)
Enable multiple instances of devpts
To support containers, allow multiple instances of devpts filesystem, such that indices of ptys allocated in one instance are independent of ptys allocated in other instances of devpts. But to preserve backward compatibility, enable this support for multiple instances only if: - CONFIG_DEVPTS_MULTIPLE_INSTANCES is set to Y, and - '-o newinstance' mount option is specified while mounting devpts To use multi-instance mount, a container startup script could: $ ns_exec -cm /bin/bash $ umount /dev/pts $ mount -t devpts -o newinstance lxcpts /dev/pts $ mount -o bind /dev/pts/ptmx /dev/ptmx $ /usr/sbin/sshd -p 1234 where 'ns_exec -cm /bin/bash' is calls clone() with CLONE_NEWNS flag and execs /bin/bash in the child process. A pty created by the sshd is not visible in the original mount of /dev/pts. USER-SPACE-IMPACT: - See Documentation/fs/devpts.txt (included in next patch) for user- space impact in multi-instance and mixed-mode operation. TODO: - Update mount(8), pts(4) man pages. Highlight impact of not redirecting /dev/ptmx to /dev/pts/ptmx after a multi-instance mount. Changelog[v6]: - [Dave Hansen] Use new get_init_pts_sb() interface - [Serge Hallyn] Don't bother displaying 'newinstance' in show_options - [Serge Hallyn] Use macros (PARSE_REMOUNT/PARSE_MOUNT) instead of 0/1. - [Serge Hallyn] Check error return from get_sb_single() (now get_init_pts_sb()) - devpts_pty_kill(): don't dput error dentries Changelog[v5]: - Move get_sb_ref() definition to earlier patch - Move usage info to Documentation/filesystems/devpts.txt (next patch) - Make ptmx node even in init_pts_ns, now that default mode is 0000 (defined in earlier patch, enabled here). - Cache ptmx dentry and use to update mode during remount (defined in earlier patch, enabled here). - Bugfix: explicitly ignore newinstance on remount (if newinstance was specified on remount of initial mount, it would be ignored but /proc/mounts would imply that the option was set) Changelog[v4]: - Update patch description to address H. Peter Anvin's comments - Consolidate multi-instance mode code under new config token, CONFIG_DEVPTS_MULTIPLE_INSTANCE. - Move usage-details from patch description to Documentation/fs/devpts.txt Changelog[v3]: - Rename new mount option to 'newinstance' - Create ptmx nodes only in 'newinstance' mounts - Bugfix: parse_mount_options() modifies @data but since we need to parse the @data twice (once in devpts_get_sb() and once during do_remount_sb()), parse a local copy of @data in devpts_get_sb(). (restructured code in devpts_get_sb() to fix this) Changelog[v2]: - Support both single-mount and multiple-mount semantics and provide '-onewmnt' option to select the semantics. Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/devpts')
-rw-r--r--fs/devpts/inode.c170
1 files changed, 163 insertions, 7 deletions
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 2d0eb2cf99e..b4a89fa2167 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -48,10 +48,11 @@ struct pts_mount_opts {
48 gid_t gid; 48 gid_t gid;
49 umode_t mode; 49 umode_t mode;
50 umode_t ptmxmode; 50 umode_t ptmxmode;
51 int newinstance;
51}; 52};
52 53
53enum { 54enum {
54 Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, 55 Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance,
55 Opt_err 56 Opt_err
56}; 57};
57 58
@@ -61,6 +62,7 @@ static const match_table_t tokens = {
61 {Opt_mode, "mode=%o"}, 62 {Opt_mode, "mode=%o"},
62#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES 63#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
63 {Opt_ptmxmode, "ptmxmode=%o"}, 64 {Opt_ptmxmode, "ptmxmode=%o"},
65 {Opt_newinstance, "newinstance"},
64#endif 66#endif
65 {Opt_err, NULL} 67 {Opt_err, NULL}
66}; 68};
@@ -78,13 +80,17 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
78 80
79static inline struct super_block *pts_sb_from_inode(struct inode *inode) 81static inline struct super_block *pts_sb_from_inode(struct inode *inode)
80{ 82{
83#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
81 if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) 84 if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
82 return inode->i_sb; 85 return inode->i_sb;
83 86#endif
84 return devpts_mnt->mnt_sb; 87 return devpts_mnt->mnt_sb;
85} 88}
86 89
87static int parse_mount_options(char *data, struct pts_mount_opts *opts) 90#define PARSE_MOUNT 0
91#define PARSE_REMOUNT 1
92
93static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
88{ 94{
89 char *p; 95 char *p;
90 96
@@ -95,6 +101,10 @@ static int parse_mount_options(char *data, struct pts_mount_opts *opts)
95 opts->mode = DEVPTS_DEFAULT_MODE; 101 opts->mode = DEVPTS_DEFAULT_MODE;
96 opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; 102 opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
97 103
104 /* newinstance makes sense only on initial mount */
105 if (op == PARSE_MOUNT)
106 opts->newinstance = 0;
107
98 while ((p = strsep(&data, ",")) != NULL) { 108 while ((p = strsep(&data, ",")) != NULL) {
99 substring_t args[MAX_OPT_ARGS]; 109 substring_t args[MAX_OPT_ARGS];
100 int token; 110 int token;
@@ -128,6 +138,11 @@ static int parse_mount_options(char *data, struct pts_mount_opts *opts)
128 return -EINVAL; 138 return -EINVAL;
129 opts->ptmxmode = option & S_IALLUGO; 139 opts->ptmxmode = option & S_IALLUGO;
130 break; 140 break;
141 case Opt_newinstance:
142 /* newinstance makes sense only on initial mount */
143 if (op == PARSE_MOUNT)
144 opts->newinstance = 1;
145 break;
131#endif 146#endif
132 default: 147 default:
133 printk(KERN_ERR "devpts: called with bogus options\n"); 148 printk(KERN_ERR "devpts: called with bogus options\n");
@@ -214,7 +229,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
214 struct pts_fs_info *fsi = DEVPTS_SB(sb); 229 struct pts_fs_info *fsi = DEVPTS_SB(sb);
215 struct pts_mount_opts *opts = &fsi->mount_opts; 230 struct pts_mount_opts *opts = &fsi->mount_opts;
216 231
217 err = parse_mount_options(data, opts); 232 err = parse_mount_options(data, PARSE_REMOUNT, opts);
218 233
219 /* 234 /*
220 * parse_mount_options() restores options to default values 235 * parse_mount_options() restores options to default values
@@ -309,8 +324,100 @@ static int compare_init_pts_sb(struct super_block *s, void *p)
309{ 324{
310 if (devpts_mnt) 325 if (devpts_mnt)
311 return devpts_mnt->mnt_sb == s; 326 return devpts_mnt->mnt_sb == s;
327 return 0;
328}
329
330#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
331/*
332 * Safely parse the mount options in @data and update @opts.
333 *
334 * devpts ends up parsing options two times during mount, due to the
335 * two modes of operation it supports. The first parse occurs in
336 * devpts_get_sb() when determining the mode (single-instance or
337 * multi-instance mode). The second parse happens in devpts_remount()
338 * or new_pts_mount() depending on the mode.
339 *
340 * Parsing of options modifies the @data making subsequent parsing
341 * incorrect. So make a local copy of @data and parse it.
342 *
343 * Return: 0 On success, -errno on error
344 */
345static int safe_parse_mount_options(void *data, struct pts_mount_opts *opts)
346{
347 int rc;
348 void *datacp;
349
350 if (!data)
351 return 0;
352
353 /* Use kstrdup() ? */
354 datacp = kmalloc(PAGE_SIZE, GFP_KERNEL);
355 if (!datacp)
356 return -ENOMEM;
357
358 memcpy(datacp, data, PAGE_SIZE);
359 rc = parse_mount_options((char *)datacp, PARSE_MOUNT, opts);
360 kfree(datacp);
361
362 return rc;
363}
364
365/*
366 * Mount a new (private) instance of devpts. PTYs created in this
367 * instance are independent of the PTYs in other devpts instances.
368 */
369static int new_pts_mount(struct file_system_type *fs_type, int flags,
370 void *data, struct vfsmount *mnt)
371{
372 int err;
373 struct pts_fs_info *fsi;
374 struct pts_mount_opts *opts;
375
376 printk(KERN_NOTICE "devpts: newinstance mount\n");
377
378 err = get_sb_nodev(fs_type, flags, data, devpts_fill_super, mnt);
379 if (err)
380 return err;
381
382 fsi = DEVPTS_SB(mnt->mnt_sb);
383 opts = &fsi->mount_opts;
384
385 err = parse_mount_options(data, PARSE_MOUNT, opts);
386 if (err)
387 goto fail;
388
389 err = mknod_ptmx(mnt->mnt_sb);
390 if (err)
391 goto fail;
312 392
313 return 0; 393 return 0;
394
395fail:
396 dput(mnt->mnt_sb->s_root);
397 deactivate_super(mnt->mnt_sb);
398 return err;
399}
400
401/*
402 * Check if 'newinstance' mount option was specified in @data.
403 *
404 * Return: -errno on error (eg: invalid mount options specified)
405 * : 1 if 'newinstance' mount option was specified
406 * : 0 if 'newinstance' mount option was NOT specified
407 */
408static int is_new_instance_mount(void *data)
409{
410 int rc;
411 struct pts_mount_opts opts;
412
413 if (!data)
414 return 0;
415
416 rc = safe_parse_mount_options(data, &opts);
417 if (!rc)
418 rc = opts.newinstance;
419
420 return rc;
314} 421}
315 422
316/* 423/*
@@ -358,11 +465,54 @@ static int get_init_pts_sb(struct file_system_type *fs_type, int flags,
358 return simple_set_mnt(mnt, s); 465 return simple_set_mnt(mnt, s);
359} 466}
360 467
468/*
469 * Mount or remount the initial kernel mount of devpts. This type of
470 * mount maintains the legacy, single-instance semantics, while the
471 * kernel still allows multiple-instances.
472 */
473static int init_pts_mount(struct file_system_type *fs_type, int flags,
474 void *data, struct vfsmount *mnt)
475{
476 int err;
477
478 err = get_init_pts_sb(fs_type, flags, data, mnt);
479 if (err)
480 return err;
481
482 err = mknod_ptmx(mnt->mnt_sb);
483 if (err) {
484 dput(mnt->mnt_sb->s_root);
485 deactivate_super(mnt->mnt_sb);
486 }
487
488 return err;
489}
490
361static int devpts_get_sb(struct file_system_type *fs_type, 491static int devpts_get_sb(struct file_system_type *fs_type,
362 int flags, const char *dev_name, void *data, struct vfsmount *mnt) 492 int flags, const char *dev_name, void *data, struct vfsmount *mnt)
363{ 493{
364 return get_init_pts_sb(fs_type, flags, data, mnt); 494 int new;
495
496 new = is_new_instance_mount(data);
497 if (new < 0)
498 return new;
499
500 if (new)
501 return new_pts_mount(fs_type, flags, data, mnt);
502
503 return init_pts_mount(fs_type, flags, data, mnt);
365} 504}
505#else
506/*
507 * This supports only the legacy single-instance semantics (no
508 * multiple-instance semantics)
509 */
510static int devpts_get_sb(struct file_system_type *fs_type, int flags,
511 const char *dev_name, void *data, struct vfsmount *mnt)
512{
513 return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt);
514}
515#endif
366 516
367static void devpts_kill_sb(struct super_block *sb) 517static void devpts_kill_sb(struct super_block *sb)
368{ 518{
@@ -488,12 +638,18 @@ void devpts_pty_kill(struct tty_struct *tty)
488 mutex_lock(&root->d_inode->i_mutex); 638 mutex_lock(&root->d_inode->i_mutex);
489 639
490 dentry = d_find_alias(inode); 640 dentry = d_find_alias(inode);
491 if (dentry && !IS_ERR(dentry)) { 641 if (IS_ERR(dentry))
642 goto out;
643
644 if (dentry) {
492 inode->i_nlink--; 645 inode->i_nlink--;
493 d_delete(dentry); 646 d_delete(dentry);
494 dput(dentry); 647 dput(dentry); // d_alloc_name() in devpts_pty_new()
495 } 648 }
496 649
650 dput(dentry); // d_find_alias above
651
652out:
497 mutex_unlock(&root->d_inode->i_mutex); 653 mutex_unlock(&root->d_inode->i_mutex);
498} 654}
499 655