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