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