- Modify opening /dev/ptmx so that it opens /dev/pts/ptmx As it appares nearly impossible to get udev to create a symlink do this with a little sprinkle of in kernel magic. - Cleanup the devpts mount code and only leave the code for new instance. Tested on debian and ubuntu without any problems. Acked-by: "Serge E. Hallyn" <serge@xxxxxxxxxx> Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> --- drivers/tty/pty.c | 4 + fs/devpts/inode.c | 180 +++++++++++++++++---------------------------- include/linux/devpts_fs.h | 5 + 3 files changed, 77 insertions(+), 112 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 6beb7e1..a605074 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -615,6 +615,10 @@ static int ptmx_open(struct inode *inode, struct file *filp) int retval; int index; + inode = devpts_redirect(filp); + if (IS_ERR(inode)) + return PTR_ERR(inode); + nonseekable_open(inode, filp); retval = tty_alloc_file(filp); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 61b54aa..38136ae 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -25,6 +25,7 @@ #include <linux/parser.h> #include <linux/fsnotify.h> #include <linux/seq_file.h> +#include <linux/file.h> #define DEVPTS_DEFAULT_MODE 0600 @@ -94,7 +95,6 @@ struct pts_mount_opts { kgid_t gid; umode_t mode; umode_t ptmxmode; - int newinstance; int max; }; @@ -116,7 +116,6 @@ static const match_table_t tokens = { struct pts_fs_info { struct ida allocated_ptys; struct pts_mount_opts mount_opts; - struct dentry *ptmx_dentry; }; static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) @@ -157,10 +156,6 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; opts->max = NR_UNIX98_PTY_MAX; - /* newinstance makes sense only on initial mount */ - if (op == PARSE_MOUNT) - opts->newinstance = 0; - while ((p = strsep(&data, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; @@ -200,9 +195,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) opts->ptmxmode = option & S_IALLUGO; break; case Opt_newinstance: - /* newinstance makes sense only on initial mount */ - if (op == PARSE_MOUNT) - opts->newinstance = 1; + /* Historical */ break; case Opt_max: if (match_int(&args[0], &option) || @@ -229,14 +222,6 @@ static int mknod_ptmx(struct super_block *sb) struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; - 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\n"); @@ -261,39 +246,17 @@ static int mknod_ptmx(struct super_block *sb) d_add(dentry, inode); - fsi->ptmx_dentry = dentry; rc = 0; 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; - } -} - static int devpts_remount(struct super_block *sb, int *flags, char *data) { - int err; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; - err = parse_mount_options(data, PARSE_REMOUNT, opts); - - /* - * 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; + return parse_mount_options(data, PARSE_REMOUNT, opts); } static int devpts_show_options(struct seq_file *seq, struct dentry *root) @@ -319,7 +282,7 @@ static const struct super_operations devpts_sops = { .show_options = devpts_show_options, }; -static void *new_pts_fs_info(void) +static struct pts_fs_info *new_pts_fs_info(void) { struct pts_fs_info *fsi; @@ -338,6 +301,8 @@ static int devpts_fill_super(struct super_block *s, void *data, int silent) { struct inode *inode; + struct pts_fs_info *fsi; + int error = -ENOMEM; s->s_blocksize = 1024; s->s_blocksize_bits = 10; @@ -345,13 +310,18 @@ devpts_fill_super(struct super_block *s, void *data, int silent) s->s_op = &devpts_sops; s->s_time_gran = 1; - s->s_fs_info = new_pts_fs_info(); - if (!s->s_fs_info) + fsi = new_pts_fs_info(); + s->s_fs_info = fsi; + if (!fsi) goto fail; + error = parse_mount_options(data, PARSE_MOUNT, &fsi->mount_opts); + if (error) + goto fail_kfree; + inode = new_inode(s); if (!inode) - goto fail; + goto fail_kfree; inode->i_ino = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; @@ -360,87 +330,40 @@ devpts_fill_super(struct super_block *s, void *data, int silent) set_nlink(inode, 2); s->s_root = d_make_root(inode); - if (s->s_root) - return 0; + if (!s->s_root) { + printk(KERN_ERR "devpts: get root dentry failed\n"); + goto fail_iput; + } - printk(KERN_ERR "devpts: get root dentry failed\n"); + error = mknod_ptmx(s); + if (error) + goto fail_put_root; + + return 0; +fail_put_root: + dput(s->s_root); + s->s_root = NULL; +fail_iput: + iput(inode); +fail_kfree: + kfree(fsi); fail: return -ENOMEM; } -static int compare_init_pts_sb(struct super_block *s, void *p) -{ - if (devpts_mnt) - return devpts_mnt->mnt_sb == s; - return 0; -} /* * devpts_mount() * - * 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. - * - * 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 - * kernel mount, like mount_single(). - * - * Mounts with 'newinstance' option create a new, private namespace. + * Mount a new (private) instance of devpts. PTYs created in this + * instance are independent of the PTYs in other devpts instances. * - * NOTE: - * - * For single-mount semantics, devpts cannot use mount_single(), - * because mount_single()/sget() find and use the super-block from - * the most recent mount of devpts. But that recent mount may be a - * 'newinstance' mount and mount_single() would pick the newinstance - * super-block instead of the initial super-block. */ static struct dentry *devpts_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - int error; - struct pts_mount_opts opts; - struct super_block *s; - - error = parse_mount_options(data, PARSE_MOUNT, &opts); - if (error) - return ERR_PTR(error); - - if (opts.newinstance) - s = sget(fs_type, NULL, set_anon_super, flags, NULL); - else - s = sget(fs_type, compare_init_pts_sb, set_anon_super, flags, - NULL); - - if (IS_ERR(s)) - return ERR_CAST(s); - - if (!s->s_root) { - error = devpts_fill_super(s, data, flags & MS_SILENT ? 1 : 0); - if (error) - goto out_undo_sget; - s->s_flags |= MS_ACTIVE; - } - - memcpy(&(DEVPTS_SB(s))->mount_opts, &opts, sizeof(opts)); - - error = mknod_ptmx(s); - if (error) - goto out_undo_sget; - - return dget(s->s_root); - -out_undo_sget: - deactivate_locked_super(s); - return ERR_PTR(error); + return mount_nodev(fs_type, flags, data, devpts_fill_super); } static void devpts_kill_sb(struct super_block *sb) @@ -457,6 +380,40 @@ static struct file_system_type devpts_fs_type = { .kill_sb = devpts_kill_sb, }; +struct inode *devpts_redirect(struct file *filp) +{ + struct inode *inode; + struct file *filp2; + + /* Is the inode already a devpts inode? */ + inode = filp->f_dentry->d_inode; + if (filp->f_dentry->d_sb->s_magic == DEVPTS_SUPER_MAGIC) + goto out; + + /* Is f_dentry->d_parent usable? */ + inode = ERR_PTR(-ENODEV); + if (filp->f_vfsmnt->mnt_root == filp->f_dentry) + goto out; + + /* Is there a devpts inode we can use instead? */ + + filp2 = file_open_root(filp->f_dentry->d_parent, filp->f_vfsmnt, + "pts/ptmx", O_PATH); + if (!IS_ERR(filp2)) { + if (filp2->f_dentry->d_sb->s_magic == DEVPTS_SUPER_MAGIC) { + struct path old; + old = filp->f_path; + filp->f_path = filp2->f_path; + inode = filp->f_dentry->d_inode; + path_get(&filp->f_path); + path_put(&old); + } + fput(filp2); + } +out: + return inode; +} + /* * The normal naming convention is simply /dev/pts/<number>; this conforms * to the System V naming convention @@ -474,8 +431,7 @@ retry: return -ENOMEM; mutex_lock(&allocated_ptys_lock); - if (pty_count >= pty_limit - - (fsi->mount_opts.newinstance ? pty_reserve : 0)) { + if (pty_count >= pty_limit - pty_reserve) { mutex_unlock(&allocated_ptys_lock); return -ENOSPC; } diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 2d539ae..fe2b41f 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -20,6 +20,7 @@ #ifdef CONFIG_UNIX98_PTYS +struct inode *devpts_redirect(struct file *filp); int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ @@ -32,6 +33,10 @@ void devpts_pty_kill(struct tty_struct *tty); #else /* Dummy stubs in the no-pty case */ +static inline struct inode *devpts_redirect(struct file *filp) +{ + return ERR_PTR(-ENODEV); +} static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } static inline int devpts_pty_new(struct inode *ptmx_inode, -- 1.7.5.4 _______________________________________________ Containers mailing list Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/containers