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