diff options
Diffstat (limited to 'fs/devpts/inode.c')
-rw-r--r-- | fs/devpts/inode.c | 115 |
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 | ||
32 | extern int pty_limit; /* Config limit on Unix98 ptys */ | 39 | extern 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 | ||
45 | enum { | 53 | enum { |
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 | ||
57 | struct pts_fs_info { | 68 | struct 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 | ||
62 | static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) | 74 | static 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 | ||
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 | static int devpts_remount(struct super_block *sb, int *flags, char *data) | 211 | static 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 | ||
129 | static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs) | 230 | static 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) | |||
163 | static int | 268 | static int |
164 | devpts_fill_super(struct super_block *s, void *data, int silent) | 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 | 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 | ||
217 | static struct file_system_type devpts_fs_type = { | 322 | static struct file_system_type devpts_fs_type = { |