2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
24 * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
25 * Copyright (c) 1992, 1993, 1994, 1995
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software contributed to Berkeley by
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95
62 #include <sys/param.h>
63 #include <sys/systm.h>
65 #include <sys/kauth.h>
69 #include <sys/types.h>
70 #include <sys/vnode_internal.h>
71 #include <sys/mount_internal.h>
72 #include <sys/namei.h>
73 #include <sys/malloc.h>
74 #include <sys/buf_internal.h>
75 #include <sys/queue.h>
77 #include <miscfs/union/union.h>
78 #include <vfs/vfs_support.h>
80 #include <sys/uio_internal.h>
82 #define FIXUP(un, p) { \
83 if (((un)->un_flags & UN_ULOCK) == 0) { \
90 struct union_node
*un
;
94 un
->un_flags
|= UN_ULOCK
;
98 union_lookup1(struct vnode
*udvp
, struct vnode
**dvpp
, struct vnode
**vpp
,
99 struct componentname
*cnp
)
102 vfs_context_t ctx
= cnp
->cn_context
;
110 * If stepping up the directory tree, check for going
111 * back across the mount point, in which case do what
112 * lookup would do by stepping back down the mount
115 if (cnp
->cn_flags
& ISDOTDOT
) {
116 while ((dvp
!= udvp
) && (dvp
->v_flag
& VROOT
)) {
118 * Don't do the NOCROSSMOUNT check
119 * at this level. By definition,
120 * union fs deals with namespaces, not
124 *dvpp
= dvp
= dvp
->v_mount
->mnt_vnodecovered
;
130 error
= VNOP_LOOKUP(dvp
, &tdvp
, cnp
, ctx
);
136 * Lastly check if the current node is a mount point in
137 * which case walk up the mount hierarchy making sure not to
138 * bump into the root of the mount tree (ie. dvp != udvp).
140 while (dvp
!= udvp
&& (dvp
->v_type
== VDIR
) &&
141 (mp
= dvp
->v_mountedhere
)) {
142 if (vfs_busy(mp
, LK_NOWAIT
)) {
146 error
= VFS_ROOT(mp
, &tdvp
, ctx
);
163 struct vnop_lookup_args
/* {
164 struct vnodeop_desc *a_desc;
166 struct vnode **a_vpp;
167 struct componentname *a_cnp;
168 vfs_context_t a_context;
173 struct vnode
*uppervp
, *lowervp
;
174 struct vnode
*upperdvp
, *lowerdvp
;
175 struct vnode
*dvp
= ap
->a_dvp
;
176 struct union_node
*dun
= VTOUNION(dvp
);
177 struct componentname
*cnp
= ap
->a_cnp
;
178 vfs_context_t ctx
= cnp
->cn_context
;
179 struct proc
*p
= vfs_context_proc(ctx
);
180 int lockparent
= cnp
->cn_flags
& LOCKPARENT
;
181 struct union_mount
*um
= MOUNTTOUNIONMOUNT(dvp
->v_mount
);
182 kauth_cred_t saved_cred
;
184 struct vnode_attr va
;
187 if (cnp
->cn_namelen
== 3 &&
188 cnp
->cn_nameptr
[2] == '.' &&
189 cnp
->cn_nameptr
[1] == '.' &&
190 cnp
->cn_nameptr
[0] == '.') {
191 dvp
= *ap
->a_vpp
= LOWERVP(ap
->a_dvp
);
200 cnp
->cn_flags
|= LOCKPARENT
;
202 upperdvp
= dun
->un_uppervp
;
203 lowerdvp
= dun
->un_lowervp
;
209 * do the lookup in the upper level.
210 * if that level comsumes additional pathnames,
211 * then assume that something special is going
212 * on and just return that vnode.
214 if (upperdvp
!= NULLVP
) {
216 uerror
= union_lookup1(um
->um_uppervp
, &upperdvp
,
218 /*if (uppervp == upperdvp)
219 dun->un_flags |= UN_KLOCK;*/
221 if (cnp
->cn_consume
!= 0) {
222 *ap
->a_vpp
= uppervp
;
224 cnp
->cn_flags
&= ~LOCKPARENT
;
227 if (uerror
== ENOENT
|| uerror
== EJUSTRETURN
) {
228 if (cnp
->cn_flags
& ISWHITEOUT
) {
230 } else if (lowerdvp
!= NULLVP
) {
232 VATTR_WANTED(&va
, va_flags
);
233 lerror
= vnode_getattr(upperdvp
, &va
, ap
->a_context
);
234 if (lerror
== 0 && (va
.va_flags
& OPAQUE
))
243 * in a similar way to the upper layer, do the lookup
244 * in the lower layer. this time, if there is some
245 * component magic going on, then vnode_put whatever we got
246 * back from the upper layer and return the lower vnode
249 if (lowerdvp
!= NULLVP
&& !iswhiteout
) {
253 * Only do a LOOKUP on the bottom node, since
254 * we won't be making changes to it anyway.
256 nameiop
= cnp
->cn_nameiop
;
257 cnp
->cn_nameiop
= LOOKUP
;
258 if (um
->um_op
== UNMNT_BELOW
) {
260 saved_cred
= cnp
->cn_context
->vc_ucred
;
261 cnp
->cn_context
->vc_ucred
= um
->um_cred
;
262 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
264 cnp
->cn_context
->vc_ucred
= saved_cred
;
266 lerror
= union_lookup1(um
->um_lowervp
, &lowerdvp
,
269 cnp
->cn_nameiop
= nameiop
;
271 if (cnp
->cn_consume
!= 0) {
272 if (uppervp
!= NULLVP
) {
276 *ap
->a_vpp
= lowervp
;
278 cnp
->cn_flags
&= ~LOCKPARENT
;
283 if ((cnp
->cn_flags
& ISDOTDOT
) && dun
->un_pvp
!= NULLVP
) {
284 lowervp
= LOWERVP(dun
->un_pvp
);
285 if (lowervp
!= NULLVP
) {
293 cnp
->cn_flags
&= ~LOCKPARENT
;
296 * at this point, we have uerror and lerror indicating
297 * possible errors with the lookups in the upper and lower
298 * layers. additionally, uppervp and lowervp are (locked)
299 * references to existing vnodes in the upper and lower layers.
301 * there are now three cases to consider.
302 * 1. if both layers returned an error, then return whatever
303 * error the upper layer generated.
305 * 2. if the top layer failed and the bottom layer succeeded
306 * then two subcases occur.
307 * a. the bottom vnode is not a directory, in which
308 * case just return a new union vnode referencing
309 * an empty top layer and the existing bottom layer.
310 * b. the bottom vnode is a directory, in which case
311 * create a new directory in the top-level and
312 * continue as in case 3.
314 * 3. if the top layer succeeded then return a new union
315 * vnode referencing whatever the new top layer and
316 * whatever the bottom layer returned.
322 if ((uerror
!= 0) && (lerror
!= 0)) {
327 if (uerror
!= 0 /* && (lerror == 0) */ ) {
328 if (lowervp
->v_type
== VDIR
) { /* case 2b. */
329 dun
->un_flags
&= ~UN_ULOCK
;
330 uerror
= union_mkshadow(um
, upperdvp
, cnp
, &uppervp
);
331 dun
->un_flags
|= UN_ULOCK
;
334 if (lowervp
!= NULLVP
) {
342 error
= union_allocvp(ap
->a_vpp
, dvp
->v_mount
, dvp
, upperdvp
, cnp
,
343 uppervp
, lowervp
, 1);
346 if (uppervp
!= NULLVP
)
348 if (lowervp
!= NULLVP
)
357 struct vnop_create_args
/* {
359 struct vnode **a_vpp;
360 struct componentname *a_cnp;
361 struct vnode_attr *a_vap;
362 vfs_context_t a_context;
365 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
366 struct vnode
*dvp
= un
->un_uppervp
;
367 struct componentname
*cnp
= ap
->a_cnp
;
368 vfs_context_t ctx
= cnp
->cn_context
;
369 struct proc
*p
= vfs_context_proc(ctx
);
378 un
->un_flags
|= UN_KLOCK
;
379 mp
= ap
->a_dvp
->v_mount
;
381 /* note that this is a direct passthrough to the filesystem */
382 error
= VNOP_CREATE(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
386 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
, cnp
, vp
,
397 struct vnop_whiteout_args
/* {
399 struct componentname *a_cnp;
401 vfs_context_t a_context;
404 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
405 struct componentname
*cnp
= ap
->a_cnp
;
406 vfs_context_t ctx
= cnp
->cn_context
;
407 struct proc
*p
= vfs_context_proc(ctx
);
409 if (un
->un_uppervp
== NULLVP
)
413 return (VNOP_WHITEOUT(un
->un_uppervp
, cnp
, ap
->a_flags
, ap
->a_context
));
418 struct vnop_mknod_args
/* {
420 struct vnode **a_vpp;
421 struct componentname *a_cnp;
422 struct vnode_attr *a_vap;
423 vfs_context_t a_context;
426 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
427 struct vnode
*dvp
= un
->un_uppervp
;
428 struct componentname
*cnp
= ap
->a_cnp
;
429 vfs_context_t ctx
= cnp
->cn_context
;
430 struct proc
*p
= vfs_context_proc(ctx
);
439 un
->un_flags
|= UN_KLOCK
;
440 mp
= ap
->a_dvp
->v_mount
;
442 /* note that this is a direct passthrough to the filesystem */
443 error
= VNOP_MKNOD(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
448 error
= union_allocvp(ap
->a_vpp
, mp
, NULLVP
, NULLVP
,
460 struct vnop_open_args
/* {
461 struct vnodeop_desc *a_desc;
464 vfs_context_t a_context;
467 struct union_node
*un
= VTOUNION(ap
->a_vp
);
469 int mode
= ap
->a_mode
;
470 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
471 struct proc
*p
= vfs_context_proc(ap
->a_context
);
475 * If there is an existing upper vp then simply open that.
477 tvp
= un
->un_uppervp
;
480 * If the lower vnode is being opened for writing, then
481 * copy the file contents to the upper vnode and open that,
482 * otherwise can simply open the lower vnode.
484 tvp
= un
->un_lowervp
;
485 if ((ap
->a_mode
& FWRITE
) && (tvp
->v_type
== VREG
)) {
486 error
= union_copyup(un
, (mode
&O_TRUNC
) == 0, cred
, p
);
488 error
= VNOP_OPEN(un
->un_uppervp
, mode
, ap
->a_context
);
493 * Just open the lower vnode
497 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
504 error
= VNOP_OPEN(tvp
, mode
, ap
->a_context
);
511 struct vnop_close_args
/* {
514 vfs_context_t a_context;
517 struct union_node
*un
= VTOUNION(ap
->a_vp
);
520 if ((vp
= un
->un_uppervp
) == NULLVP
) {
521 #ifdef UNION_DIAGNOSTIC
522 if (un
->un_openl
<= 0)
523 panic("union: un_openl cnt");
530 return (VCALL(vp
, VOFFSET(vnop_close
), ap
));
534 * Check access permission on the union vnode.
535 * The access check being enforced is to check
536 * against both the underlying vnode, and any
537 * copied vnode. This ensures that no additional
538 * file permissions are given away simply because
539 * the user caused an implicit file copy.
543 struct vnop_access_args
/* {
544 struct vnodeop_desc *a_desc;
547 vfs_context_t a_context;
550 struct union_node
*un
= VTOUNION(ap
->a_vp
);
551 struct proc
*p
= vfs_context_proc(ap
->a_context
);
555 if ((vp
= un
->un_uppervp
) != NULLVP
) {
558 return (VCALL(vp
, VOFFSET(vnop_access
), ap
));
561 if ((vp
= un
->un_lowervp
) != NULLVP
) {
563 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
565 struct union_mount
*um
= MOUNTTOUNIONMOUNT(vp
->v_mount
);
567 if (um
->um_op
== UNMNT_BELOW
) {
569 // ap->a_cred = um->um_cred;
570 error
= VCALL(vp
, VOFFSET(vnop_access
), ap
);
581 * We handle getattr only to change the fsid and
586 struct vnop_getattr_args
/* {
588 struct vnode_attr *a_vap;
589 vfs_context_t a_context;
593 struct union_node
*un
= VTOUNION(ap
->a_vp
);
594 struct vnode
*vp
= un
->un_uppervp
;
595 struct proc
*p
= vfs_context_proc(ap
->a_context
);
596 struct vnode_attr
*vap
;
597 struct vnode_attr va
;
601 * Some programs walk the filesystem hierarchy by counting
602 * links to directories to avoid stat'ing all the time.
603 * This means the link count on directories needs to be "correct".
604 * The only way to do that is to call getattr on both layers
605 * and fix up the link count. The link count will not necessarily
606 * be accurate but will be large enough to defeat the tree walkers.
614 * It's not clear whether vnop_getattr is to be
615 * called with the vnode locked or not. stat() calls
616 * it with (vp) locked, and fstat calls it with
618 * In the mean time, compensate here by checking
619 * the union_node's lock flag.
621 if (un
->un_flags
& UN_LOCKED
)
624 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
627 union_newsize(ap
->a_vp
, vap
->va_data_size
, VNOVAL
);
632 } else if (vp
->v_type
== VDIR
) {
635 /* all we want from the lower node is the link count */
636 VATTR_WANTED(&va
, va_nlink
);
643 error
= vnode_getattr(vp
, vap
, ap
->a_context
);
646 union_newsize(ap
->a_vp
, VNOVAL
, vap
->va_data_size
);
649 if ((vap
!= ap
->a_vap
) && (vap
->va_type
== VDIR
))
650 ap
->a_vap
->va_nlink
+= vap
->va_nlink
;
652 VATTR_RETURN(ap
->a_vap
, va_fsid
, ap
->a_vp
->v_mount
->mnt_vfsstat
.f_fsid
.val
[0]);
658 struct vnop_setattr_args
/* {
660 struct vnode_attr *a_vap;
661 vfs_context_t a_context;
664 struct union_node
*un
= VTOUNION(ap
->a_vp
);
665 struct proc
*p
= vfs_context_proc(ap
->a_context
);
666 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
670 * Handle case of truncating lower object to zero size,
671 * by creating a zero length upper object. This is to
672 * handle the case of open with O_TRUNC and O_CREAT.
674 if (VATTR_IS_ACTIVE(ap
->a_vap
, va_data_size
) &&
675 (un
->un_uppervp
== NULLVP
) &&
676 /* assert(un->un_lowervp != NULLVP) */
677 (un
->un_lowervp
->v_type
== VREG
)) {
678 error
= union_copyup(un
, (ap
->a_vap
->va_data_size
!= 0), cred
, p
);
684 * Try to set attributes in upper layer,
685 * otherwise return read-only filesystem error.
687 if (un
->un_uppervp
!= NULLVP
) {
689 error
= vnode_setattr(un
->un_uppervp
, ap
->a_vap
, ap
->a_context
);
690 if ((error
== 0) && VATTR_IS_ACTIVE(ap
->a_vap
, va_data_size
))
691 union_newsize(ap
->a_vp
, ap
->a_vap
->va_data_size
, VNOVAL
);
701 struct vnop_read_args
/* {
705 vfs_context_t a_context;
709 struct proc
*p
= vfs_context_proc(ap
->a_context
);
710 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
711 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
714 FIXUP(VTOUNION(ap
->a_vp
), p
);
715 error
= VNOP_READ(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
719 * perhaps the size of the underlying object has changed under
720 * our feet. take advantage of the offset information present
721 * in the uio structure.
724 struct union_node
*un
= VTOUNION(ap
->a_vp
);
725 off_t cur
= ap
->a_uio
->uio_offset
;
727 if (vp
== un
->un_uppervp
) {
728 if (cur
> un
->un_uppersz
)
729 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
731 if (cur
> un
->un_lowersz
)
732 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
741 struct vnop_read_args
/* {
745 vfs_context_t a_context;
750 struct union_node
*un
= VTOUNION(ap
->a_vp
);
751 struct proc
*p
= vfs_context_proc(ap
->a_context
);
753 vp
= UPPERVP(ap
->a_vp
);
755 panic("union: missing upper layer in write");
758 error
= VNOP_WRITE(vp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
761 * the size of the underlying object may be changed by the
765 off_t cur
= ap
->a_uio
->uio_offset
;
767 if (cur
> un
->un_uppersz
)
768 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
777 struct vnop_ioctl_args
/* {
782 vfs_context_t a_context;
785 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
788 return (VCALL(ovp
, VOFFSET(vnop_ioctl
), ap
));
793 struct vnop_select_args
/* {
798 vfs_context_t a_context;
801 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
804 return (VCALL(ovp
, VOFFSET(vnop_select
), ap
));
809 struct vnop_revoke_args
/* {
812 vfs_context_t a_context;
815 struct vnode
*vp
= ap
->a_vp
;
818 VNOP_REVOKE(UPPERVP(vp
), ap
->a_flags
, ap
->a_context
);
820 VNOP_REVOKE(LOWERVP(vp
), ap
->a_flags
, ap
->a_context
);
826 struct vnop_mmap_args
/* {
833 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
836 return (VCALL(ovp
, VOFFSET(vnop_mmap
), ap
));
841 struct vnop_fsync_args
/* {
844 vfs_context_t a_context;
848 struct proc
*p
= vfs_context_proc(ap
->a_context
);
849 struct vnode
*targetvp
= OTHERVP(ap
->a_vp
);
851 if (targetvp
!= NULLVP
) {
852 int dolock
= (targetvp
== LOWERVP(ap
->a_vp
));
855 FIXUP(VTOUNION(ap
->a_vp
), p
);
856 error
= VNOP_FSYNC(targetvp
, ap
->a_waitfor
, ap
->a_context
);
864 struct vnop_remove_args
/* {
867 struct componentname *a_cnp;
868 vfs_context_t a_context;
872 struct union_node
*dun
= VTOUNION(ap
->a_dvp
);
873 struct union_node
*un
= VTOUNION(ap
->a_vp
);
874 struct componentname
*cnp
= ap
->a_cnp
;
875 vfs_context_t ctx
= cnp
->cn_context
;
876 struct proc
*p
= vfs_context_proc(ctx
);
878 if (dun
->un_uppervp
== NULLVP
)
879 panic("union remove: null upper vnode");
881 if (un
->un_uppervp
!= NULLVP
) {
882 struct vnode
*dvp
= dun
->un_uppervp
;
883 struct vnode
*vp
= un
->un_uppervp
;
886 dun
->un_flags
|= UN_KLOCK
;
888 un
->un_flags
|= UN_KLOCK
;
890 if (union_dowhiteout(un
, cnp
->cn_context
))
891 cnp
->cn_flags
|= DOWHITEOUT
;
892 error
= VNOP_REMOVE(dvp
, vp
, cnp
, 0, ap
->a_context
);
894 union_removed_upper(un
);
897 error
= union_mkwhiteout(
898 MOUNTTOUNIONMOUNT(UNIONTOV(dun
)->v_mount
),
899 dun
->un_uppervp
, ap
->a_cnp
, un
->un_path
);
907 struct vnop_link_args
/* {
909 struct vnode *a_tdvp;
910 struct componentname *a_cnp;
911 vfs_context_t a_context;
915 struct componentname
*cnp
= ap
->a_cnp
;
916 vfs_context_t ctx
= cnp
->cn_context
;
917 struct proc
*p
= vfs_context_proc(ctx
);
918 struct union_node
*un
;
922 un
= VTOUNION(ap
->a_tdvp
);
924 if (ap
->a_tdvp
->v_op
!= ap
->a_vp
->v_op
) {
927 struct union_node
*tun
= VTOUNION(ap
->a_vp
);
928 if (tun
->un_uppervp
== NULLVP
) {
929 if (un
->un_uppervp
== tun
->un_dirvp
) {
930 un
->un_flags
&= ~UN_ULOCK
;
932 error
= union_copyup(tun
, 1, vfs_context_ucred(ctx
), p
);
933 if (un
->un_uppervp
== tun
->un_dirvp
) {
934 un
->un_flags
|= UN_ULOCK
;
937 vp
= tun
->un_uppervp
;
939 tdvp
= un
->un_uppervp
;
949 un
->un_flags
|= UN_KLOCK
;
951 return (VNOP_LINK(vp
, tdvp
, cnp
, ap
->a_context
));
956 struct vnop_rename_args
/* {
957 struct vnode *a_fdvp;
959 struct componentname *a_fcnp;
960 struct vnode *a_tdvp;
962 struct componentname *a_tcnp;
963 vfs_context_t a_context;
968 struct vnode
*fdvp
= ap
->a_fdvp
;
969 struct vnode
*fvp
= ap
->a_fvp
;
970 struct vnode
*tdvp
= ap
->a_tdvp
;
971 struct vnode
*tvp
= ap
->a_tvp
;
973 if (fdvp
->v_op
== union_vnodeop_p
) { /* always true */
974 struct union_node
*un
= VTOUNION(fdvp
);
975 if (un
->un_uppervp
== NULLVP
) {
977 * this should never happen in normal
978 * operation but might if there was
979 * a problem creating the top-level shadow
986 fdvp
= un
->un_uppervp
;
990 if (fvp
->v_op
== union_vnodeop_p
) { /* always true */
991 struct union_node
*un
= VTOUNION(fvp
);
992 if (un
->un_uppervp
== NULLVP
) {
993 /* XXX: should do a copyup */
998 if (un
->un_lowervp
!= NULLVP
)
999 ap
->a_fcnp
->cn_flags
|= DOWHITEOUT
;
1001 fvp
= un
->un_uppervp
;
1005 if (tdvp
->v_op
== union_vnodeop_p
) {
1006 struct union_node
*un
= VTOUNION(tdvp
);
1007 if (un
->un_uppervp
== NULLVP
) {
1009 * this should never happen in normal
1010 * operation but might if there was
1011 * a problem creating the top-level shadow
1018 tdvp
= un
->un_uppervp
;
1020 un
->un_flags
|= UN_KLOCK
;
1023 if (tvp
!= NULLVP
&& tvp
->v_op
== union_vnodeop_p
) {
1024 struct union_node
*un
= VTOUNION(tvp
);
1026 tvp
= un
->un_uppervp
;
1027 if (tvp
!= NULLVP
) {
1029 un
->un_flags
|= UN_KLOCK
;
1033 return (VNOP_RENAME(fdvp
, fvp
, ap
->a_fcnp
, tdvp
, tvp
, ap
->a_tcnp
, ap
->a_context
));
1041 struct vnop_mkdir_args
/* {
1042 struct vnode *a_dvp;
1043 struct vnode **a_vpp;
1044 struct componentname *a_cnp;
1045 struct vnode_attr *a_vap;
1046 vfs_context_t a_context;
1049 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1050 struct vnode
*dvp
= un
->un_uppervp
;
1051 struct componentname
*cnp
= ap
->a_cnp
;
1052 vfs_context_t ctx
= cnp
->cn_context
;
1053 struct proc
*p
= vfs_context_proc(ctx
);
1055 if (dvp
!= NULLVP
) {
1060 un
->un_flags
|= UN_KLOCK
;
1062 /* note that this is a direct fallthrough to the filesystem */
1063 error
= VNOP_MKDIR(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_context
);
1067 error
= union_allocvp(ap
->a_vpp
, ap
->a_dvp
->v_mount
, ap
->a_dvp
,
1068 NULLVP
, cnp
, vp
, NULLVP
, 1);
1078 struct vnop_rmdir_args
/* {
1079 struct vnode *a_dvp;
1081 struct componentname *a_cnp;
1082 vfs_context_t a_context;
1086 struct union_node
*dun
= VTOUNION(ap
->a_dvp
);
1087 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1088 struct componentname
*cnp
= ap
->a_cnp
;
1089 vfs_context_t ctx
= cnp
->cn_context
;
1090 struct proc
*p
= vfs_context_proc(ctx
);
1092 if (dun
->un_uppervp
== NULLVP
)
1093 panic("union rmdir: null upper vnode");
1095 if (un
->un_uppervp
!= NULLVP
) {
1096 struct vnode
*dvp
= dun
->un_uppervp
;
1097 struct vnode
*vp
= un
->un_uppervp
;
1101 dun
->un_flags
|= UN_KLOCK
;
1104 un
->un_flags
|= UN_KLOCK
;
1106 if (union_dowhiteout(un
, cnp
->cn_context
))
1107 cnp
->cn_flags
|= DOWHITEOUT
;
1108 error
= VNOP_RMDIR(dvp
, vp
, ap
->a_cnp
, ap
->a_context
);
1110 union_removed_upper(un
);
1113 error
= union_mkwhiteout(
1114 MOUNTTOUNIONMOUNT(UNIONTOV(dun
)->v_mount
),
1115 dun
->un_uppervp
, ap
->a_cnp
, un
->un_path
);
1122 struct vnop_symlink_args
/* {
1123 struct vnode *a_dvp;
1124 struct vnode **a_vpp;
1125 struct componentname *a_cnp;
1126 struct vnode_attr *a_vap;
1128 vfs_context_t a_context;
1131 struct union_node
*un
= VTOUNION(ap
->a_dvp
);
1132 struct vnode
*dvp
= un
->un_uppervp
;
1133 struct componentname
*cnp
= ap
->a_cnp
;
1134 vfs_context_t ctx
= cnp
->cn_context
;
1135 struct proc
*p
= vfs_context_proc(ctx
);
1137 if (dvp
!= NULLVP
) {
1142 un
->un_flags
|= UN_KLOCK
;
1144 error
= VNOP_SYMLINK(dvp
, &vp
, cnp
, ap
->a_vap
, ap
->a_target
, ap
->a_context
);
1145 *ap
->a_vpp
= NULLVP
;
1152 * union_readdir works in concert with getdirentries and
1153 * readdir(3) to provide a list of entries in the unioned
1154 * directories. getdirentries is responsible for walking
1155 * down the union stack. readdir(3) is responsible for
1156 * eliminating duplicate names from the returned data stream.
1160 struct vnop_readdir_args
/* {
1161 struct vnodeop_desc *a_desc;
1167 vfs_context_t a_context;
1170 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1171 struct vnode
*uvp
= un
->un_uppervp
;
1172 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1174 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
))
1182 return (VCALL(uvp
, VOFFSET(vnop_readdir
), ap
));
1187 struct vnop_readlink_args
/* {
1190 vfs_context_t a_context;
1194 struct uio
*uio
= ap
->a_uio
;
1195 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1196 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1197 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1200 FIXUP(VTOUNION(ap
->a_vp
), p
);
1202 error
= VCALL(vp
, VOFFSET(vnop_readlink
), ap
);
1209 struct vnop_inactive_args
/* {
1211 vfs_context_t a_context;
1214 struct vnode
*vp
= ap
->a_vp
;
1215 struct union_node
*un
= VTOUNION(vp
);
1219 * Do nothing (and _don't_ bypass).
1220 * Wait to vnode_put lowervp until reclaim,
1221 * so that until then our union_node is in the
1222 * cache and reusable.
1224 * NEEDSWORK: Someday, consider inactive'ing
1225 * the lowervp and then trying to reactivate it
1226 * with capabilities (v_id)
1227 * like they do in the name lookup cache code.
1228 * That's too much work for now.
1231 if (un
->un_dircache
!= 0) {
1232 for (vpp
= un
->un_dircache
; *vpp
!= NULLVP
; vpp
++)
1234 _FREE(un
->un_dircache
, M_TEMP
);
1235 un
->un_dircache
= 0;
1238 if ((un
->un_flags
& UN_CACHED
) == 0)
1246 struct vnop_reclaim_args
/* {
1248 vfs_context_t a_context;
1252 union_freevp(ap
->a_vp
);
1259 struct vnop_blockmap_args
/* {
1270 struct proc
*p
= current_proc(); /* XXX */
1271 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1272 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1275 FIXUP(VTOUNION(ap
->a_vp
), p
);
1277 error
= VCALL(vp
, VOFFSET(vnop_blockmap
), ap
);
1284 struct vnop_pathconf_args
/* {
1288 vfs_context_t a_context;
1292 struct proc
*p
= current_proc(); /* XXX */
1293 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1294 int dolock
= (vp
== LOWERVP(ap
->a_vp
));
1297 FIXUP(VTOUNION(ap
->a_vp
), p
);
1299 error
= VCALL(vp
, VOFFSET(vnop_pathconf
), ap
);
1306 struct vnop_advlock_args
/* {
1312 vfs_context_t a_context;
1315 register struct vnode
*ovp
= OTHERVP(ap
->a_vp
);
1318 return (VCALL(ovp
, VOFFSET(vnop_advlock
), ap
));
1323 * XXX - vnop_strategy must be hand coded because it has no
1324 * vnode in its arguments.
1325 * This goes away with a merged VM/buffer cache.
1329 struct vnop_strategy_args
/* {
1333 struct buf
*bp
= ap
->a_bp
;
1335 struct vnode
*savedvp
;
1337 savedvp
= buf_vnode(bp
);
1338 buf_setvnode(bp
, OTHERVP(savedvp
));
1341 if (buf_vnode(bp
) == NULLVP
)
1342 panic("union_strategy: nil vp");
1343 if (((buf_flags(bp
) & B_READ
) == 0) &&
1344 (buf_vnode(bp
) == LOWERVP(savedvp
)))
1345 panic("union_strategy: writing to lowervp");
1348 error
= VNOP_STRATEGY(bp
);
1349 buf_setvnode(bp
, savedvp
);
1357 struct vnop_pagein_args
/* {
1360 vm_offset_t a_pl_offset,
1364 vfs_context_t a_context;
1368 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1370 error
= VNOP_PAGEIN(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
1371 ap
->a_size
, ap
->a_flags
, ap
->a_context
);
1375 * perhaps the size of the underlying object has changed under
1376 * our feet. take advantage of the offset information present
1377 * in the uio structure.
1380 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1381 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1383 if (vp
== un
->un_uppervp
) {
1384 if (cur
> un
->un_uppersz
)
1385 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1387 if (cur
> un
->un_lowersz
)
1388 union_newsize(ap
->a_vp
, VNOVAL
, cur
);
1398 struct vnop_pageout_args
/* {
1401 vm_offset_t a_pl_offset,
1405 vfs_context_t a_context;
1410 struct union_node
*un
= VTOUNION(ap
->a_vp
);
1412 vp
= UPPERVP(ap
->a_vp
);
1414 panic("union: missing upper layer in pageout");
1416 error
= VNOP_PAGEOUT(vp
, ap
->a_pl
, ap
->a_pl_offset
, ap
->a_f_offset
,
1417 ap
->a_size
, ap
->a_flags
, ap
->a_context
);
1420 * the size of the underlying object may be changed by the
1424 off_t cur
= ap
->a_f_offset
+ (off_t
)ap
->a_pl_offset
;
1426 if (cur
> un
->un_uppersz
)
1427 union_newsize(ap
->a_vp
, cur
, VNOVAL
);
1433 /* Blktooff derives file offset for the given logical block number */
1436 struct vnop_blktooff_args
/* {
1443 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1445 error
= VNOP_BLKTOOFF(vp
, ap
->a_lblkno
, ap
->a_offset
);
1450 /* offtoblk derives file offset for the given logical block number */
1453 struct vnop_offtoblk_args
/* {
1456 daddr64_t *a_lblkno;
1460 struct vnode
*vp
= OTHERVP(ap
->a_vp
);
1462 error
= VNOP_OFFTOBLK(vp
, ap
->a_offset
, ap
->a_lblkno
);
1467 #define VOPFUNC int (*)(void *)
1470 * Global vfs data structures
1472 int (**union_vnodeop_p
)(void *);
1473 struct vnodeopv_entry_desc union_vnodeop_entries
[] = {
1474 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
1475 { &vnop_lookup_desc
, (VOPFUNC
)union_lookup
}, /* lookup */
1476 { &vnop_create_desc
, (VOPFUNC
)union_create
}, /* create */
1477 { &vnop_whiteout_desc
, (VOPFUNC
)union_whiteout
}, /* whiteout */
1478 { &vnop_mknod_desc
, (VOPFUNC
)union_mknod
}, /* mknod */
1479 { &vnop_open_desc
, (VOPFUNC
)union_open
}, /* open */
1480 { &vnop_close_desc
, (VOPFUNC
)union_close
}, /* close */
1481 { &vnop_access_desc
, (VOPFUNC
)union_access
}, /* access */
1482 { &vnop_getattr_desc
, (VOPFUNC
)union_getattr
}, /* getattr */
1483 { &vnop_setattr_desc
, (VOPFUNC
)union_setattr
}, /* setattr */
1484 { &vnop_read_desc
, (VOPFUNC
)union_read
}, /* read */
1485 { &vnop_write_desc
, (VOPFUNC
)union_write
}, /* write */
1486 { &vnop_ioctl_desc
, (VOPFUNC
)union_ioctl
}, /* ioctl */
1487 { &vnop_select_desc
, (VOPFUNC
)union_select
}, /* select */
1488 { &vnop_revoke_desc
, (VOPFUNC
)union_revoke
}, /* revoke */
1489 { &vnop_mmap_desc
, (VOPFUNC
)union_mmap
}, /* mmap */
1490 { &vnop_fsync_desc
, (VOPFUNC
)union_fsync
}, /* fsync */
1491 { &vnop_remove_desc
, (VOPFUNC
)union_remove
}, /* remove */
1492 { &vnop_link_desc
, (VOPFUNC
)union_link
}, /* link */
1493 { &vnop_rename_desc
, (VOPFUNC
)union_rename
}, /* rename */
1494 { &vnop_mkdir_desc
, (VOPFUNC
)union_mkdir
}, /* mkdir */
1495 { &vnop_rmdir_desc
, (VOPFUNC
)union_rmdir
}, /* rmdir */
1496 { &vnop_symlink_desc
, (VOPFUNC
)union_symlink
}, /* symlink */
1497 { &vnop_readdir_desc
, (VOPFUNC
)union_readdir
}, /* readdir */
1498 { &vnop_readlink_desc
, (VOPFUNC
)union_readlink
}, /* readlink */
1499 { &vnop_inactive_desc
, (VOPFUNC
)union_inactive
}, /* inactive */
1500 { &vnop_reclaim_desc
, (VOPFUNC
)union_reclaim
}, /* reclaim */
1501 { &vnop_strategy_desc
, (VOPFUNC
)union_strategy
}, /* strategy */
1502 { &vnop_pathconf_desc
, (VOPFUNC
)union_pathconf
}, /* pathconf */
1503 { &vnop_advlock_desc
, (VOPFUNC
)union_advlock
}, /* advlock */
1505 { &vnop_bwrite_desc
, (VOPFUNC
)union_bwrite
}, /* bwrite */
1507 { &vnop_pagein_desc
, (VOPFUNC
)union_pagein
}, /* Pagein */
1508 { &vnop_pageout_desc
, (VOPFUNC
)union_pageout
}, /* Pageout */
1509 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1510 { &vnop_blktooff_desc
, (VOPFUNC
)union_blktooff
}, /* blktooff */
1511 { &vnop_offtoblk_desc
, (VOPFUNC
)union_offtoblk
}, /* offtoblk */
1512 { &vnop_blockmap_desc
, (VOPFUNC
)union_blockmap
}, /* blockmap */
1513 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1515 struct vnodeopv_desc union_vnodeop_opv_desc
=
1516 { &union_vnodeop_p
, union_vnodeop_entries
};