/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
*
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
/*
#include <miscfs/union/union.h>
#include <vfs/vfs_support.h>
#include <sys/ubc.h>
+#include <sys/kdebug.h>
#include <sys/uio_internal.h>
-#define FIXUP(un, p) { \
- if (((un)->un_flags & UN_ULOCK) == 0) { \
- union_fixup(un, p); \
- } \
-}
-
-static void
-union_fixup(un, p)
- struct union_node *un;
- struct proc *p;
-{
-
- un->un_flags |= UN_ULOCK;
-}
-
+/* called with no union lock held */
static int
union_lookup1(struct vnode *udvp, struct vnode **dvpp, struct vnode **vpp,
struct componentname *cnp)
return (0);
}
-int
-union_lookup(
- struct vnop_lookup_args /* {
+static int
+union_lookup(struct vnop_lookup_args *ap)
+/*
+ struct vnop_lookup_args {
struct vnodeop_desc *a_desc;
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
vfs_context_t a_context;
- } */ *ap)
+ } *ap)
+*/
{
int error;
- int uerror, lerror;
+ int uerror = 0;
+ int lerror = 0;
struct vnode *uppervp, *lowervp;
struct vnode *upperdvp, *lowerdvp;
struct vnode *dvp = ap->a_dvp;
- struct union_node *dun = VTOUNION(dvp);
+ struct union_node *dun;
struct componentname *cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
int lockparent = cnp->cn_flags & LOCKPARENT;
- struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+ struct union_mount *um;
kauth_cred_t saved_cred;
int iswhiteout;
struct vnode_attr va;
+ int isfaultfs = 0;
+ int upperlookup = 0;
+ int retry_count = 0;
#ifdef notyet
if (cnp->cn_namelen == 3 &&
}
#endif
- cnp->cn_flags |= LOCKPARENT;
+
+retry:
+ union_lock();
+ um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+ dun = VTOUNION(dvp);
upperdvp = dun->un_uppervp;
lowerdvp = dun->un_lowervp;
uppervp = NULLVP;
lowervp = NULLVP;
iswhiteout = 0;
+ union_unlock();
+
+ if(UNION_FAULTIN(um))
+ isfaultfs = 1;
+
+ if (isfaultfs == 0)
+ cnp->cn_flags |= LOCKPARENT;
+
/*
* do the lookup in the upper level.
* if that level comsumes additional pathnames,
* on and just return that vnode.
*/
if (upperdvp != NULLVP) {
- FIXUP(dun, p);
+ if (lockparent != 0)
+ cnp->cn_flags &= ~LOCKPARENT;
uerror = union_lookup1(um->um_uppervp, &upperdvp,
&uppervp, cnp);
- /*if (uppervp == upperdvp)
- dun->un_flags |= UN_KLOCK;*/
+ upperlookup = 1;
if (cnp->cn_consume != 0) {
*ap->a_vpp = uppervp;
if (!lockparent)
cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
return (uerror);
}
if (uerror == ENOENT || uerror == EJUSTRETURN) {
uerror = ENOENT;
}
+ /*
+ * faultingfs: If upper layer lookup is succesful
+ * we will return that vp if it is regular file.
+ * So so skip lower level lookup
+ */
+
+ if ((isfaultfs == 1) && (upperlookup == 1) && (uerror == 0) && ((vnode_isreg(uppervp) != 0)))
+ goto donelowerlookup;
+
/*
* in a similar way to the upper layer, do the lookup
* in the lower layer. this time, if there is some
/* XXX BOGUS */
saved_cred = cnp->cn_context->vc_ucred;
cnp->cn_context->vc_ucred = um->um_cred;
+ if (lockparent != 0)
+ cnp->cn_flags &= ~LOCKPARENT;
lerror = union_lookup1(um->um_lowervp, &lowerdvp,
&lowervp, cnp);
cnp->cn_context->vc_ucred = saved_cred;
} else {
+ if (lockparent != 0)
+ cnp->cn_flags &= ~LOCKPARENT;
lerror = union_lookup1(um->um_lowervp, &lowerdvp,
&lowervp, cnp);
}
*ap->a_vpp = lowervp;
if (!lockparent)
cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
return (lerror);
}
} else {
if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
lowervp = LOWERVP(dun->un_pvp);
if (lowervp != NULLVP) {
- vnode_get(lowervp);
lerror = 0;
}
}
}
+donelowerlookup:
+
if (!lockparent)
cnp->cn_flags &= ~LOCKPARENT;
/* case 1. */
if ((uerror != 0) && (lerror != 0)) {
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
return (uerror);
}
/* case 2. */
if (uerror != 0 /* && (lerror == 0) */ ) {
if (lowervp->v_type == VDIR) { /* case 2b. */
- dun->un_flags &= ~UN_ULOCK;
+ /* No need to lock the union here */
+ /* if the vnode exists it returns it even if it marks error */
+
+ uppervp = NULLVP;
+
uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
- dun->un_flags |= UN_ULOCK;
+ if ((uerror == EEXIST)){
+ if (uppervp == NULLVP) {
+ retry_count++;
+ if (retry_count <= 2) {
+ if (lowervp != NULLVP)
+ vnode_put(lowervp);
+ goto retry;
+ }
+ }
+ uerror = 0;
+ }
+
if (uerror) {
+ if (uppervp != NULLVP) {
+ vnode_put(uppervp);
+ }
if (lowervp != NULLVP) {
vnode_put(lowervp);
- lowervp = NULLVP;
}
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
return (uerror);
}
+ } else if ((lowervp->v_type == VREG) && (isfaultfs == 1)) {
+ error = union_faultin_copyup(&uppervp, upperdvp, lowervp, cnp, ctx);
+ uerror = 0;
}
}
+
+
+ /* if this is faulting filesystem and upper vp exisits skip allocation of union node */
+ if ((isfaultfs == 1) && (uerror == 0) && (uppervp != NULLVP) && ((vnode_isreg(uppervp) != 0)|| (vnode_islnk(uppervp) != 0))) {
+ vn_checkunionwait(uppervp);
+ *ap->a_vpp = uppervp;
+ if (lowervp != NULLVP)
+ vnode_put(lowervp);
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
+ return(0);
+ }
+
+ union_lock();
error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
uppervp, lowervp, 1);
+ union_unlock();
if (error) {
if (uppervp != NULLVP)
vnode_put(lowervp);
}
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ else
+ cnp->cn_flags |= LOCKPARENT;
return (error);
}
-int
-union_create(
- struct vnop_create_args /* {
+static int
+union_create(struct vnop_create_args *ap)
+/*
+ struct vnop_create_args {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vnode_attr *a_vap;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
if (dvp != NULLVP) {
int error;
struct vnode *vp;
struct mount *mp;
- FIXUP(un, p);
- un->un_flags |= UN_KLOCK;
mp = ap->a_dvp->v_mount;
/* note that this is a direct passthrough to the filesystem */
if (error)
return (error);
+ /* if this is faulting filesystem and is a reg file, skip allocation of union node */
+ if (UNNODE_FAULTIN(un) && (vp != NULLVP) && ((vnode_isreg(vp) != 0)|| (vnode_islnk(vp) != 0))) {
+ *ap->a_vpp = vp;
+ return(0);
+ }
+
+
+ union_lock();
error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
NULLVP, 1);
+ union_unlock();
if (error)
vnode_put(vp);
return (error);
}
+
return (EROFS);
}
-int
-union_whiteout(
- struct vnop_whiteout_args /* {
+static int
+union_whiteout(struct vnop_whiteout_args *ap)
+/*
+ struct vnop_whiteout_args {
struct vnode *a_dvp;
struct componentname *a_cnp;
int a_flags;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_dvp);
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
+ int error;
- if (un->un_uppervp == NULLVP)
+ if (un->un_uppervp == NULLVP) {
return (ENOTSUP);
+ }
- FIXUP(un, p);
- return (VNOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags, ap->a_context));
+ error = (VNOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags, ap->a_context));
+ return(error);
}
-int
-union_mknod(
- struct vnop_mknod_args /* {
+/* mknod can do fifos, chr, blk or whiteout entries */
+static int
+union_mknod(struct vnop_mknod_args *ap)
+/*
+ struct vnop_mknod_args {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vnode_attr *a_vap;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
if (dvp != NULLVP) {
int error;
struct vnode *vp;
struct mount *mp;
- FIXUP(un, p);
- un->un_flags |= UN_KLOCK;
mp = ap->a_dvp->v_mount;
/* note that this is a direct passthrough to the filesystem */
return (error);
if (vp != NULLVP) {
+ union_lock();
error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
cnp, vp, NULLVP, 1);
+ union_unlock();
if (error)
vnode_put(vp);
}
return (EROFS);
}
-int
-union_open(
- struct vnop_open_args /* {
+static int
+union_open(struct vnop_open_args *ap)
+/*
+ struct vnop_open_args {
struct vnodeop_desc *a_desc;
struct vnode *a_vp;
int a_mode;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *tvp;
int mode = ap->a_mode;
- kauth_cred_t cred = vfs_context_ucred(ap->a_context);
- struct proc *p = vfs_context_proc(ap->a_context);
int error;
/*
* If there is an existing upper vp then simply open that.
*/
+
tvp = un->un_uppervp;
if (tvp == NULLVP) {
+
/*
* If the lower vnode is being opened for writing, then
* copy the file contents to the upper vnode and open that,
*/
tvp = un->un_lowervp;
if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
- error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
+ /* For above below mounts we need draining.. */
+ /* This path is not taken for faultin mode */
+ /* LOCK the union node as well **/
+ union_lock();
+ un->un_flags |= UN_LOCKED;
+
+ error = union_copyup(un, (mode&O_TRUNC) == 0, ap->a_context);
+ un->un_flags &= ~UN_LOCKED;
+ if ((un->un_flags & UN_WANT) == UN_WANT) {
+ un->un_flags &= ~UN_WANT;
+ wakeup(&un->un_flags);
+ }
+ union_unlock();
if (error == 0)
error = VNOP_OPEN(un->un_uppervp, mode, ap->a_context);
return (error);
return (error);
}
- FIXUP(un, p);
-
error = VNOP_OPEN(tvp, mode, ap->a_context);
return (error);
}
-int
-union_close(ap)
- struct vnop_close_args /* {
+static int
+union_close(struct vnop_close_args *ap)
+/*
+ struct vnop_close_args {
struct vnode *a_vp;
int a_fflag;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *vp;
+ int error = 0;
if ((vp = un->un_uppervp) == NULLVP) {
#ifdef UNION_DIAGNOSTIC
}
ap->a_vp = vp;
- return (VCALL(vp, VOFFSET(vnop_close), ap));
+ error = (VCALL(vp, VOFFSET(vnop_close), ap));
+ return(error);
}
/*
* file permissions are given away simply because
* the user caused an implicit file copy.
*/
-int
-union_access(
- struct vnop_access_args /* {
+static int
+union_access(struct vnop_access_args *ap)
+/*
+ struct vnop_access_args {
struct vnodeop_desc *a_desc;
struct vnode *a_vp;
int a_action;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_vp);
- struct proc *p = vfs_context_proc(ap->a_context);
int error = EACCES;
struct vnode *vp;
if ((vp = un->un_uppervp) != NULLVP) {
- FIXUP(un, p);
ap->a_vp = vp;
return (VCALL(vp, VOFFSET(vnop_access), ap));
}
struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
if (um->um_op == UNMNT_BELOW) {
- /* XXX fix me */
- // ap->a_cred = um->um_cred;
error = VCALL(vp, VOFFSET(vnop_access), ap);
}
}
* We handle getattr only to change the fsid and
* track object sizes
*/
-int
-union_getattr(ap)
- struct vnop_getattr_args /* {
+static int
+union_getattr(struct vnop_getattr_args *ap)
+/*
+ struct vnop_getattr_args {
struct vnode *a_vp;
struct vnode_attr *a_vap;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
- int error;
+ int error=0;
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *vp = un->un_uppervp;
- struct proc *p = vfs_context_proc(ap->a_context);
struct vnode_attr *vap;
struct vnode_attr va;
* In the mean time, compensate here by checking
* the union_node's lock flag.
*/
- if (un->un_flags & UN_LOCKED)
- FIXUP(un, p);
error = vnode_getattr(vp, vap, ap->a_context);
- if (error)
+ if (error) {
return (error);
+ }
+ union_lock();
union_newsize(ap->a_vp, vap->va_data_size, VNOVAL);
+ union_unlock();
}
if (vp == NULLVP) {
if (vp != NULLVP) {
error = vnode_getattr(vp, vap, ap->a_context);
- if (error)
+ if (error) {
return (error);
+ }
+ union_lock();
union_newsize(ap->a_vp, VNOVAL, vap->va_data_size);
+ union_unlock();
}
if ((vap != ap->a_vap) && (vap->va_type == VDIR))
return (0);
}
-int
-union_setattr(ap)
- struct vnop_setattr_args /* {
+static int
+union_setattr(struct vnop_setattr_args *ap)
+/*
+ struct vnop_setattr_args {
struct vnode *a_vp;
struct vnode_attr *a_vap;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_vp);
- struct proc *p = vfs_context_proc(ap->a_context);
- kauth_cred_t cred = vfs_context_ucred(ap->a_context);
int error;
/*
(un->un_uppervp == NULLVP) &&
/* assert(un->un_lowervp != NULLVP) */
(un->un_lowervp->v_type == VREG)) {
- error = union_copyup(un, (ap->a_vap->va_data_size != 0), cred, p);
- if (error)
+ union_lock();
+ error = union_copyup(un, (ap->a_vap->va_data_size != 0), ap->a_context);
+ union_unlock();
+ if (error) {
return (error);
+ }
}
/*
* otherwise return read-only filesystem error.
*/
if (un->un_uppervp != NULLVP) {
- FIXUP(un, p);
error = vnode_setattr(un->un_uppervp, ap->a_vap, ap->a_context);
- if ((error == 0) && VATTR_IS_ACTIVE(ap->a_vap, va_data_size))
+ if ((error == 0) && VATTR_IS_ACTIVE(ap->a_vap, va_data_size)) {
+ union_lock();
union_newsize(ap->a_vp, ap->a_vap->va_data_size, VNOVAL);
+ union_unlock();
+ }
} else {
error = EROFS;
}
return (error);
}
-int
-union_read(ap)
- struct vnop_read_args /* {
+static int
+union_read(struct vnop_read_args *ap)
+/*
+ struct vnop_read_args {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
- struct proc *p = vfs_context_proc(ap->a_context);
struct vnode *vp = OTHERVP(ap->a_vp);
- int dolock = (vp == LOWERVP(ap->a_vp));
- if (!dolock)
- FIXUP(VTOUNION(ap->a_vp), p);
error = VNOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_context);
/*
off_t cur = ap->a_uio->uio_offset;
if (vp == un->un_uppervp) {
- if (cur > un->un_uppersz)
+ if (cur > un->un_uppersz) {
+ union_lock();
union_newsize(ap->a_vp, cur, VNOVAL);
+ union_unlock();
+ }
} else {
- if (cur > un->un_lowersz)
+ if (cur > un->un_lowersz) {
+ union_lock();
union_newsize(ap->a_vp, VNOVAL, cur);
+ union_unlock();
+ }
}
}
return (error);
}
-int
-union_write(ap)
- struct vnop_read_args /* {
+static int
+union_write(struct vnop_write_args *ap)
+/*
+ struct vnop_write_args {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *vp;
struct union_node *un = VTOUNION(ap->a_vp);
- struct proc *p = vfs_context_proc(ap->a_context);
vp = UPPERVP(ap->a_vp);
if (vp == NULLVP)
panic("union: missing upper layer in write");
- FIXUP(un, p);
error = VNOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_context);
/*
if (error == 0) {
off_t cur = ap->a_uio->uio_offset;
- if (cur > un->un_uppersz)
+ if (cur > un->un_uppersz) {
+ union_lock();
union_newsize(ap->a_vp, cur, VNOVAL);
+ union_unlock();
+ }
}
return (error);
}
-int
-union_ioctl(ap)
- struct vnop_ioctl_args /* {
+static int
+union_ioctl(struct vnop_ioctl_args *ap)
+/*
+ struct vnop_ioctl_args {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
register struct vnode *ovp = OTHERVP(ap->a_vp);
return (VCALL(ovp, VOFFSET(vnop_ioctl), ap));
}
-int
-union_select(ap)
- struct vnop_select_args /* {
+static int
+union_select(struct vnop_select_args *ap)
+/*
+ struct vnop_select_args {
struct vnode *a_vp;
int a_which;
int a_fflags;
void * a_wql;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
register struct vnode *ovp = OTHERVP(ap->a_vp);
return (VCALL(ovp, VOFFSET(vnop_select), ap));
}
-int
-union_revoke(ap)
- struct vnop_revoke_args /* {
+static int
+union_revoke(struct vnop_revoke_args *ap)
+/*
+ struct vnop_revoke_args {
struct vnode *a_vp;
int a_flags;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
struct vnode *vp = ap->a_vp;
if (LOWERVP(vp))
VNOP_REVOKE(LOWERVP(vp), ap->a_flags, ap->a_context);
vnode_reclaim(vp);
+
+ return (0);
}
-int
-union_mmap(ap)
- struct vnop_mmap_args /* {
+static int
+union_mmap(struct vnop_mmap_args *ap)
+/*
+ struct vnop_mmap_args {
struct vnode *a_vp;
int a_fflags;
kauth_cred_t a_cred;
struct proc *a_p;
- } */ *ap;
+ } *ap;
+*/
{
register struct vnode *ovp = OTHERVP(ap->a_vp);
return (VCALL(ovp, VOFFSET(vnop_mmap), ap));
}
-int
-union_fsync(
- struct vnop_fsync_args /* {
+static int
+union_mnomap(struct vnop_mnomap_args *ap)
+/*
+ struct vnop_mnomap_args {
+ struct vnode *a_vp;
+ int a_fflags;
+ kauth_cred_t a_cred;
+ struct proc *a_p;
+ } *ap;
+*/
+{
+ register struct vnode *ovp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = ovp;
+ return (VCALL(ovp, VOFFSET(vnop_mnomap), ap));
+}
+
+static int
+union_fsync(struct vnop_fsync_args *ap)
+/*
+ struct vnop_fsync_args {
struct vnode *a_vp;
int a_waitfor;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
int error = 0;
- struct proc *p = vfs_context_proc(ap->a_context);
struct vnode *targetvp = OTHERVP(ap->a_vp);
if (targetvp != NULLVP) {
- int dolock = (targetvp == LOWERVP(ap->a_vp));
- if (!dolock)
- FIXUP(VTOUNION(ap->a_vp), p);
error = VNOP_FSYNC(targetvp, ap->a_waitfor, ap->a_context);
}
return (error);
}
-int
-union_remove(
- struct vnop_remove_args /* {
+static int
+union_remove(struct vnop_remove_args *ap)
+/*
+ struct vnop_remove_args {
struct vnode *a_dvp;
struct vnode *a_vp;
struct componentname *a_cnp;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
- int error;
+ int error, flags;
struct union_node *dun = VTOUNION(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_vp);
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
+ int busydel = 0;
if (dun->un_uppervp == NULLVP)
panic("union remove: null upper vnode");
+ if (UNNODE_FAULTIN(dun) && ((ap->a_vp != NULLVP) &&
+ ((vnode_isreg(ap->a_vp) != 0) || (vnode_islnk(ap->a_vp) != 0)))) {
+ return(VNOP_REMOVE(dun->un_uppervp, ap->a_vp, ap->a_cnp, ap->a_flags, ap->a_context));
+ }
+
if (un->un_uppervp != NULLVP) {
struct vnode *dvp = dun->un_uppervp;
struct vnode *vp = un->un_uppervp;
- FIXUP(dun, p);
- dun->un_flags |= UN_KLOCK;
- FIXUP(un, p);
- un->un_flags |= UN_KLOCK;
-
+ flags = ap->a_flags;
+ if (vnode_isinuse(ap->a_vp, 0))
+ busydel = 1;
+ if ((flags & VNODE_REMOVE_NODELETEBUSY) && (busydel != 0)) {
+ return(EBUSY);
+ }
if (union_dowhiteout(un, cnp->cn_context))
cnp->cn_flags |= DOWHITEOUT;
+
+ if (busydel != 0) {
+ union_lock();
+ un->un_flags |= UN_DELETED;
+ if (un->un_flags & UN_CACHED) {
+ un->un_flags &= ~UN_CACHED;
+ LIST_REMOVE(un, un_cache);
+ }
+ union_unlock();
+ vnode_ref(vp);
+ }
error = VNOP_REMOVE(dvp, vp, cnp, 0, ap->a_context);
- if (!error)
- union_removed_upper(un);
+ if (!error) {
+ union_lock();
+ if (busydel == 0)
+ union_removed_upper(un);
+ union_unlock();
+ }
} else {
- FIXUP(dun, p);
+ if (UNNODE_FAULTIN(un))
+ panic("faultfs: No uppervp");
error = union_mkwhiteout(
MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
dun->un_uppervp, ap->a_cnp, un->un_path);
return (error);
}
-int
-union_link(
- struct vnop_link_args /* {
+static int
+union_link(struct vnop_link_args *ap)
+/*
+ struct vnop_link_args {
struct vnode *a_vp;
struct vnode *a_tdvp;
struct componentname *a_cnp;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
int error = 0;
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
struct union_node *un;
struct vnode *vp;
struct vnode *tdvp;
} else {
struct union_node *tun = VTOUNION(ap->a_vp);
if (tun->un_uppervp == NULLVP) {
+ if (UNNODE_FAULTIN(tun))
+ panic("faultfs: No uppervp");
if (un->un_uppervp == tun->un_dirvp) {
- un->un_flags &= ~UN_ULOCK;
}
- error = union_copyup(tun, 1, vfs_context_ucred(ctx), p);
- if (un->un_uppervp == tun->un_dirvp) {
- un->un_flags |= UN_ULOCK;
+ union_lock();
+ /* Would need to drain for above,below mount and faulin does not enter this path */
+ un->un_flags |= UN_LOCKED;
+ error = union_copyup(tun, 1, ap->a_context);
+ un->un_flags &= ~UN_LOCKED;
+ if ((un->un_flags & UN_WANT) == UN_WANT) {
+ un->un_flags &= ~UN_WANT;
+ wakeup(&un->un_flags);
}
+ union_unlock();
}
vp = tun->un_uppervp;
}
return (error);
}
- FIXUP(un, p);
- vnode_get(tdvp);
- un->un_flags |= UN_KLOCK;
- return (VNOP_LINK(vp, tdvp, cnp, ap->a_context));
+ error = (VNOP_LINK(vp, tdvp, cnp, ap->a_context));
+ return(error);
}
-int
-union_rename(ap)
- struct vnop_rename_args /* {
+static int
+union_rename(struct vnop_rename_args *ap)
+/*
+ struct vnop_rename_args {
struct vnode *a_fdvp;
struct vnode *a_fvp;
struct componentname *a_fcnp;
struct vnode *a_tvp;
struct componentname *a_tcnp;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *tdvp = ap->a_tdvp;
struct vnode *tvp = ap->a_tvp;
+
if (fdvp->v_op == union_vnodeop_p) { /* always true */
struct union_node *un = VTOUNION(fdvp);
if (un->un_uppervp == NULLVP) {
+ if (UNNODE_FAULTIN(un))
+ panic("faultfs rename: No uppervp");
/*
* this should never happen in normal
* operation but might if there was
}
fdvp = un->un_uppervp;
- vnode_get(fdvp);
}
if (fvp->v_op == union_vnodeop_p) { /* always true */
struct union_node *un = VTOUNION(fvp);
if (un->un_uppervp == NULLVP) {
+ if (UNNODE_FAULTIN(un))
+ panic("faultfs rename: No uppervp");
/* XXX: should do a copyup */
error = EXDEV;
goto bad;
ap->a_fcnp->cn_flags |= DOWHITEOUT;
fvp = un->un_uppervp;
- vnode_get(fvp);
}
if (tdvp->v_op == union_vnodeop_p) {
* a problem creating the top-level shadow
* directory.
*/
+ if (UNNODE_FAULTIN(un))
+ panic("faultfs rename: No uppervp");
error = EXDEV;
goto bad;
}
tdvp = un->un_uppervp;
- vnode_get(tdvp);
- un->un_flags |= UN_KLOCK;
}
if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
struct union_node *un = VTOUNION(tvp);
tvp = un->un_uppervp;
- if (tvp != NULLVP) {
- vnode_get(tvp);
- un->un_flags |= UN_KLOCK;
- }
}
return (VNOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp, ap->a_context));
return (error);
}
-int
-union_mkdir(
- struct vnop_mkdir_args /* {
+static int
+union_mkdir(struct vnop_mkdir_args *ap)
+/*
+ struct vnop_mkdir_args {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vnode_attr *a_vap;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
if (dvp != NULLVP) {
int error;
struct vnode *vp;
- FIXUP(un, p);
- un->un_flags |= UN_KLOCK;
/* note that this is a direct fallthrough to the filesystem */
error = VNOP_MKDIR(dvp, &vp, cnp, ap->a_vap, ap->a_context);
if (error)
return (error);
+ union_lock();
error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
NULLVP, cnp, vp, NULLVP, 1);
+ union_unlock();
if (error)
vnode_put(vp);
return (error);
return (EROFS);
}
-int
-union_rmdir(
- struct vnop_rmdir_args /* {
+static int
+union_rmdir(struct vnop_rmdir_args *ap)
+/*
+ struct vnop_rmdir_args {
struct vnode *a_dvp;
struct vnode *a_vp;
struct componentname *a_cnp;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
int error;
struct union_node *dun = VTOUNION(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_vp);
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
+ int busydel = 0;
+ /******* NODE HAS TO BE LOCKED ******/
if (dun->un_uppervp == NULLVP)
panic("union rmdir: null upper vnode");
struct vnode *dvp = dun->un_uppervp;
struct vnode *vp = un->un_uppervp;
- FIXUP(dun, p);
- vnode_get(dvp);
- dun->un_flags |= UN_KLOCK;
- FIXUP(un, p);
- vnode_get(vp);
- un->un_flags |= UN_KLOCK;
+ if (vnode_isinuse(ap->a_vp, 0)) {
+ busydel = 1;
+ union_lock();
+ un->un_flags |= UN_DELETED;
+ if (un->un_flags & UN_CACHED) {
+ un->un_flags &= ~UN_CACHED;
+ LIST_REMOVE(un, un_cache);
+ }
+ union_unlock();
+ vnode_ref(vp);
+ }
+
if (union_dowhiteout(un, cnp->cn_context))
cnp->cn_flags |= DOWHITEOUT;
error = VNOP_RMDIR(dvp, vp, ap->a_cnp, ap->a_context);
- if (!error)
- union_removed_upper(un);
+ if (!error) {
+ union_lock();
+ if (busydel == 0)
+ union_removed_upper(un);
+ union_unlock();
+ }
} else {
- FIXUP(dun, p);
+ if (UNNODE_FAULTIN(un))
+ panic("faultfs: No uppervp");
error = union_mkwhiteout(
MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
dun->un_uppervp, ap->a_cnp, un->un_path);
return (error);
}
-int
-union_symlink(
- struct vnop_symlink_args /* {
+static int
+union_symlink(struct vnop_symlink_args *ap)
+/*
+ struct vnop_symlink_args {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vnode_attr *a_vap;
char *a_target;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
struct componentname *cnp = ap->a_cnp;
- vfs_context_t ctx = cnp->cn_context;
- struct proc *p = vfs_context_proc(ctx);
if (dvp != NULLVP) {
int error;
struct vnode *vp;
- FIXUP(un, p);
- un->un_flags |= UN_KLOCK;
-
error = VNOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target, ap->a_context);
- *ap->a_vpp = NULLVP;
+ *ap->a_vpp = vp;
return (error);
}
return (EROFS);
* down the union stack. readdir(3) is responsible for
* eliminating duplicate names from the returned data stream.
*/
-int
-union_readdir(ap)
- struct vnop_readdir_args /* {
+static int
+union_readdir(struct vnop_readdir_args *ap)
+/*
+ struct vnop_readdir_args {
struct vnodeop_desc *a_desc;
struct vnode *a_vp;
struct uio *a_uio;
int *a_eofflag;
int *a_numdirent;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *uvp = un->un_uppervp;
- struct proc *p = vfs_context_proc(ap->a_context);
if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
return (EINVAL);
if (uvp == NULLVP)
return (0);
- FIXUP(un, p);
ap->a_vp = uvp;
return (VCALL(uvp, VOFFSET(vnop_readdir), ap));
}
-int
-union_readlink(ap)
- struct vnop_readlink_args /* {
+static int
+union_readlink(struct vnop_readlink_args *ap)
+/*
+ struct vnop_readlink_args {
struct vnode *a_vp;
struct uio *a_uio;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
- struct uio *uio = ap->a_uio;
- struct proc *p = vfs_context_proc(ap->a_context);
struct vnode *vp = OTHERVP(ap->a_vp);
- int dolock = (vp == LOWERVP(ap->a_vp));
- if (!dolock)
- FIXUP(VTOUNION(ap->a_vp), p);
ap->a_vp = vp;
error = VCALL(vp, VOFFSET(vnop_readlink), ap);
return (error);
}
-int
-union_inactive(
- struct vnop_inactive_args /* {
+static int
+union_inactive(struct vnop_inactive_args *ap)
+/*
+ struct vnop_inactive_args {
struct vnode *a_vp;
vfs_context_t a_context;
- } */ *ap)
+ } *ap;
+*/
{
struct vnode *vp = ap->a_vp;
struct union_node *un = VTOUNION(vp);
- struct vnode **vpp;
/*
* Do nothing (and _don't_ bypass).
* That's too much work for now.
*/
- if (un->un_dircache != 0) {
- for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
- vnode_put(*vpp);
- _FREE(un->un_dircache, M_TEMP);
- un->un_dircache = 0;
+ union_lock();
+ if (un->un_flags & UN_DELETED) {
+ if(un->un_uppervp != NULLVP) {
+ vnode_rele(un->un_uppervp);
+ }
+ union_removed_upper(un);
}
- if ((un->un_flags & UN_CACHED) == 0)
+ if (un->un_dircache != 0) {
+ union_dircache_free(un);
+ }
+ if (un->un_flags & UN_DIRENVN) {
vnode_recycle(vp);
+ }
+
+ union_unlock();
return (0);
}
-int
-union_reclaim(ap)
- struct vnop_reclaim_args /* {
+static int
+union_reclaim(struct vnop_reclaim_args *ap)
+/*
+ struct vnop_reclaim_args {
struct vnode *a_vp;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
+ union_lock();
union_freevp(ap->a_vp);
+ union_unlock();
return (0);
}
-int
-union_blockmap(ap)
- struct vnop_blockmap_args /* {
+static int
+union_blockmap(struct vnop_blockmap_args *ap)
+/*
+ struct vnop_blockmap_args {
struct vnode *a_vp;
off_t a_offset;
size_t a_size;
size_t *a_run;
void *a_poff;
int a_flags;
- } */ *ap;
+ } *ap;
+*/
{
int error;
- struct proc *p = current_proc(); /* XXX */
struct vnode *vp = OTHERVP(ap->a_vp);
- int dolock = (vp == LOWERVP(ap->a_vp));
- if (!dolock)
- FIXUP(VTOUNION(ap->a_vp), p);
ap->a_vp = vp;
error = VCALL(vp, VOFFSET(vnop_blockmap), ap);
return (error);
}
-int
-union_pathconf(ap)
- struct vnop_pathconf_args /* {
+static int
+union_pathconf(struct vnop_pathconf_args *ap)
+/*
+ struct vnop_pathconf_args {
struct vnode *a_vp;
int a_name;
int *a_retval;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
- struct proc *p = current_proc(); /* XXX */
struct vnode *vp = OTHERVP(ap->a_vp);
- int dolock = (vp == LOWERVP(ap->a_vp));
- if (!dolock)
- FIXUP(VTOUNION(ap->a_vp), p);
ap->a_vp = vp;
error = VCALL(vp, VOFFSET(vnop_pathconf), ap);
return (error);
}
-int
-union_advlock(ap)
- struct vnop_advlock_args /* {
+static int
+union_advlock(struct vnop_advlock_args *ap)
+/*
+ struct vnop_advlock_args {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
register struct vnode *ovp = OTHERVP(ap->a_vp);
* vnode in its arguments.
* This goes away with a merged VM/buffer cache.
*/
-int
-union_strategy(ap)
- struct vnop_strategy_args /* {
+static int
+union_strategy(struct vnop_strategy_args *ap)
+/*
+ struct vnop_strategy_args {
struct buf *a_bp;
- } */ *ap;
+ } *ap;
+*/
{
struct buf *bp = ap->a_bp;
int error;
}
/* Pagein */
-int
-union_pagein(ap)
- struct vnop_pagein_args /* {
+static int
+union_pagein(struct vnop_pagein_args *ap)
+/*
+ struct vnop_pagein_args {
struct vnode *a_vp,
upl_t a_pl,
vm_offset_t a_pl_offset,
size_t a_size,
int a_flags
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
if (vp == un->un_uppervp) {
- if (cur > un->un_uppersz)
+ if (cur > un->un_uppersz) {
+ union_lock();
union_newsize(ap->a_vp, cur, VNOVAL);
+ union_unlock();
+ }
} else {
- if (cur > un->un_lowersz)
+ if (cur > un->un_lowersz) {
+ union_lock();
union_newsize(ap->a_vp, VNOVAL, cur);
+ union_unlock();
+ }
}
}
}
/* Pageout */
-int
-union_pageout(ap)
- struct vnop_pageout_args /* {
+static int
+union_pageout(struct vnop_pageout_args *ap)
+/*
+ struct vnop_pageout_args {
struct vnode *a_vp,
upl_t a_pl,
vm_offset_t a_pl_offset,
size_t a_size,
int a_flags
vfs_context_t a_context;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *vp;
if (error == 0) {
off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset;
- if (cur > un->un_uppersz)
+ if (cur > un->un_uppersz) {
+ union_lock();
union_newsize(ap->a_vp, cur, VNOVAL);
+ union_unlock();
+ }
}
return (error);
}
/* Blktooff derives file offset for the given logical block number */
-int
-union_blktooff(ap)
- struct vnop_blktooff_args /* {
+static int
+union_blktooff(struct vnop_blktooff_args *ap)
+/*
+ struct vnop_blktooff_args {
struct vnode *a_vp;
daddr64_t a_lblkno;
off_t *a_offset;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
}
/* offtoblk derives file offset for the given logical block number */
-int
-union_offtoblk(ap)
- struct vnop_offtoblk_args /* {
+static int
+union_offtoblk(struct vnop_offtoblk_args *ap)
+/*
+ struct vnop_offtoblk_args {
struct vnode *a_vp;
off_t a_offset;
daddr64_t *a_lblkno;
- } */ *ap;
+ } *ap;
+*/
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
{ &vnop_select_desc, (VOPFUNC)union_select }, /* select */
{ &vnop_revoke_desc, (VOPFUNC)union_revoke }, /* revoke */
{ &vnop_mmap_desc, (VOPFUNC)union_mmap }, /* mmap */
+ { &vnop_mnomap_desc, (VOPFUNC)union_mnomap }, /* mnomap */
{ &vnop_fsync_desc, (VOPFUNC)union_fsync }, /* fsync */
{ &vnop_remove_desc, (VOPFUNC)union_remove }, /* remove */
{ &vnop_link_desc, (VOPFUNC)union_link }, /* link */