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