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