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