Commit 1f8f1e296583f9f832c2fe7b5a219675b74bf43e

Authored by Sukadev Bhattiprolu
Committed by Linus Torvalds
1 parent e4adca27bc

Define mknod_ptmx()

/dev/ptmx is closely tied to the devpts filesystem. An open of /dev/ptmx,
allocates the next pty index and the associated device shows up in the
devpts fs as /dev/pts/n.

Wih multiple instancs of devpts filesystem, during an open of /dev/ptmx
we would be unable to determine which instance of the devpts is being
accessed.

So we move the 'ptmx' node into /dev/pts and use the inode of the 'ptmx'
node to identify the superblock and hence the devpts instance.  This patch
adds ability for the kernel to internally create the [ptmx, c, 5:2] device
when mounting devpts filesystem.  Since the ptmx node in devpts is new and
may surprise some userspace scripts, the default permissions for the new
node is 0000.  These permissions can be changed either using chmod or by
remounting with the new '-o ptmxmode=0666' mount option.

Changelog[v5]:
	- [Serge Hallyn bugfix]: Letting new_inode() assign inode number to
	  ptmx can collide with hand-assigning inode numbers to ptys. So,
	  hand-assign specific inode number to ptmx node also.
	- [Serge Hallyn]: Maybe safer to grab root dentry mutex while creating
	  ptmx node
	- [Bugfix with Serge Hallyn] Replace lookup_one_len() in mknod_ptmx()
	  wih d_alloc_name() (lookup during ->get_sb() locks up system). To
	  simplify patchset, fold the ptmx_dentry patch into this.

Changelog[v4]:
	- Change default permissions of pts/ptmx node to 0000.
	- Move code for ptmxmode under #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES.

Changelog[v3]:
	- Rename ptmx_mode to ptmxmode (for consistency with 'newinstance')

Changelog[v2]:
	- [H. Peter Anvin] Remove mknod() system call support and create the
	  ptmx node internally.

Changelog[v1]:
	- Earlier version of this patch enabled creating /dev/pts/tty as
	  well. As pointed out by Al Viro and H. Peter Anvin, that is not
	  really necessary.

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>

Showing 1 changed file with 110 additions and 5 deletions Side-by-side Diff

... ... @@ -27,6 +27,13 @@
27 27 #define DEVPTS_SUPER_MAGIC 0x1cd1
28 28  
29 29 #define DEVPTS_DEFAULT_MODE 0600
  30 +/*
  31 + * ptmx is a new node in /dev/pts and will be unused in legacy (single-
  32 + * instance) mode. To prevent surprises in user space, set permissions of
  33 + * ptmx to 0. Use 'chmod' or remount with '-o ptmxmode' to set meaningful
  34 + * permissions.
  35 + */
  36 +#define DEVPTS_DEFAULT_PTMX_MODE 0000
30 37 #define PTMX_MINOR 2
31 38  
32 39 extern int pty_limit; /* Config limit on Unix98 ptys */
33 40  
... ... @@ -40,10 +47,11 @@
40 47 uid_t uid;
41 48 gid_t gid;
42 49 umode_t mode;
  50 + umode_t ptmxmode;
43 51 };
44 52  
45 53 enum {
46   - Opt_uid, Opt_gid, Opt_mode,
  54 + Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode,
47 55 Opt_err
48 56 };
49 57  
50 58  
... ... @@ -51,12 +59,16 @@
51 59 {Opt_uid, "uid=%u"},
52 60 {Opt_gid, "gid=%u"},
53 61 {Opt_mode, "mode=%o"},
  62 +#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  63 + {Opt_ptmxmode, "ptmxmode=%o"},
  64 +#endif
54 65 {Opt_err, NULL}
55 66 };
56 67  
57 68 struct pts_fs_info {
58 69 struct ida allocated_ptys;
59 70 struct pts_mount_opts mount_opts;
  71 + struct dentry *ptmx_dentry;
60 72 };
61 73  
62 74 static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
... ... @@ -81,6 +93,7 @@
81 93 opts->uid = 0;
82 94 opts->gid = 0;
83 95 opts->mode = DEVPTS_DEFAULT_MODE;
  96 + opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
84 97  
85 98 while ((p = strsep(&data, ",")) != NULL) {
86 99 substring_t args[MAX_OPT_ARGS];
... ... @@ -109,6 +122,13 @@
109 122 return -EINVAL;
110 123 opts->mode = option & S_IALLUGO;
111 124 break;
  125 +#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  126 + case Opt_ptmxmode:
  127 + if (match_octal(&args[0], &option))
  128 + return -EINVAL;
  129 + opts->ptmxmode = option & S_IALLUGO;
  130 + break;
  131 +#endif
112 132 default:
113 133 printk(KERN_ERR "devpts: called with bogus options\n");
114 134 return -EINVAL;
115 135  
116 136  
... ... @@ -118,12 +138,93 @@
118 138 return 0;
119 139 }
120 140  
  141 +#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  142 +static int mknod_ptmx(struct super_block *sb)
  143 +{
  144 + int mode;
  145 + int rc = -ENOMEM;
  146 + struct dentry *dentry;
  147 + struct inode *inode;
  148 + struct dentry *root = sb->s_root;
  149 + struct pts_fs_info *fsi = DEVPTS_SB(sb);
  150 + struct pts_mount_opts *opts = &fsi->mount_opts;
  151 +
  152 + mutex_lock(&root->d_inode->i_mutex);
  153 +
  154 + /* If we have already created ptmx node, return */
  155 + if (fsi->ptmx_dentry) {
  156 + rc = 0;
  157 + goto out;
  158 + }
  159 +
  160 + dentry = d_alloc_name(root, "ptmx");
  161 + if (!dentry) {
  162 + printk(KERN_NOTICE "Unable to alloc dentry for ptmx node\n");
  163 + goto out;
  164 + }
  165 +
  166 + /*
  167 + * Create a new 'ptmx' node in this mount of devpts.
  168 + */
  169 + inode = new_inode(sb);
  170 + if (!inode) {
  171 + printk(KERN_ERR "Unable to alloc inode for ptmx node\n");
  172 + dput(dentry);
  173 + goto out;
  174 + }
  175 +
  176 + inode->i_ino = 2;
  177 + inode->i_uid = inode->i_gid = 0;
  178 + inode->i_blocks = 0;
  179 + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
  180 +
  181 + mode = S_IFCHR|opts->ptmxmode;
  182 + init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2));
  183 +
  184 + d_add(dentry, inode);
  185 +
  186 + fsi->ptmx_dentry = dentry;
  187 + rc = 0;
  188 +
  189 + printk(KERN_DEBUG "Created ptmx node in devpts ino %lu\n",
  190 + inode->i_ino);
  191 +out:
  192 + mutex_unlock(&root->d_inode->i_mutex);
  193 + return rc;
  194 +}
  195 +
  196 +static void update_ptmx_mode(struct pts_fs_info *fsi)
  197 +{
  198 + struct inode *inode;
  199 + if (fsi->ptmx_dentry) {
  200 + inode = fsi->ptmx_dentry->d_inode;
  201 + inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode;
  202 + }
  203 +}
  204 +#else
  205 +static inline void update_ptmx_mode(struct pts_fs_info *fsi)
  206 +{
  207 + return;
  208 +}
  209 +#endif
  210 +
121 211 static int devpts_remount(struct super_block *sb, int *flags, char *data)
122 212 {
  213 + int err;
123 214 struct pts_fs_info *fsi = DEVPTS_SB(sb);
124 215 struct pts_mount_opts *opts = &fsi->mount_opts;
125 216  
126   - return parse_mount_options(data, opts);
  217 + err = parse_mount_options(data, opts);
  218 +
  219 + /*
  220 + * parse_mount_options() restores options to default values
  221 + * before parsing and may have changed ptmxmode. So, update the
  222 + * mode in the inode too. Bogus options don't fail the remount,
  223 + * so do this even on error return.
  224 + */
  225 + update_ptmx_mode(fsi);
  226 +
  227 + return err;
127 228 }
128 229  
129 230 static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs)
... ... @@ -136,6 +237,9 @@
136 237 if (opts->setgid)
137 238 seq_printf(seq, ",gid=%u", opts->gid);
138 239 seq_printf(seq, ",mode=%03o", opts->mode);
  240 +#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
  241 + seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode);
  242 +#endif
139 243  
140 244 return 0;
141 245 }
... ... @@ -156,6 +260,7 @@
156 260  
157 261 ida_init(&fsi->allocated_ptys);
158 262 fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
  263 + fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
159 264  
160 265 return fsi;
161 266 }
... ... @@ -163,7 +268,7 @@
163 268 static int
164 269 devpts_fill_super(struct super_block *s, void *data, int silent)
165 270 {
166   - struct inode * inode;
  271 + struct inode *inode;
167 272  
168 273 s->s_blocksize = 1024;
169 274 s->s_blocksize_bits = 10;
... ... @@ -190,7 +295,7 @@
190 295 s->s_root = d_alloc_root(inode);
191 296 if (s->s_root)
192 297 return 0;
193   -
  298 +
194 299 printk("devpts: get root dentry failed\n");
195 300 iput(inode);
196 301  
... ... @@ -211,7 +316,7 @@
211 316 struct pts_fs_info *fsi = DEVPTS_SB(sb);
212 317  
213 318 kfree(fsi);
214   - kill_anon_super(sb);
  319 + kill_litter_super(sb);
215 320 }
216 321  
217 322 static struct file_system_type devpts_fs_type = {