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