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