aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>2009-01-02 08:42:02 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-02 13:19:36 -0500
commit1f8f1e296583f9f832c2fe7b5a219675b74bf43e (patch)
tree3fd16bc570df327a02c07e983c0e5d63ae13c0db /fs
parente4adca27bcbb8a73c4cf1dfa71392654cfa33345 (diff)
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/devpts/inode.c115
1 files changed, 110 insertions, 5 deletions
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 00530e82673e..8ee9dc2f9e48 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -27,6 +27,13 @@
27#define DEVPTS_SUPER_MAGIC 0x1cd1 27#define DEVPTS_SUPER_MAGIC 0x1cd1
28 28
29#define DEVPTS_DEFAULT_MODE 0600 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#define PTMX_MINOR 2 37#define PTMX_MINOR 2
31 38
32extern int pty_limit; /* Config limit on Unix98 ptys */ 39extern int pty_limit; /* Config limit on Unix98 ptys */
@@ -40,10 +47,11 @@ struct pts_mount_opts {
40 uid_t uid; 47 uid_t uid;
41 gid_t gid; 48 gid_t gid;
42 umode_t mode; 49 umode_t mode;
50 umode_t ptmxmode;
43}; 51};
44 52
45enum { 53enum {
46 Opt_uid, Opt_gid, Opt_mode, 54 Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode,
47 Opt_err 55 Opt_err
48}; 56};
49 57
@@ -51,12 +59,16 @@ static const match_table_t tokens = {
51 {Opt_uid, "uid=%u"}, 59 {Opt_uid, "uid=%u"},
52 {Opt_gid, "gid=%u"}, 60 {Opt_gid, "gid=%u"},
53 {Opt_mode, "mode=%o"}, 61 {Opt_mode, "mode=%o"},
62#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
63 {Opt_ptmxmode, "ptmxmode=%o"},
64#endif
54 {Opt_err, NULL} 65 {Opt_err, NULL}
55}; 66};
56 67
57struct pts_fs_info { 68struct pts_fs_info {
58 struct ida allocated_ptys; 69 struct ida allocated_ptys;
59 struct pts_mount_opts mount_opts; 70 struct pts_mount_opts mount_opts;
71 struct dentry *ptmx_dentry;
60}; 72};
61 73
62static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) 74static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
@@ -81,6 +93,7 @@ static int parse_mount_options(char *data, struct pts_mount_opts *opts)
81 opts->uid = 0; 93 opts->uid = 0;
82 opts->gid = 0; 94 opts->gid = 0;
83 opts->mode = DEVPTS_DEFAULT_MODE; 95 opts->mode = DEVPTS_DEFAULT_MODE;
96 opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
84 97
85 while ((p = strsep(&data, ",")) != NULL) { 98 while ((p = strsep(&data, ",")) != NULL) {
86 substring_t args[MAX_OPT_ARGS]; 99 substring_t args[MAX_OPT_ARGS];
@@ -109,6 +122,13 @@ static int parse_mount_options(char *data, struct pts_mount_opts *opts)
109 return -EINVAL; 122 return -EINVAL;
110 opts->mode = option & S_IALLUGO; 123 opts->mode = option & S_IALLUGO;
111 break; 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 default: 132 default:
113 printk(KERN_ERR "devpts: called with bogus options\n"); 133 printk(KERN_ERR "devpts: called with bogus options\n");
114 return -EINVAL; 134 return -EINVAL;
@@ -118,12 +138,93 @@ static int parse_mount_options(char *data, struct pts_mount_opts *opts)
118 return 0; 138 return 0;
119} 139}
120 140
141#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
142static 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);
191out:
192 mutex_unlock(&root->d_inode->i_mutex);
193 return rc;
194}
195
196static 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
205static inline void update_ptmx_mode(struct pts_fs_info *fsi)
206{
207 return;
208}
209#endif
210
121static int devpts_remount(struct super_block *sb, int *flags, char *data) 211static int devpts_remount(struct super_block *sb, int *flags, char *data)
122{ 212{
213 int err;
123 struct pts_fs_info *fsi = DEVPTS_SB(sb); 214 struct pts_fs_info *fsi = DEVPTS_SB(sb);
124 struct pts_mount_opts *opts = &fsi->mount_opts; 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
129static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs) 230static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs)
@@ -136,6 +237,9 @@ static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs)
136 if (opts->setgid) 237 if (opts->setgid)
137 seq_printf(seq, ",gid=%u", opts->gid); 238 seq_printf(seq, ",gid=%u", opts->gid);
138 seq_printf(seq, ",mode=%03o", opts->mode); 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 return 0; 244 return 0;
141} 245}
@@ -156,6 +260,7 @@ static void *new_pts_fs_info(void)
156 260
157 ida_init(&fsi->allocated_ptys); 261 ida_init(&fsi->allocated_ptys);
158 fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; 262 fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
263 fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
159 264
160 return fsi; 265 return fsi;
161} 266}
@@ -163,7 +268,7 @@ static void *new_pts_fs_info(void)
163static int 268static int
164devpts_fill_super(struct super_block *s, void *data, int silent) 269devpts_fill_super(struct super_block *s, void *data, int silent)
165{ 270{
166 struct inode * inode; 271 struct inode *inode;
167 272
168 s->s_blocksize = 1024; 273 s->s_blocksize = 1024;
169 s->s_blocksize_bits = 10; 274 s->s_blocksize_bits = 10;
@@ -190,7 +295,7 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
190 s->s_root = d_alloc_root(inode); 295 s->s_root = d_alloc_root(inode);
191 if (s->s_root) 296 if (s->s_root)
192 return 0; 297 return 0;
193 298
194 printk("devpts: get root dentry failed\n"); 299 printk("devpts: get root dentry failed\n");
195 iput(inode); 300 iput(inode);
196 301
@@ -211,7 +316,7 @@ static void devpts_kill_sb(struct super_block *sb)
211 struct pts_fs_info *fsi = DEVPTS_SB(sb); 316 struct pts_fs_info *fsi = DEVPTS_SB(sb);
212 317
213 kfree(fsi); 318 kfree(fsi);
214 kill_anon_super(sb); 319 kill_litter_super(sb);
215} 320}
216 321
217static struct file_system_type devpts_fs_type = { 322static struct file_system_type devpts_fs_type = {