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