Windows: Apply MAX_FID_COUNT to AFS Redirector
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index e12b0ab..36cefd4 100644 (file)
@@ -12,6 +12,7 @@
 #include <roken.h>
 
 #include <afs/stds.h>
+#include <afs/unified_afs.h>
 
 #include <windows.h>
 #include <winsock2.h>
@@ -159,17 +160,8 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
                 !(rights & PRSFS_WRITE))
                 code = 0;
             else {
-               switch (code) {
-               case CM_ERROR_ALLOFFLINE:
-               case CM_ERROR_ALLDOWN:
-               case CM_ERROR_ALLBUSY:
-               case CM_ERROR_TIMEDOUT:
-               case CM_ERROR_RETRY:
-               case CM_ERROR_WOULDBLOCK:
-                   break;
-               default:
+               if (code == CM_ERROR_LOCK_NOT_GRANTED)
                    code = CM_ERROR_SHARING_VIOLATION;
-               }
            }
         }
 
@@ -187,22 +179,43 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
 }
 
 /* return success if we can open this file in this mode */
-long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
-                    unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
+long cm_CheckNTOpen(cm_scache_t *scp,
+                    unsigned int desiredAccess,
+                    unsigned int shareAccess,
+                    unsigned int createDisp,
+                    afs_offs_t process_id,
+                    afs_offs_t handle_id,
+                    cm_user_t *userp, cm_req_t *reqp,
                    cm_lock_data_t **ldpp)
 {
     long rights;
     long code = 0;
+    afs_uint16 session_id;
 
     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
     *ldpp = NULL;
 
+    /* compute the session id */
+    if (reqp->flags & CM_REQ_SOURCE_SMB)
+        session_id = CM_SESSION_SMB;
+    else if (reqp->flags & CM_REQ_SOURCE_REDIR)
+        session_id = CM_SESSION_IFS;
+    else
+        session_id = CM_SESSION_CMINT;
+
+    /* Ignore the SYNCHRONIZE privilege */
+    desiredAccess &= ~SYNCHRONIZE;
+
     /* Always allow delete; the RPC will tell us if it's OK */
     rights = 0;
 
     if (desiredAccess == DELETE)
         goto done_2;
 
+    /* Always allow reading attributes (Hidden, System, Readonly, ...) */
+    if (desiredAccess == FILE_READ_ATTRIBUTES)
+        goto done_2;
+
     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
 
@@ -233,8 +246,9 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
         code = CM_ERROR_NOACCESS;
 
     if (code == 0 &&
-             ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
-             scp->fileType == CM_SCACHETYPE_FILE) {
+        !(shareAccess & FILE_SHARE_WRITE) &&
+        ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
+        scp->fileType == CM_SCACHETYPE_FILE) {
         cm_key_t key;
         unsigned int sLockType;
         LARGE_INTEGER LOffset, LLength;
@@ -242,12 +256,13 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
         /* Check if there's some sort of lock on the file at the
            moment. */
 
-        key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
         if (rights & PRSFS_WRITE)
             sLockType = 0;
         else
             sLockType = LOCKING_ANDX_SHARED_LOCK;
 
+        key = cm_GenerateKey(session_id, process_id, 0);
+
         /* single byte lock at offset 0x0100 0000 0000 0000 */
         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
@@ -257,38 +272,30 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
 
         if (code == 0) {
-           (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
-           if (!*ldpp) {
-               code = ENOMEM;
-               goto _done;
-           }
+            (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
+            if (!*ldpp) {
+                code = ENOMEM;
+                goto _done;
+            }
 
-           (*ldpp)->key = key;
-           (*ldpp)->sLockType = sLockType;
-           (*ldpp)->LOffset.HighPart = LOffset.HighPart;
-           (*ldpp)->LOffset.LowPart = LOffset.LowPart;
-           (*ldpp)->LLength.HighPart = LLength.HighPart;
-           (*ldpp)->LLength.LowPart = LLength.LowPart;
+            (*ldpp)->key = key;
+            (*ldpp)->sLockType = sLockType;
+            (*ldpp)->LOffset.HighPart = LOffset.HighPart;
+            (*ldpp)->LOffset.LowPart = LOffset.LowPart;
+            (*ldpp)->LLength.HighPart = LLength.HighPart;
+            (*ldpp)->LLength.LowPart = LLength.LowPart;
         } else {
-            /* In this case, we allow the file open to go through even
-               though we can't enforce mandatory locking on the
-               file. */
+            /*
+             * In this case, we allow the file open to go through even
+             * though we can't enforce mandatory locking on the
+             * file. */
             if (code == CM_ERROR_NOACCESS &&
-                !(rights & PRSFS_WRITE))
+                 !(rights & PRSFS_WRITE))
                 code = 0;
             else {
-               switch (code) {
-               case CM_ERROR_ALLOFFLINE:
-               case CM_ERROR_ALLDOWN:
-               case CM_ERROR_ALLBUSY:
-               case CM_ERROR_TIMEDOUT:
-               case CM_ERROR_RETRY:
-               case CM_ERROR_WOULDBLOCK:
-                   break;
-               default:
-                   code = CM_ERROR_SHARING_VIOLATION;
-               }
-           }
+                if (code == CM_ERROR_LOCK_NOT_GRANTED)
+                    code = CM_ERROR_SHARING_VIOLATION;
+            }
         }
     } else if (code != 0) {
         goto _done;
@@ -305,9 +312,9 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
                               cm_lock_data_t ** ldpp)
 {
-    osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
+       osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, ldpp ? *ldpp : 0);
     lock_ObtainWrite(&scp->rw);
-    if (*ldpp) {
+    if (ldpp && *ldpp) {
        cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
                  (*ldpp)->key, 0, userp, reqp);
        free(*ldpp);
@@ -899,7 +906,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
         tfid = scp->mountRootFid;
         lock_ReleaseWrite(&scp->rw);
-        code = cm_GetSCache(&tfid, outScpp, userp, reqp);
+        code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
         lock_ObtainWrite(&scp->rw);
         return code;
     }
@@ -1016,7 +1023,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
         tfid = scp->mountRootFid;
         lock_ReleaseWrite(&scp->rw);
-        code = cm_GetSCache(&tfid, outScpp, userp, reqp);
+        code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
         lock_ObtainWrite(&scp->rw);
     }
 
@@ -1261,7 +1268,7 @@ notfound:
     if ( !tscp )    /* we did not find it in the dnlc */
     {
         dnlcHit = 0;
-        code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
+        code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
         if (code)
             goto done;
     }
@@ -1342,10 +1349,16 @@ notfound:
     return code;
 }
 
-int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
+int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
 {
     clientchar_t *tp;
     int prefixCount;
+#ifdef _WIN64
+    int use_sysname64 = 0;
+
+    if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
+        use_sysname64 = 1;
+#endif
 
     tp = cm_ClientStrRChr(inp, '@');
     if (tp == NULL)
@@ -1358,6 +1371,11 @@ int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, uns
     if (outp == NULL)
         return 1;
 
+#ifdef _WIN64
+    if (use_sysname64 && index >= cm_sysName64Count)
+        return -1;
+    else
+#endif
     if (index >= cm_sysNameCount)
         return -1;
 
@@ -1365,8 +1383,14 @@ int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, uns
     prefixCount = (int)(tp - inp);
 
     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);      /* copy out "a." from "a.@sys" */
-    outp[prefixCount] = 0;             /* null terminate the "a." */
-    cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
+    outp[prefixCount] = 0;                                     /* null terminate the "a." */
+#ifdef _WIN64
+    if (use_sysname64)
+        cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
+    else
+#endif
+        cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
+
     return 1;
 }
 
@@ -1460,7 +1484,7 @@ long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * us
 
     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
 
-    code = cm_GetSCache(&fid, outScpp, userp, reqp);
+    code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
 
   _exit_cleanup:
     if (fnamep)
@@ -1509,9 +1533,9 @@ long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *us
         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
     }
 
-    if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
+    if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
-            code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
+            code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
             if (code > 0) {
                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
 #ifdef DEBUG_REFCOUNT
@@ -1587,6 +1611,7 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
     cm_dirOp_t dirop;
     cm_scache_t *scp = NULL;
     int free_fnamep = FALSE;
+    int invalidate = 0;
 
     memset(&volSync, 0, sizeof(volSync));
 
@@ -1638,6 +1663,8 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
     }
 
     /* make the RPC */
+    InterlockedIncrement(&dscp->activeRPCs);
+
     afsFid.Volume = dscp->fid.volume;
     afsFid.Vnode = dscp->fid.vnode;
     afsFid.Unique = dscp->fid.unique;
@@ -1653,7 +1680,7 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
                                 &newDirStatus, &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
+    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -1667,34 +1694,57 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
     }
     lock_ObtainWrite(&dscp->rw);
     cm_dnlcRemove(dscp, cnamep);
-    cm_SyncOpDone(dscp, NULL, sflags);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
-    } else if (code == CM_ERROR_NOSUCHFILE) {
-       /* windows would not have allowed the request to delete the file
-        * if it did not believe the file existed.  therefore, we must
-        * have an inconsistent view of the world.
-        */
-       dscp->cbServerp = NULL;
-    }
-    lock_ReleaseWrite(&dscp->rw);
-
-    if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
-        cm_DirDeleteEntry(&dirop, fnamep);
+        invalidate = 1;
+        if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
+            lock_ReleaseWrite(&dscp->rw);
+            cm_DirDeleteEntry(&dirop, fnamep);
 #ifdef USE_BPLUS
-        cm_BPlusDirDeleteEntry(&dirop, cnamep);
+            cm_BPlusDirDeleteEntry(&dirop, cnamep);
 #endif
+            lock_ObtainWrite(&dscp->rw);
+        }
+    } else {
+        InterlockedDecrement(&scp->activeRPCs);
+        if (code == CM_ERROR_NOSUCHFILE) {
+            /* windows would not have allowed the request to delete the file
+             * if it did not believe the file existed.  therefore, we must
+             * have an inconsistent view of the world.
+             */
+            dscp->cbServerp = NULL;
+        }
     }
+
+    cm_SyncOpDone(dscp, NULL, sflags);
+    lock_ReleaseWrite(&dscp->rw);
+
     cm_EndDirOp(&dirop);
 
+    if (invalidate && RDR_Initialized &&
+        scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
+        RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
+                              dscp->fid.unique, dscp->fid.hash,
+                              dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
+
     if (scp) {
         cm_ReleaseSCache(scp);
         if (code == 0) {
            lock_ObtainWrite(&scp->rw);
-            if (--scp->linkCount == 0)
+            if (--scp->linkCount == 0) {
                 scp->flags |= CM_SCACHEFLAG_DELETED;
+               lock_ObtainWrite(&cm_scacheLock);
+                cm_AdjustScacheLRU(scp);
+                cm_RemoveSCacheFromHashTable(scp);
+               lock_ReleaseWrite(&cm_scacheLock);
+            }
             cm_DiscardSCache(scp);
            lock_ReleaseWrite(&scp->rw);
+            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
+                !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                      scp->fid.unique, scp->fid.hash,
+                                      scp->fileType, AFS_INVALIDATE_DELETED))
+                buf_ClearRDRFlag(scp, "unlink");
         }
     }
 
@@ -1919,7 +1969,6 @@ long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
     int symlinkCount;          /* count of # of symlinks traversed */
     int extraFlag;             /* avoid chasing mt pts for dir cmd */
     int phase = 1;             /* 1 = tidPathp, 2 = pathp */
-#define MAX_FID_COUNT 512
     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
     int fid_count = 0;          /* number of fids processed in this path walk */
     int i;
@@ -2301,7 +2350,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     if (tscp) {
         if (lock_TryWrite(&tscp->rw)) {
             /* we have an entry that we can look at */
-            if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
+            if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
                 /* we have a callback on it.  Don't bother
                  * fetching this stat entry, since we're happy
                  * with the info we have.
@@ -2325,7 +2374,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
           !(tfid.vnode==0x1 && tfid.unique==0x1) )
     {
         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
-        return cm_GetSCache(&tfid, &tscp, NULL, NULL);
+        return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
     }
 #endif /* AFS_FREELANCE_CLIENT */
 
@@ -2347,6 +2396,7 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
     long filex;
     AFSVolSync volSync;
     cm_callbackRequest_t cbReq;
+    int lostRace;
     long filesThisCall;
     long i;
     long j;
@@ -2434,7 +2484,7 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
                         code = (&bbp->stats[0])->errorCode;
                 }
             }
-        } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
+        } while (cm_Analyze(connp, userp, reqp, &tfid, 0, &volSync, NULL, &cbReq, code));
         code = cm_MapRPCError(code, reqp);
 
         /*
@@ -2465,9 +2515,16 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
 
             if (inlinebulk && (&bbp->stats[j])->errorCode) {
                 cm_req_t treq = *reqp;
-                cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
+                cm_Analyze(NULL, userp, &treq, &tfid, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
+                switch ((&bbp->stats[j])->errorCode) {
+                case EACCES:
+                case UAEACCES:
+                case EPERM:
+                case UAEPERM:
+                    cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
+                }
             } else {
-                code = cm_GetSCache(&tfid, &scp, userp, reqp);
+                code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
                 if (code != 0)
                     continue;
 
@@ -2497,14 +2554,16 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
                 if ((scp->cbServerp == NULL &&
                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
-                     (scp->flags & CM_SCACHEFLAG_EACCESS))
+                     cm_EAccesFindEntry(userp, &scp->fid))
                 {
                     lock_ConvertRToW(&scp->rw);
-                    cm_EndCallbackGrantingCall(scp, &cbReq,
-                                               &bbp->callbacks[j],
-                                               &volSync,
-                                               CM_CALLBACK_MAINTAINCOUNT);
-                    cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
+                    lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
+                                                          &bbp->callbacks[j],
+                                                          &volSync,
+                                                          CM_CALLBACK_MAINTAINCOUNT);
+                    InterlockedIncrement(&scp->activeRPCs);
+                    if (!lostRace)
+                        cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
                     lock_ReleaseWrite(&scp->rw);
                 } else {
                     lock_ReleaseRead(&scp->rw);
@@ -2537,6 +2596,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
 
     bbp = malloc(sizeof(cm_bulkStat_t));
     memset(bbp, 0, sizeof(cm_bulkStat_t));
+    bbp->userp = userp;
     bbp->bufOffset = *offsetp;
 
     lock_ReleaseWrite(&dscp->rw);
@@ -2597,6 +2657,73 @@ void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *att
     statusp->Mask = mask;
 }
 
+int
+cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
+{
+    int spaceAvail = 1;
+    afs_uint32  code;
+    cm_conn_t *connp;
+    struct rx_connection * rxconnp;
+    AFSFetchVolumeStatus volStat;
+    cm_volume_t *volp = NULL;
+    afs_uint32   volType;
+    char *Name;
+    char *OfflineMsg;
+    char *MOTD;
+    char volName[32]="(unknown)";
+    char offLineMsg[256]="server temporarily inaccessible";
+    char motd[256]="server temporarily inaccessible";
+    osi_hyper_t freespace;
+
+    if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
+        fidp->volume==AFS_FAKE_ROOT_VOL_ID)
+    {
+        goto _done;
+    }
+
+    volp = cm_GetVolumeByFID(fidp);
+    if (!volp) {
+        spaceAvail = 0;
+        goto _done;
+    }
+    volType = cm_VolumeType(volp, fidp->volume);
+    if (volType == ROVOL || volType == BACKVOL) {
+        spaceAvail = 0;
+        goto _done;
+    }
+
+    Name = volName;
+    OfflineMsg = offLineMsg;
+    MOTD = motd;
+
+    do {
+        code = cm_ConnFromFID(fidp, userp, reqp, &connp);
+        if (code) continue;
+
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
+                                     &volStat, &Name, &OfflineMsg, &MOTD);
+        rx_PutConnection(rxconnp);
+
+    } while (cm_Analyze(connp, userp, reqp, fidp, 0, NULL, NULL, NULL, code));
+    code = cm_MapRPCError(code, reqp);
+    if (code == 0) {
+        if (volStat.MaxQuota) {
+            freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
+        } else {
+            freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
+        }
+        spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
+    }
+    /* the rpc failed, assume there is space and we can fail it later. */
+
+  _done:
+    if (volp)
+        cm_PutVolume(volp);
+
+    return spaceAvail;
+}
+
 /* set the file size, and make sure that all relevant buffers have been
  * truncated.  Ensure that any partially truncated buffers have been zeroed
  * to the end of the buffer.
@@ -2678,13 +2805,20 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
     }
     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
         /* really extending the file */
-        scp->length = *sizep;
-        scp->mask |= CM_SCACHEMASK_LENGTH;
+        /* Check to see if we have sufficient quota */
+        if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
+            scp->length = *sizep;
+            scp->mask |= CM_SCACHEMASK_LENGTH;
+        } else {
+            code = CM_ERROR_SPACE;
+            goto syncopdone;
+        }
     }
 
     /* done successfully */
     code = 0;
 
+  syncopdone:
     cm_SyncOpDone(scp, NULL,
                   CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
                   | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
@@ -2739,6 +2873,8 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     lock_ReleaseRead(&scp->rw);
 
     /* now make the RPC */
+    InterlockedIncrement(&scp->activeRPCs);
+
     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
     do {
         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
@@ -2751,7 +2887,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &scp->fid, &volSync, NULL, NULL, code));
+                         &scp->fid, 1, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -2760,10 +2896,12 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
 
     lock_ObtainWrite(&scp->rw);
-    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
     if (code == 0)
         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
+    else
+        InterlockedDecrement(&scp->activeRPCs);
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
 
     /* if we're changing the mode bits, discard the ACL cache,
      * since we changed the mode bits.
@@ -2785,6 +2923,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     cm_fid_t newFid;
     cm_scache_t *scp = NULL;
     int didEnd;
+    int lostRace;
     AFSStoreStatus inStatus;
     AFSFetchStatus updatedDirStatus;
     AFSFetchStatus newFileStatus;
@@ -2799,7 +2938,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -2841,6 +2980,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    InterlockedIncrement(&dscp->activeRPCs);
     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
@@ -2859,7 +2999,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, &volSync, NULL, &cbReq, code));
+                         &dscp->fid, 1, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -2872,10 +3012,21 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
     lock_ObtainWrite(&dscp->rw);
-    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
+        if (cm_CheckDirOpForSingleChange(&dirop)) {
+            lock_ReleaseWrite(&dscp->rw);
+            cm_DirCreateEntry(&dirop, fnamep, &newFid);
+#ifdef USE_BPLUS
+            cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
+#endif
+            lock_ObtainWrite(&dscp->rw);
+        }
+    } else {
+        InterlockedDecrement(&dscp->activeRPCs);
     }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     lock_ReleaseWrite(&dscp->rw);
 
     /* now try to create the file's entry, too, but be careful to
@@ -2884,16 +3035,17 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
      * info.
      */
     if (code == 0) {
-        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
            scp->creator = userp;               /* remember who created it */
             if (!cm_HaveCallback(scp)) {
-                cm_EndCallbackGrantingCall(scp, &cbReq,
-                                           &newFileCallback, &volSync, 0);
-                cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
-                               userp, reqp, 0);
+                lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
+                                                      &newFileCallback, &volSync, 0);
+                InterlockedIncrement(&scp->activeRPCs);
+                if (!lostRace)
+                    cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
+                                   userp, reqp, 0);
                 didEnd = 1;
             }
             lock_ReleaseWrite(&scp->rw);
@@ -2904,12 +3056,6 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     if (!didEnd)
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
 
-    if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
-        cm_DirCreateEntry(&dirop, fnamep, &newFid);
-#ifdef USE_BPLUS
-        cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
-#endif
-    }
     cm_EndDirOp(&dirop);
 
     if (fnamep)
@@ -2934,6 +3080,9 @@ long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 loc
 
     if (locked)
         lock_ReleaseWrite(&scp->rw);
+
+    osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
+
     code = buf_CleanVnode(scp, userp, reqp);
     if (code == 0) {
         lock_ObtainWrite(&scp->rw);
@@ -2967,6 +3116,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     cm_fid_t newFid;
     cm_scache_t *scp = NULL;
     int didEnd;
+    int lostRace;
     AFSStoreStatus inStatus;
     AFSFetchStatus updatedDirStatus;
     AFSFetchStatus newDirStatus;
@@ -2981,7 +3131,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -3022,6 +3172,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    InterlockedIncrement(&dscp->activeRPCs);
     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
@@ -3040,7 +3191,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                        &dscp->fid, &volSync, NULL, &cbReq, code));
+                        &dscp->fid, 1, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -3053,10 +3204,21 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
     lock_ObtainWrite(&dscp->rw);
-    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
+        if (cm_CheckDirOpForSingleChange(&dirop)) {
+            lock_ReleaseWrite(&dscp->rw);
+            cm_DirCreateEntry(&dirop, fnamep, &newFid);
+#ifdef USE_BPLUS
+            cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
+#endif
+            lock_ObtainWrite(&dscp->rw);
+        }
+    } else {
+        InterlockedDecrement(&dscp->activeRPCs);
     }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     lock_ReleaseWrite(&dscp->rw);
 
     /* now try to create the new dir's entry, too, but be careful to
@@ -3065,15 +3227,16 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
      * info.
      */
     if (code == 0) {
-        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
-                cm_EndCallbackGrantingCall(scp, &cbReq,
-                                            &newDirCallback, &volSync, 0);
-                cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
-                                userp, reqp, 0);
+                lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
+                                                      &newDirCallback, &volSync, 0);
+                InterlockedIncrement(&scp->activeRPCs);
+                if (!lostRace)
+                    cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
+                                   userp, reqp, 0);
                 didEnd = 1;
             }
             lock_ReleaseWrite(&scp->rw);
@@ -3084,12 +3247,6 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     if (!didEnd)
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
 
-    if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
-        cm_DirCreateEntry(&dirop, fnamep, &newFid);
-#ifdef USE_BPLUS
-        cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
-#endif
-    }
     cm_EndDirOp(&dirop);
 
     free(fnamep);
@@ -3118,6 +3275,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
     struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
     fschar_t * fnamep = NULL;
+    int invalidate = 0;
 
     memset(&volSync, 0, sizeof(volSync));
 
@@ -3144,6 +3302,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
 
     /* try the RPC now */
+    InterlockedIncrement(&dscp->activeRPCs);
     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
@@ -3163,8 +3322,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
         rx_PutConnection(rxconnp);
         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
 
-    } while (cm_Analyze(connp, userp, reqp,
-        &dscp->fid, &volSync, NULL, NULL, code));
+    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
 
     code = cm_MapRPCError(code, reqp);
 
@@ -3178,25 +3336,35 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
     lock_ObtainWrite(&dscp->rw);
-    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
-    }
-    lock_ReleaseWrite(&dscp->rw);
+        invalidate = 1;
 
-    if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
+            lock_ReleaseWrite(&dscp->rw);
             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
 #ifdef USE_BPLUS
             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
 #endif
+            lock_ObtainWrite(&dscp->rw);
         }
+    } else {
+        InterlockedDecrement(&dscp->activeRPCs);
     }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseWrite(&dscp->rw);
+
     cm_EndDirOp(&dirop);
 
+    if (invalidate && RDR_Initialized)
+        RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
+                             dscp->fid.unique, dscp->fid.hash,
+                             dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
+
     /* Update the linked object status */
     if (code == 0) {
         lock_ObtainWrite(&sscp->rw);
+        InterlockedIncrement(&sscp->activeRPCs);
         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
         lock_ReleaseWrite(&sscp->rw);
     }
@@ -3207,7 +3375,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
 }
 
 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
-                cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
+                cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
 {
     cm_conn_t *connp;
     long code;
@@ -3223,6 +3391,9 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
     cm_dirOp_t dirop;
     fschar_t *fnamep = NULL;
 
+    if (scpp)
+        *scpp = NULL;
+
     /* Check for RO volume */
     if (dscp->flags & CM_SCACHEFLAG_RO)
         return CM_ERROR_READONLY;
@@ -3249,6 +3420,7 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    InterlockedIncrement(&dscp->activeRPCs);
     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
@@ -3266,7 +3438,7 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, &volSync, NULL, NULL, code));
+                         &dscp->fid, 1, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -3279,22 +3451,25 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
     lock_ObtainWrite(&dscp->rw);
-    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
-    }
-    lock_ReleaseWrite(&dscp->rw);
-
-    if (code == 0) {
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         if (cm_CheckDirOpForSingleChange(&dirop)) {
+            lock_ReleaseWrite(&dscp->rw);
             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
 
             cm_DirCreateEntry(&dirop, fnamep, &newFid);
 #ifdef USE_BPLUS
             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
 #endif
+            lock_ObtainWrite(&dscp->rw);
         }
+    } else {
+        InterlockedDecrement(&dscp->activeRPCs);
     }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseWrite(&dscp->rw);
+
     cm_EndDirOp(&dirop);
 
     /* now try to create the new dir's entry, too, but be careful to
@@ -3303,16 +3478,21 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
      * info.
      */
     if (code == 0) {
-        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
+                InterlockedIncrement(&scp->activeRPCs);
                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
                                 userp, reqp, 0);
             }
             lock_ReleaseWrite(&scp->rw);
-            cm_ReleaseSCache(scp);
+
+            if (scpp) {
+                *scpp = scp;
+            } else {
+                cm_ReleaseSCache(scp);
+            }
         }
     }
 
@@ -3398,6 +3578,7 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
     didEnd = 0;
 
     /* try the RPC now */
+    InterlockedIncrement(&dscp->activeRPCs);
     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
@@ -3414,7 +3595,7 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                        &dscp->fid, &volSync, NULL, NULL, code));
+                        &dscp->fid, 1, &volSync, NULL, NULL, code));
     code = cm_MapRPCErrorRmdir(code, reqp);
 
     if (code)
@@ -3427,21 +3608,23 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
     lock_ObtainWrite(&dscp->rw);
-    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_dnlcRemove(dscp, cnamep);
         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
-    }
-    lock_ReleaseWrite(&dscp->rw);
-
-    if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
+            lock_ReleaseWrite(&dscp->rw);
             cm_DirDeleteEntry(&dirop, fnamep);
 #ifdef USE_BPLUS
             cm_BPlusDirDeleteEntry(&dirop, cnamep);
 #endif
+            lock_ObtainWrite(&dscp->rw);
         }
+    } else {
+        InterlockedDecrement(&dscp->activeRPCs);
     }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseWrite(&dscp->rw);
+
     cm_EndDirOp(&dirop);
 
     if (scp) {
@@ -3449,7 +3632,16 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
         if (code == 0) {
            lock_ObtainWrite(&scp->rw);
             scp->flags |= CM_SCACHEFLAG_DELETED;
+            lock_ObtainWrite(&cm_scacheLock);
+            cm_AdjustScacheLRU(scp);
+            cm_RemoveSCacheFromHashTable(scp);
+            lock_ReleaseWrite(&cm_scacheLock);
            lock_ReleaseWrite(&scp->rw);
+            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
+                !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                      scp->fid.unique, scp->fid.hash,
+                                      scp->fileType, AFS_INVALIDATE_DELETED))
+                buf_ClearRDRFlag(scp, "rmdir");
         }
     }
 
@@ -3508,14 +3700,14 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
                cm_req_t *reqp)
 {
     cm_conn_t *connp;
-    long code;
+    long code = 0;
     AFSFid oldDirAFSFid;
     AFSFid newDirAFSFid;
-    int didEnd;
     AFSFetchStatus updatedOldDirStatus;
     AFSFetchStatus updatedNewDirStatus;
     AFSVolSync volSync;
-    int oneDir;
+    int oneDir = 0;
+    int bTargetExists = 0;
     struct rx_connection * rxconnp;
     cm_dirOp_t oldDirOp;
     cm_fid_t   fileFid;
@@ -3523,7 +3715,8 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
     cm_dirOp_t newDirOp;
     fschar_t * newNamep = NULL;
     int free_oldNamep = FALSE;
-    cm_scache_t *oldScp = NULL, *newScp = NULL;
+    cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
+    int rpc_skipped = 0;
 
     memset(&volSync, 0, sizeof(volSync));
 
@@ -3532,44 +3725,19 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
         cm_ClientStrLen(cNewNamep) == 0)
         return CM_ERROR_INVAL;
 
-    /*
-     * Before we permit the operation, make sure that we do not already have
-     * an object in the destination directory that has a case-insensitive match
-     * for this name UNLESS the matching object is the object we are renaming.
-     */
-    code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
-    if (code) {
-        osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
-                 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
-        goto done;
-    }
-
-    code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
-    if (code == 0) {
-        /* found a matching object with the new name */
-        if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
-            /* and they don't match so return an error */
-            osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
-                      newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
-            code = CM_ERROR_EXISTS;
-        }
-        cm_ReleaseSCache(newScp);
-        newScp = NULL;
-    } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
-        code = CM_ERROR_EXISTS;
-    } else {
-        code = 0;
+    /* check for identical names */
+    if (oldDscp == newDscp &&
+        cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
+        osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
+                  oldDscp, newDscp);
+        return CM_ERROR_RENAME_IDENTICAL;
     }
 
     /* Check for RO volume */
-    if (code == 0 &&
-        (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
-        code = CM_ERROR_READONLY;
+    if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
+        return CM_ERROR_READONLY;
     }
 
-    if (code)
-        goto done;
-
     if (oldNamep == NULL) {
         code = -1;
 #ifdef USE_BPLUS
@@ -3589,21 +3757,12 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
         }
     }
 
-
     /* before starting the RPC, mark that we're changing the directory data,
      * so that someone who does a chmod on the dir will wait until our call
      * completes.  We do this in vnode order so that we don't deadlock,
      * which makes the code a little verbose.
      */
     if (oldDscp == newDscp) {
-        /* check for identical names */
-        if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
-            osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
-                      oldDscp, newDscp);
-            code = CM_ERROR_RENAME_IDENTICAL;
-            goto done;
-        }
-
         oneDir = 1;
         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
                       CM_DIROP_FLAG_NONE, &oldDirOp);
@@ -3705,11 +3864,62 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
     if (code)
         goto done;
 
-    didEnd = 0;
+    /*
+     * The source and destination directories are now locked and no other local
+     * changes can occur.
+     *
+     * Before we permit the operation, make sure that we do not already have
+     * an object in the destination directory that has a case-insensitive match
+     * for this name UNLESS the matching object is the object we are renaming.
+     */
+    code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
+    if (code) {
+        osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
+                 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
+        rpc_skipped = 1;
+        goto post_rpc;
+    }
+
+    /* Case sensitive lookup.  If this succeeds we are done. */
+    code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
+    if (code) {
+        /*
+         * Case insensitive lookup.  If this succeeds, it could have found the
+         * same file with a name that differs only by case or it could be a
+         * different file entirely.
+         */
+        code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
+        if (code == 0) {
+            /* found a matching object with the new name */
+            if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
+                /* and they don't match so return an error */
+                osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
+                          newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
+                code = CM_ERROR_EXISTS;
+            }
+            cm_ReleaseSCache(oldTargetScp);
+            oldTargetScp = NULL;
+        } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
+            code = CM_ERROR_EXISTS;
+        } else {
+            /* The target does not exist.  Clear the error and perform the rename. */
+            code = 0;
+        }
+    } else {
+        bTargetExists = 1;
+    }
+
+    if (code) {
+        rpc_skipped = 1;
+        goto post_rpc;
+    }
 
     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
 
     /* try the RPC now */
+    InterlockedIncrement(&oldDscp->activeRPCs);
+    if (!oneDir)
+        InterlockedIncrement(&newDscp->activeRPCs);
     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
               oldDscp, newDscp);
     do {
@@ -3731,7 +3941,7 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
                             &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
+    } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, 1,
                          &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
@@ -3740,44 +3950,58 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
     else
         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
 
+  post_rpc:
     /* update the individual stat cache entries for the directories */
     if (oldDirOp.scp) {
         lock_ObtainWrite(&oldDirOp.scp->dirlock);
         oldDirOp.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainWrite(&oldDscp->rw);
-    cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
 
-    if (code == 0)
+    lock_ObtainWrite(&oldDscp->rw);
+    if (code == 0) {
         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
                        userp, reqp, CM_MERGEFLAG_DIROP);
-    lock_ReleaseWrite(&oldDscp->rw);
-
-    if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
+        if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
+            lock_ReleaseWrite(&oldDscp->rw);
+            if (bTargetExists && oneDir) {
+                diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
 #ifdef USE_BPLUS
-        diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
-        if (diropCode == CM_ERROR_INEXACT_MATCH)
-            diropCode = 0;
-        else if (diropCode == EINVAL)
+                cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
 #endif
-            diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
+            }
 
-        if (diropCode == 0) {
-            if (oneDir) {
-                diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
 #ifdef USE_BPLUS
-                cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
+            diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
+            if (diropCode == CM_ERROR_INEXACT_MATCH)
+                diropCode = 0;
+            else if (diropCode == EINVAL)
 #endif
-            }
+                diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
 
             if (diropCode == 0) {
-                diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
+                if (oneDir) {
+                    diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
+#ifdef USE_BPLUS
+                    cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
+#endif
+                }
+
+                if (diropCode == 0) {
+                    diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
 #ifdef USE_BPLUS
-                cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
+                    cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
 #endif
+                }
             }
+            lock_ObtainWrite(&oldDscp->rw);
         }
+    } else {
+        if (!rpc_skipped)
+            InterlockedDecrement(&oldDscp->activeRPCs);
     }
+    cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseWrite(&oldDscp->rw);
+
     cm_EndDirOp(&oldDirOp);
 
     /* and update it for the new one, too, if necessary */
@@ -3787,51 +4011,72 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
             newDirOp.lockType = CM_DIRLOCK_WRITE;
         }
         lock_ObtainWrite(&newDscp->rw);
-        cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
-        if (code == 0)
+        if (code == 0) {
             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
                             userp, reqp, CM_MERGEFLAG_DIROP);
-        lock_ReleaseWrite(&newDscp->rw);
 
-#if 0
-        /*
-         * The following optimization does not work.
-         * When the file server processed a RXAFS_Rename() request the
-         * FID of the object being moved between directories is not
-         * preserved.  The client does not know the new FID nor the
-         * version number of the target.  Not only can we not create
-         * the directory entry in the new directory, but we can't
-         * preserve the cached data for the file.  It must be re-read
-         * from the file server.  - jaltman, 2009/02/20
-         */
-        if (code == 0) {
-            /* we only make the local change if we successfully made
-               the change in the old directory AND there was only one
-               change in the new directory */
+            /*
+             * we only make the local change if we successfully made
+             * the change in the old directory AND there was only one
+             * change in the new directory
+             */
             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
+                lock_ReleaseWrite(&newDscp->rw);
+
+                if (bTargetExists && !oneDir) {
+                    diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
+#ifdef USE_BPLUS
+                    cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
+#endif
+                }
+
                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
 #ifdef USE_BPLUS
                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
 #endif
+                lock_ObtainWrite(&newDscp->rw);
             }
+        } else {
+            if (!rpc_skipped)
+                InterlockedIncrement(&newDscp->activeRPCs);
         }
-#endif /* 0 */
-        cm_EndDirOp(&newDirOp);
+        cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
+        lock_ReleaseWrite(&newDscp->rw);
+
+        cm_EndDirOp(&newDirOp);
     }
 
-    /*
-     * After the rename the file server has invalidated the callbacks
-     * on the file that was moved nor do we have a directory reference
-     * to it anymore.
-     */
-    lock_ObtainWrite(&oldScp->rw);
-    cm_DiscardSCache(oldScp);
-    lock_ReleaseWrite(&oldScp->rw);
+    if (code == 0) {
+        /*
+         * After the rename the file server has invalidated the callbacks
+         * on the file that was moved and destroyed any target file.
+         */
+        lock_ObtainWrite(&oldScp->rw);
+        cm_DiscardSCache(oldScp);
+        lock_ReleaseWrite(&oldScp->rw);
+
+        if (RDR_Initialized)
+            RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
+                                  oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
+
+        if (oldTargetScp) {
+            lock_ObtainWrite(&oldTargetScp->rw);
+            cm_DiscardSCache(oldTargetScp);
+            lock_ReleaseWrite(&oldTargetScp->rw);
+
+            if (RDR_Initialized)
+                RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
+                                     oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
+        }
+    }
 
   done:
     if (oldScp)
         cm_ReleaseSCache(oldScp);
 
+    if (oldTargetScp)
+        cm_ReleaseSCache(oldTargetScp);
+
     if (free_oldNamep)
         free(oldNamep);
 
@@ -4154,8 +4399,8 @@ static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
     afs_int64 int_begin;
     afs_int64 int_end;
 
-    int_begin = MAX(pos->offset, neg->offset);
-    int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
+    int_begin = max(pos->offset, neg->offset);
+    int_end = min(pos->offset+pos->length, neg->offset+neg->length);
 
     if (int_begin < int_end) {
         if (int_begin == pos->offset) {
@@ -4362,6 +4607,22 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
     AFSVolSync volSync;
     afs_uint32 reqflags = reqp->flags;
 
+    osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
+
+#if 0
+    /*
+     * The file server prior to 1.6.2 does not report an accurate value
+     * and callbacks are not issued if the lock is dropped due to expiration.
+     */
+    if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
+         (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
+    {
+        code = CM_ERROR_LOCK_NOT_GRANTED;
+        osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
+        return code;
+    }
+#endif
+
     memset(&volSync, 0, sizeof(volSync));
 
     tfid.Volume = scp->fid.volume;
@@ -4369,8 +4630,6 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
     tfid.Unique = scp->fid.unique;
     cfid = scp->fid;
 
-    osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
-
     reqp->flags |= CM_REQ_NORETRY;
     lock_ReleaseWrite(&scp->rw);
 
@@ -4384,7 +4643,7 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
                              &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+    } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
                         NULL, NULL, code));
 
     code = cm_MapRPCError(code, reqp);
@@ -4394,8 +4653,20 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
     }
 
-    lock_ObtainWrite(&scp->rw);
     reqp->flags = reqflags;
+
+    lock_ObtainWrite(&scp->rw);
+    if (code == 0) {
+        /*
+         * The file server does not return a status structure so we must
+         * locally track the file server lock count to the best of our
+         * ability.
+         */
+        if (lockType == LockWrite)
+            scp->fsLockCount = -1;
+        else
+            scp->fsLockCount++;
+    }
     return code;
 }
 
@@ -4409,6 +4680,11 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
     struct rx_connection * rxconnp;
     AFSVolSync volSync;
 
+    if (scp->flags & CM_SCACHEFLAG_DELETED) {
+        osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
+        return 0;
+    }
+
     memset(&volSync, 0, sizeof(volSync));
 
     tfid.Volume = scp->fid.volume;
@@ -4429,7 +4705,7 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+    } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
                         NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
     if (code)
@@ -4440,8 +4716,18 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
                  "CALL ReleaseLock SUCCESS");
 
     lock_ObtainWrite(&scp->rw);
+    if (code == 0) {
+        /*
+         * The file server does not return a status structure so we must
+         * locally track the file server lock count to the best of our
+         * ability.
+         */
+        scp->fsLockCount--;
+        if (scp->fsLockCount < 0)
+            scp->fsLockCount = 0;
+    }
 
-    return code;
+    return (code != CM_ERROR_BADFD ? code : 0);
 }
 
 /* called with scp->rw held.  May release it during processing, but
@@ -4839,15 +5125,15 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
             code = 0;
 
+        cm_HoldUser(userp);
+
         lock_ObtainWrite(&cm_scacheLock);
         fileLock = cm_GetFileLock();
-        lock_ReleaseWrite(&cm_scacheLock);
 #ifdef DEBUG
         fileLock->fid = scp->fid;
 #endif
         fileLock->key = key;
         fileLock->lockType = Which;
-        cm_HoldUser(userp);
         fileLock->userp = userp;
         fileLock->range = range;
         fileLock->flags = (code == 0 ? 0 :
@@ -4860,7 +5146,6 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
 
-        lock_ObtainWrite(&cm_scacheLock);
         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
         cm_HoldSCacheNoLock(scp);
         fileLock->scp = scp;
@@ -4899,99 +5184,12 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
     return code;
 }
 
-/* Called with scp->rw held */
-long cm_UnlockByKey(cm_scache_t * scp,
-                   cm_key_t key,
-                   int flags,
-                   cm_user_t * userp,
-                    cm_req_t * reqp)
+static long
+cm_IntUnlock(cm_scache_t * scp,
+             cm_user_t * userp,
+             cm_req_t *  reqp)
 {
     long code = 0;
-    cm_file_lock_t *fileLock;
-    osi_queue_t *q, *qn;
-    int n_unlocks = 0;
-
-    osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
-             scp, key.process_id, key.session_id, key.file_id);
-    osi_Log1(afsd_logp, "    flags=0x%x", flags);
-
-    lock_ObtainWrite(&cm_scacheLock);
-
-    for (q = scp->fileLocksH; q; q = qn) {
-        qn = osi_QNext(q);
-
-        fileLock = (cm_file_lock_t *)
-            ((char *) q - offsetof(cm_file_lock_t, fileq));
-
-#ifdef DEBUG
-        osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
-                 fileLock,
-                 (unsigned long) fileLock->range.offset,
-                 (unsigned long) fileLock->range.length,
-                fileLock->lockType);
-        osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
-                 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
-                 fileLock->flags);
-
-        if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
-            osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
-            osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
-                     fileLock->fid.cell,
-                     fileLock->fid.volume,
-                     fileLock->fid.vnode,
-                     fileLock->fid.unique);
-            osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
-                     fileLock->scp->fid.cell,
-                     fileLock->scp->fid.volume,
-                     fileLock->scp->fid.vnode,
-                     fileLock->scp->fid.unique);
-            osi_assertx(FALSE, "invalid fid value");
-        }
-#endif
-
-        if (!IS_LOCK_DELETED(fileLock) &&
-            cm_KeyEquals(&fileLock->key, &key, flags)) {
-            osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
-                    fileLock->range.offset,
-                    fileLock->range.length,
-                    fileLock->lockType);
-
-            if (scp->fileLocksT == q)
-                scp->fileLocksT = osi_QPrev(q);
-            osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
-
-            if (IS_LOCK_CLIENTONLY(fileLock)) {
-                scp->clientLocks--;
-            } else if (IS_LOCK_ACCEPTED(fileLock)) {
-                if (fileLock->lockType == LockRead)
-                    scp->sharedLocks--;
-                else
-                    scp->exclusiveLocks--;
-            }
-
-            fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
-
-            cm_ReleaseUser(fileLock->userp);
-            cm_ReleaseSCacheNoLock(scp);
-
-            fileLock->userp = NULL;
-            fileLock->scp = NULL;
-
-            n_unlocks++;
-        }
-    }
-
-    lock_ReleaseWrite(&cm_scacheLock);
-
-    if (n_unlocks == 0) {
-        osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
-        osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
-                 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
-
-        return 0;
-    }
-
-    osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
 
     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
@@ -5023,6 +5221,17 @@ long cm_UnlockByKey(cm_scache_t * scp,
         scp->lockDataVersion = scp->dataVersion;
         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
 
+        /* before we downgrade, make sure that we have enough
+           permissions to get the read lock. */
+        code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
+        if (code != 0) {
+
+            osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
+
+            code = 0;
+            goto done;
+        }
+
         code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code) {
@@ -5060,8 +5269,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
                 cm_LockMarkSCacheLost(scp);
             }
 
-        /* failure here has no bearing on the return value of
-           cm_Unlock() */
+        /* failure here has no bearing on the return value of cm_Unlock() */
         code = 0;
 
     } else if (scp->serverLock != (-1) &&
@@ -5070,8 +5278,12 @@ long cm_UnlockByKey(cm_scache_t * scp,
         /* The serverLock should be released entirely */
 
         if (scp->serverLock == LockWrite) {
+            osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
+
             /* Make sure there are no dirty buffers left. */
             code = cm_FSync(scp, userp, reqp, TRUE);
+        } else {
+            osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
         }
 
         code = cm_IntReleaseLock(scp, userp, reqp);
@@ -5080,9 +5292,102 @@ long cm_UnlockByKey(cm_scache_t * scp,
             scp->serverLock = (-1);
     }
 
- done:
+  done:
+    return code;
+}
+/* Called with scp->rw held */
+long cm_UnlockByKey(cm_scache_t * scp,
+                   cm_key_t key,
+                   afs_uint32 flags,
+                   cm_user_t * userp,
+                   cm_req_t * reqp)
+{
+    long code = 0;
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q, *qn;
+    int n_unlocks = 0;
+
+    osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
+             scp, key.process_id, key.session_id, key.file_id);
+    osi_Log1(afsd_logp, "    flags=0x%x", flags);
+
+    lock_ObtainWrite(&cm_scacheLock);
+
+    for (q = scp->fileLocksH; q; q = qn) {
+        qn = osi_QNext(q);
+
+        fileLock = (cm_file_lock_t *)
+            ((char *) q - offsetof(cm_file_lock_t, fileq));
+
+#ifdef DEBUG
+        osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
+                 fileLock,
+                 (unsigned long) fileLock->range.offset,
+                 (unsigned long) fileLock->range.length,
+                fileLock->lockType);
+        osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
+                 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
+                 fileLock->flags);
+
+        if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+            osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
+            osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+                     fileLock->fid.cell,
+                     fileLock->fid.volume,
+                     fileLock->fid.vnode,
+                     fileLock->fid.unique);
+            osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+                     fileLock->scp->fid.cell,
+                     fileLock->scp->fid.volume,
+                     fileLock->scp->fid.vnode,
+                     fileLock->scp->fid.unique);
+            osi_assertx(FALSE, "invalid fid value");
+        }
+#endif
+
+        if (!IS_LOCK_DELETED(fileLock) &&
+            cm_KeyEquals(&fileLock->key, &key, flags)) {
+            osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
+                    fileLock->range.offset,
+                    fileLock->range.length,
+                    fileLock->lockType);
+
+            osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
+
+            if (IS_LOCK_CLIENTONLY(fileLock)) {
+                scp->clientLocks--;
+            } else if (IS_LOCK_ACCEPTED(fileLock)) {
+                if (fileLock->lockType == LockRead)
+                    scp->sharedLocks--;
+                else
+                    scp->exclusiveLocks--;
+            }
+
+            fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
+
+            cm_ReleaseUser(fileLock->userp);
+            cm_ReleaseSCacheNoLock(scp);
+
+            fileLock->userp = NULL;
+            fileLock->scp = NULL;
+
+            n_unlocks++;
+        }
+    }
+
+    lock_ReleaseWrite(&cm_scacheLock);
+
+    if (n_unlocks == 0) {
+        osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
+        osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
+                 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+
+        return 0;
+    }
 
+    code = cm_IntUnlock(scp, userp, reqp);
     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
+
     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
              (int)(signed char) scp->serverLock);
@@ -5090,6 +5395,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
     return code;
 }
 
+/* Called with scp->rw held */
 long cm_Unlock(cm_scache_t *scp,
                unsigned char sLockType,
                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
@@ -5107,7 +5413,7 @@ long cm_Unlock(cm_scache_t *scp,
     int lock_found  = 0;
     LARGE_INTEGER RangeEnd;
 
-    osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
+    osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
              key.process_id, key.session_id, key.file_id, flags);
@@ -5175,8 +5481,6 @@ long cm_Unlock(cm_scache_t *scp,
 
     /* discard lock record */
     lock_ConvertRToW(&cm_scacheLock);
-    if (scp->fileLocksT == q)
-        scp->fileLocksT = osi_QPrev(q);
     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
 
     /*
@@ -5194,122 +5498,29 @@ long cm_Unlock(cm_scache_t *scp,
     }
 
     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
+
     if (userp != NULL) {
         cm_ReleaseUser(fileLock->userp);
     } else {
         userp = fileLock->userp;
         release_userp = TRUE;
     }
-    fileLock->userp = NULL;
     cm_ReleaseSCacheNoLock(scp);
+    fileLock->userp = NULL;
     fileLock->scp = NULL;
     lock_ReleaseWrite(&cm_scacheLock);
 
-    if (!SERVERLOCKS_ENABLED(scp)) {
-        osi_Log0(afsd_logp, "   Skipping server locks for scp");
-        goto done;
-    }
-
-    /* Ideally we would go through the rest of the locks to determine
-     * if one or more locks that were formerly in WAITUNLOCK can now
-     * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
-     * scp->sharedLocks accordingly.  However, the retrying of locks
-     * in that manner is done cm_RetryLock() manually.
-     */
-
-    if (scp->serverLock == LockWrite &&
-        scp->exclusiveLocks == 0 &&
-        scp->sharedLocks > 0) {
-
-        /* The serverLock should be downgraded to LockRead */
-        osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
-
-        /* Make sure there are no dirty buffers left. */
-        code = cm_FSync(scp, userp, reqp, TRUE);
-
-        /* Since we already had a lock, we assume that there is a
-           valid server lock. */
-        scp->lockDataVersion = scp->dataVersion;
-        osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
-
-        /* before we downgrade, make sure that we have enough
-           permissions to get the read lock. */
-        code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
-        if (code != 0) {
-
-            osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
-
-            code = 0;
-            goto done;
-        }
-
-        code = cm_IntReleaseLock(scp, userp, reqp);
-
-        if (code) {
-            /* so we couldn't release it.  Just let the lock be for now */
-            code = 0;
-            goto done;
-        } else {
-            scp->serverLock = -1;
-        }
-
-        code = cm_IntSetLock(scp, userp, LockRead, reqp);
-
-        if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
-            scp->serverLock = LockRead;
-        } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
-            /* Lost a race.  We obtained a new lock, but that is
-               meaningless since someone modified the file
-               inbetween. */
-
-            osi_Log0(afsd_logp,
-                     "Data version mismatch while downgrading lock");
-            osi_Log2(afsd_logp,
-                     "  Data versions before=%I64d, after=%I64d",
-                     scp->lockDataVersion,
-                     scp->dataVersion);
-
-            code = cm_IntReleaseLock(scp, userp, reqp);
-
-            scp->serverLock = -1;
-            code = CM_ERROR_INVAL;
-        }
-
-        if (code != 0 &&
-            (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
-                (scp->serverLock == -1)) {
-                /* Oopsie */
-                cm_LockMarkSCacheLost(scp);
-            }
-
-        /* failure here has no bearing on the return value of
-           cm_Unlock() */
-        code = 0;
-
-    } else if (scp->serverLock != (-1) &&
-              scp->exclusiveLocks == 0 &&
-              scp->sharedLocks == 0) {
-        /* The serverLock should be released entirely */
-
-        if (scp->serverLock == LockWrite) {
-            /* Make sure there are no dirty buffers left. */
-            code = cm_FSync(scp, userp, reqp, TRUE);
-        }
-
-        code = cm_IntReleaseLock(scp, userp, reqp);
-
-        if (code == 0) {
-            scp->serverLock = (-1);
-        }
-    }
+    code = cm_IntUnlock(scp, userp, reqp);
 
     if (release_userp) {
         cm_ReleaseUser(userp);
         release_userp = FALSE;
     }
 
-    if (!exact_match)
+    if (!exact_match) {
+        osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
         goto try_again;         /* might be more than one lock in the range */
+    }
 
  done:
 
@@ -5381,7 +5592,21 @@ void cm_CheckLocks()
        code = -1;
 
         if (IS_LOCK_DELETED(fileLock)) {
+            cm_user_t *userp = fileLock->userp;
+            cm_scache_t *scp = fileLock->scp;
+            fileLock->userp = NULL;
+            fileLock->scp = NULL;
+
+            if (scp && userp) {
+                lock_ReleaseWrite(&cm_scacheLock);
+                lock_ObtainWrite(&scp->rw);
+                code = cm_IntUnlock(scp, userp, &req);
+                lock_ReleaseWrite(&scp->rw);
 
+                cm_ReleaseUser(userp);
+                lock_ObtainWrite(&cm_scacheLock);
+                cm_ReleaseSCacheNoLock(scp);
+            }
             osi_QRemove(&cm_allFileLocks, q);
             cm_PutFileLock(fileLock);
 
@@ -5474,7 +5699,7 @@ void cm_CheckLocks()
                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
 
                     } while (cm_Analyze(connp, userp, &req,
-                                        &cfid, &volSync, NULL, NULL,
+                                        &cfid, 1, &volSync, NULL, NULL,
                                         code));
 
                     code = cm_MapRPCError(code, &req);
@@ -5483,6 +5708,7 @@ void cm_CheckLocks()
 
                     if (code) {
                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
+                        scp->fsLockCount = 0;
                     } else {
                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
                         scp->lockDataVersion = scp->dataVersion;
@@ -5844,8 +6070,6 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
   handleCode:
     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
        lock_ObtainWrite(&cm_scacheLock);
-        if (scp->fileLocksT == &oldFileLock->fileq)
-            scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
        lock_ReleaseWrite(&cm_scacheLock);
     }
@@ -5869,7 +6093,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
     return code;
 }
 
-cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
+cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
 {
     cm_key_t key;
 
@@ -5883,7 +6107,7 @@ cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16
 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
 {
     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
-        ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
+        ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
 }
 
 void cm_ReleaseAllLocks(void)