windows-dbg-refcount-20061016
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index 8123c0f..ccbd481 100644 (file)
 #include <afs/param.h>
 #include <afs/stds.h>
 
-#ifndef DJGPP
 #include <windows.h>
 #include <winsock2.h>
-#endif /* !DJGPP */
 #include <stddef.h>
 #include <malloc.h>
 #include <string.h>
@@ -33,7 +31,7 @@
 extern void afsi_log(char *pattern, ...);
 #endif
 
-int cm_enableServerLocks = 0;
+int cm_enableServerLocks = 1;
 
 /*
  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
@@ -265,7 +263,8 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
 
     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
                       CM_SCACHESYNC_GETSTATUS
-                      | CM_SCACHESYNC_NEEDCALLBACK);
+                     | CM_SCACHESYNC_NEEDCALLBACK
+                     | CM_SCACHESYNC_LOCK);
 
     if (code == 0 && 
         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
@@ -315,8 +314,15 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
                }
            }
         }
+
+    } else if (code != 0) {
+        goto _done;
     }
 
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
+
     lock_ReleaseMutex(&scp->mx);
 
     return code;
@@ -346,7 +352,8 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
 
     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
                       CM_SCACHESYNC_GETSTATUS
-                      | CM_SCACHESYNC_NEEDCALLBACK);
+                     | CM_SCACHESYNC_NEEDCALLBACK
+                     | CM_SCACHESYNC_LOCK);
 
     /*
      * If the open will fail because the volume is readonly, then we will
@@ -356,7 +363,8 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
      */
     if (code == CM_ERROR_READONLY)
         code = CM_ERROR_NOACCESS;
-    else if (code == 0 &&
+
+    if (code == 0 &&
              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
              scp->fileType == CM_SCACHETYPE_FILE) {
         cm_key_t key;
@@ -403,8 +411,13 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
                }
            }
         }
+    } else if (code != 0) {
+        goto _done;
     }
 
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
     lock_ReleaseMutex(&scp->mx);
 
     return code;
@@ -437,8 +450,8 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
     /* First check permissions */
     lock_ObtainMutex(&dscp->mx);
     code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
-                      CM_SCACHESYNC_GETSTATUS
-                      | CM_SCACHESYNC_NEEDCALLBACK);
+                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     lock_ReleaseMutex(&dscp->mx);
     if (code)
         return code;
@@ -474,6 +487,7 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
         lock_ReleaseMutex(&scp->mx);
         lock_ObtainMutex(&bufferp->mx);
         lock_ObtainMutex(&scp->mx);
+       cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
         if (code)
             break;
     }
@@ -649,16 +663,16 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
             }
 
 #ifdef AFSIFS
-                       /* for the IFS version, we bulkstat the dirents because this
-                          routine is used in place of smb_ReceiveCoreSearchDir.  our
-                          other option is to modify smb_ReceiveCoreSearchDir itself, 
-                          but this seems to be the proper use for cm_ApplyDir. */
+           /* for the IFS version, we bulkstat the dirents because this
+              routine is used in place of smb_ReceiveCoreSearchDir.  our
+              other option is to modify smb_ReceiveCoreSearchDir itself, 
+              but this seems to be the proper use for cm_ApplyDir. */
             lock_ObtainMutex(&scp->mx);
             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
                  && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
             {
                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
-                cm_TryBulkStat(scp, &thyper, userp, reqp);
+                code = cm_TryBulkStat(scp, &thyper, userp, reqp);
                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
                 scp->bulkStatProgress = thyper;
             }
@@ -680,6 +694,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                     lock_ReleaseMutex(&scp->mx);
                     break;
                 }
+               cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
                                 
                 if (cm_HaveBuffer(scp, bufferp, 1)) {
                     lock_ReleaseMutex(&scp->mx);
@@ -815,7 +830,7 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
         return 0;
 
     sp->found = 1;
-    if(!sp->caseFold) 
+    if (!sp->caseFold) 
         sp->ExactFound = 1;
 
     if (!sp->caseFold || matchName == shortName) {
@@ -898,6 +913,8 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
         if (code) {
             goto done;
         }
+       cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+
 
         if (cm_HaveBuffer(scp, bufp, 0)) 
             break;
@@ -969,7 +986,8 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
     /* parse the volume name */
     mpNamep = scp->mountPointStringp;
-    osi_assert(mpNamep[0]);
+    if (!mpNamep[0])
+       return CM_ERROR_NOSUCHPATH;
     tlen = (int)strlen(scp->mountPointStringp);
     mtType = *scp->mountPointStringp;
     cellNamep = malloc(tlen);
@@ -1078,6 +1096,9 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
             return CM_ERROR_NOSUCHVOLUME;
         rock.fid = dscp->dotdotFid;
         goto haveFid;
+    } else if (strcmp(namep, ".") == 0) {
+       rock.fid = dscp->fid;
+       goto haveFid;
     }
 
     memset(&rock, 0, sizeof(rock));
@@ -1168,6 +1189,7 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
         cm_ReleaseSCache(tscp);
         return code;
     }
+    cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     /* tscp is now locked */
 
     if (!(flags & CM_FLAG_NOMOUNTCHASE)
@@ -1197,7 +1219,7 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
         /* lock the directory entry to prevent racing callback revokes */
         lock_ObtainMutex(&dscp->mx);
-        if ( dscp->cbServerp && dscp->cbExpires )
+        if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
             cm_dnlcEnter(dscp, namep, tscp);
         lock_ReleaseMutex(&dscp->mx);
     }
@@ -1234,13 +1256,23 @@ int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
     return 1;
 }   
 
+#ifdef DEBUG_REFCOUNT
+long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
+               cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
+#else
 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
                cm_req_t *reqp, cm_scache_t **outpScpp)
+#endif
 {
     long code;
     char tname[256];
     int sysNameIndex = 0;
-    cm_scache_t *scp = 0;
+    cm_scache_t *scp = NULL;
+
+#ifdef DEBUG_REFCOUNT
+    afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
+    osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
+#endif
 
     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
         if (flags & CM_FLAG_CHECKPATH)
@@ -1253,16 +1285,27 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
         if (code > 0) {
             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
+#ifdef DEBUG_REFCOUNT
+           afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
+           osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
+#endif
+
             if (code == 0) {
                 *outpScpp = scp;
                 return 0;
             }
             if (scp) {
                 cm_ReleaseSCache(scp);
-                scp = 0;
+                scp = NULL;
             }
         } else {
-            return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
+            code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
+#ifdef DEBUG_REFCOUNT
+           afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
+           osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
+#endif
+           *outpScpp = scp;
+           return code;
         }
     }
 
@@ -1328,6 +1371,13 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     cm_SyncOpDone(dscp, NULL, sflags);
     if (code == 0) 
         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
+       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_ReleaseMutex(&dscp->mx);
 
     return code;
@@ -1359,6 +1409,8 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
                 buf_Release(bufp);
                 return code;
             }
+           cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+
             if (cm_HaveBuffer(linkScp, bufp, 0)) 
                 break;
 
@@ -1484,9 +1536,14 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
     lock_ReleaseMutex(&linkScp->mx);
     return code;
 }
-
+#ifdef DEBUG_REFCOUNT
+long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
+               cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp, 
+              char * file, long line)
+#else
 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
+#endif
 {
     long code;
     char *tp;                  /* ptr moving through input buffer */
@@ -1507,6 +1564,13 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     int extraFlag;             /* avoid chasing mt pts for dir cmd */
     int phase = 1;             /* 1 = tidPathp, 2 = pathp */
 
+#ifdef DEBUG_REFCOUNT
+    afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
+    osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
+             rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>", 
+             flags);
+#endif
+
     tp = tidPathp;
     if (tp == NULL) {
         tp = pathp;
@@ -1520,7 +1584,8 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     tscp = rootSCachep;
     cm_HoldSCache(tscp);
     symlinkCount = 0;
-    dirScp = 0;
+    dirScp = NULL;
+
 
     while (1) {
         tc = *tp++;
@@ -1555,34 +1620,45 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                  * is a symlink, we have more to do.
                  */
                 *cp++ = 0;     /* add null termination */
-                extraFlag = 0;
-                if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
-                    extraFlag = CM_FLAG_NOMOUNTCHASE;
-                code = cm_Lookup(tscp, component,
-                                  flags | extraFlag,
-                                  userp, reqp, &nscp);
-                if (code) {
-                    cm_ReleaseSCache(tscp);
-                    if (dirScp)
+               if (!strcmp(".",component)) {
+                    code = 0;
+                    if (dirScp) {
                         cm_ReleaseSCache(dirScp);
-                    if (psp) 
-                        cm_FreeSpace(psp);
-                    if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
-                        return CM_ERROR_NOSUCHPATH;
-                    else
-                        return code;
-                }
-                haveComponent = 0;     /* component done */
-                if (dirScp)
-                    cm_ReleaseSCache(dirScp);
-                dirScp = tscp;         /* for some symlinks */
-                tscp = nscp;           /* already held */
-                nscp = 0;
-                if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
+                        dirScp = NULL;
+                    }
+                    break;
+               }
+               extraFlag = 0;
+               if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
+                   extraFlag = CM_FLAG_NOMOUNTCHASE;
+               code = cm_Lookup(tscp, component,
+                                 flags | extraFlag,
+                                 userp, reqp, &nscp);
+               if (code) {
+                   cm_ReleaseSCache(tscp);
+                   if (dirScp)
+                       cm_ReleaseSCache(dirScp);
+                   if (psp) 
+                       cm_FreeSpace(psp);
+                   if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
+                       osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
+                       return CM_ERROR_NOSUCHPATH;
+                   } else {
+                       osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
+                       return code;
+                   }
+               }       
+               haveComponent = 0;      /* component done */
+               if (dirScp)
+                   cm_ReleaseSCache(dirScp);
+               dirScp = tscp;          /* for some symlinks */
+               tscp = nscp;            /* already held */
+               nscp = NULL;
+               if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
                     code = 0;
                     if (dirScp) {
                         cm_ReleaseSCache(dirScp);
-                        dirScp = 0;
+                        dirScp = NULL;
                     }
                     break;
                 }
@@ -1597,25 +1673,28 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                 if (code) {
                     lock_ReleaseMutex(&tscp->mx);
                     cm_ReleaseSCache(tscp);
-                    tscp = 0;
+                    tscp = NULL;
                     if (dirScp) {
                         cm_ReleaseSCache(dirScp);
-                        dirScp = 0;
+                        dirScp = NULL;
                     }
                     break;
                 }
+               cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
                     /* this is a symlink; assemble a new buffer */
                     lock_ReleaseMutex(&tscp->mx);
                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
                         cm_ReleaseSCache(tscp);
-                        tscp = 0;
+                        tscp = NULL;
                         if (dirScp) {
                             cm_ReleaseSCache(dirScp);
-                            dirScp = 0;
+                            dirScp = NULL;
                         }
                         if (psp) 
                             cm_FreeSpace(psp);
+                       osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
                         return CM_ERROR_TOO_MANY_SYMLINKS;
                     }
                     if (tc == 0) 
@@ -1626,10 +1705,10 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     if (code) {
                         /* something went wrong */
                         cm_ReleaseSCache(tscp);
-                        tscp = 0;
+                        tscp = NULL;
                         if (dirScp) {
                             cm_ReleaseSCache(dirScp);
-                            dirScp = 0;
+                            dirScp = NULL;
                         }
                         break;
                     }
@@ -1648,7 +1727,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     tp = psp->data;
                     cm_ReleaseSCache(tscp);
                     tscp = linkScp;
-                    linkScp = 0;
+                    linkScp = NULL;
                     /* already held
                      * by AssembleLink
                      * now, if linkScp is null, that's
@@ -1661,7 +1740,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                      */
                     if (tscp == NULL) {
                         tscp = dirScp;
-                        dirScp = 0;
+                        dirScp = NULL;
                     }
                 } else {
                     /* not a symlink, we may be done */
@@ -1674,7 +1753,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                         }
                         if (dirScp) {
                             cm_ReleaseSCache(dirScp);
-                            dirScp = 0;
+                            dirScp = NULL;
                         }
                         code = 0;
                         break;
@@ -1682,7 +1761,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                 }
                 if (dirScp) {
                     cm_ReleaseSCache(dirScp);
-                    dirScp = 0;
+                    dirScp = NULL;
                 }
             } /* end of a component */
             else 
@@ -1699,6 +1778,11 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
         *outScpp = tscp;
     else if (tscp)
         cm_ReleaseSCache(tscp);
+
+#ifdef DEBUG_REFCOUNT
+    afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
+#endif
+    osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
     return code;
 }
 
@@ -1757,7 +1841,7 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
  * check anyway, but we want to minimize the chance that we have to leave stuff
  * unstat'd.
  */
-#define CM_BULKMAX             128
+#define CM_BULKMAX             (3 * AFSCBMAX)
 
 /* rock for bulk stat calls */
 typedef struct cm_bulkStat {
@@ -1817,7 +1901,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     if (tscp) {
         if (lock_TryMutex(&tscp->mx)) {
             /* we have an entry that we can look at */
-            if (cm_HaveCallback(tscp)) {
+            if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && 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.
@@ -1855,11 +1939,12 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
  * calls on all undeleted files in the page of the directory specified.
  */
-void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
-                     cm_req_t *reqp)
+afs_int32
+cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
+              cm_req_t *reqp)
 {
     long code;
-    cm_bulkStat_t bb;  /* this is *BIG*, probably 12K or so;
+    cm_bulkStat_t bb;  /* this is *BIG*, probably 16K or so;
                          * watch for stack problems */
     AFSCBFids fidStruct;
     AFSBulkStats statStruct;
@@ -1874,13 +1959,14 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     cm_scache_t *scp;
     cm_fid_t tfid;
     struct rx_connection * callp;
+    int inlinebulk = 0;                /* Did we use InlineBulkStatus RPC or not? */
 
     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
 
     /* should be on a buffer boundary */
     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
 
-    bb.counter = 0;
+    memset(&bb, 0, sizeof(bb));
     bb.bufOffset = *offsetp;
 
     lock_ReleaseMutex(&dscp->mx);
@@ -1890,7 +1976,7 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     /* if we failed, bail out early */
     if (code && code != CM_ERROR_STOPNOW) {
         lock_ObtainMutex(&dscp->mx);
-        return;
+        return code;
     }
 
     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
@@ -1917,18 +2003,29 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
                 continue;
 
             callp = cm_GetRxConn(connp);
-            code = RXAFS_BulkStatus(callp, &fidStruct,
+           if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
+               code = RXAFS_InlineBulkStatus(callp, &fidStruct,
                                      &statStruct, &callbackStruct, &volSync);
+               if (code == RXGEN_OPCODE) {
+                   cm_SetServerNoInlineBulk(connp->serverp, 0);
+               } else {
+                   inlinebulk = 1;
+               }
+           }
+           if (!inlinebulk) {
+               code = RXAFS_BulkStatus(callp, &fidStruct,
+                                       &statStruct, &callbackStruct, &volSync);
+           }
             rx_PutConnection(callp);
 
         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
                              &volSync, NULL, &cbReq, code));
         code = cm_MapRPCError(code, reqp);
-
         if (code)
-            osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
+            osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
+                     inlinebulk ? "Inline" : "", code);
         else
-            osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
+            osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
 
         /* may as well quit on an error, since we're not going to do
          * much better on the next immediate call, either.
@@ -1963,7 +2060,7 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
              * Right now, be pretty conservative: if there's a
              * callback or a pending call, skip it.
              */
-            if (scp->cbServerp == NULL
+            if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
                  && !(scp->flags &
                        (CM_SCACHEFLAG_FETCHING
                          | CM_SCACHEFLAG_STORING
@@ -1971,8 +2068,7 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
                 cm_EndCallbackGrantingCall(scp, &cbReq,
                                             &bb.callbacks[j],
                                             CM_CALLBACK_MAINTAINCOUNT);
-                cm_MergeStatus(scp, &bb.stats[j], &volSync,
-                                userp, 0);
+                cm_MergeStatus(scp, &bb.stats[j], &volSync, userp, 0);
             }       
             lock_ReleaseMutex(&scp->mx);
             cm_ReleaseSCache(scp);
@@ -1984,7 +2080,23 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
         filex += filesThisCall;
     }  /* while there are still more files to process */
     lock_ObtainMutex(&dscp->mx);
-    osi_Log0(afsd_logp, "END cm_TryBulkStat");
+
+#if 0
+    /* If we did the InlineBulk RPC pull out the return code */
+    if (inlinebulk) {
+       if ((&bb.stats[0])->errorCode) {
+           cm_Analyze(NULL /*connp was released by the previous cm_Analyze */, 
+                       userp, reqp, &dscp->fid, &volSync, NULL, NULL, (&bb.stats[0])->errorCode);
+           code = cm_MapRPCError((&bb.stats[0])->errorCode, reqp);
+       }
+    } else
+#endif 
+    { 
+       code = 0;
+    }
+
+    osi_Log1(afsd_logp, "END cm_TryBulkStat code = 0x%x", code);
+    return code;
 }       
 
 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
@@ -2045,7 +2157,8 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     if (code) 
         goto done;
-        
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
     if (scp->fileType != CM_SCACHETYPE_FILE) {
         code = CM_ERROR_ISDIR;
         goto done;
@@ -2104,6 +2217,10 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
     /* done successfully */
     code = 0;
 
+    cm_SyncOpDone(scp, NULL, 
+                  CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
+                  | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
+
   done:
     lock_ReleaseMutex(&scp->mx);
     lock_ReleaseWrite(&scp->bufCreateLock);
@@ -2116,7 +2233,6 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
                 cm_req_t *reqp)
 {
     long code;
-    int flags;
     AFSFetchStatus afsOutStatus;
     AFSVolSync volSync;
     cm_conn_t *connp;
@@ -2128,11 +2244,13 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     if (attrp->mask & CM_ATTRMASK_LENGTH)
         return cm_SetLength(scp, &attrp->length, userp, reqp);
 
-    flags = CM_SCACHESYNC_STORESTATUS;
-
     lock_ObtainMutex(&scp->mx);
     /* otherwise, we have to make an RPC to get the status */
     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
+    if (code) {
+       lock_ReleaseMutex(&scp->mx);
+        return code;
+    }
 
     /* make the attr structure */
     cm_StatusFromAttr(&afsInStatus, scp, attrp);
@@ -2140,10 +2258,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     tfid.Volume = scp->fid.volume;
     tfid.Vnode = scp->fid.vnode;
     tfid.Unique = scp->fid.unique;
-
-    lock_ReleaseMutex(&scp->mx);
-    if (code) 
-        return code;
+       lock_ReleaseMutex(&scp->mx);
 
     /* now make the RPC */
     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
@@ -2269,6 +2384,7 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         code = cm_GetSCache(&newFid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainMutex(&scp->mx);
+           scp->creator = userp;               /* remember who created it */
             if (!cm_HaveCallback(scp)) {
                 cm_MergeStatus(scp, &newFileStatus, &volSync,
                                 userp, 0);
@@ -2297,12 +2413,17 @@ long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     lock_ReleaseWrite(&scp->bufCreateLock);
     if (code == 0) {
         lock_ObtainMutex(&scp->mx);
-        scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
-                         | CM_SCACHEFLAG_OUTOFSPACE);
+
         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
                           | CM_SCACHEMASK_CLIENTMODTIME
                           | CM_SCACHEMASK_LENGTH))
             code = cm_StoreMini(scp, userp, reqp);
+
+        if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
+           code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
+           scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
+       }
+
         lock_ReleaseMutex(&scp->mx);
     }
     return code;
@@ -2706,7 +2827,7 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
                 lock_ReleaseMutex(&newDscp->mx);
                 if (code) {
                     /* cleanup first one */
-                    lock_ObtainMutex(&newDscp->mx);
+                    lock_ObtainMutex(&oldDscp->mx);
                     cm_SyncOpDone(oldDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
                     lock_ReleaseMutex(&oldDscp->mx);
@@ -3087,7 +3208,13 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
 /* unsafe */
 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
 
-#define SERVERLOCKS_ENABLED(scp) (!(((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks))
+#if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
+#define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
+#else
+#define SCP_SUPPORTS_BRLOCKS(scp) (1)
+#endif
+
+#define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
 
 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
 {
@@ -3097,11 +3224,11 @@ static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
     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) {
+    if (int_begin < int_end) {
+        if (int_begin == pos->offset) {
             pos->length = pos->offset + pos->length - int_end;
             pos->offset = int_end;
-        } else if(int_end == pos->offset + pos->length) {
+        } else if (int_end == pos->offset + pos->length) {
             pos->length = int_begin - pos->offset;
         }
 
@@ -3146,20 +3273,20 @@ long cm_LockCheckRead(cm_scache_t *scp,
 
     lock_ObtainRead(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
+    for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
         fileLock = 
             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
 
         if (INTERSECT_RANGE(range, fileLock->range)) {
-            if(IS_LOCK_ACTIVE(fileLock)) {
-                if(fileLock->key == key) {
+            if (IS_LOCK_ACTIVE(fileLock)) {
+                if (fileLock->key == key) {
 
                     /* If there is an active lock for this client, it
                        is safe to substract ranges.*/
                     cm_LockRangeSubtract(&range, &fileLock->range);
                     substract_ranges = TRUE;
                 } else {
-                    if(fileLock->lockType != LockRead) {
+                    if (fileLock->lockType != LockRead) {
                         code = CM_ERROR_LOCK_CONFLICT;
                         break;
                     }
@@ -3170,10 +3297,10 @@ long cm_LockCheckRead(cm_scache_t *scp,
                        is, unless we have already seen an active lock
                        belonging to the client, in which case there
                        can't be any lost locks for this client. */
-                    if(substract_ranges)
+                    if (substract_ranges)
                         cm_LockRangeSubtract(&range, &fileLock->range);
                 }
-            } else if(IS_LOCK_LOST(fileLock) &&
+            } else if (IS_LOCK_LOST(fileLock) &&
                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
                 code = CM_ERROR_BADFD;
                 break;
@@ -3227,14 +3354,14 @@ long cm_LockCheckWrite(cm_scache_t *scp,
 
     lock_ObtainRead(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
+    for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
         fileLock = 
             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
 
-        if(INTERSECT_RANGE(range, fileLock->range)) {
-            if(IS_LOCK_ACTIVE(fileLock)) {
-                if(fileLock->key == key) {
-                    if(fileLock->lockType == LockWrite) {
+        if (INTERSECT_RANGE(range, fileLock->range)) {
+            if (IS_LOCK_ACTIVE(fileLock)) {
+                if (fileLock->key == key) {
+                    if (fileLock->lockType == LockWrite) {
 
                         /* if there is an active lock for this client, it
                            is safe to substract ranges */
@@ -3247,7 +3374,7 @@ long cm_LockCheckWrite(cm_scache_t *scp,
                     code = CM_ERROR_LOCK_CONFLICT;
                     break;
                 }
-            } else if(IS_LOCK_LOST(fileLock)) {
+            } else if (IS_LOCK_LOST(fileLock)) {
                 code = CM_ERROR_BADFD;
                 break;
             }
@@ -3276,7 +3403,7 @@ static cm_file_lock_t * cm_GetFileLock(void) {
     cm_file_lock_t * l;
 
     l = (cm_file_lock_t *) cm_freeFileLocks;
-    if(l) {
+    if (l) {
         osi_QRemove(&cm_freeFileLocks, &l->q);
     } else {
         l = malloc(sizeof(cm_file_lock_t));
@@ -3293,6 +3420,159 @@ static void cm_PutFileLock(cm_file_lock_t *l) {
     osi_QAdd(&cm_freeFileLocks, &l->q);
 }
 
+/* called with scp->mx held.  May release it during processing, but
+   leaves it held on exit. */
+long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
+                   cm_req_t * reqp) {
+    long code = 0;
+    AFSFid tfid;
+    cm_fid_t cfid;
+    cm_conn_t * connp;
+    struct rx_connection * callp;
+    AFSVolSync volSync;
+
+    tfid.Volume = scp->fid.volume;
+    tfid.Vnode = scp->fid.vnode;
+    tfid.Unique = scp->fid.unique;
+    cfid = scp->fid;
+
+    osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
+
+    lock_ReleaseMutex(&scp->mx);
+
+    do {
+        code = cm_Conn(&cfid, userp, reqp, &connp);
+        if (code) 
+            break;
+
+        callp = cm_GetRxConn(connp);
+        code = RXAFS_SetLock(callp, &tfid, lockType,
+                             &volSync);
+        rx_PutConnection(callp);
+
+    } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+                        NULL, NULL, code));
+
+    code = cm_MapRPCError(code, reqp);
+    if (code) {
+        osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
+    } else {
+        osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
+    }
+
+    lock_ObtainMutex(&scp->mx);
+
+    return code;
+}
+
+/* called with scp->mx held.  Releases it during processing */
+long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
+                       cm_req_t * reqp) {
+    long code = 0;
+    AFSFid tfid;
+    cm_fid_t cfid;
+    cm_conn_t * connp;
+    struct rx_connection * callp;
+    AFSVolSync volSync;
+
+    tfid.Volume = scp->fid.volume;
+    tfid.Vnode = scp->fid.vnode;
+    tfid.Unique = scp->fid.unique;
+    cfid = scp->fid;
+
+    lock_ReleaseMutex(&scp->mx);
+
+    osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
+
+    do {
+        code = cm_Conn(&cfid, userp, reqp, &connp);
+        if (code) 
+            break;
+
+        callp = cm_GetRxConn(connp);
+        code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
+        rx_PutConnection(callp);
+
+    } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+                        NULL, NULL, code));
+    code = cm_MapRPCError(code, reqp);
+    if (code)
+        osi_Log1(afsd_logp,
+                 "CALL ReleaseLock FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp,
+                 "CALL ReleaseLock SUCCESS");
+        
+    lock_ObtainMutex(&scp->mx);
+
+    return code;
+}
+
+/* called with scp->mx held.  May release it during processing, but
+   will exit with lock held.
+
+   This will return:
+
+   - 0 if the user has permission to get the specified lock for the scp
+
+   - CM_ERROR_NOACCESS if not
+
+   Any other error from cm_SyncOp will be sent down untranslated.
+*/
+long cm_LockCheckPerms(cm_scache_t * scp,
+                       int lock_type,
+                       cm_user_t * userp,
+                       cm_req_t * reqp)
+{
+    long rights = 0;
+    long code = 0;
+
+    /* lock permissions are slightly tricky because of the 'i' bit.
+       If the user has PRSFS_LOCK, she can read-lock the file.  If the
+       user has PRSFS_WRITE, she can write-lock the file.  However, if
+       the user has PRSFS_INSERT, then she can write-lock new files,
+       but not old ones.  Since we don't have information about
+       whether a file is new or not, we assume that if the user owns
+       the scp, then she has the permissions that are granted by
+       PRSFS_INSERT. */
+
+    osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
+             scp, lock_type, userp);
+
+    if (lock_type == LockRead)
+        rights |= PRSFS_LOCK;
+    else if (lock_type == LockWrite)
+        rights |= PRSFS_WRITE;
+    else {
+        /* hmmkay */
+        osi_assert(FALSE);
+        return 0;
+    }
+
+    code = cm_SyncOp(scp, NULL, userp, reqp, rights,
+                     CM_SCACHESYNC_GETSTATUS |
+                     CM_SCACHESYNC_NEEDCALLBACK);
+
+    if (code == CM_ERROR_NOACCESS &&
+        lock_type == LockWrite &&
+       scp->creator == userp) {
+        /* check for PRSFS_INSERT. */
+
+        code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
+                         CM_SCACHESYNC_GETSTATUS |
+                         CM_SCACHESYNC_NEEDCALLBACK);
+
+       if (code == CM_ERROR_NOACCESS)
+           osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
+
+    return code;
+}
+
 /* called with scp->mx held */
 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
@@ -3302,14 +3582,11 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 {
     long code = 0;
     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
-    struct rx_connection * callp;
     cm_range_t range;
     int wait_unlock = FALSE;
+    int force_client_lock = FALSE;
 
     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
@@ -3343,11 +3620,11 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
     lock_ObtainRead(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
+    for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
         fileLock =
             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
 
-        if(IS_LOCK_LOST(fileLock)) {
+        if (IS_LOCK_LOST(fileLock)) {
             if (fileLock->key == key) {
                 code = CM_ERROR_BADFD;
                 break;
@@ -3360,10 +3637,10 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
         /* we don't need to check for deleted locks here since deleted
            locks are dequeued from scp->fileLocks */
-        if(IS_LOCK_ACCEPTED(fileLock) &&
+        if (IS_LOCK_ACCEPTED(fileLock) &&
            INTERSECT_RANGE(range, fileLock->range)) {
 
-            if((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
+            if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
                 fileLock->lockType != LockRead) {
                 wait_unlock = TRUE;
                 code = CM_ERROR_WOULDBLOCK;
@@ -3374,66 +3651,80 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
     lock_ReleaseRead(&cm_scacheLock);
 
-    if(code == 0 && SERVERLOCKS_ENABLED(scp)) {
-        if(Which == scp->serverLock ||
+    if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
+        if (Which == scp->serverLock ||
            (Which == LockRead && scp->serverLock == LockWrite)) {
 
             /* we already have the lock we need */
             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]", 
                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
-            code = 0; /* redundant */
 
-        } else if((scp->exclusiveLocks > 0) ||
-                (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
+            code = cm_LockCheckPerms(scp, Which, userp, reqp);
+
+            /* special case: if we don't have permission to read-lock
+               the file, then we force a clientside lock.  This is to
+               compensate for applications that obtain a read-lock for
+               reading files off of directories that don't grant
+               read-locks to the user. */
+            if (code == CM_ERROR_NOACCESS && Which == LockRead) {
+                osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
+                force_client_lock = TRUE;
+            }
+
+        } else if ((scp->exclusiveLocks > 0) ||
+                   (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
 
             /* We are already waiting for some other lock.  We should
                wait for the daemon to catch up instead of generating a
                flood of SetLock calls. */
             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
-            code = CM_ERROR_WOULDBLOCK;
+
+            /* see if we have permission to create the lock in the
+               first place. */
+            code = cm_LockCheckPerms(scp, Which, userp, reqp);
+            if (code == 0)
+               code = CM_ERROR_WOULDBLOCK;
+            else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
+                osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
+                force_client_lock = TRUE;
+            }
+
+            /* leave any other codes as-is */
 
         } else {
-            cm_fid_t cfid;
             int newLock;
+            int check_data_version = FALSE;
+
+            /* first check if we have permission to elevate or obtain
+               the lock. */
+            code = cm_LockCheckPerms(scp, Which, userp, reqp);
+            if (code) {
+                if (code == CM_ERROR_NOACCESS && Which == LockRead) {
+                    osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
+                    force_client_lock = TRUE;
+                }
+                goto check_code;
+            }
 
             if (scp->serverLock == LockRead && Which == LockWrite) {
-            
+
                 /* We want to escalate the lock to a LockWrite.
                    Unfortunately that's not really possible without
                    letting go of the current lock.  But for now we do
                    it anyway. */
 
-                osi_Log0(afsd_logp, "   attempting to UPGRADE from LockRead to LockWrite.");
-
-                tfid.Volume = scp->fid.volume;
-                tfid.Vnode = scp->fid.vnode;
-                tfid.Unique = scp->fid.unique;
-                cfid = scp->fid;
-
-                lock_ReleaseMutex(&scp->mx);
-
-                osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
+                osi_Log0(afsd_logp,
+                         "   attempting to UPGRADE from LockRead to LockWrite.");
+                osi_Log1(afsd_logp,
+                         "   dataVersion on scp: %d", scp->dataVersion);
 
-                do {
-                    code = cm_Conn(&cfid, userp, reqp, &connp);
-                    if (code) 
-                        break;
-
-                    callp = cm_GetRxConn(connp);
-                    code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-                    rx_PutConnection(callp);
-
-                } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                                    NULL, NULL, code));
-                code = cm_MapRPCError(code, reqp);
-
-                if (code)
-                    osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
-                else
-                    osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
+                /* we assume at this point (because scp->serverLock
+                   was valid) that we had a valid server lock. */
+                scp->lockDataVersion = scp->dataVersion;
+                check_data_version = TRUE;
         
-                lock_ObtainMutex(&scp->mx);
+                code = cm_IntReleaseLock(scp, userp, reqp);
 
                 if (code) {
                     /* We couldn't release the lock */
@@ -3445,85 +3736,56 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
             /* We need to obtain a server lock of type Which in order
                to assert this file lock */
-            tfid.Volume = scp->fid.volume;
-            tfid.Vnode = scp->fid.vnode;
-            tfid.Unique = scp->fid.unique;
-            cfid = scp->fid;
-
 #ifndef AGGRESSIVE_LOCKS
             newLock = Which;
 #else
             newLock = LockWrite;
 #endif
-            osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, newLock);
-
-            lock_ReleaseMutex(&scp->mx);
-
-            do {
-                code = cm_Conn(&cfid, userp, reqp, &connp);
-                if (code) 
-                    break;
-
-                callp = cm_GetRxConn(connp);
-                code = RXAFS_SetLock(callp, &tfid, newLock,
-                                     &volSync);
-                rx_PutConnection(callp);
-
-            } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                                NULL, NULL, code));
-
-            code = cm_MapRPCError(code, reqp);
-            
-            if (code) {
-                osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
-            } else {
-                osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
-            }
+            code = cm_IntSetLock(scp, userp, newLock, reqp);
 
             if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
-                /* we wanted LockRead.  We tried LockWrite. Now try LockRead again */
+                /* we wanted LockRead.  We tried LockWrite. Now try
+                   LockRead again */
                 newLock = Which;
 
                 /* am I sane? */
                 osi_assert(newLock == LockRead);
-                
-                osi_Log3(afsd_logp, "CALL SetLock AGAIN scp 0x%p from %d to %d", 
-                         scp, (int) scp->serverLock, newLock);
-
-                do {
-                    code = cm_Conn(&cfid, userp, reqp, &connp);
-                    if (code) 
-                        break;
 
-                    callp = cm_GetRxConn(connp);
-                    code = RXAFS_SetLock(callp, &tfid, newLock,
-                                         &volSync);
-                    rx_PutConnection(callp);
+                code = cm_IntSetLock(scp, userp, newLock, reqp);
+            }
 
-                } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                                    NULL, NULL, code));
+            if (code == 0 && check_data_version &&
+               scp->dataVersion != scp->lockDataVersion) {
+                /* We lost a race.  Although we successfully obtained
+                   a lock, someone modified the file in between.  The
+                   locks have all been technically lost. */
 
-                code = cm_MapRPCError(code, reqp);                
+                osi_Log0(afsd_logp,
+                         "  Data version mismatch while upgrading lock.");
+                osi_Log2(afsd_logp,
+                         "  Data versions before=%d, after=%d",
+                         scp->lockDataVersion,
+                         scp->dataVersion);
+                osi_Log1(afsd_logp,
+                         "  Releasing stale lock for scp 0x%x", scp);
 
-                if (code) {
-                    osi_Log1(afsd_logp, "CALL SetLock FAILURE AGAIN, code 0x%x", code);
-                } else {
-                    osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
-                }
-            }
+                code = cm_IntReleaseLock(scp, userp, reqp);
 
-            lock_ObtainMutex(&scp->mx);
+                scp->serverLock = -1;
 
-            if(code == 0)
+                code = CM_ERROR_INVAL;
+            } else if (code == 0) {
                 scp->serverLock = newLock;
-            else {
-                if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+                scp->lockDataVersion = scp->dataVersion;
+            }
+
+            if (code != 0 &&
+                (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
                     scp->serverLock == -1) {
                     /* Oops. We lost the lock. */
                     cm_LockMarkSCacheLost(scp);
                 }
             }
-        }
     } else if (code == 0) {     /* server locks not enabled */
         osi_Log0(afsd_logp,
                  "  Skipping server lock for scp");
@@ -3531,7 +3793,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
  check_code:
 
-    if (code != 0) {
+    if (code != 0 && !force_client_lock) {
         /* Special case error translations
 
            Applications don't expect certain errors from a
@@ -3546,7 +3808,13 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
         }
     }
 
-    if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait)) {
+    if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
+        force_client_lock) {
+
+        /* clear the error if we are forcing a client lock, so we
+           don't get confused later. */
+        if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
+            code = 0;
 
         lock_ObtainWrite(&cm_scacheLock);
         fileLock = cm_GetFileLock();
@@ -3564,14 +3832,13 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                             CM_FILELOCK_FLAG_WAITUNLOCK :
                             CM_FILELOCK_FLAG_WAITLOCK));
 
-        if (!SERVERLOCKS_ENABLED(scp))
+        if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
 
-        fileLock->lastUpdate = (code == 0) ? time(NULL) : 0;
-
-        osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
+        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;
         osi_QAdd(&cm_allFileLocks, &fileLock->q);
@@ -3581,16 +3848,25 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
             *lockpp = fileLock;
         }
 
-        if (IS_LOCK_ACCEPTED(fileLock)) {
-            if(Which == LockRead)
+        if (IS_LOCK_CLIENTONLY(fileLock)) {
+            scp->clientLocks++;
+        } else if (IS_LOCK_ACCEPTED(fileLock)) {
+            if (Which == LockRead)
                 scp->sharedLocks++;
             else
                 scp->exclusiveLocks++;
         }
 
-        osi_Log2(afsd_logp, "cm_Lock Lock added 0x%p flags 0x%x", fileLock, fileLock->flags);
-        osi_Log4(afsd_logp, "   scp[0x%p] exclusives[%d] shared[%d] serverLock[%d]",
-                 scp, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+        osi_Log3(afsd_logp,
+                 "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
+                 fileLock, fileLock->flags, scp);
+        osi_Log4(afsd_logp,
+                 "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
+                 scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
+                 (int)(signed char) scp->serverLock);
+    } else {
+        osi_Log1(afsd_logp,
+                 "cm_Lock Rejecting lock (code = 0x%x)", code);
     }
 
     return code;
@@ -3606,20 +3882,19 @@ long cm_UnlockByKey(cm_scache_t * scp,
                     cm_req_t * reqp)
 {
     long code = 0;
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
     cm_file_lock_t *fileLock;
     osi_queue_t *q, *qn;
-    struct rx_connection * callp;
     int n_unlocks = 0;
 
-    osi_Log3(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x",
-             scp, (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
+    osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
+             scp,
+             (unsigned long)(key >> 32),
+             (unsigned long)(key & 0xffffffff),
+             flags);
 
     lock_ObtainWrite(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q; q = qn) {
+    for (q = scp->fileLocksH; q; q = qn) {
         qn = osi_QNext(q);
 
         fileLock = (cm_file_lock_t *)
@@ -3627,14 +3902,16 @@ long cm_UnlockByKey(cm_scache_t * scp,
 
 #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,
+                 (unsigned long) fileLock->range.offset,
+                 (unsigned long) fileLock->range.length,
                 fileLock->lockType);
         osi_Log3(afsd_logp, "     key[0x%x:%x] flags[0x%x]",
                  (unsigned long)(fileLock->key >> 32),
                  (unsigned long)(fileLock->key & 0xffffffff),
                  fileLock->flags);
 
-        if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+        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,
@@ -3659,10 +3936,12 @@ long cm_UnlockByKey(cm_scache_t * scp,
 
             if (scp->fileLocksT == q)
                 scp->fileLocksT = osi_QPrev(q);
-            osi_QRemove(&scp->fileLocksH,q);
+            osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
 
-            if(IS_LOCK_ACCEPTED(fileLock)) {
-                if(fileLock->lockType == LockRead)
+            if (IS_LOCK_CLIENTONLY(fileLock)) {
+                scp->clientLocks--;
+            } else if (IS_LOCK_ACCEPTED(fileLock)) {
+                if (fileLock->lockType == LockRead)
                     scp->sharedLocks--;
                 else
                     scp->exclusiveLocks--;
@@ -3682,7 +3961,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
 
     lock_ReleaseWrite(&cm_scacheLock);
 
-    if(n_unlocks == 0) {
+    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);
@@ -3694,6 +3973,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
 
     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
+    osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
 
     if (!SERVERLOCKS_ENABLED(scp)) {
         osi_Log0(afsd_logp, "  Skipping server lock for scp");
@@ -3707,41 +3987,19 @@ long cm_UnlockByKey(cm_scache_t * scp,
      * in that manner is done cm_RetryLock() manually.
      */
 
-    if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
-
-        cm_fid_t cfid;
+    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");
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
-
-        lock_ReleaseMutex(&scp->mx);
-
-        osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
-
-        do {
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
-
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-            rx_PutConnection(callp);
-            
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
-        code = cm_MapRPCError(code, reqp);
+        /* since scp->serverLock looked sane, we are going to assume
+           that we have a valid server lock. */
+        scp->lockDataVersion = scp->dataVersion;
+        osi_Log1(afsd_logp, "  dataVersion on scp = %d", scp->dataVersion);
 
-        if (code)
-            osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
-        else
-            osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
-        
-        lock_ObtainMutex(&scp->mx);
+        code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code) {
             /* so we couldn't release it.  Just let the lock be for now */
@@ -3751,85 +4009,43 @@ long cm_UnlockByKey(cm_scache_t * scp,
             scp->serverLock = -1;
         }
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
-
-        osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
+        code = cm_IntSetLock(scp, userp, LockRead, reqp);
 
-        lock_ReleaseMutex(&scp->mx);
-
-        do {
-
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
-
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_SetLock(callp, &tfid, LockRead,
-                                 &volSync);
-
-            rx_PutConnection(callp);
+        if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
+            scp->serverLock = LockRead;
+        } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
+            /* We lost a race condition.  Although we have a valid
+               lock on the file, the data has changed and essentially
+               we have lost the lock we had during the transition. */
+
+            osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
+            osi_Log2(afsd_logp, "  Data versions before=%d, after=%d",
+                     scp->lockDataVersion,
+                     scp->dataVersion);
             
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
+            code = cm_IntReleaseLock(scp, userp, reqp);
 
-        if (code)
-            osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
-        else {
-            osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
+            code = CM_ERROR_INVAL;
+            scp->serverLock = -1;
         }
 
-        lock_ObtainMutex(&scp->mx);
-        
-        if(code == 0)
-            scp->serverLock = LockRead;
-        else {
-            if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+        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) {
-        cm_fid_t cfid;
-
+    } else if (scp->serverLock != (-1) &&
+              scp->exclusiveLocks == 0 &&
+              scp->sharedLocks == 0) {
         /* The serverLock should be released entirely */
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
-
-        lock_ReleaseMutex(&scp->mx);
-
-        osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
-
-        do {
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
-
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-            rx_PutConnection(callp);
-
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
-        code = cm_MapRPCError(code, reqp);
-
-        if (code)
-            osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
-        else
-            osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
-        
-        lock_ObtainMutex(&scp->mx);
+        code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code == 0)
             scp->serverLock = (-1);
@@ -3838,8 +4054,9 @@ long cm_UnlockByKey(cm_scache_t * scp,
  done:
 
     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
-    osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
-             scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+    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);
 
     return code;
 }
@@ -3853,13 +4070,9 @@ long cm_Unlock(cm_scache_t *scp,
 {
     long code = 0;
     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
     int release_userp = FALSE;
-    struct rx_connection * callp;
 
     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
@@ -3868,12 +4081,12 @@ long cm_Unlock(cm_scache_t *scp,
 
     lock_ObtainRead(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
+    for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
         fileLock = (cm_file_lock_t *)
             ((char *) q - offsetof(cm_file_lock_t, fileq));
 
 #ifdef DEBUG
-        if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+        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,
@@ -3896,7 +4109,7 @@ long cm_Unlock(cm_scache_t *scp,
         }
     }
 
-    if(!q) {
+    if (!q) {
         osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
         
         lock_ReleaseRead(&cm_scacheLock);
@@ -3905,22 +4118,23 @@ long cm_Unlock(cm_scache_t *scp,
         return 0;
     }
 
+    lock_ReleaseRead(&cm_scacheLock);
+
     /* discard lock record */
+    lock_ObtainWrite(&cm_scacheLock);
     if (scp->fileLocksT == q)
         scp->fileLocksT = osi_QPrev(q);
-    osi_QRemove(&scp->fileLocksH, q);
-
-    lock_ReleaseRead(&cm_scacheLock);
+    osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
 
     /*
      * Don't delete it here; let the daemon delete it, to simplify
      * the daemon's traversal of the list.
      */
 
-    lock_ObtainWrite(&cm_scacheLock);
-
-    if(IS_LOCK_ACCEPTED(fileLock)) {
-        if(fileLock->lockType == LockRead)
+    if (IS_LOCK_CLIENTONLY(fileLock)) {
+        scp->clientLocks--;
+    } else if (IS_LOCK_ACCEPTED(fileLock)) {
+        if (fileLock->lockType == LockRead)
             scp->sharedLocks--;
         else
             scp->exclusiveLocks--;
@@ -3950,42 +4164,19 @@ long cm_Unlock(cm_scache_t *scp,
      * in that manner is done cm_RetryLock() manually.
      */
 
-    if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
-
-        cm_fid_t cfid;
+    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");
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
+        /* 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 %d", scp->dataVersion);
 
-        lock_ReleaseMutex(&scp->mx);
-
-        osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
-
-        do {
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
-
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-            rx_PutConnection(callp);
-            
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
-
-        code = cm_MapRPCError(code, reqp);
-
-        if (code)
-            osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
-        else
-            osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
-        
-        lock_ObtainMutex(&scp->mx);
+        code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code) {
             /* so we couldn't release it.  Just let the lock be for now */
@@ -3995,85 +4186,45 @@ long cm_Unlock(cm_scache_t *scp,
             scp->serverLock = -1;
         }
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
-
-        osi_Log3(afsd_logp, "CALL SetLock scp 0x%p from %d to %d", scp, (int) scp->serverLock, LockRead);
-
-        lock_ReleaseMutex(&scp->mx);
-
-        do {
-
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
+        code = cm_IntSetLock(scp, userp, LockRead, reqp);
 
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_SetLock(callp, &tfid, LockRead,
-                                 &volSync);
-
-            rx_PutConnection(callp);
+        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=%d, after=%d",
+                     scp->lockDataVersion,
+                     scp->dataVersion);
             
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
+            code = cm_IntReleaseLock(scp, userp, reqp);
 
-        if (code)
-            osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
-        else {
-            osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
+            scp->serverLock = -1;
+            code = CM_ERROR_INVAL;
         }
 
-        lock_ObtainMutex(&scp->mx);
-        
-        if(code == 0)
-            scp->serverLock = LockRead;
-        else {
-            if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+        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) {
-        cm_fid_t cfid;
-
+    } else if (scp->serverLock != (-1) &&
+              scp->exclusiveLocks == 0 &&
+              scp->sharedLocks == 0) {
         /* The serverLock should be released entirely */
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
-
-        lock_ReleaseMutex(&scp->mx);
-
-        osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
-
-        do {
-            code = cm_Conn(&cfid, userp, reqp, &connp);
-            if (code) 
-                break;
-
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-            rx_PutConnection(callp);
-
-        } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
-                            NULL, NULL, code));
-        code = cm_MapRPCError(code, reqp);
-
-        if (code)
-            osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
-        else
-            osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
-        
-        lock_ObtainMutex(&scp->mx);
+        code = cm_IntReleaseLock(scp, userp, reqp);
 
         if (code == 0) {
             scp->serverLock = (-1);
@@ -4085,8 +4236,10 @@ long cm_Unlock(cm_scache_t *scp,
 
  done:
 
-    osi_Log4(afsd_logp, "cm_Unlock code 0x%x leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
-             code, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+    osi_Log1(afsd_logp, "cm_Unlock 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);
 
     return code;
 }
@@ -4107,11 +4260,12 @@ static void cm_LockMarkSCacheLost(cm_scache_t * scp)
     /* cm_scacheLock needed because we are modifying fileLock->flags */
     lock_ObtainWrite(&cm_scacheLock);
 
-    for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
+    for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
         fileLock = 
             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
 
-        if(IS_LOCK_ACTIVE(fileLock)) {
+        if (IS_LOCK_ACTIVE(fileLock) &&
+            !IS_LOCK_CLIENTONLY(fileLock)) {
             if (fileLock->lockType == LockRead)
                 scp->sharedLocks--;
             else
@@ -4122,6 +4276,7 @@ static void cm_LockMarkSCacheLost(cm_scache_t * scp)
     }
 
     scp->serverLock = -1;
+    scp->lockDataVersion = -1;
     lock_ReleaseWrite(&cm_scacheLock);
 }
 
@@ -4146,7 +4301,7 @@ void cm_CheckLocks()
 
     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
 
-    for(q = cm_allFileLocks; q; q = nq) {
+    for (q = cm_allFileLocks; q; q = nq) {
         fileLock = (cm_file_lock_t *) q;
         nq = osi_QNext(q);
        code = -1;
@@ -4160,7 +4315,7 @@ void cm_CheckLocks()
 
             /* Server locks must have been enabled for us to have
                received an active non-client-only lock. */
-            //osi_assert(cm_enableServerLocks);
+            osi_assert(cm_enableServerLocks);
 
             scp = fileLock->scp;
             osi_assert(scp != NULL);
@@ -4168,7 +4323,7 @@ void cm_CheckLocks()
             cm_HoldSCacheNoLock(scp);
 
 #ifdef DEBUG
-            if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+            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,
@@ -4204,7 +4359,8 @@ void cm_CheckLocks()
                                  | CM_SCACHESYNC_LOCK);
 
                 if (code) {
-                    osi_Log1(smb_logp, "cm_CheckLocks SyncOp failure code 0x%x", code);
+                    osi_Log1(smb_logp,
+                             "cm_CheckLocks SyncOp failure code 0x%x", code);
                     goto post_syncopdone;
                 }
 
@@ -4213,7 +4369,7 @@ void cm_CheckLocks()
                 if (!IS_LOCK_ACTIVE(fileLock))
                     goto pre_syncopdone;
 
-                if(scp->serverLock != -1) {
+                if (scp->serverLock != -1) {
                     cm_fid_t cfid;
                     cm_user_t * userp;
 
@@ -4253,11 +4409,47 @@ void cm_CheckLocks()
 
                     if (code) {
                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
-                       if (code == EINVAL || code == CM_ERROR_INVAL)
-                           cm_LockMarkSCacheLost(scp);
                     } else {
                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
+                        scp->lockDataVersion = scp->dataVersion;
+                    }
+
+                    if ((code == EINVAL || code == CM_ERROR_INVAL) &&
+                        scp->lockDataVersion == scp->dataVersion) {
+                        int lockType;
+
+                        lockType =
+                            (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
+
+                        /* we might still have a chance to obtain a
+                           new lock */
+
+                        code = cm_IntSetLock(scp, userp, lockType, &req);
+
+                        if (code) {
+                            code = CM_ERROR_INVAL;
+                        } else if (scp->lockDataVersion != scp->dataVersion) {
+
+                            /* now check if we still have the file at
+                               the right data version. */
+                            osi_Log1(afsd_logp,
+                                     "Data version mismatch on scp 0x%p",
+                                     scp);
+                            osi_Log2(afsd_logp,
+                                     "   Data versions: before=%d, after=%d",
+                                     scp->lockDataVersion,
+                                     scp->dataVersion);
+
+                            code = cm_IntReleaseLock(scp, userp, &req);
+
+                            code = CM_ERROR_INVAL;
+                        }
+                    }
+
+                    if (code == EINVAL || code == CM_ERROR_INVAL) {
+                        cm_LockMarkSCacheLost(scp);
                     }
+
                 } else {
                     /* interestingly, we have found an active lock
                        belonging to an scache that has no
@@ -4304,14 +4496,11 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 {
     long code = 0;
     cm_scache_t *scp = NULL;
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
     cm_req_t req;
-    struct rx_connection * callp;
     int newLock = -1;
+    int force_client_lock = FALSE;
 
     cm_InitReq(&req);
 
@@ -4334,14 +4523,14 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
              (unsigned)(oldFileLock->flags));
 
     /* if the lock has already been granted, then we have nothing to do */
-    if(IS_LOCK_ACTIVE(oldFileLock)) {
+    if (IS_LOCK_ACTIVE(oldFileLock)) {
         lock_ReleaseRead(&cm_scacheLock);
         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
         return 0;
     }
 
     /* we can't do anything with lost or deleted locks at the moment. */
-    if(IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
+    if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
         code = CM_ERROR_BADFD;
         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
         lock_ReleaseRead(&cm_scacheLock);
@@ -4354,14 +4543,27 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 
     lock_ReleaseRead(&cm_scacheLock);
     lock_ObtainMutex(&scp->mx);
+
+    code = cm_LockCheckPerms(scp, oldFileLock->lockType,
+                             oldFileLock->userp,
+                             &req);
+
+    if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
+        force_client_lock = TRUE;
+        code = 0;
+    } else if (code) {
+        lock_ReleaseMutex(&scp->mx);
+        return code;
+    }
+
     lock_ObtainWrite(&cm_scacheLock);
 
     /* Check if we already have a sufficient server lock to allow this
-       lock to go through */
-    if(IS_LOCK_WAITLOCK(oldFileLock) &&
-       (!SERVERLOCKS_ENABLED(scp) ||
-        scp->serverLock == oldFileLock->lockType ||
-        scp->serverLock == LockWrite)) {
+       lock to go through. */
+    if (IS_LOCK_WAITLOCK(oldFileLock) &&
+        (!SERVERLOCKS_ENABLED(scp) ||
+         scp->serverLock == oldFileLock->lockType ||
+         scp->serverLock == LockWrite)) {
 
         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
 
@@ -4378,16 +4580,15 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         return 0;
     }
 
-    if(IS_LOCK_WAITUNLOCK(oldFileLock)) {
+    if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
 
         /* check if the conflicting locks have dissappeared already */
-
-        for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
+        for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
 
             fileLock = (cm_file_lock_t *)
                 ((char *) q - offsetof(cm_file_lock_t, fileq));
 
-            if(IS_LOCK_LOST(fileLock)) {
+            if (IS_LOCK_LOST(fileLock)) {
                 if (fileLock->key == oldFileLock->key) {
                     code = CM_ERROR_BADFD;
                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
@@ -4402,10 +4603,10 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
                 }
             }
 
-            if(IS_LOCK_ACCEPTED(fileLock) &&
-               INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
+            if (IS_LOCK_ACCEPTED(fileLock) &&
+                INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
 
-                if(oldFileLock->lockType != LockRead ||
+                if (oldFileLock->lockType != LockRead ||
                    fileLock->lockType != LockRead) {
 
                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
@@ -4442,20 +4643,34 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
     }
 
-    if (!SERVERLOCKS_ENABLED(scp) ||
+    if (force_client_lock ||
+        !SERVERLOCKS_ENABLED(scp) ||
         scp->serverLock == oldFileLock->lockType ||
         (oldFileLock->lockType == LockRead &&
          scp->serverLock == LockWrite)) {
 
         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
 
+        if ((force_client_lock ||
+             !SERVERLOCKS_ENABLED(scp)) &&
+            !IS_LOCK_CLIENTONLY(oldFileLock)) {
+
+            oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
+
+            if (oldFileLock->lockType == LockRead)
+                scp->sharedLocks--;
+            else
+                scp->exclusiveLocks--;
+
+            scp->clientLocks++;
+        }
+
         lock_ReleaseWrite(&cm_scacheLock);
         lock_ReleaseMutex(&scp->mx);
 
         return 0;
 
     } else {
-        cm_fid_t cfid;
         cm_user_t * userp;
 
         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
@@ -4468,13 +4683,9 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
             goto post_syncopdone;
         }
 
-        if(!IS_LOCK_WAITLOCK(oldFileLock))
+        if (!IS_LOCK_WAITLOCK(oldFileLock))
             goto pre_syncopdone;
 
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        cfid = scp->fid;
         userp = oldFileLock->userp;
 
 #ifndef AGGRESSIVE_LOCKS
@@ -4483,33 +4694,10 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         newLock = LockWrite;
 #endif
 
-        osi_Log1(afsd_logp, "CALL SetLock lock 0x%p", oldFileLock);
-
         lock_ReleaseWrite(&cm_scacheLock);
-        lock_ReleaseMutex(&scp->mx);
-
-        do {
-            code = cm_Conn(&cfid, userp, &req, &connp);
-            if (code) 
-                break;
 
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_SetLock(callp, &tfid, newLock,
-                                  &volSync);
-            rx_PutConnection(callp);
-
-        } while (cm_Analyze(connp, userp, &req,
-                             &cfid, &volSync,
-                             NULL, NULL, code));
-        code = cm_MapRPCError(code, &req);
-
-        if (code) {
-            osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
-        } else {
-            osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
-        }
+        code = cm_IntSetLock(scp, userp, newLock, &req);
 
-        lock_ObtainMutex(&scp->mx);
     pre_syncopdone:
         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
     post_syncopdone:
@@ -4518,9 +4706,11 @@ 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_QRemove(&scp->fileLocksH, &oldFileLock->fileq);
+        osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
+       lock_ReleaseWrite(&cm_scacheLock);
     } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
         scp->serverLock = newLock;
     }
@@ -4546,9 +4736,16 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 
 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
 {
-    return (((cm_key_t) process_id) << 32) |
-        (((cm_key_t) session_id) << 16) |
-        (((cm_key_t) file_id));
+#ifdef DEBUG
+    osi_assert((process_id & 0xffffffff) == process_id);
+    osi_assert((session_id & 0xffff) == session_id);
+    osi_assert((file_id & 0xffff) == file_id);
+#endif
+
+    return 
+        (((cm_key_t) (process_id & 0xffffffff)) << 32) |
+        (((cm_key_t) (session_id & 0xffff)) << 16) |
+        (((cm_key_t) (file_id & 0xffff)));
 }
 
 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
@@ -4567,7 +4764,7 @@ void cm_ReleaseAllLocks(void)
     cm_user_t *userp;
     cm_key_t   key;
     cm_file_lock_t *fileLock;
-    int i;
+    unsigned int i;
 
     for (i = 0; i < cm_data.hashTableSize; i++)
     {