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