1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
31 * Copyright (c) 1992, 1993, 1994, 1995
32 *	The Regents of the University of California.  All rights reserved.
33 *
34 * This code is derived from software contributed to Berkeley by
35 * Jan-Simon Pendry.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 *    must display the following acknowledgement:
47 *	This product includes software developed by the University of
48 *	California, Berkeley and its contributors.
49 * 4. Neither the name of the University nor the names of its contributors
50 *    may be used to endorse or promote products derived from this software
51 *    without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 *	@(#)union_vnops.c	8.32 (Berkeley) 6/23/95
66 */
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/proc.h>
71#include <sys/kauth.h>
72#include <sys/file.h>
73#include <sys/time.h>
74#include <sys/stat.h>
75#include <sys/types.h>
76#include <sys/vnode_internal.h>
77#include <sys/mount_internal.h>
78#include <sys/namei.h>
79#include <sys/malloc.h>
80#include <sys/buf_internal.h>
81#include <sys/queue.h>
82#include <sys/lock.h>
83#include <miscfs/union/union.h>
84#include <vfs/vfs_support.h>
85#include <sys/ubc.h>
86#include <sys/kdebug.h>
87#include <sys/uio_internal.h>
88
89/* called with no union lock held */
90static int
91union_lookup1(struct vnode *udvp, struct vnode **dvpp, struct vnode **vpp,
92	struct componentname *cnp)
93{
94	int error;
95	vfs_context_t ctx = cnp->cn_context;
96	struct vnode *tdvp;
97	struct vnode *dvp;
98	struct mount *mp;
99
100	dvp = *dvpp;
101
102	/*
103	 * If stepping up the directory tree, check for going
104	 * back across the mount point, in which case do what
105	 * lookup would do by stepping back down the mount
106	 * hierarchy.
107	 */
108	if (cnp->cn_flags & ISDOTDOT) {
109		while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
110			/*
111			 * Don't do the NOCROSSMOUNT check
112			 * at this level.  By definition,
113			 * union fs deals with namespaces, not
114			 * filesystems.
115			 */
116			tdvp = dvp;
117			*dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
118			vnode_put(tdvp);
119			vnode_get(dvp);
120		}
121	}
122
123	error = VNOP_LOOKUP(dvp, &tdvp, cnp, ctx);
124	if (error)
125		return (error);
126
127	dvp = tdvp;
128	/*
129	 * Lastly check if the current node is a mount point in
130	 * which case walk up the mount hierarchy making sure not to
131	 * bump into the root of the mount tree (ie. dvp != udvp).
132	 */
133	while (dvp != udvp && (dvp->v_type == VDIR) &&
134	       (mp = dvp->v_mountedhere)) {
135		if (vfs_busy(mp, LK_NOWAIT)) {
136			vnode_put(dvp);
137			return(ENOENT);
138		}
139		error = VFS_ROOT(mp, &tdvp, ctx);
140		vfs_unbusy(mp);
141		if (error) {
142			vnode_put(dvp);
143			return (error);
144		}
145
146		vnode_put(dvp);
147		dvp = tdvp;
148	}
149
150	*vpp = dvp;
151	return (0);
152}
153
154static int
155union_lookup(struct vnop_lookup_args *ap)
156/*
157	struct vnop_lookup_args {
158		struct vnodeop_desc *a_desc;
159		struct vnode *a_dvp;
160		struct vnode **a_vpp;
161		struct componentname *a_cnp;
162		vfs_context_t a_context;
163	} *ap)
164*/
165{
166	int error;
167	int uerror = 0;
168	int  lerror = 0;
169	struct vnode *uppervp, *lowervp;
170	struct vnode *upperdvp, *lowerdvp;
171	struct vnode *dvp = ap->a_dvp;
172	struct union_node *dun;
173	struct componentname *cnp = ap->a_cnp;
174	vfs_context_t ctx = cnp->cn_context;
175	int lockparent = cnp->cn_flags & LOCKPARENT;
176	struct union_mount *um;
177	kauth_cred_t saved_cred;
178	int iswhiteout;
179	struct vnode_attr va;
180	int isfaultfs = 0;
181	int upperlookup = 0;
182	int retry_count = 0;
183
184#ifdef notyet
185	if (cnp->cn_namelen == 3 &&
186			cnp->cn_nameptr[2] == '.' &&
187			cnp->cn_nameptr[1] == '.' &&
188			cnp->cn_nameptr[0] == '.') {
189		dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
190		if (dvp == NULLVP)
191			return (ENOENT);
192		vnode_get(dvp);
193
194		return (0);
195	}
196#endif
197
198
199
200retry:
201	union_lock();
202	um = MOUNTTOUNIONMOUNT(dvp->v_mount);
203	dun = VTOUNION(dvp);
204	upperdvp = dun->un_uppervp;
205	lowerdvp = dun->un_lowervp;
206	uppervp = NULLVP;
207	lowervp = NULLVP;
208	iswhiteout = 0;
209
210	union_unlock();
211
212	if(UNION_FAULTIN(um))
213		isfaultfs = 1;
214
215	if (isfaultfs == 0)
216		cnp->cn_flags |= LOCKPARENT;
217
218	/*
219	 * do the lookup in the upper level.
220	 * if that level comsumes additional pathnames,
221	 * then assume that something special is going
222	 * on and just return that vnode.
223	 */
224	if (upperdvp != NULLVP) {
225		if (lockparent != 0)
226			cnp->cn_flags &= ~LOCKPARENT;
227		uerror = union_lookup1(um->um_uppervp, &upperdvp,
228					&uppervp, cnp);
229		upperlookup = 1;
230
231		if (cnp->cn_consume != 0) {
232			*ap->a_vpp = uppervp;
233			if (!lockparent)
234				cnp->cn_flags &= ~LOCKPARENT;
235			else
236				cnp->cn_flags |= LOCKPARENT;
237			return (uerror);
238		}
239		if (uerror == ENOENT || uerror == EJUSTRETURN) {
240			if (cnp->cn_flags & ISWHITEOUT) {
241				iswhiteout = 1;
242			} else if (lowerdvp != NULLVP) {
243				VATTR_INIT(&va);
244				VATTR_WANTED(&va, va_flags);
245				lerror = vnode_getattr(upperdvp, &va, ap->a_context);
246				if (lerror == 0 && (va.va_flags & OPAQUE))
247					iswhiteout = 1;
248			}
249		}
250	} else {
251		uerror = ENOENT;
252	}
253
254	/*
255	 * faultingfs: If upper layer lookup is succesful
256	 * we will return that vp if it is regular file.
257	 * So so skip lower level lookup
258	 */
259
260	if ((isfaultfs == 1) && (upperlookup == 1) && (uerror == 0) && ((vnode_isreg(uppervp) != 0)))
261		goto donelowerlookup;
262
263	/*
264	 * in a similar way to the upper layer, do the lookup
265	 * in the lower layer.   this time, if there is some
266	 * component magic going on, then vnode_put whatever we got
267	 * back from the upper layer and return the lower vnode
268	 * instead.
269	 */
270	if (lowerdvp != NULLVP && !iswhiteout) {
271		int nameiop;
272
273		/*
274		 * Only do a LOOKUP on the bottom node, since
275		 * we won't be making changes to it anyway.
276		 */
277		nameiop = cnp->cn_nameiop;
278		cnp->cn_nameiop = LOOKUP;
279		if (um->um_op == UNMNT_BELOW) {
280			/* XXX BOGUS */
281			saved_cred = cnp->cn_context->vc_ucred;
282			cnp->cn_context->vc_ucred = um->um_cred;
283			if (lockparent != 0)
284				cnp->cn_flags &= ~LOCKPARENT;
285			lerror = union_lookup1(um->um_lowervp, &lowerdvp,
286					&lowervp, cnp);
287			cnp->cn_context->vc_ucred = saved_cred;
288		} else {
289			if (lockparent != 0)
290				cnp->cn_flags &= ~LOCKPARENT;
291			lerror = union_lookup1(um->um_lowervp, &lowerdvp,
292					&lowervp, cnp);
293		}
294		cnp->cn_nameiop = nameiop;
295
296		if (cnp->cn_consume != 0) {
297			if (uppervp != NULLVP) {
298			        vnode_put(uppervp);
299				uppervp = NULLVP;
300			}
301			*ap->a_vpp = lowervp;
302			if (!lockparent)
303				cnp->cn_flags &= ~LOCKPARENT;
304			else
305				cnp->cn_flags |= LOCKPARENT;
306			return (lerror);
307		}
308	} else {
309		lerror = ENOENT;
310		if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
311			lowervp = LOWERVP(dun->un_pvp);
312			if (lowervp != NULLVP) {
313				lerror = 0;
314			}
315		}
316	}
317
318donelowerlookup:
319
320	if (!lockparent)
321		cnp->cn_flags &= ~LOCKPARENT;
322
323	/*
324	 * at this point, we have uerror and lerror indicating
325	 * possible errors with the lookups in the upper and lower
326	 * layers.  additionally, uppervp and lowervp are (locked)
327	 * references to existing vnodes in the upper and lower layers.
328	 *
329	 * there are now three cases to consider.
330	 * 1. if both layers returned an error, then return whatever
331	 *    error the upper layer generated.
332	 *
333	 * 2. if the top layer failed and the bottom layer succeeded
334	 *    then two subcases occur.
335	 *    a.  the bottom vnode is not a directory, in which
336	 *	  case just return a new union vnode referencing
337	 *	  an empty top layer and the existing bottom layer.
338	 *    b.  the bottom vnode is a directory, in which case
339	 *	  create a new directory in the top-level and
340	 *	  continue as in case 3.
341	 *
342	 * 3. if the top layer succeeded then return a new union
343	 *    vnode referencing whatever the new top layer and
344	 *    whatever the bottom layer returned.
345	 */
346
347	*ap->a_vpp = NULLVP;
348
349	/* case 1. */
350	if ((uerror != 0) && (lerror != 0)) {
351			if (!lockparent)
352				cnp->cn_flags &= ~LOCKPARENT;
353			else
354				cnp->cn_flags |= LOCKPARENT;
355		return (uerror);
356	}
357
358	/* case 2. */
359	if (uerror != 0 /* && (lerror == 0) */ ) {
360		if (lowervp->v_type == VDIR) { /* case 2b. */
361			/* No need to lock the union here */
362			/* if the vnode exists it returns it even if it marks error */
363
364			uppervp = NULLVP;
365
366			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
367
368			if ((uerror == EEXIST)){
369				if (uppervp == NULLVP) {
370					retry_count++;
371					if (retry_count <= 2) {
372						if (lowervp != NULLVP)
373							vnode_put(lowervp);
374						goto retry;
375					}
376				}
377				uerror = 0;
378			}
379
380			if (uerror) {
381				if (uppervp != NULLVP) {
382					vnode_put(uppervp);
383				}
384				if (lowervp != NULLVP) {
385					vnode_put(lowervp);
386				}
387				if (!lockparent)
388					cnp->cn_flags &= ~LOCKPARENT;
389				else
390					cnp->cn_flags |= LOCKPARENT;
391				return (uerror);
392			}
393		} else if ((lowervp->v_type == VREG) && (isfaultfs == 1)) {
394			error = union_faultin_copyup(&uppervp, upperdvp, lowervp, cnp, ctx);
395			uerror = 0;
396		}
397	}
398
399
400	/* if this is faulting filesystem and upper vp exisits skip allocation of union node */
401	if ((isfaultfs == 1) && (uerror == 0) && (uppervp != NULLVP) &&  ((vnode_isreg(uppervp) != 0)|| (vnode_islnk(uppervp) != 0))) {
402		vn_checkunionwait(uppervp);
403		*ap->a_vpp = uppervp;
404		if (lowervp != NULLVP)
405			vnode_put(lowervp);
406		if (!lockparent)
407			cnp->cn_flags &= ~LOCKPARENT;
408		else
409			cnp->cn_flags |= LOCKPARENT;
410		return(0);
411	}
412
413	union_lock();
414	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
415			      uppervp, lowervp, 1);
416	union_unlock();
417
418	if (error) {
419		if (uppervp != NULLVP)
420			vnode_put(uppervp);
421		if (lowervp != NULLVP)
422			vnode_put(lowervp);
423	}
424
425	if (!lockparent)
426		cnp->cn_flags &= ~LOCKPARENT;
427	else
428		cnp->cn_flags |= LOCKPARENT;
429	return (error);
430}
431
432static int
433union_create(struct vnop_create_args *ap)
434/*
435	struct vnop_create_args {
436		struct vnode *a_dvp;
437		struct vnode **a_vpp;
438		struct componentname *a_cnp;
439		struct vnode_attr *a_vap;
440		vfs_context_t a_context;
441	} *ap;
442*/
443{
444	struct union_node *un = VTOUNION(ap->a_dvp);
445	struct vnode *dvp = un->un_uppervp;
446	struct componentname *cnp = ap->a_cnp;
447
448	if (dvp != NULLVP) {
449		int error;
450		struct vnode *vp;
451		struct mount *mp;
452
453
454		mp = ap->a_dvp->v_mount;
455
456		/* note that this is a direct passthrough to the filesystem */
457		error = VNOP_CREATE(dvp, &vp, cnp, ap->a_vap, ap->a_context);
458		if (error)
459			return (error);
460
461		/* if this is faulting filesystem and is a reg file, skip allocation of union node */
462		if (UNNODE_FAULTIN(un) && (vp != NULLVP) && ((vnode_isreg(vp) != 0)|| (vnode_islnk(vp) != 0))) {
463			*ap->a_vpp = vp;
464			return(0);
465		}
466
467
468		union_lock();
469		error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
470				NULLVP, 1);
471		union_unlock();
472		if (error)
473			vnode_put(vp);
474		return (error);
475	}
476
477	return (EROFS);
478}
479
480static int
481union_whiteout(struct vnop_whiteout_args *ap)
482/*
483	struct vnop_whiteout_args {
484		struct vnode *a_dvp;
485		struct componentname *a_cnp;
486		int a_flags;
487		vfs_context_t a_context;
488	} *ap;
489*/
490{
491	struct union_node *un = VTOUNION(ap->a_dvp);
492	struct componentname *cnp = ap->a_cnp;
493	int error;
494
495	if (un->un_uppervp == NULLVP) {
496		return (ENOTSUP);
497	}
498
499	error =  (VNOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags, ap->a_context));
500	return(error);
501}
502
503/* mknod can do  fifos, chr, blk or whiteout entries */
504static int
505union_mknod(struct vnop_mknod_args *ap)
506/*
507	struct vnop_mknod_args {
508		struct vnode *a_dvp;
509		struct vnode **a_vpp;
510		struct componentname *a_cnp;
511		struct vnode_attr *a_vap;
512		vfs_context_t a_context;
513	} *ap;
514*/
515{
516	struct union_node *un = VTOUNION(ap->a_dvp);
517	struct vnode *dvp = un->un_uppervp;
518	struct componentname *cnp = ap->a_cnp;
519
520	if (dvp != NULLVP) {
521		int error;
522		struct vnode *vp;
523		struct mount *mp;
524
525
526		mp = ap->a_dvp->v_mount;
527
528		/* note that this is a direct passthrough to the filesystem */
529		error = VNOP_MKNOD(dvp, &vp, cnp, ap->a_vap, ap->a_context);
530		if (error)
531			return (error);
532
533		if (vp != NULLVP) {
534			union_lock();
535			error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
536					cnp, vp, NULLVP, 1);
537			union_unlock();
538			if (error)
539				vnode_put(vp);
540		}
541		return (error);
542	}
543	return (EROFS);
544}
545
546static int
547union_open(struct vnop_open_args *ap)
548/*
549	struct vnop_open_args {
550		struct vnodeop_desc *a_desc;
551		struct vnode *a_vp;
552		int a_mode;
553		vfs_context_t a_context;
554	} *ap;
555*/
556{
557	struct union_node *un = VTOUNION(ap->a_vp);
558	struct vnode *tvp;
559	int mode = ap->a_mode;
560	int error;
561
562	/*
563	 * If there is an existing upper vp then simply open that.
564	 */
565
566	tvp = un->un_uppervp;
567	if (tvp == NULLVP) {
568
569		/*
570		 * If the lower vnode is being opened for writing, then
571		 * copy the file contents to the upper vnode and open that,
572		 * otherwise can simply open the lower vnode.
573		 */
574		tvp = un->un_lowervp;
575		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
576			/* For above below mounts we need draining.. */
577			/* This path is not taken for faultin mode */
578			/*  LOCK the union node as well **/
579			union_lock();
580			un->un_flags |= UN_LOCKED;
581
582			error = union_copyup(un, (mode&O_TRUNC) == 0, ap->a_context);
583			un->un_flags &= ~UN_LOCKED;
584			if ((un->un_flags & UN_WANT) == UN_WANT) {
585				un->un_flags &=  ~UN_WANT;
586				wakeup(&un->un_flags);
587			}
588			union_unlock();
589			if (error == 0)
590				error = VNOP_OPEN(un->un_uppervp, mode, ap->a_context);
591			return (error);
592		}
593
594		/*
595		 * Just open the lower vnode
596		 */
597		un->un_openl++;
598
599		error = VNOP_OPEN(tvp, mode, ap->a_context);
600
601		return (error);
602	}
603
604	error = VNOP_OPEN(tvp, mode, ap->a_context);
605
606	return (error);
607}
608
609static int
610union_close(struct vnop_close_args *ap)
611/*
612	struct vnop_close_args {
613		struct vnode *a_vp;
614		int  a_fflag;
615		vfs_context_t a_context;
616	} *ap;
617*/
618{
619	struct union_node *un = VTOUNION(ap->a_vp);
620	struct vnode *vp;
621	int error = 0;
622
623	if ((vp = un->un_uppervp) == NULLVP) {
624#ifdef UNION_DIAGNOSTIC
625		if (un->un_openl <= 0)
626			panic("union: un_openl cnt");
627#endif
628		--un->un_openl;
629		vp = un->un_lowervp;
630	}
631
632	ap->a_vp = vp;
633	error =  (VCALL(vp, VOFFSET(vnop_close), ap));
634	return(error);
635}
636
637/*
638 * Check access permission on the union vnode.
639 * The access check being enforced is to check
640 * against both the underlying vnode, and any
641 * copied vnode.  This ensures that no additional
642 * file permissions are given away simply because
643 * the user caused an implicit file copy.
644 */
645static int
646union_access(struct vnop_access_args *ap)
647/*
648	struct vnop_access_args {
649		struct vnodeop_desc *a_desc;
650		struct vnode *a_vp;
651		int a_action;
652		vfs_context_t a_context;
653	} *ap;
654*/
655{
656	struct union_node *un = VTOUNION(ap->a_vp);
657	int error = EACCES;
658	struct vnode *vp;
659
660	if ((vp = un->un_uppervp) != NULLVP) {
661		ap->a_vp = vp;
662		return (VCALL(vp, VOFFSET(vnop_access), ap));
663	}
664
665	if ((vp = un->un_lowervp) != NULLVP) {
666		ap->a_vp = vp;
667		error = VCALL(vp, VOFFSET(vnop_access), ap);
668		if (error == 0) {
669			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
670
671			if (um->um_op == UNMNT_BELOW) {
672				error = VCALL(vp, VOFFSET(vnop_access), ap);
673			}
674		}
675		if (error)
676			return (error);
677	}
678
679	return (error);
680}
681
682/*
683 * We handle getattr only to change the fsid and
684 * track object sizes
685 */
686static int
687union_getattr(struct vnop_getattr_args *ap)
688/*
689	struct vnop_getattr_args {
690		struct vnode *a_vp;
691		struct vnode_attr *a_vap;
692		vfs_context_t a_context;
693	} *ap;
694*/
695{
696	int error=0;
697	struct union_node *un = VTOUNION(ap->a_vp);
698	struct vnode *vp = un->un_uppervp;
699	struct vnode_attr *vap;
700	struct vnode_attr va;
701
702
703	/*
704	 * Some programs walk the filesystem hierarchy by counting
705	 * links to directories to avoid stat'ing all the time.
706	 * This means the link count on directories needs to be "correct".
707	 * The only way to do that is to call getattr on both layers
708	 * and fix up the link count.  The link count will not necessarily
709	 * be accurate but will be large enough to defeat the tree walkers.
710	 */
711
712	vap = ap->a_vap;
713
714	vp = un->un_uppervp;
715	if (vp != NULLVP) {
716		/*
717		 * It's not clear whether vnop_getattr is to be
718		 * called with the vnode locked or not.  stat() calls
719		 * it with (vp) locked, and fstat calls it with
720		 * (vp) unlocked.
721		 * In the mean time, compensate here by checking
722		 * the union_node's lock flag.
723		 */
724
725		error = vnode_getattr(vp, vap, ap->a_context);
726		if (error) {
727			return (error);
728		}
729		union_lock();
730		union_newsize(ap->a_vp, vap->va_data_size, VNOVAL);
731		union_unlock();
732	}
733
734	if (vp == NULLVP) {
735		vp = un->un_lowervp;
736	} else if (vp->v_type == VDIR) {
737		vp = un->un_lowervp;
738		VATTR_INIT(&va);
739		/* all we want from the lower node is the link count */
740		VATTR_WANTED(&va, va_nlink);
741		vap = &va;
742	} else {
743		vp = NULLVP;
744	}
745
746	if (vp != NULLVP) {
747		error = vnode_getattr(vp, vap, ap->a_context);
748		if (error) {
749			return (error);
750		}
751		union_lock();
752		union_newsize(ap->a_vp, VNOVAL, vap->va_data_size);
753		union_unlock();
754	}
755
756	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
757		ap->a_vap->va_nlink += vap->va_nlink;
758
759	VATTR_RETURN(ap->a_vap, va_fsid, ap->a_vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
760	return (0);
761}
762
763static int
764union_setattr(struct vnop_setattr_args *ap)
765/*
766	struct vnop_setattr_args {
767		struct vnode *a_vp;
768		struct vnode_attr *a_vap;
769		vfs_context_t a_context;
770	} *ap;
771*/
772{
773	struct union_node *un = VTOUNION(ap->a_vp);
774	int error;
775
776	/*
777	 * Handle case of truncating lower object to zero size,
778	 * by creating a zero length upper object.  This is to
779	 * handle the case of open with O_TRUNC and O_CREAT.
780	 */
781	if (VATTR_IS_ACTIVE(ap->a_vap, va_data_size) &&
782	    (un->un_uppervp == NULLVP) &&
783	    /* assert(un->un_lowervp != NULLVP) */
784	    (un->un_lowervp->v_type == VREG)) {
785		union_lock();
786		error = union_copyup(un, (ap->a_vap->va_data_size != 0), ap->a_context);
787		union_unlock();
788		if (error) {
789			return (error);
790		}
791	}
792
793	/*
794	 * Try to set attributes in upper layer,
795	 * otherwise return read-only filesystem error.
796	 */
797	if (un->un_uppervp != NULLVP) {
798		error = vnode_setattr(un->un_uppervp, ap->a_vap, ap->a_context);
799		if ((error == 0) && VATTR_IS_ACTIVE(ap->a_vap, va_data_size)) {
800			union_lock();
801			union_newsize(ap->a_vp, ap->a_vap->va_data_size, VNOVAL);
802			union_unlock();
803		}
804	} else {
805		error = EROFS;
806	}
807
808	return (error);
809}
810
811static int
812union_read(struct vnop_read_args *ap)
813/*
814	struct vnop_read_args {
815		struct vnode *a_vp;
816		struct uio *a_uio;
817		int  a_ioflag;
818		vfs_context_t a_context;
819	} *ap;
820*/
821{
822	int error;
823	struct vnode *vp = OTHERVP(ap->a_vp);
824
825	error = VNOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_context);
826
827	/*
828	 * XXX
829	 * perhaps the size of the underlying object has changed under
830	 * our feet.  take advantage of the offset information present
831	 * in the uio structure.
832	 */
833	if (error == 0) {
834		struct union_node *un = VTOUNION(ap->a_vp);
835		off_t cur = ap->a_uio->uio_offset;
836
837		if (vp == un->un_uppervp) {
838			if (cur > un->un_uppersz) {
839				union_lock();
840				union_newsize(ap->a_vp, cur, VNOVAL);
841				union_unlock();
842			}
843		} else {
844			if (cur > un->un_lowersz) {
845				union_lock();
846				union_newsize(ap->a_vp, VNOVAL, cur);
847				union_unlock();
848			}
849		}
850	}
851
852	return (error);
853}
854
855static int
856union_write(struct vnop_write_args *ap)
857/*
858	struct vnop_write_args {
859		struct vnode *a_vp;
860		struct uio *a_uio;
861		int  a_ioflag;
862		vfs_context_t a_context;
863	} *ap;
864*/
865{
866	int error;
867	struct vnode *vp;
868	struct union_node *un = VTOUNION(ap->a_vp);
869
870	vp = UPPERVP(ap->a_vp);
871	if (vp == NULLVP)
872		panic("union: missing upper layer in write");
873
874	error = VNOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_context);
875
876	/*
877	 * the size of the underlying object may be changed by the
878	 * write.
879	 */
880	if (error == 0) {
881		off_t cur = ap->a_uio->uio_offset;
882
883		if (cur > un->un_uppersz) {
884			union_lock();
885			union_newsize(ap->a_vp, cur, VNOVAL);
886			union_unlock();
887		}
888	}
889
890	return (error);
891}
892
893
894static int
895union_ioctl(struct vnop_ioctl_args *ap)
896/*
897	struct vnop_ioctl_args {
898		struct vnode *a_vp;
899		int  a_command;
900		caddr_t  a_data;
901		int  a_fflag;
902		vfs_context_t a_context;
903	} *ap;
904*/
905{
906	register struct vnode *ovp = OTHERVP(ap->a_vp);
907
908	ap->a_vp = ovp;
909	return (VCALL(ovp, VOFFSET(vnop_ioctl), ap));
910}
911
912static int
913union_select(struct vnop_select_args *ap)
914/*
915	struct vnop_select_args {
916		struct vnode *a_vp;
917		int  a_which;
918		int  a_fflags;
919		void * a_wql;
920		vfs_context_t a_context;
921	} *ap;
922*/
923{
924	register struct vnode *ovp = OTHERVP(ap->a_vp);
925
926	ap->a_vp = ovp;
927	return (VCALL(ovp, VOFFSET(vnop_select), ap));
928}
929
930static int
931union_revoke(struct vnop_revoke_args *ap)
932/*
933	struct vnop_revoke_args {
934		struct vnode *a_vp;
935		int a_flags;
936		vfs_context_t a_context;
937	} *ap;
938*/
939{
940	struct vnode *vp = ap->a_vp;
941
942	if (UPPERVP(vp))
943		VNOP_REVOKE(UPPERVP(vp), ap->a_flags, ap->a_context);
944	if (LOWERVP(vp))
945		VNOP_REVOKE(LOWERVP(vp), ap->a_flags, ap->a_context);
946	vnode_reclaim(vp);
947
948	return (0);
949}
950
951static int
952union_mmap(struct vnop_mmap_args *ap)
953/*
954	struct vnop_mmap_args {
955		struct vnode *a_vp;
956		int  a_fflags;
957		kauth_cred_t a_cred;
958		struct proc *a_p;
959	} *ap;
960*/
961{
962	register struct vnode *ovp = OTHERVP(ap->a_vp);
963
964	ap->a_vp = ovp;
965	return (VCALL(ovp, VOFFSET(vnop_mmap), ap));
966}
967
968static int
969union_mnomap(struct vnop_mnomap_args *ap)
970/*
971	struct vnop_mnomap_args {
972		struct vnode *a_vp;
973		int  a_fflags;
974		kauth_cred_t a_cred;
975		struct proc *a_p;
976	} *ap;
977*/
978{
979	register struct vnode *ovp = OTHERVP(ap->a_vp);
980
981	ap->a_vp = ovp;
982	return (VCALL(ovp, VOFFSET(vnop_mnomap), ap));
983}
984
985static int
986union_fsync(struct vnop_fsync_args *ap)
987/*
988	struct vnop_fsync_args {
989		struct vnode *a_vp;
990		int  a_waitfor;
991		vfs_context_t a_context;
992	} *ap;
993*/
994{
995	int error = 0;
996	struct vnode *targetvp = OTHERVP(ap->a_vp);
997
998	if (targetvp != NULLVP) {
999
1000		error = VNOP_FSYNC(targetvp, ap->a_waitfor, ap->a_context);
1001	}
1002
1003	return (error);
1004}
1005
1006static int
1007union_remove(struct vnop_remove_args *ap)
1008/*
1009	struct vnop_remove_args {
1010		struct vnode *a_dvp;
1011		struct vnode *a_vp;
1012		struct componentname *a_cnp;
1013		vfs_context_t a_context;
1014	} *ap;
1015*/
1016{
1017	int error, flags;
1018	struct union_node *dun = VTOUNION(ap->a_dvp);
1019	struct union_node *un = VTOUNION(ap->a_vp);
1020	struct componentname *cnp = ap->a_cnp;
1021	int busydel = 0;
1022
1023	if (dun->un_uppervp == NULLVP)
1024		panic("union remove: null upper vnode");
1025
1026	if (UNNODE_FAULTIN(dun) && ((ap->a_vp != NULLVP) &&
1027		((vnode_isreg(ap->a_vp) != 0) || (vnode_islnk(ap->a_vp) != 0)))) {
1028			return(VNOP_REMOVE(dun->un_uppervp, ap->a_vp, ap->a_cnp, ap->a_flags, ap->a_context));
1029	}
1030
1031	if (un->un_uppervp != NULLVP) {
1032		struct vnode *dvp = dun->un_uppervp;
1033		struct vnode *vp = un->un_uppervp;
1034
1035		flags = ap->a_flags;
1036		if (vnode_isinuse(ap->a_vp, 0))
1037			busydel = 1;
1038		if ((flags & VNODE_REMOVE_NODELETEBUSY) && (busydel != 0)) {
1039				return(EBUSY);
1040		}
1041		if (union_dowhiteout(un, cnp->cn_context))
1042			cnp->cn_flags |= DOWHITEOUT;
1043
1044		if (busydel != 0)  {
1045			union_lock();
1046			un->un_flags |= UN_DELETED;
1047			if (un->un_flags & UN_CACHED) {
1048				un->un_flags &= ~UN_CACHED;
1049				LIST_REMOVE(un, un_cache);
1050			}
1051			union_unlock();
1052			vnode_ref(vp);
1053		}
1054		error = VNOP_REMOVE(dvp, vp, cnp, 0, ap->a_context);
1055		if (!error) {
1056			union_lock();
1057			if (busydel == 0)
1058				union_removed_upper(un);
1059			union_unlock();
1060		}
1061	} else {
1062		if (UNNODE_FAULTIN(un))
1063			panic("faultfs: No uppervp");
1064		error = union_mkwhiteout(
1065			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
1066			dun->un_uppervp, ap->a_cnp, un->un_path);
1067	}
1068
1069	return (error);
1070}
1071
1072static int
1073union_link(struct vnop_link_args *ap)
1074/*
1075	struct vnop_link_args {
1076		struct vnode *a_vp;
1077		struct vnode *a_tdvp;
1078		struct componentname *a_cnp;
1079		vfs_context_t a_context;
1080	} *ap;
1081*/
1082{
1083	int error = 0;
1084	struct componentname *cnp = ap->a_cnp;
1085	struct union_node *un;
1086	struct vnode *vp;
1087	struct vnode *tdvp;
1088
1089	un = VTOUNION(ap->a_tdvp);
1090
1091	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
1092		vp = ap->a_vp;
1093	} else {
1094		struct union_node *tun = VTOUNION(ap->a_vp);
1095		if (tun->un_uppervp == NULLVP) {
1096			if (UNNODE_FAULTIN(tun))
1097				panic("faultfs: No uppervp");
1098			if (un->un_uppervp == tun->un_dirvp) {
1099			}
1100			union_lock();
1101			/* Would need to drain for above,below mount and faulin does not enter this path */
1102			un->un_flags |= UN_LOCKED;
1103			error = union_copyup(tun, 1, ap->a_context);
1104			un->un_flags &= ~UN_LOCKED;
1105			if ((un->un_flags & UN_WANT) == UN_WANT) {
1106				un->un_flags &=  ~UN_WANT;
1107				wakeup(&un->un_flags);
1108			}
1109			union_unlock();
1110		}
1111		vp = tun->un_uppervp;
1112	}
1113	tdvp = un->un_uppervp;
1114	if (tdvp == NULLVP)
1115		error = EROFS;
1116
1117	if (error) {
1118		return (error);
1119	}
1120
1121
1122	error =  (VNOP_LINK(vp, tdvp, cnp, ap->a_context));
1123	return(error);
1124}
1125
1126static int
1127union_rename(struct vnop_rename_args *ap)
1128/*
1129	struct vnop_rename_args {
1130		struct vnode *a_fdvp;
1131		struct vnode *a_fvp;
1132		struct componentname *a_fcnp;
1133		struct vnode *a_tdvp;
1134		struct vnode *a_tvp;
1135		struct componentname *a_tcnp;
1136		vfs_context_t a_context;
1137	} *ap;
1138*/
1139{
1140	int error;
1141
1142	struct vnode *fdvp = ap->a_fdvp;
1143	struct vnode *fvp = ap->a_fvp;
1144	struct vnode *tdvp = ap->a_tdvp;
1145	struct vnode *tvp = ap->a_tvp;
1146
1147
1148	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
1149		struct union_node *un = VTOUNION(fdvp);
1150		if (un->un_uppervp == NULLVP) {
1151			if (UNNODE_FAULTIN(un))
1152				panic("faultfs rename: No uppervp");
1153			/*
1154			 * this should never happen in normal
1155			 * operation but might if there was
1156			 * a problem creating the top-level shadow
1157			 * directory.
1158			 */
1159			error = EXDEV;
1160			goto bad;
1161		}
1162
1163		fdvp = un->un_uppervp;
1164	}
1165
1166	if (fvp->v_op == union_vnodeop_p) {	/* always true */
1167		struct union_node *un = VTOUNION(fvp);
1168		if (un->un_uppervp == NULLVP) {
1169			if (UNNODE_FAULTIN(un))
1170				panic("faultfs rename: No uppervp");
1171			/* XXX: should do a copyup */
1172			error = EXDEV;
1173			goto bad;
1174		}
1175
1176		if (un->un_lowervp != NULLVP)
1177			ap->a_fcnp->cn_flags |= DOWHITEOUT;
1178
1179		fvp = un->un_uppervp;
1180	}
1181
1182	if (tdvp->v_op == union_vnodeop_p) {
1183		struct union_node *un = VTOUNION(tdvp);
1184		if (un->un_uppervp == NULLVP) {
1185			/*
1186			 * this should never happen in normal
1187			 * operation but might if there was
1188			 * a problem creating the top-level shadow
1189			 * directory.
1190			 */
1191			if (UNNODE_FAULTIN(un))
1192				panic("faultfs rename: No uppervp");
1193			error = EXDEV;
1194			goto bad;
1195		}
1196
1197		tdvp = un->un_uppervp;
1198	}
1199
1200	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
1201		struct union_node *un = VTOUNION(tvp);
1202
1203		tvp = un->un_uppervp;
1204	}
1205
1206	return (VNOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp, ap->a_context));
1207
1208bad:
1209	return (error);
1210}
1211
1212static int
1213union_mkdir(struct vnop_mkdir_args *ap)
1214/*
1215	struct vnop_mkdir_args {
1216		struct vnode *a_dvp;
1217		struct vnode **a_vpp;
1218		struct componentname *a_cnp;
1219		struct vnode_attr *a_vap;
1220		vfs_context_t a_context;
1221	} *ap;
1222*/
1223{
1224	struct union_node *un = VTOUNION(ap->a_dvp);
1225	struct vnode *dvp = un->un_uppervp;
1226	struct componentname *cnp = ap->a_cnp;
1227
1228	if (dvp != NULLVP) {
1229		int error;
1230		struct vnode *vp;
1231
1232
1233		/* note that this is a direct fallthrough to the filesystem */
1234		error = VNOP_MKDIR(dvp, &vp, cnp, ap->a_vap, ap->a_context);
1235		if (error)
1236			return (error);
1237
1238		union_lock();
1239		error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
1240				NULLVP, cnp, vp, NULLVP, 1);
1241		union_unlock();
1242		if (error)
1243			vnode_put(vp);
1244		return (error);
1245	}
1246	return (EROFS);
1247}
1248
1249static int
1250union_rmdir(struct vnop_rmdir_args *ap)
1251/*
1252	struct vnop_rmdir_args {
1253		struct vnode *a_dvp;
1254		struct vnode *a_vp;
1255		struct componentname *a_cnp;
1256		vfs_context_t a_context;
1257	} *ap;
1258*/
1259{
1260	int error;
1261	struct union_node *dun = VTOUNION(ap->a_dvp);
1262	struct union_node *un = VTOUNION(ap->a_vp);
1263	struct componentname *cnp = ap->a_cnp;
1264	int busydel = 0;
1265
1266	/******* NODE HAS TO BE LOCKED ******/
1267	if (dun->un_uppervp == NULLVP)
1268		panic("union rmdir: null upper vnode");
1269
1270	if (un->un_uppervp != NULLVP) {
1271		struct vnode *dvp = dun->un_uppervp;
1272		struct vnode *vp = un->un_uppervp;
1273
1274		if (vnode_isinuse(ap->a_vp, 0)) {
1275			busydel = 1;
1276			union_lock();
1277			un->un_flags |= UN_DELETED;
1278			if (un->un_flags & UN_CACHED) {
1279				un->un_flags &= ~UN_CACHED;
1280				LIST_REMOVE(un, un_cache);
1281			}
1282			union_unlock();
1283			vnode_ref(vp);
1284		}
1285
1286
1287		if (union_dowhiteout(un, cnp->cn_context))
1288			cnp->cn_flags |= DOWHITEOUT;
1289		error = VNOP_RMDIR(dvp, vp, ap->a_cnp, ap->a_context);
1290		if (!error) {
1291			union_lock();
1292			if (busydel == 0)
1293				union_removed_upper(un);
1294			union_unlock();
1295		}
1296	} else {
1297		if (UNNODE_FAULTIN(un))
1298			panic("faultfs: No uppervp");
1299		error = union_mkwhiteout(
1300			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
1301			dun->un_uppervp, ap->a_cnp, un->un_path);
1302	}
1303	return (error);
1304}
1305
1306static int
1307union_symlink(struct vnop_symlink_args *ap)
1308/*
1309	struct vnop_symlink_args {
1310		struct vnode *a_dvp;
1311		struct vnode **a_vpp;
1312		struct componentname *a_cnp;
1313		struct vnode_attr *a_vap;
1314		char *a_target;
1315		vfs_context_t a_context;
1316	} *ap;
1317*/
1318{
1319	struct union_node *un = VTOUNION(ap->a_dvp);
1320	struct vnode *dvp = un->un_uppervp;
1321	struct componentname *cnp = ap->a_cnp;
1322
1323	if (dvp != NULLVP) {
1324		int error;
1325		struct vnode *vp;
1326
1327		error = VNOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target, ap->a_context);
1328		*ap->a_vpp = vp;
1329		return (error);
1330	}
1331	return (EROFS);
1332}
1333
1334/*
1335 * union_readdir works in concert with getdirentries and
1336 * readdir(3) to provide a list of entries in the unioned
1337 * directories.  getdirentries is responsible for walking
1338 * down the union stack.  readdir(3) is responsible for
1339 * eliminating duplicate names from the returned data stream.
1340 */
1341static int
1342union_readdir(struct vnop_readdir_args *ap)
1343/*
1344	struct vnop_readdir_args {
1345		struct vnodeop_desc *a_desc;
1346		struct vnode *a_vp;
1347		struct uio *a_uio;
1348		int a_flags;
1349		int *a_eofflag;
1350		int *a_numdirent;
1351		vfs_context_t a_context;
1352	} *ap;
1353*/
1354{
1355	struct union_node *un = VTOUNION(ap->a_vp);
1356	struct vnode *uvp = un->un_uppervp;
1357
1358	if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
1359		return (EINVAL);
1360
1361	if (uvp == NULLVP)
1362		return (0);
1363
1364	ap->a_vp = uvp;
1365	return (VCALL(uvp, VOFFSET(vnop_readdir), ap));
1366}
1367
1368static int
1369union_readlink(struct vnop_readlink_args *ap)
1370/*
1371	struct vnop_readlink_args {
1372		struct vnode *a_vp;
1373		struct uio *a_uio;
1374		vfs_context_t a_context;
1375	} *ap;
1376*/
1377{
1378	int error;
1379	struct vnode *vp = OTHERVP(ap->a_vp);
1380
1381	ap->a_vp = vp;
1382	error = VCALL(vp, VOFFSET(vnop_readlink), ap);
1383
1384	return (error);
1385}
1386
1387static int
1388union_inactive(struct vnop_inactive_args *ap)
1389/*
1390	struct vnop_inactive_args {
1391		struct vnode *a_vp;
1392		vfs_context_t a_context;
1393	} *ap;
1394*/
1395{
1396	struct vnode *vp = ap->a_vp;
1397	struct union_node *un = VTOUNION(vp);
1398
1399	/*
1400	 * Do nothing (and _don't_ bypass).
1401	 * Wait to vnode_put lowervp until reclaim,
1402	 * so that until then our union_node is in the
1403	 * cache and reusable.
1404	 *
1405	 * NEEDSWORK: Someday, consider inactive'ing
1406	 * the lowervp and then trying to reactivate it
1407	 * with capabilities (v_id)
1408	 * like they do in the name lookup cache code.
1409	 * That's too much work for now.
1410	 */
1411
1412	union_lock();
1413	if (un->un_flags & UN_DELETED) {
1414		if(un->un_uppervp != NULLVP) {
1415			vnode_rele(un->un_uppervp);
1416		}
1417		union_removed_upper(un);
1418	}
1419
1420	if (un->un_dircache != 0)  {
1421			union_dircache_free(un);
1422	}
1423	if (un->un_flags & UN_DIRENVN) {
1424		vnode_recycle(vp);
1425	}
1426
1427	union_unlock();
1428
1429	return (0);
1430}
1431
1432static int
1433union_reclaim(struct vnop_reclaim_args *ap)
1434/*
1435	struct vnop_reclaim_args {
1436		struct vnode *a_vp;
1437		vfs_context_t a_context;
1438	} *ap;
1439*/
1440{
1441
1442	union_lock();
1443	union_freevp(ap->a_vp);
1444	union_unlock();
1445
1446	return (0);
1447}
1448
1449static int
1450union_blockmap(struct vnop_blockmap_args *ap)
1451/*
1452	struct vnop_blockmap_args {
1453		struct vnode *a_vp;
1454		off_t a_offset;
1455		size_t a_size;
1456		daddr64_t *a_bpn;
1457		size_t *a_run;
1458		void *a_poff;
1459		int a_flags;
1460	} *ap;
1461*/
1462{
1463	int error;
1464	struct vnode *vp = OTHERVP(ap->a_vp);
1465
1466	ap->a_vp = vp;
1467	error = VCALL(vp, VOFFSET(vnop_blockmap), ap);
1468
1469	return (error);
1470}
1471
1472static int
1473union_pathconf(struct vnop_pathconf_args *ap)
1474/*
1475	struct vnop_pathconf_args {
1476		struct vnode *a_vp;
1477		int a_name;
1478		int *a_retval;
1479		vfs_context_t a_context;
1480	} *ap;
1481*/
1482{
1483	int error;
1484	struct vnode *vp = OTHERVP(ap->a_vp);
1485
1486	ap->a_vp = vp;
1487	error = VCALL(vp, VOFFSET(vnop_pathconf), ap);
1488
1489	return (error);
1490}
1491
1492static int
1493union_advlock(struct vnop_advlock_args *ap)
1494/*
1495	struct vnop_advlock_args {
1496		struct vnode *a_vp;
1497		caddr_t  a_id;
1498		int  a_op;
1499		struct flock *a_fl;
1500		int  a_flags;
1501		vfs_context_t a_context;
1502	} *ap;
1503*/
1504{
1505	register struct vnode *ovp = OTHERVP(ap->a_vp);
1506
1507	ap->a_vp = ovp;
1508	return (VCALL(ovp, VOFFSET(vnop_advlock), ap));
1509}
1510
1511
1512/*
1513 * XXX - vnop_strategy must be hand coded because it has no
1514 * vnode in its arguments.
1515 * This goes away with a merged VM/buffer cache.
1516 */
1517static int
1518union_strategy(struct vnop_strategy_args *ap)
1519/*
1520	struct vnop_strategy_args {
1521		struct buf *a_bp;
1522	} *ap;
1523*/
1524{
1525	struct buf *bp = ap->a_bp;
1526	int error;
1527	struct vnode *savedvp;
1528
1529	savedvp = buf_vnode(bp);
1530	buf_setvnode(bp, OTHERVP(savedvp));
1531
1532#if DIAGNOSTIC
1533	if (buf_vnode(bp) == NULLVP)
1534		panic("union_strategy: nil vp");
1535	if (((buf_flags(bp) & B_READ) == 0) &&
1536	    (buf_vnode(bp) == LOWERVP(savedvp)))
1537		panic("union_strategy: writing to lowervp");
1538#endif
1539
1540	error = VNOP_STRATEGY(bp);
1541	buf_setvnode(bp, savedvp);
1542
1543	return (error);
1544}
1545
1546/* Pagein */
1547static int
1548union_pagein(struct vnop_pagein_args *ap)
1549/*
1550	struct vnop_pagein_args {
1551	   	struct vnode 	*a_vp,
1552	   	upl_t		a_pl,
1553		vm_offset_t	a_pl_offset,
1554		off_t		a_f_offset,
1555		size_t		a_size,
1556		int		a_flags
1557		vfs_context_t	a_context;
1558	} *ap;
1559*/
1560{
1561	int error;
1562	struct vnode *vp = OTHERVP(ap->a_vp);
1563
1564	error = VNOP_PAGEIN(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1565	                    ap->a_size, ap->a_flags, ap->a_context);
1566
1567	/*
1568	 * XXX
1569	 * perhaps the size of the underlying object has changed under
1570	 * our feet.  take advantage of the offset information present
1571	 * in the uio structure.
1572	 */
1573	if (error == 0) {
1574		struct union_node *un = VTOUNION(ap->a_vp);
1575		off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
1576
1577		if (vp == un->un_uppervp) {
1578			if (cur > un->un_uppersz) {
1579				union_lock();
1580				union_newsize(ap->a_vp, cur, VNOVAL);
1581				union_unlock();
1582			}
1583		} else {
1584			if (cur > un->un_lowersz) {
1585				union_lock();
1586				union_newsize(ap->a_vp, VNOVAL, cur);
1587				union_unlock();
1588			}
1589		}
1590	}
1591
1592	return (error);
1593}
1594
1595/* Pageout  */
1596static int
1597union_pageout(struct vnop_pageout_args *ap)
1598/*
1599	struct vnop_pageout_args {
1600	   	struct vnode 	*a_vp,
1601	   	upl_t		a_pl,
1602		vm_offset_t	a_pl_offset,
1603		off_t		a_f_offset,
1604		size_t		a_size,
1605		int		a_flags
1606		vfs_context_t	a_context;
1607	} *ap;
1608*/
1609{
1610	int error;
1611	struct vnode *vp;
1612	struct union_node *un = VTOUNION(ap->a_vp);
1613
1614	vp = UPPERVP(ap->a_vp);
1615	if (vp == NULLVP)
1616		panic("union: missing upper layer in pageout");
1617
1618	error = VNOP_PAGEOUT(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1619	                     ap->a_size, ap->a_flags, ap->a_context);
1620
1621	/*
1622	 * the size of the underlying object may be changed by the
1623	 * write.
1624	 */
1625	if (error == 0) {
1626		off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
1627
1628		if (cur > un->un_uppersz) {
1629			union_lock();
1630			union_newsize(ap->a_vp, cur, VNOVAL);
1631			union_unlock();
1632		}
1633	}
1634
1635	return (error);
1636}
1637
1638/* Blktooff derives file offset for the given logical block number */
1639static int
1640union_blktooff(struct vnop_blktooff_args *ap)
1641/*
1642	struct vnop_blktooff_args {
1643		struct vnode *a_vp;
1644		daddr64_t a_lblkno;
1645		off_t *a_offset;
1646	} *ap;
1647*/
1648{
1649	int error;
1650	struct vnode *vp = OTHERVP(ap->a_vp);
1651
1652	error = VNOP_BLKTOOFF(vp, ap->a_lblkno, ap->a_offset);
1653
1654	return(error);
1655}
1656
1657/* offtoblk derives file offset for the given logical block number */
1658static int
1659union_offtoblk(struct vnop_offtoblk_args *ap)
1660/*
1661	struct vnop_offtoblk_args  {
1662		struct vnode *a_vp;
1663		off_t a_offset;
1664		daddr64_t *a_lblkno;
1665	} *ap;
1666*/
1667{
1668	int error;
1669	struct vnode *vp = OTHERVP(ap->a_vp);
1670
1671	error = VNOP_OFFTOBLK(vp, ap->a_offset, ap->a_lblkno);
1672
1673	return(error);
1674}
1675
1676#define VOPFUNC int (*)(void *)
1677
1678/*
1679 * Global vfs data structures
1680 */
1681int (**union_vnodeop_p)(void *);
1682struct vnodeopv_entry_desc union_vnodeop_entries[] = {
1683	{ &vnop_default_desc, (VOPFUNC)vn_default_error },
1684	{ &vnop_lookup_desc, (VOPFUNC)union_lookup },		/* lookup */
1685	{ &vnop_create_desc, (VOPFUNC)union_create },		/* create */
1686	{ &vnop_whiteout_desc, (VOPFUNC)union_whiteout },	/* whiteout */
1687	{ &vnop_mknod_desc, (VOPFUNC)union_mknod },		/* mknod */
1688	{ &vnop_open_desc, (VOPFUNC)union_open },		/* open */
1689	{ &vnop_close_desc, (VOPFUNC)union_close },		/* close */
1690	{ &vnop_access_desc, (VOPFUNC)union_access },		/* access */
1691	{ &vnop_getattr_desc, (VOPFUNC)union_getattr },		/* getattr */
1692	{ &vnop_setattr_desc, (VOPFUNC)union_setattr },		/* setattr */
1693	{ &vnop_read_desc, (VOPFUNC)union_read },		/* read */
1694	{ &vnop_write_desc, (VOPFUNC)union_write },		/* write */
1695	{ &vnop_ioctl_desc, (VOPFUNC)union_ioctl },		/* ioctl */
1696	{ &vnop_select_desc, (VOPFUNC)union_select },		/* select */
1697	{ &vnop_revoke_desc, (VOPFUNC)union_revoke },		/* revoke */
1698	{ &vnop_mmap_desc, (VOPFUNC)union_mmap },		/* mmap */
1699	{ &vnop_mnomap_desc, (VOPFUNC)union_mnomap },		/* mnomap */
1700	{ &vnop_fsync_desc, (VOPFUNC)union_fsync },		/* fsync */
1701	{ &vnop_remove_desc, (VOPFUNC)union_remove },		/* remove */
1702	{ &vnop_link_desc, (VOPFUNC)union_link },		/* link */
1703	{ &vnop_rename_desc, (VOPFUNC)union_rename },		/* rename */
1704	{ &vnop_mkdir_desc, (VOPFUNC)union_mkdir },		/* mkdir */
1705	{ &vnop_rmdir_desc, (VOPFUNC)union_rmdir },		/* rmdir */
1706	{ &vnop_symlink_desc, (VOPFUNC)union_symlink },		/* symlink */
1707	{ &vnop_readdir_desc, (VOPFUNC)union_readdir },		/* readdir */
1708	{ &vnop_readlink_desc, (VOPFUNC)union_readlink },	/* readlink */
1709	{ &vnop_inactive_desc, (VOPFUNC)union_inactive },	/* inactive */
1710	{ &vnop_reclaim_desc, (VOPFUNC)union_reclaim },		/* reclaim */
1711	{ &vnop_strategy_desc, (VOPFUNC)union_strategy },	/* strategy */
1712	{ &vnop_pathconf_desc, (VOPFUNC)union_pathconf },	/* pathconf */
1713	{ &vnop_advlock_desc, (VOPFUNC)union_advlock },		/* advlock */
1714#ifdef notdef
1715	{ &vnop_bwrite_desc, (VOPFUNC)union_bwrite },		/* bwrite */
1716#endif
1717	{ &vnop_pagein_desc, (VOPFUNC)union_pagein },		/* Pagein */
1718	{ &vnop_pageout_desc, (VOPFUNC)union_pageout },		/* Pageout */
1719        { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },		/* Copyfile */
1720	{ &vnop_blktooff_desc, (VOPFUNC)union_blktooff },	/* blktooff */
1721	{ &vnop_offtoblk_desc, (VOPFUNC)union_offtoblk },	/* offtoblk */
1722	{ &vnop_blockmap_desc, (VOPFUNC)union_blockmap },	/* blockmap */
1723	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
1724};
1725struct vnodeopv_desc union_vnodeop_opv_desc =
1726	{ &union_vnodeop_p, union_vnodeop_entries };
1727