]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/unionfs/union_vnops.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.git] / sys / fs / unionfs / union_vnops.c
1 /*-
2  * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
3  * Copyright (c) 1992, 1993, 1994, 1995
4  *      The Regents of the University of California.
5  * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
6  * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *      @(#)union_vnops.c       8.32 (Berkeley) 6/23/95
37  * $FreeBSD$
38  *
39  */
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 #include <sys/kernel.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mount.h>
48 #include <sys/mutex.h>
49 #include <sys/namei.h>
50 #include <sys/sysctl.h>
51 #include <sys/vnode.h>
52 #include <sys/kdb.h>
53 #include <sys/fcntl.h>
54 #include <sys/stat.h>
55 #include <sys/dirent.h>
56 #include <sys/proc.h>
57 #include <sys/bio.h>
58 #include <sys/buf.h>
59
60 #include <fs/unionfs/union.h>
61
62 #include <vm/vm.h>
63 #include <vm/vm_extern.h>
64 #include <vm/vm_object.h>
65 #include <vm/vnode_pager.h>
66
67 #if 0
68 #define UNIONFS_INTERNAL_DEBUG(msg, args...)    printf(msg, ## args)
69 #define UNIONFS_IDBG_RENAME
70 #else
71 #define UNIONFS_INTERNAL_DEBUG(msg, args...)
72 #endif
73
74 /* lockmgr lock <-> reverse table */
75 struct lk_lr_table {
76         int     lock;
77         int     revlock;
78 };
79
80 static struct lk_lr_table un_llt[] = {
81         {LK_SHARED, LK_RELEASE},
82         {LK_EXCLUSIVE, LK_RELEASE},
83         {LK_UPGRADE, LK_DOWNGRADE},
84         {LK_DOWNGRADE, LK_UPGRADE},
85         {0, 0}
86 };
87
88
89 static int
90 unionfs_lookup(struct vop_cachedlookup_args *ap)
91 {
92         int             iswhiteout;
93         int             lockflag;
94         int             error , uerror, lerror;
95         u_long          nameiop;
96         u_long          cnflags, cnflagsbk;
97         struct unionfs_node *dunp;
98         struct vnode   *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
99         struct vattr    va;
100         struct componentname *cnp;
101         struct thread  *td;
102
103         iswhiteout = 0;
104         lockflag = 0;
105         error = uerror = lerror = ENOENT;
106         cnp = ap->a_cnp;
107         nameiop = cnp->cn_nameiop;
108         cnflags = cnp->cn_flags;
109         dvp = ap->a_dvp;
110         dunp = VTOUNIONFS(dvp);
111         udvp = dunp->un_uppervp;
112         ldvp = dunp->un_lowervp;
113         vp = uvp = lvp = NULLVP;
114         td = curthread;
115         *(ap->a_vpp) = NULLVP;
116
117         UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr);
118
119         if (dvp->v_type != VDIR)
120                 return (ENOTDIR);
121
122         /*
123          * If read-only and op is not LOOKUP, will return EROFS.
124          */
125         if ((cnflags & ISLASTCN) &&
126             (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
127             LOOKUP != nameiop)
128                 return (EROFS);
129
130         /*
131          * lookup dotdot
132          */
133         if (cnflags & ISDOTDOT) {
134                 if (LOOKUP != nameiop && udvp == NULLVP)
135                         return (EROFS);
136
137                 if (udvp != NULLVP) {
138                         dtmpvp = udvp;
139                         if (ldvp != NULLVP)
140                                 VOP_UNLOCK(ldvp, 0);
141                 }
142                 else
143                         dtmpvp = ldvp;
144
145                 error = VOP_LOOKUP(dtmpvp, &vp, cnp);
146
147                 if (dtmpvp == udvp && ldvp != NULLVP) {
148                         VOP_UNLOCK(udvp, 0);
149                         vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
150                 }
151
152                 if (error == 0) {
153                         /*
154                          * Exchange lock and reference from vp to
155                          * dunp->un_dvp. vp is upper/lower vnode, but it
156                          * will need to return the unionfs vnode.
157                          */
158                         if (nameiop == DELETE  || nameiop == RENAME ||
159                             (cnp->cn_lkflags & LK_TYPE_MASK))
160                                 VOP_UNLOCK(vp, 0);
161                         vrele(vp);
162
163                         VOP_UNLOCK(dvp, 0);
164                         *(ap->a_vpp) = dunp->un_dvp;
165                         vref(dunp->un_dvp);
166
167                         if (nameiop == DELETE || nameiop == RENAME)
168                                 vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY);
169                         else if (cnp->cn_lkflags & LK_TYPE_MASK)
170                                 vn_lock(dunp->un_dvp, cnp->cn_lkflags |
171                                     LK_RETRY);
172
173                         vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
174                 } else if (error == ENOENT && (cnflags & MAKEENTRY) &&
175                     nameiop != CREATE)
176                         cache_enter(dvp, NULLVP, cnp);
177
178                 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);
179
180                 return (error);
181         }
182
183         /*
184          * lookup upper layer
185          */
186         if (udvp != NULLVP) {
187                 uerror = VOP_LOOKUP(udvp, &uvp, cnp);
188
189                 if (uerror == 0) {
190                         if (udvp == uvp) {      /* is dot */
191                                 vrele(uvp);
192                                 *(ap->a_vpp) = dvp;
193                                 vref(dvp);
194
195                                 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror);
196
197                                 return (uerror);
198                         }
199                         if (nameiop == DELETE || nameiop == RENAME ||
200                             (cnp->cn_lkflags & LK_TYPE_MASK))
201                                 VOP_UNLOCK(uvp, 0);
202                 }
203
204                 /* check whiteout */
205                 if (uerror == ENOENT || uerror == EJUSTRETURN)
206                         if (cnp->cn_flags & ISWHITEOUT)
207                                 iswhiteout = 1; /* don't lookup lower */
208                 if (iswhiteout == 0 && ldvp != NULLVP)
209                         if (!VOP_GETATTR(udvp, &va, cnp->cn_cred) &&
210                             (va.va_flags & OPAQUE))
211                                 iswhiteout = 1; /* don't lookup lower */
212 #if 0
213                 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr);
214 #endif
215         }
216
217         /*
218          * lookup lower layer
219          */
220         if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) {
221                 /* always op is LOOKUP */
222                 cnp->cn_nameiop = LOOKUP;
223                 cnflagsbk = cnp->cn_flags;
224                 cnp->cn_flags = cnflags;
225
226                 lerror = VOP_LOOKUP(ldvp, &lvp, cnp);
227
228                 cnp->cn_nameiop = nameiop;
229                 if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN))
230                         cnp->cn_flags = cnflagsbk;
231
232                 if (lerror == 0) {
233                         if (ldvp == lvp) {      /* is dot */
234                                 if (uvp != NULLVP)
235                                         vrele(uvp);     /* no need? */
236                                 vrele(lvp);
237                                 *(ap->a_vpp) = dvp;
238                                 vref(dvp);
239
240                                 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror);
241
242                                 return (lerror);
243                         }
244                         if (cnp->cn_lkflags & LK_TYPE_MASK)
245                                 VOP_UNLOCK(lvp, 0);
246                 }
247         }
248
249         /*
250          * check lookup result
251          */
252         if (uvp == NULLVP && lvp == NULLVP) {
253                 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n",
254                     (udvp != NULLVP ? uerror : lerror));
255                 return (udvp != NULLVP ? uerror : lerror);
256         }
257
258         /*
259          * check vnode type
260          */
261         if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) {
262                 vrele(lvp);
263                 lvp = NULLVP;
264         }
265
266         /*
267          * check shadow dir
268          */
269         if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP &&
270             lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR &&
271             !(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
272             (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
273                 /* get unionfs vnode in order to create a new shadow dir. */
274                 error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp,
275                     cnp, td);
276                 if (error != 0)
277                         goto unionfs_lookup_out;
278
279                 if (LK_SHARED == (cnp->cn_lkflags & LK_TYPE_MASK))
280                         VOP_UNLOCK(vp, 0);
281                 if (LK_EXCLUSIVE != VOP_ISLOCKED(vp)) {
282                         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
283                         lockflag = 1;
284                 }
285                 error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount),
286                     udvp, VTOUNIONFS(vp), cnp, td);
287                 if (lockflag != 0)
288                         VOP_UNLOCK(vp, 0);
289                 if (error != 0) {
290                         UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir.");
291                         if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE)
292                                 vput(vp);
293                         else
294                                 vrele(vp);
295                         goto unionfs_lookup_out;
296                 }
297                 if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_SHARED)
298                         vn_lock(vp, LK_SHARED | LK_RETRY);
299         }
300         /*
301          * get unionfs vnode.
302          */
303         else {
304                 if (uvp != NULLVP)
305                         error = uerror;
306                 else
307                         error = lerror;
308                 if (error != 0)
309                         goto unionfs_lookup_out;
310                 error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp,
311                     cnp, td);
312                 if (error != 0) {
313                         UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode.");
314                         goto unionfs_lookup_out;
315                 }
316                 if ((nameiop == DELETE || nameiop == RENAME) &&
317                     (cnp->cn_lkflags & LK_TYPE_MASK) == 0)
318                         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
319         }
320
321         *(ap->a_vpp) = vp;
322
323         if (cnflags & MAKEENTRY)
324                 cache_enter(dvp, vp, cnp);
325
326 unionfs_lookup_out:
327         if (uvp != NULLVP)
328                 vrele(uvp);
329         if (lvp != NULLVP)
330                 vrele(lvp);
331
332         if (error == ENOENT && (cnflags & MAKEENTRY) && nameiop != CREATE)
333                 cache_enter(dvp, NULLVP, cnp);
334
335         UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);
336
337         return (error);
338 }
339
340 static int
341 unionfs_create(struct vop_create_args *ap)
342 {
343         struct unionfs_node *dunp;
344         struct componentname *cnp;
345         struct vnode   *udvp;
346         struct vnode   *vp;
347         int             error;
348
349         UNIONFS_INTERNAL_DEBUG("unionfs_create: enter\n");
350
351         dunp = VTOUNIONFS(ap->a_dvp);
352         cnp = ap->a_cnp;
353         udvp = dunp->un_uppervp;
354         error = EROFS;
355
356         if (udvp != NULLVP) {
357                 if ((error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap)) == 0) {
358                         VOP_UNLOCK(vp, 0);
359                         error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
360                             ap->a_dvp, ap->a_vpp, cnp, curthread);
361                         vrele(vp);
362                 }
363         }
364
365         UNIONFS_INTERNAL_DEBUG("unionfs_create: leave (%d)\n", error);
366
367         return (error);
368 }
369
370 static int
371 unionfs_whiteout(struct vop_whiteout_args *ap)
372 {
373         struct unionfs_node *dunp;
374         struct componentname *cnp;
375         struct vnode   *udvp;
376         int             error;
377
378         UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: enter\n");
379
380         dunp = VTOUNIONFS(ap->a_dvp);
381         cnp = ap->a_cnp;
382         udvp = dunp->un_uppervp;
383         error = EOPNOTSUPP;
384
385         if (udvp != NULLVP) {
386                 switch (ap->a_flags) {
387                 case CREATE:
388                 case DELETE:
389                 case LOOKUP:
390                         error = VOP_WHITEOUT(udvp, cnp, ap->a_flags);
391                         break;
392                 default:
393                         error = EINVAL;
394                         break;
395                 }
396         }
397
398         UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: leave (%d)\n", error);
399
400         return (error);
401 }
402
403 static int
404 unionfs_mknod(struct vop_mknod_args *ap)
405 {
406         struct unionfs_node *dunp;
407         struct componentname *cnp;
408         struct vnode   *udvp;
409         struct vnode   *vp;
410         int             error;
411
412         UNIONFS_INTERNAL_DEBUG("unionfs_mknod: enter\n");
413
414         dunp = VTOUNIONFS(ap->a_dvp);
415         cnp = ap->a_cnp;
416         udvp = dunp->un_uppervp;
417         error = EROFS;
418
419         if (udvp != NULLVP) {
420                 if ((error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap)) == 0) {
421                         VOP_UNLOCK(vp, 0);
422                         error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
423                             ap->a_dvp, ap->a_vpp, cnp, curthread);
424                         vrele(vp);
425                 }
426         }
427
428         UNIONFS_INTERNAL_DEBUG("unionfs_mknod: leave (%d)\n", error);
429
430         return (error);
431 }
432
433 static int
434 unionfs_open(struct vop_open_args *ap)
435 {
436         int             error;
437         struct unionfs_node *unp;
438         struct unionfs_node_status *unsp;
439         struct vnode   *uvp;
440         struct vnode   *lvp;
441         struct vnode   *targetvp;
442         struct ucred   *cred;
443         struct thread  *td;
444
445         UNIONFS_INTERNAL_DEBUG("unionfs_open: enter\n");
446
447         error = 0;
448         unp = VTOUNIONFS(ap->a_vp);
449         uvp = unp->un_uppervp;
450         lvp = unp->un_lowervp;
451         targetvp = NULLVP;
452         cred = ap->a_cred;
453         td = ap->a_td;
454
455         unionfs_get_node_status(unp, td, &unsp);
456
457         if (unsp->uns_lower_opencnt > 0 || unsp->uns_upper_opencnt > 0) {
458                 /* vnode is already opend. */
459                 if (unsp->uns_upper_opencnt > 0)
460                         targetvp = uvp;
461                 else
462                         targetvp = lvp;
463
464                 if (targetvp == lvp &&
465                     (ap->a_mode & FWRITE) && lvp->v_type == VREG)
466                         targetvp = NULLVP;
467         }
468         if (targetvp == NULLVP) {
469                 if (uvp == NULLVP) {
470                         if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) {
471                                 error = unionfs_copyfile(unp,
472                                     !(ap->a_mode & O_TRUNC), cred, td);
473                                 if (error != 0)
474                                         goto unionfs_open_abort;
475                                 targetvp = uvp = unp->un_uppervp;
476                         } else
477                                 targetvp = lvp;
478                 } else
479                         targetvp = uvp;
480         }
481
482         error = VOP_OPEN(targetvp, ap->a_mode, cred, td, ap->a_fp);
483         if (error == 0) {
484                 if (targetvp == uvp) {
485                         if (uvp->v_type == VDIR && lvp != NULLVP &&
486                             unsp->uns_lower_opencnt <= 0) {
487                                 /* open lower for readdir */
488                                 error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
489                                 if (error != 0) {
490                                         VOP_CLOSE(uvp, ap->a_mode, cred, td);
491                                         goto unionfs_open_abort;
492                                 }
493                                 unsp->uns_node_flag |= UNS_OPENL_4_READDIR;
494                                 unsp->uns_lower_opencnt++;
495                         }
496                         unsp->uns_upper_opencnt++;
497                 } else {
498                         unsp->uns_lower_opencnt++;
499                         unsp->uns_lower_openmode = ap->a_mode;
500                 }
501                 ap->a_vp->v_object = targetvp->v_object;
502         }
503
504 unionfs_open_abort:
505         if (error != 0)
506                 unionfs_tryrem_node_status(unp, unsp);
507
508         UNIONFS_INTERNAL_DEBUG("unionfs_open: leave (%d)\n", error);
509
510         return (error);
511 }
512
513 static int
514 unionfs_close(struct vop_close_args *ap)
515 {
516         int             error;
517         int             locked;
518         struct unionfs_node *unp;
519         struct unionfs_node_status *unsp;
520         struct ucred   *cred;
521         struct thread  *td;
522         struct vnode   *ovp;
523
524         UNIONFS_INTERNAL_DEBUG("unionfs_close: enter\n");
525
526         locked = 0;
527         unp = VTOUNIONFS(ap->a_vp);
528         cred = ap->a_cred;
529         td = ap->a_td;
530
531         if (VOP_ISLOCKED(ap->a_vp) != LK_EXCLUSIVE) {
532                 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
533                 locked = 1;
534         }
535         unionfs_get_node_status(unp, td, &unsp);
536
537         if (unsp->uns_lower_opencnt <= 0 && unsp->uns_upper_opencnt <= 0) {
538 #ifdef DIAGNOSTIC
539                 printf("unionfs_close: warning: open count is 0\n");
540 #endif
541                 if (unp->un_uppervp != NULLVP)
542                         ovp = unp->un_uppervp;
543                 else
544                         ovp = unp->un_lowervp;
545         } else if (unsp->uns_upper_opencnt > 0)
546                 ovp = unp->un_uppervp;
547         else
548                 ovp = unp->un_lowervp;
549
550         error = VOP_CLOSE(ovp, ap->a_fflag, cred, td);
551
552         if (error != 0)
553                 goto unionfs_close_abort;
554
555         ap->a_vp->v_object = ovp->v_object;
556
557         if (ovp == unp->un_uppervp) {
558                 unsp->uns_upper_opencnt--;
559                 if (unsp->uns_upper_opencnt == 0) {
560                         if (unsp->uns_node_flag & UNS_OPENL_4_READDIR) {
561                                 VOP_CLOSE(unp->un_lowervp, FREAD, cred, td);
562                                 unsp->uns_node_flag &= ~UNS_OPENL_4_READDIR;
563                                 unsp->uns_lower_opencnt--;
564                         }
565                         if (unsp->uns_lower_opencnt > 0)
566                                 ap->a_vp->v_object = unp->un_lowervp->v_object;
567                 }
568         } else
569                 unsp->uns_lower_opencnt--;
570
571 unionfs_close_abort:
572         unionfs_tryrem_node_status(unp, unsp);
573
574         if (locked != 0)
575                 VOP_UNLOCK(ap->a_vp, 0);
576
577         UNIONFS_INTERNAL_DEBUG("unionfs_close: leave (%d)\n", error);
578
579         return (error);
580 }
581
582 /*
583  * Check the access mode toward shadow file/dir.
584  */
585 static int
586 unionfs_check_corrected_access(u_short mode,
587                              struct vattr *va,
588                              struct ucred *cred)
589 {
590         int             count;
591         uid_t           uid;    /* upper side vnode's uid */
592         gid_t           gid;    /* upper side vnode's gid */
593         u_short         vmode;  /* upper side vnode's mode */
594         gid_t          *gp;
595         u_short         mask;
596
597         mask = 0;
598         uid = va->va_uid;
599         gid = va->va_gid;
600         vmode = va->va_mode;
601
602         /* check owner */
603         if (cred->cr_uid == uid) {
604                 if (mode & VEXEC)
605                         mask |= S_IXUSR;
606                 if (mode & VREAD)
607                         mask |= S_IRUSR;
608                 if (mode & VWRITE)
609                         mask |= S_IWUSR;
610                 return ((vmode & mask) == mask ? 0 : EACCES);
611         }
612
613         /* check group */
614         count = 0;
615         gp = cred->cr_groups;
616         for (; count < cred->cr_ngroups; count++, gp++) {
617                 if (gid == *gp) {
618                         if (mode & VEXEC)
619                                 mask |= S_IXGRP;
620                         if (mode & VREAD)
621                                 mask |= S_IRGRP;
622                         if (mode & VWRITE)
623                                 mask |= S_IWGRP;
624                         return ((vmode & mask) == mask ? 0 : EACCES);
625                 }
626         }
627
628         /* check other */
629         if (mode & VEXEC)
630                 mask |= S_IXOTH;
631         if (mode & VREAD)
632                 mask |= S_IROTH;
633         if (mode & VWRITE)
634                 mask |= S_IWOTH;
635
636         return ((vmode & mask) == mask ? 0 : EACCES);
637 }
638
639 static int
640 unionfs_access(struct vop_access_args *ap)
641 {
642         struct unionfs_mount *ump;
643         struct unionfs_node *unp;
644         struct vnode   *uvp;
645         struct vnode   *lvp;
646         struct thread  *td;
647         struct vattr    va;
648         int             mode;
649         int             error;
650
651         UNIONFS_INTERNAL_DEBUG("unionfs_access: enter\n");
652
653         ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
654         unp = VTOUNIONFS(ap->a_vp);
655         uvp = unp->un_uppervp;
656         lvp = unp->un_lowervp;
657         td = ap->a_td;
658         mode = ap->a_mode;
659         error = EACCES;
660
661         if ((mode & VWRITE) &&
662             (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) {
663                 switch (ap->a_vp->v_type) {
664                 case VREG:
665                 case VDIR:
666                 case VLNK:
667                         return (EROFS);
668                 default:
669                         break;
670                 }
671         }
672
673         if (uvp != NULLVP) {
674                 error = VOP_ACCESS(uvp, mode, ap->a_cred, td);
675
676                 UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
677
678                 return (error);
679         }
680
681         if (lvp != NULLVP) {
682                 if (mode & VWRITE) {
683                         if (ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY) {
684                                 switch (ap->a_vp->v_type) {
685                                 case VREG:
686                                 case VDIR:
687                                 case VLNK:
688                                         return (EROFS);
689                                 default:
690                                         break;
691                                 }
692                         } else if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
693                                 /* check shadow file/dir */
694                                 if (ump->um_copymode != UNIONFS_TRANSPARENT) {
695                                         error = unionfs_create_uppervattr(ump,
696                                             lvp, &va, ap->a_cred, td);
697                                         if (error != 0)
698                                                 return (error);
699
700                                         error = unionfs_check_corrected_access(
701                                             mode, &va, ap->a_cred);
702                                         if (error != 0)
703                                                 return (error);
704                                 }
705                         }
706                         mode &= ~VWRITE;
707                         mode |= VREAD; /* will copy to upper */
708                 }
709                 error = VOP_ACCESS(lvp, mode, ap->a_cred, td);
710         }
711
712         UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
713
714         return (error);
715 }
716
717 static int
718 unionfs_getattr(struct vop_getattr_args *ap)
719 {
720         int             error;
721         struct unionfs_node *unp;
722         struct unionfs_mount *ump;
723         struct vnode   *uvp;
724         struct vnode   *lvp;
725         struct thread  *td;
726         struct vattr    va;
727
728         UNIONFS_INTERNAL_DEBUG("unionfs_getattr: enter\n");
729
730         unp = VTOUNIONFS(ap->a_vp);
731         ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
732         uvp = unp->un_uppervp;
733         lvp = unp->un_lowervp;
734         td = curthread;
735
736         if (uvp != NULLVP) {
737                 if ((error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred)) == 0)
738                         ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
739
740                 UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
741                     ap->a_vap->va_mode, ap->a_vap->va_uid,
742                     ap->a_vap->va_gid, error);
743
744                 return (error);
745         }
746
747         error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred);
748
749         if (error == 0 && !(ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY)) {
750                 /* correct the attr toward shadow file/dir. */
751                 if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
752                         unionfs_create_uppervattr_core(ump, ap->a_vap, &va, td);
753                         ap->a_vap->va_mode = va.va_mode;
754                         ap->a_vap->va_uid = va.va_uid;
755                         ap->a_vap->va_gid = va.va_gid;
756                 }
757         }
758
759         if (error == 0)
760                 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
761
762         UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
763             ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error);
764
765         return (error);
766 }
767
768 static int
769 unionfs_setattr(struct vop_setattr_args *ap)
770 {
771         int             error;
772         struct unionfs_node *unp;
773         struct vnode   *uvp;
774         struct vnode   *lvp;
775         struct thread  *td;
776         struct vattr   *vap;
777
778         UNIONFS_INTERNAL_DEBUG("unionfs_setattr: enter\n");
779
780         error = EROFS;
781         unp = VTOUNIONFS(ap->a_vp);
782         uvp = unp->un_uppervp;
783         lvp = unp->un_lowervp;
784         td = curthread;
785         vap = ap->a_vap;
786
787         if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) &&
788             (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
789              vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
790              vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL))
791                 return (EROFS);
792
793         if (uvp == NULLVP && lvp->v_type == VREG) {
794                 error = unionfs_copyfile(unp, (vap->va_size != 0),
795                     ap->a_cred, td);
796                 if (error != 0)
797                         return (error);
798                 uvp = unp->un_uppervp;
799         }
800
801         if (uvp != NULLVP)
802                 error = VOP_SETATTR(uvp, vap, ap->a_cred);
803
804         UNIONFS_INTERNAL_DEBUG("unionfs_setattr: leave (%d)\n", error);
805
806         return (error);
807 }
808
809 static int
810 unionfs_read(struct vop_read_args *ap)
811 {
812         int             error;
813         struct unionfs_node *unp;
814         struct vnode   *tvp;
815
816         /* UNIONFS_INTERNAL_DEBUG("unionfs_read: enter\n"); */
817
818         unp = VTOUNIONFS(ap->a_vp);
819         tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
820
821         error = VOP_READ(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
822
823         /* UNIONFS_INTERNAL_DEBUG("unionfs_read: leave (%d)\n", error); */
824
825         return (error);
826 }
827
828 static int
829 unionfs_write(struct vop_write_args *ap)
830 {
831         int             error;
832         struct unionfs_node *unp;
833         struct vnode   *tvp;
834
835         /* UNIONFS_INTERNAL_DEBUG("unionfs_write: enter\n"); */
836
837         unp = VTOUNIONFS(ap->a_vp);
838         tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
839
840         error = VOP_WRITE(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
841
842         /* UNIONFS_INTERNAL_DEBUG("unionfs_write: leave (%d)\n", error); */
843
844         return (error);
845 }
846
847 static int
848 unionfs_lease(struct vop_lease_args *ap)
849 {
850         int error;
851         struct unionfs_node *unp;
852         struct vnode   *vp;
853
854         UNIONFS_INTERNAL_DEBUG("unionfs_lease: enter\n");
855
856         unp = VTOUNIONFS(ap->a_vp);
857         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
858
859         error = VOP_LEASE(vp, ap->a_td, ap->a_cred, ap->a_flag);
860
861         UNIONFS_INTERNAL_DEBUG("unionfs_lease: lease (%d)\n", error);
862
863         return (error);
864 }
865
866 static int
867 unionfs_ioctl(struct vop_ioctl_args *ap)
868 {
869         int error;
870         struct unionfs_node *unp;
871         struct unionfs_node_status *unsp;
872         struct vnode   *ovp;
873
874         UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: enter\n");
875
876         vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
877         unp = VTOUNIONFS(ap->a_vp);
878         unionfs_get_node_status(unp, ap->a_td, &unsp);
879         ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
880         unionfs_tryrem_node_status(unp, unsp);
881         VOP_UNLOCK(ap->a_vp, 0);
882
883         if (ovp == NULLVP)
884                 return (EBADF);
885
886         error = VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag,
887             ap->a_cred, ap->a_td);
888
889         UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: lease (%d)\n", error);
890
891         return (error);
892 }
893
894 static int
895 unionfs_poll(struct vop_poll_args *ap)
896 {
897         struct unionfs_node *unp;
898         struct unionfs_node_status *unsp;
899         struct vnode   *ovp;
900
901         vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
902         unp = VTOUNIONFS(ap->a_vp);
903         unionfs_get_node_status(unp, ap->a_td, &unsp);
904         ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
905         unionfs_tryrem_node_status(unp, unsp);
906         VOP_UNLOCK(ap->a_vp, 0);
907
908         if (ovp == NULLVP)
909                 return (EBADF);
910
911         return (VOP_POLL(ovp, ap->a_events, ap->a_cred, ap->a_td));
912 }
913
914 static int
915 unionfs_fsync(struct vop_fsync_args *ap)
916 {
917         struct unionfs_node *unp;
918         struct unionfs_node_status *unsp;
919         struct vnode   *ovp;
920
921         unp = VTOUNIONFS(ap->a_vp);
922         unionfs_get_node_status(unp, ap->a_td, &unsp);
923         ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
924         unionfs_tryrem_node_status(unp, unsp);
925
926         if (ovp == NULLVP)
927                 return (EBADF);
928
929         return (VOP_FSYNC(ovp, ap->a_waitfor, ap->a_td));
930 }
931
932 static int
933 unionfs_remove(struct vop_remove_args *ap)
934 {
935         int             error;
936         struct unionfs_node *dunp;
937         struct unionfs_node *unp;
938         struct unionfs_mount *ump;
939         struct vnode   *udvp;
940         struct vnode   *uvp;
941         struct vnode   *lvp;
942         struct componentname *cnp;
943         struct thread  *td;
944
945         UNIONFS_INTERNAL_DEBUG("unionfs_remove: enter\n");
946
947         error = 0;
948         dunp = VTOUNIONFS(ap->a_dvp);
949         unp = VTOUNIONFS(ap->a_vp);
950         udvp = dunp->un_uppervp;
951         uvp = unp->un_uppervp;
952         lvp = unp->un_lowervp;
953         cnp = ap->a_cnp;
954         td = curthread;
955
956         if (udvp == NULLVP)
957                 return (EROFS);
958
959         if (uvp != NULLVP) {
960                 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
961                 if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP)
962                         cnp->cn_flags |= DOWHITEOUT;
963                 error = VOP_REMOVE(udvp, uvp, cnp);
964         } else if (lvp != NULLVP)
965                 error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path);
966
967         UNIONFS_INTERNAL_DEBUG("unionfs_remove: leave (%d)\n", error);
968
969         return (error);
970 }
971
972 static int
973 unionfs_link(struct vop_link_args *ap)
974 {
975         int             error;
976         int             needrelookup;
977         struct unionfs_node *dunp;
978         struct unionfs_node *unp;
979         struct vnode   *udvp;
980         struct vnode   *uvp;
981         struct componentname *cnp;
982         struct thread  *td;
983
984         UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n");
985
986         error = 0;
987         needrelookup = 0;
988         dunp = VTOUNIONFS(ap->a_tdvp);
989         unp = NULL;
990         udvp = dunp->un_uppervp;
991         uvp = NULLVP;
992         cnp = ap->a_cnp;
993         td = curthread;
994
995         if (udvp == NULLVP)
996                 return (EROFS);
997
998         if (ap->a_vp->v_op != &unionfs_vnodeops)
999                 uvp = ap->a_vp;
1000         else {
1001                 unp = VTOUNIONFS(ap->a_vp);
1002
1003                 if (unp->un_uppervp == NULLVP) {
1004                         if (ap->a_vp->v_type != VREG)
1005                                 return (EOPNOTSUPP);
1006
1007                         error = unionfs_copyfile(unp, 1, cnp->cn_cred, td);
1008                         if (error != 0)
1009                                 return (error);
1010                         needrelookup = 1;
1011                 }
1012                 uvp = unp->un_uppervp;
1013         }
1014
1015         if (needrelookup != 0)
1016                 error = unionfs_relookup_for_create(ap->a_tdvp, cnp, td);
1017
1018         if (error == 0)
1019                 error = VOP_LINK(udvp, uvp, cnp);
1020
1021         UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error);
1022
1023         return (error);
1024 }
1025
1026 static int
1027 unionfs_rename(struct vop_rename_args *ap)
1028 {
1029         int             error;
1030         struct vnode   *fdvp;
1031         struct vnode   *fvp;
1032         struct componentname *fcnp;
1033         struct vnode   *tdvp;
1034         struct vnode   *tvp;
1035         struct componentname *tcnp;
1036         struct vnode   *ltdvp;
1037         struct vnode   *ltvp;
1038         struct thread  *td;
1039
1040         /* rename target vnodes */
1041         struct vnode   *rfdvp;
1042         struct vnode   *rfvp;
1043         struct vnode   *rtdvp;
1044         struct vnode   *rtvp;
1045
1046         int             needrelookup;
1047         struct unionfs_mount *ump;
1048         struct unionfs_node *unp;
1049
1050         UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n");
1051
1052         error = 0;
1053         fdvp = ap->a_fdvp;
1054         fvp = ap->a_fvp;
1055         fcnp = ap->a_fcnp;
1056         tdvp = ap->a_tdvp;
1057         tvp = ap->a_tvp;
1058         tcnp = ap->a_tcnp;
1059         ltdvp = NULLVP;
1060         ltvp = NULLVP;
1061         td = curthread;
1062         rfdvp = fdvp;
1063         rfvp = fvp;
1064         rtdvp = tdvp;
1065         rtvp = tvp;
1066         needrelookup = 0;
1067
1068 #ifdef DIAGNOSTIC
1069         if (!(fcnp->cn_flags & HASBUF) || !(tcnp->cn_flags & HASBUF))
1070                 panic("unionfs_rename: no name");
1071 #endif
1072
1073         /* check for cross device rename */
1074         if (fvp->v_mount != tdvp->v_mount ||
1075             (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) {
1076                 error = EXDEV;
1077                 goto unionfs_rename_abort;
1078         }
1079
1080         /* Renaming a file to itself has no effect. */
1081         if (fvp == tvp)
1082                 goto unionfs_rename_abort;
1083
1084         /*
1085          * from/to vnode is unionfs node.
1086          */
1087
1088         unp = VTOUNIONFS(fdvp);
1089 #ifdef UNIONFS_IDBG_RENAME
1090         UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n", fdvp, unp->un_uppervp, unp->un_lowervp);
1091 #endif
1092         if (unp->un_uppervp == NULLVP) {
1093                 error = ENODEV;
1094                 goto unionfs_rename_abort;
1095         }
1096         rfdvp = unp->un_uppervp;
1097         vref(rfdvp);
1098
1099         unp = VTOUNIONFS(fvp);
1100 #ifdef UNIONFS_IDBG_RENAME
1101         UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n", fvp, unp->un_uppervp, unp->un_lowervp);
1102 #endif
1103         ump = MOUNTTOUNIONFSMOUNT(fvp->v_mount);
1104         if (unp->un_uppervp == NULLVP) {
1105                 switch (fvp->v_type) {
1106                 case VREG:
1107                         if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
1108                                 goto unionfs_rename_abort;
1109                         error = unionfs_copyfile(unp, 1, fcnp->cn_cred, td);
1110                         VOP_UNLOCK(fvp, 0);
1111                         if (error != 0)
1112                                 goto unionfs_rename_abort;
1113                         break;
1114                 case VDIR:
1115                         if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
1116                                 goto unionfs_rename_abort;
1117                         error = unionfs_mkshadowdir(ump, rfdvp, unp, fcnp, td);
1118                         VOP_UNLOCK(fvp, 0);
1119                         if (error != 0)
1120                                 goto unionfs_rename_abort;
1121                         break;
1122                 default:
1123                         error = ENODEV;
1124                         goto unionfs_rename_abort;
1125                 }
1126
1127                 needrelookup = 1;
1128         }
1129
1130         if (unp->un_lowervp != NULLVP)
1131                 fcnp->cn_flags |= DOWHITEOUT;
1132         rfvp = unp->un_uppervp;
1133         vref(rfvp);
1134
1135         unp = VTOUNIONFS(tdvp);
1136 #ifdef UNIONFS_IDBG_RENAME
1137         UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n", tdvp, unp->un_uppervp, unp->un_lowervp);
1138 #endif
1139         if (unp->un_uppervp == NULLVP) {
1140                 error = ENODEV;
1141                 goto unionfs_rename_abort;
1142         }
1143         rtdvp = unp->un_uppervp;
1144         ltdvp = unp->un_lowervp;
1145         vref(rtdvp);
1146
1147         if (tdvp == tvp) {
1148                 rtvp = rtdvp;
1149                 vref(rtvp);
1150         } else if (tvp != NULLVP) {
1151                 unp = VTOUNIONFS(tvp);
1152 #ifdef UNIONFS_IDBG_RENAME
1153                 UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n", tvp, unp->un_uppervp, unp->un_lowervp);
1154 #endif
1155                 if (unp->un_uppervp == NULLVP)
1156                         rtvp = NULLVP;
1157                 else {
1158                         if (tvp->v_type == VDIR) {
1159                                 error = EINVAL;
1160                                 goto unionfs_rename_abort;
1161                         }
1162                         rtvp = unp->un_uppervp;
1163                         ltvp = unp->un_lowervp;
1164                         vref(rtvp);
1165                 }
1166         }
1167
1168         if (rfvp == rtvp)
1169                 goto unionfs_rename_abort;
1170
1171         if (needrelookup != 0) {
1172                 if ((error = vn_lock(fdvp, LK_EXCLUSIVE)) != 0)
1173                         goto unionfs_rename_abort;
1174                 error = unionfs_relookup_for_delete(fdvp, fcnp, td);
1175                 VOP_UNLOCK(fdvp, 0);
1176                 if (error != 0)
1177                         goto unionfs_rename_abort;
1178
1179                 /* Locke of tvp is canceled in order to avoid recursive lock. */
1180                 if (tvp != NULLVP && tvp != tdvp)
1181                         VOP_UNLOCK(tvp, 0);
1182                 error = unionfs_relookup_for_rename(tdvp, tcnp, td);
1183                 if (tvp != NULLVP && tvp != tdvp)
1184                         vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
1185                 if (error != 0)
1186                         goto unionfs_rename_abort;
1187         }
1188
1189         error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp);
1190
1191         if (error == 0) {
1192                 if (rtvp != NULLVP && rtvp->v_type == VDIR)
1193                         cache_purge(tdvp);
1194                 if (fvp->v_type == VDIR && fdvp != tdvp)
1195                         cache_purge(fdvp);
1196         }
1197
1198         if (ltdvp != NULLVP)
1199                 VOP_UNLOCK(ltdvp, 0);
1200         if (tdvp != rtdvp)
1201                 vrele(tdvp);
1202         if (ltvp != NULLVP)
1203                 VOP_UNLOCK(ltvp, 0);
1204         if (tvp != rtvp && tvp != NULLVP) {
1205                 if (rtvp == NULLVP)
1206                         vput(tvp);
1207                 else
1208                         vrele(tvp);
1209         }
1210         if (fdvp != rfdvp)
1211                 vrele(fdvp);
1212         if (fvp != rfvp)
1213                 vrele(fvp);
1214
1215         UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
1216
1217         return (error);
1218
1219 unionfs_rename_abort:
1220         vput(tdvp);
1221         if (tdvp != rtdvp)
1222                 vrele(rtdvp);
1223         if (tvp != NULLVP) {
1224                 if (tdvp != tvp)
1225                         vput(tvp);
1226                 else
1227                         vrele(tvp);
1228         }
1229         if (tvp != rtvp && rtvp != NULLVP)
1230                 vrele(rtvp);
1231         if (fdvp != rfdvp)
1232                 vrele(rfdvp);
1233         if (fvp != rfvp)
1234                 vrele(rfvp);
1235         vrele(fdvp);
1236         vrele(fvp);
1237
1238         UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
1239
1240         return (error);
1241 }
1242
1243 static int
1244 unionfs_mkdir(struct vop_mkdir_args *ap)
1245 {
1246         int             error;
1247         int             lkflags;
1248         struct unionfs_node *dunp;
1249         struct componentname *cnp;
1250         struct thread  *td;
1251         struct vnode   *udvp;
1252         struct vnode   *uvp;
1253         struct vattr    va;
1254
1255         UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: enter\n");
1256
1257         error = EROFS;
1258         dunp = VTOUNIONFS(ap->a_dvp);
1259         cnp = ap->a_cnp;
1260         lkflags = cnp->cn_lkflags;
1261         td = curthread;
1262         udvp = dunp->un_uppervp;
1263
1264         if (udvp != NULLVP) {
1265                 /* check opaque */
1266                 if (!(cnp->cn_flags & ISWHITEOUT)) {
1267                         error = VOP_GETATTR(udvp, &va, cnp->cn_cred);
1268                         if (error != 0)
1269                                 return (error);
1270                         if (va.va_flags & OPAQUE) 
1271                                 cnp->cn_flags |= ISWHITEOUT;
1272                 }
1273
1274                 if ((error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap)) == 0) {
1275                         VOP_UNLOCK(uvp, 0);
1276                         cnp->cn_lkflags = LK_EXCLUSIVE;
1277                         error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
1278                             ap->a_dvp, ap->a_vpp, cnp, td);
1279                         cnp->cn_lkflags = lkflags;
1280                         vrele(uvp);
1281                 }
1282         }
1283
1284         UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: leave (%d)\n", error);
1285
1286         return (error);
1287 }
1288
1289 static int
1290 unionfs_rmdir(struct vop_rmdir_args *ap)
1291 {
1292         int             error;
1293         struct unionfs_node *dunp;
1294         struct unionfs_node *unp;
1295         struct unionfs_mount *ump;
1296         struct componentname *cnp;
1297         struct thread  *td;
1298         struct vnode   *udvp;
1299         struct vnode   *uvp;
1300         struct vnode   *lvp;
1301
1302         UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: enter\n");
1303
1304         error = 0;
1305         dunp = VTOUNIONFS(ap->a_dvp);
1306         unp = VTOUNIONFS(ap->a_vp);
1307         cnp = ap->a_cnp;
1308         td = curthread;
1309         udvp = dunp->un_uppervp;
1310         uvp = unp->un_uppervp;
1311         lvp = unp->un_lowervp;
1312
1313         if (udvp == NULLVP)
1314                 return (EROFS);
1315
1316         if (udvp == uvp)
1317                 return (EOPNOTSUPP);
1318
1319         if (uvp != NULLVP) {
1320                 if (lvp != NULLVP) {
1321                         error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td);
1322                         if (error != 0)
1323                                 return (error);
1324                 }
1325                 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
1326                 if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP)
1327                         cnp->cn_flags |= DOWHITEOUT;
1328                 error = VOP_RMDIR(udvp, uvp, cnp);
1329         }
1330         else if (lvp != NULLVP)
1331                 error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path);
1332
1333         if (error == 0) {
1334                 cache_purge(ap->a_dvp);
1335                 cache_purge(ap->a_vp);
1336         }
1337
1338         UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: leave (%d)\n", error);
1339
1340         return (error);
1341 }
1342
1343 static int
1344 unionfs_symlink(struct vop_symlink_args *ap)
1345 {
1346         int             error;
1347         int             lkflags;
1348         struct unionfs_node *dunp;
1349         struct componentname *cnp;
1350         struct thread  *td;
1351         struct vnode   *udvp;
1352         struct vnode   *uvp;
1353
1354         UNIONFS_INTERNAL_DEBUG("unionfs_symlink: enter\n");
1355
1356         error = EROFS;
1357         dunp = VTOUNIONFS(ap->a_dvp);
1358         cnp = ap->a_cnp;
1359         lkflags = cnp->cn_lkflags;
1360         td = curthread;
1361         udvp = dunp->un_uppervp;
1362
1363         if (udvp != NULLVP) {
1364                 error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target);
1365                 if (error == 0) {
1366                         VOP_UNLOCK(uvp, 0);
1367                         cnp->cn_lkflags = LK_EXCLUSIVE;
1368                         error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
1369                             ap->a_dvp, ap->a_vpp, cnp, td);
1370                         cnp->cn_lkflags = lkflags;
1371                         vrele(uvp);
1372                 }
1373         }
1374
1375         UNIONFS_INTERNAL_DEBUG("unionfs_symlink: leave (%d)\n", error);
1376
1377         return (error);
1378 }
1379
1380 static int
1381 unionfs_readdir(struct vop_readdir_args *ap)
1382 {
1383         int             error;
1384         int             eofflag;
1385         int             locked;
1386         struct unionfs_node *unp;
1387         struct unionfs_node_status *unsp;
1388         struct uio     *uio;
1389         struct vnode   *uvp;
1390         struct vnode   *lvp;
1391         struct thread  *td;
1392         struct vattr    va;
1393
1394         int             ncookies_bk;
1395         u_long         *cookies_bk;
1396
1397         UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n");
1398
1399         error = 0;
1400         eofflag = 0;
1401         locked = 0;
1402         unp = VTOUNIONFS(ap->a_vp);
1403         uio = ap->a_uio;
1404         uvp = unp->un_uppervp;
1405         lvp = unp->un_lowervp;
1406         td = uio->uio_td;
1407         ncookies_bk = 0;
1408         cookies_bk = NULL;
1409
1410         if (ap->a_vp->v_type != VDIR)
1411                 return (ENOTDIR);
1412
1413         /* check opaque */
1414         if (uvp != NULLVP && lvp != NULLVP) {
1415                 if ((error = VOP_GETATTR(uvp, &va, ap->a_cred)) != 0)
1416                         goto unionfs_readdir_exit;
1417                 if (va.va_flags & OPAQUE)
1418                         lvp = NULLVP;
1419         }
1420
1421         /* check the open count. unionfs needs to open before readdir. */
1422         if (VOP_ISLOCKED(ap->a_vp) != LK_EXCLUSIVE) {
1423                 vn_lock(ap->a_vp, LK_UPGRADE | LK_RETRY);
1424                 locked = 1;
1425         }
1426         unionfs_get_node_status(unp, td, &unsp);
1427         if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) ||
1428             (lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) {
1429                 unionfs_tryrem_node_status(unp, unsp);
1430                 error = EBADF;
1431         }
1432         if (locked == 1)
1433                 vn_lock(ap->a_vp, LK_DOWNGRADE | LK_RETRY);
1434         if (error != 0)
1435                 goto unionfs_readdir_exit;
1436
1437         /* upper only */
1438         if (uvp != NULLVP && lvp == NULLVP) {
1439                 error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag,
1440                     ap->a_ncookies, ap->a_cookies);
1441                 unsp->uns_readdir_status = 0;
1442
1443                 goto unionfs_readdir_exit;
1444         }
1445
1446         /* lower only */
1447         if (uvp == NULLVP && lvp != NULLVP) {
1448                 error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
1449                     ap->a_ncookies, ap->a_cookies);
1450                 unsp->uns_readdir_status = 2;
1451
1452                 goto unionfs_readdir_exit;
1453         }
1454
1455         /*
1456          * readdir upper and lower
1457          */
1458         KASSERT(uvp != NULLVP, ("unionfs_readdir: null upper vp"));
1459         KASSERT(lvp != NULLVP, ("unionfs_readdir: null lower vp"));
1460         if (uio->uio_offset == 0)
1461                 unsp->uns_readdir_status = 0;
1462
1463         if (unsp->uns_readdir_status == 0) {
1464                 /* read upper */
1465                 error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag,
1466                                     ap->a_ncookies, ap->a_cookies);
1467
1468                 if (error != 0 || eofflag == 0)
1469                         goto unionfs_readdir_exit;
1470                 unsp->uns_readdir_status = 1;
1471
1472                 /*
1473                  * ufs(and other fs) needs size of uio_resid larger than
1474                  * DIRBLKSIZ.
1475                  * size of DIRBLKSIZ equals DEV_BSIZE.
1476                  * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h)
1477                  */
1478                 if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1)))
1479                         goto unionfs_readdir_exit;
1480
1481                 /*
1482                  * backup cookies
1483                  * It prepares to readdir in lower.
1484                  */
1485                 if (ap->a_ncookies != NULL) {
1486                         ncookies_bk = *(ap->a_ncookies);
1487                         *(ap->a_ncookies) = 0;
1488                 }
1489                 if (ap->a_cookies != NULL) {
1490                         cookies_bk = *(ap->a_cookies);
1491                         *(ap->a_cookies) = NULL;
1492                 }
1493         }
1494
1495         /* initialize for readdir in lower */
1496         if (unsp->uns_readdir_status == 1) {
1497                 unsp->uns_readdir_status = 2;
1498                 uio->uio_offset = 0;
1499         }
1500
1501         if (lvp == NULLVP) {
1502                 error = EBADF;
1503                 goto unionfs_readdir_exit;
1504         }
1505         /* read lower */
1506         error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
1507                             ap->a_ncookies, ap->a_cookies);
1508
1509         if (cookies_bk != NULL) {
1510                 /* merge cookies */
1511                 int             size;
1512                 u_long         *newcookies, *pos;
1513
1514                 size = *(ap->a_ncookies) + ncookies_bk;
1515                 newcookies = (u_long *) malloc(size * sizeof(u_long),
1516                     M_TEMP, M_WAITOK);
1517                 pos = newcookies;
1518
1519                 memcpy(pos, cookies_bk, ncookies_bk * sizeof(u_long));
1520                 pos += ncookies_bk * sizeof(u_long);
1521                 memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(u_long));
1522                 free(cookies_bk, M_TEMP);
1523                 free(*(ap->a_cookies), M_TEMP);
1524                 *(ap->a_ncookies) = size;
1525                 *(ap->a_cookies) = newcookies;
1526         }
1527
1528 unionfs_readdir_exit:
1529         if (error != 0 && ap->a_eofflag != NULL)
1530                 *(ap->a_eofflag) = 1;
1531
1532         UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);
1533
1534         return (error);
1535 }
1536
1537 static int
1538 unionfs_readlink(struct vop_readlink_args *ap)
1539 {
1540         int error;
1541         struct unionfs_node *unp;
1542         struct vnode   *vp;
1543
1544         UNIONFS_INTERNAL_DEBUG("unionfs_readlink: enter\n");
1545
1546         unp = VTOUNIONFS(ap->a_vp);
1547         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1548
1549         error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
1550
1551         UNIONFS_INTERNAL_DEBUG("unionfs_readlink: leave (%d)\n", error);
1552
1553         return (error);
1554 }
1555
1556 static int
1557 unionfs_getwritemount(struct vop_getwritemount_args *ap)
1558 {
1559         int             error;
1560         struct vnode   *uvp;
1561         struct vnode   *vp;
1562
1563         UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: enter\n");
1564
1565         error = 0;
1566         vp = ap->a_vp;
1567
1568         if (vp == NULLVP || (vp->v_mount->mnt_flag & MNT_RDONLY))
1569                 return (EACCES);
1570
1571         uvp = UNIONFSVPTOUPPERVP(vp);
1572         if (uvp == NULLVP && VREG == vp->v_type)
1573                 uvp = UNIONFSVPTOUPPERVP(VTOUNIONFS(vp)->un_dvp);
1574
1575         if (uvp != NULLVP)
1576                 error = VOP_GETWRITEMOUNT(uvp, ap->a_mpp);
1577         else {
1578                 VI_LOCK(vp);
1579                 if (vp->v_iflag & VI_FREE)
1580                         error = EOPNOTSUPP;
1581                 else
1582                         error = EACCES;
1583                 VI_UNLOCK(vp);
1584         }
1585
1586         UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: leave (%d)\n", error);
1587
1588         return (error);
1589 }
1590
1591 static int
1592 unionfs_inactive(struct vop_inactive_args *ap)
1593 {
1594         ap->a_vp->v_object = NULL;
1595         vrecycle(ap->a_vp, ap->a_td);
1596         return (0);
1597 }
1598
1599 static int
1600 unionfs_reclaim(struct vop_reclaim_args *ap)
1601 {
1602         /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: enter\n"); */
1603
1604         unionfs_noderem(ap->a_vp, ap->a_td);
1605
1606         /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: leave\n"); */
1607
1608         return (0);
1609 }
1610
1611 static int
1612 unionfs_print(struct vop_print_args *ap)
1613 {
1614         struct unionfs_node *unp;
1615         /* struct unionfs_node_status *unsp; */
1616
1617         unp = VTOUNIONFS(ap->a_vp);
1618         /* unionfs_get_node_status(unp, curthread, &unsp); */
1619
1620         printf("unionfs_vp=%p, uppervp=%p, lowervp=%p\n",
1621             ap->a_vp, unp->un_uppervp, unp->un_lowervp);
1622         /*
1623         printf("unionfs opencnt: uppervp=%d, lowervp=%d\n",
1624             unsp->uns_upper_opencnt, unsp->uns_lower_opencnt);
1625         */
1626
1627         if (unp->un_uppervp != NULLVP)
1628                 vprint("unionfs: upper", unp->un_uppervp);
1629         if (unp->un_lowervp != NULLVP)
1630                 vprint("unionfs: lower", unp->un_lowervp);
1631
1632         return (0);
1633 }
1634
1635 static int
1636 unionfs_get_llt_revlock(int flags)
1637 {
1638         int count;
1639
1640         flags &= LK_TYPE_MASK;
1641         for (count = 0; un_llt[count].lock != 0; count++) {
1642                 if (flags == un_llt[count].lock) {
1643                         return un_llt[count].revlock;
1644                 }
1645         }
1646
1647         return 0;
1648 }
1649
1650 static int
1651 unionfs_lock(struct vop_lock1_args *ap)
1652 {
1653         int             error;
1654         int             flags;
1655         int             revlock;
1656         int             uhold;
1657         struct mount   *mp;
1658         struct unionfs_mount *ump;
1659         struct unionfs_node *unp;
1660         struct vnode   *vp;
1661         struct vnode   *uvp;
1662         struct vnode   *lvp;
1663
1664         error = 0;
1665         uhold = 0;
1666         flags = ap->a_flags;
1667         vp = ap->a_vp;
1668
1669         if (LK_RELEASE == (flags & LK_TYPE_MASK) || !(flags & LK_TYPE_MASK))
1670                 return (VOP_UNLOCK(vp, flags));
1671
1672         if ((revlock = unionfs_get_llt_revlock(flags)) == 0)
1673                 panic("unknown lock type: 0x%x", flags & LK_TYPE_MASK);
1674
1675         if ((flags & LK_INTERLOCK) == 0)
1676                 VI_LOCK(vp);
1677
1678         mp = vp->v_mount;
1679         if (mp == NULL)
1680                 goto unionfs_lock_null_vnode;
1681
1682         ump = MOUNTTOUNIONFSMOUNT(mp);
1683         unp = VTOUNIONFS(vp);
1684         if (ump == NULL || unp == NULL)
1685                 goto unionfs_lock_null_vnode;
1686         lvp = unp->un_lowervp;
1687         uvp = unp->un_uppervp;
1688
1689         if ((mp->mnt_kern_flag & MNTK_MPSAFE) != 0 &&
1690             (vp->v_iflag & VI_OWEINACT) != 0)
1691                 flags |= LK_NOWAIT;
1692
1693         /*
1694          * Sometimes, lower or upper is already exclusive locked.
1695          * (ex. vfs_domount: mounted vnode is already locked.)
1696          */
1697         if ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE &&
1698             vp == ump->um_rootvp)
1699                 flags |= LK_CANRECURSE;
1700
1701         if (lvp != NULLVP) {
1702                 VI_LOCK_FLAGS(lvp, MTX_DUPOK);
1703                 flags |= LK_INTERLOCK;
1704                 vholdl(lvp);
1705
1706                 VI_UNLOCK(vp);
1707                 ap->a_flags &= ~LK_INTERLOCK;
1708
1709                 error = VOP_LOCK(lvp, flags);
1710
1711                 VI_LOCK(vp);
1712                 unp = VTOUNIONFS(vp);
1713                 if (unp == NULL) {
1714                         VI_UNLOCK(vp);
1715                         if (error == 0)
1716                                 VOP_UNLOCK(lvp, 0);
1717                         vdrop(lvp);
1718                         return (vop_stdlock(ap));
1719                 }
1720         }
1721
1722         if (error == 0 && uvp != NULLVP) {
1723                 VI_LOCK_FLAGS(uvp, MTX_DUPOK);
1724                 flags |= LK_INTERLOCK;
1725                 vholdl(uvp);
1726                 uhold = 1;
1727
1728                 VI_UNLOCK(vp);
1729                 ap->a_flags &= ~LK_INTERLOCK;
1730
1731                 error = VOP_LOCK(uvp, flags);
1732
1733                 VI_LOCK(vp);
1734                 unp = VTOUNIONFS(vp);
1735                 if (unp == NULL) {
1736                         VI_UNLOCK(vp);
1737                         if (error == 0) {
1738                                 VOP_UNLOCK(uvp, 0);
1739                                 if (lvp != NULLVP)
1740                                         VOP_UNLOCK(lvp, 0);
1741                         }
1742                         if (lvp != NULLVP)
1743                                 vdrop(lvp);
1744                         vdrop(uvp);
1745                         return (vop_stdlock(ap));
1746                 }
1747
1748                 if (error != 0 && lvp != NULLVP) {
1749                         VI_UNLOCK(vp);
1750                         if ((revlock & LK_TYPE_MASK) == LK_RELEASE)
1751                                 VOP_UNLOCK(lvp, revlock);
1752                         else
1753                                 vn_lock(lvp, revlock | LK_RETRY);
1754                         goto unionfs_lock_abort;
1755                 }
1756         }
1757
1758         VI_UNLOCK(vp);
1759 unionfs_lock_abort:
1760         if (lvp != NULLVP)
1761                 vdrop(lvp);
1762         if (uhold != 0)
1763                 vdrop(uvp);
1764
1765         return (error);
1766
1767 unionfs_lock_null_vnode:
1768         ap->a_flags |= LK_INTERLOCK;
1769         return (vop_stdlock(ap));
1770 }
1771
1772 static int
1773 unionfs_unlock(struct vop_unlock_args *ap)
1774 {
1775         int             error;
1776         int             flags;
1777         int             mtxlkflag;
1778         int             uhold;
1779         struct vnode   *vp;
1780         struct vnode   *lvp;
1781         struct vnode   *uvp;
1782         struct unionfs_node *unp;
1783
1784         error = 0;
1785         mtxlkflag = 0;
1786         uhold = 0;
1787         flags = ap->a_flags | LK_RELEASE;
1788         vp = ap->a_vp;
1789
1790         if ((flags & LK_INTERLOCK) != 0)
1791                 mtxlkflag = 1;
1792         else if (mtx_owned(VI_MTX(vp)) == 0) {
1793                 VI_LOCK(vp);
1794                 mtxlkflag = 2;
1795         }
1796
1797         unp = VTOUNIONFS(vp);
1798         if (unp == NULL)
1799                 goto unionfs_unlock_null_vnode;
1800         lvp = unp->un_lowervp;
1801         uvp = unp->un_uppervp;
1802
1803         if (lvp != NULLVP) {
1804                 VI_LOCK_FLAGS(lvp, MTX_DUPOK);
1805                 flags |= LK_INTERLOCK;
1806                 vholdl(lvp);
1807
1808                 VI_UNLOCK(vp);
1809                 ap->a_flags &= ~LK_INTERLOCK;
1810
1811                 error = VOP_UNLOCK(lvp, flags);
1812
1813                 VI_LOCK(vp);
1814         }
1815
1816         if (error == 0 && uvp != NULLVP) {
1817                 VI_LOCK_FLAGS(uvp, MTX_DUPOK);
1818                 flags |= LK_INTERLOCK;
1819                 vholdl(uvp);
1820                 uhold = 1;
1821
1822                 VI_UNLOCK(vp);
1823                 ap->a_flags &= ~LK_INTERLOCK;
1824
1825                 error = VOP_UNLOCK(uvp, flags);
1826
1827                 VI_LOCK(vp);
1828         }
1829
1830         VI_UNLOCK(vp);
1831         if (lvp != NULLVP)
1832                 vdrop(lvp);
1833         if (uhold != 0)
1834                 vdrop(uvp);
1835         if (mtxlkflag == 0)
1836                 VI_LOCK(vp);
1837
1838         return error;
1839
1840 unionfs_unlock_null_vnode:
1841         if (mtxlkflag == 2)
1842                 VI_UNLOCK(vp);
1843         return (vop_stdunlock(ap));
1844 }
1845
1846 static int
1847 unionfs_pathconf(struct vop_pathconf_args *ap)
1848 {
1849         struct unionfs_node *unp;
1850         struct vnode   *vp;
1851
1852         unp = VTOUNIONFS(ap->a_vp);
1853         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1854
1855         return (VOP_PATHCONF(vp, ap->a_name, ap->a_retval));
1856 }
1857
1858 static int
1859 unionfs_advlock(struct vop_advlock_args *ap)
1860 {
1861         int error;
1862         struct unionfs_node *unp;
1863         struct unionfs_node_status *unsp;
1864         struct vnode   *vp;
1865         struct vnode   *uvp;
1866         struct thread  *td;
1867
1868         UNIONFS_INTERNAL_DEBUG("unionfs_advlock: enter\n");
1869
1870         vp = ap->a_vp;
1871         td = curthread;
1872
1873         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
1874
1875         unp = VTOUNIONFS(ap->a_vp);
1876         uvp = unp->un_uppervp;
1877
1878         if (uvp == NULLVP) {
1879                 error = unionfs_copyfile(unp, 1, td->td_ucred, td);
1880                 if (error != 0)
1881                         goto unionfs_advlock_abort;
1882                 uvp = unp->un_uppervp;
1883
1884                 unionfs_get_node_status(unp, td, &unsp);
1885                 if (unsp->uns_lower_opencnt > 0) {
1886                         /* try reopen the vnode */
1887                         error = VOP_OPEN(uvp, unsp->uns_lower_openmode,
1888                                 td->td_ucred, td, NULL);
1889                         if (error)
1890                                 goto unionfs_advlock_abort;
1891                         unsp->uns_upper_opencnt++;
1892                         VOP_CLOSE(unp->un_lowervp, unsp->uns_lower_openmode, td->td_ucred, td);
1893                         unsp->uns_lower_opencnt--;
1894                 } else
1895                         unionfs_tryrem_node_status(unp, unsp);
1896         }
1897
1898         VOP_UNLOCK(vp, 0);
1899
1900         error = VOP_ADVLOCK(uvp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags);
1901
1902         UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
1903
1904         return error;
1905
1906 unionfs_advlock_abort:
1907         VOP_UNLOCK(vp, 0);
1908
1909         UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
1910
1911         return error;
1912 }
1913
1914 static int
1915 unionfs_strategy(struct vop_strategy_args *ap)
1916 {
1917         struct unionfs_node *unp;
1918         struct vnode   *vp;
1919
1920         unp = VTOUNIONFS(ap->a_vp);
1921         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1922
1923 #ifdef DIAGNOSTIC
1924         if (vp == NULLVP)
1925                 panic("unionfs_strategy: nullvp");
1926
1927         if (ap->a_bp->b_iocmd == BIO_WRITE && vp == unp->un_lowervp)
1928                 panic("unionfs_strategy: writing to lowervp");
1929 #endif
1930
1931         return (VOP_STRATEGY(vp, ap->a_bp));
1932 }
1933
1934 static int
1935 unionfs_getacl(struct vop_getacl_args *ap)
1936 {
1937         int             error;
1938         struct unionfs_node *unp;
1939         struct vnode   *vp;
1940
1941         unp = VTOUNIONFS(ap->a_vp);
1942         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1943
1944         UNIONFS_INTERNAL_DEBUG("unionfs_getacl: enter\n");
1945
1946         error = VOP_GETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
1947
1948         UNIONFS_INTERNAL_DEBUG("unionfs_getacl: leave (%d)\n", error);
1949
1950         return (error);
1951 }
1952
1953 static int
1954 unionfs_setacl(struct vop_setacl_args *ap)
1955 {
1956         int             error;
1957         struct unionfs_node *unp;
1958         struct vnode   *uvp;
1959         struct vnode   *lvp;
1960         struct thread  *td;
1961
1962         UNIONFS_INTERNAL_DEBUG("unionfs_setacl: enter\n");
1963
1964         error = EROFS;
1965         unp = VTOUNIONFS(ap->a_vp);
1966         uvp = unp->un_uppervp;
1967         lvp = unp->un_lowervp;
1968         td = ap->a_td;
1969
1970         if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
1971                 return (EROFS);
1972
1973         if (uvp == NULLVP && lvp->v_type == VREG) {
1974                 if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0)
1975                         return (error);
1976                 uvp = unp->un_uppervp;
1977         }
1978
1979         if (uvp != NULLVP)
1980                 error = VOP_SETACL(uvp, ap->a_type, ap->a_aclp, ap->a_cred, td);
1981
1982         UNIONFS_INTERNAL_DEBUG("unionfs_setacl: leave (%d)\n", error);
1983
1984         return (error);
1985 }
1986
1987 static int
1988 unionfs_aclcheck(struct vop_aclcheck_args *ap)
1989 {
1990         int             error;
1991         struct unionfs_node *unp;
1992         struct vnode   *vp;
1993
1994         UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: enter\n");
1995
1996         unp = VTOUNIONFS(ap->a_vp);
1997         vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1998
1999         error = VOP_ACLCHECK(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
2000
2001         UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: leave (%d)\n", error);
2002
2003         return (error);
2004 }
2005
2006 static int
2007 unionfs_openextattr(struct vop_openextattr_args *ap)
2008 {
2009         int             error;
2010         struct unionfs_node *unp;
2011         struct vnode   *vp;
2012         struct vnode   *tvp;
2013
2014         vp = ap->a_vp;
2015         unp = VTOUNIONFS(vp);
2016         tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2017
2018         if ((tvp == unp->un_uppervp && (unp->un_flag & UNIONFS_OPENEXTU)) ||
2019             (tvp == unp->un_lowervp && (unp->un_flag & UNIONFS_OPENEXTL)))
2020                 return (EBUSY);
2021
2022         error = VOP_OPENEXTATTR(tvp, ap->a_cred, ap->a_td);
2023
2024         if (error == 0) {
2025                 vn_lock(vp, LK_UPGRADE | LK_RETRY);
2026                 if (tvp == unp->un_uppervp)
2027                         unp->un_flag |= UNIONFS_OPENEXTU;
2028                 else
2029                         unp->un_flag |= UNIONFS_OPENEXTL;
2030                 vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
2031         }
2032
2033         return (error);
2034 }
2035
2036 static int
2037 unionfs_closeextattr(struct vop_closeextattr_args *ap)
2038 {
2039         int             error;
2040         struct unionfs_node *unp;
2041         struct vnode   *vp;
2042         struct vnode   *tvp;
2043
2044         vp = ap->a_vp;
2045         unp = VTOUNIONFS(vp);
2046         tvp = NULLVP;
2047
2048         if (unp->un_flag & UNIONFS_OPENEXTU)
2049                 tvp = unp->un_uppervp;
2050         else if (unp->un_flag & UNIONFS_OPENEXTL)
2051                 tvp = unp->un_lowervp;
2052
2053         if (tvp == NULLVP)
2054                 return (EOPNOTSUPP);
2055
2056         error = VOP_CLOSEEXTATTR(tvp, ap->a_commit, ap->a_cred, ap->a_td);
2057
2058         if (error == 0) {
2059                 vn_lock(vp, LK_UPGRADE | LK_RETRY);
2060                 if (tvp == unp->un_uppervp)
2061                         unp->un_flag &= ~UNIONFS_OPENEXTU;
2062                 else
2063                         unp->un_flag &= ~UNIONFS_OPENEXTL;
2064                 vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
2065         }
2066
2067         return (error);
2068 }
2069
2070 static int
2071 unionfs_getextattr(struct vop_getextattr_args *ap)
2072 {
2073         struct unionfs_node *unp;
2074         struct vnode   *vp;
2075
2076         unp = VTOUNIONFS(ap->a_vp);
2077         vp = NULLVP;
2078
2079         if (unp->un_flag & UNIONFS_OPENEXTU)
2080                 vp = unp->un_uppervp;
2081         else if (unp->un_flag & UNIONFS_OPENEXTL)
2082                 vp = unp->un_lowervp;
2083
2084         if (vp == NULLVP)
2085                 return (EOPNOTSUPP);
2086
2087         return (VOP_GETEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
2088             ap->a_uio, ap->a_size, ap->a_cred, ap->a_td));
2089 }
2090
2091 static int
2092 unionfs_setextattr(struct vop_setextattr_args *ap)
2093 {
2094         int             error;
2095         struct unionfs_node *unp;
2096         struct vnode   *uvp;
2097         struct vnode   *lvp;
2098         struct vnode   *ovp;
2099         struct ucred   *cred;
2100         struct thread  *td;
2101
2102         error = EROFS;
2103         unp = VTOUNIONFS(ap->a_vp);
2104         uvp = unp->un_uppervp;
2105         lvp = unp->un_lowervp;
2106         ovp = NULLVP;
2107         cred = ap->a_cred;
2108         td = ap->a_td;
2109
2110         UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: enter (un_flag=%x)\n", unp->un_flag);
2111
2112         if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2113                 return (EROFS);
2114
2115         if (unp->un_flag & UNIONFS_OPENEXTU)
2116                 ovp = unp->un_uppervp;
2117         else if (unp->un_flag & UNIONFS_OPENEXTL)
2118                 ovp = unp->un_lowervp;
2119
2120         if (ovp == NULLVP)
2121                 return (EOPNOTSUPP);
2122
2123         if (ovp == lvp && lvp->v_type == VREG) {
2124                 VOP_CLOSEEXTATTR(lvp, 0, cred, td);
2125                 if (uvp == NULLVP &&
2126                     (error = unionfs_copyfile(unp, 1, cred, td)) != 0) {
2127 unionfs_setextattr_reopen:
2128                         if ((unp->un_flag & UNIONFS_OPENEXTL) &&
2129                             VOP_OPENEXTATTR(lvp, cred, td)) {
2130 #ifdef DIAGNOSTIC
2131                                 panic("unionfs: VOP_OPENEXTATTR failed");
2132 #endif
2133                                 unp->un_flag &= ~UNIONFS_OPENEXTL;
2134                         }
2135                         goto unionfs_setextattr_abort;
2136                 }
2137                 uvp = unp->un_uppervp;
2138                 if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
2139                         goto unionfs_setextattr_reopen;
2140                 unp->un_flag &= ~UNIONFS_OPENEXTL;
2141                 unp->un_flag |= UNIONFS_OPENEXTU;
2142                 ovp = uvp;
2143         }
2144
2145         if (ovp == uvp)
2146                 error = VOP_SETEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
2147                     ap->a_uio, cred, td);
2148
2149 unionfs_setextattr_abort:
2150         UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: leave (%d)\n", error);
2151
2152         return (error);
2153 }
2154
2155 static int
2156 unionfs_listextattr(struct vop_listextattr_args *ap)
2157 {
2158         struct unionfs_node *unp;
2159         struct vnode   *vp;
2160
2161         unp = VTOUNIONFS(ap->a_vp);
2162         vp = NULLVP;
2163
2164         if (unp->un_flag & UNIONFS_OPENEXTU)
2165                 vp = unp->un_uppervp;
2166         else if (unp->un_flag & UNIONFS_OPENEXTL)
2167                 vp = unp->un_lowervp;
2168
2169         if (vp == NULLVP)
2170                 return (EOPNOTSUPP);
2171
2172         return (VOP_LISTEXTATTR(vp, ap->a_attrnamespace, ap->a_uio,
2173             ap->a_size, ap->a_cred, ap->a_td));
2174 }
2175
2176 static int
2177 unionfs_deleteextattr(struct vop_deleteextattr_args *ap)
2178 {
2179         int             error;
2180         struct unionfs_node *unp;
2181         struct vnode   *uvp;
2182         struct vnode   *lvp;
2183         struct vnode   *ovp;
2184         struct ucred   *cred;
2185         struct thread  *td;
2186
2187         error = EROFS;
2188         unp = VTOUNIONFS(ap->a_vp);
2189         uvp = unp->un_uppervp;
2190         lvp = unp->un_lowervp;
2191         ovp = NULLVP;
2192         cred = ap->a_cred;
2193         td = ap->a_td;
2194
2195         UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: enter (un_flag=%x)\n", unp->un_flag);
2196
2197         if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2198                 return (EROFS);
2199
2200         if (unp->un_flag & UNIONFS_OPENEXTU)
2201                 ovp = unp->un_uppervp;
2202         else if (unp->un_flag & UNIONFS_OPENEXTL)
2203                 ovp = unp->un_lowervp;
2204
2205         if (ovp == NULLVP)
2206                 return (EOPNOTSUPP);
2207
2208         if (ovp == lvp && lvp->v_type == VREG) {
2209                 VOP_CLOSEEXTATTR(lvp, 0, cred, td);
2210                 if (uvp == NULLVP &&
2211                     (error = unionfs_copyfile(unp, 1, cred, td)) != 0) {
2212 unionfs_deleteextattr_reopen:
2213                         if ((unp->un_flag & UNIONFS_OPENEXTL) &&
2214                             VOP_OPENEXTATTR(lvp, cred, td)) {
2215 #ifdef DIAGNOSTIC
2216                                 panic("unionfs: VOP_OPENEXTATTR failed");
2217 #endif
2218                                 unp->un_flag &= ~UNIONFS_OPENEXTL;
2219                         }
2220                         goto unionfs_deleteextattr_abort;
2221                 }
2222                 uvp = unp->un_uppervp;
2223                 if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
2224                         goto unionfs_deleteextattr_reopen;
2225                 unp->un_flag &= ~UNIONFS_OPENEXTL;
2226                 unp->un_flag |= UNIONFS_OPENEXTU;
2227                 ovp = uvp;
2228         }
2229
2230         if (ovp == uvp)
2231                 error = VOP_DELETEEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
2232                     ap->a_cred, ap->a_td);
2233
2234 unionfs_deleteextattr_abort:
2235         UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: leave (%d)\n", error);
2236
2237         return (error);
2238 }
2239
2240 static int
2241 unionfs_setlabel(struct vop_setlabel_args *ap)
2242 {
2243         int             error;
2244         struct unionfs_node *unp;
2245         struct vnode   *uvp;
2246         struct vnode   *lvp;
2247         struct thread  *td;
2248
2249         UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: enter\n");
2250
2251         error = EROFS;
2252         unp = VTOUNIONFS(ap->a_vp);
2253         uvp = unp->un_uppervp;
2254         lvp = unp->un_lowervp;
2255         td = ap->a_td;
2256
2257         if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2258                 return (EROFS);
2259
2260         if (uvp == NULLVP && lvp->v_type == VREG) {
2261                 if ((error = unionfs_copyfile(unp, 1, ap->a_cred, td)) != 0)
2262                         return (error);
2263                 uvp = unp->un_uppervp;
2264         }
2265
2266         if (uvp != NULLVP)
2267                 error = VOP_SETLABEL(uvp, ap->a_label, ap->a_cred, td);
2268
2269         UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: leave (%d)\n", error);
2270
2271         return (error);
2272 }
2273
2274 static int
2275 unionfs_vptofh(struct vop_vptofh_args *ap)
2276 {
2277         return (EOPNOTSUPP);
2278 }
2279
2280 struct vop_vector unionfs_vnodeops = {
2281         .vop_default =          &default_vnodeops,
2282
2283         .vop_access =           unionfs_access,
2284         .vop_aclcheck =         unionfs_aclcheck,
2285         .vop_advlock =          unionfs_advlock,
2286         .vop_bmap =             VOP_EOPNOTSUPP,
2287         .vop_cachedlookup =     unionfs_lookup,
2288         .vop_close =            unionfs_close,
2289         .vop_closeextattr =     unionfs_closeextattr,
2290         .vop_create =           unionfs_create,
2291         .vop_deleteextattr =    unionfs_deleteextattr,
2292         .vop_fsync =            unionfs_fsync,
2293         .vop_getacl =           unionfs_getacl,
2294         .vop_getattr =          unionfs_getattr,
2295         .vop_getextattr =       unionfs_getextattr,
2296         .vop_getwritemount =    unionfs_getwritemount,
2297         .vop_inactive =         unionfs_inactive,
2298         .vop_ioctl =            unionfs_ioctl,
2299         .vop_lease =            unionfs_lease,
2300         .vop_link =             unionfs_link,
2301         .vop_listextattr =      unionfs_listextattr,
2302         .vop_lock1 =            unionfs_lock,
2303         .vop_lookup =           vfs_cache_lookup,
2304         .vop_mkdir =            unionfs_mkdir,
2305         .vop_mknod =            unionfs_mknod,
2306         .vop_open =             unionfs_open,
2307         .vop_openextattr =      unionfs_openextattr,
2308         .vop_pathconf =         unionfs_pathconf,
2309         .vop_poll =             unionfs_poll,
2310         .vop_print =            unionfs_print,
2311         .vop_read =             unionfs_read,
2312         .vop_readdir =          unionfs_readdir,
2313         .vop_readlink =         unionfs_readlink,
2314         .vop_reclaim =          unionfs_reclaim,
2315         .vop_remove =           unionfs_remove,
2316         .vop_rename =           unionfs_rename,
2317         .vop_rmdir =            unionfs_rmdir,
2318         .vop_setacl =           unionfs_setacl,
2319         .vop_setattr =          unionfs_setattr,
2320         .vop_setextattr =       unionfs_setextattr,
2321         .vop_setlabel =         unionfs_setlabel,
2322         .vop_strategy =         unionfs_strategy,
2323         .vop_symlink =          unionfs_symlink,
2324         .vop_unlock =           unionfs_unlock,
2325         .vop_whiteout =         unionfs_whiteout,
2326         .vop_write =            unionfs_write,
2327         .vop_vptofh =           unionfs_vptofh,
2328 };