macos: fix race in afs_root
[openafs.git] / src / afs / DARWIN / osi_vnodeops.c
index 08565f7..1d8a163 100644 (file)
@@ -11,9 +11,7 @@
 #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>
@@ -68,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) *);
@@ -132,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 */
@@ -248,21 +237,13 @@ 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
         }
        if (vnode_ref(vp)) {
-#if 1
-           panic("vn_hold on terminating vnode");
-#else           
            vnode_put(vp);
            if (haveGlock) AFS_GLOCK(); 
            return;
-#endif
        }
        vnode_put(vp);
 #else
@@ -300,12 +281,19 @@ 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
     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) 
@@ -351,7 +339,7 @@ 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;
@@ -414,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;
@@ -436,7 +424,7 @@ afs_vop_create(ap)
 
     if (vcp) {
 #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;
@@ -492,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
@@ -506,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
@@ -515,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;
 }
 
@@ -534,6 +522,9 @@ 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);
@@ -564,6 +555,14 @@ afs_vop_access(ap)
     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)))
@@ -634,13 +633,17 @@ afs_vop_access(ap)
     /* we can't check for KAUTH_VNODE_TAKE_OWNERSHIP, so we always permit it */
     
     code = afs_AccessOK(tvc, bits, &treq, cmb);
-#if defined(AFS_DARWIN80_ENV)
-    /* In a dropbox, cp on 10.4 behaves badly, looping on EACCES */
-    /* In a dropbox, Finder may reopen the file. Let it. */
-    if (code == 0 && ((bits &~(PRSFS_READ|PRSFS_WRITE)) == 0)) {
+    /*
+     * 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);
-    }
-#endif
+    /* 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->f.m.Mode & 0100) != 0100) {
@@ -686,11 +689,54 @@ afs_vop_getattr(ap)
 {
     int code;
 
-    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
+    /* 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);
@@ -720,11 +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;
 }
@@ -753,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;
 }
@@ -770,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;
@@ -839,7 +898,7 @@ 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->f.states |= CMAPPED;
@@ -915,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;
@@ -1030,9 +1089,8 @@ 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
@@ -1043,7 +1101,6 @@ afs_vop_pageout(ap)
            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 */
@@ -1150,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)
@@ -1188,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) {
@@ -1253,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();
@@ -1305,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
@@ -1325,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.
@@ -1358,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;
@@ -1385,7 +1433,6 @@ afs_vop_rename(ap)
            return (ENOENT);
         }
         error=VOP_REMOVE(fdvp, fvp, fcnp);
-#endif
         
         if (fdvp == fvp)
             vrele(fdvp);
@@ -1394,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
@@ -1412,36 +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
-#ifdef notdef
-    if (error == EXDEV) {
-       /* The idea would be to have a userspace handler like afsdb to
-        * run mv as the user, thus:
-        */
-       printf("su %d -c /bin/mv /afs/.:mount/%d:%d:%d:%d/%s /afs/.:mount/%d:%d:%d:%d/%s\n",
-              afs_cr_uid(cn_cred(tcnp)), 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);
-    }
-#endif
-#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)
@@ -1452,6 +1471,86 @@ 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);
@@ -1467,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;
@@ -1492,7 +1591,7 @@ afs_vop_mkdir(ap)
     }
     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 */
@@ -1518,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) {
@@ -1555,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. */
 
@@ -1655,7 +1754,7 @@ 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)
@@ -1689,7 +1788,7 @@ afs_vop_reclaim(ap)
 {
     int error = 0;
     int sl, writelocked;
-    register struct vnode *vp = ap->a_vp;
+    struct vnode *vp = ap->a_vp;
     struct vcache *tvc = VTOAFS(vp);
 
     osi_Assert(!ISAFS_GLOCK());
@@ -1765,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;
@@ -1775,7 +1873,6 @@ afs_vop_pathconf(ap)
     case _PC_CASE_PRESERVING:
         *ap->a_retval = 1;
        break;
-#endif /* defined(AFS_DARWIN70_ENV) */
     default:
        return EINVAL;
     }
@@ -1866,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);
@@ -1900,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
@@ -1913,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
@@ -1964,8 +2061,8 @@ afs_vop_print(ap)
                                 * struct vnode *a_vp;
                                 * } */ *ap;
 {
-    register struct vnode *vp = ap->a_vp;
-    register struct vcache *vc = VTOAFS(ap->a_vp);
+    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->f.fid.Cell, vc->f.fid.Fid.Volume, vc->f.fid.Fid.Vnode,
@@ -1978,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");
     }
@@ -2028,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->f.states & CStatd) { 
-       par.vnfs_vtype = avc->f.m.Type;
-       par.vnfs_vops = afs_vnodeop_p;
-       par.vnfs_filesize = avc->f.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;
 
@@ -2062,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->f.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->f.states |= CDeadVnode;
-#endif
     }
     return error;
 #else
@@ -2090,67 +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;
-   vnode_t nvp;
-   int error;
-   struct vnode_fsparam par;
-   AFS_GLOCK();
-   ObtainWriteLock(&avc->lock,325);
-   ovp = AFSTOV(avc);
-   if (!(avc->f.states & CDeadVnode) && vnode_vtype(ovp) != VNON) {
-        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);
+           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);
+       vnode_rele(ovp);
        AFS_GLOCK();
-        ReleaseWriteLock(&avc->lock);
+       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 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);
-          }
-       }
-       avc->v = nvp;
-       avc->f.states &=~ CDeadVnode;
-   }
-   vnode_put(ovp);
-   vnode_rele(ovp);
-   AFS_GLOCK();
-   ReleaseWriteLock(&avc->lock);
-   if (!error)
-      afs_osi_Wakeup(&avc->f.states);
-   AFS_GUNLOCK();
-   return error;
+    return error;
 }
 #endif