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