1 | /* $NetBSD: ptyfs_vfsops.c,v 1.55 2014/10/21 16:05:01 christos Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1992, 1993, 1995 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * |
7 | * This code is derived from software donated to Berkeley by |
8 | * Jan-Simon Pendry. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | * |
34 | */ |
35 | |
36 | /* |
37 | * Pseudo-tty Filesystem |
38 | */ |
39 | |
40 | #include <sys/cdefs.h> |
41 | __KERNEL_RCSID(0, "$NetBSD: ptyfs_vfsops.c,v 1.55 2014/10/21 16:05:01 christos Exp $" ); |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/sysctl.h> |
46 | #include <sys/conf.h> |
47 | #include <sys/proc.h> |
48 | #include <sys/vnode.h> |
49 | #include <sys/mount.h> |
50 | #include <sys/namei.h> |
51 | #include <sys/stat.h> |
52 | #include <sys/dirent.h> |
53 | #include <sys/malloc.h> |
54 | #include <sys/syslog.h> |
55 | #include <sys/select.h> |
56 | #include <sys/filedesc.h> |
57 | #include <sys/tty.h> |
58 | #include <sys/pty.h> |
59 | #include <sys/kauth.h> |
60 | #include <sys/module.h> |
61 | |
62 | #include <fs/ptyfs/ptyfs.h> |
63 | #include <miscfs/genfs/genfs.h> |
64 | #include <miscfs/specfs/specdev.h> |
65 | |
66 | MODULE(MODULE_CLASS_VFS, ptyfs, NULL); |
67 | |
68 | MALLOC_JUSTDEFINE(M_PTYFSMNT, "ptyfs mount" , "ptyfs mount structures" ); |
69 | MALLOC_JUSTDEFINE(M_PTYFSTMP, "ptyfs temp" , "ptyfs temporary structures" ); |
70 | |
71 | VFS_PROTOS(ptyfs); |
72 | |
73 | static struct sysctllog *ptyfs_sysctl_log; |
74 | |
75 | static int ptyfs__allocvp(struct mount *, struct lwp *, struct vnode **, |
76 | dev_t, char); |
77 | static int ptyfs__makename(struct mount *, struct lwp *, char *, size_t, |
78 | dev_t, char); |
79 | static void ptyfs__getvattr(struct mount *, struct lwp *, struct vattr *); |
80 | static int ptyfs__getmp(struct lwp *, struct mount **); |
81 | |
82 | /* |
83 | * ptm glue: When we mount, we make ptm point to us. |
84 | */ |
85 | struct ptm_pty *ptyfs_save_ptm; |
86 | static int ptyfs_count; |
87 | |
88 | static TAILQ_HEAD(, ptyfsmount) ptyfs_head; |
89 | |
90 | struct ptm_pty ptm_ptyfspty = { |
91 | ptyfs__allocvp, |
92 | ptyfs__makename, |
93 | ptyfs__getvattr, |
94 | ptyfs__getmp, |
95 | }; |
96 | |
97 | static int |
98 | ptyfs__getmp(struct lwp *l, struct mount **mpp) |
99 | { |
100 | struct cwdinfo *cwdi = l->l_proc->p_cwdi; |
101 | struct mount *mp; |
102 | struct ptyfsmount *pmnt; |
103 | |
104 | TAILQ_FOREACH(pmnt, &ptyfs_head, pmnt_le) { |
105 | mp = pmnt->pmnt_mp; |
106 | if (cwdi->cwdi_rdir == NULL) |
107 | goto ok; |
108 | |
109 | if (vn_isunder(mp->mnt_vnodecovered, cwdi->cwdi_rdir, l)) |
110 | goto ok; |
111 | } |
112 | *mpp = NULL; |
113 | return EOPNOTSUPP; |
114 | ok: |
115 | *mpp = mp; |
116 | return 0; |
117 | } |
118 | |
119 | static const char * |
120 | ptyfs__getpath(struct lwp *l, const struct mount *mp) |
121 | { |
122 | #define MAXBUF (sizeof(mp->mnt_stat.f_mntonname) + 32) |
123 | struct cwdinfo *cwdi = l->l_proc->p_cwdi; |
124 | char *buf; |
125 | const char *rv; |
126 | size_t len; |
127 | char *bp; |
128 | int error; |
129 | |
130 | rv = mp->mnt_stat.f_mntonname; |
131 | if (cwdi->cwdi_rdir == NULL) |
132 | return rv; |
133 | |
134 | buf = malloc(MAXBUF, M_TEMP, M_WAITOK); |
135 | bp = buf + MAXBUF; |
136 | *--bp = '\0'; |
137 | error = getcwd_common(mp->mnt_vnodecovered, cwdi->cwdi_rdir, &bp, |
138 | buf, MAXBUF / 2, 0, l); |
139 | if (error) { /* Mount point is out of rdir */ |
140 | rv = NULL; |
141 | goto out; |
142 | } |
143 | |
144 | len = strlen(bp); |
145 | if (len < sizeof(mp->mnt_stat.f_mntonname)) /* XXX */ |
146 | rv += strlen(rv) - len; |
147 | out: |
148 | free(buf, M_TEMP); |
149 | return rv; |
150 | } |
151 | |
152 | static int |
153 | ptyfs__makename(struct mount *mp, struct lwp *l, char *tbuf, size_t bufsiz, |
154 | dev_t dev, char ms) |
155 | { |
156 | size_t len; |
157 | const char *np; |
158 | int pty = minor(dev); |
159 | |
160 | switch (ms) { |
161 | case 'p': |
162 | /* We don't provide access to the master, should we? */ |
163 | len = snprintf(tbuf, bufsiz, "/dev/null" ); |
164 | break; |
165 | case 't': |
166 | /* |
167 | * We support traditional ptys, so we can get here, |
168 | * if pty had been opened before PTYFS was mounted, |
169 | * or was opened through /dev/ptyXX devices. |
170 | * Return it only outside chroot for more security . |
171 | */ |
172 | if (l->l_proc->p_cwdi->cwdi_rdir == NULL |
173 | && ptyfs_save_ptm != NULL |
174 | && ptyfs_next_active(mp, pty) != pty) |
175 | return (*ptyfs_save_ptm->makename)(mp, l, |
176 | tbuf, bufsiz, dev, ms); |
177 | |
178 | np = ptyfs__getpath(l, mp); |
179 | if (np == NULL) |
180 | return EOPNOTSUPP; |
181 | len = snprintf(tbuf, bufsiz, "%s/%llu" , np, |
182 | (unsigned long long)minor(dev)); |
183 | break; |
184 | default: |
185 | return EINVAL; |
186 | } |
187 | |
188 | return len >= bufsiz ? ENOSPC : 0; |
189 | } |
190 | |
191 | |
192 | static int |
193 | /*ARGSUSED*/ |
194 | ptyfs__allocvp(struct mount *mp, struct lwp *l, struct vnode **vpp, |
195 | dev_t dev, char ms) |
196 | { |
197 | int error; |
198 | ptyfstype type; |
199 | |
200 | switch (ms) { |
201 | case 'p': |
202 | type = PTYFSptc; |
203 | break; |
204 | case 't': |
205 | type = PTYFSpts; |
206 | break; |
207 | default: |
208 | return EINVAL; |
209 | } |
210 | |
211 | error = ptyfs_allocvp(mp, vpp, type, minor(dev)); |
212 | if (error) |
213 | return error; |
214 | error = vn_lock(*vpp, LK_EXCLUSIVE); |
215 | if (error) { |
216 | vrele(*vpp); |
217 | *vpp = NULL; |
218 | return error; |
219 | } |
220 | if (type == PTYFSptc) |
221 | ptyfs_set_active(mp, minor(dev)); |
222 | return 0; |
223 | } |
224 | |
225 | |
226 | static void |
227 | ptyfs__getvattr(struct mount *mp, struct lwp *l, struct vattr *vattr) |
228 | { |
229 | struct ptyfsmount *pmnt = VFSTOPTY(mp); |
230 | vattr_null(vattr); |
231 | /* get real uid */ |
232 | vattr->va_uid = kauth_cred_getuid(l->l_cred); |
233 | vattr->va_gid = pmnt->pmnt_gid; |
234 | vattr->va_mode = pmnt->pmnt_mode; |
235 | } |
236 | |
237 | |
238 | void |
239 | ptyfs_init(void) |
240 | { |
241 | |
242 | TAILQ_INIT(&ptyfs_head); |
243 | malloc_type_attach(M_PTYFSMNT); |
244 | malloc_type_attach(M_PTYFSTMP); |
245 | ptyfs_hashinit(); |
246 | } |
247 | |
248 | void |
249 | ptyfs_reinit(void) |
250 | { |
251 | |
252 | } |
253 | |
254 | void |
255 | ptyfs_done(void) |
256 | { |
257 | |
258 | ptyfs_hashdone(); |
259 | malloc_type_detach(M_PTYFSTMP); |
260 | malloc_type_detach(M_PTYFSMNT); |
261 | } |
262 | |
263 | #define OSIZE sizeof(struct { int f; gid_t g; mode_t m; }) |
264 | /* |
265 | * Mount the Pseudo tty params filesystem |
266 | */ |
267 | int |
268 | ptyfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) |
269 | { |
270 | struct lwp *l = curlwp; |
271 | int error = 0; |
272 | struct ptyfsmount *pmnt; |
273 | struct ptyfs_args *args = data; |
274 | |
275 | if (args == NULL) |
276 | return EINVAL; |
277 | if (*data_len != sizeof *args) { |
278 | if (*data_len != OSIZE || args->version >= PTYFS_ARGSVERSION) |
279 | return EINVAL; |
280 | } |
281 | |
282 | if (UIO_MX & (UIO_MX - 1)) { |
283 | log(LOG_ERR, "ptyfs: invalid directory entry size" ); |
284 | return EINVAL; |
285 | } |
286 | |
287 | if (mp->mnt_flag & MNT_GETARGS) { |
288 | pmnt = VFSTOPTY(mp); |
289 | if (pmnt == NULL) |
290 | return EIO; |
291 | args->mode = pmnt->pmnt_mode; |
292 | args->gid = pmnt->pmnt_gid; |
293 | if (args->version >= PTYFS_ARGSVERSION) { |
294 | args->flags = pmnt->pmnt_flags; |
295 | *data_len = sizeof *args; |
296 | } else { |
297 | *data_len = OSIZE; |
298 | } |
299 | return 0; |
300 | } |
301 | |
302 | #if 0 |
303 | /* Don't allow more than one mount */ |
304 | if (ptyfs_count) |
305 | return EBUSY; |
306 | #endif |
307 | |
308 | if (mp->mnt_flag & MNT_UPDATE) |
309 | return EOPNOTSUPP; |
310 | |
311 | if (args->version > PTYFS_ARGSVERSION) |
312 | return EINVAL; |
313 | |
314 | pmnt = malloc(sizeof(struct ptyfsmount), M_PTYFSMNT, M_WAITOK); |
315 | |
316 | mp->mnt_data = pmnt; |
317 | mutex_init(&pmnt->pmnt_lock, MUTEX_DEFAULT, IPL_NONE); |
318 | pmnt->pmnt_gid = args->gid; |
319 | pmnt->pmnt_mode = args->mode; |
320 | if (args->version >= PTYFS_ARGSVERSION) |
321 | pmnt->pmnt_flags = args->flags; |
322 | else |
323 | pmnt->pmnt_flags = 0; |
324 | pmnt->pmnt_bitmap_size = 0; |
325 | pmnt->pmnt_bitmap = NULL; |
326 | mp->mnt_flag |= MNT_LOCAL; |
327 | vfs_getnewfsid(mp); |
328 | |
329 | if ((error = set_statvfs_info(path, UIO_USERSPACE, "ptyfs" , |
330 | UIO_SYSSPACE, mp->mnt_op->vfs_name, mp, l)) != 0) { |
331 | free(pmnt, M_PTYFSMNT); |
332 | return error; |
333 | } |
334 | |
335 | pmnt->pmnt_mp = mp; |
336 | TAILQ_INSERT_TAIL(&ptyfs_head, pmnt, pmnt_le); |
337 | if (ptyfs_count++ == 0) { |
338 | /* Point pty access to us */ |
339 | ptyfs_save_ptm = pty_sethandler(&ptm_ptyfspty); |
340 | } |
341 | return 0; |
342 | } |
343 | |
344 | /*ARGSUSED*/ |
345 | int |
346 | ptyfs_start(struct mount *mp, int flags) |
347 | { |
348 | return 0; |
349 | } |
350 | |
351 | /*ARGSUSED*/ |
352 | int |
353 | ptyfs_unmount(struct mount *mp, int mntflags) |
354 | { |
355 | int error; |
356 | int flags = 0; |
357 | struct ptyfsmount *pmnt; |
358 | |
359 | if (mntflags & MNT_FORCE) |
360 | flags |= FORCECLOSE; |
361 | |
362 | if ((error = vflush(mp, 0, flags)) != 0) |
363 | return error; |
364 | |
365 | ptyfs_count--; |
366 | if (ptyfs_count == 0) { |
367 | /* Restore where pty access was pointing */ |
368 | (void)pty_sethandler(ptyfs_save_ptm); |
369 | ptyfs_save_ptm = NULL; |
370 | } |
371 | TAILQ_FOREACH(pmnt, &ptyfs_head, pmnt_le) { |
372 | if (pmnt->pmnt_mp == mp) { |
373 | TAILQ_REMOVE(&ptyfs_head, pmnt, pmnt_le); |
374 | break; |
375 | } |
376 | } |
377 | |
378 | /* |
379 | * Finally, throw away the ptyfsmount structure |
380 | */ |
381 | if (pmnt->pmnt_bitmap_size > 0) |
382 | kmem_free(pmnt->pmnt_bitmap, pmnt->pmnt_bitmap_size); |
383 | mutex_destroy(&pmnt->pmnt_lock); |
384 | free(mp->mnt_data, M_PTYFSMNT); |
385 | mp->mnt_data = NULL; |
386 | |
387 | return 0; |
388 | } |
389 | |
390 | int |
391 | ptyfs_root(struct mount *mp, struct vnode **vpp) |
392 | { |
393 | int error; |
394 | |
395 | /* setup "." */ |
396 | error = ptyfs_allocvp(mp, vpp, PTYFSroot, 0); |
397 | if (error) |
398 | return error; |
399 | error = vn_lock(*vpp, LK_EXCLUSIVE); |
400 | if (error) { |
401 | vrele(*vpp); |
402 | *vpp = NULL; |
403 | return error; |
404 | } |
405 | return 0; |
406 | } |
407 | |
408 | /*ARGSUSED*/ |
409 | int |
410 | ptyfs_sync(struct mount *mp, int waitfor, |
411 | kauth_cred_t uc) |
412 | { |
413 | return 0; |
414 | } |
415 | |
416 | /* |
417 | * Initialize this vnode / ptynode pair. |
418 | * Only for the slave side of a pty, caller assures |
419 | * no other thread will try to load this node. |
420 | */ |
421 | int |
422 | ptyfs_loadvnode(struct mount *mp, struct vnode *vp, |
423 | const void *key, size_t key_len, const void **new_key) |
424 | { |
425 | struct ptyfskey pkey; |
426 | struct ptyfsnode *ptyfs; |
427 | |
428 | KASSERT(key_len == sizeof(pkey)); |
429 | memcpy(&pkey, key, key_len); |
430 | |
431 | ptyfs = ptyfs_get_node(pkey.ptk_type, pkey.ptk_pty); |
432 | KASSERT(memcmp(&ptyfs->ptyfs_key, &pkey, sizeof(pkey)) == 0); |
433 | |
434 | switch (pkey.ptk_type) { |
435 | case PTYFSroot: /* /pts = dr-xr-xr-x */ |
436 | vp->v_type = VDIR; |
437 | vp->v_vflag = VV_ROOT; |
438 | break; |
439 | |
440 | case PTYFSpts: /* /pts/N = cxxxxxxxxx */ |
441 | case PTYFSptc: /* controlling side = cxxxxxxxxx */ |
442 | vp->v_type = VCHR; |
443 | spec_node_init(vp, PTYFS_MAKEDEV(ptyfs)); |
444 | break; |
445 | default: |
446 | panic("ptyfs_loadvnode" ); |
447 | } |
448 | |
449 | vp->v_tag = VT_PTYFS; |
450 | vp->v_op = ptyfs_vnodeop_p; |
451 | vp->v_data = ptyfs; |
452 | uvm_vnp_setsize(vp, 0); |
453 | *new_key = &ptyfs->ptyfs_key; |
454 | return 0; |
455 | } |
456 | |
457 | /* |
458 | * Kernfs flat namespace lookup. |
459 | * Currently unsupported. |
460 | */ |
461 | /*ARGSUSED*/ |
462 | int |
463 | ptyfs_vget(struct mount *mp, ino_t ino, |
464 | struct vnode **vpp) |
465 | { |
466 | return EOPNOTSUPP; |
467 | } |
468 | |
469 | extern const struct vnodeopv_desc ptyfs_vnodeop_opv_desc; |
470 | |
471 | const struct vnodeopv_desc * const ptyfs_vnodeopv_descs[] = { |
472 | &ptyfs_vnodeop_opv_desc, |
473 | NULL, |
474 | }; |
475 | |
476 | struct vfsops ptyfs_vfsops = { |
477 | .vfs_name = MOUNT_PTYFS, |
478 | .vfs_min_mount_data = sizeof (struct ptyfs_args), |
479 | .vfs_mount = ptyfs_mount, |
480 | .vfs_start = ptyfs_start, |
481 | .vfs_unmount = ptyfs_unmount, |
482 | .vfs_root = ptyfs_root, |
483 | .vfs_quotactl = (void *)eopnotsupp, |
484 | .vfs_statvfs = genfs_statvfs, |
485 | .vfs_sync = ptyfs_sync, |
486 | .vfs_vget = ptyfs_vget, |
487 | .vfs_loadvnode = ptyfs_loadvnode, |
488 | .vfs_fhtovp = (void *)eopnotsupp, |
489 | .vfs_vptofh = (void *)eopnotsupp, |
490 | .vfs_init = ptyfs_init, |
491 | .vfs_reinit = ptyfs_reinit, |
492 | .vfs_done = ptyfs_done, |
493 | .vfs_snapshot = (void *)eopnotsupp, |
494 | .vfs_extattrctl = (void *)eopnotsupp, |
495 | .vfs_suspendctl = (void *)eopnotsupp, |
496 | .vfs_renamelock_enter = genfs_renamelock_enter, |
497 | .vfs_renamelock_exit = genfs_renamelock_exit, |
498 | .vfs_fsync = (void *)eopnotsupp, |
499 | .vfs_opv_descs = ptyfs_vnodeopv_descs |
500 | }; |
501 | |
502 | static int |
503 | ptyfs_modcmd(modcmd_t cmd, void *arg) |
504 | { |
505 | int error; |
506 | |
507 | switch (cmd) { |
508 | case MODULE_CMD_INIT: |
509 | error = vfs_attach(&ptyfs_vfsops); |
510 | if (error != 0) |
511 | break; |
512 | sysctl_createv(&ptyfs_sysctl_log, 0, NULL, NULL, |
513 | CTLFLAG_PERMANENT, |
514 | CTLTYPE_NODE, "ptyfs" , |
515 | SYSCTL_DESCR("Pty file system" ), |
516 | NULL, 0, NULL, 0, |
517 | CTL_VFS, 23, CTL_EOL); |
518 | /* |
519 | * XXX the "23" above could be dynamic, thereby eliminating |
520 | * one more instance of the "number to vfs" mapping problem, |
521 | * but "23" is the order as taken from sys/mount.h |
522 | */ |
523 | break; |
524 | case MODULE_CMD_FINI: |
525 | error = vfs_detach(&ptyfs_vfsops); |
526 | if (error != 0) |
527 | break; |
528 | sysctl_teardown(&ptyfs_sysctl_log); |
529 | break; |
530 | default: |
531 | error = ENOTTY; |
532 | break; |
533 | } |
534 | |
535 | return (error); |
536 | } |
537 | |