macos: fix race in afs_root
[openafs.git] / src / afs / DARWIN / osi_vnodeops.c
index 258fbda..1d8a163 100644 (file)
@@ -4,8 +4,6 @@
 #include <afsconfig.h>
 #include <afs/param.h>
 
-RCSID
-    ("$Header$");
 
 #include <afs/sysincludes.h>   /* Standard vendor system headers */
 #include <afsincludes.h>       /* Afs-based standard headers */
@@ -13,9 +11,7 @@ RCSID
 #include <sys/malloc.h>
 #include <sys/namei.h>
 #include <sys/ubc.h>
-#if defined(AFS_DARWIN70_ENV)
 #include <vfs/vfs_support.h>
-#endif /* defined(AFS_DARWIN70_ENV) */
 #ifdef AFS_DARWIN80_ENV
 #include <sys/vnode_if.h>
 #include <sys/kauth.h>
@@ -70,9 +66,6 @@ int afs_vop_rmdir(struct VOPPROT(rmdir_args) *);
 int afs_vop_symlink(struct VOPPROT(symlink_args) *);
 int afs_vop_readdir(struct VOPPROT(readdir_args) *);
 int afs_vop_readlink(struct VOPPROT(readlink_args) *);
-#if !defined(AFS_DARWIN70_ENV)
-extern int ufs_abortop(struct vop_abortop_args *);
-#endif /* !defined(AFS_DARWIN70_ENV) */
 int afs_vop_inactive(struct VOPPROT(inactive_args) *);
 int afs_vop_reclaim(struct VOPPROT(reclaim_args) *);
 int afs_vop_strategy(struct VOPPROT(strategy_args) *);
@@ -134,13 +127,7 @@ struct vnodeopv_entry_desc afs_vnodeop_entries[] = {
     {VOPPREF(readdir_desc), (VOPFUNC)afs_vop_readdir}, /* readdir */
     {VOPPREF(readlink_desc), (VOPFUNC)afs_vop_readlink},       /* readlink */
 #ifndef AFS_DARWIN80_ENV
-#if defined(AFS_DARWIN70_ENV)
     {VOPPREF(abortop_desc), (VOPFUNC)nop_abortop },             /* abortop */
-#else /* ! defined(AFS_DARWIN70_ENV) */
-    /* Yes, we use the ufs_abortop call.  It just releases the namei
-     * buffer stuff */
-    {VOPPREF(abortop_desc), (VOPFUNC)ufs_abortop},     /* abortop */
-#endif /* defined(AFS_DARWIN70_ENV) */
 #endif
     {VOPPREF(inactive_desc), (VOPFUNC)afs_vop_inactive},       /* inactive */
     {VOPPREF(reclaim_desc), (VOPFUNC)afs_vop_reclaim}, /* reclaim */
@@ -237,12 +224,12 @@ darwin_vn_hold(struct vnode *vp)
     struct vcache *tvc = VTOAFS(vp);
 
 #ifndef AFS_DARWIN80_ENV
-    tvc->states |= CUBCinit;
+    tvc->f.states |= CUBCinit;
 #endif
 #ifdef AFS_DARWIN80_ENV
-    if (tvc->states & CDeadVnode)
+    osi_Assert((tvc->f.states & CVInit) == 0);
+    if (tvc->f.states & CDeadVnode)
        osi_Assert(!vnode_isinuse(vp, 1));
-     osi_Assert((tvc->states & CVInit) == 0);
 #endif
     if (haveGlock) AFS_GUNLOCK(); 
 
@@ -250,14 +237,14 @@ darwin_vn_hold(struct vnode *vp)
        if (vnode_get(vp)) {
            /* being terminated. kernel won't give us a ref. Now what? our
               callers don't expect us to fail */
-#if 1
-           panic("vn_hold on terminating vnode");
-#else           
            if (haveGlock) AFS_GLOCK(); 
            return;
-#endif
         }
-       vnode_ref(vp);
+       if (vnode_ref(vp)) {
+           vnode_put(vp);
+           if (haveGlock) AFS_GLOCK(); 
+           return;
+       }
        vnode_put(vp);
 #else
     /* vget needed for 0 ref'd vnode in GetVCache to not panic in vref.
@@ -279,7 +266,7 @@ darwin_vn_hold(struct vnode *vp)
 
     if (haveGlock) AFS_GLOCK(); 
 #ifndef AFS_DARWIN80_ENV
-    tvc->states &= ~CUBCinit;
+    tvc->f.states &= ~CUBCinit;
 #endif
 }
 int
@@ -294,16 +281,26 @@ afs_vop_lookup(ap)
     int error;
     struct vcache *vcp;
     struct vnode *vp, *dvp;
-    register int flags = ap->a_cnp->cn_flags;
+    int flags = ap->a_cnp->cn_flags;
     int lockparent;            /* 1 => lockparent flag is set */
     int wantparent;            /* 1 => wantparent or lockparent flag */
     struct proc *p;
 #ifdef AFS_DARWIN80_ENV
-    error = cache_lookup(ap->a_dvp, ap->a_vpp, ap->a_cnp);
-    if (error == -1) 
-       return 0;
-    if (error == ENOENT) 
-       return error;
+    vcp = VTOAFS(ap->a_dvp);
+    /*
+     * ._ file attribute mirroring touches this.
+     * we can't flag the vcache as there is none, so fail here.
+     * needed for fsevents support.
+     */
+    if (ap->a_context == afs_osi_ctxtp)
+       return ENOENT;
+    if (vcp->mvstat != 1) {
+       error = cache_lookup(ap->a_dvp, ap->a_vpp, ap->a_cnp);
+       if (error == -1) 
+           return 0;
+       if (error == ENOENT) 
+           return error;
+    }
 #endif
 
     GETNAME();
@@ -342,7 +339,8 @@ afs_vop_lookup(ap)
        return (error);
     }
 #ifdef AFS_DARWIN80_ENV
-    if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0))) {
+    if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0))) {
+       DROPNAME();
        *ap->a_vpp = 0;
        return error;
     }
@@ -404,7 +402,7 @@ afs_vop_create(ap)
 {
     int error = 0;
     struct vcache *vcp;
-    register struct vnode *dvp = ap->a_dvp;
+    struct vnode *dvp = ap->a_dvp;
     struct proc *p;
     GETNAME();
     p = vop_cn_proc;
@@ -426,9 +424,10 @@ afs_vop_create(ap)
 
     if (vcp) {
 #ifdef AFS_DARWIN80_ENV
-        if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0))) {
-             *ap->a_vpp=0;
-             return error;
+      if ((error=afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0))) {
+           DROPNAME();
+           *ap->a_vpp=0;
+           return error;
         }
 #endif
        *ap->a_vpp = AFSTOV(vcp);
@@ -436,9 +435,9 @@ afs_vop_create(ap)
        (*ap->a_vpp)->v_vfsp = dvp->v_vfsp;
        vn_lock(*ap->a_vpp, LK_EXCLUSIVE | LK_RETRY, p);
        if (UBCINFOMISSING(*ap->a_vpp) || UBCINFORECLAIMED(*ap->a_vpp)) {
-           vcp->states |= CUBCinit;
+           vcp->f.states |= CUBCinit;
            ubc_info_init(*ap->a_vpp);
-           vcp->states &= ~CUBCinit;
+           vcp->f.states &= ~CUBCinit;
        }
 #endif
     } else
@@ -481,7 +480,7 @@ afs_vop_open(ap)
     int error;
     struct vnode *vp = ap->a_vp;
     struct vcache *vc = VTOAFS(vp);
-#if defined(AFS_DARWIN14_ENV) && !defined(AFS_DARWIN80_ENV)
+#if !defined(AFS_DARWIN80_ENV)
     int didhold = 0;
     /*----------------------------------------------------------------
      * osi_VM_TryReclaim() removes the ubcinfo of a vnode, but that vnode
@@ -495,7 +494,7 @@ afs_vop_open(ap)
     if (vp->v_type == VREG && !(vp->v_flag & VSYSTEM)
       && vp->v_ubcinfo->ui_refcount < 2)
        didhold = ubc_hold(vp);
-#endif /* AFS_DARWIN14_ENV */
+#endif /* !AFS_DARWIN80_ENV */
     AFS_GLOCK();
     error = afs_open(&vc, ap->a_mode, vop_cred);
 #ifdef DIAGNOSTIC
@@ -504,10 +503,10 @@ afs_vop_open(ap)
 #endif
     osi_FlushPages(vc, vop_cred);
     AFS_GUNLOCK();
-#if defined(AFS_DARWIN14_ENV) && !defined(AFS_DARWIN80_ENV)
+#if !defined(AFS_DARWIN80_ENV)
     if (error && didhold)
        ubc_rele(vp);
-#endif /* AFS_DARWIN14_ENV */
+#endif /* !AFS_DARWIN80_ENV */
     return error;
 }
 
@@ -523,12 +522,17 @@ afs_vop_close(ap)
     int code;
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
+    /* allows faking FSE_CONTENT_MODIFIED */
+    if (afs_osi_ctxtp == ap->a_context)
+       return 0;
     AFS_GLOCK();
     if (vop_cred)
-       code = afs_close(avc, ap->a_fflag, vop_cred, vop_proc);
+       code = afs_close(avc, ap->a_fflag, vop_cred);
     else
-       code = afs_close(avc, ap->a_fflag, &afs_osi_cred, vop_proc);
+       code = afs_close(avc, ap->a_fflag, &afs_osi_cred);
     osi_FlushPages(avc, vop_cred);     /* hold bozon lock, but not basic vnode lock */
+    /* This is legit; it just forces the fstrace event to happen */
+    code = afs_CheckCode(code, NULL, 60);
     AFS_GUNLOCK();
 
     return code;
@@ -550,6 +554,15 @@ afs_vop_access(ap)
     struct afs_fakestat_state fakestate;
     struct vcache * tvc = VTOAFS(ap->a_vp);
     int bits=0;
+    int cmb = CHECK_MODE_BITS;
+#ifdef AFS_DARWIN80_ENV
+    /*
+     * needed for fsevents. ._ file attribute mirroring touches this.
+     * we can't flag the vcache, as there is none, so fail here.
+     */
+    if (ap->a_context == afs_osi_ctxtp)
+       return ENOENT;
+#endif
     AFS_GLOCK();
     afs_InitFakeStat(&fakestate);
     if ((code = afs_InitReq(&treq, vop_cred)))
@@ -566,7 +579,7 @@ afs_vop_access(ap)
         code = afs_CheckCode(code, &treq, 56);
         goto out;
     }
-    if (afs_fakestat_enable && tvc->mvstat && !(tvc->states & CStatd)) {
+    if (afs_fakestat_enable && tvc->mvstat && !(tvc->f.states & CStatd)) {
         code = 0;
         goto out;
     }
@@ -600,6 +613,12 @@ afs_vop_access(ap)
           bits |= PRSFS_LOOKUP;
        if (ap->a_action & KAUTH_VNODE_READ_SECURITY) /* mode bits/gid, not afs acl */
           bits |= PRSFS_LOOKUP;
+       if ((ap->a_action & ((1 << 25) - 1)) == KAUTH_VNODE_EXECUTE)
+          /* if only exec, don't check for read mode bit */
+          /* high bits of ap->a_action are not for 'generic rights bits', and
+             so should not be checked (KAUTH_VNODE_ACCESS is often present
+             and needs to be masked off) */
+         cmb |= CMB_ALLOW_EXEC_AS_READ;
     }
     if (ap->a_action & KAUTH_VNODE_WRITE_ATTRIBUTES)
        bits |= PRSFS_WRITE;
@@ -613,17 +632,27 @@ afs_vop_access(ap)
        bits |= PRSFS_WRITE;
     /* we can't check for KAUTH_VNODE_TAKE_OWNERSHIP, so we always permit it */
     
-    code = afs_AccessOK(tvc, bits, &treq, CHECK_MODE_BITS);
+    code = afs_AccessOK(tvc, bits, &treq, cmb);
+    /*
+     * Special cased dropbox handling:
+     * cp on 10.4 behaves badly, looping on EACCES
+     * Finder may reopen the file. Let it.
+     */
+    if (code == 0 && ((bits &~(PRSFS_READ|PRSFS_WRITE)) == 0))
+       code = afs_AccessOK(tvc, PRSFS_ADMINISTER|PRSFS_INSERT|bits, &treq, cmb);
+    /* Finder also treats dropboxes as insert+delete. fake it out. */
+    if (code == 0 && (bits == (PRSFS_INSERT|PRSFS_DELETE)))
+       code = afs_AccessOK(tvc, PRSFS_INSERT, &treq, cmb);
 
     if (code == 1 && vnode_vtype(ap->a_vp) == VREG &&
         ap->a_action & KAUTH_VNODE_EXECUTE &&
-        (tvc->m.Mode & 0100) != 0100) {
+        (tvc->f.m.Mode & 0100) != 0100) {
         code = 0;
-     }
+    }
     if (code) {
         code= 0;               /* if access is ok */
     } else {
-        code = afs_CheckCode(EACCES, &treq, 57);        /* failure code */
+           code = afs_CheckCode(EACCES, &treq, 57);        /* failure code */
     }
 out:
      afs_PutFakeStat(&fakestate);
@@ -660,14 +689,60 @@ afs_vop_getattr(ap)
 {
     int code;
 
-    AFS_GLOCK();
-    code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
-    AFS_GUNLOCK();
+#ifdef AFS_DARWIN80_ENV
+    /* CEvent excludes the fsevent. our context excludes the ._ */
+    if ((VTOAFS(ap->a_vp)->f.states & CEvent) ||
+       (ap->a_context == afs_osi_ctxtp)){
+       struct vcache *avc = VTOAFS(ap->a_vp);
+       int isglock = ISAFS_GLOCK();
+
+       /* this is needed because of how and when we re-enter */
+       if (!isglock)
+         AFS_GLOCK();
+       /* do minimal work to return fake result for fsevents */
+       if (afs_fakestat_enable && VTOAFS(ap->a_vp)->mvstat == 1) {
+           struct afs_fakestat_state fakestat;
+           struct vrequest treq;
+
+           code = afs_InitReq(&treq, vop_cred);
+           if (code) {
+               if (!isglock)
+                   AFS_GUNLOCK();
+               return code;
+           }
+           afs_InitFakeStat(&fakestat);
+           /* expects GLOCK */
+           code = afs_TryEvalFakeStat(&avc, &fakestat, &treq);
+           if (code) {
+               if (!isglock)
+                   AFS_GUNLOCK();
+               afs_PutFakeStat(&fakestat);
+               return code;
+           }
+       }
+       code = afs_CopyOutAttrs(avc, ap->a_vap);
+       if (!isglock)
+           AFS_GUNLOCK();
+       if (0 && !code) {
+           /* tweak things so finder will recheck */
+           (ap->a_vap)->va_gid = ((ap->a_vap)->va_gid == 1) ? 2 : 1;
+           (ap->a_vap)->va_mode &= ~(VSGID);
+       }
+    } else
+#endif
+    {
+       AFS_GLOCK();
+       code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
+       /* This is legit; it just forces the fstrace event to happen */
+       code = afs_CheckCode(code, NULL, 58);
+       AFS_GUNLOCK();
+    }
 #ifdef AFS_DARWIN80_ENV
     VATTR_SET_SUPPORTED(ap->a_vap, va_type);
     VATTR_SET_SUPPORTED(ap->a_vap, va_mode);
     VATTR_SET_SUPPORTED(ap->a_vap, va_uid);
     VATTR_SET_SUPPORTED(ap->a_vap, va_gid);
+    VATTR_SET_SUPPORTED(ap->a_vap, va_fsid);
     VATTR_SET_SUPPORTED(ap->a_vap, va_fileid);
     VATTR_SET_SUPPORTED(ap->a_vap, va_nlink);
     VATTR_SET_SUPPORTED(ap->a_vap, va_data_size);
@@ -691,9 +766,24 @@ afs_vop_setattr(ap)
                                 * struct proc *a_p;
                                 * } */ *ap;
 {
-    int code;
+    int code, pass = 0;
+    struct vcache *avc = VTOAFS(ap->a_vp);
+#ifdef AFS_DARWIN80_ENV
+    /* fsevents tries to set attributes. drop it. */
+    if (ap->a_context == afs_osi_ctxtp)
+       return 0;
+#endif
     AFS_GLOCK();
-    code = afs_setattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
+retry:
+    code = afs_setattr(avc, ap->a_vap, vop_cred);
+    /* This is legit; it just forces the fstrace event to happen */
+    code = afs_CheckCode(code, NULL, 59);
+    if (!pass && code == EINVAL && (VATTR_IS_ACTIVE(ap->a_vap, va_mode) &&
+                                   (vType(avc) == VLNK))) {
+       VATTR_CLEAR_ACTIVE(ap->a_vap, va_mode);
+       pass++;
+       goto retry;
+    }
     AFS_GUNLOCK();
     return code;
 }
@@ -710,8 +800,11 @@ afs_vop_read(ap)
     int code;
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
+
+    if (vnode_isdir(ap->a_vp)) 
+       return EISDIR;
 #ifdef AFS_DARWIN80_ENV
-    ubc_sync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_PUSHDIRTY);
+    ubc_msync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_PUSHDIRTY);
 #else
     if (UBCINFOEXISTS(ap->a_vp)) {
        ubc_clean(ap->a_vp, 0);
@@ -719,7 +812,7 @@ afs_vop_read(ap)
 #endif
     AFS_GLOCK();
     osi_FlushPages(avc, vop_cred);     /* hold bozon lock, but not basic vnode lock */
-    code = afs_read(avc, ap->a_uio, vop_cred, 0, 0, 0);
+    code = afs_read(avc, ap->a_uio, vop_cred, 0);
     AFS_GUNLOCK();
     return code;
 }
@@ -736,7 +829,7 @@ afs_vop_pagein(ap)
                                 * int a_flags;
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *vp = ap->a_vp;
     upl_t pl = ap->a_pl;
     size_t size = ap->a_size;
     off_t f_offset = ap->a_f_offset;
@@ -805,10 +898,10 @@ afs_vop_pagein(ap)
 #endif
     AFS_GLOCK();
     osi_FlushPages(tvc, vop_cred);     /* hold bozon lock, but not basic vnode lock */
-    code = afs_read(tvc, uio, cred, 0, 0, 0);
+    code = afs_read(tvc, uio, cred, 0);
     if (code == 0) {
        ObtainWriteLock(&tvc->lock, 2);
-       tvc->states |= CMAPPED;
+       tvc->f.states |= CMAPPED;
        ReleaseWriteLock(&tvc->lock);
     }
     AFS_GUNLOCK();
@@ -852,7 +945,7 @@ afs_vop_write(ap)
     struct vcache *avc = VTOAFS(ap->a_vp);
     void *object;
 #ifdef AFS_DARWIN80_ENV
-    ubc_sync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_INVALIDATE);
+    ubc_msync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_INVALIDATE);
 #else
     if (UBCINFOEXISTS(ap->a_vp)) {
        ubc_clean(ap->a_vp, 1);
@@ -881,7 +974,7 @@ afs_vop_pageout(ap)
                                 * int           a_flags
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *vp = ap->a_vp;
     upl_t pl = ap->a_pl;
     size_t size = ap->a_size;
     off_t f_offset = ap->a_f_offset;
@@ -958,7 +1051,7 @@ afs_vop_pageout(ap)
                                   UPL_ABORT_FREE_ON_EMPTY);
        return (EINVAL);
     }
-    if (f_offset >= tvc->m.Length) {
+    if (f_offset >= tvc->f.m.Length) {
        if (!nocommit)
            OSI_UPL_ABORT_RANGE(pl, pl_offset, size,
                                   UPL_ABORT_FREE_ON_EMPTY);
@@ -970,8 +1063,8 @@ afs_vop_pageout(ap)
 
     /* size will always be a multiple of PAGE_SIZE */
     /* pageout isn't supposed to extend files */
-    if (f_offset + size > tvc->m.Length) 
-        iosize = tvc->m.Length - f_offset;
+    if (f_offset + size > tvc->f.m.Length) 
+        iosize = tvc->f.m.Length - f_offset;
     else
         iosize = size;
 
@@ -996,20 +1089,18 @@ afs_vop_pageout(ap)
     auio.uio_resid = aiov.iov_len = iosize;
     aiov.iov_base = (caddr_t) ioaddr;
 #endif
-#if 1                          /* USV [ */
     {
-       /* 
+       /* USV?
         * check for partial page and clear the
         * contents past end of the file before
         * releasing it in the VM page cache
         */
-       if ((f_offset < tvc->m.Length) && (f_offset + size) > tvc->m.Length) {
-           size_t io = tvc->m.Length - f_offset;
+       if ((f_offset < tvc->f.m.Length) && (f_offset + size) > tvc->f.m.Length) {
+           size_t io = tvc->f.m.Length - f_offset;
 
            memset((caddr_t) (ioaddr + pl_offset + io), 0, size - io);
        }
     }
-#endif /* ] USV */
 
     AFS_GLOCK();
     osi_FlushPages(tvc, vop_cred);     /* hold bozon lock, but not basic vnode lock */
@@ -1061,8 +1152,7 @@ afs_vop_ioctl(ap)
     if (((ap->a_command >> 8) & 0xff) == 'V') {
        /* This is a VICEIOCTL call */
        AFS_GLOCK();
-       error = HandleIoctl(tvc, (struct file *)0 /*Not used */ ,
-                           ap->a_command, ap->a_data);
+       error = HandleIoctl(tvc, ap->a_command, ap->a_data);
        AFS_GUNLOCK();
        return (error);
     } else {
@@ -1117,9 +1207,13 @@ afs_vop_fsync(ap)
 {
     int wait = ap->a_waitfor == MNT_WAIT;
     int error;
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *vp = ap->a_vp;
     int haveGlock = ISAFS_GLOCK();
 
+    /* in order to recycle faked vnodes for bulkstat */
+    if (VTOAFS(vp) == NULL)
+       return ENOTSUP;
+
     /* afs_vop_lookup glocks, can call us through vinvalbuf from GetVCache */
     if (!haveGlock) AFS_GLOCK();
     if (vop_cred)
@@ -1155,8 +1249,8 @@ afs_vop_remove(ap)
                                 * } */ *ap;
 {
     int error = 0;
-    register struct vnode *vp = ap->a_vp;
-    register struct vnode *dvp = ap->a_dvp;
+    struct vnode *vp = ap->a_vp;
+    struct vnode *dvp = ap->a_dvp;
 
 #ifdef AFS_DARWIN80_ENV
     if (ap->a_flags & VNODE_REMOVE_NODELETEBUSY) {
@@ -1170,12 +1264,17 @@ afs_vop_remove(ap)
     GETNAME();
     AFS_GLOCK();
     error = afs_remove(VTOAFS(dvp), name, vop_cn_cred);
+    error = afs_CheckCode(error, NULL, 61);
     AFS_GUNLOCK();
     cache_purge(vp);
     if (!error) {
 #ifdef AFS_DARWIN80_ENV
-        ubc_setsize(vp, (off_t)0);
-        vnode_recycle(vp);
+       struct vcache *tvc = VTOAFS(vp);
+       
+       if (!(tvc->f.states & CUnlinked)) {
+            ubc_setsize(vp, (off_t)0);
+            vnode_recycle(vp);
+       }
 #else
         /* necessary so we don't deadlock ourselves in vclean */
         VOP_UNLOCK(vp, 0, cnp->cn_proc);
@@ -1183,6 +1282,12 @@ afs_vop_remove(ap)
        /* If crashes continue in ubc_hold, comment this out */
         (void)ubc_uncache(vp);
 #endif
+    } else {
+       /* should check for PRSFS_INSERT and not PRSFS_DELETE, but the
+          goal here is to deal with Finder's unhappiness with resource
+          forks that have no resources in a dropbox setting */
+       if (name[0] == '.' && name[1] == '_' && error == EACCES) 
+           error = 0;
     }
 
 #ifndef AFS_DARWIN80_ENV
@@ -1209,8 +1314,8 @@ afs_vop_link(ap)
                                 * } */ *ap;
 {
     int error = 0;
-    register struct vnode *dvp = ap->a_tdvp;
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *dvp = ap->a_tdvp;
+    struct vnode *vp = ap->a_vp;
     struct proc *p;
 
     GETNAME();
@@ -1261,17 +1366,20 @@ afs_vop_rename(ap)
     struct componentname *tcnp = ap->a_tcnp;
     char *tname;
     struct vnode *tvp = ap->a_tvp;
-    register struct vnode *tdvp = ap->a_tdvp;
+    struct vnode *tdvp = ap->a_tdvp;
     struct vnode *fvp = ap->a_fvp;
-    register struct vnode *fdvp = ap->a_fdvp;
+    struct vnode *fdvp = ap->a_fdvp;
     struct proc *p; 
 
     p = cn_proc(fcnp);
 
 #ifdef AFS_DARWIN80_ENV
-/* generic code tests for v_mount equality, so we don't have to, but we don't
-   get the multiple-mount "benefits" of the old behavior
-*/
+    /*
+     * generic code tests for v_mount equality, so we don't have to, but we
+     * don't get the multiple-mount "benefits" of the old behavior
+     * the generic code doesn't do this, so we really should, but all the
+     * vrele's are wrong...
+     */
 #else
     /* Check for cross-device rename.
      * For AFS, this means anything not in AFS-space
@@ -1281,12 +1389,7 @@ afs_vop_rename(ap)
        error = EXDEV;
        goto abortit;
     }
-#endif
 
-#ifdef AFS_DARWIN80_ENV
-   /* the generic code doesn't do this, so we really should, but all the
-      vrele's are wrong... */
-#else
     /*
      * if fvp == tvp, we're just removing one name of a pair of
      * directory entries for the same element.  convert call into rename.
@@ -1314,17 +1417,6 @@ afs_vop_rename(ap)
        vput(tdvp);
        vput(tvp);
        /* Delete source. */
-#if defined(AFS_DARWIN80_ENV) 
-
-        MALLOC(fname, char *, fcnp->cn_namelen + 1, M_TEMP, M_WAITOK);
-        memcpy(fname, fcnp->cn_nameptr, fcnp->cn_namelen);
-        fname[fcnp->cn_namelen] = '\0';
-        AFS_GLOCK();
-        error = afs_remove(VTOAFS(fdvp), fname, vop_cn_cred);
-        AFS_GUNLOCK();
-        FREE(fname, M_TEMP);
-        cache_purge(fvp);
-#else
        vrele(fdvp);
        vrele(fvp);
        fcnp->cn_flags &= ~MODMASK;
@@ -1341,7 +1433,6 @@ afs_vop_rename(ap)
            return (ENOENT);
         }
         error=VOP_REMOVE(fdvp, fvp, fcnp);
-#endif
         
         if (fdvp == fvp)
             vrele(fdvp);
@@ -1350,8 +1441,6 @@ afs_vop_rename(ap)
         vput(fvp);
         return (error);
     }
-#endif
-#if !defined(AFS_DARWIN80_ENV) 
     if (error = vn_lock(fvp, LK_EXCLUSIVE, p))
        goto abortit;
 #endif
@@ -1368,26 +1457,10 @@ afs_vop_rename(ap)
     /* XXX use "from" or "to" creds? NFS uses "to" creds */
     error =
        afs_rename(VTOAFS(fdvp), fname, VTOAFS(tdvp), tname, cn_cred(tcnp));
-    AFS_GUNLOCK();
 
 #if !defined(AFS_DARWIN80_ENV) 
+    AFS_GUNLOCK();
     VOP_UNLOCK(fvp, 0, p);
-#endif
-    FREE(fname, M_TEMP);
-    FREE(tname, M_TEMP);
-#ifdef AFS_DARWIN80_ENV
-    cache_purge(fdvp);
-    cache_purge(fvp);
-    cache_purge(tdvp);
-    if (tvp) {
-       cache_purge(tvp);
-       if (!error) {
-          vnode_recycle(tvp);
-       }
-    }
-    if (!error)
-       cache_enter(tdvp, fvp, tcnp);
-#else
     if (error)
        goto abortit;           /* XXX */
     if (tdvp == tvp)
@@ -1398,7 +1471,89 @@ afs_vop_rename(ap)
        vput(tvp);
     vrele(fdvp);
     vrele(fvp);
+#else
+    if (error == EXDEV) {
+        struct brequest *tb;
+        struct afs_uspc_param mvReq;
+        struct vcache *tvc;
+        struct vcache *fvc = VTOAFS(fdvp);
+        int code = 0;
+        struct afs_fakestat_state fakestate;
+        int fakestatdone = 0;
+
+       tvc = VTOAFS(tdvp);
+
+        /* unrewritten mount point? */
+        if (tvc->mvstat == 1) {
+            if (tvc->mvid && (tvc->f.states & CMValid)) {
+                struct vrequest treq;
+
+                afs_InitFakeStat(&fakestate);
+                code = afs_InitReq(&treq, vop_cred);
+                if (!code) {
+                    fakestatdone = 1;
+                    code = afs_EvalFakeStat(&tvc, &fakestate, &treq);
+                } else
+                    afs_PutFakeStat(&fakestate);
+            }
+        }
+
+        if (!code) {
+           /* at some point in the future we should allow other types */
+           mvReq.reqtype = AFS_USPC_UMV;
+            mvReq.req.umv.id = afs_cr_uid(cn_cred(tcnp));
+            mvReq.req.umv.idtype = IDTYPE_UID;
+            mvReq.req.umv.sCell = fvc->f.fid.Cell;
+            mvReq.req.umv.sVolume = fvc->f.fid.Fid.Volume;
+            mvReq.req.umv.sVnode = fvc->f.fid.Fid.Vnode;
+            mvReq.req.umv.sUnique = fvc->f.fid.Fid.Unique;
+            mvReq.req.umv.dCell = tvc->f.fid.Cell;
+            mvReq.req.umv.dVolume = tvc->f.fid.Fid.Volume;
+            mvReq.req.umv.dVnode = tvc->f.fid.Fid.Vnode;
+            mvReq.req.umv.dUnique = tvc->f.fid.Fid.Unique;
+
+           /*
+            * su %d -c mv /afs/.:mount/%d:%d:%d:%d/%s
+            * /afs/.:mount/%d:%d:%d:%d/%s where:
+            * mvReq.req.umv.id, fvc->f.fid.Cell, fvc->f.fid.Fid.Volume,
+            * fvc->f.fid.Fid.Vnode, fvc->f.fid.Fid.Unique, fname,
+            * tvc->f.fid.Cell, tvc->f.fid.Fid.Volume, tvc->f.fid.Fid.Vnode,
+            * tvc->f.fid.Fid.Unique, tname
+            */
+
+            tb = afs_BQueue(BOP_MOVE, NULL, 0, 1, cn_cred(tcnp),
+                            0L, 0L, &mvReq, fname, tname);
+           /* wait to collect result */
+            while ((tb->flags & BUVALID) == 0) {
+                tb->flags |= BUWAIT;
+                afs_osi_Sleep(tb);
+            }
+            /* if we succeeded, clear the error. otherwise, EXDEV */
+            if (mvReq.retval == 0)
+                error = 0;
+
+            afs_BRelease(tb);
+        }
+
+        if (fakestatdone)
+            afs_PutFakeStat(&fakestate);
+    }
+    AFS_GUNLOCK();
+
+    cache_purge(fdvp);
+    cache_purge(fvp);
+    cache_purge(tdvp);
+    if (tvp) {
+       cache_purge(tvp);
+       if (!error) {
+           vnode_recycle(tvp);
+       }
+    }
+    if (!error)
+       cache_enter(tdvp, fvp, tcnp);
 #endif
+    FREE(fname, M_TEMP);
+    FREE(tname, M_TEMP);
     return error;
 }
 
@@ -1411,8 +1566,8 @@ afs_vop_mkdir(ap)
                                 * struct vattr *a_vap;
                                 * } */ *ap;
 {
-    register struct vnode *dvp = ap->a_dvp;
-    register struct vattr *vap = ap->a_vap;
+    struct vnode *dvp = ap->a_dvp;
+    struct vattr *vap = ap->a_vap;
     int error = 0;
     struct vcache *vcp;
     struct proc *p;
@@ -1427,14 +1582,16 @@ afs_vop_mkdir(ap)
     error = afs_mkdir(VTOAFS(dvp), name, vap, &vcp, vop_cn_cred);
     AFS_GUNLOCK();
     if (error) {
+#ifndef AFS_DARWIN80_ENV
        VOP_ABORTOP(dvp, cnp);
        vput(dvp);
+#endif
        DROPNAME();
        return (error);
     }
     if (vcp) {
 #ifdef AFS_DARWIN80_ENV
-        afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0);
+       afs_darwin_finalizevnode(vcp, ap->a_dvp, ap->a_cnp, 0, 0);
 #endif
        *ap->a_vpp = AFSTOV(vcp);
 #ifndef AFS_DARWIN80_ENV /* XXX needed for multi-mount thing, but can't have it yet */
@@ -1460,8 +1617,8 @@ afs_vop_rmdir(ap)
                                 * } */ *ap;
 {
     int error = 0;
-    register struct vnode *vp = ap->a_vp;
-    register struct vnode *dvp = ap->a_dvp;
+    struct vnode *vp = ap->a_vp;
+    struct vnode *dvp = ap->a_dvp;
 
     GETNAME();
     if (dvp == vp) {
@@ -1497,7 +1654,7 @@ afs_vop_symlink(ap)
                                 * char *a_target;
                                 * } */ *ap;
 {
-    register struct vnode *dvp = ap->a_dvp;
+    struct vnode *dvp = ap->a_dvp;
     int error = 0;
     /* NFS ignores a_vpp; so do we. */
 
@@ -1597,16 +1754,25 @@ afs_vop_inactive(ap)
                                 * struct proc *a_p;
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *vp = ap->a_vp;
     struct vcache *tvc = VTOAFS(vp);
 #ifndef AFS_DARWIN80_ENV
     if (prtactive && vp->v_usecount != 0)
        vprint("afs_vop_inactive(): pushing active", vp);
 #endif
     if (tvc) {
-       AFS_GLOCK();
-       afs_InactiveVCache(tvc, 0);     /* decrs ref counts */
-       AFS_GUNLOCK();
+#ifdef AFS_DARWIN80_ENV
+        int unlinked = tvc->f.states & CUnlinked;
+#endif
+       AFS_GLOCK();
+       afs_InactiveVCache(tvc, 0);     /* decrs ref counts */
+       AFS_GUNLOCK();
+#ifdef AFS_DARWIN80_ENV
+       if (unlinked) {
+           vnode_recycle(vp);
+           cache_purge(vp);
+       }
+#endif
     }
 #ifndef AFS_DARWIN80_ENV
     VOP_UNLOCK(vp, 0, ap->a_p);
@@ -1621,33 +1787,48 @@ afs_vop_reclaim(ap)
                                 * } */ *ap;
 {
     int error = 0;
-    int sl;
-    register struct vnode *vp = ap->a_vp;
+    int sl, writelocked;
+    struct vnode *vp = ap->a_vp;
     struct vcache *tvc = VTOAFS(vp);
 
     osi_Assert(!ISAFS_GLOCK());
     cache_purge(vp);           /* just in case... */
     if (tvc) {
        AFS_GLOCK();
-       ObtainWriteLock(&afs_xvcache, 335);
-       error = afs_FlushVCache(tvc, &sl);      /* toss our stuff from vnode */
-       if (tvc->states & (CVInit
+       writelocked = (0 == NBObtainWriteLock(&afs_xvcache, 335));
+       if (!writelocked) {
+          ObtainWriteLock(&afs_xvreclaim, 176);
 #ifdef AFS_DARWIN80_ENV
-                         | CDeadVnode
-#endif
-                         )) {
-          tvc->states &= ~(CVInit
+          vnode_clearfsnode(AFSTOV(tvc));
+          vnode_removefsref(AFSTOV(tvc));
+#else
+          tvc->v->v_data = NULL;  /* remove from vnode */
+#endif
+          AFSTOV(tvc) = NULL;             /* also drop the ptr to vnode */
+          tvc->f.states |= CVInit; /* also CDeadVnode? */
+          tvc->nextfree = ReclaimedVCList;
+          ReclaimedVCList = tvc;
+          ReleaseWriteLock(&afs_xvreclaim);
+       } else {
+          error = afs_FlushVCache(tvc, &sl);   /* toss our stuff from vnode */
+          if (tvc->f.states & (CVInit
 #ifdef AFS_DARWIN80_ENV
-                          | CDeadVnode
+                             | CDeadVnode
 #endif
-                          );
-          afs_osi_Wakeup(&tvc->states);
+                  )) {
+              tvc->f.states &= ~(CVInit
+#ifdef AFS_DARWIN80_ENV
+                               | CDeadVnode
+#endif
+                  );
+              afs_osi_Wakeup(&tvc->f.states);
+          }
+          if (!error && vnode_fsnode(vp))
+              panic("afs_reclaim: vnode not cleaned");
+          if (!error && (tvc->v != NULL)) 
+              panic("afs_reclaim: vcache not cleaned");
+          ReleaseWriteLock(&afs_xvcache);
        }
-       if (!error && vnode_fsnode(vp))
-          panic("afs_reclaim: vnode not cleaned");
-       if (!error && (tvc->v != NULL)) 
-           panic("afs_reclaim: vcache not cleaned");
-       ReleaseWriteLock(&afs_xvcache);
        AFS_GUNLOCK();
     }
     return error;
@@ -1683,7 +1864,6 @@ afs_vop_pathconf(ap)
     case _PC_PIPE_BUF:
        return EINVAL;
        break;
-#if defined(AFS_DARWIN70_ENV)
     case _PC_NAME_CHARS_MAX:
         *ap->a_retval = NAME_MAX;
        break;
@@ -1693,7 +1873,6 @@ afs_vop_pathconf(ap)
     case _PC_CASE_PRESERVING:
         *ap->a_retval = 1;
        break;
-#endif /* defined(AFS_DARWIN70_ENV) */
     default:
        return EINVAL;
     }
@@ -1715,7 +1894,10 @@ afs_vop_advlock(ap)
 {
     int error;
     struct ucred *tcr;
+    int clid;
+    int op;
 #ifdef AFS_DARWIN80_ENV
+    proc_t p;
     tcr=vop_cred;
 #else
     struct proc *p = current_proc();
@@ -1725,11 +1907,26 @@ afs_vop_advlock(ap)
     pcred_unlock(p);
     tcr=&cr;
 #endif
+    if (ap->a_flags & F_POSIX) {
+#ifdef AFS_DARWIN80_ENV
+       p = (proc_t) ap->a_id;
+       clid = proc_pid(p);
+#else
+       p = (struct proc *) ap->a_id;
+       clid = p->p_pid;
+#endif
+    } else {
+       clid = (int)ap->a_id;
+    }
+    if (ap->a_op == F_UNLCK) {
+       op = F_SETLK;
+    } else if (ap->a_op == F_SETLK && ap->a_flags & F_WAIT) {
+       op = F_SETLKW;
+    } else {
+       op = ap->a_op;
+    }
     AFS_GLOCK();
-    error =
-       afs_lockctl(VTOAFS(ap->a_vp), ap->a_fl,
-                   ap->a_op == F_UNLCK ? F_SETLK : ap->a_op, tcr, 
-                   (int)ap->a_id);
+    error = afs_lockctl(VTOAFS(ap->a_vp), ap->a_fl, op, tcr, clid);
     AFS_GUNLOCK();
     return error;
 }
@@ -1766,8 +1963,8 @@ afs_vop_lock(ap)
                                 * struct vnode *a_vp;
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
-    register struct vcache *avc = VTOAFS(vp);
+    struct vnode *vp = ap->a_vp;
+    struct vcache *avc = VTOAFS(vp);
 
     if (vp->v_tag == VT_NON)
        return (ENOENT);
@@ -1800,8 +1997,8 @@ afs_vop_truncate(ap)
                                 * struct proc *a_p;
                                 * } */ *ap;
 {
-    printf("stray afs_vop_truncate\n");
-    return EOPNOTSUPP;
+    /* printf("stray afs_vop_truncate\n"); */
+    return ENOTSUP;
 }
 
 int
@@ -1813,8 +2010,8 @@ afs_vop_update(ap)
                                 * int a_waitfor;
                                 * } */ *ap;
 {
-    printf("stray afs_vop_update\n");
-    return EOPNOTSUPP;
+    /* printf("stray afs_vop_update\n"); */
+    return ENOTSUP;
 }
 
 int
@@ -1864,12 +2061,12 @@ afs_vop_print(ap)
                                 * struct vnode *a_vp;
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
-    register struct vcache *vc = VTOAFS(ap->a_vp);
-    int s = vc->states;
+    struct vnode *vp = ap->a_vp;
+    struct vcache *vc = VTOAFS(ap->a_vp);
+    int s = vc->f.states;
     printf("tag %d, fid: %ld.%x.%x.%x, opens %d, writers %d", vp->v_tag,
-          vc->fid.Cell, vc->fid.Fid.Volume, vc->fid.Fid.Vnode,
-          vc->fid.Fid.Unique, vc->opens, vc->execsOrWriters);
+          vc->f.fid.Cell, vc->f.fid.Fid.Volume, vc->f.fid.Fid.Vnode,
+          vc->f.fid.Fid.Unique, vc->opens, vc->execsOrWriters);
     printf("\n  states%s%s%s%s%s", (s & CStatd) ? " statd" : "",
           (s & CRO) ? " readonly" : "", (s & CDirty) ? " dirty" : "",
           (s & CMAPPED) ? " mapped" : "",
@@ -1878,13 +2075,9 @@ afs_vop_print(ap)
        printf("\n  UBC: ");
        if (UBCINFOEXISTS(vp)) {
            printf("exists, ");
-#ifdef AFS_DARWIN14_ENV
            printf("refs %d%s%s", vp->v_ubcinfo->ui_refcount,
                   ubc_issetflags(vp, UI_HASOBJREF) ? " HASOBJREF" : "",
                   ubc_issetflags(vp, UI_WASMAPPED) ? " WASMAPPED" : "");
-#else
-           printf("holdcnt %d", vp->v_ubcinfo->ui_holdcnt);
-#endif
        } else
            printf("does not exist");
     }
@@ -1928,32 +2121,9 @@ afs_darwin_getnewvnode(struct vcache *avc)
     struct vnode_fsparam par;
 
     memset(&par, 0, sizeof(struct vnode_fsparam));
-#if 0
-    AFS_GLOCK();
-    ObtainWriteLock(&avc->lock,342);
-    if (avc->states & CStatd) { 
-       par.vnfs_vtype = avc->m.Type;
-       par.vnfs_vops = afs_vnodeop_p;
-       par.vnfs_filesize = avc->m.Length;
-       if (!ac->cnp)
-           par.vnfs_flags = VNFS_NOCACHE;
-       dead = 0;
-    } else {
-       par.vnfs_vtype = VNON;
-       par.vnfs_vops = afs_dead_vnodeop_p;
-       par.vnfs_flags = VNFS_NOCACHE|VNFS_CANTCACHE;
-       dead = 1;
-    }
-    ReleaseWriteLock(&avc->lock);
-    AFS_GUNLOCK();
-    par.vnfs_dvp = ac->dvp;
-    par.vnfs_cnp = ac->cnp;
-    par.vnfs_markroot = ac->markroot;
-#else
     par.vnfs_vtype = VNON;
     par.vnfs_vops = afs_dead_vnodeop_p;
     par.vnfs_flags = VNFS_NOCACHE|VNFS_CANTCACHE;
-#endif
     par.vnfs_mp = afs_globalVFS;
     par.vnfs_fsnode = avc;
 
@@ -1962,20 +2132,8 @@ afs_darwin_getnewvnode(struct vcache *avc)
       vnode_addfsref(vp);
       vnode_ref(vp);
       avc->v = vp;
-#if 0
-      if (dead) {
-         vnode_recycle(vp); /* terminate as soon as iocount drops */
-         avc->states |= CDeadVnode;
-      } else if (!ac->markroot && !ac->cnp) {
-       /* the caller doesn't know anything about this vnode. if markroot
-          should have been set and wasn't, bad things may happen, so encourage
-          it to recycle */
-          vnode_recycle(vp);
-      }
-#else
       vnode_recycle(vp); /* terminate as soon as iocount drops */
-      avc->states |= CDeadVnode;
-#endif
+      avc->f.states |= CDeadVnode;
     }
     return error;
 #else
@@ -1990,54 +2148,105 @@ afs_darwin_getnewvnode(struct vcache *avc)
 #ifdef AFS_DARWIN80_ENV
 /* if this fails, then tvc has been unrefed and may have been freed. 
    Don't touch! */
-int 
-afs_darwin_finalizevnode(struct vcache *avc, struct vnode *dvp, struct componentname *cnp, int isroot) {
-   vnode_t ovp = AFSTOV(avc);
-   vnode_t nvp;
-   int error;
-   struct vnode_fsparam par;
-   AFS_GLOCK();
-   ObtainWriteLock(&avc->lock,325);
-   if (!(avc->states & CDeadVnode) && vnode_vtype(ovp) != VNON) {
-        ReleaseWriteLock(&avc->lock);
-        AFS_GUNLOCK();
+int
+afs_darwin_finalizevnode(struct vcache *avc, struct vnode *dvp,
+                        struct componentname *cnp, int isroot, int locked)
+{
+    vnode_t ovp;
+    vnode_t nvp;
+    int error;
+    struct vnode_fsparam par;
+
+    if (!locked) {
+       AFS_GLOCK();
+       ObtainWriteLock(&avc->lock,325);
+    }
+    ovp = AFSTOV(avc);
+
+    if (!(avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON) {
+       AFS_GUNLOCK();
 #if 0 /* unsupported */
         if (dvp && cnp)
-        vnode_update_identity(ovp, dvp, cnp->cn_nameptr, cnp->cn_namelen,
-                              cnp->cn_hash,
-                              VNODE_UPDATE_PARENT|VNODE_UPDATE_NAME);
-#endif
-        vnode_rele(ovp);
-        return 0;
-   }
-   if ((avc->states & CDeadVnode) && vnode_vtype(ovp) != VNON) 
-       panic("vcache %p should not be CDeadVnode", avc);
-   AFS_GUNLOCK();
-   memset(&par, 0, sizeof(struct vnode_fsparam));
-   par.vnfs_mp = afs_globalVFS;
-   par.vnfs_vtype = avc->m.Type;
-   par.vnfs_vops = afs_vnodeop_p;
-   par.vnfs_filesize = avc->m.Length;
-   par.vnfs_fsnode = avc;
-   par.vnfs_dvp = dvp;
-   par.vnfs_cnp = cnp;
-   if (isroot)
-       par.vnfs_markroot = 1;
-   error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &par, &nvp);
-   if (!error) {
-     vnode_addfsref(nvp);
-     avc->v = nvp;
-     avc->states &=~ CDeadVnode;
-     vnode_clearfsnode(ovp);
-     vnode_removefsref(ovp);
-   }
-   AFS_GLOCK();
-   ReleaseWriteLock(&avc->lock);
-   if (!error)
-      afs_osi_Wakeup(&avc->states);
-   AFS_GUNLOCK();
-   vnode_put(ovp);
-   vnode_rele(ovp);
-   return error;
+           vnode_update_identity(ovp, dvp, cnp->cn_nameptr, cnp->cn_namelen,
+                                 cnp->cn_hash,
+                                 VNODE_UPDATE_PARENT|VNODE_UPDATE_NAME);
+#endif
+       /* Can end up in reclaim... drop GLOCK */
+       vnode_rele(ovp);
+       AFS_GLOCK();
+       if (!locked) {
+           ReleaseWriteLock(&avc->lock);
+           AFS_GUNLOCK();
+       }
+       return 0;
+    }
+
+    if ((avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON)
+       panic("vcache %p should not be CDeadVnode", avc);
+    AFS_GUNLOCK();
+    memset(&par, 0, sizeof(struct vnode_fsparam));
+    par.vnfs_mp = afs_globalVFS;
+    par.vnfs_vtype = avc->f.m.Type;
+    par.vnfs_vops = afs_vnodeop_p;
+    par.vnfs_filesize = avc->f.m.Length;
+    par.vnfs_fsnode = avc;
+    par.vnfs_dvp = dvp;
+    if (cnp && (cnp->cn_flags & ISDOTDOT) == 0)
+       par.vnfs_cnp = cnp;
+    if (!dvp || !cnp || (cnp->cn_flags & MAKEENTRY) == 0)
+       par.vnfs_flags = VNFS_NOCACHE;
+    if (isroot)
+       par.vnfs_markroot = 1;
+    error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &par, &nvp);
+    if (!error) {
+       vnode_addfsref(nvp);
+       if ((avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON)
+           printf("vcache %p should not be CDeadVnode", avc);
+       if (avc->v == ovp) {
+           if (!(avc->f.states & CVInit)) {
+               vnode_clearfsnode(ovp);
+               vnode_removefsref(ovp);
+           }
+           /* we're discarding on a fixup. mark for recycle */
+           if (!(avc->f.states & CDeadVnode))
+               vnode_recycle(ovp);
+       }
+       avc->v = nvp;
+       avc->f.states &=~ CDeadVnode;
+       /* If we were carrying an extra ref for dirty, hold/push it. */
+       if (avc->f.ddirty_flags) {
+           vnode_get(nvp);
+           vnode_ref(nvp);
+       }
+       /* If we were carrying an extra ref for shadow, hold/push it. */
+       if (avc->f.shadow.vnode) {
+           vnode_get(nvp);
+           vnode_ref(nvp);
+       }
+    }
+    /* Drop any extra dirty ref on the old vnode */
+    if (avc->f.ddirty_flags) {
+       vnode_put(ovp);
+       vnode_rele(ovp);
+    }
+    /* Drop any extra shadow ref on the old vnode */
+    if (avc->f.shadow.vnode) {
+       vnode_put(ovp);
+       vnode_rele(ovp);
+    }
+    /* If it's ref'd still, unmark stat'd to force new lookup */
+    if ((vnode_vtype(ovp) != avc->f.m.Type) && VREFCOUNT_GT(avc, 1))
+       avc->f.states &= ~CStatd;
+
+    vnode_put(ovp);
+    vnode_rele(ovp);
+    AFS_GLOCK();
+    if (!locked)
+       ReleaseWriteLock(&avc->lock);
+    if (!error)
+       afs_osi_Wakeup(&avc->f.states);
+    if (!locked)
+       AFS_GUNLOCK();
+    return error;
 }
 #endif