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.11 (Berkeley) 04/03/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 	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 	struct timeval tv;
401 
402 	bzero(vap, sizeof(*vap));
403 	vattr_null(vap);
404 	vap->va_uid = 0;
405 	vap->va_gid = 0;
406 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
407 	vap->va_size = DEV_BSIZE;
408 	vap->va_blocksize = DEV_BSIZE;
409 	microtime(&tv);
410 	TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime);
411 	vap->va_mtime = vap->va_atime;
412 	vap->va_ctime = vap->va_ctime;
413 	vap->va_gen = 0;
414 	vap->va_flags = 0;
415 	vap->va_rdev = 0;
416 	/* vap->va_qbytes = 0; */
417 	vap->va_bytes = 0;
418 	/* vap->va_qsize = 0; */
419 	if (vp->v_flag & VROOT) {
420 		vap->va_type = VDIR;
421 		vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
422 				S_IRGRP|S_IWGRP|S_IXGRP|
423 				S_IROTH|S_IWOTH|S_IXOTH;
424 		vap->va_nlink = 2;
425 		vap->va_fileid = 2;
426 	} else {
427 		vap->va_type = VREG;
428 		vap->va_mode = S_IRUSR|S_IWUSR|
429 				S_IRGRP|S_IWGRP|
430 				S_IROTH|S_IWOTH;
431 		vap->va_nlink = 1;
432 		vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
433 	}
434 	return (0);
435 }
436 
437 int
438 portal_setattr(ap)
439 	struct vop_setattr_args /* {
440 		struct vnode *a_vp;
441 		struct vattr *a_vap;
442 		struct ucred *a_cred;
443 		struct proc *a_p;
444 	} */ *ap;
445 {
446 
447 	/*
448 	 * Can't mess with the root vnode
449 	 */
450 	if (ap->a_vp->v_flag & VROOT)
451 		return (EACCES);
452 
453 	return (0);
454 }
455 
456 /*
457  * Fake readdir, just return empty directory.
458  * It is hard to deal with '.' and '..' so don't bother.
459  */
460 int
461 portal_readdir(ap)
462 	struct vop_readdir_args /* {
463 		struct vnode *a_vp;
464 		struct uio *a_uio;
465 		struct ucred *a_cred;
466 		int *a_eofflag;
467 		u_long *a_cookies;
468 		int a_ncookies;
469 	} */ *ap;
470 {
471 
472 	/*
473 	 * We don't allow exporting portal mounts, and currently local
474 	 * requests do not need cookies.
475 	 */
476 	if (ap->a_ncookies)
477 		panic("portal_readdir: not hungry");
478 
479 	return (0);
480 }
481 
482 int
483 portal_inactive(ap)
484 	struct vop_inactive_args /* {
485 		struct vnode *a_vp;
486 	} */ *ap;
487 {
488 
489 	return (0);
490 }
491 
492 int
493 portal_reclaim(ap)
494 	struct vop_reclaim_args /* {
495 		struct vnode *a_vp;
496 	} */ *ap;
497 {
498 	struct portalnode *pt = VTOPORTAL(ap->a_vp);
499 
500 	if (pt->pt_arg) {
501 		free((caddr_t) pt->pt_arg, M_TEMP);
502 		pt->pt_arg = 0;
503 	}
504 	FREE(ap->a_vp->v_data, M_TEMP);
505 	ap->a_vp->v_data = 0;
506 
507 	return (0);
508 }
509 
510 /*
511  * Return POSIX pathconf information applicable to special devices.
512  */
513 portal_pathconf(ap)
514 	struct vop_pathconf_args /* {
515 		struct vnode *a_vp;
516 		int a_name;
517 		int *a_retval;
518 	} */ *ap;
519 {
520 
521 	switch (ap->a_name) {
522 	case _PC_LINK_MAX:
523 		*ap->a_retval = LINK_MAX;
524 		return (0);
525 	case _PC_MAX_CANON:
526 		*ap->a_retval = MAX_CANON;
527 		return (0);
528 	case _PC_MAX_INPUT:
529 		*ap->a_retval = MAX_INPUT;
530 		return (0);
531 	case _PC_PIPE_BUF:
532 		*ap->a_retval = PIPE_BUF;
533 		return (0);
534 	case _PC_CHOWN_RESTRICTED:
535 		*ap->a_retval = 1;
536 		return (0);
537 	case _PC_VDISABLE:
538 		*ap->a_retval = _POSIX_VDISABLE;
539 		return (0);
540 	default:
541 		return (EINVAL);
542 	}
543 	/* NOTREACHED */
544 }
545 
546 /*
547  * Print out the contents of a Portal vnode.
548  */
549 /* ARGSUSED */
550 int
551 portal_print(ap)
552 	struct vop_print_args /* {
553 		struct vnode *a_vp;
554 	} */ *ap;
555 {
556 
557 	printf("tag VT_PORTAL, portal vnode\n");
558 	return (0);
559 }
560 
561 /*void*/
562 int
563 portal_vfree(ap)
564 	struct vop_vfree_args /* {
565 		struct vnode *a_pvp;
566 		ino_t a_ino;
567 		int a_mode;
568 	} */ *ap;
569 {
570 
571 	return (0);
572 }
573 
574 
575 /*
576  * Portal vnode unsupported operation
577  */
578 int
579 portal_enotsupp()
580 {
581 
582 	return (EOPNOTSUPP);
583 }
584 
585 /*
586  * Portal "should never get here" operation
587  */
588 int
589 portal_badop()
590 {
591 
592 	panic("portal: bad op");
593 	/* NOTREACHED */
594 }
595 
596 /*
597  * Portal vnode null operation
598  */
599 int
600 portal_nullop()
601 {
602 
603 	return (0);
604 }
605 
606 #define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
607 #define portal_mknod ((int (*) __P((struct  vop_mknod_args *)))portal_enotsupp)
608 #define portal_close ((int (*) __P((struct  vop_close_args *)))nullop)
609 #define portal_access ((int (*) __P((struct  vop_access_args *)))nullop)
610 #define portal_read ((int (*) __P((struct  vop_read_args *)))portal_enotsupp)
611 #define portal_write ((int (*) __P((struct  vop_write_args *)))portal_enotsupp)
612 #define portal_ioctl ((int (*) __P((struct  vop_ioctl_args *)))portal_enotsupp)
613 #define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
614 #define portal_mmap ((int (*) __P((struct  vop_mmap_args *)))portal_enotsupp)
615 #define	portal_revoke vop_revoke
616 #define portal_fsync ((int (*) __P((struct  vop_fsync_args *)))nullop)
617 #define portal_seek ((int (*) __P((struct  vop_seek_args *)))nullop)
618 #define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
619 #define portal_link ((int (*) __P((struct  vop_link_args *)))portal_enotsupp)
620 #define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
621 #define portal_mkdir ((int (*) __P((struct  vop_mkdir_args *)))portal_enotsupp)
622 #define portal_rmdir ((int (*) __P((struct  vop_rmdir_args *)))portal_enotsupp)
623 #define portal_symlink \
624 	((int (*) __P((struct  vop_symlink_args *)))portal_enotsupp)
625 #define portal_readlink \
626 	((int (*) __P((struct  vop_readlink_args *)))portal_enotsupp)
627 #define portal_abortop ((int (*) __P((struct  vop_abortop_args *)))nullop)
628 #define portal_lock ((int (*) __P((struct  vop_lock_args *)))nullop)
629 #define portal_unlock ((int (*) __P((struct  vop_unlock_args *)))nullop)
630 #define portal_bmap ((int (*) __P((struct  vop_bmap_args *)))portal_badop)
631 #define portal_strategy \
632 	((int (*) __P((struct  vop_strategy_args *)))portal_badop)
633 #define portal_islocked ((int (*) __P((struct  vop_islocked_args *)))nullop)
634 #define portal_advlock \
635 	((int (*) __P((struct  vop_advlock_args *)))portal_enotsupp)
636 #define portal_blkatoff \
637 	((int (*) __P((struct  vop_blkatoff_args *)))portal_enotsupp)
638 #define portal_valloc ((int(*) __P(( \
639 		struct vnode *pvp, \
640 		int mode, \
641 		struct ucred *cred, \
642 		struct vnode **vpp))) portal_enotsupp)
643 #define portal_truncate \
644 	((int (*) __P((struct  vop_truncate_args *)))portal_enotsupp)
645 #define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
646 #define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
647 
648 int (**portal_vnodeop_p)();
649 struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
650 	{ &vop_default_desc, vn_default_error },
651 	{ &vop_lookup_desc, portal_lookup },		/* lookup */
652 	{ &vop_create_desc, portal_create },		/* create */
653 	{ &vop_mknod_desc, portal_mknod },		/* mknod */
654 	{ &vop_open_desc, portal_open },		/* open */
655 	{ &vop_close_desc, portal_close },		/* close */
656 	{ &vop_access_desc, portal_access },		/* access */
657 	{ &vop_getattr_desc, portal_getattr },		/* getattr */
658 	{ &vop_setattr_desc, portal_setattr },		/* setattr */
659 	{ &vop_read_desc, portal_read },		/* read */
660 	{ &vop_write_desc, portal_write },		/* write */
661 	{ &vop_ioctl_desc, portal_ioctl },		/* ioctl */
662 	{ &vop_select_desc, portal_select },		/* select */
663 	{ &vop_mmap_desc, portal_mmap },		/* mmap */
664 	{ &vop_revoke_desc, portal_revoke },		/* revoke */
665 	{ &vop_fsync_desc, portal_fsync },		/* fsync */
666 	{ &vop_seek_desc, portal_seek },		/* seek */
667 	{ &vop_remove_desc, portal_remove },		/* remove */
668 	{ &vop_link_desc, portal_link },		/* link */
669 	{ &vop_rename_desc, portal_rename },		/* rename */
670 	{ &vop_mkdir_desc, portal_mkdir },		/* mkdir */
671 	{ &vop_rmdir_desc, portal_rmdir },		/* rmdir */
672 	{ &vop_symlink_desc, portal_symlink },		/* symlink */
673 	{ &vop_readdir_desc, portal_readdir },		/* readdir */
674 	{ &vop_readlink_desc, portal_readlink },	/* readlink */
675 	{ &vop_abortop_desc, portal_abortop },		/* abortop */
676 	{ &vop_inactive_desc, portal_inactive },	/* inactive */
677 	{ &vop_reclaim_desc, portal_reclaim },		/* reclaim */
678 	{ &vop_lock_desc, portal_lock },		/* lock */
679 	{ &vop_unlock_desc, portal_unlock },		/* unlock */
680 	{ &vop_bmap_desc, portal_bmap },		/* bmap */
681 	{ &vop_strategy_desc, portal_strategy },	/* strategy */
682 	{ &vop_print_desc, portal_print },		/* print */
683 	{ &vop_islocked_desc, portal_islocked },	/* islocked */
684 	{ &vop_pathconf_desc, portal_pathconf },	/* pathconf */
685 	{ &vop_advlock_desc, portal_advlock },		/* advlock */
686 	{ &vop_blkatoff_desc, portal_blkatoff },	/* blkatoff */
687 	{ &vop_valloc_desc, portal_valloc },		/* valloc */
688 	{ &vop_vfree_desc, portal_vfree },		/* vfree */
689 	{ &vop_truncate_desc, portal_truncate },	/* truncate */
690 	{ &vop_update_desc, portal_update },		/* update */
691 	{ &vop_bwrite_desc, portal_bwrite },		/* bwrite */
692 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
693 };
694 struct vnodeopv_desc portal_vnodeop_opv_desc =
695 	{ &portal_vnodeop_p, portal_vnodeop_entries };
696