ptyfs_vnops.c revision 1.59 1 /* $NetBSD: ptyfs_vnops.c,v 1.59 2020/05/16 18:31:49 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.59 2020/05/16 18:31:49 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 EINVAL;
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 if (ptyfs->ptyfs_type == PTYFSroot)
460 return EPERM;
461 error = ptyfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
462 if (error)
463 return error;
464 }
465
466 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
467 vap->va_birthtime.tv_sec != VNOVAL) {
468 if (vp->v_mount->mnt_flag & MNT_RDONLY)
469 return EROFS;
470 if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0)
471 return EPERM;
472 error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
473 NULL, genfs_can_chtimes(vp, cred, ptyfs->ptyfs_uid,
474 vap->va_vaflags));
475 if (error)
476 return (error);
477 if (vap->va_atime.tv_sec != VNOVAL)
478 if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
479 ptyfs->ptyfs_status |= PTYFS_ACCESS;
480 if (vap->va_mtime.tv_sec != VNOVAL) {
481 ptyfs->ptyfs_status |= PTYFS_CHANGE | PTYFS_MODIFY;
482 if (vp->v_mount->mnt_flag & MNT_RELATIME)
483 ptyfs->ptyfs_status |= PTYFS_ACCESS;
484 }
485 if (vap->va_birthtime.tv_sec != VNOVAL)
486 ptyfs->ptyfs_birthtime = vap->va_birthtime;
487 ptyfs->ptyfs_status |= PTYFS_CHANGE;
488 error = ptyfs_update(vp, &vap->va_atime, &vap->va_mtime, 0);
489 if (error)
490 return error;
491 }
492 if (vap->va_mode != (mode_t)VNOVAL) {
493 if (vp->v_mount->mnt_flag & MNT_RDONLY)
494 return EROFS;
495 if (ptyfs->ptyfs_type == PTYFSroot)
496 return EPERM;
497 if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0 &&
498 (vap->va_mode &
499 (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IXOTH|S_IWOTH)))
500 return EPERM;
501 error = ptyfs_chmod(vp, vap->va_mode, cred, l);
502 if (error)
503 return error;
504 }
505 VN_KNOTE(vp, NOTE_ATTRIB);
506 return 0;
507 }
508
509 /*
510 * Change the mode on a file.
511 * Inode must be locked before calling.
512 */
513 static int
514 ptyfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l)
515 {
516 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
517 int error;
518
519 error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
520 NULL, genfs_can_chmod(vp, cred, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid,
521 mode));
522 if (error)
523 return (error);
524
525 ptyfs->ptyfs_mode &= ~ALLPERMS;
526 ptyfs->ptyfs_mode |= (mode & ALLPERMS);
527 return 0;
528 }
529
530 /*
531 * Perform chown operation on inode ip;
532 * inode must be locked prior to call.
533 */
534 static int
535 ptyfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
536 struct lwp *l)
537 {
538 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
539 int error;
540
541 if (uid == (uid_t)VNOVAL)
542 uid = ptyfs->ptyfs_uid;
543 if (gid == (gid_t)VNOVAL)
544 gid = ptyfs->ptyfs_gid;
545
546 error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,
547 NULL, genfs_can_chown(vp, cred, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid,
548 uid, gid));
549 if (error)
550 return (error);
551
552 ptyfs->ptyfs_gid = gid;
553 ptyfs->ptyfs_uid = uid;
554 return 0;
555 }
556
557 /*
558 * implement access checking.
559 *
560 * actually, the check for super-user is slightly
561 * broken since it will allow read access to write-only
562 * objects. this doesn't cause any particular trouble
563 * but does mean that the i/o entry points need to check
564 * that the operation really does make sense.
565 */
566 int
567 ptyfs_access(void *v)
568 {
569 struct vop_access_args /* {
570 struct vnode *a_vp;
571 accmode_t a_accmode;
572 kauth_cred_t a_cred;
573 } */ *ap = v;
574 struct vattr va;
575 int error;
576
577 if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0)
578 return error;
579
580 return kauth_authorize_vnode(ap->a_cred,
581 KAUTH_ACCESS_ACTION(ap->a_accmode, ap->a_vp->v_type, va.va_mode),
582 ap->a_vp, NULL, genfs_can_access(ap->a_vp, ap->a_cred, va.va_uid,
583 va.va_gid, va.va_mode, NULL, ap->a_accmode));
584 }
585
586 /*
587 * lookup. this is incredibly complicated in the
588 * general case, however for most pseudo-filesystems
589 * very little needs to be done.
590 *
591 * Locking isn't hard here, just poorly documented.
592 *
593 * If we're looking up ".", just vref the parent & return it.
594 *
595 * If we're looking up "..", unlock the parent, and lock "..". If everything
596 * went ok, try to re-lock the parent. We do this to prevent lock races.
597 *
598 * For anything else, get the needed node.
599 *
600 * We try to exit with the parent locked in error cases.
601 */
602 int
603 ptyfs_lookup(void *v)
604 {
605 struct vop_lookup_v2_args /* {
606 struct vnode * a_dvp;
607 struct vnode ** a_vpp;
608 struct componentname * a_cnp;
609 } */ *ap = v;
610 struct componentname *cnp = ap->a_cnp;
611 struct vnode **vpp = ap->a_vpp;
612 struct vnode *dvp = ap->a_dvp;
613 const char *pname = cnp->cn_nameptr;
614 struct ptyfsnode *ptyfs;
615 int pty, error;
616
617 *vpp = NULL;
618
619 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
620 return EROFS;
621
622 if (cnp->cn_namelen == 1 && *pname == '.') {
623 *vpp = dvp;
624 vref(dvp);
625 return 0;
626 }
627
628 ptyfs = VTOPTYFS(dvp);
629 switch (ptyfs->ptyfs_type) {
630 case PTYFSroot:
631 /*
632 * Shouldn't get here with .. in the root node.
633 */
634 if (cnp->cn_flags & ISDOTDOT)
635 return EIO;
636
637 pty = atoi(pname, cnp->cn_namelen);
638 if (pty < 0 || ptyfs_next_active(dvp->v_mount, pty) != pty)
639 break;
640 error = ptyfs_allocvp(dvp->v_mount, vpp, PTYFSpts, pty);
641 if (error)
642 return error;
643 if (ptyfs_next_active(dvp->v_mount, pty) != pty) {
644 vrele(*vpp);
645 *vpp = NULL;
646 return ENOENT;
647 }
648 return 0;
649
650 default:
651 return ENOTDIR;
652 }
653
654 return cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS;
655 }
656
657 /*
658 * readdir returns directory entries from ptyfsnode (vp).
659 *
660 * the strategy here with ptyfs is to generate a single
661 * directory entry at a time (struct dirent) and then
662 * copy that out to userland using uiomove. a more efficent
663 * though more complex implementation, would try to minimize
664 * the number of calls to uiomove(). for ptyfs, this is
665 * hardly worth the added code complexity.
666 *
667 * this should just be done through read()
668 */
669 int
670 ptyfs_readdir(void *v)
671 {
672 struct vop_readdir_args /* {
673 struct vnode *a_vp;
674 struct uio *a_uio;
675 kauth_cred_t a_cred;
676 int *a_eofflag;
677 off_t **a_cookies;
678 int *a_ncookies;
679 } */ *ap = v;
680 struct uio *uio = ap->a_uio;
681 struct dirent *dp;
682 struct ptyfsnode *ptyfs;
683 off_t i;
684 int error;
685 off_t *cookies = NULL;
686 int ncookies;
687 struct vnode *vp;
688 int n, nc = 0;
689
690 vp = ap->a_vp;
691 ptyfs = VTOPTYFS(vp);
692
693 if (uio->uio_resid < UIO_MX)
694 return EINVAL;
695 if (uio->uio_offset < 0)
696 return EINVAL;
697
698 dp = malloc(sizeof(struct dirent), M_PTYFSTMP, M_WAITOK | M_ZERO);
699
700 error = 0;
701 i = uio->uio_offset;
702 dp->d_reclen = UIO_MX;
703 ncookies = uio->uio_resid / UIO_MX;
704
705 if (ptyfs->ptyfs_type != PTYFSroot) {
706 error = ENOTDIR;
707 goto out;
708 }
709
710 if (i >= npty)
711 goto out;
712
713 if (ap->a_ncookies) {
714 ncookies = uimin(ncookies, (npty + 2 - i));
715 cookies = malloc(ncookies * sizeof (off_t),
716 M_TEMP, M_WAITOK);
717 *ap->a_cookies = cookies;
718 }
719
720 for (; i < 2; i++) {
721 /* `.' and/or `..' */
722 dp->d_fileno = PTYFS_FILENO(0, PTYFSroot);
723 dp->d_namlen = i + 1;
724 (void)memcpy(dp->d_name, "..", dp->d_namlen);
725 dp->d_name[i + 1] = '\0';
726 dp->d_type = DT_DIR;
727 if ((error = uiomove(dp, UIO_MX, uio)) != 0)
728 goto out;
729 if (cookies)
730 *cookies++ = i + 1;
731 nc++;
732 }
733 while (uio->uio_resid >= UIO_MX) {
734 /* check for used ptys */
735 n = ptyfs_next_active(vp->v_mount, i - 2);
736 if (n < 0)
737 break;
738 dp->d_fileno = PTYFS_FILENO(n, PTYFSpts);
739 dp->d_namlen = snprintf(dp->d_name, sizeof(dp->d_name),
740 "%lld", (long long)(n));
741 dp->d_type = DT_CHR;
742 if ((error = uiomove(dp, UIO_MX, uio)) != 0)
743 goto out;
744 i = n + 3;
745 if (cookies)
746 *cookies++ = i;
747 nc++;
748 }
749
750 out:
751 /* not pertinent in error cases */
752 ncookies = nc;
753
754 if (ap->a_ncookies) {
755 if (error) {
756 if (cookies)
757 free(*ap->a_cookies, M_TEMP);
758 *ap->a_ncookies = 0;
759 *ap->a_cookies = NULL;
760 } else
761 *ap->a_ncookies = ncookies;
762 }
763 uio->uio_offset = i;
764 free(dp, M_PTYFSTMP);
765 return error;
766 }
767
768 int
769 ptyfs_open(void *v)
770 {
771 struct vop_open_args /* {
772 struct vnode *a_vp;
773 int a_mode;
774 kauth_cred_t a_cred;
775 } */ *ap = v;
776 struct vnode *vp = ap->a_vp;
777 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
778
779 switch (ptyfs->ptyfs_type) {
780 case PTYFSpts:
781 case PTYFSptc:
782 return spec_open(v);
783 case PTYFSroot:
784 return 0;
785 default:
786 return EINVAL;
787 }
788 }
789
790 int
791 ptyfs_close(void *v)
792 {
793 struct vop_close_args /* {
794 struct vnode *a_vp;
795 int a_fflag;
796 kauth_cred_t a_cred;
797 } */ *ap = v;
798 struct vnode *vp = ap->a_vp;
799 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
800
801 mutex_enter(vp->v_interlock);
802 if (vrefcnt(vp) > 1)
803 PTYFS_ITIMES(ptyfs, NULL, NULL, NULL);
804 mutex_exit(vp->v_interlock);
805
806 switch (ptyfs->ptyfs_type) {
807 case PTYFSpts:
808 case PTYFSptc:
809 return spec_close(v);
810 case PTYFSroot:
811 return 0;
812 default:
813 return EINVAL;
814 }
815 }
816
817 int
818 ptyfs_read(void *v)
819 {
820 struct vop_read_args /* {
821 struct vnode *a_vp;
822 struct uio *a_uio;
823 int a_ioflag;
824 kauth_cred_t a_cred;
825 } */ *ap = v;
826 struct timespec ts;
827 struct vnode *vp = ap->a_vp;
828 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
829 int error;
830
831 if (vp->v_type == VDIR)
832 return EISDIR;
833
834 ptyfs->ptyfs_status |= PTYFS_ACCESS;
835 /* hardclock() resolution is good enough for ptyfs */
836 getnanotime(&ts);
837 (void)ptyfs_update(vp, &ts, &ts, 0);
838
839 switch (ptyfs->ptyfs_type) {
840 case PTYFSpts:
841 case PTYFSptc:
842 VOP_UNLOCK(vp);
843 error = cdev_read(vp->v_rdev, ap->a_uio, ap->a_ioflag);
844 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE);
845 return error;
846 default:
847 return EOPNOTSUPP;
848 }
849 }
850
851 int
852 ptyfs_write(void *v)
853 {
854 struct vop_write_args /* {
855 struct vnode *a_vp;
856 struct uio *a_uio;
857 int a_ioflag;
858 kauth_cred_t a_cred;
859 } */ *ap = v;
860 struct timespec ts;
861 struct vnode *vp = ap->a_vp;
862 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
863 int error;
864
865 ptyfs->ptyfs_status |= PTYFS_MODIFY;
866 getnanotime(&ts);
867 (void)ptyfs_update(vp, &ts, &ts, 0);
868
869 switch (ptyfs->ptyfs_type) {
870 case PTYFSpts:
871 case PTYFSptc:
872 VOP_UNLOCK(vp);
873 error = cdev_write(vp->v_rdev, ap->a_uio, ap->a_ioflag);
874 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE);
875 return error;
876 default:
877 return EOPNOTSUPP;
878 }
879 }
880
881 int
882 ptyfs_ioctl(void *v)
883 {
884 struct vop_ioctl_args /* {
885 struct vnode *a_vp;
886 u_long a_command;
887 void *a_data;
888 int a_fflag;
889 kauth_cred_t a_cred;
890 } */ *ap = v;
891 struct vnode *vp = ap->a_vp;
892 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
893
894 switch (ptyfs->ptyfs_type) {
895 case PTYFSpts:
896 case PTYFSptc:
897 return cdev_ioctl(vp->v_rdev, ap->a_command,
898 ap->a_data, ap->a_fflag, curlwp);
899 default:
900 return EOPNOTSUPP;
901 }
902 }
903
904 int
905 ptyfs_poll(void *v)
906 {
907 struct vop_poll_args /* {
908 struct vnode *a_vp;
909 int a_events;
910 } */ *ap = v;
911 struct vnode *vp = ap->a_vp;
912 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
913
914 switch (ptyfs->ptyfs_type) {
915 case PTYFSpts:
916 case PTYFSptc:
917 return cdev_poll(vp->v_rdev, ap->a_events, curlwp);
918 default:
919 return genfs_poll(v);
920 }
921 }
922
923 int
924 ptyfs_kqfilter(void *v)
925 {
926 struct vop_kqfilter_args /* {
927 struct vnode *a_vp;
928 struct knote *a_kn;
929 } */ *ap = v;
930 struct vnode *vp = ap->a_vp;
931 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
932
933 switch (ptyfs->ptyfs_type) {
934 case PTYFSpts:
935 case PTYFSptc:
936 return cdev_kqfilter(vp->v_rdev, ap->a_kn);
937 default:
938 return genfs_kqfilter(v);
939 }
940 }
941
942 static int
943 ptyfs_update(struct vnode *vp, const struct timespec *acc,
944 const struct timespec *mod, int flags)
945 {
946 struct ptyfsnode *ptyfs = VTOPTYFS(vp);
947
948 if (vp->v_mount->mnt_flag & MNT_RDONLY)
949 return 0;
950
951 PTYFS_ITIMES(ptyfs, acc, mod, NULL);
952 return 0;
953 }
954
955 void
956 ptyfs_itimes(struct ptyfsnode *ptyfs, const struct timespec *acc,
957 const struct timespec *mod, const struct timespec *cre)
958 {
959 struct timespec now;
960
961 KASSERT(ptyfs->ptyfs_status & (PTYFS_ACCESS|PTYFS_CHANGE|PTYFS_MODIFY));
962
963 getnanotime(&now);
964 if (ptyfs->ptyfs_status & PTYFS_ACCESS) {
965 if (acc == NULL)
966 acc = &now;
967 ptyfs->ptyfs_atime = *acc;
968 }
969 if (ptyfs->ptyfs_status & PTYFS_MODIFY) {
970 if (mod == NULL)
971 mod = &now;
972 ptyfs->ptyfs_mtime = *mod;
973 }
974 if (ptyfs->ptyfs_status & PTYFS_CHANGE) {
975 if (cre == NULL)
976 cre = &now;
977 ptyfs->ptyfs_ctime = *cre;
978 }
979 ptyfs->ptyfs_status &= ~(PTYFS_ACCESS|PTYFS_CHANGE|PTYFS_MODIFY);
980 }
981
982 /*
983 * convert decimal ascii to int
984 */
985 static int
986 atoi(const char *b, size_t len)
987 {
988 int p = 0;
989
990 while (len--) {
991 char c = *b++;
992 if (c < '0' || c > '9')
993 return -1;
994 p = 10 * p + (c - '0');
995 }
996
997 return p;
998 }
999