afs: Create afs_SetDataVersion
[openafs.git] / src / afs / VNOPS / afs_vnop_create.c
index ad0f0e0..30eaa7b 100644 (file)
@@ -16,8 +16,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 */
@@ -25,43 +23,30 @@ RCSID
 #include "afs/afs_cbqueue.h"
 #include "afs/nfsclient.h"
 #include "afs/afs_osidnlc.h"
+#include "afs/unified_afs.h"
 
 /* question: does afs_create need to set CDirty in the adp or the avc?
  * I think we can get away without it, but I'm not sure.  Note that
  * afs_setattr is called in here for truncation.
  */
-#ifdef AFS_OSF_ENV
-int
-afs_create(struct nameidata *ndp, struct vattr *attrs)
-#else /* AFS_OSF_ENV */
 #ifdef AFS_SGI64_ENV
 int
 afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs, int flags,
-          int amode, struct vcache **avcp, struct AFS_UCRED *acred)
+          int amode, struct vcache **avcp, afs_ucred_t *acred)
 #else /* AFS_SGI64_ENV */
 int
 afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
           enum vcexcl aexcl, int amode, struct vcache **avcp,
-          struct AFS_UCRED *acred)
+          afs_ucred_t *acred)
 #endif                         /* AFS_SGI64_ENV */
-#endif                         /* AFS_OSF_ENV */
 {
-#ifdef AFS_OSF_ENV
-    register struct vcache *adp = VTOAFS(ndp->ni_dvp);
-    char *aname = ndp->ni_dent.d_name;
-    enum vcexcl aexcl = NONEXCL;       /* XXX - create called properly */
-    int amode = 0;             /* XXX - checked in higher level */
-    struct vcache **avcp = (struct vcache **)&(ndp->ni_vp);
-    struct ucred *acred = ndp->ni_cred;
-#endif
-
     afs_int32 origCBs, origZaps, finalZaps;
-    struct vrequest treq;
-    register afs_int32 code;
-    register struct conn *tc;
+    struct vrequest *treq = NULL;
+    afs_int32 code;
+    struct afs_conn *tc;
     struct VenusFid newFid;
     struct AFSStoreStatus InStatus;
-    struct AFSFetchStatus OutFidStatus, OutDirStatus;
+    struct AFSFetchStatus *OutFidStatus, *OutDirStatus;
     struct AFSVolSync tsync;
     struct AFSCallBack CallBack;
     afs_int32 now;
@@ -71,12 +56,17 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
     struct vcache *tvc;
     struct volume *volp = 0;
     struct afs_fakestat_state fakestate;
+    struct rx_connection *rxconn;
     XSTATS_DECLS;
     OSI_VC_CONVERT(adp);
 
-
     AFS_STATCNT(afs_create);
-    if ((code = afs_InitReq(&treq, acred)))
+
+    OutFidStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
+    OutDirStatus = osi_AllocSmallSpace(sizeof(struct AFSFetchStatus));
+    memset(&InStatus, 0, sizeof(InStatus));
+
+    if ((code = afs_CreateReq(&treq, acred)))
        goto done2;
 
     afs_Trace3(afs_iclSetp, CM_TRACE_CREATE, ICL_TYPE_POINTER, adp,
@@ -97,12 +87,12 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
 
     if (strlen(aname) > AFSNAMEMAX) {
        code = ENAMETOOLONG;
-       goto done;
+       goto done3;
     }
 
     if (!afs_ENameOK(aname)) {
        code = EINVAL;
-       goto done;
+       goto done3;
     }
     switch (attrs->va_type) {
     case VBLK:
@@ -113,27 +103,34 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
     case VFIFO:
        /* We don't support special devices or FIFOs */
        code = EINVAL;
-       goto done;
+       goto done3;
     default:
        ;
     }
-    code = afs_EvalFakeStat(&adp, &fakestate, &treq);
+    AFS_DISCON_LOCK();
+
+    code = afs_EvalFakeStat(&adp, &fakestate, treq);
     if (code)
        goto done;
   tagain:
-    code = afs_VerifyVCache(adp, &treq);
+    code = afs_VerifyVCache(adp, treq);
     if (code)
        goto done;
 
     /** If the volume is read-only, return error without making an RPC to the
       * fileserver
       */
-    if (adp->states & CRO) {
+    if (adp->f.states & CRO) {
        code = EROFS;
        goto done;
     }
 
-    tdc = afs_GetDCache(adp, (afs_size_t) 0, &treq, &offset, &len, 1);
+    if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) {
+        code = ENETDOWN;
+        goto done;
+    }
+
+    tdc = afs_GetDCache(adp, (afs_size_t) 0, treq, &offset, &len, 1);
     ObtainWriteLock(&adp->lock, 135);
     if (tdc)
        ObtainSharedLock(&tdc->lock, 630);
@@ -142,8 +139,8 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
      * Make sure that the data in the cache is current. We may have
      * received a callback while we were waiting for the write lock.
      */
-    if (!(adp->states & CStatd)
-       || (tdc && !hsame(adp->m.DataVersion, tdc->f.versionNo))) {
+    if (!(adp->f.states & CStatd)
+       || (tdc && !hsame(adp->f.m.DataVersion, tdc->f.versionNo))) {
        ReleaseWriteLock(&adp->lock);
        if (tdc) {
            ReleaseSharedLock(&tdc->lock);
@@ -154,7 +151,7 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
     if (tdc) {
        /* see if file already exists.  If it does, we only set 
         * the size attributes (to handle O_TRUNC) */
-       code = afs_dir_Lookup(&tdc->f.inode, aname, &newFid.Fid);       /* use dnlc first xxx */
+       code = afs_dir_Lookup(tdc, aname, &newFid.Fid); /* use dnlc first xxx */
        if (code == 0) {
            ReleaseSharedLock(&tdc->lock);
            afs_PutDCache(tdc);
@@ -168,14 +165,14 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                goto done;
            }
            /* found the file, so use it */
-           newFid.Cell = adp->fid.Cell;
-           newFid.Fid.Volume = adp->fid.Fid.Volume;
+           newFid.Cell = adp->f.fid.Cell;
+           newFid.Fid.Volume = adp->f.fid.Fid.Volume;
            tvc = NULL;
            if (newFid.Fid.Unique == 0) {
-               tvc = afs_LookupVCache(&newFid, &treq, NULL, adp, aname);
+               tvc = afs_LookupVCache(&newFid, treq, NULL, adp, aname);
            }
            if (!tvc)           /* lookup failed or wasn't called */
-               tvc = afs_GetVCache(&newFid, &treq, NULL, NULL);
+               tvc = afs_GetVCache(&newFid, treq, NULL, NULL);
 
            if (tvc) {
                /* if the thing exists, we need the right access to open it.
@@ -188,29 +185,33 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                 * has mode -w-w-w, which is wrong.
                 */
                if ((amode & VREAD)
-                   && !afs_AccessOK(tvc, PRSFS_READ, &treq, CHECK_MODE_BITS)) {
+                   && !afs_AccessOK(tvc, PRSFS_READ, treq, CHECK_MODE_BITS)) {
                    afs_PutVCache(tvc);
                    code = EACCES;
                    goto done;
                }
-#if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+#if defined(AFS_DARWIN80_ENV)
+               if ((amode & VWRITE) || VATTR_IS_ACTIVE(attrs, va_data_size))
+#elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
                if ((amode & VWRITE) || (attrs->va_mask & AT_SIZE))
 #else
                if ((amode & VWRITE) || len != 0xffffffff)
 #endif
                {
                    /* needed for write access check */
-                   tvc->parentVnode = adp->fid.Fid.Vnode;
-                   tvc->parentUnique = adp->fid.Fid.Unique;
+                   tvc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+                   tvc->f.parent.unique = adp->f.fid.Fid.Unique;
                    /* need write mode for these guys */
                    if (!afs_AccessOK
-                       (tvc, PRSFS_WRITE, &treq, CHECK_MODE_BITS)) {
+                       (tvc, PRSFS_WRITE, treq, CHECK_MODE_BITS)) {
                        afs_PutVCache(tvc);
                        code = EACCES;
                        goto done;
                    }
                }
-#if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+#if defined(AFS_DARWIN80_ENV)
+               if (VATTR_IS_ACTIVE(attrs, va_data_size))
+#elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
                if (attrs->va_mask & AT_SIZE)
 #else
                if (len != 0xffffffff)
@@ -222,14 +223,20 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                        goto done;
                    }
                    /* do a truncate */
-#if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+#if defined(AFS_DARWIN80_ENV)
+                   VATTR_INIT(attrs);
+                   VATTR_SET_SUPPORTED(attrs, va_data_size);
+                   VATTR_SET_ACTIVE(attrs, va_data_size);
+#elif defined(UKERNEL)
+                   attrs->va_mask = ATTR_SIZE;
+#elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
                    attrs->va_mask = AT_SIZE;
 #else
                    VATTR_NULL(attrs);
 #endif
                    attrs->va_size = len;
                    ObtainWriteLock(&tvc->lock, 136);
-                   tvc->states |= CCreating;
+                   tvc->f.states |= CCreating;
                    ReleaseWriteLock(&tvc->lock);
 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
 #if defined(AFS_SGI64_ENV)
@@ -243,7 +250,7 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                    code = afs_setattr(tvc, attrs, acred);
 #endif /* SUN5 || SGI */
                    ObtainWriteLock(&tvc->lock, 137);
-                   tvc->states &= ~CCreating;
+                   tvc->f.states &= ~CCreating;
                    ReleaseWriteLock(&tvc->lock);
                    if (code) {
                        afs_PutVCache(tvc);
@@ -251,16 +258,20 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                    }
                }
                *avcp = tvc;
-           } else
-               code = ENOENT;  /* shouldn't get here */
+
+           } else {
+               /* Directory entry already exists, but we cannot fetch the
+                * fid it points to. */
+               code = EIO;
+           }
            /* make sure vrefCount bumped only if code == 0 */
            goto done;
        }
     }
-
+    
     /* if we create the file, we don't do any access checks, since
      * that's how O_CREAT is supposed to work */
-    if (adp->states & CForeign) {
+    if (adp->f.states & CForeign) {
        origCBs = afs_allCBs;
        origZaps = afs_allZaps;
     } else {
@@ -269,7 +280,7 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
     }
     InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
     InStatus.ClientModTime = osi_Time();
-    InStatus.Group = (afs_int32) acred->cr_gid;
+    InStatus.Group = (afs_int32) afs_cr_gid(acred);
     if (AFS_NFSXLATORREQ(acred)) {
        /*
         * XXX The following is mainly used to fix a bug in the HP-UX
@@ -289,97 +300,101 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
                attrs->va_mode = 0x1b6; /* XXX default mode: rw-rw-rw XXX */
        }
     }
-    InStatus.UnixModeBits = attrs->va_mode & 0xffff;   /* only care about protection bits */
-    do {
-       tc = afs_Conn(&adp->fid, &treq, SHARED_LOCK);
-       if (tc) {
-           hostp = tc->srvr->server;   /* remember for callback processing */
-           now = osi_Time();
-           XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE);
-           RX_AFS_GUNLOCK();
-           code =
-               RXAFS_CreateFile(tc->id, (struct AFSFid *)&adp->fid.Fid,
+
+    if (!AFS_IS_DISCONNECTED) {
+       /* If not disconnected, connect to the server.*/
+
+       InStatus.UnixModeBits = attrs->va_mode & 0xffff;        /* only care about protection bits */
+       do {
+           tc = afs_Conn(&adp->f.fid, treq, SHARED_LOCK, &rxconn);
+           if (tc) {
+               hostp = tc->parent->srvr->server; /* remember for callback processing */
+               now = osi_Time();
+               XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_CREATEFILE);
+               RX_AFS_GUNLOCK();
+               code =
+                   RXAFS_CreateFile(rxconn, (struct AFSFid *)&adp->f.fid.Fid,
                                 aname, &InStatus, (struct AFSFid *)
-                                &newFid.Fid, &OutFidStatus, &OutDirStatus,
+                                &newFid.Fid, OutFidStatus, OutDirStatus,
                                 &CallBack, &tsync);
-           RX_AFS_GLOCK();
-           XSTATS_END_TIME;
-           CallBack.ExpirationTime += now;
-       } else
-           code = -1;
-    } while (afs_Analyze
-            (tc, code, &adp->fid, &treq, AFS_STATS_FS_RPCIDX_CREATEFILE,
-             SHARED_LOCK, NULL));
-
-#if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV)
-    if (code == EEXIST && aexcl == NONEXCL) {
-       /* This lookup was handled in the common vn_open code in the
-        * vnode layer */
-       if (tdc) {
-           ReleaseSharedLock(&tdc->lock);
-           afs_PutDCache(tdc);
-       }
-       ReleaseWriteLock(&adp->lock);
-       goto done;
-    }
-#else /* AFS_OSF_ENV */
+               RX_AFS_GLOCK();
+               XSTATS_END_TIME;
+               CallBack.ExpirationTime += now;
+           } else
+               code = -1;
+       } while (afs_Analyze
+                (tc, rxconn, code, &adp->f.fid, treq, AFS_STATS_FS_RPCIDX_CREATEFILE,
+                 SHARED_LOCK, NULL));
+
+       if ((code == EEXIST || code == UAEEXIST) &&
 #ifdef AFS_SGI64_ENV
-    if (code == EEXIST && !(flags & VEXCL)) {
+       !(flags & VEXCL)
 #else /* AFS_SGI64_ENV */
-    if (code == EEXIST && aexcl == NONEXCL) {
-#endif /* AFS_SGI64_ENV */
-       /* if we get an EEXIST in nonexcl mode, just do a lookup */
-       if (tdc) {
-           ReleaseSharedLock(&tdc->lock);
-           afs_PutDCache(tdc);
-       }
-       ReleaseWriteLock(&adp->lock);
-#if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+       aexcl == NONEXCL
+#endif
+       ) {
+           /* if we get an EEXIST in nonexcl mode, just do a lookup */
+           if (tdc) {
+               ReleaseSharedLock(&tdc->lock);
+               afs_PutDCache(tdc);
+           }
+           ReleaseWriteLock(&adp->lock);
+
+
 #if defined(AFS_SGI64_ENV)
-       code =
-           afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp, NULL,
-                      0, NULL, acred);
-#else
-       code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred);
-#endif /* AFS_SGI64_ENV */
-#else /* SUN5 || SGI */
-       code = afs_lookup(adp, aname, avcp, acred);
-#endif /* SUN5 || SGI */
+           code = afs_lookup(VNODE_TO_FIRST_BHV((vnode_t *) adp), aname, avcp,
+                                 NULL, 0, NULL, acred);
+#elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
+           code = afs_lookup(adp, aname, avcp, NULL, 0, NULL, acred);
+#elif defined(UKERNEL)
+           code = afs_lookup(adp, aname, avcp, acred, 0);
+#elif !defined(AFS_DARWIN_ENV)
+           code = afs_lookup(adp, aname, avcp, acred);
+#endif
        goto done;
-    }
-#endif /* AFS_OSF_ENV */
-    if (code) {
-       if (code < 0) {
-           ObtainWriteLock(&afs_xcbhash, 488);
-           afs_DequeueCallback(adp);
-           adp->states &= ~CStatd;
-           ReleaseWriteLock(&afs_xcbhash);
-           osi_dnlc_purgedp(adp);
-       }
-       ReleaseWriteLock(&adp->lock);
-       if (tdc) {
-           ReleaseSharedLock(&tdc->lock);
-           afs_PutDCache(tdc);
-       }
+        }
+
+       if (code) {
+           if (code < 0) {
+               afs_StaleVCache(adp);
+           }
+           ReleaseWriteLock(&adp->lock);
+           if (tdc) {
+               ReleaseSharedLock(&tdc->lock);
+               afs_PutDCache(tdc);
+           }
        goto done;
-    }
+       }
+
+    } else {
+       /* Generate a fake FID for disconnected mode. */
+       newFid.Cell = adp->f.fid.Cell;
+       newFid.Fid.Volume = adp->f.fid.Fid.Volume;
+       afs_GenFakeFid(&newFid, VREG, 1);
+    }                          /* if (!AFS_IS_DISCON_RW) */
+
     /* otherwise, we should see if we can make the change to the dir locally */
     if (tdc)
        UpgradeSToWLock(&tdc->lock, 631);
-    if (afs_LocalHero(adp, tdc, &OutDirStatus, 1)) {
+    if (AFS_IS_DISCON_RW || afs_LocalHero(adp, tdc, OutDirStatus, 1)) {
        /* we can do it locally */
-       code = afs_dir_Create(&tdc->f.inode, aname, &newFid.Fid);
+       ObtainWriteLock(&afs_xdcache, 291);
+       code = afs_dir_Create(tdc, aname, &newFid.Fid);
+       ReleaseWriteLock(&afs_xdcache);
        if (code) {
            ZapDCE(tdc);
-           DZap(&tdc->f.inode);
+           DZap(tdc);
        }
     }
     if (tdc) {
        ReleaseWriteLock(&tdc->lock);
        afs_PutDCache(tdc);
     }
-    newFid.Cell = adp->fid.Cell;
-    newFid.Fid.Volume = adp->fid.Fid.Volume;
+    if (AFS_IS_DISCON_RW)
+       adp->f.m.LinkCount++;
+
+    newFid.Cell = adp->f.fid.Cell;
+    newFid.Fid.Volume = adp->f.fid.Fid.Volume;
     ReleaseWriteLock(&adp->lock);
     volp = afs_FindVolume(&newFid, READ_LOCK);
 
@@ -401,7 +416,7 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
      * would fail, since no call would be able to update the local vnode status after modifying
      * a file on a file server. */
     ObtainWriteLock(&afs_xvcache, 138);
-    if (adp->states & CForeign)
+    if (adp->f.states & CForeign)
        finalZaps = afs_allZaps;        /* do this before calling newvcache */
     else
        finalZaps = afs_evenZaps;       /* do this before calling newvcache */
@@ -420,29 +435,40 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
            ObtainWriteLock(&afs_xcbhash, 489);
            finalCBs = afs_evenCBs;
            /* add the callback in */
-           if (adp->states & CForeign) {
-               tvc->states |= CForeign;
+           if (adp->f.states & CForeign) {
+               tvc->f.states |= CForeign;
                finalCBs = afs_allCBs;
            }
            if (origCBs == finalCBs && origZaps == finalZaps) {
-               tvc->states |= CStatd;  /* we've fake entire thing, so don't stat */
-               tvc->states &= ~CBulkFetching;
-               tvc->cbExpires = CallBack.ExpirationTime;
-               afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
+               tvc->f.states |= CStatd;        /* we've fake entire thing, so don't stat */
+               tvc->f.states &= ~CBulkFetching;
+               if (!AFS_IS_DISCON_RW) {
+                   tvc->cbExpires = CallBack.ExpirationTime;
+                   afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), volp);
+               }
            } else {
-               afs_DequeueCallback(tvc);
-               tvc->states &= ~(CStatd | CUnique);
-               tvc->callback = 0;
-               if (tvc->fid.Fid.Vnode & 1 || (vType(tvc) == VDIR))
-                   osi_dnlc_purgedp(tvc);
+               afs_StaleVCacheFlags(tvc,
+                                    AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
+                                    CUnique);
            }
            ReleaseWriteLock(&afs_xcbhash);
-           afs_ProcessFS(tvc, &OutFidStatus, &treq);
+           if (AFS_IS_DISCON_RW) {
+               afs_DisconAddDirty(tvc, VDisconCreate, 0);
+               afs_GenDisconStatus(adp, tvc, &newFid, attrs, treq, VREG);
+           } else {
+               afs_ProcessFS(tvc, OutFidStatus, treq);
+           }
+
+           tvc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+           tvc->f.parent.unique = adp->f.fid.Fid.Unique;
            ReleaseWriteLock(&tvc->lock);
            *avcp = tvc;
            code = 0;
-       } else
-           code = ENOENT;
+
+       } else {
+           /* Cannot create a new vcache. */
+           code = EIO;
+       }
     } else {
        /* otherwise cache entry already exists, someone else must
         * have created it.  Comments used to say:  "don't need write
@@ -458,27 +484,28 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
     ReleaseWriteLock(&afs_xvcache);
 
   done:
+    AFS_DISCON_UNLOCK();
+
+  done3:
     if (volp)
        afs_PutVolume(volp, READ_LOCK);
 
     if (code == 0) {
-       afs_AddMarinerName(aname, *avcp);
+       if (afs_mariner)
+           afs_AddMarinerName(aname, *avcp);
        /* return the new status in vattr */
        afs_CopyOutAttrs(*avcp, attrs);
+       if (afs_mariner)
+           afs_MarinerLog("store$Creating", *avcp);
     }
-#ifdef AFS_OSF_ENV
-    if (!code && !strcmp(aname, "core"))
-       tvc->states |= CCore1;
-#endif
 
     afs_PutFakeStat(&fakestate);
-    code = afs_CheckCode(code, &treq, 20);
+    code = afs_CheckCode(code, treq, 20);
+    afs_DestroyReq(treq);
 
   done2:
-#ifdef AFS_OSF_ENV
-    afs_PutVCache(adp);
-#endif /* AFS_OSF_ENV */
-
+    osi_FreeSmallSpace(OutFidStatus);
+    osi_FreeSmallSpace(OutDirStatus);
     return code;
 }
 
@@ -494,34 +521,46 @@ afs_create(OSI_VC_DECL(adp), char *aname, struct vattr *attrs,
  * and dcache entry write-locked.
  */
 int
-afs_LocalHero(register struct vcache *avc, register struct dcache *adc,
-             register AFSFetchStatus * astat, register int aincr)
+afs_LocalHero(struct vcache *avc, struct dcache *adc,
+             AFSFetchStatus * astat, int aincr)
 {
-    register afs_int32 ok;
+    afs_int32 ok;
     afs_hyper_t avers;
 
     AFS_STATCNT(afs_LocalHero);
     hset64(avers, astat->dataVersionHigh, astat->DataVersion);
-    /* this *is* the version number, no matter what */
+    /* avers *is* the version number now, no matter what */
+
     if (adc) {
-       ok = (hsame(avc->m.DataVersion, adc->f.versionNo) && avc->callback
-             && (avc->states & CStatd) && avc->cbExpires >= osi_Time());
+       /* does what's in the dcache *now* match what's in the vcache *now*,
+        * and do we have a valid callback? if not, our local copy is not "ok" */
+       ok = (hsame(avc->f.m.DataVersion, adc->f.versionNo) && avc->callback
+             && (avc->f.states & CStatd) && avc->cbExpires >= osi_Time());
     } else {
        ok = 0;
     }
+    if (ok) {
+       /* check that the DV on the server is what we expect it to be */
+       afs_hyper_t newDV;
+       hset(newDV, adc->f.versionNo);
+       hadd32(newDV, aincr);
+       if (!hsame(avers, newDV)) {
+           ok = 0;
+       }
+    }
 #if defined(AFS_SGI_ENV)
     osi_Assert(avc->v.v_type == VDIR);
 #endif
     /* The bulk status code used the length as a sequence number.  */
     /* Don't update the vcache entry unless the stats are current. */
-    if (avc->states & CStatd) {
-       hset(avc->m.DataVersion, avers);
+    if (avc->f.states & CStatd) {
+       afs_SetDataVersion(avc, &avers);
 #ifdef AFS_64BIT_CLIENT
-       FillInt64(avc->m.Length, astat->Length_hi, astat->Length);
-#else /* AFS_64BIT_ENV */
-       avc->m.Length = astat->Length;
-#endif /* AFS_64BIT_ENV */
-       avc->m.Date = astat->ClientModTime;
+       FillInt64(avc->f.m.Length, astat->Length_hi, astat->Length);
+#else /* AFS_64BIT_CLIENT */
+       avc->f.m.Length = astat->Length;
+#endif /* AFS_64BIT_CLIENT */
+       avc->f.m.Date = astat->ClientModTime;
     }
     if (ok) {
        /* we've been tracking things correctly */
@@ -531,9 +570,9 @@ afs_LocalHero(register struct vcache *avc, register struct dcache *adc,
     } else {
        if (adc) {
            ZapDCE(adc);
-           DZap(&adc->f.inode);
+           DZap(adc);
        }
-       if (avc->states & CStatd) {
+       if (avc->f.states & CStatd) {
            osi_dnlc_purgedp(avc);
        }
        return 0;