diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2016-06-02 11:29:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-06-05 13:36:01 -0400 |
commit | eedf265aa003b4781de24cfed40a655a664457e6 (patch) | |
tree | 0e37f0a0c6fd15f7528aa3d3bfaec5685f083282 /fs/devpts/inode.c | |
parent | 049ec1b5a76d34a6980cccdb7c0baeb4eed7a993 (diff) |
devpts: Make each mount of devpts an independent filesystem.
The /dev/ptmx device node is changed to lookup the directory entry "pts"
in the same directory as the /dev/ptmx device node was opened in. If
there is a "pts" entry and that entry is a devpts filesystem /dev/ptmx
uses that filesystem. Otherwise the open of /dev/ptmx fails.
The DEVPTS_MULTIPLE_INSTANCES configuration option is removed, so that
userspace can now safely depend on each mount of devpts creating a new
instance of the filesystem.
Each mount of devpts is now a separate and equal filesystem.
Reserved ttys are now available to all instances of devpts where the
mounter is in the initial mount namespace.
A new vfs helper path_pts is introduced that finds a directory entry
named "pts" in the directory of the passed in path, and changes the
passed in path to point to it. The helper path_pts uses a function
path_parent_directory that was factored out of follow_dotdot.
In the implementation of devpts:
- devpts_mnt is killed as it is no longer meaningful if all mounts of
devpts are equal.
- pts_sb_from_inode is replaced by just inode->i_sb as all cached
inodes in the tty layer are now from the devpts filesystem.
- devpts_add_ref is rolled into the new function devpts_ptmx. And the
unnecessary inode hold is removed.
- devpts_del_ref is renamed devpts_release and reduced to just a
deacrivate_super.
- The newinstance mount option continues to be accepted but is now
ignored.
In devpts_fs.h definitions for when !CONFIG_UNIX98_PTYS are removed as
they are never used.
Documentation/filesystems/devices.txt is updated to describe the current
situation.
This has been verified to work properly on openwrt-15.05, centos5,
centos6, centos7, debian-6.0.2, debian-7.9, debian-8.2, ubuntu-14.04.3,
ubuntu-15.10, fedora23, magia-5, mint-17.3, opensuse-42.1,
slackware-14.1, gentoo-20151225 (13.0?), archlinux-2015-12-01. With the
caveat that on centos6 and on slackware-14.1 that there wind up being
two instances of the devpts filesystem mounted on /dev/pts, the lower
copy does not end up getting used.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Greg KH <greg@kroah.com>
Cc: Peter Hurley <peter@hurleysoftware.com>
Cc: Peter Anvin <hpa@zytor.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Serge Hallyn <serge.hallyn@ubuntu.com>
Cc: Willy Tarreau <w@1wt.eu>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk>
Cc: Jann Horn <jann@thejh.net>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: Florian Weimer <fw@deneb.enyo.de>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/devpts/inode.c')
-rw-r--r-- | fs/devpts/inode.c | 191 |
1 files changed, 55 insertions, 136 deletions
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 0b2954d7172d..37c134a132c7 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c | |||
@@ -95,8 +95,6 @@ static struct ctl_table pty_root_table[] = { | |||
95 | 95 | ||
96 | static DEFINE_MUTEX(allocated_ptys_lock); | 96 | static DEFINE_MUTEX(allocated_ptys_lock); |
97 | 97 | ||
98 | static struct vfsmount *devpts_mnt; | ||
99 | |||
100 | struct pts_mount_opts { | 98 | struct pts_mount_opts { |
101 | int setuid; | 99 | int setuid; |
102 | int setgid; | 100 | int setgid; |
@@ -104,7 +102,7 @@ struct pts_mount_opts { | |||
104 | kgid_t gid; | 102 | kgid_t gid; |
105 | umode_t mode; | 103 | umode_t mode; |
106 | umode_t ptmxmode; | 104 | umode_t ptmxmode; |
107 | int newinstance; | 105 | int reserve; |
108 | int max; | 106 | int max; |
109 | }; | 107 | }; |
110 | 108 | ||
@@ -117,11 +115,9 @@ static const match_table_t tokens = { | |||
117 | {Opt_uid, "uid=%u"}, | 115 | {Opt_uid, "uid=%u"}, |
118 | {Opt_gid, "gid=%u"}, | 116 | {Opt_gid, "gid=%u"}, |
119 | {Opt_mode, "mode=%o"}, | 117 | {Opt_mode, "mode=%o"}, |
120 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
121 | {Opt_ptmxmode, "ptmxmode=%o"}, | 118 | {Opt_ptmxmode, "ptmxmode=%o"}, |
122 | {Opt_newinstance, "newinstance"}, | 119 | {Opt_newinstance, "newinstance"}, |
123 | {Opt_max, "max=%d"}, | 120 | {Opt_max, "max=%d"}, |
124 | #endif | ||
125 | {Opt_err, NULL} | 121 | {Opt_err, NULL} |
126 | }; | 122 | }; |
127 | 123 | ||
@@ -137,15 +133,48 @@ static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) | |||
137 | return sb->s_fs_info; | 133 | return sb->s_fs_info; |
138 | } | 134 | } |
139 | 135 | ||
140 | static inline struct super_block *pts_sb_from_inode(struct inode *inode) | 136 | struct pts_fs_info *devpts_acquire(struct file *filp) |
141 | { | 137 | { |
142 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | 138 | struct pts_fs_info *result; |
143 | if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) | 139 | struct path path; |
144 | return inode->i_sb; | 140 | struct super_block *sb; |
145 | #endif | 141 | int err; |
146 | if (!devpts_mnt) | 142 | |
147 | return NULL; | 143 | path = filp->f_path; |
148 | return devpts_mnt->mnt_sb; | 144 | path_get(&path); |
145 | |||
146 | /* Has the devpts filesystem already been found? */ | ||
147 | sb = path.mnt->mnt_sb; | ||
148 | if (sb->s_magic != DEVPTS_SUPER_MAGIC) { | ||
149 | /* Is a devpts filesystem at "pts" in the same directory? */ | ||
150 | err = path_pts(&path); | ||
151 | if (err) { | ||
152 | result = ERR_PTR(err); | ||
153 | goto out; | ||
154 | } | ||
155 | |||
156 | /* Is the path the root of a devpts filesystem? */ | ||
157 | result = ERR_PTR(-ENODEV); | ||
158 | sb = path.mnt->mnt_sb; | ||
159 | if ((sb->s_magic != DEVPTS_SUPER_MAGIC) || | ||
160 | (path.mnt->mnt_root != sb->s_root)) | ||
161 | goto out; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * pty code needs to hold extra references in case of last /dev/tty close | ||
166 | */ | ||
167 | atomic_inc(&sb->s_active); | ||
168 | result = DEVPTS_SB(sb); | ||
169 | |||
170 | out: | ||
171 | path_put(&path); | ||
172 | return result; | ||
173 | } | ||
174 | |||
175 | void devpts_release(struct pts_fs_info *fsi) | ||
176 | { | ||
177 | deactivate_super(fsi->sb); | ||
149 | } | 178 | } |
150 | 179 | ||
151 | #define PARSE_MOUNT 0 | 180 | #define PARSE_MOUNT 0 |
@@ -154,9 +183,7 @@ static inline struct super_block *pts_sb_from_inode(struct inode *inode) | |||
154 | /* | 183 | /* |
155 | * parse_mount_options(): | 184 | * parse_mount_options(): |
156 | * Set @opts to mount options specified in @data. If an option is not | 185 | * Set @opts to mount options specified in @data. If an option is not |
157 | * specified in @data, set it to its default value. The exception is | 186 | * specified in @data, set it to its default value. |
158 | * 'newinstance' option which can only be set/cleared on a mount (i.e. | ||
159 | * cannot be changed during remount). | ||
160 | * | 187 | * |
161 | * Note: @data may be NULL (in which case all options are set to default). | 188 | * Note: @data may be NULL (in which case all options are set to default). |
162 | */ | 189 | */ |
@@ -174,9 +201,12 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
174 | opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; | 201 | opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; |
175 | opts->max = NR_UNIX98_PTY_MAX; | 202 | opts->max = NR_UNIX98_PTY_MAX; |
176 | 203 | ||
177 | /* newinstance makes sense only on initial mount */ | 204 | /* Only allow instances mounted from the initial mount |
205 | * namespace to tap the reserve pool of ptys. | ||
206 | */ | ||
178 | if (op == PARSE_MOUNT) | 207 | if (op == PARSE_MOUNT) |
179 | opts->newinstance = 0; | 208 | opts->reserve = |
209 | (current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns); | ||
180 | 210 | ||
181 | while ((p = strsep(&data, ",")) != NULL) { | 211 | while ((p = strsep(&data, ",")) != NULL) { |
182 | substring_t args[MAX_OPT_ARGS]; | 212 | substring_t args[MAX_OPT_ARGS]; |
@@ -211,16 +241,12 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
211 | return -EINVAL; | 241 | return -EINVAL; |
212 | opts->mode = option & S_IALLUGO; | 242 | opts->mode = option & S_IALLUGO; |
213 | break; | 243 | break; |
214 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
215 | case Opt_ptmxmode: | 244 | case Opt_ptmxmode: |
216 | if (match_octal(&args[0], &option)) | 245 | if (match_octal(&args[0], &option)) |
217 | return -EINVAL; | 246 | return -EINVAL; |
218 | opts->ptmxmode = option & S_IALLUGO; | 247 | opts->ptmxmode = option & S_IALLUGO; |
219 | break; | 248 | break; |
220 | case Opt_newinstance: | 249 | case Opt_newinstance: |
221 | /* newinstance makes sense only on initial mount */ | ||
222 | if (op == PARSE_MOUNT) | ||
223 | opts->newinstance = 1; | ||
224 | break; | 250 | break; |
225 | case Opt_max: | 251 | case Opt_max: |
226 | if (match_int(&args[0], &option) || | 252 | if (match_int(&args[0], &option) || |
@@ -228,7 +254,6 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
228 | return -EINVAL; | 254 | return -EINVAL; |
229 | opts->max = option; | 255 | opts->max = option; |
230 | break; | 256 | break; |
231 | #endif | ||
232 | default: | 257 | default: |
233 | pr_err("called with bogus options\n"); | 258 | pr_err("called with bogus options\n"); |
234 | return -EINVAL; | 259 | return -EINVAL; |
@@ -238,7 +263,6 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) | |||
238 | return 0; | 263 | return 0; |
239 | } | 264 | } |
240 | 265 | ||
241 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
242 | static int mknod_ptmx(struct super_block *sb) | 266 | static int mknod_ptmx(struct super_block *sb) |
243 | { | 267 | { |
244 | int mode; | 268 | int mode; |
@@ -305,12 +329,6 @@ static void update_ptmx_mode(struct pts_fs_info *fsi) | |||
305 | inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode; | 329 | inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode; |
306 | } | 330 | } |
307 | } | 331 | } |
308 | #else | ||
309 | static inline void update_ptmx_mode(struct pts_fs_info *fsi) | ||
310 | { | ||
311 | return; | ||
312 | } | ||
313 | #endif | ||
314 | 332 | ||
315 | static int devpts_remount(struct super_block *sb, int *flags, char *data) | 333 | static int devpts_remount(struct super_block *sb, int *flags, char *data) |
316 | { | 334 | { |
@@ -344,11 +362,9 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root) | |||
344 | seq_printf(seq, ",gid=%u", | 362 | seq_printf(seq, ",gid=%u", |
345 | from_kgid_munged(&init_user_ns, opts->gid)); | 363 | from_kgid_munged(&init_user_ns, opts->gid)); |
346 | seq_printf(seq, ",mode=%03o", opts->mode); | 364 | seq_printf(seq, ",mode=%03o", opts->mode); |
347 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
348 | seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); | 365 | seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); |
349 | if (opts->max < NR_UNIX98_PTY_MAX) | 366 | if (opts->max < NR_UNIX98_PTY_MAX) |
350 | seq_printf(seq, ",max=%d", opts->max); | 367 | seq_printf(seq, ",max=%d", opts->max); |
351 | #endif | ||
352 | 368 | ||
353 | return 0; | 369 | return 0; |
354 | } | 370 | } |
@@ -410,40 +426,11 @@ fail: | |||
410 | return -ENOMEM; | 426 | return -ENOMEM; |
411 | } | 427 | } |
412 | 428 | ||
413 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
414 | static int compare_init_pts_sb(struct super_block *s, void *p) | ||
415 | { | ||
416 | if (devpts_mnt) | ||
417 | return devpts_mnt->mnt_sb == s; | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | /* | 429 | /* |
422 | * devpts_mount() | 430 | * devpts_mount() |
423 | * | 431 | * |
424 | * If the '-o newinstance' mount option was specified, mount a new | 432 | * Mount a new (private) instance of devpts. PTYs created in this |
425 | * (private) instance of devpts. PTYs created in this instance are | 433 | * instance are independent of the PTYs in other devpts instances. |
426 | * independent of the PTYs in other devpts instances. | ||
427 | * | ||
428 | * If the '-o newinstance' option was not specified, mount/remount the | ||
429 | * initial kernel mount of devpts. This type of mount gives the | ||
430 | * legacy, single-instance semantics. | ||
431 | * | ||
432 | * The 'newinstance' option is needed to support multiple namespace | ||
433 | * semantics in devpts while preserving backward compatibility of the | ||
434 | * current 'single-namespace' semantics. i.e all mounts of devpts | ||
435 | * without the 'newinstance' mount option should bind to the initial | ||
436 | * kernel mount, like mount_single(). | ||
437 | * | ||
438 | * Mounts with 'newinstance' option create a new, private namespace. | ||
439 | * | ||
440 | * NOTE: | ||
441 | * | ||
442 | * For single-mount semantics, devpts cannot use mount_single(), | ||
443 | * because mount_single()/sget() find and use the super-block from | ||
444 | * the most recent mount of devpts. But that recent mount may be a | ||
445 | * 'newinstance' mount and mount_single() would pick the newinstance | ||
446 | * super-block instead of the initial super-block. | ||
447 | */ | 434 | */ |
448 | static struct dentry *devpts_mount(struct file_system_type *fs_type, | 435 | static struct dentry *devpts_mount(struct file_system_type *fs_type, |
449 | int flags, const char *dev_name, void *data) | 436 | int flags, const char *dev_name, void *data) |
@@ -456,18 +443,7 @@ static struct dentry *devpts_mount(struct file_system_type *fs_type, | |||
456 | if (error) | 443 | if (error) |
457 | return ERR_PTR(error); | 444 | return ERR_PTR(error); |
458 | 445 | ||
459 | /* Require newinstance for all user namespace mounts to ensure | 446 | s = sget(fs_type, NULL, set_anon_super, flags, NULL); |
460 | * the mount options are not changed. | ||
461 | */ | ||
462 | if ((current_user_ns() != &init_user_ns) && !opts.newinstance) | ||
463 | return ERR_PTR(-EINVAL); | ||
464 | |||
465 | if (opts.newinstance) | ||
466 | s = sget(fs_type, NULL, set_anon_super, flags, NULL); | ||
467 | else | ||
468 | s = sget(fs_type, compare_init_pts_sb, set_anon_super, flags, | ||
469 | NULL); | ||
470 | |||
471 | if (IS_ERR(s)) | 447 | if (IS_ERR(s)) |
472 | return ERR_CAST(s); | 448 | return ERR_CAST(s); |
473 | 449 | ||
@@ -491,18 +467,6 @@ out_undo_sget: | |||
491 | return ERR_PTR(error); | 467 | return ERR_PTR(error); |
492 | } | 468 | } |
493 | 469 | ||
494 | #else | ||
495 | /* | ||
496 | * This supports only the legacy single-instance semantics (no | ||
497 | * multiple-instance semantics) | ||
498 | */ | ||
499 | static struct dentry *devpts_mount(struct file_system_type *fs_type, int flags, | ||
500 | const char *dev_name, void *data) | ||
501 | { | ||
502 | return mount_single(fs_type, flags, data, devpts_fill_super); | ||
503 | } | ||
504 | #endif | ||
505 | |||
506 | static void devpts_kill_sb(struct super_block *sb) | 470 | static void devpts_kill_sb(struct super_block *sb) |
507 | { | 471 | { |
508 | struct pts_fs_info *fsi = DEVPTS_SB(sb); | 472 | struct pts_fs_info *fsi = DEVPTS_SB(sb); |
@@ -516,9 +480,7 @@ static struct file_system_type devpts_fs_type = { | |||
516 | .name = "devpts", | 480 | .name = "devpts", |
517 | .mount = devpts_mount, | 481 | .mount = devpts_mount, |
518 | .kill_sb = devpts_kill_sb, | 482 | .kill_sb = devpts_kill_sb, |
519 | #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES | ||
520 | .fs_flags = FS_USERNS_MOUNT | FS_USERNS_DEV_MOUNT, | 483 | .fs_flags = FS_USERNS_MOUNT | FS_USERNS_DEV_MOUNT, |
521 | #endif | ||
522 | }; | 484 | }; |
523 | 485 | ||
524 | /* | 486 | /* |
@@ -531,16 +493,13 @@ int devpts_new_index(struct pts_fs_info *fsi) | |||
531 | int index; | 493 | int index; |
532 | int ida_ret; | 494 | int ida_ret; |
533 | 495 | ||
534 | if (!fsi) | ||
535 | return -ENODEV; | ||
536 | |||
537 | retry: | 496 | retry: |
538 | if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) | 497 | if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) |
539 | return -ENOMEM; | 498 | return -ENOMEM; |
540 | 499 | ||
541 | mutex_lock(&allocated_ptys_lock); | 500 | mutex_lock(&allocated_ptys_lock); |
542 | if (pty_count >= pty_limit - | 501 | if (pty_count >= (pty_limit - |
543 | (fsi->mount_opts.newinstance ? pty_reserve : 0)) { | 502 | (fsi->mount_opts.reserve ? 0 : pty_reserve))) { |
544 | mutex_unlock(&allocated_ptys_lock); | 503 | mutex_unlock(&allocated_ptys_lock); |
545 | return -ENOSPC; | 504 | return -ENOSPC; |
546 | } | 505 | } |
@@ -571,30 +530,6 @@ void devpts_kill_index(struct pts_fs_info *fsi, int idx) | |||
571 | mutex_unlock(&allocated_ptys_lock); | 530 | mutex_unlock(&allocated_ptys_lock); |
572 | } | 531 | } |
573 | 532 | ||
574 | /* | ||
575 | * pty code needs to hold extra references in case of last /dev/tty close | ||
576 | */ | ||
577 | struct pts_fs_info *devpts_get_ref(struct inode *ptmx_inode, struct file *file) | ||
578 | { | ||
579 | struct super_block *sb; | ||
580 | struct pts_fs_info *fsi; | ||
581 | |||
582 | sb = pts_sb_from_inode(ptmx_inode); | ||
583 | if (!sb) | ||
584 | return NULL; | ||
585 | fsi = DEVPTS_SB(sb); | ||
586 | if (!fsi) | ||
587 | return NULL; | ||
588 | |||
589 | atomic_inc(&sb->s_active); | ||
590 | return fsi; | ||
591 | } | ||
592 | |||
593 | void devpts_put_ref(struct pts_fs_info *fsi) | ||
594 | { | ||
595 | deactivate_super(fsi->sb); | ||
596 | } | ||
597 | |||
598 | /** | 533 | /** |
599 | * devpts_pty_new -- create a new inode in /dev/pts/ | 534 | * devpts_pty_new -- create a new inode in /dev/pts/ |
600 | * @ptmx_inode: inode of the master | 535 | * @ptmx_inode: inode of the master |
@@ -607,16 +542,12 @@ void devpts_put_ref(struct pts_fs_info *fsi) | |||
607 | struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) | 542 | struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) |
608 | { | 543 | { |
609 | struct dentry *dentry; | 544 | struct dentry *dentry; |
610 | struct super_block *sb; | 545 | struct super_block *sb = fsi->sb; |
611 | struct inode *inode; | 546 | struct inode *inode; |
612 | struct dentry *root; | 547 | struct dentry *root; |
613 | struct pts_mount_opts *opts; | 548 | struct pts_mount_opts *opts; |
614 | char s[12]; | 549 | char s[12]; |
615 | 550 | ||
616 | if (!fsi) | ||
617 | return ERR_PTR(-ENODEV); | ||
618 | |||
619 | sb = fsi->sb; | ||
620 | root = sb->s_root; | 551 | root = sb->s_root; |
621 | opts = &fsi->mount_opts; | 552 | opts = &fsi->mount_opts; |
622 | 553 | ||
@@ -676,20 +607,8 @@ void devpts_pty_kill(struct dentry *dentry) | |||
676 | static int __init init_devpts_fs(void) | 607 | static int __init init_devpts_fs(void) |
677 | { | 608 | { |
678 | int err = register_filesystem(&devpts_fs_type); | 609 | int err = register_filesystem(&devpts_fs_type); |
679 | struct ctl_table_header *table; | ||
680 | |||
681 | if (!err) { | 610 | if (!err) { |
682 | struct vfsmount *mnt; | 611 | register_sysctl_table(pty_root_table); |
683 | |||
684 | table = register_sysctl_table(pty_root_table); | ||
685 | mnt = kern_mount(&devpts_fs_type); | ||
686 | if (IS_ERR(mnt)) { | ||
687 | err = PTR_ERR(mnt); | ||
688 | unregister_filesystem(&devpts_fs_type); | ||
689 | unregister_sysctl_table(table); | ||
690 | } else { | ||
691 | devpts_mnt = mnt; | ||
692 | } | ||
693 | } | 612 | } |
694 | return err; | 613 | return err; |
695 | } | 614 | } |