2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
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.
34 * This code is derived from software contributed to Berkeley by
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
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.
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
65 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95
68 #include <sys/param.h>
69 #include <sys/systm.h>
71 #include <sys/kauth.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>
83 #include <miscfs/union/union.h>
84 #include <vfs/vfs_support.h>
86 #include <sys/kdebug.h>
87 #include <sys/uio_internal.h>
89 /* called with no union lock held */
91 union_lookup1(struct vnode
*udvp
, struct vnode
**dvpp
, struct vnode
**vpp
,
92 struct componentname
*cnp
)
95 vfs_context_t ctx
= cnp
->cn_context
;
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
108 if (cnp
->cn_flags
& ISDOTDOT
) {
109 while ((dvp
!= udvp
) && (dvp
->v_flag
& VROOT
)) {
111 * Don't do the NOCROSSMOUNT check
112 * at this level. By definition,
113 * union fs deals with namespaces, not
117 *dvpp
= dvp
= dvp
->v_mount
->mnt_vnodecovered
;
123 error
= VNOP_LOOKUP(dvp
, &tdvp
, cnp
, ctx
);
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).
133 while (dvp
!= udvp
&& (dvp
->v_type
== VDIR
) &&
134 (mp
= dvp
->v_mountedhere
)) {
135 if (vfs_busy(mp
, LK_NOWAIT
)) {
139 error
= VFS_ROOT(mp
, &tdvp
, ctx
);
155 union_lookup(struct vnop_lookup_args
*ap
)
157 struct vnop_lookup_args {
158 struct vnodeop_desc *a_desc;
160 struct vnode **a_vpp;
161 struct componentname *a_cnp;
162 vfs_context_t a_context;
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
;
179 struct vnode_attr va
;
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
);
202 um
= MOUNTTOUNIONMOUNT(dvp
->v_mount
);
204 upperdvp
= dun
->un_uppervp
;
205 lowerdvp
= dun
->un_lowervp
;
212 if(UNION_FAULTIN(um
))
216 cnp
->cn_flags
|= LOCKPARENT
;
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.
224 if (upperdvp
!= NULLVP
) {
226 cnp
->cn_flags
&= ~LOCKPARENT
;
227 uerror
= union_lookup1(um
->um_uppervp
, &upperdvp
,
231 if (cnp
->cn_consume
!= 0) {
232 *ap
->a_vpp
= uppervp
;
234 cnp
->cn_flags
&= ~LOCKPARENT
;
236 cnp
->cn_flags
|= LOCKPARENT
;
239 if (uerror
== ENOENT
|| uerror
== EJUSTRETURN
) {
240 if (cnp
->cn_flags
& ISWHITEOUT
) {
242 } else if (lowerdvp
!= NULLVP
) {
244 VATTR_WANTED(&va
, va_flags
);
245 lerror
= vnode_getattr(upperdvp
, &va
, ap
->a_context
);
246 if (lerror
== 0 && (va
.va_flags
& OPAQUE
))
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
260 if ((isfaultfs
== 1) && (upperlookup
== 1) && (uerror
== 0) && ((vnode_isreg(uppervp
) != 0)))
261 goto donelowerlookup
;
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
270 if (lowerdvp
!= NULLVP
&& !iswhiteout
) {
274 * Only do a LOOKUP on the bottom node, since
275 * we won't be making changes to it anyway.
277 nameiop
= cnp
->cn_nameiop
;
278 cnp
->cn_nameiop
= LOOKUP
;
279 if (um
->um_op
== UNMNT_BELOW
) {
281 saved_cred
= cnp
->cn_context
->vc_ucred
;
282 cnp
->cn_context
->vc_ucred
= um
->um_cred
;
284 cnp
->cn_flags
&= ~LOCKPARENT
;
285 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
287 cnp
->cn_context
->vc_ucred
= saved_cred
;
290 cnp
->cn_flags
&= ~LOCKPARENT
;
291 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
294 cnp
->cn_nameiop
= nameiop
;
296 if (cnp
->cn_consume
!= 0) {
297 if (uppervp
!= NULLVP
) {
301 *ap
->a_vpp
= lowervp
;
303 cnp
->cn_flags
&= ~LOCKPARENT
;
305 cnp
->cn_flags
|= LOCKPARENT
;
310 if ((cnp
->cn_flags
& ISDOTDOT
) && dun
->un_pvp
!= NULLVP
) {
311 lowervp
= LOWERVP(dun
->un_pvp
);
312 if (lowervp
!= NULLVP
) {
321 cnp
->cn_flags
&= ~LOCKPARENT
;
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.
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.
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.
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.
350 if ((uerror
!= 0) && (lerror
!= 0)) {
352 cnp
->cn_flags
&= ~LOCKPARENT
;
354 cnp
->cn_flags
|= LOCKPARENT
;
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 */
366 uerror
= union_mkshadow(um
, upperdvp
, cnp
, &uppervp
);
368 if ((uerror
== EEXIST
)){
369 if (uppervp
== NULLVP
) {
371 if (retry_count
<= 2) {
372 if (lowervp
!= NULLVP
)
381 if (uppervp
!= NULLVP
) {
384 if (lowervp
!= NULLVP
) {
388 cnp
->cn_flags
&= ~LOCKPARENT
;
390 cnp
->cn_flags
|= LOCKPARENT
;
393 } else if ((lowervp
->v_type
== VREG
) && (isfaultfs
== 1)) {
394 error
= union_faultin_copyup(&uppervp
, upperdvp
, lowervp
, cnp
, ctx
);
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
)
407 cnp
->cn_flags
&= ~LOCKPARENT
;
409 cnp
->cn_flags
|= LOCKPARENT
;
414 error
= union_allocvp(ap
->a_vpp
, dvp
->v_mount
, dvp
, upperdvp
, cnp
,
415 uppervp
, lowervp
, 1);
419 if (uppervp
!= NULLVP
)
421 if (lowervp
!= NULLVP
)
426 cnp
->cn_flags
&= ~LOCKPARENT
;
428 cnp
->cn_flags
|= LOCKPARENT
;
433 union_create(struct vnop_create_args
*ap
)
435 struct vnop_create_args {
437 struct vnode **a_vpp;
438 struct componentname *a_cnp;
439 struct vnode_attr *a_vap;
440 vfs_context_t a_context;
444 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
445 struct vnode
*dvp
= un
->un_uppervp
;
446 struct componentname
*cnp
= ap
->a_cnp
;
454 mp
= ap
->a_dvp
->v_mount
;
456 /* note that this is a direct passthrough to the filesystem */
457 error
= VNOP_CREATE(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
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))) {
469 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
, cnp
, vp
,
481 union_whiteout(struct vnop_whiteout_args
*ap
)
483 struct vnop_whiteout_args {
485 struct componentname *a_cnp;
487 vfs_context_t a_context;
491 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
492 struct componentname
*cnp
= ap
->a_cnp
;
495 if (un
->un_uppervp
== NULLVP
) {
499 error
= (VNOP_WHITEOUT(un
->un_uppervp
, cnp
, ap
->a_flags
, ap
->a_context
));
503 /* mknod can do fifos, chr, blk or whiteout entries */
505 union_mknod(struct vnop_mknod_args
*ap
)
507 struct vnop_mknod_args {
509 struct vnode **a_vpp;
510 struct componentname *a_cnp;
511 struct vnode_attr *a_vap;
512 vfs_context_t a_context;
516 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
517 struct vnode
*dvp
= un
->un_uppervp
;
518 struct componentname
*cnp
= ap
->a_cnp
;
526 mp
= ap
->a_dvp
->v_mount
;
528 /* note that this is a direct passthrough to the filesystem */
529 error
= VNOP_MKNOD(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
535 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
,
547 union_open(struct vnop_open_args
*ap
)
549 struct vnop_open_args {
550 struct vnodeop_desc *a_desc;
553 vfs_context_t a_context;
557 struct union_node
*un
= VTOUNION(ap
->a_vp
);
559 int mode
= ap
->a_mode
;
563 * If there is an existing upper vp then simply open that.
566 tvp
= un
->un_uppervp
;
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.
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 **/
580 un
->un_flags
|= UN_LOCKED
;
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
);
590 error
= VNOP_OPEN(un
->un_uppervp
, mode
, ap
->a_context
);
595 * Just open the lower vnode
599 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
604 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
610 union_close(struct vnop_close_args
*ap
)
612 struct vnop_close_args {
615 vfs_context_t a_context;
619 struct union_node
*un
= VTOUNION(ap
->a_vp
);
623 if ((vp
= un
->un_uppervp
) == NULLVP
) {
624 #ifdef UNION_DIAGNOSTIC
625 if (un
->un_openl
<= 0)
626 panic("union: un_openl cnt");
633 error
= (VCALL(vp
, VOFFSET(vnop_close
), ap
));
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.
646 union_access(struct vnop_access_args
*ap
)
648 struct vnop_access_args {
649 struct vnodeop_desc *a_desc;
652 vfs_context_t a_context;
656 struct union_node
*un
= VTOUNION(ap
->a_vp
);
660 if ((vp
= un
->un_uppervp
) != NULLVP
) {
662 return (VCALL(vp
, VOFFSET(vnop_access
), ap
));
665 if ((vp
= un
->un_lowervp
) != NULLVP
) {
667 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
669 struct union_mount
*um
= MOUNTTOUNIONMOUNT(vp
->v_mount
);
671 if (um
->um_op
== UNMNT_BELOW
) {
672 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
683 * We handle getattr only to change the fsid and
687 union_getattr(struct vnop_getattr_args
*ap
)
689 struct vnop_getattr_args {
691 struct vnode_attr *a_vap;
692 vfs_context_t a_context;
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
;
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.
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
721 * In the mean time, compensate here by checking
722 * the union_node's lock flag.
725 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
730 union_newsize(ap
->a_vp
, vap
->va_data_size
, VNOVAL
);
736 } else if (vp
->v_type
== VDIR
) {
739 /* all we want from the lower node is the link count */
740 VATTR_WANTED(&va
, va_nlink
);
747 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
752 union_newsize(ap
->a_vp
, VNOVAL
, vap
->va_data_size
);
756 if ((vap
!= ap
->a_vap
) && (vap
->va_type
== VDIR
))
757 ap
->a_vap
->va_nlink
+= vap
->va_nlink
;
759 VATTR_RETURN(ap
->a_vap
, va_fsid
, ap
->a_vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
764 union_setattr(struct vnop_setattr_args
*ap
)
766 struct vnop_setattr_args {
768 struct vnode_attr *a_vap;
769 vfs_context_t a_context;
773 struct union_node
*un
= VTOUNION(ap
->a_vp
);
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.
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
)) {
786 error
= union_copyup(un
, (ap
->a_vap
->va_data_size
!= 0), ap
->a_context
);
794 * Try to set attributes in upper layer,
795 * otherwise return read-only filesystem error.
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
)) {
801 union_newsize(ap
->a_vp
, ap
->a_vap
->va_data_size
, VNOVAL
);
812 union_read(struct vnop_read_args
*ap
)
814 struct vnop_read_args {
818 vfs_context_t a_context;
823 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
825 error
= VNOP_READ(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
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.
834 struct union_node
*un
= VTOUNION(ap
->a_vp
);
835 off_t cur
= ap
->a_uio
->uio_offset
;
837 if (vp
== un
->un_uppervp
) {
838 if (cur
> un
->un_uppersz
) {
840 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
844 if (cur
> un
->un_lowersz
) {
846 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
856 union_write(struct vnop_write_args
*ap
)
858 struct vnop_write_args {
862 vfs_context_t a_context;
868 struct union_node
*un
= VTOUNION(ap
->a_vp
);
870 vp
= UPPERVP(ap
->a_vp
);
872 panic("union: missing upper layer in write");
874 error
= VNOP_WRITE(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
877 * the size of the underlying object may be changed by the
881 off_t cur
= ap
->a_uio
->uio_offset
;
883 if (cur
> un
->un_uppersz
) {
885 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
895 union_ioctl(struct vnop_ioctl_args
*ap
)
897 struct vnop_ioctl_args {
902 vfs_context_t a_context;
906 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
909 return (VCALL(ovp
, VOFFSET(vnop_ioctl
), ap
));
913 union_select(struct vnop_select_args
*ap
)
915 struct vnop_select_args {
920 vfs_context_t a_context;
924 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
927 return (VCALL(ovp
, VOFFSET(vnop_select
), ap
));
931 union_revoke(struct vnop_revoke_args
*ap
)
933 struct vnop_revoke_args {
936 vfs_context_t a_context;
940 struct vnode
*vp
= ap
->a_vp
;
943 VNOP_REVOKE(UPPERVP(vp
), ap
->a_flags
, ap
->a_context
);
945 VNOP_REVOKE(LOWERVP(vp
), ap
->a_flags
, ap
->a_context
);
952 union_mmap(struct vnop_mmap_args
*ap
)
954 struct vnop_mmap_args {
962 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
965 return (VCALL(ovp
, VOFFSET(vnop_mmap
), ap
));
969 union_mnomap(struct vnop_mnomap_args
*ap
)
971 struct vnop_mnomap_args {
979 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
982 return (VCALL(ovp
, VOFFSET(vnop_mnomap
), ap
));
986 union_fsync(struct vnop_fsync_args
*ap
)
988 struct vnop_fsync_args {
991 vfs_context_t a_context;
996 struct vnode
*targetvp
= OTHERVP(ap
->a_vp
);
998 if (targetvp
!= NULLVP
) {
1000 error
= VNOP_FSYNC(targetvp
, ap
->a_waitfor
, ap
->a_context
);
1007 union_remove(struct vnop_remove_args
*ap
)
1009 struct vnop_remove_args {
1010 struct vnode *a_dvp;
1012 struct componentname *a_cnp;
1013 vfs_context_t a_context;
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
;
1023 if (dun
->un_uppervp
== NULLVP
)
1024 panic("union remove: null upper vnode");
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
));
1031 if (un
->un_uppervp
!= NULLVP
) {
1032 struct vnode
*dvp
= dun
->un_uppervp
;
1033 struct vnode
*vp
= un
->un_uppervp
;
1035 flags
= ap
->a_flags
;
1036 if (vnode_isinuse(ap
->a_vp
, 0))
1038 if ((flags
& VNODE_REMOVE_NODELETEBUSY
) && (busydel
!= 0)) {
1041 if (union_dowhiteout(un
, cnp
->cn_context
))
1042 cnp
->cn_flags
|= DOWHITEOUT
;
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
);
1054 error
= VNOP_REMOVE(dvp
, vp
, cnp
, 0, ap
->a_context
);
1058 union_removed_upper(un
);
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
);
1073 union_link(struct vnop_link_args
*ap
)
1075 struct vnop_link_args {
1077 struct vnode *a_tdvp;
1078 struct componentname *a_cnp;
1079 vfs_context_t a_context;
1084 struct componentname
*cnp
= ap
->a_cnp
;
1085 struct union_node
*un
;
1089 un
= VTOUNION(ap
->a_tdvp
);
1091 if (ap
->a_tdvp
->v_op
!= ap
->a_vp
->v_op
) {
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
) {
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
);
1111 vp
= tun
->un_uppervp
;
1113 tdvp
= un
->un_uppervp
;
1122 error
= (VNOP_LINK(vp
, tdvp
, cnp
, ap
->a_context
));
1127 union_rename(struct vnop_rename_args
*ap
)
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;
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
;
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");
1154 * this should never happen in normal
1155 * operation but might if there was
1156 * a problem creating the top-level shadow
1163 fdvp
= un
->un_uppervp
;
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 */
1176 if (un
->un_lowervp
!= NULLVP
)
1177 ap
->a_fcnp
->cn_flags
|= DOWHITEOUT
;
1179 fvp
= un
->un_uppervp
;
1182 if (tdvp
->v_op
== union_vnodeop_p
) {
1183 struct union_node
*un
= VTOUNION(tdvp
);
1184 if (un
->un_uppervp
== NULLVP
) {
1186 * this should never happen in normal
1187 * operation but might if there was
1188 * a problem creating the top-level shadow
1191 if (UNNODE_FAULTIN(un
))
1192 panic("faultfs rename: No uppervp");
1197 tdvp
= un
->un_uppervp
;
1200 if (tvp
!= NULLVP
&& tvp
->v_op
== union_vnodeop_p
) {
1201 struct union_node
*un
= VTOUNION(tvp
);
1203 tvp
= un
->un_uppervp
;
1206 return (VNOP_RENAME(fdvp
, fvp
, ap
->a_fcnp
, tdvp
, tvp
, ap
->a_tcnp
, ap
->a_context
));
1213 union_mkdir(struct vnop_mkdir_args
*ap
)
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;
1224 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1225 struct vnode
*dvp
= un
->un_uppervp
;
1226 struct componentname
*cnp
= ap
->a_cnp
;
1228 if (dvp
!= NULLVP
) {
1233 /* note that this is a direct fallthrough to the filesystem */
1234 error
= VNOP_MKDIR(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
1239 error
= union_allocvp(ap
->a_vpp
, ap
->a_dvp
->v_mount
, ap
->a_dvp
,
1240 NULLVP
, cnp
, vp
, NULLVP
, 1);
1250 union_rmdir(struct vnop_rmdir_args
*ap
)
1252 struct vnop_rmdir_args {
1253 struct vnode *a_dvp;
1255 struct componentname *a_cnp;
1256 vfs_context_t a_context;
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
;
1266 /******* NODE HAS TO BE LOCKED ******/
1267 if (dun
->un_uppervp
== NULLVP
)
1268 panic("union rmdir: null upper vnode");
1270 if (un
->un_uppervp
!= NULLVP
) {
1271 struct vnode
*dvp
= dun
->un_uppervp
;
1272 struct vnode
*vp
= un
->un_uppervp
;
1274 if (vnode_isinuse(ap
->a_vp
, 0)) {
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
);
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
);
1293 union_removed_upper(un
);
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
);
1307 union_symlink(struct vnop_symlink_args
*ap
)
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;
1315 vfs_context_t a_context;
1319 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1320 struct vnode
*dvp
= un
->un_uppervp
;
1321 struct componentname
*cnp
= ap
->a_cnp
;
1323 if (dvp
!= NULLVP
) {
1327 error
= VNOP_SYMLINK(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_target
, ap
->a_context
);
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.
1342 union_readdir(struct vnop_readdir_args
*ap
)
1344 struct vnop_readdir_args {
1345 struct vnodeop_desc *a_desc;
1351 vfs_context_t a_context;
1355 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1356 struct vnode
*uvp
= un
->un_uppervp
;
1358 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
))
1365 return (VCALL(uvp
, VOFFSET(vnop_readdir
), ap
));
1369 union_readlink(struct vnop_readlink_args
*ap
)
1371 struct vnop_readlink_args {
1374 vfs_context_t a_context;
1379 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1382 error
= VCALL(vp
, VOFFSET(vnop_readlink
), ap
);
1388 union_inactive(struct vnop_inactive_args
*ap
)
1390 struct vnop_inactive_args {
1392 vfs_context_t a_context;
1396 struct vnode
*vp
= ap
->a_vp
;
1397 struct union_node
*un
= VTOUNION(vp
);
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.
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.
1413 if (un
->un_flags
& UN_DELETED
) {
1414 if(un
->un_uppervp
!= NULLVP
) {
1415 vnode_rele(un
->un_uppervp
);
1417 union_removed_upper(un
);
1420 if (un
->un_dircache
!= 0) {
1421 union_dircache_free(un
);
1423 if (un
->un_flags
& UN_DIRENVN
) {
1433 union_reclaim(struct vnop_reclaim_args
*ap
)
1435 struct vnop_reclaim_args {
1437 vfs_context_t a_context;
1443 union_freevp(ap
->a_vp
);
1450 union_blockmap(struct vnop_blockmap_args
*ap
)
1452 struct vnop_blockmap_args {
1464 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1467 error
= VCALL(vp
, VOFFSET(vnop_blockmap
), ap
);
1473 union_pathconf(struct vnop_pathconf_args
*ap
)
1475 struct vnop_pathconf_args {
1479 vfs_context_t a_context;
1484 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1487 error
= VCALL(vp
, VOFFSET(vnop_pathconf
), ap
);
1493 union_advlock(struct vnop_advlock_args
*ap
)
1495 struct vnop_advlock_args {
1501 vfs_context_t a_context;
1505 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
1508 return (VCALL(ovp
, VOFFSET(vnop_advlock
), ap
));
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.
1518 union_strategy(struct vnop_strategy_args
*ap
)
1520 struct vnop_strategy_args {
1525 struct buf
*bp
= ap
->a_bp
;
1527 struct vnode
*savedvp
;
1529 savedvp
= buf_vnode(bp
);
1530 buf_setvnode(bp
, OTHERVP(savedvp
));
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");
1540 error
= VNOP_STRATEGY(bp
);
1541 buf_setvnode(bp
, savedvp
);
1548 union_pagein(struct vnop_pagein_args
*ap
)
1550 struct vnop_pagein_args {
1553 vm_offset_t a_pl_offset,
1557 vfs_context_t a_context;
1562 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
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
);
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.
1574 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1575 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1577 if (vp
== un
->un_uppervp
) {
1578 if (cur
> un
->un_uppersz
) {
1580 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1584 if (cur
> un
->un_lowersz
) {
1586 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
1597 union_pageout(struct vnop_pageout_args
*ap
)
1599 struct vnop_pageout_args {
1602 vm_offset_t a_pl_offset,
1606 vfs_context_t a_context;
1612 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1614 vp
= UPPERVP(ap
->a_vp
);
1616 panic("union: missing upper layer in pageout");
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
);
1622 * the size of the underlying object may be changed by the
1626 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1628 if (cur
> un
->un_uppersz
) {
1630 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1638 /* Blktooff derives file offset for the given logical block number */
1640 union_blktooff(struct vnop_blktooff_args
*ap
)
1642 struct vnop_blktooff_args {
1650 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1652 error
= VNOP_BLKTOOFF(vp
, ap
->a_lblkno
, ap
->a_offset
);
1657 /* offtoblk derives file offset for the given logical block number */
1659 union_offtoblk(struct vnop_offtoblk_args
*ap
)
1661 struct vnop_offtoblk_args {
1664 daddr64_t *a_lblkno;
1669 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1671 error
= VNOP_OFFTOBLK(vp
, ap
->a_offset
, ap
->a_lblkno
);
1676 #define VOPFUNC int (*)(void *)
1679 * Global vfs data structures
1681 int (**union_vnodeop_p
)(void *);
1682 struct 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 */
1715 { &vnop_bwrite_desc
, (VOPFUNC
)union_bwrite
}, /* bwrite */
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
}
1725 struct vnodeopv_desc union_vnodeop_opv_desc
=
1726 { &union_vnodeop_p
, union_vnodeop_entries
};