]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/union/union_vnops.c
xnu-344.34.tar.gz
[apple/xnu.git] / bsd / miscfs / union / union_vnops.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
de355530
A
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.
1c79356b 11 *
de355530
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
de355530
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23/*
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.
27 *
28 * This code is derived from software contributed to Berkeley by
29 * Jan-Simon Pendry.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
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.
46 *
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
57 * SUCH DAMAGE.
58 *
59 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95
60 */
61
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/proc.h>
65#include <sys/file.h>
66#include <sys/time.h>
67#include <sys/stat.h>
68#include <sys/types.h>
69#include <sys/vnode.h>
70#include <sys/mount.h>
71#include <sys/namei.h>
72#include <sys/malloc.h>
73#include <sys/buf.h>
74#include <sys/queue.h>
75#include <sys/lock.h>
76#include <miscfs/union/union.h>
77#include <vfs/vfs_support.h>
78#include <sys/ubc.h>
79
80#define FIXUP(un, p) { \
81 if (((un)->un_flags & UN_ULOCK) == 0) { \
82 union_fixup(un, p); \
83 } \
84}
85
86static void
87union_fixup(un, p)
88 struct union_node *un;
89 struct proc *p;
90{
91
92 vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
93 un->un_flags |= UN_ULOCK;
94}
95
96static int
97union_lookup1(udvp, dvpp, vpp, cnp)
98 struct vnode *udvp;
99 struct vnode **dvpp;
100 struct vnode **vpp;
101 struct componentname *cnp;
102{
103 int error;
104 struct proc *p = cnp->cn_proc;
105 struct vnode *tdvp;
106 struct vnode *dvp;
107 struct mount *mp;
108
109 dvp = *dvpp;
110
111 /*
112 * If stepping up the directory tree, check for going
113 * back across the mount point, in which case do what
114 * lookup would do by stepping back down the mount
115 * hierarchy.
116 */
117 if (cnp->cn_flags & ISDOTDOT) {
118 while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
119 /*
120 * Don't do the NOCROSSMOUNT check
121 * at this level. By definition,
122 * union fs deals with namespaces, not
123 * filesystems.
124 */
125 tdvp = dvp;
126 *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
127 vput(tdvp);
128 VREF(dvp);
129 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
130 }
131 }
132
133 error = VOP_LOOKUP(dvp, &tdvp, cnp);
134 if (error)
135 return (error);
136
137 /*
138 * The parent directory will have been unlocked, unless lookup
139 * found the last component. In which case, re-lock the node
140 * here to allow it to be unlocked again (phew) in union_lookup.
141 */
142 if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
143 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
144
145 dvp = tdvp;
146
147 /*
148 * Lastly check if the current node is a mount point in
149 * which case walk up the mount hierarchy making sure not to
150 * bump into the root of the mount tree (ie. dvp != udvp).
151 */
152 while (dvp != udvp && (dvp->v_type == VDIR) &&
153 (mp = dvp->v_mountedhere)) {
154
155 if (vfs_busy(mp, 0, 0, p))
156 continue;
157
158 error = VFS_ROOT(mp, &tdvp);
159 vfs_unbusy(mp, p);
160 if (error) {
161 vput(dvp);
162 return (error);
163 }
164
165 vput(dvp);
166 dvp = tdvp;
167 }
168
169 *vpp = dvp;
170 return (0);
171}
172
173int
174union_lookup(ap)
175 struct vop_lookup_args /* {
176 struct vnodeop_desc *a_desc;
177 struct vnode *a_dvp;
178 struct vnode **a_vpp;
179 struct componentname *a_cnp;
180 } */ *ap;
181{
182 int error;
183 int uerror, lerror;
184 struct vnode *uppervp, *lowervp;
185 struct vnode *upperdvp, *lowerdvp;
186 struct vnode *dvp = ap->a_dvp;
187 struct union_node *dun = VTOUNION(dvp);
188 struct componentname *cnp = ap->a_cnp;
189 struct proc *p = cnp->cn_proc;
190 int lockparent = cnp->cn_flags & LOCKPARENT;
191 int rdonly = cnp->cn_flags & RDONLY;
192 struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
193 struct ucred *saved_cred;
194 int iswhiteout;
195 struct vattr va;
196
197#ifdef notyet
198 if (cnp->cn_namelen == 3 &&
199 cnp->cn_nameptr[2] == '.' &&
200 cnp->cn_nameptr[1] == '.' &&
201 cnp->cn_nameptr[0] == '.') {
202 dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
203 if (dvp == NULLVP)
204 return (ENOENT);
205 VREF(dvp);
206 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
207 if (!lockparent || !(cnp->cn_flags & ISLASTCN))
208 VOP_UNLOCK(ap->a_dvp, 0, p);
209 return (0);
210 }
211#endif
212
213 cnp->cn_flags |= LOCKPARENT;
214
215 upperdvp = dun->un_uppervp;
216 lowerdvp = dun->un_lowervp;
217 uppervp = NULLVP;
218 lowervp = NULLVP;
219 iswhiteout = 0;
220
221 /*
222 * do the lookup in the upper level.
223 * if that level comsumes additional pathnames,
224 * then assume that something special is going
225 * on and just return that vnode.
226 */
227 if (upperdvp != NULLVP) {
228 FIXUP(dun, p);
229 uerror = union_lookup1(um->um_uppervp, &upperdvp,
230 &uppervp, cnp);
231 /*if (uppervp == upperdvp)
232 dun->un_flags |= UN_KLOCK;*/
233
234 if (cnp->cn_consume != 0) {
235 *ap->a_vpp = uppervp;
236 if (!lockparent)
237 cnp->cn_flags &= ~LOCKPARENT;
238 return (uerror);
239 }
240 if (uerror == ENOENT || uerror == EJUSTRETURN) {
241 if (cnp->cn_flags & ISWHITEOUT) {
242 iswhiteout = 1;
243 } else if (lowerdvp != NULLVP) {
244 lerror = VOP_GETATTR(upperdvp, &va,
245 cnp->cn_cred, cnp->cn_proc);
246 if (lerror == 0 && (va.va_flags & OPAQUE))
247 iswhiteout = 1;
248 }
249 }
250 } else {
251 uerror = ENOENT;
252 }
253
254 /*
255 * in a similar way to the upper layer, do the lookup
256 * in the lower layer. this time, if there is some
257 * component magic going on, then vput whatever we got
258 * back from the upper layer and return the lower vnode
259 * instead.
260 */
261 if (lowerdvp != NULLVP && !iswhiteout) {
262 int nameiop;
263
264 vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, p);
265
266 /*
267 * Only do a LOOKUP on the bottom node, since
268 * we won't be making changes to it anyway.
269 */
270 nameiop = cnp->cn_nameiop;
271 cnp->cn_nameiop = LOOKUP;
272 if (um->um_op == UNMNT_BELOW) {
273 saved_cred = cnp->cn_cred;
274 cnp->cn_cred = um->um_cred;
275 }
276 lerror = union_lookup1(um->um_lowervp, &lowerdvp,
277 &lowervp, cnp);
278 if (um->um_op == UNMNT_BELOW)
279 cnp->cn_cred = saved_cred;
280 cnp->cn_nameiop = nameiop;
281
282 if (lowervp != lowerdvp)
283 VOP_UNLOCK(lowerdvp, 0, p);
284
285 if (cnp->cn_consume != 0) {
286 if (uppervp != NULLVP) {
287 if (uppervp == upperdvp)
288 vrele(uppervp);
289 else
290 vput(uppervp);
291 uppervp = NULLVP;
292 }
293 *ap->a_vpp = lowervp;
294 if (!lockparent)
295 cnp->cn_flags &= ~LOCKPARENT;
296 return (lerror);
297 }
298 } else {
299 lerror = ENOENT;
300 if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
301 lowervp = LOWERVP(dun->un_pvp);
302 if (lowervp != NULLVP) {
303 VREF(lowervp);
304 vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, p);
305 lerror = 0;
306 }
307 }
308 }
309
310 if (!lockparent)
311 cnp->cn_flags &= ~LOCKPARENT;
312
313 /*
314 * at this point, we have uerror and lerror indicating
315 * possible errors with the lookups in the upper and lower
316 * layers. additionally, uppervp and lowervp are (locked)
317 * references to existing vnodes in the upper and lower layers.
318 *
319 * there are now three cases to consider.
320 * 1. if both layers returned an error, then return whatever
321 * error the upper layer generated.
322 *
323 * 2. if the top layer failed and the bottom layer succeeded
324 * then two subcases occur.
325 * a. the bottom vnode is not a directory, in which
326 * case just return a new union vnode referencing
327 * an empty top layer and the existing bottom layer.
328 * b. the bottom vnode is a directory, in which case
329 * create a new directory in the top-level and
330 * continue as in case 3.
331 *
332 * 3. if the top layer succeeded then return a new union
333 * vnode referencing whatever the new top layer and
334 * whatever the bottom layer returned.
335 */
336
337 *ap->a_vpp = NULLVP;
338
339 /* case 1. */
340 if ((uerror != 0) && (lerror != 0)) {
341 return (uerror);
342 }
343
344 /* case 2. */
345 if (uerror != 0 /* && (lerror == 0) */ ) {
346 if (lowervp->v_type == VDIR) { /* case 2b. */
347 dun->un_flags &= ~UN_ULOCK;
348 VOP_UNLOCK(upperdvp, 0, p);
349 uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
350 vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY, p);
351 dun->un_flags |= UN_ULOCK;
352
353 if (uerror) {
354 if (lowervp != NULLVP) {
355 vput(lowervp);
356 lowervp = NULLVP;
357 }
358 return (uerror);
359 }
360 }
361 }
362
363 if (lowervp != NULLVP)
364 VOP_UNLOCK(lowervp, 0, p);
365
366 error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
367 uppervp, lowervp, 1);
368
369 if (error) {
370 if (uppervp != NULLVP)
371 vput(uppervp);
372 if (lowervp != NULLVP)
373 vrele(lowervp);
374 } else {
375 if (*ap->a_vpp != dvp)
376 if (!lockparent || !(cnp->cn_flags & ISLASTCN))
377 VOP_UNLOCK(dvp, 0, p);
378 }
379
380 return (error);
381}
382
383int
384union_create(ap)
385 struct vop_create_args /* {
386 struct vnode *a_dvp;
387 struct vnode **a_vpp;
388 struct componentname *a_cnp;
389 struct vattr *a_vap;
390 } */ *ap;
391{
392 struct union_node *un = VTOUNION(ap->a_dvp);
393 struct vnode *dvp = un->un_uppervp;
394 struct componentname *cnp = ap->a_cnp;
395 struct proc *p = cnp->cn_proc;
396
397 if (dvp != NULLVP) {
398 int error;
399 struct vnode *vp;
400 struct mount *mp;
401
402 FIXUP(un, p);
403
404 VREF(dvp);
405 un->un_flags |= UN_KLOCK;
406 mp = ap->a_dvp->v_mount;
407 vput(ap->a_dvp);
408 error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap);
409 if (error)
410 return (error);
411
412 error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
413 NULLVP, 1);
414 if (error)
415 vput(vp);
416 return (error);
417 }
418
419 vput(ap->a_dvp);
420 return (EROFS);
421}
422
423int
424union_whiteout(ap)
425 struct vop_whiteout_args /* {
426 struct vnode *a_dvp;
427 struct componentname *a_cnp;
428 int a_flags;
429 } */ *ap;
430{
431 struct union_node *un = VTOUNION(ap->a_dvp);
432 struct componentname *cnp = ap->a_cnp;
433 struct proc *p = cnp->cn_proc;
434
435 if (un->un_uppervp == NULLVP)
436 return (EOPNOTSUPP);
437
438 FIXUP(un, p);
439 return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags));
440}
441
442int
443union_mknod(ap)
444 struct vop_mknod_args /* {
445 struct vnode *a_dvp;
446 struct vnode **a_vpp;
447 struct componentname *a_cnp;
448 struct vattr *a_vap;
449 } */ *ap;
450{
451 struct union_node *un = VTOUNION(ap->a_dvp);
452 struct vnode *dvp = un->un_uppervp;
453 struct componentname *cnp = ap->a_cnp;
454 struct proc *p = cnp->cn_proc;
455
456 if (dvp != NULLVP) {
457 int error;
458 struct vnode *vp;
459 struct mount *mp;
460
461 FIXUP(un, p);
462
463 VREF(dvp);
464 un->un_flags |= UN_KLOCK;
465 mp = ap->a_dvp->v_mount;
466 vput(ap->a_dvp);
467 error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap);
468 if (error)
469 return (error);
470
471 if (vp != NULLVP) {
472 error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
473 cnp, vp, NULLVP, 1);
474 if (error)
475 vput(vp);
476 }
477 return (error);
478 }
479
480 vput(ap->a_dvp);
481 return (EROFS);
482}
483
484int
485union_open(ap)
486 struct vop_open_args /* {
487 struct vnodeop_desc *a_desc;
488 struct vnode *a_vp;
489 int a_mode;
490 struct ucred *a_cred;
491 struct proc *a_p;
492 } */ *ap;
493{
494 struct union_node *un = VTOUNION(ap->a_vp);
495 struct vnode *tvp;
496 int mode = ap->a_mode;
497 struct ucred *cred = ap->a_cred;
498 struct proc *p = ap->a_p;
499 int error;
500
501 /*
502 * If there is an existing upper vp then simply open that.
503 */
504 tvp = un->un_uppervp;
505 if (tvp == NULLVP) {
506 /*
507 * If the lower vnode is being opened for writing, then
508 * copy the file contents to the upper vnode and open that,
509 * otherwise can simply open the lower vnode.
510 */
511 tvp = un->un_lowervp;
512 if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
513 error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
514 if (error == 0)
515 error = VOP_OPEN(un->un_uppervp, mode, cred, p);
516 return (error);
517 }
518
519 /*
520 * Just open the lower vnode
521 */
522 un->un_openl++;
523 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
524 error = VOP_OPEN(tvp, mode, cred, p);
525 VOP_UNLOCK(tvp, 0, p);
526
527 return (error);
528 }
529
530 FIXUP(un, p);
531
532 error = VOP_OPEN(tvp, mode, cred, p);
533
534 return (error);
535}
536
537int
538union_close(ap)
539 struct vop_close_args /* {
540 struct vnode *a_vp;
541 int a_fflag;
542 struct ucred *a_cred;
543 struct proc *a_p;
544 } */ *ap;
545{
546 struct union_node *un = VTOUNION(ap->a_vp);
547 struct vnode *vp;
548
549 if ((vp = un->un_uppervp) == NULLVP) {
550#ifdef UNION_DIAGNOSTIC
551 if (un->un_openl <= 0)
552 panic("union: un_openl cnt");
553#endif
554 --un->un_openl;
555 vp = un->un_lowervp;
556 }
557
558 ap->a_vp = vp;
559 return (VCALL(vp, VOFFSET(vop_close), ap));
560}
561
562/*
563 * Check access permission on the union vnode.
564 * The access check being enforced is to check
565 * against both the underlying vnode, and any
566 * copied vnode. This ensures that no additional
567 * file permissions are given away simply because
568 * the user caused an implicit file copy.
569 */
570int
571union_access(ap)
572 struct vop_access_args /* {
573 struct vnodeop_desc *a_desc;
574 struct vnode *a_vp;
575 int a_mode;
576 struct ucred *a_cred;
577 struct proc *a_p;
578 } */ *ap;
579{
580 struct union_node *un = VTOUNION(ap->a_vp);
581 struct proc *p = ap->a_p;
582 int error = EACCES;
583 struct vnode *vp;
584
585 if ((vp = un->un_uppervp) != NULLVP) {
586 FIXUP(un, p);
587 ap->a_vp = vp;
588 return (VCALL(vp, VOFFSET(vop_access), ap));
589 }
590
591 if ((vp = un->un_lowervp) != NULLVP) {
592 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
593 ap->a_vp = vp;
594 error = VCALL(vp, VOFFSET(vop_access), ap);
595 if (error == 0) {
596 struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
597
598 if (um->um_op == UNMNT_BELOW) {
599 ap->a_cred = um->um_cred;
600 error = VCALL(vp, VOFFSET(vop_access), ap);
601 }
602 }
603 VOP_UNLOCK(vp, 0, p);
604 if (error)
605 return (error);
606 }
607
608 return (error);
609}
610
611/*
612 * We handle getattr only to change the fsid and
613 * track object sizes
614 */
615int
616union_getattr(ap)
617 struct vop_getattr_args /* {
618 struct vnode *a_vp;
619 struct vattr *a_vap;
620 struct ucred *a_cred;
621 struct proc *a_p;
622 } */ *ap;
623{
624 int error;
625 struct union_node *un = VTOUNION(ap->a_vp);
626 struct vnode *vp = un->un_uppervp;
627 struct proc *p = ap->a_p;
628 struct vattr *vap;
629 struct vattr va;
630
631
632 /*
633 * Some programs walk the filesystem hierarchy by counting
634 * links to directories to avoid stat'ing all the time.
635 * This means the link count on directories needs to be "correct".
636 * The only way to do that is to call getattr on both layers
637 * and fix up the link count. The link count will not necessarily
638 * be accurate but will be large enough to defeat the tree walkers.
639 */
640
641 vap = ap->a_vap;
642
643 vp = un->un_uppervp;
644 if (vp != NULLVP) {
645 /*
646 * It's not clear whether VOP_GETATTR is to be
647 * called with the vnode locked or not. stat() calls
648 * it with (vp) locked, and fstat calls it with
649 * (vp) unlocked.
650 * In the mean time, compensate here by checking
651 * the union_node's lock flag.
652 */
653 if (un->un_flags & UN_LOCKED)
654 FIXUP(un, p);
655
656 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
657 if (error)
658 return (error);
659 union_newsize(ap->a_vp, vap->va_size, VNOVAL);
660 }
661
662 if (vp == NULLVP) {
663 vp = un->un_lowervp;
664 } else if (vp->v_type == VDIR) {
665 vp = un->un_lowervp;
666 vap = &va;
667 } else {
668 vp = NULLVP;
669 }
670
671 if (vp != NULLVP) {
672 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
673 if (error)
674 return (error);
675 union_newsize(ap->a_vp, VNOVAL, vap->va_size);
676 }
677
678 if ((vap != ap->a_vap) && (vap->va_type == VDIR))
679 ap->a_vap->va_nlink += vap->va_nlink;
680
681 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
682 return (0);
683}
684
685int
686union_setattr(ap)
687 struct vop_setattr_args /* {
688 struct vnode *a_vp;
689 struct vattr *a_vap;
690 struct ucred *a_cred;
691 struct proc *a_p;
692 } */ *ap;
693{
694 struct union_node *un = VTOUNION(ap->a_vp);
695 struct proc *p = ap->a_p;
696 int error;
697
698 /*
699 * Handle case of truncating lower object to zero size,
700 * by creating a zero length upper object. This is to
701 * handle the case of open with O_TRUNC and O_CREAT.
702 */
703 if ((un->un_uppervp == NULLVP) &&
704 /* assert(un->un_lowervp != NULLVP) */
705 (un->un_lowervp->v_type == VREG)) {
706 error = union_copyup(un, (ap->a_vap->va_size != 0),
707 ap->a_cred, ap->a_p);
708 if (error)
709 return (error);
710 }
711
712 /*
713 * Try to set attributes in upper layer,
714 * otherwise return read-only filesystem error.
715 */
716 if (un->un_uppervp != NULLVP) {
717 FIXUP(un, p);
718 error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
719 ap->a_cred, ap->a_p);
720 if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
721 union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
722 } else {
723 error = EROFS;
724 }
725
726 return (error);
727}
728
729int
730union_read(ap)
731 struct vop_read_args /* {
732 struct vnode *a_vp;
733 struct uio *a_uio;
734 int a_ioflag;
735 struct ucred *a_cred;
736 } */ *ap;
737{
738 int error;
739 struct proc *p = ap->a_uio->uio_procp;
740 struct vnode *vp = OTHERVP(ap->a_vp);
741 int dolock = (vp == LOWERVP(ap->a_vp));
742
743 if (dolock)
744 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
745 else
746 FIXUP(VTOUNION(ap->a_vp), p);
747 error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
748 if (dolock)
749 VOP_UNLOCK(vp, 0, p);
750
751 /*
752 * XXX
753 * perhaps the size of the underlying object has changed under
754 * our feet. take advantage of the offset information present
755 * in the uio structure.
756 */
757 if (error == 0) {
758 struct union_node *un = VTOUNION(ap->a_vp);
759 off_t cur = ap->a_uio->uio_offset;
760
761 if (vp == un->un_uppervp) {
762 if (cur > un->un_uppersz)
763 union_newsize(ap->a_vp, cur, VNOVAL);
764 } else {
765 if (cur > un->un_lowersz)
766 union_newsize(ap->a_vp, VNOVAL, cur);
767 }
768 }
769
770 return (error);
771}
772
773int
774union_write(ap)
775 struct vop_read_args /* {
776 struct vnode *a_vp;
777 struct uio *a_uio;
778 int a_ioflag;
779 struct ucred *a_cred;
780 } */ *ap;
781{
782 int error;
783 struct vnode *vp;
784 struct union_node *un = VTOUNION(ap->a_vp);
785 struct proc *p = ap->a_uio->uio_procp;
786
787 vp = UPPERVP(ap->a_vp);
788 if (vp == NULLVP)
789 panic("union: missing upper layer in write");
790
791 FIXUP(un, p);
792 error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
793
794 /*
795 * the size of the underlying object may be changed by the
796 * write.
797 */
798 if (error == 0) {
799 off_t cur = ap->a_uio->uio_offset;
800
801 if (cur > un->un_uppersz)
802 union_newsize(ap->a_vp, cur, VNOVAL);
803 }
804
805 return (error);
806}
807
808union_lease(ap)
809 struct vop_lease_args /* {
810 struct vnode *a_vp;
811 struct proc *a_p;
812 struct ucred *a_cred;
813 int a_flag;
814 } */ *ap;
815{
816 register struct vnode *ovp = OTHERVP(ap->a_vp);
817
818 ap->a_vp = ovp;
819 return (VCALL(ovp, VOFFSET(vop_lease), ap));
820}
821
822int
823union_ioctl(ap)
824 struct vop_ioctl_args /* {
825 struct vnode *a_vp;
826 int a_command;
827 caddr_t a_data;
828 int a_fflag;
829 struct ucred *a_cred;
830 struct proc *a_p;
831 } */ *ap;
832{
833 register struct vnode *ovp = OTHERVP(ap->a_vp);
834
835 ap->a_vp = ovp;
836 return (VCALL(ovp, VOFFSET(vop_ioctl), ap));
837}
838
839int
840union_select(ap)
841 struct vop_select_args /* {
842 struct vnode *a_vp;
843 int a_which;
844 int a_fflags;
845 struct ucred *a_cred;
0b4e3aa0 846 void * a_wql;
1c79356b
A
847 struct proc *a_p;
848 } */ *ap;
849{
850 register struct vnode *ovp = OTHERVP(ap->a_vp);
851
852 ap->a_vp = ovp;
853 return (VCALL(ovp, VOFFSET(vop_select), ap));
854}
855
856int
857union_revoke(ap)
858 struct vop_revoke_args /* {
859 struct vnode *a_vp;
860 int a_flags;
861 struct proc *a_p;
862 } */ *ap;
863{
864 struct vnode *vp = ap->a_vp;
865
866 if (UPPERVP(vp))
867 VOP_REVOKE(UPPERVP(vp), ap->a_flags);
868 if (LOWERVP(vp))
869 VOP_REVOKE(LOWERVP(vp), ap->a_flags);
870 vgone(vp);
871}
872
873int
874union_mmap(ap)
875 struct vop_mmap_args /* {
876 struct vnode *a_vp;
877 int a_fflags;
878 struct ucred *a_cred;
879 struct proc *a_p;
880 } */ *ap;
881{
882 register struct vnode *ovp = OTHERVP(ap->a_vp);
883
884 ap->a_vp = ovp;
885 return (VCALL(ovp, VOFFSET(vop_mmap), ap));
886}
887
888int
889union_fsync(ap)
890 struct vop_fsync_args /* {
891 struct vnode *a_vp;
892 struct ucred *a_cred;
893 int a_waitfor;
894 struct proc *a_p;
895 } */ *ap;
896{
897 int error = 0;
898 struct proc *p = ap->a_p;
899 struct vnode *targetvp = OTHERVP(ap->a_vp);
900
901 if (targetvp != NULLVP) {
902 int dolock = (targetvp == LOWERVP(ap->a_vp));
903
904 if (dolock)
905 vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
906 else
907 FIXUP(VTOUNION(ap->a_vp), p);
908 error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p);
909 if (dolock)
910 VOP_UNLOCK(targetvp, 0, p);
911 }
912
913 return (error);
914}
915
916int
917union_seek(ap)
918 struct vop_seek_args /* {
919 struct vnode *a_vp;
920 off_t a_oldoff;
921 off_t a_newoff;
922 struct ucred *a_cred;
923 } */ *ap;
924{
925 register struct vnode *ovp = OTHERVP(ap->a_vp);
926
927 ap->a_vp = ovp;
928 return (VCALL(ovp, VOFFSET(vop_seek), ap));
929}
930
931int
932union_remove(ap)
933 struct vop_remove_args /* {
934 struct vnode *a_dvp;
935 struct vnode *a_vp;
936 struct componentname *a_cnp;
937 } */ *ap;
938{
939 int error;
940 struct union_node *dun = VTOUNION(ap->a_dvp);
941 struct union_node *un = VTOUNION(ap->a_vp);
942 struct componentname *cnp = ap->a_cnp;
943 struct proc *p = cnp->cn_proc;
944
945 if (dun->un_uppervp == NULLVP)
946 panic("union remove: null upper vnode");
947
948 if (un->un_uppervp != NULLVP) {
949 struct vnode *dvp = dun->un_uppervp;
950 struct vnode *vp = un->un_uppervp;
951
952 FIXUP(dun, p);
953 VREF(dvp);
954 dun->un_flags |= UN_KLOCK;
955 vput(ap->a_dvp);
956 FIXUP(un, p);
957 VREF(vp);
958 un->un_flags |= UN_KLOCK;
959 vput(ap->a_vp);
960
961 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
962 cnp->cn_flags |= DOWHITEOUT;
963 error = VOP_REMOVE(dvp, vp, cnp);
964 if (!error)
965 union_removed_upper(un);
966 } else {
967 FIXUP(dun, p);
968 error = union_mkwhiteout(
969 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
970 dun->un_uppervp, ap->a_cnp, un->un_path);
971 vput(ap->a_dvp);
972 vput(ap->a_vp);
973 }
974
975 return (error);
976}
977
978int
979union_link(ap)
980 struct vop_link_args /* {
981 struct vnode *a_vp;
982 struct vnode *a_tdvp;
983 struct componentname *a_cnp;
984 } */ *ap;
985{
986 int error = 0;
987 struct componentname *cnp = ap->a_cnp;
988 struct proc *p = cnp->cn_proc;
989 struct union_node *un;
990 struct vnode *vp;
991 struct vnode *tdvp;
992
993 un = VTOUNION(ap->a_tdvp);
994
995 if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
996 vp = ap->a_vp;
997 } else {
998 struct union_node *tun = VTOUNION(ap->a_vp);
999 if (tun->un_uppervp == NULLVP) {
1000 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
1001 if (un->un_uppervp == tun->un_dirvp) {
1002 un->un_flags &= ~UN_ULOCK;
1003 VOP_UNLOCK(un->un_uppervp, 0, p);
1004 }
1005 error = union_copyup(tun, 1, cnp->cn_cred, p);
1006 if (un->un_uppervp == tun->un_dirvp) {
1007 vn_lock(un->un_uppervp,
1008 LK_EXCLUSIVE | LK_RETRY, p);
1009 un->un_flags |= UN_ULOCK;
1010 }
1011 VOP_UNLOCK(ap->a_vp, 0, p);
1012 }
1013 vp = tun->un_uppervp;
1014 }
1015
1016 tdvp = un->un_uppervp;
1017 if (tdvp == NULLVP)
1018 error = EROFS;
1019
1020 if (error) {
1021 vput(ap->a_tdvp);
1022 return (error);
1023 }
1024
1025 FIXUP(un, p);
1026 VREF(tdvp);
1027 un->un_flags |= UN_KLOCK;
1028 vput(ap->a_tdvp);
1029
1030 return (VOP_LINK(vp, tdvp, cnp));
1031}
1032
1033int
1034union_rename(ap)
1035 struct vop_rename_args /* {
1036 struct vnode *a_fdvp;
1037 struct vnode *a_fvp;
1038 struct componentname *a_fcnp;
1039 struct vnode *a_tdvp;
1040 struct vnode *a_tvp;
1041 struct componentname *a_tcnp;
1042 } */ *ap;
1043{
1044 int error;
1045
1046 struct vnode *fdvp = ap->a_fdvp;
1047 struct vnode *fvp = ap->a_fvp;
1048 struct vnode *tdvp = ap->a_tdvp;
1049 struct vnode *tvp = ap->a_tvp;
1050
1051 if (fdvp->v_op == union_vnodeop_p) { /* always true */
1052 struct union_node *un = VTOUNION(fdvp);
1053 if (un->un_uppervp == NULLVP) {
1054 /*
1055 * this should never happen in normal
1056 * operation but might if there was
1057 * a problem creating the top-level shadow
1058 * directory.
1059 */
1060 error = EXDEV;
1061 goto bad;
1062 }
1063
1064 fdvp = un->un_uppervp;
1065 VREF(fdvp);
1066 vrele(ap->a_fdvp);
1067 }
1068
1069 if (fvp->v_op == union_vnodeop_p) { /* always true */
1070 struct union_node *un = VTOUNION(fvp);
1071 if (un->un_uppervp == NULLVP) {
1072 /* XXX: should do a copyup */
1073 error = EXDEV;
1074 goto bad;
1075 }
1076
1077 if (un->un_lowervp != NULLVP)
1078 ap->a_fcnp->cn_flags |= DOWHITEOUT;
1079
1080 fvp = un->un_uppervp;
1081 VREF(fvp);
1082 vrele(ap->a_fvp);
1083 }
1084
1085 if (tdvp->v_op == union_vnodeop_p) {
1086 struct union_node *un = VTOUNION(tdvp);
1087 if (un->un_uppervp == NULLVP) {
1088 /*
1089 * this should never happen in normal
1090 * operation but might if there was
1091 * a problem creating the top-level shadow
1092 * directory.
1093 */
1094 error = EXDEV;
1095 goto bad;
1096 }
1097
1098 tdvp = un->un_uppervp;
1099 VREF(tdvp);
1100 un->un_flags |= UN_KLOCK;
1101 vput(ap->a_tdvp);
1102 }
1103
1104 if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
1105 struct union_node *un = VTOUNION(tvp);
1106
1107 tvp = un->un_uppervp;
1108 if (tvp != NULLVP) {
1109 VREF(tvp);
1110 un->un_flags |= UN_KLOCK;
1111 }
1112 vput(ap->a_tvp);
1113 }
1114
1115 return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
1116
1117bad:
1118 vrele(fdvp);
1119 vrele(fvp);
1120 vput(tdvp);
1121 if (tvp != NULLVP)
1122 vput(tvp);
1123
1124 return (error);
1125}
1126
1127int
1128union_mkdir(ap)
1129 struct vop_mkdir_args /* {
1130 struct vnode *a_dvp;
1131 struct vnode **a_vpp;
1132 struct componentname *a_cnp;
1133 struct vattr *a_vap;
1134 } */ *ap;
1135{
1136 struct union_node *un = VTOUNION(ap->a_dvp);
1137 struct vnode *dvp = un->un_uppervp;
1138 struct componentname *cnp = ap->a_cnp;
1139 struct proc *p = cnp->cn_proc;
1140
1141 if (dvp != NULLVP) {
1142 int error;
1143 struct vnode *vp;
1144
1145 FIXUP(un, p);
1146 VREF(dvp);
1147 un->un_flags |= UN_KLOCK;
1148 VOP_UNLOCK(ap->a_dvp, 0, p);
1149 error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap);
1150 if (error) {
1151 vrele(ap->a_dvp);
1152 return (error);
1153 }
1154
1155 error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
1156 NULLVP, cnp, vp, NULLVP, 1);
1157 vrele(ap->a_dvp);
1158 if (error)
1159 vput(vp);
1160 return (error);
1161 }
1162
1163 vput(ap->a_dvp);
1164 return (EROFS);
1165}
1166
1167int
1168union_rmdir(ap)
1169 struct vop_rmdir_args /* {
1170 struct vnode *a_dvp;
1171 struct vnode *a_vp;
1172 struct componentname *a_cnp;
1173 } */ *ap;
1174{
1175 int error;
1176 struct union_node *dun = VTOUNION(ap->a_dvp);
1177 struct union_node *un = VTOUNION(ap->a_vp);
1178 struct componentname *cnp = ap->a_cnp;
1179 struct proc *p = cnp->cn_proc;
1180
1181 if (dun->un_uppervp == NULLVP)
1182 panic("union rmdir: null upper vnode");
1183
1184 if (un->un_uppervp != NULLVP) {
1185 struct vnode *dvp = dun->un_uppervp;
1186 struct vnode *vp = un->un_uppervp;
1187
1188 FIXUP(dun, p);
1189 VREF(dvp);
1190 dun->un_flags |= UN_KLOCK;
1191 vput(ap->a_dvp);
1192 FIXUP(un, p);
1193 VREF(vp);
1194 un->un_flags |= UN_KLOCK;
1195 vput(ap->a_vp);
1196
1197 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
1198 cnp->cn_flags |= DOWHITEOUT;
1199 error = VOP_RMDIR(dvp, vp, ap->a_cnp);
1200 if (!error)
1201 union_removed_upper(un);
1202 } else {
1203 FIXUP(dun, p);
1204 error = union_mkwhiteout(
1205 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
1206 dun->un_uppervp, ap->a_cnp, un->un_path);
1207 vput(ap->a_dvp);
1208 vput(ap->a_vp);
1209 }
1210
1211 return (error);
1212}
1213
1214int
1215union_symlink(ap)
1216 struct vop_symlink_args /* {
1217 struct vnode *a_dvp;
1218 struct vnode **a_vpp;
1219 struct componentname *a_cnp;
1220 struct vattr *a_vap;
1221 char *a_target;
1222 } */ *ap;
1223{
1224 struct union_node *un = VTOUNION(ap->a_dvp);
1225 struct vnode *dvp = un->un_uppervp;
1226 struct componentname *cnp = ap->a_cnp;
1227 struct proc *p = cnp->cn_proc;
1228
1229 if (dvp != NULLVP) {
1230 int error;
1231 struct vnode *vp;
1232 struct mount *mp = ap->a_dvp->v_mount;
1233
1234 FIXUP(un, p);
1235 VREF(dvp);
1236 un->un_flags |= UN_KLOCK;
1237 vput(ap->a_dvp);
1238 error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target);
1239 *ap->a_vpp = NULLVP;
1240 return (error);
1241 }
1242
1243 vput(ap->a_dvp);
1244 return (EROFS);
1245}
1246
1247/*
1248 * union_readdir works in concert with getdirentries and
1249 * readdir(3) to provide a list of entries in the unioned
1250 * directories. getdirentries is responsible for walking
1251 * down the union stack. readdir(3) is responsible for
1252 * eliminating duplicate names from the returned data stream.
1253 */
1254int
1255union_readdir(ap)
1256 struct vop_readdir_args /* {
1257 struct vnodeop_desc *a_desc;
1258 struct vnode *a_vp;
1259 struct uio *a_uio;
1260 struct ucred *a_cred;
1261 int *a_eofflag;
1262 u_long *a_cookies;
1263 int a_ncookies;
1264 } */ *ap;
1265{
1266 struct union_node *un = VTOUNION(ap->a_vp);
1267 struct vnode *uvp = un->un_uppervp;
1268 struct proc *p = ap->a_uio->uio_procp;
1269
1270 if (uvp == NULLVP)
1271 return (0);
1272
1273 FIXUP(un, p);
1274 ap->a_vp = uvp;
1275 return (VCALL(uvp, VOFFSET(vop_readdir), ap));
1276}
1277
1278int
1279union_readlink(ap)
1280 struct vop_readlink_args /* {
1281 struct vnode *a_vp;
1282 struct uio *a_uio;
1283 struct ucred *a_cred;
1284 } */ *ap;
1285{
1286 int error;
1287 struct uio *uio = ap->a_uio;
1288 struct proc *p = uio->uio_procp;
1289 struct vnode *vp = OTHERVP(ap->a_vp);
1290 int dolock = (vp == LOWERVP(ap->a_vp));
1291
1292 if (dolock)
1293 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1294 else
1295 FIXUP(VTOUNION(ap->a_vp), p);
1296 ap->a_vp = vp;
1297 error = VCALL(vp, VOFFSET(vop_readlink), ap);
1298 if (dolock)
1299 VOP_UNLOCK(vp, 0, p);
1300
1301 return (error);
1302}
1303
1304int
1305union_abortop(ap)
1306 struct vop_abortop_args /* {
1307 struct vnode *a_dvp;
1308 struct componentname *a_cnp;
1309 } */ *ap;
1310{
1311 int error;
1312 struct componentname *cnp = ap->a_cnp;
1313 struct proc *p = cnp->cn_proc;
1314 struct vnode *vp = OTHERVP(ap->a_dvp);
1315 struct union_node *un = VTOUNION(ap->a_dvp);
1316 int islocked = un->un_flags & UN_LOCKED;
1317 int dolock = (vp == LOWERVP(ap->a_dvp));
1318
1319 if (islocked) {
1320 if (dolock)
1321 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1322 else
1323 FIXUP(VTOUNION(ap->a_dvp), p);
1324 }
1325 ap->a_dvp = vp;
1326 error = VCALL(vp, VOFFSET(vop_abortop), ap);
1327 if (islocked && dolock)
1328 VOP_UNLOCK(vp, 0, p);
1329
1330 return (error);
1331}
1332
1333int
1334union_inactive(ap)
1335 struct vop_inactive_args /* {
1336 struct vnode *a_vp;
1337 struct proc *a_p;
1338 } */ *ap;
1339{
1340 struct vnode *vp = ap->a_vp;
1341 struct proc *p = ap->a_p;
1342 struct union_node *un = VTOUNION(vp);
1343 struct vnode **vpp;
1344
1345 /*
1346 * Do nothing (and _don't_ bypass).
1347 * Wait to vrele lowervp until reclaim,
1348 * so that until then our union_node is in the
1349 * cache and reusable.
1350 *
1351 * NEEDSWORK: Someday, consider inactive'ing
1352 * the lowervp and then trying to reactivate it
1353 * with capabilities (v_id)
1354 * like they do in the name lookup cache code.
1355 * That's too much work for now.
1356 */
1357
1358 if (un->un_dircache != 0) {
1359 for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
1360 vrele(*vpp);
1361 _FREE(un->un_dircache, M_TEMP);
1362 un->un_dircache = 0;
1363 }
1364
1365 VOP_UNLOCK(vp, 0, p);
1366
1367 if ((un->un_flags & UN_CACHED) == 0)
1368 vgone(vp);
1369
1370 return (0);
1371}
1372
1373int
1374union_reclaim(ap)
1375 struct vop_reclaim_args /* {
1376 struct vnode *a_vp;
1377 } */ *ap;
1378{
1379
1380 union_freevp(ap->a_vp);
1381
1382 return (0);
1383}
1384
1385int
1386union_lock(ap)
1387 struct vop_lock_args *ap;
1388{
1389 struct vnode *vp = ap->a_vp;
1390 struct proc *p = ap->a_p;
1391 int flags = ap->a_flags;
1392 struct union_node *un;
1393 int error;
1394
1395
1396 vop_nolock(ap);
1397 /*
1398 * Need to do real lockmgr-style locking here.
1399 * in the mean time, draining won't work quite right,
1400 * which could lead to a few race conditions.
1401 * the following test was here, but is not quite right, we
1402 * still need to take the lock:
1403 if ((flags & LK_TYPE_MASK) == LK_DRAIN)
1404 return (0);
1405 */
1406 flags &= ~LK_INTERLOCK;
1407
1408start:
1409 un = VTOUNION(vp);
1410
1411 if (un->un_uppervp != NULLVP) {
1412 if (((un->un_flags & UN_ULOCK) == 0) &&
1413 (vp->v_usecount != 0)) {
1414 error = vn_lock(un->un_uppervp, flags, p);
1415 if (error)
1416 return (error);
1417 un->un_flags |= UN_ULOCK;
1418 }
1419#if DIAGNOSTIC
1420 if (un->un_flags & UN_KLOCK) {
1421 vprint("union: dangling klock", vp);
1422 panic("union: dangling upper lock (%lx)", vp);
1423 }
1424#endif
1425 }
1426
1427 if (un->un_flags & UN_LOCKED) {
1428#if DIAGNOSTIC
1429 if (current_proc() && un->un_pid == current_proc()->p_pid &&
1430 un->un_pid > -1 && current_proc()->p_pid > -1)
1431 panic("union: locking against myself");
1432#endif
1433 un->un_flags |= UN_WANT;
1434 tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0);
1435 goto start;
1436 }
1437
1438#if DIAGNOSTIC
1439 if (current_proc())
1440 un->un_pid = current_proc()->p_pid;
1441 else
1442 un->un_pid = -1;
1443#endif
1444
1445 un->un_flags |= UN_LOCKED;
1446 return (0);
1447}
1448
1449/*
1450 * When operations want to vput() a union node yet retain a lock on
1451 * the upper vnode (say, to do some further operations like link(),
1452 * mkdir(), ...), they set UN_KLOCK on the union node, then call
1453 * vput() which calls VOP_UNLOCK() and comes here. union_unlock()
1454 * unlocks the union node (leaving the upper vnode alone), clears the
1455 * KLOCK flag, and then returns to vput(). The caller then does whatever
1456 * is left to do with the upper vnode, and ensures that it gets unlocked.
1457 *
1458 * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
1459 */
1460int
1461union_unlock(ap)
1462 struct vop_unlock_args /* {
1463 struct vnode *a_vp;
1464 int a_flags;
1465 struct proc *a_p;
1466 } */ *ap;
1467{
1468 struct union_node *un = VTOUNION(ap->a_vp);
1469 struct proc *p = ap->a_p;
1470
1471#if DIAGNOSTIC
1472 if ((un->un_flags & UN_LOCKED) == 0)
1473 panic("union: unlock unlocked node");
1474 if (current_proc() && un->un_pid != current_proc()->p_pid &&
1475 current_proc()->p_pid > -1 && un->un_pid > -1)
1476 panic("union: unlocking other process's union node");
1477#endif
1478
1479 un->un_flags &= ~UN_LOCKED;
1480
1481 if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
1482 VOP_UNLOCK(un->un_uppervp, 0, p);
1483
1484 un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
1485
1486 if (un->un_flags & UN_WANT) {
1487 un->un_flags &= ~UN_WANT;
1488 wakeup((caddr_t) &un->un_flags);
1489 }
1490
1491#if DIAGNOSTIC
1492 un->un_pid = 0;
1493#endif
1494 vop_nounlock(ap);
1495
1496 return (0);
1497}
1498
1499int
1500union_bmap(ap)
1501 struct vop_bmap_args /* {
1502 struct vnode *a_vp;
1503 daddr_t a_bn;
1504 struct vnode **a_vpp;
1505 daddr_t *a_bnp;
1506 int *a_runp;
1507 } */ *ap;
1508{
1509 int error;
1510 struct proc *p = current_proc(); /* XXX */
1511 struct vnode *vp = OTHERVP(ap->a_vp);
1512 int dolock = (vp == LOWERVP(ap->a_vp));
1513
1514 if (dolock)
1515 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1516 else
1517 FIXUP(VTOUNION(ap->a_vp), p);
1518 ap->a_vp = vp;
1519 error = VCALL(vp, VOFFSET(vop_bmap), ap);
1520 if (dolock)
1521 VOP_UNLOCK(vp, 0, p);
1522
1523 return (error);
1524}
1525
1526int
1527union_cmap(ap)
1528 struct vop_cmap_args /* {
1529 struct vnode *a_vp;
1530 off_t a_offset;
1531 size_t a_size;
1532 daddr_t *a_bpn;
1533 size_t *a_run;
1534 void *a_poff;
1535 } */ *ap;
1536{
1537 int error;
1538 struct proc *p = current_proc(); /* XXX */
1539 struct vnode *vp = OTHERVP(ap->a_vp);
1540 int dolock = (vp == LOWERVP(ap->a_vp));
1541
1542 if (dolock)
1543 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1544 else
1545 FIXUP(VTOUNION(ap->a_vp), p);
1546 ap->a_vp = vp;
1547 error = VCALL(vp, VOFFSET(vop_cmap), ap);
1548 if (dolock)
1549 VOP_UNLOCK(vp, 0, p);
1550
1551 return (error);
1552}
1553
1554int
1555union_print(ap)
1556 struct vop_print_args /* {
1557 struct vnode *a_vp;
1558 } */ *ap;
1559{
1560 struct vnode *vp = ap->a_vp;
1561
1562 printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
1563 vp, UPPERVP(vp), LOWERVP(vp));
1564 if (UPPERVP(vp) != NULLVP)
1565 vprint("union: upper", UPPERVP(vp));
1566 if (LOWERVP(vp) != NULLVP)
1567 vprint("union: lower", LOWERVP(vp));
1568
1569 return (0);
1570}
1571
1572int
1573union_islocked(ap)
1574 struct vop_islocked_args /* {
1575 struct vnode *a_vp;
1576 } */ *ap;
1577{
1578
1579 return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
1580}
1581
1582int
1583union_pathconf(ap)
1584 struct vop_pathconf_args /* {
1585 struct vnode *a_vp;
1586 int a_name;
1587 int *a_retval;
1588 } */ *ap;
1589{
1590 int error;
1591 struct proc *p = current_proc(); /* XXX */
1592 struct vnode *vp = OTHERVP(ap->a_vp);
1593 int dolock = (vp == LOWERVP(ap->a_vp));
1594
1595 if (dolock)
1596 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1597 else
1598 FIXUP(VTOUNION(ap->a_vp), p);
1599 ap->a_vp = vp;
1600 error = VCALL(vp, VOFFSET(vop_pathconf), ap);
1601 if (dolock)
1602 VOP_UNLOCK(vp, 0, p);
1603
1604 return (error);
1605}
1606
1607int
1608union_advlock(ap)
1609 struct vop_advlock_args /* {
1610 struct vnode *a_vp;
1611 caddr_t a_id;
1612 int a_op;
1613 struct flock *a_fl;
1614 int a_flags;
1615 } */ *ap;
1616{
1617 register struct vnode *ovp = OTHERVP(ap->a_vp);
1618
1619 ap->a_vp = ovp;
1620 return (VCALL(ovp, VOFFSET(vop_advlock), ap));
1621}
1622
1623
1624/*
1625 * XXX - vop_strategy must be hand coded because it has no
1626 * vnode in its arguments.
1627 * This goes away with a merged VM/buffer cache.
1628 */
1629int
1630union_strategy(ap)
1631 struct vop_strategy_args /* {
1632 struct buf *a_bp;
1633 } */ *ap;
1634{
1635 struct buf *bp = ap->a_bp;
1636 int error;
1637 struct vnode *savedvp;
1638
1639 savedvp = bp->b_vp;
1640 bp->b_vp = OTHERVP(bp->b_vp);
1641
1642#if DIAGNOSTIC
1643 if (bp->b_vp == NULLVP)
1644 panic("union_strategy: nil vp");
1645 if (((bp->b_flags & B_READ) == 0) &&
1646 (bp->b_vp == LOWERVP(savedvp)))
1647 panic("union_strategy: writing to lowervp");
1648#endif
1649
1650 error = VOP_STRATEGY(bp);
1651 bp->b_vp = savedvp;
1652
1653 return (error);
1654}
1655
1656/* Pagein */
1657union_pagein(ap)
1658 struct vop_pagein_args /* {
1659 struct vnode *a_vp,
1660 upl_t a_pl,
1661 vm_offset_t a_pl_offset,
1662 off_t a_f_offset,
1663 size_t a_size,
1664 struct ucred *a_cred,
1665 int a_flags
1666 } */ *ap;
1667{
1668 int error;
1669 struct proc *p = current_proc();
1670 struct vnode *vp = OTHERVP(ap->a_vp);
1671
1672 error = VOP_PAGEIN(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1673 ap->a_size, ap->a_cred,ap->a_flags);
1674
1675 /*
1676 * XXX
1677 * perhaps the size of the underlying object has changed under
1678 * our feet. take advantage of the offset information present
1679 * in the uio structure.
1680 */
1681 if (error == 0) {
1682 struct union_node *un = VTOUNION(ap->a_vp);
1683 off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
1684
1685 if (vp == un->un_uppervp) {
1686 if (cur > un->un_uppersz)
1687 union_newsize(ap->a_vp, cur, VNOVAL);
1688 } else {
1689 if (cur > un->un_lowersz)
1690 union_newsize(ap->a_vp, VNOVAL, cur);
1691 }
1692 }
1693
1694 return (error);
1695}
1696
1697/* Pageout */
1698union_pageout(ap)
1699 struct vop_pageout_args /* {
1700 struct vnode *a_vp,
1701 upl_t a_pl,
1702 vm_offset_t a_pl_offset,
1703 off_t a_f_offset,
1704 size_t a_size,
1705 struct ucred *a_cred,
1706 int a_flags
1707 } */ *ap;
1708{
1709 int error;
1710 struct vnode *vp;
1711 struct union_node *un = VTOUNION(ap->a_vp);
1712
1713 vp = UPPERVP(ap->a_vp);
1714 if (vp == NULLVP)
1715 panic("union: missing upper layer in pageout");
1716
1717 error = VOP_PAGEOUT(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset,
1718 ap->a_size, ap->a_cred,ap->a_flags);
1719
1720 /*
1721 * the size of the underlying object may be changed by the
1722 * write.
1723 */
1724 if (error == 0) {
1725 off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
1726
1727 if (cur > un->un_uppersz)
1728 union_newsize(ap->a_vp, cur, VNOVAL);
1729 }
1730
1731 return (error);
1732}
1733
1734/* Blktooff derives file offset for the given logical block number */
1735int
1736union_blktooff(ap)
1737 struct vop_blktooff_args /* {
1738 struct vnode *a_vp;
1739 daddr_t a_lblkno;
1740 off_t *a_offset;
1741 } */ *ap;
1742{
1743 int error;
1744 struct vnode *vp = OTHERVP(ap->a_vp);
1745
1746 error = VOP_BLKTOOFF(vp, ap->a_lblkno, ap->a_offset);
1747
1748 return(error);
1749}
1750
1751/* offtoblk derives file offset for the given logical block number */
1752int
1753union_offtoblk(ap)
1754 struct vop_offtoblk_args /* {
1755 struct vnode *a_vp;
1756 off_t a_offset;
1757 daddr_t *a_lblkno;
1758 } */ *ap;
1759{
1760 int error;
1761 struct vnode *vp = OTHERVP(ap->a_vp);
1762
1763 error = VOP_OFFTOBLK(vp, ap->a_offset, ap->a_lblkno);
1764
1765 return(error);
1766}
1767
1768#define VOPFUNC int (*)(void *)
1769
1770/*
1771 * Global vfs data structures
1772 */
1773int (**union_vnodeop_p)(void *);
1774struct vnodeopv_entry_desc union_vnodeop_entries[] = {
1775 { &vop_default_desc, (VOPFUNC)vn_default_error },
1776 { &vop_lookup_desc, (VOPFUNC)union_lookup }, /* lookup */
1777 { &vop_create_desc, (VOPFUNC)union_create }, /* create */
1778 { &vop_whiteout_desc, (VOPFUNC)union_whiteout }, /* whiteout */
1779 { &vop_mknod_desc, (VOPFUNC)union_mknod }, /* mknod */
1780 { &vop_open_desc, (VOPFUNC)union_open }, /* open */
1781 { &vop_close_desc, (VOPFUNC)union_close }, /* close */
1782 { &vop_access_desc, (VOPFUNC)union_access }, /* access */
1783 { &vop_getattr_desc, (VOPFUNC)union_getattr }, /* getattr */
1784 { &vop_setattr_desc, (VOPFUNC)union_setattr }, /* setattr */
1785 { &vop_read_desc, (VOPFUNC)union_read }, /* read */
1786 { &vop_write_desc, (VOPFUNC)union_write }, /* write */
1787 { &vop_lease_desc, (VOPFUNC)union_lease }, /* lease */
1788 { &vop_ioctl_desc, (VOPFUNC)union_ioctl }, /* ioctl */
1789 { &vop_select_desc, (VOPFUNC)union_select }, /* select */
1790 { &vop_revoke_desc, (VOPFUNC)union_revoke }, /* revoke */
1791 { &vop_mmap_desc, (VOPFUNC)union_mmap }, /* mmap */
1792 { &vop_fsync_desc, (VOPFUNC)union_fsync }, /* fsync */
1793 { &vop_seek_desc, (VOPFUNC)union_seek }, /* seek */
1794 { &vop_remove_desc, (VOPFUNC)union_remove }, /* remove */
1795 { &vop_link_desc, (VOPFUNC)union_link }, /* link */
1796 { &vop_rename_desc, (VOPFUNC)union_rename }, /* rename */
1797 { &vop_mkdir_desc, (VOPFUNC)union_mkdir }, /* mkdir */
1798 { &vop_rmdir_desc, (VOPFUNC)union_rmdir }, /* rmdir */
1799 { &vop_symlink_desc, (VOPFUNC)union_symlink }, /* symlink */
1800 { &vop_readdir_desc, (VOPFUNC)union_readdir }, /* readdir */
1801 { &vop_readlink_desc, (VOPFUNC)union_readlink }, /* readlink */
1802 { &vop_abortop_desc, (VOPFUNC)union_abortop }, /* abortop */
1803 { &vop_inactive_desc, (VOPFUNC)union_inactive }, /* inactive */
1804 { &vop_reclaim_desc, (VOPFUNC)union_reclaim }, /* reclaim */
1805 { &vop_lock_desc, (VOPFUNC)union_lock }, /* lock */
1806 { &vop_unlock_desc, (VOPFUNC)union_unlock }, /* unlock */
1807 { &vop_bmap_desc, (VOPFUNC)union_bmap }, /* bmap */
1808 { &vop_strategy_desc, (VOPFUNC)union_strategy }, /* strategy */
1809 { &vop_print_desc, (VOPFUNC)union_print }, /* print */
1810 { &vop_islocked_desc, (VOPFUNC)union_islocked }, /* islocked */
1811 { &vop_pathconf_desc, (VOPFUNC)union_pathconf }, /* pathconf */
1812 { &vop_advlock_desc, (VOPFUNC)union_advlock }, /* advlock */
1813#ifdef notdef
1814 { &vop_blkatoff_desc, (VOPFUNC)union_blkatoff }, /* blkatoff */
1815 { &vop_valloc_desc, (VOPFUNC)union_valloc }, /* valloc */
1816 { &vop_vfree_desc, (VOPFUNC)union_vfree }, /* vfree */
1817 { &vop_truncate_desc, (VOPFUNC)union_truncate }, /* truncate */
1818 { &vop_update_desc, (VOPFUNC)union_update }, /* update */
1819 { &vop_bwrite_desc, (VOPFUNC)union_bwrite }, /* bwrite */
1820#endif
1821 { &vop_pagein_desc, (VOPFUNC)union_pagein }, /* Pagein */
1822 { &vop_pageout_desc, (VOPFUNC)union_pageout }, /* Pageout */
1823 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1824 { &vop_blktooff_desc, (VOPFUNC)union_blktooff }, /* blktooff */
1825 { &vop_offtoblk_desc, (VOPFUNC)union_offtoblk }, /* offtoblk */
1826 { &vop_cmap_desc, (VOPFUNC)union_cmap }, /* cmap */
1827 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1828};
1829struct vnodeopv_desc union_vnodeop_opv_desc =
1830 { &union_vnodeop_p, union_vnodeop_entries };