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