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