1 /*
2  * Copyright (c) 1992 The Regents of the University of California
3  * Copyright (c) 1990, 1992 Jan-Simon Pendry
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	7.2 (Berkeley) 07/13/92
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 	int s;
194 	struct uio auio;
195 	struct iovec aiov[2];
196 	int res;
197 	struct mbuf *cm = 0;
198 	struct cmsghdr *cmsg;
199 	int newfds;
200 	int *ip;
201 	int fd;
202 	int error;
203 	int len;
204 	struct portalmount *fmp;
205 	struct file *fp;
206 	struct portal_cred pcred;
207 
208 	/*
209 	 * Nothing to do when opening the root node.
210 	 */
211 	if (ap->a_vp->v_flag & VROOT)
212 		return (0);
213 
214 #ifdef PORTAL_DIAGNOSTIC
215 	printf("portal_open(%x)\n", ap->a_vp);
216 #endif
217 
218 	/*
219 	 * Can't be opened unless the caller is set up
220 	 * to deal with the side effects.  Check for this
221 	 * by testing whether the p_dupfd has been set.
222 	 */
223 	if (ap->a_p->p_dupfd >= 0)
224 		return (ENODEV);
225 
226 	pt = VTOPORTAL(ap->a_vp);
227 	fmp = VFSTOPORTAL(ap->a_vp->v_mount);
228 
229 	/*
230 	 * Create a new socket.
231 	 */
232 	error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
233 	if (error)
234 		goto bad;
235 
236 	/*
237 	 * Reserve some buffer space
238 	 */
239 #ifdef PORTAL_DIAGNOSTIC
240 	printf("portal_open: calling soreserve\n");
241 #endif
242 	res = pt->pt_size + sizeof(pcred) + 512;	/* XXX */
243 	error = soreserve(so, res, res);
244 	if (error)
245 		goto bad;
246 
247 	/*
248 	 * Kick off connection
249 	 */
250 #ifdef PORTAL_DIAGNOSTIC
251 	printf("portal_open: calling portal_connect\n");
252 #endif
253 	error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
254 	if (error)
255 		goto bad;
256 
257 	/*
258 	 * Wait for connection to complete
259 	 */
260 #ifdef PORTAL_DIAGNOSTIC
261 	printf("portal_open: waiting for connect\n");
262 #endif
263 	/*
264 	 * XXX: Since the mount point is holding a reference on the
265 	 * underlying server socket, it is not easy to find out whether
266 	 * the server process is still running.  To handle this problem
267 	 * we loop waiting for the new socket to be connected (something
268 	 * which will only happen if the server is still running) or for
269 	 * the reference count on the server socket to drop to 1, which
270 	 * will happen if the server dies.  Sleep for 5 second intervals
271 	 * and keep polling the reference count.   XXX.
272 	 */
273 	s = splnet();
274 	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
275 		if (fmp->pm_server->f_count == 1) {
276 			error = ECONNREFUSED;
277 			splx(s);
278 #ifdef PORTAL_DIAGNOSTIC
279 			printf("portal_open: server process has gone away\n");
280 #endif
281 			goto bad;
282 		}
283 		(void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
284 	}
285 	splx(s);
286 
287 	if (so->so_error) {
288 		error = so->so_error;
289 		goto bad;
290 	}
291 
292 	/*
293 	 * Set miscellaneous flags
294 	 */
295 	so->so_rcv.sb_timeo = 0;
296 	so->so_snd.sb_timeo = 0;
297 	so->so_rcv.sb_flags |= SB_NOINTR;
298 	so->so_snd.sb_flags |= SB_NOINTR;
299 
300 #ifdef PORTAL_DIAGNOSTIC
301 	printf("portal_open: constructing data uio\n");
302 #endif
303 
304 	pcred.pcr_flag = ap->a_mode;
305 	pcred.pcr_uid = ap->a_cred->cr_uid;
306 	pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
307 	bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
308 	aiov[0].iov_base = (caddr_t) &pcred;
309 	aiov[0].iov_len = sizeof(pcred);
310 	aiov[1].iov_base = pt->pt_arg;
311 	aiov[1].iov_len = pt->pt_size;
312 	auio.uio_iov = aiov;
313 	auio.uio_iovcnt = 2;
314 	auio.uio_rw = UIO_WRITE;
315 	auio.uio_segflg = UIO_SYSSPACE;
316 	auio.uio_procp = ap->a_p;
317 	auio.uio_offset = 0;
318 	auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
319 
320 #ifdef PORTAL_DIAGNOSTIC
321 	printf("portal_open: sending data to server\n");
322 #endif
323 	error = sosend(so, (struct sockaddr *) 0, &auio,
324 			(struct mbuf *) 0, (struct mbuf *) 0, 0);
325 	if (error)
326 		goto bad;
327 
328 	len = auio.uio_resid = sizeof(int);
329 	do {
330 		struct mbuf *m = 0;
331 		int flags = MSG_WAITALL;
332 #ifdef PORTAL_DIAGNOSTIC
333 		printf("portal_open: receiving data from server\n");
334 		printf("portal_open: so = %x, cm = %x, resid = %d\n",
335 				so, cm, auio.uio_resid);
336 		printf("portal_open, uio=%x, mp0=%x, controlp=%x\n", &auio, &cm);
337 #endif
338 		error = soreceive(so, (struct mbuf **) 0, &auio,
339 					&m, &cm, &flags);
340 #ifdef PORTAL_DIAGNOSTIC
341 		printf("portal_open: after receiving data\n");
342 		printf("portal_open: so = %x, cm = %x, resid = %d\n",
343 				so, cm, auio.uio_resid);
344 #endif
345 		if (error)
346 			goto bad;
347 
348 		/*
349 		 * Grab an error code from the mbuf.
350 		 */
351 		if (m) {
352 			m = m_pullup(m, sizeof(int));	/* Needed? */
353 			if (m) {
354 				error = *(mtod(m, int *));
355 				m_freem(m);
356 			} else {
357 				error = EINVAL;
358 			}
359 #ifdef PORTAL_DIAGNOSTIC
360 			printf("portal_open: error returned is %d\n", error);
361 #endif
362 		} else {
363 			if (cm == 0) {
364 #ifdef PORTAL_DIAGNOSTIC
365 				printf("portal_open: no rights received\n");
366 #endif
367 				error = ECONNRESET;	 /* XXX */
368 #ifdef notdef
369 				break;
370 #endif
371 			}
372 		}
373 	} while (cm == 0 && auio.uio_resid == len && !error);
374 
375 	if (cm == 0)
376 		goto bad;
377 
378 	if (auio.uio_resid) {
379 #ifdef PORTAL_DIAGNOSTIC
380 		printf("portal_open: still need another %d bytes\n", auio.uio_resid);
381 #endif
382 		error = 0;
383 #ifdef notdef
384 		error = EMSGSIZE;
385 		goto bad;
386 #endif
387 	}
388 
389 	/*
390 	 * XXX: Break apart the control message, and retrieve the
391 	 * received file descriptor.  Note that more than one descriptor
392 	 * may have been received, or that the rights chain may have more
393 	 * than a single mbuf in it.  What to do?
394 	 */
395 #ifdef PORTAL_DIAGNOSTIC
396 	printf("portal_open: about to break apart control message\n");
397 #endif
398 	cmsg = mtod(cm, struct cmsghdr *);
399 	newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
400 	if (newfds == 0) {
401 #ifdef PORTAL_DIAGNOSTIC
402 		printf("portal_open: received no fds\n");
403 #endif
404 		error = ECONNREFUSED;
405 		goto bad;
406 	}
407 	/*
408 	 * At this point the rights message consists of a control message
409 	 * header, followed by a data region containing a vector of
410 	 * integer file descriptors.  The fds were allocated by the action
411 	 * of receiving the control message.
412 	 */
413 	ip = (int *) (cmsg + 1);
414 	fd = *ip++;
415 	if (newfds > 1) {
416 		/*
417 		 * Close extra fds.
418 		 */
419 		int i;
420 		printf("portal_open: %d extra fds\n", newfds - 1);
421 		for (i = 1; i < newfds; i++) {
422 			portal_closefd(ap->a_p, *ip);
423 			ip++;
424 		}
425 	}
426 
427 	/*
428 	 * Check that the ap->a_mode the file is being opened for is a subset
429 	 * of the ap->a_mode of the existing descriptor.
430 	 */
431 #ifdef PORTAL_DIAGNOSTIC
432 	printf("portal_open: checking file flags, fd = %d\n", fd);
433 #endif
434  	fp = ap->a_p->p_fd->fd_ofiles[fd];
435 	if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
436 		portal_closefd(ap->a_p, fd);
437 		error = EACCES;
438 		goto bad;
439 	}
440 
441 #ifdef PORTAL_DIAGNOSTIC
442 	printf("portal_open: got fd = %d\n", fd);
443 #endif
444 	/*
445 	 * Save the dup fd in the proc structure then return the
446 	 * special error code (ENXIO) which causes magic things to
447 	 * happen in vn_open.  The whole concept is, well, hmmm.
448 	 */
449 	ap->a_p->p_dupfd = fd;
450 	error = ENXIO;
451 
452 bad:;
453 	/*
454 	 * And discard the control message.
455 	 */
456 	if (cm) {
457 #ifdef PORTAL_DIAGNOSTIC
458 		printf("portal_open: free'ing control message\n");
459 #endif
460 		m_freem(cm);
461 	}
462 
463 	if (so) {
464 #ifdef PORTAL_DIAGNOSTIC
465 		printf("portal_open: calling soshutdown\n");
466 #endif
467 		soshutdown(so, 2);
468 #ifdef PORTAL_DIAGNOSTIC
469 		printf("portal_open: calling soclose\n");
470 #endif
471 		soclose(so);
472 	}
473 #ifdef PORTAL_DIAGNOSTIC
474 	if (error != ENODEV)
475 		printf("portal_open: error = %d\n", error);
476 #endif
477 	return (error);
478 }
479 
480 portal_getattr(ap)
481 	struct vop_getattr_args /* {
482 		struct vnode *a_vp;
483 		struct vattr *a_vap;
484 		struct ucred *a_cred;
485 		struct proc *a_p;
486 	} */ *ap;
487 {
488 	unsigned fd;
489 	int error;
490 
491 	bzero((caddr_t) ap->a_vap, sizeof(*ap->a_vap));
492 	vattr_null(ap->a_vap);
493 	ap->a_vap->va_uid = 0;
494 	ap->a_vap->va_gid = 0;
495 	ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
496 	ap->a_vap->va_size = DEV_BSIZE;
497 	ap->a_vap->va_blocksize = DEV_BSIZE;
498 	microtime(&ap->a_vap->va_atime);
499 	ap->a_vap->va_mtime = ap->a_vap->va_atime;
500 	ap->a_vap->va_ctime = ap->a_vap->va_ctime;
501 	ap->a_vap->va_gen = 0;
502 	ap->a_vap->va_flags = 0;
503 	ap->a_vap->va_rdev = 0;
504 	/* ap->a_vap->va_qbytes = 0; */
505 	ap->a_vap->va_bytes = 0;
506 	/* ap->a_vap->va_qsize = 0; */
507 	if (ap->a_vp->v_flag & VROOT) {
508 #ifdef PORTAL_DIAGNOSTIC
509 		printf("portal_getattr: stat rootdir\n");
510 #endif
511 		ap->a_vap->va_type = VDIR;
512 		ap->a_vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
513 				S_IRGRP|S_IWGRP|S_IXGRP|
514 				S_IROTH|S_IWOTH|S_IXOTH;
515 		ap->a_vap->va_nlink = 2;
516 		ap->a_vap->va_fileid = 2;
517 	} else {
518 #ifdef PORTAL_DIAGNOSTIC
519 		printf("portal_getattr: stat portal\n");
520 #endif
521 		ap->a_vap->va_type = VREG;
522 		ap->a_vap->va_mode = S_IRUSR|S_IWUSR|
523 				S_IRGRP|S_IWGRP|
524 				S_IROTH|S_IWOTH;
525 		ap->a_vap->va_nlink = 1;
526 		ap->a_vap->va_fileid = VTOPORTAL(ap->a_vp)->pt_fileid;
527 	}
528 	return (0);
529 }
530 
531 portal_setattr(ap)
532 	struct vop_setattr_args /* {
533 		struct vnode *a_vp;
534 		struct vattr *a_vap;
535 		struct ucred *a_cred;
536 		struct proc *a_p;
537 	} */ *ap;
538 {
539 	/*
540 	 * Can't mess with the root vnode
541 	 */
542 	if (ap->a_vp->v_flag & VROOT)
543 		return (EACCES);
544 
545 	return (0);
546 }
547 
548 /*
549  * Fake readdir, just return empty directory.
550  * It is hard to deal with '.' and '..' so don't bother.
551  */
552 portal_readdir(ap)
553 	struct vop_readdir_args /* {
554 		struct vnode *a_vp;
555 		struct uio *a_uio;
556 		struct ucred *a_cred;
557 	} */ *ap;
558 {
559 	/* *ap->a_eofflagp = 1; */
560 	return (0);
561 }
562 
563 portal_inactive(ap)
564 	struct vop_inactive_args /* {
565 		struct vnode *a_vp;
566 	} */ *ap;
567 {
568 #ifdef PORTAL_DIAGNOSTIC
569 	if (VTOPORTAL(ap->a_vp)->pt_arg)
570 		printf("portal_inactive(%x, %s)\n", ap->a_vp, VTOPORTAL(ap->a_vp)->pt_arg);
571 	else
572 		printf("portal_inactive(%x)\n", ap->a_vp);
573 #endif
574 	/*vgone(ap->a_vp);*/
575 	return (0);
576 }
577 
578 portal_reclaim(ap)
579 	struct vop_reclaim_args /* {
580 		struct vnode *a_vp;
581 	} */ *ap;
582 {
583 	struct portalnode *pt = VTOPORTAL(ap->a_vp);
584 #ifdef PORTAL_DIAGOISTIC
585 	printf("portal_reclaim(%x)\n", ap->a_vp);
586 #endif
587 	if (pt->pt_arg) {
588 		free((caddr_t) pt->pt_arg, M_TEMP);
589 		pt->pt_arg = 0;
590 	}
591 	FREE(pt, M_TEMP);
592 	return (0);
593 }
594 
595 /*
596  * Print out the contents of a Portal vnode.
597  */
598 /* ARGSUSED */
599 portal_print(ap)
600 	struct vop_print_args /* {
601 		struct vnode *a_vp;
602 	} */ *ap;
603 {
604 
605 	printf("tag VT_PORTAL, portal vnode\n");
606 	return (0);
607 }
608 
609 /*void*/
610 portal_vfree(ap)
611 	struct vop_vfree_args /* {
612 		struct vnode *a_pvp;
613 		ino_t a_ino;
614 		int a_mode;
615 	} */ *ap;
616 {
617 
618 	return (0);
619 }
620 
621 
622 /*
623  * Portal vnode unsupported operation
624  */
625 portal_enotsupp()
626 {
627 
628 	return (EOPNOTSUPP);
629 }
630 
631 /*
632  * Portal "should never get here" operation
633  */
634 portal_badop()
635 {
636 
637 	panic("portal: bad op");
638 	/* NOTREACHED */
639 }
640 
641 /*
642  * Portal vnode null operation
643  */
644 portal_nullop()
645 {
646 
647 	return (0);
648 }
649 
650 #define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
651 #define portal_mknod ((int (*) __P((struct  vop_mknod_args *)))portal_enotsupp)
652 #define portal_close ((int (*) __P((struct  vop_close_args *)))nullop)
653 #define portal_access ((int (*) __P((struct  vop_access_args *)))nullop)
654 #define portal_read ((int (*) __P((struct  vop_read_args *)))portal_enotsupp)
655 #define portal_write ((int (*) __P((struct  vop_write_args *)))portal_enotsupp)
656 #define portal_ioctl ((int (*) __P((struct  vop_ioctl_args *)))portal_enotsupp)
657 #define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
658 #define portal_mmap ((int (*) __P((struct  vop_mmap_args *)))portal_enotsupp)
659 #define portal_fsync ((int (*) __P((struct  vop_fsync_args *)))nullop)
660 #define portal_seek ((int (*) __P((struct  vop_seek_args *)))nullop)
661 #define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
662 #define portal_link ((int (*) __P((struct  vop_link_args *)))portal_enotsupp)
663 #define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
664 #define portal_mkdir ((int (*) __P((struct  vop_mkdir_args *)))portal_enotsupp)
665 #define portal_rmdir ((int (*) __P((struct  vop_rmdir_args *)))portal_enotsupp)
666 #define portal_symlink \
667 	((int (*) __P((struct  vop_symlink_args *)))portal_enotsupp)
668 #define portal_readlink \
669 	((int (*) __P((struct  vop_readlink_args *)))portal_enotsupp)
670 #define portal_abortop ((int (*) __P((struct  vop_abortop_args *)))nullop)
671 #define portal_lock ((int (*) __P((struct  vop_lock_args *)))nullop)
672 #define portal_unlock ((int (*) __P((struct  vop_unlock_args *)))nullop)
673 #define portal_bmap ((int (*) __P((struct  vop_bmap_args *)))portal_badop)
674 #define portal_strategy \
675 	((int (*) __P((struct  vop_strategy_args *)))portal_badop)
676 #define portal_islocked ((int (*) __P((struct  vop_islocked_args *)))nullop)
677 #define portal_advlock \
678 	((int (*) __P((struct  vop_advlock_args *)))portal_enotsupp)
679 #define portal_blkatoff \
680 	((int (*) __P((struct  vop_blkatoff_args *)))portal_enotsupp)
681 #define portal_valloc ((int(*) __P(( \
682 		struct vnode *pvp, \
683 		int mode, \
684 		struct ucred *cred, \
685 		struct vnode **vpp))) portal_enotsupp)
686 #define portal_truncate \
687 	((int (*) __P((struct  vop_truncate_args *)))portal_enotsupp)
688 #define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
689 #define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
690 
691 int (**portal_vnodeop_p)();
692 struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
693 	{ &vop_default_desc, vn_default_error },
694 	{ &vop_lookup_desc, portal_lookup },		/* lookup */
695 	{ &vop_create_desc, portal_create },		/* create */
696 	{ &vop_mknod_desc, portal_mknod },		/* mknod */
697 	{ &vop_open_desc, portal_open },		/* open */
698 	{ &vop_close_desc, portal_close },		/* close */
699 	{ &vop_access_desc, portal_access },		/* access */
700 	{ &vop_getattr_desc, portal_getattr },		/* getattr */
701 	{ &vop_setattr_desc, portal_setattr },		/* setattr */
702 	{ &vop_read_desc, portal_read },		/* read */
703 	{ &vop_write_desc, portal_write },		/* write */
704 	{ &vop_ioctl_desc, portal_ioctl },		/* ioctl */
705 	{ &vop_select_desc, portal_select },		/* select */
706 	{ &vop_mmap_desc, portal_mmap },		/* mmap */
707 	{ &vop_fsync_desc, portal_fsync },		/* fsync */
708 	{ &vop_seek_desc, portal_seek },		/* seek */
709 	{ &vop_remove_desc, portal_remove },		/* remove */
710 	{ &vop_link_desc, portal_link },		/* link */
711 	{ &vop_rename_desc, portal_rename },		/* rename */
712 	{ &vop_mkdir_desc, portal_mkdir },		/* mkdir */
713 	{ &vop_rmdir_desc, portal_rmdir },		/* rmdir */
714 	{ &vop_symlink_desc, portal_symlink },		/* symlink */
715 	{ &vop_readdir_desc, portal_readdir },		/* readdir */
716 	{ &vop_readlink_desc, portal_readlink },	/* readlink */
717 	{ &vop_abortop_desc, portal_abortop },		/* abortop */
718 	{ &vop_inactive_desc, portal_inactive },	/* inactive */
719 	{ &vop_reclaim_desc, portal_reclaim },		/* reclaim */
720 	{ &vop_lock_desc, portal_lock },		/* lock */
721 	{ &vop_unlock_desc, portal_unlock },		/* unlock */
722 	{ &vop_bmap_desc, portal_bmap },		/* bmap */
723 	{ &vop_strategy_desc, portal_strategy },	/* strategy */
724 	{ &vop_print_desc, portal_print },		/* print */
725 	{ &vop_islocked_desc, portal_islocked },	/* islocked */
726 	{ &vop_advlock_desc, portal_advlock },		/* advlock */
727 	{ &vop_blkatoff_desc, portal_blkatoff },	/* blkatoff */
728 	{ &vop_valloc_desc, portal_valloc },		/* valloc */
729 	{ &vop_vfree_desc, portal_vfree },		/* vfree */
730 	{ &vop_truncate_desc, portal_truncate },	/* truncate */
731 	{ &vop_update_desc, portal_update },		/* update */
732 	{ &vop_bwrite_desc, portal_bwrite },		/* bwrite */
733 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
734 };
735 struct vnodeopv_desc portal_vnodeop_opv_desc =
736 	{ &portal_vnodeop_p, portal_vnodeop_entries };
737