2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
32 * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
33 * Copyright (c) 1992, 1993, 1994, 1995
34 * The Regents of the University of California. All rights reserved.
36 * This code is derived from software contributed to Berkeley by
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95
70 #include <sys/param.h>
71 #include <sys/systm.h>
73 #include <sys/kauth.h>
77 #include <sys/types.h>
78 #include <sys/vnode_internal.h>
79 #include <sys/mount_internal.h>
80 #include <sys/namei.h>
81 #include <sys/malloc.h>
82 #include <sys/buf_internal.h>
83 #include <sys/queue.h>
85 #include <miscfs/union/union.h>
86 #include <vfs/vfs_support.h>
88 #include <sys/uio_internal.h>
90 #define FIXUP(un, p) { \
91 if (((un)->un_flags & UN_ULOCK) == 0) { \
98 struct union_node
*un
;
102 un
->un_flags
|= UN_ULOCK
;
106 union_lookup1(struct vnode
*udvp
, struct vnode
**dvpp
, struct vnode
**vpp
,
107 struct componentname
*cnp
)
110 vfs_context_t ctx
= cnp
->cn_context
;
118 * If stepping up the directory tree, check for going
119 * back across the mount point, in which case do what
120 * lookup would do by stepping back down the mount
123 if (cnp
->cn_flags
& ISDOTDOT
) {
124 while ((dvp
!= udvp
) && (dvp
->v_flag
& VROOT
)) {
126 * Don't do the NOCROSSMOUNT check
127 * at this level. By definition,
128 * union fs deals with namespaces, not
132 *dvpp
= dvp
= dvp
->v_mount
->mnt_vnodecovered
;
138 error
= VNOP_LOOKUP(dvp
, &tdvp
, cnp
, ctx
);
144 * Lastly check if the current node is a mount point in
145 * which case walk up the mount hierarchy making sure not to
146 * bump into the root of the mount tree (ie. dvp != udvp).
148 while (dvp
!= udvp
&& (dvp
->v_type
== VDIR
) &&
149 (mp
= dvp
->v_mountedhere
)) {
150 if (vfs_busy(mp
, LK_NOWAIT
)) {
154 error
= VFS_ROOT(mp
, &tdvp
, ctx
);
171 struct vnop_lookup_args
/* {
172 struct vnodeop_desc *a_desc;
174 struct vnode **a_vpp;
175 struct componentname *a_cnp;
176 vfs_context_t a_context;
181 struct vnode
*uppervp
, *lowervp
;
182 struct vnode
*upperdvp
, *lowerdvp
;
183 struct vnode
*dvp
= ap
->a_dvp
;
184 struct union_node
*dun
= VTOUNION(dvp
);
185 struct componentname
*cnp
= ap
->a_cnp
;
186 vfs_context_t ctx
= cnp
->cn_context
;
187 struct proc
*p
= vfs_context_proc(ctx
);
188 int lockparent
= cnp
->cn_flags
& LOCKPARENT
;
189 struct union_mount
*um
= MOUNTTOUNIONMOUNT(dvp
->v_mount
);
190 kauth_cred_t saved_cred
;
192 struct vnode_attr va
;
195 if (cnp
->cn_namelen
== 3 &&
196 cnp
->cn_nameptr
[2] == '.' &&
197 cnp
->cn_nameptr
[1] == '.' &&
198 cnp
->cn_nameptr
[0] == '.') {
199 dvp
= *ap
->a_vpp
= LOWERVP(ap
->a_dvp
);
208 cnp
->cn_flags
|= LOCKPARENT
;
210 upperdvp
= dun
->un_uppervp
;
211 lowerdvp
= dun
->un_lowervp
;
217 * do the lookup in the upper level.
218 * if that level comsumes additional pathnames,
219 * then assume that something special is going
220 * on and just return that vnode.
222 if (upperdvp
!= NULLVP
) {
224 uerror
= union_lookup1(um
->um_uppervp
, &upperdvp
,
226 /*if (uppervp == upperdvp)
227 dun->un_flags |= UN_KLOCK;*/
229 if (cnp
->cn_consume
!= 0) {
230 *ap
->a_vpp
= uppervp
;
232 cnp
->cn_flags
&= ~LOCKPARENT
;
235 if (uerror
== ENOENT
|| uerror
== EJUSTRETURN
) {
236 if (cnp
->cn_flags
& ISWHITEOUT
) {
238 } else if (lowerdvp
!= NULLVP
) {
240 VATTR_WANTED(&va
, va_flags
);
241 lerror
= vnode_getattr(upperdvp
, &va
, ap
->a_context
);
242 if (lerror
== 0 && (va
.va_flags
& OPAQUE
))
251 * in a similar way to the upper layer, do the lookup
252 * in the lower layer. this time, if there is some
253 * component magic going on, then vnode_put whatever we got
254 * back from the upper layer and return the lower vnode
257 if (lowerdvp
!= NULLVP
&& !iswhiteout
) {
261 * Only do a LOOKUP on the bottom node, since
262 * we won't be making changes to it anyway.
264 nameiop
= cnp
->cn_nameiop
;
265 cnp
->cn_nameiop
= LOOKUP
;
266 if (um
->um_op
== UNMNT_BELOW
) {
268 saved_cred
= cnp
->cn_context
->vc_ucred
;
269 cnp
->cn_context
->vc_ucred
= um
->um_cred
;
270 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
272 cnp
->cn_context
->vc_ucred
= saved_cred
;
274 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
277 cnp
->cn_nameiop
= nameiop
;
279 if (cnp
->cn_consume
!= 0) {
280 if (uppervp
!= NULLVP
) {
284 *ap
->a_vpp
= lowervp
;
286 cnp
->cn_flags
&= ~LOCKPARENT
;
291 if ((cnp
->cn_flags
& ISDOTDOT
) && dun
->un_pvp
!= NULLVP
) {
292 lowervp
= LOWERVP(dun
->un_pvp
);
293 if (lowervp
!= NULLVP
) {
301 cnp
->cn_flags
&= ~LOCKPARENT
;
304 * at this point, we have uerror and lerror indicating
305 * possible errors with the lookups in the upper and lower
306 * layers. additionally, uppervp and lowervp are (locked)
307 * references to existing vnodes in the upper and lower layers.
309 * there are now three cases to consider.
310 * 1. if both layers returned an error, then return whatever
311 * error the upper layer generated.
313 * 2. if the top layer failed and the bottom layer succeeded
314 * then two subcases occur.
315 * a. the bottom vnode is not a directory, in which
316 * case just return a new union vnode referencing
317 * an empty top layer and the existing bottom layer.
318 * b. the bottom vnode is a directory, in which case
319 * create a new directory in the top-level and
320 * continue as in case 3.
322 * 3. if the top layer succeeded then return a new union
323 * vnode referencing whatever the new top layer and
324 * whatever the bottom layer returned.
330 if ((uerror
!= 0) && (lerror
!= 0)) {
335 if (uerror
!= 0 /* && (lerror == 0) */ ) {
336 if (lowervp
->v_type
== VDIR
) { /* case 2b. */
337 dun
->un_flags
&= ~UN_ULOCK
;
338 uerror
= union_mkshadow(um
, upperdvp
, cnp
, &uppervp
);
339 dun
->un_flags
|= UN_ULOCK
;
342 if (lowervp
!= NULLVP
) {
350 error
= union_allocvp(ap
->a_vpp
, dvp
->v_mount
, dvp
, upperdvp
, cnp
,
351 uppervp
, lowervp
, 1);
354 if (uppervp
!= NULLVP
)
356 if (lowervp
!= NULLVP
)
365 struct vnop_create_args
/* {
367 struct vnode **a_vpp;
368 struct componentname *a_cnp;
369 struct vnode_attr *a_vap;
370 vfs_context_t a_context;
373 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
374 struct vnode
*dvp
= un
->un_uppervp
;
375 struct componentname
*cnp
= ap
->a_cnp
;
376 vfs_context_t ctx
= cnp
->cn_context
;
377 struct proc
*p
= vfs_context_proc(ctx
);
386 un
->un_flags
|= UN_KLOCK
;
387 mp
= ap
->a_dvp
->v_mount
;
389 /* note that this is a direct passthrough to the filesystem */
390 error
= VNOP_CREATE(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
394 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
, cnp
, vp
,
405 struct vnop_whiteout_args
/* {
407 struct componentname *a_cnp;
409 vfs_context_t a_context;
412 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
413 struct componentname
*cnp
= ap
->a_cnp
;
414 vfs_context_t ctx
= cnp
->cn_context
;
415 struct proc
*p
= vfs_context_proc(ctx
);
417 if (un
->un_uppervp
== NULLVP
)
421 return (VNOP_WHITEOUT(un
->un_uppervp
, cnp
, ap
->a_flags
, ap
->a_context
));
426 struct vnop_mknod_args
/* {
428 struct vnode **a_vpp;
429 struct componentname *a_cnp;
430 struct vnode_attr *a_vap;
431 vfs_context_t a_context;
434 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
435 struct vnode
*dvp
= un
->un_uppervp
;
436 struct componentname
*cnp
= ap
->a_cnp
;
437 vfs_context_t ctx
= cnp
->cn_context
;
438 struct proc
*p
= vfs_context_proc(ctx
);
447 un
->un_flags
|= UN_KLOCK
;
448 mp
= ap
->a_dvp
->v_mount
;
450 /* note that this is a direct passthrough to the filesystem */
451 error
= VNOP_MKNOD(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
456 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
,
468 struct vnop_open_args
/* {
469 struct vnodeop_desc *a_desc;
472 vfs_context_t a_context;
475 struct union_node
*un
= VTOUNION(ap
->a_vp
);
477 int mode
= ap
->a_mode
;
478 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
479 struct proc
*p
= vfs_context_proc(ap
->a_context
);
483 * If there is an existing upper vp then simply open that.
485 tvp
= un
->un_uppervp
;
488 * If the lower vnode is being opened for writing, then
489 * copy the file contents to the upper vnode and open that,
490 * otherwise can simply open the lower vnode.
492 tvp
= un
->un_lowervp
;
493 if ((ap
->a_mode
& FWRITE
) && (tvp
->v_type
== VREG
)) {
494 error
= union_copyup(un
, (mode
&O_TRUNC
) == 0, cred
, p
);
496 error
= VNOP_OPEN(un
->un_uppervp
, mode
, ap
->a_context
);
501 * Just open the lower vnode
505 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
512 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
519 struct vnop_close_args
/* {
522 vfs_context_t a_context;
525 struct union_node
*un
= VTOUNION(ap
->a_vp
);
528 if ((vp
= un
->un_uppervp
) == NULLVP
) {
529 #ifdef UNION_DIAGNOSTIC
530 if (un
->un_openl
<= 0)
531 panic("union: un_openl cnt");
538 return (VCALL(vp
, VOFFSET(vnop_close
), ap
));
542 * Check access permission on the union vnode.
543 * The access check being enforced is to check
544 * against both the underlying vnode, and any
545 * copied vnode. This ensures that no additional
546 * file permissions are given away simply because
547 * the user caused an implicit file copy.
551 struct vnop_access_args
/* {
552 struct vnodeop_desc *a_desc;
555 vfs_context_t a_context;
558 struct union_node
*un
= VTOUNION(ap
->a_vp
);
559 struct proc
*p
= vfs_context_proc(ap
->a_context
);
563 if ((vp
= un
->un_uppervp
) != NULLVP
) {
566 return (VCALL(vp
, VOFFSET(vnop_access
), ap
));
569 if ((vp
= un
->un_lowervp
) != NULLVP
) {
571 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
573 struct union_mount
*um
= MOUNTTOUNIONMOUNT(vp
->v_mount
);
575 if (um
->um_op
== UNMNT_BELOW
) {
577 // ap->a_cred = um->um_cred;
578 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
589 * We handle getattr only to change the fsid and
594 struct vnop_getattr_args
/* {
596 struct vnode_attr *a_vap;
597 vfs_context_t a_context;
601 struct union_node
*un
= VTOUNION(ap
->a_vp
);
602 struct vnode
*vp
= un
->un_uppervp
;
603 struct proc
*p
= vfs_context_proc(ap
->a_context
);
604 struct vnode_attr
*vap
;
605 struct vnode_attr va
;
609 * Some programs walk the filesystem hierarchy by counting
610 * links to directories to avoid stat'ing all the time.
611 * This means the link count on directories needs to be "correct".
612 * The only way to do that is to call getattr on both layers
613 * and fix up the link count. The link count will not necessarily
614 * be accurate but will be large enough to defeat the tree walkers.
622 * It's not clear whether vnop_getattr is to be
623 * called with the vnode locked or not. stat() calls
624 * it with (vp) locked, and fstat calls it with
626 * In the mean time, compensate here by checking
627 * the union_node's lock flag.
629 if (un
->un_flags
& UN_LOCKED
)
632 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
635 union_newsize(ap
->a_vp
, vap
->va_data_size
, VNOVAL
);
640 } else if (vp
->v_type
== VDIR
) {
643 /* all we want from the lower node is the link count */
644 VATTR_WANTED(&va
, va_nlink
);
651 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
654 union_newsize(ap
->a_vp
, VNOVAL
, vap
->va_data_size
);
657 if ((vap
!= ap
->a_vap
) && (vap
->va_type
== VDIR
))
658 ap
->a_vap
->va_nlink
+= vap
->va_nlink
;
660 VATTR_RETURN(ap
->a_vap
, va_fsid
, ap
->a_vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
666 struct vnop_setattr_args
/* {
668 struct vnode_attr *a_vap;
669 vfs_context_t a_context;
672 struct union_node
*un
= VTOUNION(ap
->a_vp
);
673 struct proc
*p
= vfs_context_proc(ap
->a_context
);
674 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
678 * Handle case of truncating lower object to zero size,
679 * by creating a zero length upper object. This is to
680 * handle the case of open with O_TRUNC and O_CREAT.
682 if (VATTR_IS_ACTIVE(ap
->a_vap
, va_data_size
) &&
683 (un
->un_uppervp
== NULLVP
) &&
684 /* assert(un->un_lowervp != NULLVP) */
685 (un
->un_lowervp
->v_type
== VREG
)) {
686 error
= union_copyup(un
, (ap
->a_vap
->va_data_size
!= 0), cred
, p
);
692 * Try to set attributes in upper layer,
693 * otherwise return read-only filesystem error.
695 if (un
->un_uppervp
!= NULLVP
) {
697 error
= vnode_setattr(un
->un_uppervp
, ap
->a_vap
, ap
->a_context
);
698 if ((error
== 0) && VATTR_IS_ACTIVE(ap
->a_vap
, va_data_size
))
699 union_newsize(ap
->a_vp
, ap
->a_vap
->va_data_size
, VNOVAL
);
709 struct vnop_read_args
/* {
713 vfs_context_t a_context;
717 struct proc
*p
= vfs_context_proc(ap
->a_context
);
718 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
719 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
722 FIXUP(VTOUNION(ap
->a_vp
), p
);
723 error
= VNOP_READ(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
727 * perhaps the size of the underlying object has changed under
728 * our feet. take advantage of the offset information present
729 * in the uio structure.
732 struct union_node
*un
= VTOUNION(ap
->a_vp
);
733 off_t cur
= ap
->a_uio
->uio_offset
;
735 if (vp
== un
->un_uppervp
) {
736 if (cur
> un
->un_uppersz
)
737 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
739 if (cur
> un
->un_lowersz
)
740 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
749 struct vnop_read_args
/* {
753 vfs_context_t a_context;
758 struct union_node
*un
= VTOUNION(ap
->a_vp
);
759 struct proc
*p
= vfs_context_proc(ap
->a_context
);
761 vp
= UPPERVP(ap
->a_vp
);
763 panic("union: missing upper layer in write");
766 error
= VNOP_WRITE(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
769 * the size of the underlying object may be changed by the
773 off_t cur
= ap
->a_uio
->uio_offset
;
775 if (cur
> un
->un_uppersz
)
776 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
785 struct vnop_ioctl_args
/* {
790 vfs_context_t a_context;
793 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
796 return (VCALL(ovp
, VOFFSET(vnop_ioctl
), ap
));
801 struct vnop_select_args
/* {
806 vfs_context_t a_context;
809 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
812 return (VCALL(ovp
, VOFFSET(vnop_select
), ap
));
817 struct vnop_revoke_args
/* {
820 vfs_context_t a_context;
823 struct vnode
*vp
= ap
->a_vp
;
826 VNOP_REVOKE(UPPERVP(vp
), ap
->a_flags
, ap
->a_context
);
828 VNOP_REVOKE(LOWERVP(vp
), ap
->a_flags
, ap
->a_context
);
834 struct vnop_mmap_args
/* {
841 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
844 return (VCALL(ovp
, VOFFSET(vnop_mmap
), ap
));
849 struct vnop_fsync_args
/* {
852 vfs_context_t a_context;
856 struct proc
*p
= vfs_context_proc(ap
->a_context
);
857 struct vnode
*targetvp
= OTHERVP(ap
->a_vp
);
859 if (targetvp
!= NULLVP
) {
860 int dolock
= (targetvp
== LOWERVP(ap
->a_vp
));
863 FIXUP(VTOUNION(ap
->a_vp
), p
);
864 error
= VNOP_FSYNC(targetvp
, ap
->a_waitfor
, ap
->a_context
);
872 struct vnop_remove_args
/* {
875 struct componentname *a_cnp;
876 vfs_context_t a_context;
880 struct union_node
*dun
= VTOUNION(ap
->a_dvp
);
881 struct union_node
*un
= VTOUNION(ap
->a_vp
);
882 struct componentname
*cnp
= ap
->a_cnp
;
883 vfs_context_t ctx
= cnp
->cn_context
;
884 struct proc
*p
= vfs_context_proc(ctx
);
886 if (dun
->un_uppervp
== NULLVP
)
887 panic("union remove: null upper vnode");
889 if (un
->un_uppervp
!= NULLVP
) {
890 struct vnode
*dvp
= dun
->un_uppervp
;
891 struct vnode
*vp
= un
->un_uppervp
;
894 dun
->un_flags
|= UN_KLOCK
;
896 un
->un_flags
|= UN_KLOCK
;
898 if (union_dowhiteout(un
, cnp
->cn_context
))
899 cnp
->cn_flags
|= DOWHITEOUT
;
900 error
= VNOP_REMOVE(dvp
, vp
, cnp
, 0, ap
->a_context
);
902 union_removed_upper(un
);
905 error
= union_mkwhiteout(
906 MOUNTTOUNIONMOUNT(UNIONTOV(dun
)->v_mount
),
907 dun
->un_uppervp
, ap
->a_cnp
, un
->un_path
);
915 struct vnop_link_args
/* {
917 struct vnode *a_tdvp;
918 struct componentname *a_cnp;
919 vfs_context_t a_context;
923 struct componentname
*cnp
= ap
->a_cnp
;
924 vfs_context_t ctx
= cnp
->cn_context
;
925 struct proc
*p
= vfs_context_proc(ctx
);
926 struct union_node
*un
;
930 un
= VTOUNION(ap
->a_tdvp
);
932 if (ap
->a_tdvp
->v_op
!= ap
->a_vp
->v_op
) {
935 struct union_node
*tun
= VTOUNION(ap
->a_vp
);
936 if (tun
->un_uppervp
== NULLVP
) {
937 if (un
->un_uppervp
== tun
->un_dirvp
) {
938 un
->un_flags
&= ~UN_ULOCK
;
940 error
= union_copyup(tun
, 1, vfs_context_ucred(ctx
), p
);
941 if (un
->un_uppervp
== tun
->un_dirvp
) {
942 un
->un_flags
|= UN_ULOCK
;
945 vp
= tun
->un_uppervp
;
947 tdvp
= un
->un_uppervp
;
957 un
->un_flags
|= UN_KLOCK
;
959 return (VNOP_LINK(vp
, tdvp
, cnp
, ap
->a_context
));
964 struct vnop_rename_args
/* {
965 struct vnode *a_fdvp;
967 struct componentname *a_fcnp;
968 struct vnode *a_tdvp;
970 struct componentname *a_tcnp;
971 vfs_context_t a_context;
976 struct vnode
*fdvp
= ap
->a_fdvp
;
977 struct vnode
*fvp
= ap
->a_fvp
;
978 struct vnode
*tdvp
= ap
->a_tdvp
;
979 struct vnode
*tvp
= ap
->a_tvp
;
981 if (fdvp
->v_op
== union_vnodeop_p
) { /* always true */
982 struct union_node
*un
= VTOUNION(fdvp
);
983 if (un
->un_uppervp
== NULLVP
) {
985 * this should never happen in normal
986 * operation but might if there was
987 * a problem creating the top-level shadow
994 fdvp
= un
->un_uppervp
;
998 if (fvp
->v_op
== union_vnodeop_p
) { /* always true */
999 struct union_node
*un
= VTOUNION(fvp
);
1000 if (un
->un_uppervp
== NULLVP
) {
1001 /* XXX: should do a copyup */
1006 if (un
->un_lowervp
!= NULLVP
)
1007 ap
->a_fcnp
->cn_flags
|= DOWHITEOUT
;
1009 fvp
= un
->un_uppervp
;
1013 if (tdvp
->v_op
== union_vnodeop_p
) {
1014 struct union_node
*un
= VTOUNION(tdvp
);
1015 if (un
->un_uppervp
== NULLVP
) {
1017 * this should never happen in normal
1018 * operation but might if there was
1019 * a problem creating the top-level shadow
1026 tdvp
= un
->un_uppervp
;
1028 un
->un_flags
|= UN_KLOCK
;
1031 if (tvp
!= NULLVP
&& tvp
->v_op
== union_vnodeop_p
) {
1032 struct union_node
*un
= VTOUNION(tvp
);
1034 tvp
= un
->un_uppervp
;
1035 if (tvp
!= NULLVP
) {
1037 un
->un_flags
|= UN_KLOCK
;
1041 return (VNOP_RENAME(fdvp
, fvp
, ap
->a_fcnp
, tdvp
, tvp
, ap
->a_tcnp
, ap
->a_context
));
1049 struct vnop_mkdir_args
/* {
1050 struct vnode *a_dvp;
1051 struct vnode **a_vpp;
1052 struct componentname *a_cnp;
1053 struct vnode_attr *a_vap;
1054 vfs_context_t a_context;
1057 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1058 struct vnode
*dvp
= un
->un_uppervp
;
1059 struct componentname
*cnp
= ap
->a_cnp
;
1060 vfs_context_t ctx
= cnp
->cn_context
;
1061 struct proc
*p
= vfs_context_proc(ctx
);
1063 if (dvp
!= NULLVP
) {
1068 un
->un_flags
|= UN_KLOCK
;
1070 /* note that this is a direct fallthrough to the filesystem */
1071 error
= VNOP_MKDIR(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
1075 error
= union_allocvp(ap
->a_vpp
, ap
->a_dvp
->v_mount
, ap
->a_dvp
,
1076 NULLVP
, cnp
, vp
, NULLVP
, 1);
1086 struct vnop_rmdir_args
/* {
1087 struct vnode *a_dvp;
1089 struct componentname *a_cnp;
1090 vfs_context_t a_context;
1094 struct union_node
*dun
= VTOUNION(ap
->a_dvp
);
1095 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1096 struct componentname
*cnp
= ap
->a_cnp
;
1097 vfs_context_t ctx
= cnp
->cn_context
;
1098 struct proc
*p
= vfs_context_proc(ctx
);
1100 if (dun
->un_uppervp
== NULLVP
)
1101 panic("union rmdir: null upper vnode");
1103 if (un
->un_uppervp
!= NULLVP
) {
1104 struct vnode
*dvp
= dun
->un_uppervp
;
1105 struct vnode
*vp
= un
->un_uppervp
;
1109 dun
->un_flags
|= UN_KLOCK
;
1112 un
->un_flags
|= UN_KLOCK
;
1114 if (union_dowhiteout(un
, cnp
->cn_context
))
1115 cnp
->cn_flags
|= DOWHITEOUT
;
1116 error
= VNOP_RMDIR(dvp
, vp
, ap
->a_cnp
, ap
->a_context
);
1118 union_removed_upper(un
);
1121 error
= union_mkwhiteout(
1122 MOUNTTOUNIONMOUNT(UNIONTOV(dun
)->v_mount
),
1123 dun
->un_uppervp
, ap
->a_cnp
, un
->un_path
);
1130 struct vnop_symlink_args
/* {
1131 struct vnode *a_dvp;
1132 struct vnode **a_vpp;
1133 struct componentname *a_cnp;
1134 struct vnode_attr *a_vap;
1136 vfs_context_t a_context;
1139 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1140 struct vnode
*dvp
= un
->un_uppervp
;
1141 struct componentname
*cnp
= ap
->a_cnp
;
1142 vfs_context_t ctx
= cnp
->cn_context
;
1143 struct proc
*p
= vfs_context_proc(ctx
);
1145 if (dvp
!= NULLVP
) {
1150 un
->un_flags
|= UN_KLOCK
;
1152 error
= VNOP_SYMLINK(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_target
, ap
->a_context
);
1153 *ap
->a_vpp
= NULLVP
;
1160 * union_readdir works in concert with getdirentries and
1161 * readdir(3) to provide a list of entries in the unioned
1162 * directories. getdirentries is responsible for walking
1163 * down the union stack. readdir(3) is responsible for
1164 * eliminating duplicate names from the returned data stream.
1168 struct vnop_readdir_args
/* {
1169 struct vnodeop_desc *a_desc;
1175 vfs_context_t a_context;
1178 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1179 struct vnode
*uvp
= un
->un_uppervp
;
1180 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1182 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
))
1190 return (VCALL(uvp
, VOFFSET(vnop_readdir
), ap
));
1195 struct vnop_readlink_args
/* {
1198 vfs_context_t a_context;
1202 struct uio
*uio
= ap
->a_uio
;
1203 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1204 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1205 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1208 FIXUP(VTOUNION(ap
->a_vp
), p
);
1210 error
= VCALL(vp
, VOFFSET(vnop_readlink
), ap
);
1217 struct vnop_inactive_args
/* {
1219 vfs_context_t a_context;
1222 struct vnode
*vp
= ap
->a_vp
;
1223 struct union_node
*un
= VTOUNION(vp
);
1227 * Do nothing (and _don't_ bypass).
1228 * Wait to vnode_put lowervp until reclaim,
1229 * so that until then our union_node is in the
1230 * cache and reusable.
1232 * NEEDSWORK: Someday, consider inactive'ing
1233 * the lowervp and then trying to reactivate it
1234 * with capabilities (v_id)
1235 * like they do in the name lookup cache code.
1236 * That's too much work for now.
1239 if (un
->un_dircache
!= 0) {
1240 for (vpp
= un
->un_dircache
; *vpp
!= NULLVP
; vpp
++)
1242 _FREE(un
->un_dircache
, M_TEMP
);
1243 un
->un_dircache
= 0;
1246 if ((un
->un_flags
& UN_CACHED
) == 0)
1254 struct vnop_reclaim_args
/* {
1256 vfs_context_t a_context;
1260 union_freevp(ap
->a_vp
);
1267 struct vnop_blockmap_args
/* {
1278 struct proc
*p
= current_proc(); /* XXX */
1279 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1280 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1283 FIXUP(VTOUNION(ap
->a_vp
), p
);
1285 error
= VCALL(vp
, VOFFSET(vnop_blockmap
), ap
);
1292 struct vnop_pathconf_args
/* {
1296 vfs_context_t a_context;
1300 struct proc
*p
= current_proc(); /* XXX */
1301 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1302 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1305 FIXUP(VTOUNION(ap
->a_vp
), p
);
1307 error
= VCALL(vp
, VOFFSET(vnop_pathconf
), ap
);
1314 struct vnop_advlock_args
/* {
1320 vfs_context_t a_context;
1323 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
1326 return (VCALL(ovp
, VOFFSET(vnop_advlock
), ap
));
1331 * XXX - vnop_strategy must be hand coded because it has no
1332 * vnode in its arguments.
1333 * This goes away with a merged VM/buffer cache.
1337 struct vnop_strategy_args
/* {
1341 struct buf
*bp
= ap
->a_bp
;
1343 struct vnode
*savedvp
;
1345 savedvp
= buf_vnode(bp
);
1346 buf_setvnode(bp
, OTHERVP(savedvp
));
1349 if (buf_vnode(bp
) == NULLVP
)
1350 panic("union_strategy: nil vp");
1351 if (((buf_flags(bp
) & B_READ
) == 0) &&
1352 (buf_vnode(bp
) == LOWERVP(savedvp
)))
1353 panic("union_strategy: writing to lowervp");
1356 error
= VNOP_STRATEGY(bp
);
1357 buf_setvnode(bp
, savedvp
);
1365 struct vnop_pagein_args
/* {
1368 vm_offset_t a_pl_offset,
1372 vfs_context_t a_context;
1376 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1378 error
= VNOP_PAGEIN(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
1379 ap
->a_size
, ap
->a_flags
, ap
->a_context
);
1383 * perhaps the size of the underlying object has changed under
1384 * our feet. take advantage of the offset information present
1385 * in the uio structure.
1388 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1389 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1391 if (vp
== un
->un_uppervp
) {
1392 if (cur
> un
->un_uppersz
)
1393 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1395 if (cur
> un
->un_lowersz
)
1396 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
1406 struct vnop_pageout_args
/* {
1409 vm_offset_t a_pl_offset,
1413 vfs_context_t a_context;
1418 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1420 vp
= UPPERVP(ap
->a_vp
);
1422 panic("union: missing upper layer in pageout");
1424 error
= VNOP_PAGEOUT(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
1425 ap
->a_size
, ap
->a_flags
, ap
->a_context
);
1428 * the size of the underlying object may be changed by the
1432 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1434 if (cur
> un
->un_uppersz
)
1435 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1441 /* Blktooff derives file offset for the given logical block number */
1444 struct vnop_blktooff_args
/* {
1451 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1453 error
= VNOP_BLKTOOFF(vp
, ap
->a_lblkno
, ap
->a_offset
);
1458 /* offtoblk derives file offset for the given logical block number */
1461 struct vnop_offtoblk_args
/* {
1464 daddr64_t *a_lblkno;
1468 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1470 error
= VNOP_OFFTOBLK(vp
, ap
->a_offset
, ap
->a_lblkno
);
1475 #define VOPFUNC int (*)(void *)
1478 * Global vfs data structures
1480 int (**union_vnodeop_p
)(void *);
1481 struct vnodeopv_entry_desc union_vnodeop_entries
[] = {
1482 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
1483 { &vnop_lookup_desc
, (VOPFUNC
)union_lookup
}, /* lookup */
1484 { &vnop_create_desc
, (VOPFUNC
)union_create
}, /* create */
1485 { &vnop_whiteout_desc
, (VOPFUNC
)union_whiteout
}, /* whiteout */
1486 { &vnop_mknod_desc
, (VOPFUNC
)union_mknod
}, /* mknod */
1487 { &vnop_open_desc
, (VOPFUNC
)union_open
}, /* open */
1488 { &vnop_close_desc
, (VOPFUNC
)union_close
}, /* close */
1489 { &vnop_access_desc
, (VOPFUNC
)union_access
}, /* access */
1490 { &vnop_getattr_desc
, (VOPFUNC
)union_getattr
}, /* getattr */
1491 { &vnop_setattr_desc
, (VOPFUNC
)union_setattr
}, /* setattr */
1492 { &vnop_read_desc
, (VOPFUNC
)union_read
}, /* read */
1493 { &vnop_write_desc
, (VOPFUNC
)union_write
}, /* write */
1494 { &vnop_ioctl_desc
, (VOPFUNC
)union_ioctl
}, /* ioctl */
1495 { &vnop_select_desc
, (VOPFUNC
)union_select
}, /* select */
1496 { &vnop_revoke_desc
, (VOPFUNC
)union_revoke
}, /* revoke */
1497 { &vnop_mmap_desc
, (VOPFUNC
)union_mmap
}, /* mmap */
1498 { &vnop_fsync_desc
, (VOPFUNC
)union_fsync
}, /* fsync */
1499 { &vnop_remove_desc
, (VOPFUNC
)union_remove
}, /* remove */
1500 { &vnop_link_desc
, (VOPFUNC
)union_link
}, /* link */
1501 { &vnop_rename_desc
, (VOPFUNC
)union_rename
}, /* rename */
1502 { &vnop_mkdir_desc
, (VOPFUNC
)union_mkdir
}, /* mkdir */
1503 { &vnop_rmdir_desc
, (VOPFUNC
)union_rmdir
}, /* rmdir */
1504 { &vnop_symlink_desc
, (VOPFUNC
)union_symlink
}, /* symlink */
1505 { &vnop_readdir_desc
, (VOPFUNC
)union_readdir
}, /* readdir */
1506 { &vnop_readlink_desc
, (VOPFUNC
)union_readlink
}, /* readlink */
1507 { &vnop_inactive_desc
, (VOPFUNC
)union_inactive
}, /* inactive */
1508 { &vnop_reclaim_desc
, (VOPFUNC
)union_reclaim
}, /* reclaim */
1509 { &vnop_strategy_desc
, (VOPFUNC
)union_strategy
}, /* strategy */
1510 { &vnop_pathconf_desc
, (VOPFUNC
)union_pathconf
}, /* pathconf */
1511 { &vnop_advlock_desc
, (VOPFUNC
)union_advlock
}, /* advlock */
1513 { &vnop_bwrite_desc
, (VOPFUNC
)union_bwrite
}, /* bwrite */
1515 { &vnop_pagein_desc
, (VOPFUNC
)union_pagein
}, /* Pagein */
1516 { &vnop_pageout_desc
, (VOPFUNC
)union_pageout
}, /* Pageout */
1517 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1518 { &vnop_blktooff_desc
, (VOPFUNC
)union_blktooff
}, /* blktooff */
1519 { &vnop_offtoblk_desc
, (VOPFUNC
)union_offtoblk
}, /* offtoblk */
1520 { &vnop_blockmap_desc
, (VOPFUNC
)union_blockmap
}, /* blockmap */
1521 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1523 struct vnodeopv_desc union_vnodeop_opv_desc
=
1524 { &union_vnodeop_p
, union_vnodeop_entries
};