xref: /freebsd/sys/fs/unionfs/union_vnops.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
1 /*
2  * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
3  * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)union_vnops.c	8.6 (Berkeley) 2/17/94
38  * $Id: union_vnops.c,v 1.7 1994/10/10 07:55:48 phk Exp $
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/proc.h>
44 #include <sys/file.h>
45 #include <sys/time.h>
46 #include <sys/kernel.h>
47 #include <sys/types.h>
48 #include <sys/vnode.h>
49 #include <sys/mount.h>
50 #include <sys/namei.h>
51 #include <sys/malloc.h>
52 #include <sys/buf.h>
53 #include <sys/queue.h>
54 #include <miscfs/union/union.h>
55 
56 /* FIXUP throws the lock on the uppervp vnode if the union_node is already
57  * locked and the uppervp vnode is not.  Before, this was thrown regardless
58  * of the state of the union_node which resulted in locked vnodes which
59  * were never unlocked (since the union would never be unlocked).
60  */
61 #define FIXUP(un) { \
62 	if (((un)->un_flags & (UN_LOCKED|UN_ULOCK)) == UN_LOCKED) { \
63 		union_fixup(un); \
64 	} \
65 }
66 
67 static void
68 union_fixup(un)
69 	struct union_node *un;
70 {
71 
72 	VOP_LOCK(un->un_uppervp);
73 	un->un_flags |= UN_ULOCK;
74 }
75 
76 static int
77 union_lookup1(udvp, dvp, vpp, cnp)
78 	struct vnode *udvp;
79 	struct vnode *dvp;
80 	struct vnode **vpp;
81 	struct componentname *cnp;
82 {
83 	int error;
84 	struct vnode *tdvp;
85 	struct mount *mp;
86 
87 	/*
88 	 * If stepping up the directory tree, check for going
89 	 * back across the mount point, in which case do what
90 	 * lookup would do by stepping back down the mount
91 	 * hierarchy.
92 	 */
93 	if (cnp->cn_flags & ISDOTDOT) {
94 		for (;;) {
95 			/*
96 			 * Don't do the NOCROSSMOUNT check
97 			 * at this level.  By definition,
98 			 * union fs deals with namespaces, not
99 			 * filesystems.
100 			 */
101 			if ((dvp->v_flag & VROOT) == 0)
102 				break;
103 
104 			tdvp = dvp;
105 			dvp = dvp->v_mount->mnt_vnodecovered;
106 			vput(tdvp);
107 			VREF(dvp);
108 			VOP_LOCK(dvp);
109 		}
110 	}
111 
112         error = VOP_LOOKUP(dvp, &tdvp, cnp);
113 	if (error)
114 		return (error);
115 
116 	/*
117 	 * The parent directory will have been unlocked, unless lookup
118 	 * found the last component.  In which case, re-lock the node
119 	 * here to allow it to be unlocked again (phew) in union_lookup.
120 	 */
121 	if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
122 		VOP_LOCK(dvp);
123 
124 	dvp = tdvp;
125 
126 	/*
127 	 * Lastly check if the current node is a mount point in
128 	 * which case walk up the mount hierarchy making sure not to
129 	 * bump into the root of the mount tree (ie. dvp != udvp).
130 	 */
131 	while (dvp != udvp && (dvp->v_type == VDIR) &&
132 	       (mp = dvp->v_mountedhere)) {
133 
134 		if (mp->mnt_flag & MNT_MLOCK) {
135 			mp->mnt_flag |= MNT_MWAIT;
136 			(void) tsleep((caddr_t) mp, PVFS, "unlkup", 0);
137 			continue;
138 		}
139 
140 		error = VFS_ROOT(mp, &tdvp);
141 		if (error) {
142 			vput(dvp);
143 			return (error);
144 		}
145 
146 		vput(dvp);
147 		dvp = tdvp;
148 	}
149 
150 	*vpp = dvp;
151 	return (0);
152 }
153 
154 int
155 union_lookup(ap)
156 	struct vop_lookup_args /* {
157 		struct vnodeop_desc *a_desc;
158 		struct vnode *a_dvp;
159 		struct vnode **a_vpp;
160 		struct componentname *a_cnp;
161 	} */ *ap;
162 {
163 	int error;
164 	int uerror, lerror;
165 	struct vnode *uppervp, *lowervp;
166 	struct vnode *upperdvp, *lowerdvp;
167 	struct vnode *dvp = ap->a_dvp;
168 	struct union_node *dun = VTOUNION(dvp);
169 	struct componentname *cnp = ap->a_cnp;
170 	int lockparent = cnp->cn_flags & LOCKPARENT;
171 	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
172 	struct ucred *saved_cred = 0;
173 
174 	cnp->cn_flags |= LOCKPARENT;
175 
176 	upperdvp = dun->un_uppervp;
177 	lowerdvp = dun->un_lowervp;
178 	uppervp = NULLVP;
179 	lowervp = NULLVP;
180 
181 	/*
182 	 * do the lookup in the upper level.
183 	 * if that level comsumes additional pathnames,
184 	 * then assume that something special is going
185 	 * on and just return that vnode.
186 	 */
187 	if (upperdvp) {
188 		FIXUP(dun);
189 		uerror = union_lookup1(um->um_uppervp, upperdvp,
190 					&uppervp, cnp);
191 		/*if (uppervp == upperdvp)
192 			dun->un_flags |= UN_KLOCK;*/
193 
194 		if (cnp->cn_consume != 0) {
195 			*ap->a_vpp = uppervp;
196 			if (!lockparent)
197 				cnp->cn_flags &= ~LOCKPARENT;
198 			return (uerror);
199 		}
200 	} else {
201 		uerror = ENOENT;
202 	}
203 
204 	/*
205 	 * in a similar way to the upper layer, do the lookup
206 	 * in the lower layer.   this time, if there is some
207 	 * component magic going on, then vput whatever we got
208 	 * back from the upper layer and return the lower vnode
209 	 * instead.
210 	 */
211 	if (lowerdvp) {
212 		int nameiop;
213 
214 		VOP_LOCK(lowerdvp);
215 
216 		/*
217 		 * Only do a LOOKUP on the bottom node, since
218 		 * we won't be making changes to it anyway.
219 		 */
220 		nameiop = cnp->cn_nameiop;
221 		cnp->cn_nameiop = LOOKUP;
222 		if (um->um_op == UNMNT_BELOW) {
223 			saved_cred = cnp->cn_cred;
224 			cnp->cn_cred = um->um_cred;
225 		}
226 		lerror = union_lookup1(um->um_lowervp, lowerdvp,
227 				&lowervp, cnp);
228 		if (um->um_op == UNMNT_BELOW)
229 			cnp->cn_cred = saved_cred;
230 		cnp->cn_nameiop = nameiop;
231 
232 		if (lowervp != lowerdvp)
233 			VOP_UNLOCK(lowerdvp);
234 
235 		if (cnp->cn_consume != 0) {
236 			if (uppervp) {
237 				if (uppervp == upperdvp)
238 					vrele(uppervp);
239 				else
240 					vput(uppervp);
241 				uppervp = NULLVP;
242 			}
243 			*ap->a_vpp = lowervp;
244 			if (!lockparent)
245 				cnp->cn_flags &= ~LOCKPARENT;
246 			return (lerror);
247 		}
248 	} else {
249 		lerror = ENOENT;
250 	}
251 
252 	if (!lockparent)
253 		cnp->cn_flags &= ~LOCKPARENT;
254 
255 	/*
256 	 * at this point, we have uerror and lerror indicating
257 	 * possible errors with the lookups in the upper and lower
258 	 * layers.  additionally, uppervp and lowervp are (locked)
259 	 * references to existing vnodes in the upper and lower layers.
260 	 *
261 	 * there are now three cases to consider.
262 	 * 1. if both layers returned an error, then return whatever
263 	 *    error the upper layer generated.
264 	 *
265 	 * 2. if the top layer failed and the bottom layer succeeded
266 	 *    then two subcases occur.
267 	 *    a.  the bottom vnode is not a directory, in which
268 	 *	  case just return a new union vnode referencing
269 	 *	  an empty top layer and the existing bottom layer.
270 	 *    b.  the bottom vnode is a directory, in which case
271 	 *	  create a new directory in the top-level and
272 	 *	  continue as in case 3.
273 	 *
274 	 * 3. if the top layer succeeded then return a new union
275 	 *    vnode referencing whatever the new top layer and
276 	 *    whatever the bottom layer returned.
277 	 */
278 
279 	*ap->a_vpp = NULLVP;
280 
281 	/* case 1. */
282 	if ((uerror != 0) && (lerror != 0)) {
283 		return (uerror);
284 	}
285 
286 	/* case 2. */
287 	if (uerror != 0 /* && (lerror == 0) */ ) {
288 		if (lowervp->v_type == VDIR) { /* case 2b. */
289 			dun->un_flags &= ~UN_ULOCK;
290 			VOP_UNLOCK(upperdvp);
291 			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
292 			VOP_LOCK(upperdvp);
293 			dun->un_flags |= UN_ULOCK;
294 
295 			if (uerror) {
296 				if (lowervp) {
297 					vput(lowervp);
298 					lowervp = NULLVP;
299 				}
300 				return (uerror);
301 			}
302 		}
303 	}
304 
305 	if (lowervp)
306 		VOP_UNLOCK(lowervp);
307 
308 	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
309 			      uppervp, lowervp);
310 
311 	if (error) {
312 		if (uppervp)
313 			vput(uppervp);
314 		if (lowervp)
315 			vrele(lowervp);
316 	} else {
317 		if (*ap->a_vpp != dvp)
318 			if (!lockparent || !(cnp->cn_flags & ISLASTCN))
319 				VOP_UNLOCK(dvp);
320 	}
321 
322 	return (error);
323 }
324 
325 int
326 union_create(ap)
327 	struct vop_create_args /* {
328 		struct vnode *a_dvp;
329 		struct vnode **a_vpp;
330 		struct componentname *a_cnp;
331 		struct vattr *a_vap;
332 	} */ *ap;
333 {
334 	struct union_node *un = VTOUNION(ap->a_dvp);
335 	struct vnode *dvp = un->un_uppervp;
336 
337 	if (dvp) {
338 		int error;
339 		struct vnode *vp;
340 
341 		FIXUP(un);
342 
343 		VREF(dvp);
344 		un->un_flags |= UN_KLOCK;
345 		vput(ap->a_dvp);
346 		error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
347 		if (error)
348 			return (error);
349 
350 		error = union_allocvp(
351 				ap->a_vpp,
352 				ap->a_dvp->v_mount,
353 				ap->a_dvp,
354 				NULLVP,
355 				ap->a_cnp,
356 				vp,
357 				NULLVP);
358 		if (error)
359 			vput(vp);
360 		return (error);
361 	}
362 
363 	vput(ap->a_dvp);
364 	return (EROFS);
365 }
366 
367 int
368 union_mknod(ap)
369 	struct vop_mknod_args /* {
370 		struct vnode *a_dvp;
371 		struct vnode **a_vpp;
372 		struct componentname *a_cnp;
373 		struct vattr *a_vap;
374 	} */ *ap;
375 {
376 	struct union_node *un = VTOUNION(ap->a_dvp);
377 	struct vnode *dvp = un->un_uppervp;
378 
379 	if (dvp) {
380 		int error;
381 		struct vnode *vp;
382 
383 		FIXUP(un);
384 
385 		VREF(dvp);
386 		un->un_flags |= UN_KLOCK;
387 		vput(ap->a_dvp);
388 		error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
389 		if (error)
390 			return (error);
391 
392 		if (vp) {
393 			error = union_allocvp(
394 					ap->a_vpp,
395 					ap->a_dvp->v_mount,
396 					ap->a_dvp,
397 					NULLVP,
398 					ap->a_cnp,
399 					vp,
400 					NULLVP);
401 			if (error)
402 				vput(vp);
403 		}
404 		return (error);
405 	}
406 
407 	vput(ap->a_dvp);
408 	return (EROFS);
409 }
410 
411 int
412 union_open(ap)
413 	struct vop_open_args /* {
414 		struct vnodeop_desc *a_desc;
415 		struct vnode *a_vp;
416 		int a_mode;
417 		struct ucred *a_cred;
418 		struct proc *a_p;
419 	} */ *ap;
420 {
421 	struct union_node *un = VTOUNION(ap->a_vp);
422 	struct vnode *tvp;
423 	int mode = ap->a_mode;
424 	struct ucred *cred = ap->a_cred;
425 	struct proc *p = ap->a_p;
426 	int error;
427 
428 	/*
429 	 * If there is an existing upper vp then simply open that.
430 	 */
431 	tvp = un->un_uppervp;
432 	if (tvp == NULLVP) {
433 		/*
434 		 * If the lower vnode is being opened for writing, then
435 		 * copy the file contents to the upper vnode and open that,
436 		 * otherwise can simply open the lower vnode.
437 		 */
438 		tvp = un->un_lowervp;
439 		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
440 			struct vnode *vp;
441 			int i;
442 
443 			/*
444 			 * Open the named file in the upper layer.  Note that
445 			 * the file may have come into existence *since* the
446 			 * lookup was done, since the upper layer may really
447 			 * be a loopback mount of some other filesystem...
448 			 * so open the file with exclusive create and barf if
449 			 * it already exists.
450 			 * XXX - perhaps should re-lookup the node (once more
451 			 * with feeling) and simply open that.  Who knows.
452 			 */
453 			error = union_vn_create(&vp, un, p);
454 			if (error)
455 				return (error);
456 
457 			/* at this point, uppervp is locked */
458 			union_newupper(un, vp);
459 			un->un_flags |= UN_ULOCK;
460 
461 			/*
462 			 * Now, if the file is being opened with truncation,
463 			 * then the (new) upper vnode is ready to fly,
464 			 * otherwise the data from the lower vnode must be
465 			 * copied to the upper layer first.  This only works
466 			 * for regular files (check is made above).
467 			 */
468 			if ((mode & O_TRUNC) == 0) {
469 				/*
470 				 * XXX - should not ignore errors
471 				 * from VOP_CLOSE
472 				 */
473 				VOP_LOCK(tvp);
474 				error = VOP_OPEN(tvp, FREAD, cred, p);
475 				if (error == 0) {
476 					error = union_copyfile(p, cred,
477 						       tvp, un->un_uppervp);
478 					VOP_UNLOCK(tvp);
479 					(void) VOP_CLOSE(tvp, FREAD);
480 				} else {
481 					VOP_UNLOCK(tvp);
482 				}
483 
484 #ifdef UNION_DIAGNOSTIC
485 				if (!error)
486 					uprintf("union: copied up %s\n",
487 								un->un_path);
488 #endif
489 			}
490 
491 			un->un_flags &= ~UN_ULOCK;
492 			VOP_UNLOCK(un->un_uppervp);
493 			union_vn_close(un->un_uppervp, FWRITE, cred, p);
494 			VOP_LOCK(un->un_uppervp);
495 			un->un_flags |= UN_ULOCK;
496 
497 			/*
498 			 * Subsequent IOs will go to the top layer, so
499 			 * call close on the lower vnode and open on the
500 			 * upper vnode to ensure that the filesystem keeps
501 			 * its references counts right.  This doesn't do
502 			 * the right thing with (cred) and (FREAD) though.
503 			 * Ignoring error returns is not righ, either.
504 			 */
505 			for (i = 0; i < un->un_openl; i++) {
506 				(void) VOP_CLOSE(tvp, FREAD);
507 				(void) VOP_OPEN(un->un_uppervp, FREAD, cred, p);
508 			}
509 			un->un_openl = 0;
510 
511 			if (error == 0)
512 				error = VOP_OPEN(un->un_uppervp, mode, cred, p);
513 			return (error);
514 		}
515 
516 		/*
517 		 * Just open the lower vnode
518 		 */
519 		un->un_openl++;
520 		VOP_LOCK(tvp);
521 		error = VOP_OPEN(tvp, mode, cred, p);
522 		VOP_UNLOCK(tvp);
523 
524 		return (error);
525 	}
526 
527 	FIXUP(un);
528 
529 	error = VOP_OPEN(tvp, mode, cred, p);
530 
531 	return (error);
532 }
533 
534 int
535 union_close(ap)
536 	struct vop_close_args /* {
537 		struct vnode *a_vp;
538 		int  a_fflag;
539 		struct ucred *a_cred;
540 		struct proc *a_p;
541 	} */ *ap;
542 {
543 	struct union_node *un = VTOUNION(ap->a_vp);
544 	struct vnode *vp;
545 
546 	if (un->un_uppervp) {
547 		vp = un->un_uppervp;
548 	} else {
549 #ifdef UNION_DIAGNOSTIC
550 		if (un->un_openl <= 0)
551 			panic("union: un_openl cnt");
552 #endif
553 		--un->un_openl;
554 		vp = un->un_lowervp;
555 	}
556 
557 	return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
558 }
559 
560 /*
561  * Check access permission on the union vnode.
562  * The access check being enforced is to check
563  * against both the underlying vnode, and any
564  * copied vnode.  This ensures that no additional
565  * file permissions are given away simply because
566  * the user caused an implicit file copy.
567  */
568 int
569 union_access(ap)
570 	struct vop_access_args /* {
571 		struct vnodeop_desc *a_desc;
572 		struct vnode *a_vp;
573 		int a_mode;
574 		struct ucred *a_cred;
575 		struct proc *a_p;
576 	} */ *ap;
577 {
578 	struct union_node *un = VTOUNION(ap->a_vp);
579 	int error = EACCES;
580 	struct vnode *vp;
581 
582 	vp = un->un_uppervp;
583 	if (vp) {
584 		FIXUP(un);
585 		return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
586 	}
587 
588 	vp = un->un_lowervp;
589 	if (vp) {
590 		VOP_LOCK(vp);
591 		error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
592 		if (error == 0) {
593 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
594 
595 			if (um->um_op == UNMNT_BELOW)
596 				error = VOP_ACCESS(vp, ap->a_mode,
597 						um->um_cred, ap->a_p);
598 		}
599 		VOP_UNLOCK(vp);
600 		if (error)
601 			return (error);
602 	}
603 
604 	return (error);
605 }
606 
607 /*
608  *  We handle getattr only to change the fsid.
609  */
610 int
611 union_getattr(ap)
612 	struct vop_getattr_args /* {
613 		struct vnode *a_vp;
614 		struct vattr *a_vap;
615 		struct ucred *a_cred;
616 		struct proc *a_p;
617 	} */ *ap;
618 {
619 	int error;
620 	struct union_node *un = VTOUNION(ap->a_vp);
621 	struct vnode *vp = un->un_uppervp;
622 	struct vattr *vap;
623 	struct vattr va;
624 
625 
626 	/*
627 	 * Some programs walk the filesystem hierarchy by counting
628 	 * links to directories to avoid stat'ing all the time.
629 	 * This means the link count on directories needs to be "correct".
630 	 * The only way to do that is to call getattr on both layers
631 	 * and fix up the link count.  The link count will not necessarily
632 	 * be accurate but will be large enough to defeat the tree walkers.
633 	 */
634 
635 	vap = ap->a_vap;
636 
637 	vp = un->un_uppervp;
638 	if (vp != NULLVP) {
639 		FIXUP(un);
640 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
641 		if (error)
642 			return (error);
643 	}
644 
645 	if (vp == NULLVP) {
646 		vp = un->un_lowervp;
647 	} else if (vp->v_type == VDIR) {
648 		vp = un->un_lowervp;
649 		vap = &va;
650 	} else {
651 		vp = NULLVP;
652 	}
653 
654 	if (vp != NULLVP) {
655 		VOP_LOCK(vp);
656 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
657 		VOP_UNLOCK(vp);
658 		if (error)
659 			return (error);
660 	}
661 
662 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
663 		ap->a_vap->va_nlink += vap->va_nlink;
664 
665 	vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
666 	return (0);
667 }
668 
669 int
670 union_setattr(ap)
671 	struct vop_setattr_args /* {
672 		struct vnode *a_vp;
673 		struct vattr *a_vap;
674 		struct ucred *a_cred;
675 		struct proc *a_p;
676 	} */ *ap;
677 {
678 	struct union_node *un = VTOUNION(ap->a_vp);
679 	int error;
680 
681 	/*
682 	 * Handle case of truncating lower object to zero size,
683 	 * by creating a zero length upper object.  This is to
684 	 * handle the case of open with O_TRUNC and O_CREAT.
685 	 */
686 	if ((un->un_uppervp == NULLVP) &&
687 	    /* assert(un->un_lowervp != NULLVP) */
688 	    (un->un_lowervp->v_type == VREG) &&
689 	    (ap->a_vap->va_size == 0)) {
690 		struct vnode *vp;
691 
692 		error = union_vn_create(&vp, un, ap->a_p);
693 		if (error)
694 			return (error);
695 
696 		/* at this point, uppervp is locked */
697 		union_newupper(un, vp);
698 
699 		VOP_UNLOCK(vp);
700 		union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
701 		VOP_LOCK(vp);
702 		un->un_flags |= UN_ULOCK;
703 	}
704 
705 	/*
706 	 * Try to set attributes in upper layer,
707 	 * otherwise return read-only filesystem error.
708 	 */
709 	if (un->un_uppervp != NULLVP) {
710 		FIXUP(un);
711 		error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
712 					ap->a_cred, ap->a_p);
713 	} else {
714 		error = EROFS;
715 	}
716 
717 	return (error);
718 }
719 
720 int
721 union_read(ap)
722 	struct vop_read_args /* {
723 		struct vnode *a_vp;
724 		struct uio *a_uio;
725 		int  a_ioflag;
726 		struct ucred *a_cred;
727 	} */ *ap;
728 {
729 	int error;
730 	struct vnode *vp = OTHERVP(ap->a_vp);
731 	int dolock = (vp == LOWERVP(ap->a_vp));
732 
733 	if (dolock)
734 		VOP_LOCK(vp);
735 	else
736 		FIXUP(VTOUNION(ap->a_vp));
737 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
738 	if (dolock)
739 		VOP_UNLOCK(vp);
740 
741 	return (error);
742 }
743 
744 int
745 union_write(ap)
746 	struct vop_read_args /* {
747 		struct vnode *a_vp;
748 		struct uio *a_uio;
749 		int  a_ioflag;
750 		struct ucred *a_cred;
751 	} */ *ap;
752 {
753 	int error;
754 	struct vnode *vp = OTHERVP(ap->a_vp);
755 	int dolock = (vp == LOWERVP(ap->a_vp));
756 
757 	if (dolock)
758 		VOP_LOCK(vp);
759 	else
760 		FIXUP(VTOUNION(ap->a_vp));
761 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
762 	if (dolock)
763 		VOP_UNLOCK(vp);
764 
765 	return (error);
766 }
767 
768 int
769 union_ioctl(ap)
770 	struct vop_ioctl_args /* {
771 		struct vnode *a_vp;
772 		int  a_command;
773 		caddr_t  a_data;
774 		int  a_fflag;
775 		struct ucred *a_cred;
776 		struct proc *a_p;
777 	} */ *ap;
778 {
779 
780 	return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
781 				ap->a_fflag, ap->a_cred, ap->a_p));
782 }
783 
784 int
785 union_select(ap)
786 	struct vop_select_args /* {
787 		struct vnode *a_vp;
788 		int  a_which;
789 		int  a_fflags;
790 		struct ucred *a_cred;
791 		struct proc *a_p;
792 	} */ *ap;
793 {
794 
795 	return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
796 				ap->a_cred, ap->a_p));
797 }
798 
799 int
800 union_mmap(ap)
801 	struct vop_mmap_args /* {
802 		struct vnode *a_vp;
803 		int  a_fflags;
804 		struct ucred *a_cred;
805 		struct proc *a_p;
806 	} */ *ap;
807 {
808 
809 	return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
810 				ap->a_cred, ap->a_p));
811 }
812 
813 int
814 union_fsync(ap)
815 	struct vop_fsync_args /* {
816 		struct vnode *a_vp;
817 		struct ucred *a_cred;
818 		int  a_waitfor;
819 		struct proc *a_p;
820 	} */ *ap;
821 {
822 	int error = 0;
823 	struct vnode *targetvp = OTHERVP(ap->a_vp);
824 
825 	if (targetvp) {
826 		int dolock = (targetvp == LOWERVP(ap->a_vp));
827 
828 		if (dolock)
829 			VOP_LOCK(targetvp);
830 		else
831 			FIXUP(VTOUNION(ap->a_vp));
832 		error = VOP_FSYNC(targetvp, ap->a_cred,
833 					ap->a_waitfor, ap->a_p);
834 		if (dolock)
835 			VOP_UNLOCK(targetvp);
836 	}
837 
838 	return (error);
839 }
840 
841 int
842 union_seek(ap)
843 	struct vop_seek_args /* {
844 		struct vnode *a_vp;
845 		off_t  a_oldoff;
846 		off_t  a_newoff;
847 		struct ucred *a_cred;
848 	} */ *ap;
849 {
850 
851 	return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
852 }
853 
854 int
855 union_remove(ap)
856 	struct vop_remove_args /* {
857 		struct vnode *a_dvp;
858 		struct vnode *a_vp;
859 		struct componentname *a_cnp;
860 	} */ *ap;
861 {
862 	int error;
863 	struct union_node *dun = VTOUNION(ap->a_dvp);
864 	struct union_node *un = VTOUNION(ap->a_vp);
865 
866 	if (dun->un_uppervp && un->un_uppervp) {
867 		struct vnode *dvp = dun->un_uppervp;
868 		struct vnode *vp = un->un_uppervp;
869 
870 		FIXUP(dun);
871 		VREF(dvp);
872 		dun->un_flags |= UN_KLOCK;
873 		vput(ap->a_dvp);
874 		FIXUP(un);
875 		VREF(vp);
876 		un->un_flags |= UN_KLOCK;
877 		vput(ap->a_vp);
878 
879 		error = VOP_REMOVE(dvp, vp, ap->a_cnp);
880 		if (!error)
881 			union_removed_upper(un);
882 
883 		/*
884 		 * XXX: should create a whiteout here
885 		 */
886 	} else {
887 		/*
888 		 * XXX: should create a whiteout here
889 		 */
890 		vput(ap->a_dvp);
891 		vput(ap->a_vp);
892 		error = EROFS;
893 	}
894 
895 	return (error);
896 }
897 
898 int
899 union_link(ap)
900 	struct vop_link_args /* {
901 		struct vnode *a_vp;
902 		struct vnode *a_tdvp;
903 		struct componentname *a_cnp;
904 	} */ *ap;
905 {
906 	int error;
907 	struct union_node *dun = VTOUNION(ap->a_vp);
908 	struct union_node *un = VTOUNION(ap->a_tdvp);
909 
910 	if (dun->un_uppervp && un->un_uppervp) {
911 		struct vnode *dvp = dun->un_uppervp;
912 		struct vnode *vp = un->un_uppervp;
913 
914 		FIXUP(dun);
915 		VREF(dvp);
916 		dun->un_flags |= UN_KLOCK;
917 		vput(ap->a_vp);
918 		FIXUP(un);
919 		VREF(vp);
920 		vrele(ap->a_tdvp);
921 
922 		error = VOP_LINK(dvp, vp, ap->a_cnp);
923 	} else {
924 		/*
925 		 * XXX: need to copy to upper layer
926 		 * and do the link there.
927 		 */
928 		vput(ap->a_vp);
929 		vrele(ap->a_tdvp);
930 		error = EROFS;
931 	}
932 
933 	return (error);
934 }
935 
936 int
937 union_rename(ap)
938 	struct vop_rename_args  /* {
939 		struct vnode *a_fdvp;
940 		struct vnode *a_fvp;
941 		struct componentname *a_fcnp;
942 		struct vnode *a_tdvp;
943 		struct vnode *a_tvp;
944 		struct componentname *a_tcnp;
945 	} */ *ap;
946 {
947 	int error;
948 
949 	struct vnode *fdvp = ap->a_fdvp;
950 	struct vnode *fvp = ap->a_fvp;
951 	struct vnode *tdvp = ap->a_tdvp;
952 	struct vnode *tvp = ap->a_tvp;
953 
954 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
955 		struct union_node *un = VTOUNION(fdvp);
956 		if (un->un_uppervp == NULLVP) {
957 			error = EROFS;
958 			goto bad;
959 		}
960 
961 		FIXUP(un);
962 		fdvp = un->un_uppervp;
963 		VREF(fdvp);
964 		vrele(ap->a_fdvp);
965 	}
966 
967 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
968 		struct union_node *un = VTOUNION(fvp);
969 		if (un->un_uppervp == NULLVP) {
970 			error = EROFS;
971 			goto bad;
972 		}
973 
974 		FIXUP(un);
975 		fvp = un->un_uppervp;
976 		VREF(fvp);
977 		vrele(ap->a_fvp);
978 	}
979 
980 	if (tdvp->v_op == union_vnodeop_p) {
981 		struct union_node *un = VTOUNION(tdvp);
982 		if (un->un_uppervp == NULLVP) {
983 			error = EROFS;
984 			goto bad;
985 		}
986 
987 		tdvp = un->un_uppervp;
988 		VREF(tdvp);
989 		un->un_flags |= UN_KLOCK;
990 		vput(ap->a_tdvp);
991 	}
992 
993 	if (tvp && tvp->v_op == union_vnodeop_p) {
994 		struct union_node *un = VTOUNION(tvp);
995 		if (un->un_uppervp == NULLVP) {
996 			error = EROFS;
997 			goto bad;
998 		}
999 
1000 		tvp = un->un_uppervp;
1001 		VREF(tvp);
1002 		un->un_flags |= UN_KLOCK;
1003 		vput(ap->a_tvp);
1004 	}
1005 
1006 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
1007 
1008 bad:
1009 	vrele(fdvp);
1010 	vrele(fvp);
1011 	vput(tdvp);
1012 	if (tvp)
1013 		vput(tvp);
1014 
1015 	return (error);
1016 }
1017 
1018 int
1019 union_mkdir(ap)
1020 	struct vop_mkdir_args /* {
1021 		struct vnode *a_dvp;
1022 		struct vnode **a_vpp;
1023 		struct componentname *a_cnp;
1024 		struct vattr *a_vap;
1025 	} */ *ap;
1026 {
1027 	struct union_node *un = VTOUNION(ap->a_dvp);
1028 	struct vnode *dvp = un->un_uppervp;
1029 
1030 	if (dvp) {
1031 		int error;
1032 		struct vnode *vp;
1033 
1034 		FIXUP(un);
1035 		VREF(dvp);
1036 		un->un_flags |= UN_KLOCK;
1037 		vput(ap->a_dvp);
1038 		error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
1039 		if (error)
1040 			return (error);
1041 
1042 		error = union_allocvp(
1043 				ap->a_vpp,
1044 				ap->a_dvp->v_mount,
1045 				ap->a_dvp,
1046 				NULLVP,
1047 				ap->a_cnp,
1048 				vp,
1049 				NULLVP);
1050 		if (error)
1051 			vput(vp);
1052 		return (error);
1053 	}
1054 
1055 	vput(ap->a_dvp);
1056 	return (EROFS);
1057 }
1058 
1059 int
1060 union_rmdir(ap)
1061 	struct vop_rmdir_args /* {
1062 		struct vnode *a_dvp;
1063 		struct vnode *a_vp;
1064 		struct componentname *a_cnp;
1065 	} */ *ap;
1066 {
1067 	int error;
1068 	struct union_node *dun = VTOUNION(ap->a_dvp);
1069 	struct union_node *un = VTOUNION(ap->a_vp);
1070 
1071 	if (dun->un_uppervp && un->un_uppervp) {
1072 		struct vnode *dvp = dun->un_uppervp;
1073 		struct vnode *vp = un->un_uppervp;
1074 
1075 		FIXUP(dun);
1076 		VREF(dvp);
1077 		dun->un_flags |= UN_KLOCK;
1078 		vput(ap->a_dvp);
1079 		FIXUP(un);
1080 		VREF(vp);
1081 		un->un_flags |= UN_KLOCK;
1082 		vput(ap->a_vp);
1083 
1084 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
1085 		if (!error)
1086 			union_removed_upper(un);
1087 
1088 		/*
1089 		 * XXX: should create a whiteout here
1090 		 */
1091 	} else {
1092 		/*
1093 		 * XXX: should create a whiteout here
1094 		 */
1095 		vput(ap->a_dvp);
1096 		vput(ap->a_vp);
1097 		error = EROFS;
1098 	}
1099 
1100 	return (error);
1101 }
1102 
1103 int
1104 union_symlink(ap)
1105 	struct vop_symlink_args /* {
1106 		struct vnode *a_dvp;
1107 		struct vnode **a_vpp;
1108 		struct componentname *a_cnp;
1109 		struct vattr *a_vap;
1110 		char *a_target;
1111 	} */ *ap;
1112 {
1113 	struct union_node *un = VTOUNION(ap->a_dvp);
1114 	struct vnode *dvp = un->un_uppervp;
1115 
1116 	if (dvp) {
1117 		int error;
1118 		struct vnode *vp;
1119 
1120 		FIXUP(un);
1121 		VREF(dvp);
1122 		un->un_flags |= UN_KLOCK;
1123 		vput(ap->a_dvp);
1124 		error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
1125 					ap->a_vap, ap->a_target);
1126 		*ap->a_vpp = NULLVP;
1127 		return (error);
1128 	}
1129 
1130 	vput(ap->a_dvp);
1131 	return (EROFS);
1132 }
1133 
1134 /*
1135  * union_readdir works in concert with getdirentries and
1136  * readdir(3) to provide a list of entries in the unioned
1137  * directories.  getdirentries is responsible for walking
1138  * down the union stack.  readdir(3) is responsible for
1139  * eliminating duplicate names from the returned data stream.
1140  */
1141 int
1142 union_readdir(ap)
1143 	struct vop_readdir_args /* {
1144 		struct vnode *a_vp;
1145 		struct uio *a_uio;
1146 		struct ucred *a_cred;
1147 	} */ *ap;
1148 {
1149 	int error = 0;
1150 	struct union_node *un = VTOUNION(ap->a_vp);
1151 
1152 	if (un->un_uppervp) {
1153 		FIXUP(un);
1154 		error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred, NULL, NULL, NULL);
1155 	}
1156 
1157 	return (error);
1158 }
1159 
1160 int
1161 union_readlink(ap)
1162 	struct vop_readlink_args /* {
1163 		struct vnode *a_vp;
1164 		struct uio *a_uio;
1165 		struct ucred *a_cred;
1166 	} */ *ap;
1167 {
1168 	int error;
1169 	struct vnode *vp = OTHERVP(ap->a_vp);
1170 	int dolock = (vp == LOWERVP(ap->a_vp));
1171 
1172 	if (dolock)
1173 		VOP_LOCK(vp);
1174 	else
1175 		FIXUP(VTOUNION(ap->a_vp));
1176 	error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
1177 	if (dolock)
1178 		VOP_UNLOCK(vp);
1179 
1180 	return (error);
1181 }
1182 
1183 int
1184 union_abortop(ap)
1185 	struct vop_abortop_args /* {
1186 		struct vnode *a_dvp;
1187 		struct componentname *a_cnp;
1188 	} */ *ap;
1189 {
1190 	int error;
1191 	struct vnode *vp = OTHERVP(ap->a_dvp);
1192 	struct union_node *un = VTOUNION(ap->a_dvp);
1193 	int islocked = un->un_flags & UN_LOCKED;
1194 	int dolock = (vp == LOWERVP(ap->a_dvp));
1195 
1196 	if (islocked) {
1197 		if (dolock)
1198 			VOP_LOCK(vp);
1199 		else
1200 			FIXUP(VTOUNION(ap->a_dvp));
1201 	}
1202 	error = VOP_ABORTOP(vp, ap->a_cnp);
1203 	if (islocked && dolock)
1204 		VOP_UNLOCK(vp);
1205 
1206 	return (error);
1207 }
1208 
1209 int
1210 union_inactive(ap)
1211 	struct vop_inactive_args /* {
1212 		struct vnode *a_vp;
1213 	} */ *ap;
1214 {
1215 
1216 	/*
1217 	 * Do nothing (and _don't_ bypass).
1218 	 * Wait to vrele lowervp until reclaim,
1219 	 * so that until then our union_node is in the
1220 	 * cache and reusable.
1221 	 *
1222 	 * NEEDSWORK: Someday, consider inactive'ing
1223 	 * the lowervp and then trying to reactivate it
1224 	 * with capabilities (v_id)
1225 	 * like they do in the name lookup cache code.
1226 	 * That's too much work for now.
1227 	 */
1228 
1229 #ifdef UNION_DIAGNOSTIC
1230 	struct union_node *un = VTOUNION(ap->a_vp);
1231 
1232 	if (un->un_flags & UN_LOCKED)
1233 		panic("union: inactivating locked node");
1234 #endif
1235 
1236 	return (0);
1237 }
1238 
1239 int
1240 union_reclaim(ap)
1241 	struct vop_reclaim_args /* {
1242 		struct vnode *a_vp;
1243 	} */ *ap;
1244 {
1245 
1246 	union_freevp(ap->a_vp);
1247 
1248 	return (0);
1249 }
1250 
1251 int
1252 union_lock(ap)
1253 	struct vop_lock_args *ap;
1254 {
1255 	struct vnode *vp = ap->a_vp;
1256 	struct union_node *un;
1257 
1258 start:
1259 	while (vp->v_flag & VXLOCK) {
1260 		vp->v_flag |= VXWANT;
1261 		(void) tsleep((caddr_t)vp, PINOD, "unnlk1", 0);
1262 	}
1263 
1264 	un = VTOUNION(vp);
1265 
1266 	if (un->un_uppervp) {
1267 		if ((un->un_flags & UN_ULOCK) == 0) {
1268 			un->un_flags |= UN_ULOCK;
1269 			VOP_LOCK(un->un_uppervp);
1270 		}
1271 #ifdef DIAGNOSTIC
1272 		if (un->un_flags & UN_KLOCK)
1273 			panic("union: dangling upper lock");
1274 #endif
1275 	}
1276 
1277 	if (un->un_flags & UN_LOCKED) {
1278 #ifdef DIAGNOSTIC
1279 		if (curproc && un->un_pid == curproc->p_pid &&
1280 			    un->un_pid > -1 && curproc->p_pid > -1)
1281 			panic("union: locking against myself");
1282 #endif
1283 		un->un_flags |= UN_WANT;
1284 		(void) tsleep((caddr_t) &un->un_flags, PINOD, "unnlk2", 0);
1285 		goto start;
1286 	}
1287 
1288 #ifdef DIAGNOSTIC
1289 	if (curproc)
1290 		un->un_pid = curproc->p_pid;
1291 	else
1292 		un->un_pid = -1;
1293 #endif
1294 
1295 	un->un_flags |= UN_LOCKED;
1296 	return (0);
1297 }
1298 
1299 int
1300 union_unlock(ap)
1301 	struct vop_lock_args *ap;
1302 {
1303 	struct union_node *un = VTOUNION(ap->a_vp);
1304 
1305 #ifdef DIAGNOSTIC
1306 	if ((un->un_flags & UN_LOCKED) == 0)
1307 		panic("union: unlock unlocked node");
1308 	if (curproc && un->un_pid != curproc->p_pid &&
1309 			curproc->p_pid > -1 && un->un_pid > -1)
1310 		panic("union: unlocking other process's union node");
1311 #endif
1312 
1313 	un->un_flags &= ~UN_LOCKED;
1314 
1315 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
1316 		VOP_UNLOCK(un->un_uppervp);
1317 
1318 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
1319 
1320 	if (un->un_flags & UN_WANT) {
1321 		un->un_flags &= ~UN_WANT;
1322 		wakeup((caddr_t) &un->un_flags);
1323 	}
1324 
1325 #ifdef DIAGNOSTIC
1326 	un->un_pid = 0;
1327 #endif
1328 
1329 	return (0);
1330 }
1331 
1332 int
1333 union_bmap(ap)
1334 	struct vop_bmap_args /* {
1335 		struct vnode *a_vp;
1336 		daddr_t  a_bn;
1337 		struct vnode **a_vpp;
1338 		daddr_t *a_bnp;
1339 		int *a_runp;
1340 	} */ *ap;
1341 {
1342 	int error;
1343 	struct vnode *vp = OTHERVP(ap->a_vp);
1344 	int dolock = (vp == LOWERVP(ap->a_vp));
1345 
1346 	if (dolock)
1347 		VOP_LOCK(vp);
1348 	else
1349 		FIXUP(VTOUNION(ap->a_vp));
1350 	error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
1351 	if (dolock)
1352 		VOP_UNLOCK(vp);
1353 
1354 	return (error);
1355 }
1356 
1357 int
1358 union_print(ap)
1359 	struct vop_print_args /* {
1360 		struct vnode *a_vp;
1361 	} */ *ap;
1362 {
1363 	struct vnode *vp = ap->a_vp;
1364 
1365 	printf("\ttag VT_UNION, vp=%p, uppervp=%p, lowervp=%p\n",
1366 			vp, UPPERVP(vp), LOWERVP(vp));
1367 	return (0);
1368 }
1369 
1370 int
1371 union_islocked(ap)
1372 	struct vop_islocked_args /* {
1373 		struct vnode *a_vp;
1374 	} */ *ap;
1375 {
1376 
1377 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
1378 }
1379 
1380 int
1381 union_pathconf(ap)
1382 	struct vop_pathconf_args /* {
1383 		struct vnode *a_vp;
1384 		int a_name;
1385 		int *a_retval;
1386 	} */ *ap;
1387 {
1388 	int error;
1389 	struct vnode *vp = OTHERVP(ap->a_vp);
1390 	int dolock = (vp == LOWERVP(ap->a_vp));
1391 
1392 	if (dolock)
1393 		VOP_LOCK(vp);
1394 	else
1395 		FIXUP(VTOUNION(ap->a_vp));
1396 	error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
1397 	if (dolock)
1398 		VOP_UNLOCK(vp);
1399 
1400 	return (error);
1401 }
1402 
1403 int
1404 union_advlock(ap)
1405 	struct vop_advlock_args /* {
1406 		struct vnode *a_vp;
1407 		caddr_t  a_id;
1408 		int  a_op;
1409 		struct flock *a_fl;
1410 		int  a_flags;
1411 	} */ *ap;
1412 {
1413 
1414 	return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
1415 				ap->a_fl, ap->a_flags));
1416 }
1417 
1418 
1419 /*
1420  * XXX - vop_strategy must be hand coded because it has no
1421  * vnode in its arguments.
1422  * This goes away with a merged VM/buffer cache.
1423  */
1424 int
1425 union_strategy(ap)
1426 	struct vop_strategy_args /* {
1427 		struct buf *a_bp;
1428 	} */ *ap;
1429 {
1430 	struct buf *bp = ap->a_bp;
1431 	int error;
1432 	struct vnode *savedvp;
1433 
1434 	savedvp = bp->b_vp;
1435 	bp->b_vp = OTHERVP(bp->b_vp);
1436 
1437 #ifdef DIAGNOSTIC
1438 	if (bp->b_vp == NULLVP)
1439 		panic("union_strategy: nil vp");
1440 	if (((bp->b_flags & B_READ) == 0) &&
1441 	    (bp->b_vp == LOWERVP(savedvp)))
1442 		panic("union_strategy: writing to lowervp");
1443 #endif
1444 
1445 	error = VOP_STRATEGY(bp);
1446 	bp->b_vp = savedvp;
1447 
1448 	return (error);
1449 }
1450 
1451 /*
1452  * Global vfs data structures
1453  */
1454 int (**union_vnodeop_p)();
1455 struct vnodeopv_entry_desc union_vnodeop_entries[] = {
1456 	{ &vop_default_desc, vn_default_error },
1457 	{ &vop_lookup_desc, union_lookup },		/* lookup */
1458 	{ &vop_create_desc, union_create },		/* create */
1459 	{ &vop_mknod_desc, union_mknod },		/* mknod */
1460 	{ &vop_open_desc, union_open },			/* open */
1461 	{ &vop_close_desc, union_close },		/* close */
1462 	{ &vop_access_desc, union_access },		/* access */
1463 	{ &vop_getattr_desc, union_getattr },		/* getattr */
1464 	{ &vop_setattr_desc, union_setattr },		/* setattr */
1465 	{ &vop_read_desc, union_read },			/* read */
1466 	{ &vop_write_desc, union_write },		/* write */
1467 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
1468 	{ &vop_select_desc, union_select },		/* select */
1469 	{ &vop_mmap_desc, union_mmap },			/* mmap */
1470 	{ &vop_fsync_desc, union_fsync },		/* fsync */
1471 	{ &vop_seek_desc, union_seek },			/* seek */
1472 	{ &vop_remove_desc, union_remove },		/* remove */
1473 	{ &vop_link_desc, union_link },			/* link */
1474 	{ &vop_rename_desc, union_rename },		/* rename */
1475 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
1476 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
1477 	{ &vop_symlink_desc, union_symlink },		/* symlink */
1478 	{ &vop_readdir_desc, union_readdir },		/* readdir */
1479 	{ &vop_readlink_desc, union_readlink },		/* readlink */
1480 	{ &vop_abortop_desc, union_abortop },		/* abortop */
1481 	{ &vop_inactive_desc, union_inactive },		/* inactive */
1482 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
1483 	{ &vop_lock_desc, union_lock },			/* lock */
1484 	{ &vop_unlock_desc, union_unlock },		/* unlock */
1485 	{ &vop_bmap_desc, union_bmap },			/* bmap */
1486 	{ &vop_strategy_desc, union_strategy },		/* strategy */
1487 	{ &vop_print_desc, union_print },		/* print */
1488 	{ &vop_islocked_desc, union_islocked },		/* islocked */
1489 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
1490 	{ &vop_advlock_desc, union_advlock },		/* advlock */
1491 #ifdef notdef
1492 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
1493 	{ &vop_valloc_desc, union_valloc },		/* valloc */
1494 	{ &vop_vfree_desc, union_vfree },		/* vfree */
1495 	{ &vop_truncate_desc, union_truncate },		/* truncate */
1496 	{ &vop_update_desc, union_update },		/* update */
1497 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
1498 #endif
1499 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
1500 };
1501 struct vnodeopv_desc union_vnodeop_opv_desc =
1502 	{ &union_vnodeop_p, union_vnodeop_entries };
1503 
1504 VNODEOP_SET(union_vnodeop_opv_desc);
1505