checkpoint (first pass at "...")
[unix-history] / usr / src / sys / miscfs / union / union_vnops.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
3 * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Jan-Simon Pendry.
8 *
9 * %sccs.include.redist.c%
10 *
11 * @(#)union_vnops.c 8.18 (Berkeley) %G%
12 */
13
14#include <sys/param.h>
15#include <sys/systm.h>
16#include <sys/proc.h>
17#include <sys/file.h>
18#include <sys/time.h>
19#include <sys/types.h>
20#include <sys/vnode.h>
21#include <sys/mount.h>
22#include <sys/namei.h>
23#include <sys/malloc.h>
24#include <sys/buf.h>
25#include <sys/queue.h>
26#include <miscfs/union/union.h>
27
28#define FIXUP(un) { \
29 if (((un)->un_flags & UN_ULOCK) == 0) { \
30 union_fixup(un); \
31 } \
32}
33
34static void
35union_fixup(un)
36 struct union_node *un;
37{
38
39 VOP_LOCK(un->un_uppervp);
40 un->un_flags |= UN_ULOCK;
41}
42
43static int
44union_lookup1(udvp, dvpp, vpp, cnp)
45 struct vnode *udvp;
46 struct vnode **dvpp;
47 struct vnode **vpp;
48 struct componentname *cnp;
49{
50 int error;
51 struct vnode *tdvp;
52 struct vnode *dvp;
53 struct mount *mp;
54
55 dvp = *dvpp;
56
57 /*
58 * If stepping up the directory tree, check for going
59 * back across the mount point, in which case do what
60 * lookup would do by stepping back down the mount
61 * hierarchy.
62 */
63 if (cnp->cn_flags & ISDOTDOT) {
64 while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
65 /*
66 * Don't do the NOCROSSMOUNT check
67 * at this level. By definition,
68 * union fs deals with namespaces, not
69 * filesystems.
70 */
71 tdvp = dvp;
72 *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
73 vput(tdvp);
74 VREF(dvp);
75 VOP_LOCK(dvp);
76 }
77 }
78
79 error = VOP_LOOKUP(dvp, &tdvp, cnp);
80 if (error)
81 return (error);
82
83 /*
84 * The parent directory will have been unlocked, unless lookup
85 * found the last component. In which case, re-lock the node
86 * here to allow it to be unlocked again (phew) in union_lookup.
87 */
88 if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
89 VOP_LOCK(dvp);
90
91 dvp = tdvp;
92
93 /*
94 * Lastly check if the current node is a mount point in
95 * which case walk up the mount hierarchy making sure not to
96 * bump into the root of the mount tree (ie. dvp != udvp).
97 */
98 while (dvp != udvp && (dvp->v_type == VDIR) &&
99 (mp = dvp->v_mountedhere)) {
100
101 if (mp->mnt_flag & MNT_MLOCK) {
102 mp->mnt_flag |= MNT_MWAIT;
103 sleep((caddr_t) mp, PVFS);
104 continue;
105 }
106
107 if (error = VFS_ROOT(mp, &tdvp)) {
108 vput(dvp);
109 return (error);
110 }
111
112 vput(dvp);
113 dvp = tdvp;
114 }
115
116 *vpp = dvp;
117 return (0);
118}
119
120int
121union_lookup(ap)
122 struct vop_lookup_args /* {
123 struct vnodeop_desc *a_desc;
124 struct vnode *a_dvp;
125 struct vnode **a_vpp;
126 struct componentname *a_cnp;
127 } */ *ap;
128{
129 int error;
130 int uerror, lerror;
131 struct vnode *uppervp, *lowervp;
132 struct vnode *upperdvp, *lowerdvp;
133 struct vnode *dvp = ap->a_dvp;
134 struct union_node *dun = VTOUNION(dvp);
135 struct componentname *cnp = ap->a_cnp;
136 int lockparent = cnp->cn_flags & LOCKPARENT;
137 int rdonly = cnp->cn_flags & RDONLY;
138 struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
139 struct ucred *saved_cred;
140
141#ifdef notyet
142 if (cnp->cn_namelen == 3 &&
143 cnp->cn_nameptr[2] == '.' &&
144 cnp->cn_nameptr[1] == '.' &&
145 cnp->cn_nameptr[0] == '.') {
146 dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
147 if (dvp == NULLVP)
148 return (ENOENT);
149 VREF(dvp);
150 VOP_LOCK(dvp);
151 if (!lockparent || !(cnp->cn_flags & ISLASTCN))
152 VOP_UNLOCK(ap->a_dvp);
153 return (0);
154 }
155#endif
156
157 cnp->cn_flags |= LOCKPARENT;
158
159 upperdvp = dun->un_uppervp;
160 lowerdvp = dun->un_lowervp;
161 uppervp = NULLVP;
162 lowervp = NULLVP;
163
164 /*
165 * do the lookup in the upper level.
166 * if that level comsumes additional pathnames,
167 * then assume that something special is going
168 * on and just return that vnode.
169 */
170 if (upperdvp != NULLVP) {
171 FIXUP(dun);
172 uerror = union_lookup1(um->um_uppervp, &upperdvp,
173 &uppervp, cnp);
174 /*if (uppervp == upperdvp)
175 dun->un_flags |= UN_KLOCK;*/
176
177 if (cnp->cn_consume != 0) {
178 *ap->a_vpp = uppervp;
179 if (!lockparent)
180 cnp->cn_flags &= ~LOCKPARENT;
181 return (uerror);
182 }
183 } else {
184 uerror = ENOENT;
185 }
186
187 /*
188 * in a similar way to the upper layer, do the lookup
189 * in the lower layer. this time, if there is some
190 * component magic going on, then vput whatever we got
191 * back from the upper layer and return the lower vnode
192 * instead.
193 */
194 if (lowerdvp != NULLVP) {
195 int nameiop;
196
197 VOP_LOCK(lowerdvp);
198
199 /*
200 * Only do a LOOKUP on the bottom node, since
201 * we won't be making changes to it anyway.
202 */
203 nameiop = cnp->cn_nameiop;
204 cnp->cn_nameiop = LOOKUP;
205 if (um->um_op == UNMNT_BELOW) {
206 saved_cred = cnp->cn_cred;
207 cnp->cn_cred = um->um_cred;
208 }
209 lerror = union_lookup1(um->um_lowervp, &lowerdvp,
210 &lowervp, cnp);
211 if (um->um_op == UNMNT_BELOW)
212 cnp->cn_cred = saved_cred;
213 cnp->cn_nameiop = nameiop;
214
215 if (lowervp != lowerdvp)
216 VOP_UNLOCK(lowerdvp);
217
218 if (cnp->cn_consume != 0) {
219 if (uppervp != NULLVP) {
220 if (uppervp == upperdvp)
221 vrele(uppervp);
222 else
223 vput(uppervp);
224 uppervp = NULLVP;
225 }
226 *ap->a_vpp = lowervp;
227 if (!lockparent)
228 cnp->cn_flags &= ~LOCKPARENT;
229 return (lerror);
230 }
231 } else {
232 lerror = ENOENT;
233 if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
234 lowervp = LOWERVP(dun->un_pvp);
235 if (lowervp != NULLVP) {
236 VREF(lowervp);
237 VOP_LOCK(lowervp);
238 lerror = 0;
239 }
240 }
241 }
242
243 if (!lockparent)
244 cnp->cn_flags &= ~LOCKPARENT;
245
246 /*
247 * at this point, we have uerror and lerror indicating
248 * possible errors with the lookups in the upper and lower
249 * layers. additionally, uppervp and lowervp are (locked)
250 * references to existing vnodes in the upper and lower layers.
251 *
252 * there are now three cases to consider.
253 * 1. if both layers returned an error, then return whatever
254 * error the upper layer generated.
255 *
256 * 2. if the top layer failed and the bottom layer succeeded
257 * then two subcases occur.
258 * a. the bottom vnode is not a directory, in which
259 * case just return a new union vnode referencing
260 * an empty top layer and the existing bottom layer.
261 * b. the bottom vnode is a directory, in which case
262 * create a new directory in the top-level and
263 * continue as in case 3.
264 *
265 * 3. if the top layer succeeded then return a new union
266 * vnode referencing whatever the new top layer and
267 * whatever the bottom layer returned.
268 */
269
270 *ap->a_vpp = NULLVP;
271
272 /* case 1. */
273 if ((uerror != 0) && (lerror != 0)) {
274 return (uerror);
275 }
276
277 /* case 2. */
278 if (uerror != 0 /* && (lerror == 0) */ ) {
279 if (lowervp->v_type == VDIR) { /* case 2b. */
280 dun->un_flags &= ~UN_ULOCK;
281 VOP_UNLOCK(upperdvp);
282 uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
283 VOP_LOCK(upperdvp);
284 dun->un_flags |= UN_ULOCK;
285
286 if (uerror) {
287 if (lowervp != NULLVP) {
288 vput(lowervp);
289 lowervp = NULLVP;
290 }
291 return (uerror);
292 }
293 }
294 }
295
296 if (lowervp != NULLVP)
297 VOP_UNLOCK(lowervp);
298
299 error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
300 uppervp, lowervp);
301
302 if (error) {
303 if (uppervp != NULLVP)
304 vput(uppervp);
305 if (lowervp != NULLVP)
306 vrele(lowervp);
307 } else {
308 if (*ap->a_vpp != dvp)
309 if (!lockparent || !(cnp->cn_flags & ISLASTCN))
310 VOP_UNLOCK(dvp);
311 }
312
313 return (error);
314}
315
316int
317union_create(ap)
318 struct vop_create_args /* {
319 struct vnode *a_dvp;
320 struct vnode **a_vpp;
321 struct componentname *a_cnp;
322 struct vattr *a_vap;
323 } */ *ap;
324{
325 struct union_node *un = VTOUNION(ap->a_dvp);
326 struct vnode *dvp = un->un_uppervp;
327
328 if (dvp != NULLVP) {
329 int error;
330 struct vnode *vp;
331
332 FIXUP(un);
333
334 VREF(dvp);
335 un->un_flags |= UN_KLOCK;
336 vput(ap->a_dvp);
337 error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
338 if (error)
339 return (error);
340
341 error = union_allocvp(
342 ap->a_vpp,
343 ap->a_dvp->v_mount,
344 ap->a_dvp,
345 NULLVP,
346 ap->a_cnp,
347 vp,
348 NULLVP);
349 if (error)
350 vput(vp);
351 return (error);
352 }
353
354 vput(ap->a_dvp);
355 return (EROFS);
356}
357
358int
359union_mknod(ap)
360 struct vop_mknod_args /* {
361 struct vnode *a_dvp;
362 struct vnode **a_vpp;
363 struct componentname *a_cnp;
364 struct vattr *a_vap;
365 } */ *ap;
366{
367 struct union_node *un = VTOUNION(ap->a_dvp);
368 struct vnode *dvp = un->un_uppervp;
369
370 if (dvp != NULLVP) {
371 int error;
372 struct vnode *vp;
373
374 FIXUP(un);
375
376 VREF(dvp);
377 un->un_flags |= UN_KLOCK;
378 vput(ap->a_dvp);
379 error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
380 if (error)
381 return (error);
382
383 if (vp != NULLVP) {
384 error = union_allocvp(
385 ap->a_vpp,
386 ap->a_dvp->v_mount,
387 ap->a_dvp,
388 NULLVP,
389 ap->a_cnp,
390 vp,
391 NULLVP);
392 if (error)
393 vput(vp);
394 }
395 return (error);
396 }
397
398 vput(ap->a_dvp);
399 return (EROFS);
400}
401
402int
403union_open(ap)
404 struct vop_open_args /* {
405 struct vnodeop_desc *a_desc;
406 struct vnode *a_vp;
407 int a_mode;
408 struct ucred *a_cred;
409 struct proc *a_p;
410 } */ *ap;
411{
412 struct union_node *un = VTOUNION(ap->a_vp);
413 struct vnode *tvp;
414 int mode = ap->a_mode;
415 struct ucred *cred = ap->a_cred;
416 struct proc *p = ap->a_p;
417 int error;
418
419 /*
420 * If there is an existing upper vp then simply open that.
421 */
422 tvp = un->un_uppervp;
423 if (tvp == NULLVP) {
424 /*
425 * If the lower vnode is being opened for writing, then
426 * copy the file contents to the upper vnode and open that,
427 * otherwise can simply open the lower vnode.
428 */
429 tvp = un->un_lowervp;
430 if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
431 error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
432 if (error == 0)
433 error = VOP_OPEN(un->un_uppervp, mode, cred, p);
434 return (error);
435 }
436
437 /*
438 * Just open the lower vnode
439 */
440 un->un_openl++;
441 VOP_LOCK(tvp);
442 error = VOP_OPEN(tvp, mode, cred, p);
443 VOP_UNLOCK(tvp);
444
445 return (error);
446 }
447
448 FIXUP(un);
449
450 error = VOP_OPEN(tvp, mode, cred, p);
451
452 return (error);
453}
454
455int
456union_close(ap)
457 struct vop_close_args /* {
458 struct vnode *a_vp;
459 int a_fflag;
460 struct ucred *a_cred;
461 struct proc *a_p;
462 } */ *ap;
463{
464 struct union_node *un = VTOUNION(ap->a_vp);
465 struct vnode *vp;
466
467 if (un->un_uppervp != NULLVP) {
468 vp = un->un_uppervp;
469 } else {
470#ifdef UNION_DIAGNOSTIC
471 if (un->un_openl <= 0)
472 panic("union: un_openl cnt");
473#endif
474 --un->un_openl;
475 vp = un->un_lowervp;
476 }
477
478 return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
479}
480
481/*
482 * Check access permission on the union vnode.
483 * The access check being enforced is to check
484 * against both the underlying vnode, and any
485 * copied vnode. This ensures that no additional
486 * file permissions are given away simply because
487 * the user caused an implicit file copy.
488 */
489int
490union_access(ap)
491 struct vop_access_args /* {
492 struct vnodeop_desc *a_desc;
493 struct vnode *a_vp;
494 int a_mode;
495 struct ucred *a_cred;
496 struct proc *a_p;
497 } */ *ap;
498{
499 struct union_node *un = VTOUNION(ap->a_vp);
500 int error = EACCES;
501 struct vnode *vp;
502
503 if ((vp = un->un_uppervp) != NULLVP) {
504 FIXUP(un);
505 return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
506 }
507
508 if ((vp = un->un_lowervp) != NULLVP) {
509 VOP_LOCK(vp);
510 error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
511 if (error == 0) {
512 struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
513
514 if (um->um_op == UNMNT_BELOW)
515 error = VOP_ACCESS(vp, ap->a_mode,
516 um->um_cred, ap->a_p);
517 }
518 VOP_UNLOCK(vp);
519 if (error)
520 return (error);
521 }
522
523 return (error);
524}
525
526/*
527 * We handle getattr only to change the fsid and
528 * track object sizes
529 */
530int
531union_getattr(ap)
532 struct vop_getattr_args /* {
533 struct vnode *a_vp;
534 struct vattr *a_vap;
535 struct ucred *a_cred;
536 struct proc *a_p;
537 } */ *ap;
538{
539 int error;
540 struct union_node *un = VTOUNION(ap->a_vp);
541 struct vnode *vp = un->un_uppervp;
542 struct vattr *vap;
543 struct vattr va;
544
545
546 /*
547 * Some programs walk the filesystem hierarchy by counting
548 * links to directories to avoid stat'ing all the time.
549 * This means the link count on directories needs to be "correct".
550 * The only way to do that is to call getattr on both layers
551 * and fix up the link count. The link count will not necessarily
552 * be accurate but will be large enough to defeat the tree walkers.
553 */
554
555 vap = ap->a_vap;
556
557 vp = un->un_uppervp;
558 if (vp != NULLVP) {
559 /*
560 * It's not clear whether VOP_GETATTR is to be
561 * called with the vnode locked or not. stat() calls
562 * it with (vp) locked, and fstat calls it with
563 * (vp) unlocked.
564 * In the mean time, compensate here by checking
565 * the union_node's lock flag.
566 */
567 if (un->un_flags & UN_LOCKED)
568 FIXUP(un);
569
570 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
571 if (error)
572 return (error);
573 union_newsize(ap->a_vp, vap->va_size, VNOVAL);
574 }
575
576 if (vp == NULLVP) {
577 vp = un->un_lowervp;
578 } else if (vp->v_type == VDIR) {
579 vp = un->un_lowervp;
580 vap = &va;
581 } else {
582 vp = NULLVP;
583 }
584
585 if (vp != NULLVP) {
586 VOP_LOCK(vp);
587 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
588 VOP_UNLOCK(vp);
589 if (error)
590 return (error);
591 union_newsize(ap->a_vp, VNOVAL, vap->va_size);
592 }
593
594 if ((vap != ap->a_vap) && (vap->va_type == VDIR))
595 ap->a_vap->va_nlink += vap->va_nlink;
596
597 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
598 return (0);
599}
600
601int
602union_setattr(ap)
603 struct vop_setattr_args /* {
604 struct vnode *a_vp;
605 struct vattr *a_vap;
606 struct ucred *a_cred;
607 struct proc *a_p;
608 } */ *ap;
609{
610 struct union_node *un = VTOUNION(ap->a_vp);
611 int error;
612
613 /*
614 * Handle case of truncating lower object to zero size,
615 * by creating a zero length upper object. This is to
616 * handle the case of open with O_TRUNC and O_CREAT.
617 */
618 if ((un->un_uppervp == NULLVP) &&
619 /* assert(un->un_lowervp != NULLVP) */
620 (un->un_lowervp->v_type == VREG) &&
621 (ap->a_vap->va_size == 0)) {
622 struct vnode *vp;
623
624 error = union_vn_create(&vp, un, ap->a_p);
625 if (error)
626 return (error);
627
628 /* at this point, uppervp is locked */
629 union_newupper(un, vp);
630
631 VOP_UNLOCK(vp);
632 union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
633 VOP_LOCK(vp);
634 un->un_flags |= UN_ULOCK;
635 }
636
637 /*
638 * Try to set attributes in upper layer,
639 * otherwise return read-only filesystem error.
640 */
641 if (un->un_uppervp != NULLVP) {
642 FIXUP(un);
643 error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
644 ap->a_cred, ap->a_p);
645 if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
646 union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
647 } else {
648 error = EROFS;
649 }
650
651 return (error);
652}
653
654int
655union_read(ap)
656 struct vop_read_args /* {
657 struct vnode *a_vp;
658 struct uio *a_uio;
659 int a_ioflag;
660 struct ucred *a_cred;
661 } */ *ap;
662{
663 int error;
664 struct vnode *vp = OTHERVP(ap->a_vp);
665 int dolock = (vp == LOWERVP(ap->a_vp));
666
667 if (dolock)
668 VOP_LOCK(vp);
669 else
670 FIXUP(VTOUNION(ap->a_vp));
671 error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
672 if (dolock)
673 VOP_UNLOCK(vp);
674
675 /*
676 * XXX
677 * perhaps the size of the underlying object has changed under
678 * our feet. take advantage of the offset information present
679 * in the uio structure.
680 */
681 if (error == 0) {
682 struct union_node *un = VTOUNION(ap->a_vp);
683 off_t cur = ap->a_uio->uio_offset;
684
685 if (vp == un->un_uppervp) {
686 if (cur > un->un_uppersz)
687 union_newsize(ap->a_vp, cur, VNOVAL);
688 } else {
689 if (cur > un->un_lowersz)
690 union_newsize(ap->a_vp, VNOVAL, cur);
691 }
692 }
693
694 return (error);
695}
696
697int
698union_write(ap)
699 struct vop_read_args /* {
700 struct vnode *a_vp;
701 struct uio *a_uio;
702 int a_ioflag;
703 struct ucred *a_cred;
704 } */ *ap;
705{
706 int error;
707 struct vnode *vp = OTHERVP(ap->a_vp);
708 int dolock = (vp == LOWERVP(ap->a_vp));
709
710 if (dolock)
711 VOP_LOCK(vp);
712 else
713 FIXUP(VTOUNION(ap->a_vp));
714 error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
715 if (dolock)
716 VOP_UNLOCK(vp);
717
718 /*
719 * the size of the underlying object may be changed by the
720 * write.
721 */
722 if (error == 0) {
723 struct union_node *un = VTOUNION(ap->a_vp);
724 off_t cur = ap->a_uio->uio_offset;
725
726 if (vp == un->un_uppervp) {
727 if (cur > un->un_uppersz)
728 union_newsize(ap->a_vp, cur, VNOVAL);
729 } else {
730 if (cur > un->un_lowersz)
731 union_newsize(ap->a_vp, VNOVAL, cur);
732 }
733 }
734
735 return (error);
736}
737
738int
739union_ioctl(ap)
740 struct vop_ioctl_args /* {
741 struct vnode *a_vp;
742 int a_command;
743 caddr_t a_data;
744 int a_fflag;
745 struct ucred *a_cred;
746 struct proc *a_p;
747 } */ *ap;
748{
749
750 return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
751 ap->a_fflag, ap->a_cred, ap->a_p));
752}
753
754int
755union_select(ap)
756 struct vop_select_args /* {
757 struct vnode *a_vp;
758 int a_which;
759 int a_fflags;
760 struct ucred *a_cred;
761 struct proc *a_p;
762 } */ *ap;
763{
764
765 return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
766 ap->a_cred, ap->a_p));
767}
768
769int
770union_mmap(ap)
771 struct vop_mmap_args /* {
772 struct vnode *a_vp;
773 int a_fflags;
774 struct ucred *a_cred;
775 struct proc *a_p;
776 } */ *ap;
777{
778
779 return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
780 ap->a_cred, ap->a_p));
781}
782
783int
784union_fsync(ap)
785 struct vop_fsync_args /* {
786 struct vnode *a_vp;
787 struct ucred *a_cred;
788 int a_waitfor;
789 struct proc *a_p;
790 } */ *ap;
791{
792 int error = 0;
793 struct vnode *targetvp = OTHERVP(ap->a_vp);
794
795 if (targetvp != NULLVP) {
796 int dolock = (targetvp == LOWERVP(ap->a_vp));
797
798 if (dolock)
799 VOP_LOCK(targetvp);
800 else
801 FIXUP(VTOUNION(ap->a_vp));
802 error = VOP_FSYNC(targetvp, ap->a_cred,
803 ap->a_waitfor, ap->a_p);
804 if (dolock)
805 VOP_UNLOCK(targetvp);
806 }
807
808 return (error);
809}
810
811int
812union_seek(ap)
813 struct vop_seek_args /* {
814 struct vnode *a_vp;
815 off_t a_oldoff;
816 off_t a_newoff;
817 struct ucred *a_cred;
818 } */ *ap;
819{
820
821 return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
822}
823
824int
825union_remove(ap)
826 struct vop_remove_args /* {
827 struct vnode *a_dvp;
828 struct vnode *a_vp;
829 struct componentname *a_cnp;
830 } */ *ap;
831{
832 int error;
833 struct union_node *dun = VTOUNION(ap->a_dvp);
834 struct union_node *un = VTOUNION(ap->a_vp);
835
836 if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
837 struct vnode *dvp = dun->un_uppervp;
838 struct vnode *vp = un->un_uppervp;
839
840 FIXUP(dun);
841 VREF(dvp);
842 dun->un_flags |= UN_KLOCK;
843 vput(ap->a_dvp);
844 FIXUP(un);
845 VREF(vp);
846 un->un_flags |= UN_KLOCK;
847 vput(ap->a_vp);
848
849 error = VOP_REMOVE(dvp, vp, ap->a_cnp);
850 if (!error)
851 union_removed_upper(un);
852
853 /*
854 * XXX: should create a whiteout here
855 */
856 } else {
857 /*
858 * XXX: should create a whiteout here
859 */
860 vput(ap->a_dvp);
861 vput(ap->a_vp);
862 error = EROFS;
863 }
864
865 return (error);
866}
867
868int
869union_link(ap)
870 struct vop_link_args /* {
871 struct vnode *a_vp;
872 struct vnode *a_tdvp;
873 struct componentname *a_cnp;
874 } */ *ap;
875{
876 int error = 0;
877 struct union_node *un;
878 struct vnode *vp;
879 struct vnode *tdvp;
880
881 un = VTOUNION(ap->a_vp);
882
883 if (ap->a_vp->v_op != ap->a_tdvp->v_op) {
884 tdvp = ap->a_tdvp;
885 } else {
886 struct union_node *tdun = VTOUNION(ap->a_tdvp);
887 if (tdun->un_uppervp == NULLVP) {
888 VOP_LOCK(ap->a_tdvp);
889 if (un->un_uppervp == tdun->un_dirvp) {
890 un->un_flags &= ~UN_ULOCK;
891 VOP_UNLOCK(un->un_uppervp);
892 }
893 error = union_copyup(tdun, 1, ap->a_cnp->cn_cred,
894 ap->a_cnp->cn_proc);
895 if (un->un_uppervp == tdun->un_dirvp) {
896 VOP_LOCK(un->un_uppervp);
897 un->un_flags |= UN_ULOCK;
898 }
899 VOP_UNLOCK(ap->a_tdvp);
900 }
901 tdvp = tdun->un_uppervp;
902 }
903
904 vp = un->un_uppervp;
905 if (vp == NULLVP)
906 error = EROFS;
907
908 if (error) {
909 vput(ap->a_vp);
910 return (error);
911 }
912
913 FIXUP(un);
914 VREF(vp);
915 un->un_flags |= UN_KLOCK;
916 vput(ap->a_vp);
917
918 return (VOP_LINK(vp, tdvp, ap->a_cnp));
919}
920
921int
922union_rename(ap)
923 struct vop_rename_args /* {
924 struct vnode *a_fdvp;
925 struct vnode *a_fvp;
926 struct componentname *a_fcnp;
927 struct vnode *a_tdvp;
928 struct vnode *a_tvp;
929 struct componentname *a_tcnp;
930 } */ *ap;
931{
932 int error;
933
934 struct vnode *fdvp = ap->a_fdvp;
935 struct vnode *fvp = ap->a_fvp;
936 struct vnode *tdvp = ap->a_tdvp;
937 struct vnode *tvp = ap->a_tvp;
938
939 if (fdvp->v_op == union_vnodeop_p) { /* always true */
940 struct union_node *un = VTOUNION(fdvp);
941 if (un->un_uppervp == NULLVP) {
942 error = EROFS;
943 goto bad;
944 }
945
946 fdvp = un->un_uppervp;
947 VREF(fdvp);
948 vrele(ap->a_fdvp);
949 }
950
951 if (fvp->v_op == union_vnodeop_p) { /* always true */
952 struct union_node *un = VTOUNION(fvp);
953 if (un->un_uppervp == NULLVP) {
954 error = EROFS;
955 goto bad;
956 }
957
958 fvp = un->un_uppervp;
959 VREF(fvp);
960 vrele(ap->a_fvp);
961 }
962
963 if (tdvp->v_op == union_vnodeop_p) {
964 struct union_node *un = VTOUNION(tdvp);
965 if (un->un_uppervp == NULLVP) {
966 /*
967 * this should never happen in normal
968 * operation but might if there was
969 * a problem creating the top-level shadow
970 * directory.
971 */
972 error = EROFS;
973 goto bad;
974 }
975
976 tdvp = un->un_uppervp;
977 VREF(tdvp);
978 un->un_flags |= UN_KLOCK;
979 vput(ap->a_tdvp);
980 }
981
982 if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
983 struct union_node *un = VTOUNION(tvp);
984
985 tvp = un->un_uppervp;
986 if (tvp != NULLVP) {
987 VREF(tvp);
988 un->un_flags |= UN_KLOCK;
989 }
990 vput(ap->a_tvp);
991 }
992
993 return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
994
995bad:
996 vrele(fdvp);
997 vrele(fvp);
998 vput(tdvp);
999 if (tvp != NULLVP)
1000 vput(tvp);
1001
1002 return (error);
1003}
1004
1005int
1006union_mkdir(ap)
1007 struct vop_mkdir_args /* {
1008 struct vnode *a_dvp;
1009 struct vnode **a_vpp;
1010 struct componentname *a_cnp;
1011 struct vattr *a_vap;
1012 } */ *ap;
1013{
1014 struct union_node *un = VTOUNION(ap->a_dvp);
1015 struct vnode *dvp = un->un_uppervp;
1016
1017 if (dvp != NULLVP) {
1018 int error;
1019 struct vnode *vp;
1020
1021 FIXUP(un);
1022 VREF(dvp);
1023 un->un_flags |= UN_KLOCK;
1024 vput(ap->a_dvp);
1025 error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
1026 if (error)
1027 return (error);
1028
1029 error = union_allocvp(
1030 ap->a_vpp,
1031 ap->a_dvp->v_mount,
1032 ap->a_dvp,
1033 NULLVP,
1034 ap->a_cnp,
1035 vp,
1036 NULLVP);
1037 if (error)
1038 vput(vp);
1039 return (error);
1040 }
1041
1042 vput(ap->a_dvp);
1043 return (EROFS);
1044}
1045
1046int
1047union_rmdir(ap)
1048 struct vop_rmdir_args /* {
1049 struct vnode *a_dvp;
1050 struct vnode *a_vp;
1051 struct componentname *a_cnp;
1052 } */ *ap;
1053{
1054 int error;
1055 struct union_node *dun = VTOUNION(ap->a_dvp);
1056 struct union_node *un = VTOUNION(ap->a_vp);
1057
1058 if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
1059 struct vnode *dvp = dun->un_uppervp;
1060 struct vnode *vp = un->un_uppervp;
1061
1062 FIXUP(dun);
1063 VREF(dvp);
1064 dun->un_flags |= UN_KLOCK;
1065 vput(ap->a_dvp);
1066 FIXUP(un);
1067 VREF(vp);
1068 un->un_flags |= UN_KLOCK;
1069 vput(ap->a_vp);
1070
1071 error = VOP_RMDIR(dvp, vp, ap->a_cnp);
1072 if (!error)
1073 union_removed_upper(un);
1074
1075 /*
1076 * XXX: should create a whiteout here
1077 */
1078 } else {
1079 /*
1080 * XXX: should create a whiteout here
1081 */
1082 vput(ap->a_dvp);
1083 vput(ap->a_vp);
1084 error = EROFS;
1085 }
1086
1087 return (error);
1088}
1089
1090int
1091union_symlink(ap)
1092 struct vop_symlink_args /* {
1093 struct vnode *a_dvp;
1094 struct vnode **a_vpp;
1095 struct componentname *a_cnp;
1096 struct vattr *a_vap;
1097 char *a_target;
1098 } */ *ap;
1099{
1100 struct union_node *un = VTOUNION(ap->a_dvp);
1101 struct vnode *dvp = un->un_uppervp;
1102
1103 if (dvp != NULLVP) {
1104 int error;
1105 struct vnode *vp;
1106 struct mount *mp = ap->a_dvp->v_mount;
1107
1108 FIXUP(un);
1109 VREF(dvp);
1110 un->un_flags |= UN_KLOCK;
1111 vput(ap->a_dvp);
1112 error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
1113 ap->a_vap, ap->a_target);
1114 *ap->a_vpp = NULLVP;
1115 return (error);
1116 }
1117
1118 vput(ap->a_dvp);
1119 return (EROFS);
1120}
1121
1122/*
1123 * union_readdir works in concert with getdirentries and
1124 * readdir(3) to provide a list of entries in the unioned
1125 * directories. getdirentries is responsible for walking
1126 * down the union stack. readdir(3) is responsible for
1127 * eliminating duplicate names from the returned data stream.
1128 */
1129int
1130union_readdir(ap)
1131 struct vop_readdir_args /* {
1132 struct vnodeop_desc *a_desc;
1133 struct vnode *a_vp;
1134 struct uio *a_uio;
1135 struct ucred *a_cred;
1136 int *a_eofflag;
1137 u_long *a_cookies;
1138 int a_ncookies;
1139 } */ *ap;
1140{
1141 register struct union_node *un = VTOUNION(ap->a_vp);
1142 register struct vnode *uvp = un->un_uppervp;
1143
1144 if (uvp == NULLVP)
1145 return (0);
1146
1147 FIXUP(un);
1148 ap->a_vp = uvp;
1149 return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
1150}
1151
1152int
1153union_readlink(ap)
1154 struct vop_readlink_args /* {
1155 struct vnode *a_vp;
1156 struct uio *a_uio;
1157 struct ucred *a_cred;
1158 } */ *ap;
1159{
1160 int error;
1161 struct vnode *vp = OTHERVP(ap->a_vp);
1162 int dolock = (vp == LOWERVP(ap->a_vp));
1163
1164 if (dolock)
1165 VOP_LOCK(vp);
1166 else
1167 FIXUP(VTOUNION(ap->a_vp));
1168 error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
1169 if (dolock)
1170 VOP_UNLOCK(vp);
1171
1172 return (error);
1173}
1174
1175int
1176union_abortop(ap)
1177 struct vop_abortop_args /* {
1178 struct vnode *a_dvp;
1179 struct componentname *a_cnp;
1180 } */ *ap;
1181{
1182 int error;
1183 struct vnode *vp = OTHERVP(ap->a_dvp);
1184 struct union_node *un = VTOUNION(ap->a_dvp);
1185 int islocked = un->un_flags & UN_LOCKED;
1186 int dolock = (vp == LOWERVP(ap->a_dvp));
1187
1188 if (islocked) {
1189 if (dolock)
1190 VOP_LOCK(vp);
1191 else
1192 FIXUP(VTOUNION(ap->a_dvp));
1193 }
1194 error = VOP_ABORTOP(vp, ap->a_cnp);
1195 if (islocked && dolock)
1196 VOP_UNLOCK(vp);
1197
1198 return (error);
1199}
1200
1201int
1202union_inactive(ap)
1203 struct vop_inactive_args /* {
1204 struct vnode *a_vp;
1205 } */ *ap;
1206{
1207 struct union_node *un = VTOUNION(ap->a_vp);
1208
1209 /*
1210 * Do nothing (and _don't_ bypass).
1211 * Wait to vrele lowervp until reclaim,
1212 * so that until then our union_node is in the
1213 * cache and reusable.
1214 *
1215 * NEEDSWORK: Someday, consider inactive'ing
1216 * the lowervp and then trying to reactivate it
1217 * with capabilities (v_id)
1218 * like they do in the name lookup cache code.
1219 * That's too much work for now.
1220 */
1221
1222#ifdef UNION_DIAGNOSTIC
1223 if (un->un_flags & UN_LOCKED)
1224 panic("union: inactivating locked node");
1225 if (un->un_flags & UN_ULOCK)
1226 panic("union: inactivating w/locked upper node");
1227#endif
1228
1229 if ((un->un_flags & UN_CACHED) == 0)
1230 vgone(ap->a_vp);
1231
1232 return (0);
1233}
1234
1235int
1236union_reclaim(ap)
1237 struct vop_reclaim_args /* {
1238 struct vnode *a_vp;
1239 } */ *ap;
1240{
1241
1242 union_freevp(ap->a_vp);
1243
1244 return (0);
1245}
1246
1247int
1248union_lock(ap)
1249 struct vop_lock_args *ap;
1250{
1251 struct vnode *vp = ap->a_vp;
1252 struct union_node *un;
1253
1254start:
1255 while (vp->v_flag & VXLOCK) {
1256 vp->v_flag |= VXWANT;
1257 sleep((caddr_t)vp, PINOD);
1258 }
1259
1260 un = VTOUNION(vp);
1261
1262 if (un->un_uppervp != NULLVP) {
1263 if (((un->un_flags & UN_ULOCK) == 0) &&
1264 (vp->v_usecount != 0)) {
1265 un->un_flags |= UN_ULOCK;
1266 VOP_LOCK(un->un_uppervp);
1267 }
1268#ifdef DIAGNOSTIC
1269 if (un->un_flags & UN_KLOCK)
1270 panic("union: dangling upper lock");
1271#endif
1272 }
1273
1274 if (un->un_flags & UN_LOCKED) {
1275#ifdef DIAGNOSTIC
1276 if (curproc && un->un_pid == curproc->p_pid &&
1277 un->un_pid > -1 && curproc->p_pid > -1)
1278 panic("union: locking against myself");
1279#endif
1280 un->un_flags |= UN_WANT;
1281 sleep((caddr_t) &un->un_flags, PINOD);
1282 goto start;
1283 }
1284
1285#ifdef DIAGNOSTIC
1286 if (curproc)
1287 un->un_pid = curproc->p_pid;
1288 else
1289 un->un_pid = -1;
1290#endif
1291
1292 un->un_flags |= UN_LOCKED;
1293 return (0);
1294}
1295
1296int
1297union_unlock(ap)
1298 struct vop_lock_args *ap;
1299{
1300 struct union_node *un = VTOUNION(ap->a_vp);
1301
1302#ifdef DIAGNOSTIC
1303 if ((un->un_flags & UN_LOCKED) == 0)
1304 panic("union: unlock unlocked node");
1305 if (curproc && un->un_pid != curproc->p_pid &&
1306 curproc->p_pid > -1 && un->un_pid > -1)
1307 panic("union: unlocking other process's union node");
1308#endif
1309
1310 un->un_flags &= ~UN_LOCKED;
1311
1312 if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
1313 VOP_UNLOCK(un->un_uppervp);
1314
1315 un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
1316
1317 if (un->un_flags & UN_WANT) {
1318 un->un_flags &= ~UN_WANT;
1319 wakeup((caddr_t) &un->un_flags);
1320 }
1321
1322#ifdef DIAGNOSTIC
1323 un->un_pid = 0;
1324#endif
1325
1326 return (0);
1327}
1328
1329int
1330union_bmap(ap)
1331 struct vop_bmap_args /* {
1332 struct vnode *a_vp;
1333 daddr_t a_bn;
1334 struct vnode **a_vpp;
1335 daddr_t *a_bnp;
1336 int *a_runp;
1337 } */ *ap;
1338{
1339 int error;
1340 struct vnode *vp = OTHERVP(ap->a_vp);
1341 int dolock = (vp == LOWERVP(ap->a_vp));
1342
1343 if (dolock)
1344 VOP_LOCK(vp);
1345 else
1346 FIXUP(VTOUNION(ap->a_vp));
1347 error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
1348 if (dolock)
1349 VOP_UNLOCK(vp);
1350
1351 return (error);
1352}
1353
1354int
1355union_print(ap)
1356 struct vop_print_args /* {
1357 struct vnode *a_vp;
1358 } */ *ap;
1359{
1360 struct vnode *vp = ap->a_vp;
1361
1362 printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
1363 vp, UPPERVP(vp), LOWERVP(vp));
1364 return (0);
1365}
1366
1367int
1368union_islocked(ap)
1369 struct vop_islocked_args /* {
1370 struct vnode *a_vp;
1371 } */ *ap;
1372{
1373
1374 return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
1375}
1376
1377int
1378union_pathconf(ap)
1379 struct vop_pathconf_args /* {
1380 struct vnode *a_vp;
1381 int a_name;
1382 int *a_retval;
1383 } */ *ap;
1384{
1385 int error;
1386 struct vnode *vp = OTHERVP(ap->a_vp);
1387 int dolock = (vp == LOWERVP(ap->a_vp));
1388
1389 if (dolock)
1390 VOP_LOCK(vp);
1391 else
1392 FIXUP(VTOUNION(ap->a_vp));
1393 error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
1394 if (dolock)
1395 VOP_UNLOCK(vp);
1396
1397 return (error);
1398}
1399
1400int
1401union_advlock(ap)
1402 struct vop_advlock_args /* {
1403 struct vnode *a_vp;
1404 caddr_t a_id;
1405 int a_op;
1406 struct flock *a_fl;
1407 int a_flags;
1408 } */ *ap;
1409{
1410
1411 return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
1412 ap->a_fl, ap->a_flags));
1413}
1414
1415
1416/*
1417 * XXX - vop_strategy must be hand coded because it has no
1418 * vnode in its arguments.
1419 * This goes away with a merged VM/buffer cache.
1420 */
1421int
1422union_strategy(ap)
1423 struct vop_strategy_args /* {
1424 struct buf *a_bp;
1425 } */ *ap;
1426{
1427 struct buf *bp = ap->a_bp;
1428 int error;
1429 struct vnode *savedvp;
1430
1431 savedvp = bp->b_vp;
1432 bp->b_vp = OTHERVP(bp->b_vp);
1433
1434#ifdef DIAGNOSTIC
1435 if (bp->b_vp == NULLVP)
1436 panic("union_strategy: nil vp");
1437 if (((bp->b_flags & B_READ) == 0) &&
1438 (bp->b_vp == LOWERVP(savedvp)))
1439 panic("union_strategy: writing to lowervp");
1440#endif
1441
1442 error = VOP_STRATEGY(bp);
1443 bp->b_vp = savedvp;
1444
1445 return (error);
1446}
1447
1448/*
1449 * Global vfs data structures
1450 */
1451int (**union_vnodeop_p)();
1452struct vnodeopv_entry_desc union_vnodeop_entries[] = {
1453 { &vop_default_desc, vn_default_error },
1454 { &vop_lookup_desc, union_lookup }, /* lookup */
1455 { &vop_create_desc, union_create }, /* create */
1456 { &vop_mknod_desc, union_mknod }, /* mknod */
1457 { &vop_open_desc, union_open }, /* open */
1458 { &vop_close_desc, union_close }, /* close */
1459 { &vop_access_desc, union_access }, /* access */
1460 { &vop_getattr_desc, union_getattr }, /* getattr */
1461 { &vop_setattr_desc, union_setattr }, /* setattr */
1462 { &vop_read_desc, union_read }, /* read */
1463 { &vop_write_desc, union_write }, /* write */
1464 { &vop_ioctl_desc, union_ioctl }, /* ioctl */
1465 { &vop_select_desc, union_select }, /* select */
1466 { &vop_mmap_desc, union_mmap }, /* mmap */
1467 { &vop_fsync_desc, union_fsync }, /* fsync */
1468 { &vop_seek_desc, union_seek }, /* seek */
1469 { &vop_remove_desc, union_remove }, /* remove */
1470 { &vop_link_desc, union_link }, /* link */
1471 { &vop_rename_desc, union_rename }, /* rename */
1472 { &vop_mkdir_desc, union_mkdir }, /* mkdir */
1473 { &vop_rmdir_desc, union_rmdir }, /* rmdir */
1474 { &vop_symlink_desc, union_symlink }, /* symlink */
1475 { &vop_readdir_desc, union_readdir }, /* readdir */
1476 { &vop_readlink_desc, union_readlink }, /* readlink */
1477 { &vop_abortop_desc, union_abortop }, /* abortop */
1478 { &vop_inactive_desc, union_inactive }, /* inactive */
1479 { &vop_reclaim_desc, union_reclaim }, /* reclaim */
1480 { &vop_lock_desc, union_lock }, /* lock */
1481 { &vop_unlock_desc, union_unlock }, /* unlock */
1482 { &vop_bmap_desc, union_bmap }, /* bmap */
1483 { &vop_strategy_desc, union_strategy }, /* strategy */
1484 { &vop_print_desc, union_print }, /* print */
1485 { &vop_islocked_desc, union_islocked }, /* islocked */
1486 { &vop_pathconf_desc, union_pathconf }, /* pathconf */
1487 { &vop_advlock_desc, union_advlock }, /* advlock */
1488#ifdef notdef
1489 { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */
1490 { &vop_valloc_desc, union_valloc }, /* valloc */
1491 { &vop_vfree_desc, union_vfree }, /* vfree */
1492 { &vop_truncate_desc, union_truncate }, /* truncate */
1493 { &vop_update_desc, union_update }, /* update */
1494 { &vop_bwrite_desc, union_bwrite }, /* bwrite */
1495#endif
1496 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1497};
1498struct vnodeopv_desc union_vnodeop_opv_desc =
1499 { &union_vnodeop_p, union_vnodeop_entries };