1 /*        $NetBSD: ptyfs_vnops.c,v 1.69 2022/08/05 10:36:02 riastradh Exp $     */
2 
3 /*
4  * Copyright (c) 1993, 1995
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed 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  *        @(#)procfs_vnops.c  8.18 (Berkeley) 5/21/95
35  */
36 
37 /*
38  * Copyright (c) 1993 Jan-Simon Pendry
39  *
40  * This code is derived from software contributed to Berkeley by
41  * Jan-Simon Pendry.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *        This product includes software developed by the University of
54  *        California, Berkeley and its contributors.
55  * 4. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  *
71  *        @(#)procfs_vnops.c  8.18 (Berkeley) 5/21/95
72  */
73 
74 /*
75  * ptyfs vnode interface
76  */
77 
78 #include <sys/cdefs.h>
79 __KERNEL_RCSID(0, "$NetBSD: ptyfs_vnops.c,v 1.69 2022/08/05 10:36:02 riastradh Exp $");
80 
81 #include <sys/param.h>
82 #include <sys/systm.h>
83 #include <sys/time.h>
84 #include <sys/kernel.h>
85 #include <sys/file.h>
86 #include <sys/filedesc.h>
87 #include <sys/proc.h>
88 #include <sys/vnode.h>
89 #include <sys/namei.h>
90 #include <sys/malloc.h>
91 #include <sys/mount.h>
92 #include <sys/select.h>
93 #include <sys/dirent.h>
94 #include <sys/resourcevar.h>
95 #include <sys/stat.h>
96 #include <sys/conf.h>
97 #include <sys/tty.h>
98 #include <sys/pty.h>
99 #include <sys/kauth.h>
100 
101 #include <uvm/uvm_extern.h>   /* for PAGE_SIZE */
102 
103 #include <machine/reg.h>
104 
105 #include <fs/ptyfs/ptyfs.h>
106 #include <miscfs/genfs/genfs.h>
107 #include <miscfs/specfs/specdev.h>
108 
109 MALLOC_DECLARE(M_PTYFSTMP);
110 
111 /*
112  * Vnode Operations.
113  *
114  */
115 
116 int       ptyfs_lookup        (void *);
117 int       ptyfs_open          (void *);
118 int       ptyfs_close         (void *);
119 int       ptyfs_access        (void *);
120 int       ptyfs_getattr       (void *);
121 int       ptyfs_setattr       (void *);
122 int       ptyfs_read          (void *);
123 int       ptyfs_write         (void *);
124 int       ptyfs_ioctl         (void *);
125 int       ptyfs_poll          (void *);
126 int       ptyfs_kqfilter      (void *);
127 int       ptyfs_readdir       (void *);
128 int       ptyfs_reclaim       (void *);
129 int       ptyfs_inactive      (void *);
130 int       ptyfs_print         (void *);
131 int       ptyfs_pathconf      (void *);
132 int       ptyfs_advlock       (void *);
133 
134 static int ptyfs_update(struct vnode *, const struct timespec *,
135     const struct timespec *, int);
136 static int ptyfs_chown(struct vnode *, uid_t, gid_t, kauth_cred_t,
137     struct lwp *);
138 static int ptyfs_chmod(struct vnode *, mode_t, kauth_cred_t, struct lwp *);
139 static int atoi(const char *, size_t);
140 
141 /*
142  * ptyfs vnode operations.
143  */
144 int (**ptyfs_vnodeop_p)(void *);
145 const struct vnodeopv_entry_desc ptyfs_vnodeop_entries[] = {
146           { &vop_default_desc, vn_default_error },
147           { &vop_parsepath_desc, genfs_parsepath },         /* parsepath */
148           { &vop_lookup_desc, ptyfs_lookup },               /* lookup */
149           { &vop_create_desc, genfs_eopnotsupp },           /* create */
150           { &vop_mknod_desc, genfs_eopnotsupp },            /* mknod */
151           { &vop_open_desc, ptyfs_open },                             /* open */
152           { &vop_close_desc, ptyfs_close },                 /* close */
153           { &vop_access_desc, ptyfs_access },               /* access */
154           { &vop_accessx_desc, genfs_accessx },             /* accessx */
155           { &vop_getattr_desc, ptyfs_getattr },             /* getattr */
156           { &vop_setattr_desc, ptyfs_setattr },             /* setattr */
157           { &vop_read_desc, ptyfs_read },                             /* read */
158           { &vop_write_desc, ptyfs_write },                 /* write */
159           { &vop_fallocate_desc, genfs_eopnotsupp },        /* fallocate */
160           { &vop_fdiscard_desc, genfs_eopnotsupp },         /* fdiscard */
161           { &vop_ioctl_desc, ptyfs_ioctl },                 /* ioctl */
162           { &vop_fcntl_desc, genfs_fcntl },                 /* fcntl */
163           { &vop_poll_desc, ptyfs_poll },                             /* poll */
164           { &vop_kqfilter_desc, ptyfs_kqfilter },           /* kqfilter */
165           { &vop_revoke_desc, genfs_revoke },               /* revoke */
166           { &vop_mmap_desc, genfs_eopnotsupp },             /* mmap */
167           { &vop_fsync_desc, genfs_nullop },                /* fsync */
168           { &vop_seek_desc, genfs_nullop },                 /* seek */
169           { &vop_remove_desc, genfs_eopnotsupp },           /* remove */
170           { &vop_link_desc, genfs_eopnotsupp },             /* link */
171           { &vop_rename_desc, genfs_eopnotsupp },           /* rename */
172           { &vop_mkdir_desc, genfs_eopnotsupp },            /* mkdir */
173           { &vop_rmdir_desc, genfs_eopnotsupp },            /* rmdir */
174           { &vop_symlink_desc, genfs_eopnotsupp },          /* symlink */
175           { &vop_readdir_desc, ptyfs_readdir },             /* readdir */
176           { &vop_readlink_desc, genfs_eopnotsupp },         /* readlink */
177           { &vop_abortop_desc, genfs_abortop },             /* abortop */
178           { &vop_inactive_desc, ptyfs_inactive },           /* inactive */
179           { &vop_reclaim_desc, ptyfs_reclaim },             /* reclaim */
180           { &vop_lock_desc, genfs_lock },                             /* lock */
181           { &vop_unlock_desc, genfs_unlock },               /* unlock */
182           { &vop_bmap_desc, genfs_eopnotsupp },             /* bmap */
183           { &vop_strategy_desc, genfs_badop },              /* strategy */
184           { &vop_print_desc, ptyfs_print },                 /* print */
185           { &vop_islocked_desc, genfs_islocked },           /* islocked */
186           { &vop_pathconf_desc, ptyfs_pathconf },           /* pathconf */
187           { &vop_advlock_desc, ptyfs_advlock },             /* advlock */
188           { &vop_bwrite_desc, genfs_eopnotsupp },           /* bwrite */
189           { &vop_putpages_desc, genfs_null_putpages },      /* putpages */
190           { NULL, NULL }
191 };
192 const struct vnodeopv_desc ptyfs_vnodeop_opv_desc =
193           { &ptyfs_vnodeop_p, ptyfs_vnodeop_entries };
194 
195 /*
196  * free any private data and remove the node
197  * from any private lists.
198  */
199 int
ptyfs_reclaim(void * v)200 ptyfs_reclaim(void *v)
201 {
202           struct vop_reclaim_v2_args /* {
203                     struct vnode *a_vp;
204           } */ *ap = v;
205           struct vnode *vp = ap->a_vp;
206 
207           VOP_UNLOCK(vp);
208 
209           vp->v_data = NULL;
210           return 0;
211 }
212 
213 int
ptyfs_inactive(void * v)214 ptyfs_inactive(void *v)
215 {
216           struct vop_inactive_v2_args /* {
217                     struct vnode *a_vp;
218                     bool *a_recycle;
219           } */ *ap = v;
220           struct vnode *vp = ap->a_vp;
221           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
222 
223           if (ptyfs->ptyfs_type == PTYFSptc)
224                     ptyfs_clr_active(vp->v_mount, ptyfs->ptyfs_pty);
225 
226           return 0;
227 }
228 
229 /*
230  * Return POSIX pathconf information applicable to special devices.
231  */
232 int
ptyfs_pathconf(void * v)233 ptyfs_pathconf(void *v)
234 {
235           struct vop_pathconf_args /* {
236                     struct vnode *a_vp;
237                     int a_name;
238                     register_t *a_retval;
239           } */ *ap = v;
240 
241           switch (ap->a_name) {
242           case _PC_LINK_MAX:
243                     *ap->a_retval = LINK_MAX;
244                     return 0;
245           case _PC_MAX_CANON:
246                     *ap->a_retval = MAX_CANON;
247                     return 0;
248           case _PC_MAX_INPUT:
249                     *ap->a_retval = MAX_INPUT;
250                     return 0;
251           case _PC_PIPE_BUF:
252                     *ap->a_retval = PIPE_BUF;
253                     return 0;
254           case _PC_CHOWN_RESTRICTED:
255                     *ap->a_retval = 1;
256                     return 0;
257           case _PC_VDISABLE:
258                     *ap->a_retval = _POSIX_VDISABLE;
259                     return 0;
260           case _PC_SYNC_IO:
261                     *ap->a_retval = 1;
262                     return 0;
263           default:
264                     return genfs_pathconf(ap);
265           }
266 }
267 
268 /*
269  * _print is used for debugging.
270  * just print a readable description
271  * of (vp).
272  */
273 int
ptyfs_print(void * v)274 ptyfs_print(void *v)
275 {
276           struct vop_print_args /* {
277                     struct vnode *a_vp;
278           } */ *ap = v;
279           struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
280 
281           printf("tag VT_PTYFS, type %d, pty %d\n",
282               ptyfs->ptyfs_type, ptyfs->ptyfs_pty);
283           return 0;
284 }
285 
286 /*
287  * support advisory locking on pty nodes
288  */
289 int
ptyfs_advlock(void * v)290 ptyfs_advlock(void *v)
291 {
292           struct vop_print_args /* {
293                     struct vnode *a_vp;
294           } */ *ap = v;
295           struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
296 
297           switch (ptyfs->ptyfs_type) {
298           case PTYFSpts:
299           case PTYFSptc:
300                     return spec_advlock(v);
301           default:
302                     return EOPNOTSUPP;
303           }
304 }
305 
306 /*
307  * Invent attributes for ptyfsnode (vp) and store
308  * them in (vap).
309  * Directories lengths are returned as zero since
310  * any real length would require the genuine size
311  * to be computed, and nothing cares anyway.
312  *
313  * this is relatively minimal for ptyfs.
314  */
315 int
ptyfs_getattr(void * v)316 ptyfs_getattr(void *v)
317 {
318           struct vop_getattr_args /* {
319                     struct vnode *a_vp;
320                     struct vattr *a_vap;
321                     kauth_cred_t a_cred;
322           } */ *ap = v;
323           struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
324           struct vattr *vap = ap->a_vap;
325 
326           PTYFS_ITIMES(ptyfs, NULL, NULL, NULL);
327 
328           /* start by zeroing out the attributes */
329           vattr_null(vap);
330 
331           /* next do all the common fields */
332           vap->va_type = ap->a_vp->v_type;
333           vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
334           vap->va_fileid = ptyfs->ptyfs_fileno;
335           vap->va_gen = 0;
336           vap->va_flags = 0;
337           vap->va_blocksize = PAGE_SIZE;
338 
339           vap->va_atime = ptyfs->ptyfs_atime;
340           vap->va_mtime = ptyfs->ptyfs_mtime;
341           vap->va_ctime = ptyfs->ptyfs_ctime;
342           vap->va_birthtime = ptyfs->ptyfs_birthtime;
343           vap->va_mode = ptyfs->ptyfs_mode;
344           vap->va_flags = ptyfs->ptyfs_flags;
345           vap->va_uid = ptyfs->ptyfs_uid;
346           vap->va_gid = ptyfs->ptyfs_gid;
347 
348           switch (ptyfs->ptyfs_type) {
349           case PTYFSpts:
350           case PTYFSptc:
351                     if (pty_isfree(ptyfs->ptyfs_pty, 1))
352                               return ENOENT;
353                     vap->va_bytes = vap->va_size = 0;
354                     vap->va_rdev = ap->a_vp->v_rdev;
355                     vap->va_nlink = 1;
356                     break;
357           case PTYFSroot:
358                     vap->va_rdev = 0;
359                     vap->va_bytes = vap->va_size = DEV_BSIZE;
360                     vap->va_nlink = 2;
361                     break;
362           default:
363                     return EOPNOTSUPP;
364           }
365 
366           return 0;
367 }
368 
369 /*ARGSUSED*/
370 int
ptyfs_setattr(void * v)371 ptyfs_setattr(void *v)
372 {
373           struct vop_setattr_args /* {
374                     struct vnodeop_desc *a_desc;
375                     struct vnode *a_vp;
376                     struct vattr *a_vap;
377                     kauth_cred_t a_cred;
378           } */ *ap = v;
379           struct vnode *vp = ap->a_vp;
380           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
381           struct vattr *vap = ap->a_vap;
382           kauth_cred_t cred = ap->a_cred;
383           struct lwp *l = curlwp;
384           int error;
385           kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
386           bool changing_sysflags = false;
387 
388           if (vap->va_size != VNOVALSIZE) {
389                     switch (ptyfs->ptyfs_type) {
390                     case PTYFSroot:
391                               return EISDIR;
392                     case PTYFSpts:
393                     case PTYFSptc:
394                               break;
395                     default:
396                               return EINVAL;
397                     }
398           }
399 
400           if (vap->va_flags != VNOVALFLAGS) {
401                     if (vp->v_mount->mnt_flag & MNT_RDONLY)
402                               return EROFS;
403 
404                     /* Immutable and append-only flags are not supported on ptyfs. */
405                     if (vap->va_flags & (IMMUTABLE | APPEND))
406                               return EINVAL;
407 
408                     /* Snapshot flag cannot be set or cleared */
409                     if ((vap->va_flags & SF_SNAPSHOT) != (ptyfs->ptyfs_flags & SF_SNAPSHOT))
410                               return EPERM;
411 
412                     if ((ptyfs->ptyfs_flags & SF_SETTABLE) != (vap->va_flags & SF_SETTABLE)) {
413                               changing_sysflags = true;
414                               action |= KAUTH_VNODE_WRITE_SYSFLAGS;
415                     }
416 
417                     error = kauth_authorize_vnode(cred, action, vp, NULL,
418                         genfs_can_chflags(vp, cred, ptyfs->ptyfs_uid,
419                         changing_sysflags));
420                     if (error)
421                               return error;
422 
423                     if (changing_sysflags) {
424                               ptyfs->ptyfs_flags = vap->va_flags;
425                     } else {
426                               ptyfs->ptyfs_flags &= SF_SETTABLE;
427                               ptyfs->ptyfs_flags |= (vap->va_flags & UF_SETTABLE);
428                     }
429                     ptyfs->ptyfs_status |= PTYFS_CHANGE;
430           }
431 
432           /*
433            * Go through the fields and update iff not VNOVAL.
434            */
435           if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
436                     if (vp->v_mount->mnt_flag & MNT_RDONLY)
437                               return EROFS;
438                     error = ptyfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
439                     if (error)
440                               return error;
441           }
442 
443           if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
444               vap->va_birthtime.tv_sec != VNOVAL) {
445                     if (vp->v_mount->mnt_flag & MNT_RDONLY)
446                               return EROFS;
447                     if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0)
448                               return EPERM;
449                     error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
450                         NULL, genfs_can_chtimes(vp, cred, ptyfs->ptyfs_uid,
451                         vap->va_vaflags));
452                     if (error)
453                               return (error);
454                     if (vap->va_atime.tv_sec != VNOVAL)
455                               if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
456                                         ptyfs->ptyfs_status |= PTYFS_ACCESS;
457                     if (vap->va_mtime.tv_sec != VNOVAL) {
458                               ptyfs->ptyfs_status |= PTYFS_CHANGE | PTYFS_MODIFY;
459                               if (vp->v_mount->mnt_flag & MNT_RELATIME)
460                                         ptyfs->ptyfs_status |= PTYFS_ACCESS;
461                     }
462                     if (vap->va_birthtime.tv_sec != VNOVAL)
463                               ptyfs->ptyfs_birthtime = vap->va_birthtime;
464                     ptyfs->ptyfs_status |= PTYFS_CHANGE;
465                     error = ptyfs_update(vp, &vap->va_atime, &vap->va_mtime, 0);
466                     if (error)
467                               return error;
468           }
469           if (vap->va_mode != (mode_t)VNOVAL) {
470                     if (vp->v_mount->mnt_flag & MNT_RDONLY)
471                               return EROFS;
472                     if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0 &&
473                         (vap->va_mode &
474                         (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IXOTH|S_IWOTH)))
475                               return EPERM;
476                     error = ptyfs_chmod(vp, vap->va_mode, cred, l);
477                     if (error)
478                               return error;
479           }
480           return 0;
481 }
482 
483 /*
484  * Change the mode on a file.
485  * Inode must be locked before calling.
486  */
487 static int
ptyfs_chmod(struct vnode * vp,mode_t mode,kauth_cred_t cred,struct lwp * l)488 ptyfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l)
489 {
490           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
491           int error;
492 
493           error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
494               NULL, genfs_can_chmod(vp, cred, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid,
495               mode));
496           if (error)
497                     return (error);
498 
499           ptyfs->ptyfs_mode &= ~ALLPERMS;
500           ptyfs->ptyfs_mode |= (mode & ALLPERMS);
501           return 0;
502 }
503 
504 /*
505  * Perform chown operation on inode ip;
506  * inode must be locked prior to call.
507  */
508 static int
ptyfs_chown(struct vnode * vp,uid_t uid,gid_t gid,kauth_cred_t cred,struct lwp * l)509 ptyfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
510     struct lwp *l)
511 {
512           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
513           int error;
514 
515           if (uid == (uid_t)VNOVAL)
516                     uid = ptyfs->ptyfs_uid;
517           if (gid == (gid_t)VNOVAL)
518                     gid = ptyfs->ptyfs_gid;
519 
520           error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,
521               NULL, genfs_can_chown(vp, cred, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid,
522               uid, gid));
523           if (error)
524                     return (error);
525 
526           ptyfs->ptyfs_gid = gid;
527           ptyfs->ptyfs_uid = uid;
528           return 0;
529 }
530 
531 /*
532  * implement access checking.
533  *
534  * actually, the check for super-user is slightly
535  * broken since it will allow read access to write-only
536  * objects.  this doesn't cause any particular trouble
537  * but does mean that the i/o entry points need to check
538  * that the operation really does make sense.
539  */
540 int
ptyfs_access(void * v)541 ptyfs_access(void *v)
542 {
543           struct vop_access_args /* {
544                     struct vnode *a_vp;
545                     accmode_t a_accmode;
546                     kauth_cred_t a_cred;
547           } */ *ap = v;
548           struct vattr va;
549           int error;
550 
551           if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0)
552                     return error;
553 
554           return kauth_authorize_vnode(ap->a_cred,
555               KAUTH_ACCESS_ACTION(ap->a_accmode, ap->a_vp->v_type, va.va_mode),
556               ap->a_vp, NULL, genfs_can_access(ap->a_vp, ap->a_cred, va.va_uid,
557               va.va_gid, va.va_mode, NULL, ap->a_accmode));
558 }
559 
560 /*
561  * lookup.  this is incredibly complicated in the
562  * general case, however for most pseudo-filesystems
563  * very little needs to be done.
564  *
565  * Locking isn't hard here, just poorly documented.
566  *
567  * If we're looking up ".", just vref the parent & return it.
568  *
569  * If we're looking up "..", unlock the parent, and lock "..". If everything
570  * went ok, try to re-lock the parent. We do this to prevent lock races.
571  *
572  * For anything else, get the needed node.
573  *
574  * We try to exit with the parent locked in error cases.
575  */
576 int
ptyfs_lookup(void * v)577 ptyfs_lookup(void *v)
578 {
579           struct vop_lookup_v2_args /* {
580                     struct vnode * a_dvp;
581                     struct vnode ** a_vpp;
582                     struct componentname * a_cnp;
583           } */ *ap = v;
584           struct componentname *cnp = ap->a_cnp;
585           struct vnode **vpp = ap->a_vpp;
586           struct vnode *dvp = ap->a_dvp;
587           const char *pname = cnp->cn_nameptr;
588           struct ptyfsnode *ptyfs;
589           int pty, error;
590 
591           *vpp = NULL;
592 
593           if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
594                     return EROFS;
595 
596           if (cnp->cn_namelen == 1 && *pname == '.') {
597                     *vpp = dvp;
598                     vref(dvp);
599                     return 0;
600           }
601 
602           ptyfs = VTOPTYFS(dvp);
603           switch (ptyfs->ptyfs_type) {
604           case PTYFSroot:
605                     /*
606                      * Shouldn't get here with .. in the root node.
607                      */
608                     if (cnp->cn_flags & ISDOTDOT)
609                               return EIO;
610 
611                     pty = atoi(pname, cnp->cn_namelen);
612                     if (pty < 0 || ptyfs_next_active(dvp->v_mount, pty) != pty)
613                               break;
614                     error = ptyfs_allocvp(dvp->v_mount, vpp, PTYFSpts, pty);
615                     if (error)
616                               return error;
617                     if (ptyfs_next_active(dvp->v_mount, pty) != pty) {
618                               vrele(*vpp);
619                               *vpp = NULL;
620                               return ENOENT;
621                     }
622                     return 0;
623 
624           default:
625                     return ENOTDIR;
626           }
627 
628           return cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS;
629 }
630 
631 /*
632  * readdir returns directory entries from ptyfsnode (vp).
633  *
634  * the strategy here with ptyfs is to generate a single
635  * directory entry at a time (struct dirent) and then
636  * copy that out to userland using uiomove.  a more efficient
637  * though more complex implementation, would try to minimize
638  * the number of calls to uiomove().  for ptyfs, this is
639  * hardly worth the added code complexity.
640  *
641  * this should just be done through read()
642  */
643 int
ptyfs_readdir(void * v)644 ptyfs_readdir(void *v)
645 {
646           struct vop_readdir_args /* {
647                     struct vnode *a_vp;
648                     struct uio *a_uio;
649                     kauth_cred_t a_cred;
650                     int *a_eofflag;
651                     off_t **a_cookies;
652                     int *a_ncookies;
653           } */ *ap = v;
654           struct uio *uio = ap->a_uio;
655           struct dirent *dp;
656           struct ptyfsnode *ptyfs;
657           off_t i;
658           int error;
659           off_t *cookies = NULL;
660           int ncookies;
661           struct vnode *vp;
662           int n, nc = 0;
663 
664           vp = ap->a_vp;
665           ptyfs = VTOPTYFS(vp);
666 
667           if (uio->uio_resid < UIO_MX)
668                     return EINVAL;
669           if (uio->uio_offset < 0)
670                     return EINVAL;
671 
672           dp = malloc(sizeof(struct dirent), M_PTYFSTMP, M_WAITOK | M_ZERO);
673 
674           error = 0;
675           i = uio->uio_offset;
676           dp->d_reclen = UIO_MX;
677           ncookies = uio->uio_resid / UIO_MX;
678 
679           if (ptyfs->ptyfs_type != PTYFSroot) {
680                     error = ENOTDIR;
681                     goto out;
682           }
683 
684           if (i >= npty)
685                     goto out;
686 
687           if (ap->a_ncookies) {
688                     ncookies = uimin(ncookies, (npty + 2 - i));
689                     cookies = malloc(ncookies * sizeof (off_t),
690                         M_TEMP, M_WAITOK);
691                     *ap->a_cookies = cookies;
692           }
693 
694           for (; i < 2 && uio->uio_resid >= UIO_MX; i++) {
695                     /* `.' and/or `..' */
696                     dp->d_fileno = PTYFS_FILENO(PTYFSroot, 0);
697                     dp->d_namlen = i + 1;
698                     (void)memcpy(dp->d_name, "..", dp->d_namlen);
699                     dp->d_name[i + 1] = '\0';
700                     dp->d_type = DT_DIR;
701                     if ((error = uiomove(dp, UIO_MX, uio)) != 0)
702                               goto out;
703                     if (cookies)
704                               *cookies++ = i + 1;
705                     nc++;
706           }
707           while (uio->uio_resid >= UIO_MX) {
708                     /* check for used ptys */
709                     n = ptyfs_next_active(vp->v_mount, i - 2);
710                     if (n < 0)
711                               break;
712                     dp->d_fileno = PTYFS_FILENO(PTYFSpts, n);
713                     dp->d_namlen = snprintf(dp->d_name, sizeof(dp->d_name),
714                         "%lld", (long long)(n));
715                     dp->d_type = DT_CHR;
716                     if ((error = uiomove(dp, UIO_MX, uio)) != 0)
717                               goto out;
718                     i = n + 3;
719                     if (cookies)
720                               *cookies++ = i;
721                     nc++;
722           }
723 
724 out:
725           /* not pertinent in error cases */
726           ncookies = nc;
727 
728           if (ap->a_ncookies) {
729                     if (error) {
730                               if (cookies)
731                                         free(*ap->a_cookies, M_TEMP);
732                               *ap->a_ncookies = 0;
733                               *ap->a_cookies = NULL;
734                     } else
735                               *ap->a_ncookies = ncookies;
736           }
737           uio->uio_offset = i;
738           free(dp, M_PTYFSTMP);
739           return error;
740 }
741 
742 int
ptyfs_open(void * v)743 ptyfs_open(void *v)
744 {
745           struct vop_open_args /* {
746                     struct vnode *a_vp;
747                     int  a_mode;
748                     kauth_cred_t a_cred;
749           } */ *ap = v;
750           struct vnode *vp = ap->a_vp;
751           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
752 
753           switch (ptyfs->ptyfs_type) {
754           case PTYFSpts:
755           case PTYFSptc:
756                     return spec_open(v);
757           case PTYFSroot:
758                     return 0;
759           default:
760                     return EINVAL;
761           }
762 }
763 
764 int
ptyfs_close(void * v)765 ptyfs_close(void *v)
766 {
767           struct vop_close_args /* {
768                     struct vnode *a_vp;
769                     int  a_fflag;
770                     kauth_cred_t a_cred;
771           } */ *ap = v;
772           struct vnode *vp = ap->a_vp;
773           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
774 
775           mutex_enter(vp->v_interlock);
776           if (vrefcnt(vp) > 1)
777                     PTYFS_ITIMES(ptyfs, NULL, NULL, NULL);
778           mutex_exit(vp->v_interlock);
779 
780           switch (ptyfs->ptyfs_type) {
781           case PTYFSpts:
782           case PTYFSptc:
783                     return spec_close(v);
784           case PTYFSroot:
785                     return 0;
786           default:
787                     return EINVAL;
788           }
789 }
790 
791 int
ptyfs_read(void * v)792 ptyfs_read(void *v)
793 {
794           struct vop_read_args /* {
795                     struct vnode *a_vp;
796                     struct uio *a_uio;
797                     int  a_ioflag;
798                     kauth_cred_t a_cred;
799           } */ *ap = v;
800           struct timespec ts;
801           struct vnode *vp = ap->a_vp;
802           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
803           int error;
804 
805           if (vp->v_type == VDIR)
806                     return EISDIR;
807 
808           ptyfs->ptyfs_status |= PTYFS_ACCESS;
809           /* hardclock() resolution is good enough for ptyfs */
810           getnanotime(&ts);
811           (void)ptyfs_update(vp, &ts, &ts, 0);
812 
813           switch (ptyfs->ptyfs_type) {
814           case PTYFSpts:
815           case PTYFSptc:
816                     VOP_UNLOCK(vp);
817                     error = cdev_read(vp->v_rdev, ap->a_uio, ap->a_ioflag);
818                     vn_lock(vp, LK_RETRY|LK_EXCLUSIVE);
819                     return error;
820           default:
821                     return EOPNOTSUPP;
822           }
823 }
824 
825 int
ptyfs_write(void * v)826 ptyfs_write(void *v)
827 {
828           struct vop_write_args /* {
829                     struct vnode *a_vp;
830                     struct uio *a_uio;
831                     int  a_ioflag;
832                     kauth_cred_t a_cred;
833           } */ *ap = v;
834           struct timespec ts;
835           struct vnode *vp = ap->a_vp;
836           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
837           int error;
838 
839           ptyfs->ptyfs_status |= PTYFS_MODIFY;
840           getnanotime(&ts);
841           (void)ptyfs_update(vp, &ts, &ts, 0);
842 
843           switch (ptyfs->ptyfs_type) {
844           case PTYFSpts:
845           case PTYFSptc:
846                     VOP_UNLOCK(vp);
847                     error = cdev_write(vp->v_rdev, ap->a_uio, ap->a_ioflag);
848                     vn_lock(vp, LK_RETRY|LK_EXCLUSIVE);
849                     return error;
850           default:
851                     return EOPNOTSUPP;
852           }
853 }
854 
855 int
ptyfs_ioctl(void * v)856 ptyfs_ioctl(void *v)
857 {
858           struct vop_ioctl_args /* {
859                     struct vnode *a_vp;
860                     u_long a_command;
861                     void *a_data;
862                     int  a_fflag;
863                     kauth_cred_t a_cred;
864           } */ *ap = v;
865           struct vnode *vp = ap->a_vp;
866           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
867 
868           switch (ptyfs->ptyfs_type) {
869           case PTYFSpts:
870           case PTYFSptc:
871                     return cdev_ioctl(vp->v_rdev, ap->a_command,
872                         ap->a_data, ap->a_fflag, curlwp);
873           default:
874                     return EOPNOTSUPP;
875           }
876 }
877 
878 int
ptyfs_poll(void * v)879 ptyfs_poll(void *v)
880 {
881           struct vop_poll_args /* {
882                     struct vnode *a_vp;
883                     int a_events;
884           } */ *ap = v;
885           struct vnode *vp = ap->a_vp;
886           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
887 
888           switch (ptyfs->ptyfs_type) {
889           case PTYFSpts:
890           case PTYFSptc:
891                     return cdev_poll(vp->v_rdev, ap->a_events, curlwp);
892           default:
893                     return genfs_poll(v);
894           }
895 }
896 
897 int
ptyfs_kqfilter(void * v)898 ptyfs_kqfilter(void *v)
899 {
900           struct vop_kqfilter_args /* {
901                     struct vnode *a_vp;
902                     struct knote *a_kn;
903           } */ *ap = v;
904           struct vnode *vp = ap->a_vp;
905           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
906 
907           switch (ptyfs->ptyfs_type) {
908           case PTYFSpts:
909           case PTYFSptc:
910                     return cdev_kqfilter(vp->v_rdev, ap->a_kn);
911           default:
912                     return genfs_kqfilter(v);
913           }
914 }
915 
916 static int
ptyfs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int flags)917 ptyfs_update(struct vnode *vp, const struct timespec *acc,
918     const struct timespec *mod, int flags)
919 {
920           struct ptyfsnode *ptyfs = VTOPTYFS(vp);
921 
922           if (vp->v_mount->mnt_flag & MNT_RDONLY)
923                     return 0;
924 
925           PTYFS_ITIMES(ptyfs, acc, mod, NULL);
926           return 0;
927 }
928 
929 void
ptyfs_itimes(struct ptyfsnode * ptyfs,const struct timespec * acc,const struct timespec * mod,const struct timespec * cre)930 ptyfs_itimes(struct ptyfsnode *ptyfs, const struct timespec *acc,
931     const struct timespec *mod, const struct timespec *cre)
932 {
933           struct timespec now;
934 
935           KASSERT(ptyfs->ptyfs_status & (PTYFS_ACCESS|PTYFS_CHANGE|PTYFS_MODIFY));
936 
937           getnanotime(&now);
938           if (ptyfs->ptyfs_status & PTYFS_ACCESS) {
939                     if (acc == NULL)
940                               acc = &now;
941                     ptyfs->ptyfs_atime = *acc;
942           }
943           if (ptyfs->ptyfs_status & PTYFS_MODIFY) {
944                     if (mod == NULL)
945                               mod = &now;
946                     ptyfs->ptyfs_mtime = *mod;
947           }
948           if (ptyfs->ptyfs_status & PTYFS_CHANGE) {
949                     if (cre == NULL)
950                               cre = &now;
951                     ptyfs->ptyfs_ctime = *cre;
952           }
953           ptyfs->ptyfs_status &= ~(PTYFS_ACCESS|PTYFS_CHANGE|PTYFS_MODIFY);
954 }
955 
956 /*
957  * convert decimal ascii to int
958  */
959 static int
atoi(const char * b,size_t len)960 atoi(const char *b, size_t len)
961 {
962           int p = 0;
963 
964           while (len--) {
965                     char c = *b++;
966                     if (c < '0' || c > '9')
967                               return -1;
968                     p = 10 * p + (c - '0');
969           }
970 
971           return p;
972 }
973