1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * All rights reserved.
5  *
6  * This code is derived from software donated to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)portal_vnops.c	8.1 (Berkeley) 06/10/93
12  *
13  * $Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp jsp $
14  */
15 
16 /*
17  * Portal Filesystem
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/types.h>
24 #include <sys/time.h>
25 #include <sys/proc.h>
26 #include <sys/filedesc.h>
27 #include <sys/vnode.h>
28 #include <sys/file.h>
29 #include <sys/stat.h>
30 #include <sys/mount.h>
31 #include <sys/malloc.h>
32 #include <sys/namei.h>
33 #include <sys/mbuf.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 #include <sys/un.h>
37 #include <sys/unpcb.h>
38 #include <miscfs/portal/portal.h>
39 
40 static int portal_fileid = PORTAL_ROOTFILEID+1;
41 
42 static void
43 portal_closefd(p, fd)
44 	struct proc *p;
45 	int fd;
46 {
47 	int error;
48 	struct {
49 		int fd;
50 	} ua;
51 	int rc;
52 
53 	ua.fd = fd;
54 	error = close(p, &ua, &rc);
55 	/*
56 	 * We should never get an error, and there isn't anything
57 	 * we could do if we got one, so just print a message.
58 	 */
59 	if (error)
60 		printf("portal_closefd: error = %d\n", error);
61 }
62 
63 /*
64  * vp is the current namei directory
65  * cnp is the name to locate in that directory...
66  */
67 portal_lookup(ap)
68 	struct vop_lookup_args /* {
69 		struct vnode * a_dvp;
70 		struct vnode ** a_vpp;
71 		struct componentname * a_cnp;
72 	} */ *ap;
73 {
74 	char *pname = ap->a_cnp->cn_nameptr;
75 	struct portalnode *pt;
76 	int error;
77 	struct vnode *fvp = 0;
78 	char *path;
79 	int size;
80 
81 #ifdef PORTAL_DIAGNOSTIC
82 	printf("portal_lookup(%s)\n", pname);
83 #endif
84 	if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
85 		*ap->a_vpp = ap->a_dvp;
86 		VREF(ap->a_dvp);
87 		/*VOP_LOCK(ap->a_dvp);*/
88 		return (0);
89 	}
90 
91 
92 #ifdef PORTAL_DIAGNOSTIC
93 	printf("portal_lookup: allocate new vnode\n");
94 #endif
95 	error = getnewvnode(VT_UFS, ap->a_dvp->v_mount, portal_vnodeop_p, &fvp);
96 	if (error)
97 		goto bad;
98 	fvp->v_type = VREG;
99 	MALLOC(fvp->v_data, void *, sizeof(struct portalnode),
100 		M_TEMP, M_WAITOK);
101 
102 	pt = VTOPORTAL(fvp);
103 	/*
104 	 * Save all of the remaining pathname and
105 	 * advance the namei next pointer to the end
106 	 * of the string.
107 	 */
108 	for (size = 0, path = pname; *path; path++)
109 		size++;
110 	ap->a_cnp->cn_consume = size - ap->a_cnp->cn_namelen;
111 
112 	pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK);
113 	pt->pt_size = size+1;
114 	bcopy(pname, pt->pt_arg, pt->pt_size);
115 	pt->pt_fileid = portal_fileid++;
116 
117 	*ap->a_vpp = fvp;
118 	/*VOP_LOCK(fvp);*/
119 #ifdef PORTAL_DIAGNOSTIC
120 	printf("portal_lookup: newvp = %x\n", fvp);
121 #endif
122 	return (0);
123 
124 bad:;
125 	if (fvp) {
126 #ifdef PORTAL_DIAGNOSTIC
127 		printf("portal_lookup: vrele(%x)\n", fvp);
128 #endif
129 		vrele(fvp);
130 	}
131 	*ap->a_vpp = NULL;
132 #ifdef PORTAL_DIAGNOSTIC
133 	printf("portal_lookup: error = %d\n", error);
134 #endif
135 	return (error);
136 }
137 
138 static int
139 portal_connect(so, so2)
140 	struct socket *so;
141 	struct socket *so2;
142 {
143 	/* from unp_connect, bypassing the namei stuff... */
144 
145 	struct socket *so3;
146 	struct unpcb *unp2;
147 	struct unpcb *unp3;
148 
149 #ifdef PORTAL_DIAGNOSTIC
150 	printf("portal_connect\n");
151 #endif
152 
153 	if (so2 == 0)
154 		return (ECONNREFUSED);
155 
156 	if (so->so_type != so2->so_type)
157 		return (EPROTOTYPE);
158 
159 	if ((so2->so_options & SO_ACCEPTCONN) == 0)
160 		return (ECONNREFUSED);
161 
162 #ifdef PORTAL_DIAGNOSTIC
163 	printf("portal_connect: calling sonewconn\n");
164 #endif
165 
166 	if ((so3 = sonewconn(so2, 0)) == 0)
167 		return (ECONNREFUSED);
168 
169 	unp2 = sotounpcb(so2);
170 	unp3 = sotounpcb(so3);
171 	if (unp2->unp_addr)
172 		unp3->unp_addr = m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
173 
174 	so2 = so3;
175 
176 #ifdef PORTAL_DIAGNOSTIC
177 	printf("portal_connect: calling unp_connect2\n");
178 #endif
179 
180 	return (unp_connect2(so, so2));
181 }
182 
183 portal_open(ap)
184 	struct vop_open_args /* {
185 		struct vnode *a_vp;
186 		int  a_mode;
187 		struct ucred *a_cred;
188 		struct proc *a_p;
189 	} */ *ap;
190 {
191 	struct socket *so = 0;
192 	struct portalnode *pt;
193 	struct proc *p = ap->a_p;
194 	struct vnode *vp = ap->a_vp;
195 	int s;
196 	struct uio auio;
197 	struct iovec aiov[2];
198 	int res;
199 	struct mbuf *cm = 0;
200 	struct cmsghdr *cmsg;
201 	int newfds;
202 	int *ip;
203 	int fd;
204 	int error;
205 	int len;
206 	struct portalmount *fmp;
207 	struct file *fp;
208 	struct portal_cred pcred;
209 
210 	/*
211 	 * Nothing to do when opening the root node.
212 	 */
213 	if (vp->v_flag & VROOT)
214 		return (0);
215 
216 #ifdef PORTAL_DIAGNOSTIC
217 	printf("portal_open(%x)\n", vp);
218 #endif
219 
220 	/*
221 	 * Can't be opened unless the caller is set up
222 	 * to deal with the side effects.  Check for this
223 	 * by testing whether the p_dupfd has been set.
224 	 */
225 	if (p->p_dupfd >= 0)
226 		return (ENODEV);
227 
228 	pt = VTOPORTAL(vp);
229 	fmp = VFSTOPORTAL(vp->v_mount);
230 
231 	/*
232 	 * Create a new socket.
233 	 */
234 	error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
235 	if (error)
236 		goto bad;
237 
238 	/*
239 	 * Reserve some buffer space
240 	 */
241 #ifdef PORTAL_DIAGNOSTIC
242 	printf("portal_open: calling soreserve\n");
243 #endif
244 	res = pt->pt_size + sizeof(pcred) + 512;	/* XXX */
245 	error = soreserve(so, res, res);
246 	if (error)
247 		goto bad;
248 
249 	/*
250 	 * Kick off connection
251 	 */
252 #ifdef PORTAL_DIAGNOSTIC
253 	printf("portal_open: calling portal_connect\n");
254 #endif
255 	error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
256 	if (error)
257 		goto bad;
258 
259 	/*
260 	 * Wait for connection to complete
261 	 */
262 #ifdef PORTAL_DIAGNOSTIC
263 	printf("portal_open: waiting for connect\n");
264 #endif
265 	/*
266 	 * XXX: Since the mount point is holding a reference on the
267 	 * underlying server socket, it is not easy to find out whether
268 	 * the server process is still running.  To handle this problem
269 	 * we loop waiting for the new socket to be connected (something
270 	 * which will only happen if the server is still running) or for
271 	 * the reference count on the server socket to drop to 1, which
272 	 * will happen if the server dies.  Sleep for 5 second intervals
273 	 * and keep polling the reference count.   XXX.
274 	 */
275 	s = splnet();
276 	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
277 		if (fmp->pm_server->f_count == 1) {
278 			error = ECONNREFUSED;
279 			splx(s);
280 #ifdef PORTAL_DIAGNOSTIC
281 			printf("portal_open: server process has gone away\n");
282 #endif
283 			goto bad;
284 		}
285 		(void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
286 	}
287 	splx(s);
288 
289 	if (so->so_error) {
290 		error = so->so_error;
291 		goto bad;
292 	}
293 
294 	/*
295 	 * Set miscellaneous flags
296 	 */
297 	so->so_rcv.sb_timeo = 0;
298 	so->so_snd.sb_timeo = 0;
299 	so->so_rcv.sb_flags |= SB_NOINTR;
300 	so->so_snd.sb_flags |= SB_NOINTR;
301 
302 #ifdef PORTAL_DIAGNOSTIC
303 	printf("portal_open: constructing data uio\n");
304 #endif
305 
306 	pcred.pcr_flag = ap->a_mode;
307 	pcred.pcr_uid = ap->a_cred->cr_uid;
308 	pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
309 	bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
310 	aiov[0].iov_base = (caddr_t) &pcred;
311 	aiov[0].iov_len = sizeof(pcred);
312 	aiov[1].iov_base = pt->pt_arg;
313 	aiov[1].iov_len = pt->pt_size;
314 	auio.uio_iov = aiov;
315 	auio.uio_iovcnt = 2;
316 	auio.uio_rw = UIO_WRITE;
317 	auio.uio_segflg = UIO_SYSSPACE;
318 	auio.uio_procp = p;
319 	auio.uio_offset = 0;
320 	auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
321 
322 #ifdef PORTAL_DIAGNOSTIC
323 	printf("portal_open: sending data to server\n");
324 #endif
325 	error = sosend(so, (struct sockaddr *) 0, &auio,
326 			(struct mbuf *) 0, (struct mbuf *) 0, 0);
327 	if (error)
328 		goto bad;
329 
330 	len = auio.uio_resid = sizeof(int);
331 	do {
332 		struct mbuf *m = 0;
333 		int flags = MSG_WAITALL;
334 #ifdef PORTAL_DIAGNOSTIC
335 		printf("portal_open: receiving data from server\n");
336 		printf("portal_open: so = %x, cm = %x, resid = %d\n",
337 				so, cm, auio.uio_resid);
338 		printf("portal_open, uio=%x, mp0=%x, controlp=%x\n", &auio, &cm);
339 #endif
340 		error = soreceive(so, (struct mbuf **) 0, &auio,
341 					&m, &cm, &flags);
342 #ifdef PORTAL_DIAGNOSTIC
343 		printf("portal_open: after receiving data\n");
344 		printf("portal_open: so = %x, cm = %x, resid = %d\n",
345 				so, cm, auio.uio_resid);
346 #endif
347 		if (error)
348 			goto bad;
349 
350 		/*
351 		 * Grab an error code from the mbuf.
352 		 */
353 		if (m) {
354 			m = m_pullup(m, sizeof(int));	/* Needed? */
355 			if (m) {
356 				error = *(mtod(m, int *));
357 				m_freem(m);
358 			} else {
359 				error = EINVAL;
360 			}
361 #ifdef PORTAL_DIAGNOSTIC
362 			printf("portal_open: error returned is %d\n", error);
363 #endif
364 		} else {
365 			if (cm == 0) {
366 #ifdef PORTAL_DIAGNOSTIC
367 				printf("portal_open: no rights received\n");
368 #endif
369 				error = ECONNRESET;	 /* XXX */
370 #ifdef notdef
371 				break;
372 #endif
373 			}
374 		}
375 	} while (cm == 0 && auio.uio_resid == len && !error);
376 
377 	if (cm == 0)
378 		goto bad;
379 
380 	if (auio.uio_resid) {
381 #ifdef PORTAL_DIAGNOSTIC
382 		printf("portal_open: still need another %d bytes\n", auio.uio_resid);
383 #endif
384 		error = 0;
385 #ifdef notdef
386 		error = EMSGSIZE;
387 		goto bad;
388 #endif
389 	}
390 
391 	/*
392 	 * XXX: Break apart the control message, and retrieve the
393 	 * received file descriptor.  Note that more than one descriptor
394 	 * may have been received, or that the rights chain may have more
395 	 * than a single mbuf in it.  What to do?
396 	 */
397 #ifdef PORTAL_DIAGNOSTIC
398 	printf("portal_open: about to break apart control message\n");
399 #endif
400 	cmsg = mtod(cm, struct cmsghdr *);
401 	newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
402 	if (newfds == 0) {
403 #ifdef PORTAL_DIAGNOSTIC
404 		printf("portal_open: received no fds\n");
405 #endif
406 		error = ECONNREFUSED;
407 		goto bad;
408 	}
409 	/*
410 	 * At this point the rights message consists of a control message
411 	 * header, followed by a data region containing a vector of
412 	 * integer file descriptors.  The fds were allocated by the action
413 	 * of receiving the control message.
414 	 */
415 	ip = (int *) (cmsg + 1);
416 	fd = *ip++;
417 	if (newfds > 1) {
418 		/*
419 		 * Close extra fds.
420 		 */
421 		int i;
422 		printf("portal_open: %d extra fds\n", newfds - 1);
423 		for (i = 1; i < newfds; i++) {
424 			portal_closefd(p, *ip);
425 			ip++;
426 		}
427 	}
428 
429 	/*
430 	 * Check that the mode the file is being opened for is a subset
431 	 * of the mode of the existing descriptor.
432 	 */
433 #ifdef PORTAL_DIAGNOSTIC
434 	printf("portal_open: checking file flags, fd = %d\n", fd);
435 #endif
436  	fp = p->p_fd->fd_ofiles[fd];
437 	if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
438 		portal_closefd(p, fd);
439 		error = EACCES;
440 		goto bad;
441 	}
442 
443 #ifdef PORTAL_DIAGNOSTIC
444 	printf("portal_open: got fd = %d\n", fd);
445 #endif
446 	/*
447 	 * Save the dup fd in the proc structure then return the
448 	 * special error code (ENXIO) which causes magic things to
449 	 * happen in vn_open.  The whole concept is, well, hmmm.
450 	 */
451 	p->p_dupfd = fd;
452 	error = ENXIO;
453 
454 bad:;
455 	/*
456 	 * And discard the control message.
457 	 */
458 	if (cm) {
459 #ifdef PORTAL_DIAGNOSTIC
460 		printf("portal_open: free'ing control message\n");
461 #endif
462 		m_freem(cm);
463 	}
464 
465 	if (so) {
466 #ifdef PORTAL_DIAGNOSTIC
467 		printf("portal_open: calling soshutdown\n");
468 #endif
469 		soshutdown(so, 2);
470 #ifdef PORTAL_DIAGNOSTIC
471 		printf("portal_open: calling soclose\n");
472 #endif
473 		soclose(so);
474 	}
475 #ifdef PORTAL_DIAGNOSTIC
476 	if (error != ENODEV)
477 		printf("portal_open: error = %d\n", error);
478 #endif
479 	return (error);
480 }
481 
482 portal_getattr(ap)
483 	struct vop_getattr_args /* {
484 		struct vnode *a_vp;
485 		struct vattr *a_vap;
486 		struct ucred *a_cred;
487 		struct proc *a_p;
488 	} */ *ap;
489 {
490 	struct vnode *vp = ap->a_vp;
491 	struct vattr *vap = ap->a_vap;
492 	unsigned fd;
493 	int error;
494 
495 	bzero((caddr_t) vap, sizeof(*vap));
496 	vattr_null(vap);
497 	vap->va_uid = 0;
498 	vap->va_gid = 0;
499 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
500 	vap->va_size = DEV_BSIZE;
501 	vap->va_blocksize = DEV_BSIZE;
502 	microtime(&vap->va_atime);
503 	vap->va_mtime = vap->va_atime;
504 	vap->va_ctime = vap->va_ctime;
505 	vap->va_gen = 0;
506 	vap->va_flags = 0;
507 	vap->va_rdev = 0;
508 	/* vap->va_qbytes = 0; */
509 	vap->va_bytes = 0;
510 	/* vap->va_qsize = 0; */
511 	if (vp->v_flag & VROOT) {
512 #ifdef PORTAL_DIAGNOSTIC
513 		printf("portal_getattr: stat rootdir\n");
514 #endif
515 		vap->va_type = VDIR;
516 		vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
517 				S_IRGRP|S_IWGRP|S_IXGRP|
518 				S_IROTH|S_IWOTH|S_IXOTH;
519 		vap->va_nlink = 2;
520 		vap->va_fileid = 2;
521 	} else {
522 #ifdef PORTAL_DIAGNOSTIC
523 		printf("portal_getattr: stat portal\n");
524 #endif
525 		vap->va_type = VREG;
526 		vap->va_mode = S_IRUSR|S_IWUSR|
527 				S_IRGRP|S_IWGRP|
528 				S_IROTH|S_IWOTH;
529 		vap->va_nlink = 1;
530 		vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
531 	}
532 	return (0);
533 }
534 
535 portal_setattr(ap)
536 	struct vop_setattr_args /* {
537 		struct vnode *a_vp;
538 		struct vattr *a_vap;
539 		struct ucred *a_cred;
540 		struct proc *a_p;
541 	} */ *ap;
542 {
543 	/*
544 	 * Can't mess with the root vnode
545 	 */
546 	if (ap->a_vp->v_flag & VROOT)
547 		return (EACCES);
548 
549 	return (0);
550 }
551 
552 /*
553  * Fake readdir, just return empty directory.
554  * It is hard to deal with '.' and '..' so don't bother.
555  */
556 portal_readdir(ap)
557 	struct vop_readdir_args /* {
558 		struct vnode *a_vp;
559 		struct uio *a_uio;
560 		struct ucred *a_cred;
561 	} */ *ap;
562 {
563 	/* *ap->a_eofflagp = 1; */
564 	return (0);
565 }
566 
567 portal_inactive(ap)
568 	struct vop_inactive_args /* {
569 		struct vnode *a_vp;
570 	} */ *ap;
571 {
572 #ifdef PORTAL_DIAGNOSTIC
573 	if (VTOPORTAL(ap->a_vp)->pt_arg)
574 		printf("portal_inactive(%x, %s)\n", ap->a_vp, VTOPORTAL(ap->a_vp)->pt_arg);
575 	else
576 		printf("portal_inactive(%x)\n", ap->a_vp);
577 #endif
578 	/*vgone(ap->a_vp);*/
579 	return (0);
580 }
581 
582 portal_reclaim(ap)
583 	struct vop_reclaim_args /* {
584 		struct vnode *a_vp;
585 	} */ *ap;
586 {
587 	struct portalnode *pt = VTOPORTAL(ap->a_vp);
588 #ifdef PORTAL_DIAGOISTIC
589 	printf("portal_reclaim(%x)\n", ap->a_vp);
590 #endif
591 	if (pt->pt_arg) {
592 		free((caddr_t) pt->pt_arg, M_TEMP);
593 		pt->pt_arg = 0;
594 	}
595 	FREE(pt, M_TEMP);
596 	return (0);
597 }
598 
599 /*
600  * Print out the contents of a Portal vnode.
601  */
602 /* ARGSUSED */
603 portal_print(ap)
604 	struct vop_print_args /* {
605 		struct vnode *a_vp;
606 	} */ *ap;
607 {
608 
609 	printf("tag VT_PORTAL, portal vnode\n");
610 	return (0);
611 }
612 
613 /*void*/
614 portal_vfree(ap)
615 	struct vop_vfree_args /* {
616 		struct vnode *a_pvp;
617 		ino_t a_ino;
618 		int a_mode;
619 	} */ *ap;
620 {
621 
622 	return (0);
623 }
624 
625 
626 /*
627  * Portal vnode unsupported operation
628  */
629 portal_enotsupp()
630 {
631 
632 	return (EOPNOTSUPP);
633 }
634 
635 /*
636  * Portal "should never get here" operation
637  */
638 portal_badop()
639 {
640 
641 	panic("portal: bad op");
642 	/* NOTREACHED */
643 }
644 
645 /*
646  * Portal vnode null operation
647  */
648 portal_nullop()
649 {
650 
651 	return (0);
652 }
653 
654 #define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
655 #define portal_mknod ((int (*) __P((struct  vop_mknod_args *)))portal_enotsupp)
656 #define portal_close ((int (*) __P((struct  vop_close_args *)))nullop)
657 #define portal_access ((int (*) __P((struct  vop_access_args *)))nullop)
658 #define portal_read ((int (*) __P((struct  vop_read_args *)))portal_enotsupp)
659 #define portal_write ((int (*) __P((struct  vop_write_args *)))portal_enotsupp)
660 #define portal_ioctl ((int (*) __P((struct  vop_ioctl_args *)))portal_enotsupp)
661 #define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
662 #define portal_mmap ((int (*) __P((struct  vop_mmap_args *)))portal_enotsupp)
663 #define portal_fsync ((int (*) __P((struct  vop_fsync_args *)))nullop)
664 #define portal_seek ((int (*) __P((struct  vop_seek_args *)))nullop)
665 #define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
666 #define portal_link ((int (*) __P((struct  vop_link_args *)))portal_enotsupp)
667 #define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
668 #define portal_mkdir ((int (*) __P((struct  vop_mkdir_args *)))portal_enotsupp)
669 #define portal_rmdir ((int (*) __P((struct  vop_rmdir_args *)))portal_enotsupp)
670 #define portal_symlink \
671 	((int (*) __P((struct  vop_symlink_args *)))portal_enotsupp)
672 #define portal_readlink \
673 	((int (*) __P((struct  vop_readlink_args *)))portal_enotsupp)
674 #define portal_abortop ((int (*) __P((struct  vop_abortop_args *)))nullop)
675 #define portal_lock ((int (*) __P((struct  vop_lock_args *)))nullop)
676 #define portal_unlock ((int (*) __P((struct  vop_unlock_args *)))nullop)
677 #define portal_bmap ((int (*) __P((struct  vop_bmap_args *)))portal_badop)
678 #define portal_strategy \
679 	((int (*) __P((struct  vop_strategy_args *)))portal_badop)
680 #define portal_islocked ((int (*) __P((struct  vop_islocked_args *)))nullop)
681 #define portal_advlock \
682 	((int (*) __P((struct  vop_advlock_args *)))portal_enotsupp)
683 #define portal_blkatoff \
684 	((int (*) __P((struct  vop_blkatoff_args *)))portal_enotsupp)
685 #define portal_valloc ((int(*) __P(( \
686 		struct vnode *pvp, \
687 		int mode, \
688 		struct ucred *cred, \
689 		struct vnode **vpp))) portal_enotsupp)
690 #define portal_truncate \
691 	((int (*) __P((struct  vop_truncate_args *)))portal_enotsupp)
692 #define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
693 #define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
694 
695 int (**portal_vnodeop_p)();
696 struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
697 	{ &vop_default_desc, vn_default_error },
698 	{ &vop_lookup_desc, portal_lookup },		/* lookup */
699 	{ &vop_create_desc, portal_create },		/* create */
700 	{ &vop_mknod_desc, portal_mknod },		/* mknod */
701 	{ &vop_open_desc, portal_open },		/* open */
702 	{ &vop_close_desc, portal_close },		/* close */
703 	{ &vop_access_desc, portal_access },		/* access */
704 	{ &vop_getattr_desc, portal_getattr },		/* getattr */
705 	{ &vop_setattr_desc, portal_setattr },		/* setattr */
706 	{ &vop_read_desc, portal_read },		/* read */
707 	{ &vop_write_desc, portal_write },		/* write */
708 	{ &vop_ioctl_desc, portal_ioctl },		/* ioctl */
709 	{ &vop_select_desc, portal_select },		/* select */
710 	{ &vop_mmap_desc, portal_mmap },		/* mmap */
711 	{ &vop_fsync_desc, portal_fsync },		/* fsync */
712 	{ &vop_seek_desc, portal_seek },		/* seek */
713 	{ &vop_remove_desc, portal_remove },		/* remove */
714 	{ &vop_link_desc, portal_link },		/* link */
715 	{ &vop_rename_desc, portal_rename },		/* rename */
716 	{ &vop_mkdir_desc, portal_mkdir },		/* mkdir */
717 	{ &vop_rmdir_desc, portal_rmdir },		/* rmdir */
718 	{ &vop_symlink_desc, portal_symlink },		/* symlink */
719 	{ &vop_readdir_desc, portal_readdir },		/* readdir */
720 	{ &vop_readlink_desc, portal_readlink },	/* readlink */
721 	{ &vop_abortop_desc, portal_abortop },		/* abortop */
722 	{ &vop_inactive_desc, portal_inactive },	/* inactive */
723 	{ &vop_reclaim_desc, portal_reclaim },		/* reclaim */
724 	{ &vop_lock_desc, portal_lock },		/* lock */
725 	{ &vop_unlock_desc, portal_unlock },		/* unlock */
726 	{ &vop_bmap_desc, portal_bmap },		/* bmap */
727 	{ &vop_strategy_desc, portal_strategy },	/* strategy */
728 	{ &vop_print_desc, portal_print },		/* print */
729 	{ &vop_islocked_desc, portal_islocked },	/* islocked */
730 	{ &vop_advlock_desc, portal_advlock },		/* advlock */
731 	{ &vop_blkatoff_desc, portal_blkatoff },	/* blkatoff */
732 	{ &vop_valloc_desc, portal_valloc },		/* valloc */
733 	{ &vop_vfree_desc, portal_vfree },		/* vfree */
734 	{ &vop_truncate_desc, portal_truncate },	/* truncate */
735 	{ &vop_update_desc, portal_update },		/* update */
736 	{ &vop_bwrite_desc, portal_bwrite },		/* bwrite */
737 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
738 };
739 struct vnodeopv_desc portal_vnodeop_opv_desc =
740 	{ &portal_vnodeop_p, portal_vnodeop_entries };
741