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