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