windows-dbg-refcount-20061016
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index 2ee3744..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>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <osi.h>
 
@@ -32,7 +31,7 @@
 extern void afsi_log(char *pattern, ...);
 #endif
 
-unsigned int cm_mountRootGen = 0;
+int cm_enableServerLocks = 1;
 
 /*
  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
@@ -130,7 +129,6 @@ char cm_LegalChars[256] = {
 int cm_Is8Dot3(char *namep)
 {
     int sawDot = 0;
-    int sawUpper = 0, sawLower = 0;
     unsigned char tc;
     int charCount = 0;
         
@@ -155,10 +153,6 @@ int cm_Is8Dot3(char *namep)
         }
         if (cm_LegalChars[tc] == 0)
             return 0;
-        if (tc >= 'A' && tc <= 'Z')
-            sawUpper = 1;
-        else if (tc >= 'a' && tc <= 'z')
-            sawLower = 1;
         charCount++;
         if (!sawDot && charCount > 8)
             /* more than 8 chars in name */
@@ -167,25 +161,19 @@ int cm_Is8Dot3(char *namep)
             /* more than 3 chars in extension */
             return 0;
     }
-    /*
-     * Used to check that all characters were the same case.
-     * This doesn't help 16-bit apps, and meanwhile it causes the
-     * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
-     *
-     if (sawUpper && sawLower)
-         return 0;
-     */
     return 1;
 }
 
 /*
  * Number unparsing map for generating 8.3 names;
- * Taken from DFS.
+ * The version taken from DFS was on drugs.  
+ * You can't include '&' and '@' in a file name.
  */
-char cm_8Dot3Mapping[41] =
+char cm_8Dot3Mapping[42] =
 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
- 'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
+ 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
 };
 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
 
@@ -275,7 +263,66 @@ 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)) &&
+        scp->fileType == CM_SCACHETYPE_FILE) {
+
+        cm_key_t key;
+        unsigned int sLockType;
+        LARGE_INTEGER LOffset, LLength;
+
+        /* 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;
+
+        LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
+        LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
+        LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
+        LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
+
+        code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
+
+        if (code == 0) {
+            cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
+        } else {
+            /* 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))
+                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;
+               }
+           }
+        }
+
+    } else if (code != 0) {
+        goto _done;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
+
     lock_ReleaseMutex(&scp->mx);
 
     return code;
@@ -305,8 +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);
-    lock_ReleaseMutex(&scp->mx);
+                     | CM_SCACHESYNC_NEEDCALLBACK
+                     | CM_SCACHESYNC_LOCK);
 
     /*
      * If the open will fail because the volume is readonly, then we will
@@ -317,6 +364,62 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
     if (code == CM_ERROR_READONLY)
         code = CM_ERROR_NOACCESS;
 
+    if (code == 0 &&
+             ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
+             scp->fileType == CM_SCACHETYPE_FILE) {
+        cm_key_t key;
+        unsigned int sLockType;
+        LARGE_INTEGER LOffset, LLength;
+
+        /* 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;
+
+        /* single byte lock at offset 0x0100 0000 0000 0000 */
+        LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
+        LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
+        LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
+        LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
+
+        code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
+
+        if (code == 0) {
+            cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
+        } else {
+            /* 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))
+                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;
+               }
+           }
+        }
+    } else if (code != 0) {
+        goto _done;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
+    lock_ReleaseMutex(&scp->mx);
+
     return code;
 }
 
@@ -324,7 +427,7 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
  * done in three steps:
  * (1) open for deletion (NT_CREATE_AND_X)
- * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
+ * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
  * (3) close (CLOSE)
  * We must not do the RPC until step 3.  But if we are going to return an error
  * code (e.g. directory not empty), we must return it by step 2, otherwise most
@@ -347,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;
@@ -384,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;
     }
@@ -474,20 +578,20 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
         * do not have an associated cm_server_t
         */
     if ( !(cm_freelanceEnabled &&
-                       sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
-                       sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
+            sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+            sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
 #endif /* AFS_FREELANCE_CLIENT */
-               {
-               int casefold = sp->caseFold;
-                       sp->caseFold = 0; /* we have a strong preference for exact matches */
-                       if ( *retscp = cm_dnlcLookup(scp, sp))  /* dnlc hit */
-                       {
-                               sp->caseFold = casefold;
-                               lock_ReleaseMutex(&scp->mx);
-                               return 0;
-                       }
-               sp->caseFold = casefold;
-               }
+    {
+        int casefold = sp->caseFold;
+        sp->caseFold = 0; /* we have a strong preference for exact matches */
+        if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
+        {
+            sp->caseFold = casefold;
+            lock_ReleaseMutex(&scp->mx);
+            return 0;
+        }
+        sp->caseFold = casefold;
+    }
     }  
 
     /*
@@ -540,7 +644,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
          * the offset of the buffer we have.  If not, get the buffer.
          */
         thyper.HighPart = curOffset.HighPart;
-        thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
+        thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
             /* wrong buffer */
             if (bufferp) {
@@ -552,10 +656,30 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
             lock_ObtainRead(&scp->bufCreateLock);
             code = buf_Get(scp, &thyper, &bufferp);
             lock_ReleaseRead(&scp->bufCreateLock);
+            if (code) {
+                /* if buf_Get() fails we do not have a buffer object to lock */
+                bufferp = NULL;
+                break;
+            }
+
+#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. */
+            lock_ObtainMutex(&scp->mx);
+            if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
+                 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
+            {
+                scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
+                code = cm_TryBulkStat(scp, &thyper, userp, reqp);
+                scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
+                scp->bulkStatProgress = thyper;
+            }
+            lock_ReleaseMutex(&scp->mx);
+#endif
 
             lock_ObtainMutex(&bufferp->mx);
-            if (code) 
-                break;
             bufferOffset = thyper;
 
             /* now get the data in the cache */
@@ -570,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);
@@ -592,12 +717,12 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 break;
             }
         }      /* if (wrong buffer) ... */
-                
+           
         /* now we have the buffer containing the entry we're interested
          * in; copy it out if it represents a non-deleted entry.
          */
         entryInDir = curOffset.LowPart & (2048-1);
-        entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
+        entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
 
         /* page header will help tell us which entries are free.  Page
          * header can change more often than once per buffer, since
@@ -605,7 +730,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
          * buffer package buffer.
          */
         /* only look intra-buffer */
-        temp = curOffset.LowPart & (buf_bufferSize - 1);
+        temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
         temp &= ~(2048 - 1);   /* turn off intra-page bits */
         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
 
@@ -699,13 +824,13 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
             match = cm_stricmp(matchName, sp->searchNamep);
         else
             match = strcmp(matchName, sp->searchNamep);
-    }       
+    }
 
     if (match != 0)
         return 0;
 
     sp->found = 1;
-    if(!sp->caseFold) 
+    if (!sp->caseFold) 
         sp->ExactFound = 1;
 
     if (!sp->caseFold || matchName == shortName) {
@@ -767,7 +892,7 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     osi_hyper_t thyper;
     int tlen;
 
-    if (scp->mountPointStringp) 
+    if (scp->mountPointStringp[0]) 
         return 0;
         
     /* otherwise, we have to read it in */
@@ -788,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;
@@ -807,13 +934,12 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     }
 
     /* someone else did the work while we were out */
-    if (scp->mountPointStringp) {
+    if (scp->mountPointStringp[0]) {
         code = 0;
         goto done;
     }
 
     /* otherwise, copy out the link */
-    scp->mountPointStringp = malloc(tlen);
     memcpy(scp->mountPointStringp, bufp->datap, tlen);
 
     /* now make it null-terminated.  Note that the original contents of a
@@ -835,7 +961,7 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
  * scp remains locked, just for simplicity of describing the interface.
  */
 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
-                          cm_req_t *reqp, cm_scache_t **outScpp)
+                         cm_req_t *reqp, cm_scache_t **outScpp)
 {
     char *cellNamep;
     char *volNamep;
@@ -850,8 +976,8 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     size_t vnLength;
     int type;
 
-    if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
-        tfid = *scp->mountRootFidp;
+    if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
+        tfid = scp->mountRootFid;
         lock_ReleaseMutex(&scp->mx);
         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
         lock_ObtainMutex(&scp->mx);
@@ -860,8 +986,9 @@ 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);
-    tlen = strlen(scp->mountPointStringp);
+    if (!mpNamep[0])
+       return CM_ERROR_NOSUCHPATH;
+    tlen = (int)strlen(scp->mountPointStringp);
     mtType = *scp->mountPointStringp;
     cellNamep = malloc(tlen);
     volNamep = malloc(tlen);
@@ -917,15 +1044,10 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
          * (defect 11489)
          */
         lock_ObtainMutex(&volp->mx);
-        if(volp->dotdotFidp == (cm_fid_t *) NULL) 
-            volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
-        *(volp->dotdotFidp) = dscp->fid;
+        volp->dotdotFid = dscp->fid;
         lock_ReleaseMutex(&volp->mx);
 
-        if (scp->mountRootFidp == 0) {
-            scp->mountRootFidp = malloc(sizeof(cm_fid_t));
-        }
-        scp->mountRootFidp->cell = cellp->cellID;
+        scp->mountRootFid.cell = cellp->cellID;
         /* if the mt pt is in a read-only volume (not just a
          * backup), and if there is a read-only volume for the
          * target, and if this is a type '#' mount point, use
@@ -935,18 +1057,18 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
              && volp->roID != 0 && type == RWVOL)
             type = ROVOL;
         if (type == ROVOL)
-            scp->mountRootFidp->volume = volp->roID;
+            scp->mountRootFid.volume = volp->roID;
         else if (type == BACKVOL)
-            scp->mountRootFidp->volume = volp->bkID;
+            scp->mountRootFid.volume = volp->bkID;
         else
-            scp->mountRootFidp->volume = volp->rwID;
+            scp->mountRootFid.volume = volp->rwID;
 
         /* the rest of the fid is a magic number */
-        scp->mountRootFidp->vnode = 1;
-        scp->mountRootFidp->unique = 1;
-        scp->mountRootGen = cm_mountRootGen;
+        scp->mountRootFid.vnode = 1;
+        scp->mountRootFid.unique = 1;
+        scp->mountRootGen = cm_data.mountRootGen;
 
-        tfid = *scp->mountRootFidp;
+        tfid = scp->mountRootFid;
         lock_ReleaseMutex(&scp->mx);
         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
         lock_ObtainMutex(&scp->mx);
@@ -970,11 +1092,13 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
 
     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
          && strcmp(namep, "..") == 0) {
-        if (dscp->dotdotFidp == (cm_fid_t *)NULL
-             || dscp->dotdotFidp->volume == 0)
+        if (dscp->dotdotFid.volume == 0)
             return CM_ERROR_NOSUCHVOLUME;
-        rock.fid = *dscp->dotdotFidp;
+        rock.fid = dscp->dotdotFid;
         goto haveFid;
+    } else if (strcmp(namep, ".") == 0) {
+       rock.fid = dscp->fid;
+       goto haveFid;
     }
 
     memset(&rock, 0, sizeof(rock));
@@ -992,10 +1116,21 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
      * that we stopped early, probably because we found the entry we're
      * looking for.  Any other non-zero code is an error.
      */
-    if (code && code != CM_ERROR_STOPNOW) 
+    if (code && code != CM_ERROR_STOPNOW) {
+        /* if the cm_scache_t we are searching in is not a directory 
+         * we must return path not found because the error 
+         * is to describe the final component not an intermediary
+         */
+        if (code == CM_ERROR_NOTDIR) {
+            if (flags & CM_FLAG_CHECKPATH)
+                return CM_ERROR_NOSUCHPATH;
+            else
+                return CM_ERROR_NOSUCHFILE;
+        }
         return code;
+    }
 
-    getroot = (dscp==cm_rootSCachep) ;
+    getroot = (dscp==cm_data.rootSCachep) ;
     if (!rock.found) {
         if (!cm_freelanceEnabled || !getroot) {
             if (flags & CM_FLAG_CHECKPATH)
@@ -1018,12 +1153,13 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
                         code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
                 }
             } else {
-                if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE))
+                if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
                     found = 1;
-                if ( stricmp(namep, fullname) )
-                    code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
-                else
-                    code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
+                    if ( stricmp(namep, fullname) )
+                        code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
+                    else
+                        code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
+                }
             }
             if (!found || code < 0) {   /* add mount point failed, so give up */
                 if (flags & CM_FLAG_CHECKPATH)
@@ -1053,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)
@@ -1082,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);
     }
@@ -1111,7 +1248,7 @@ int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
         return -1;
 
     /* otherwise generate the properly expanded @sys name */
-    prefixCount = tp - inp;
+    prefixCount = (int)(tp - inp);
 
     strncpy(outp, inp, prefixCount);   /* copy out "a." from "a.@sys" */
     outp[prefixCount] = 0;             /* null terminate the "a." */
@@ -1119,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)
@@ -1138,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;
         }
     }
 
@@ -1169,7 +1327,7 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     struct rx_connection * callp;
 
 #ifdef AFS_FREELANCE_CLIENT
-    if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
+    if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
         /* deleting a mount point from the root dir. */
         code = cm_FreelanceRemoveMount(namep);
         return code;
@@ -1188,6 +1346,8 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     afsFid.Volume = dscp->fid.volume;
     afsFid.Vnode = dscp->fid.vnode;
     afsFid.Unique = dscp->fid.unique;
+
+    osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) 
@@ -1201,11 +1361,23 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
+    if (code)
+        osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
     cm_dnlcRemove(dscp, namep);
     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;
@@ -1222,7 +1394,7 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
     osi_hyper_t thyper;
 
     lock_AssertMutex(&linkScp->mx);
-    if (!linkScp->mountPointStringp) {
+    if (!linkScp->mountPointStringp[0]) {
         /* read the link data */
         lock_ReleaseMutex(&linkScp->mx);
         thyper.LowPart = thyper.HighPart = 0;
@@ -1237,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;
 
@@ -1249,7 +1423,7 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
                 
         /* now if we still have no link read in,
          * copy the data from the buffer */
-        if ((temp = linkScp->length.LowPart) >= 1024) {
+        if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
             buf_Release(bufp);
             return CM_ERROR_TOOBIG;
         }
@@ -1258,8 +1432,7 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
          * lost race with someone else referencing this link above),
          * and if so, copy in the data.
          */
-        if (linkScp->mountPointStringp == NULL) {
-            linkScp->mountPointStringp = malloc(temp+1);
+        if (!linkScp->mountPointStringp[0]) {
             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
             linkScp->mountPointStringp[temp] = 0;      /* null terminate */
         }
@@ -1279,7 +1452,8 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
                       cm_user_t *userp, cm_req_t *reqp)
 {
-    long code;
+    long code = 0;
+    long len;
     char *linkp;
     cm_space_t *tsp;
 
@@ -1303,22 +1477,51 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
             strcpy(tsp->data, linkp+cm_mountRootLen+1);
         else
             tsp->data[0] = 0;
-        *newRootScpp = cm_rootSCachep;
-        cm_HoldSCache(cm_rootSCachep);
+        *newRootScpp = cm_data.rootSCachep;
+        cm_HoldSCache(cm_data.rootSCachep);
+    } else if (linkp[0] == '\\' && linkp[1] == '\\') {
+        if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
+        {
+            char * p = &linkp[len + 3];
+            if (strnicmp(p, "all", 3) == 0)
+                p += 4;
+
+            strcpy(tsp->data, p);
+            for (p = tsp->data; *p; p++) {
+                if (*p == '\\')
+                    *p = '/';
+            }
+            *newRootScpp = cm_data.rootSCachep;
+            cm_HoldSCache(cm_data.rootSCachep);
+        } else {
+            linkScp->fileType = CM_SCACHETYPE_DFSLINK;
+            strcpy(tsp->data, linkp);
+            *newRootScpp = NULL;
+            code = CM_ERROR_PATH_NOT_COVERED;
+        }
+    } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
+        linkScp->fileType = CM_SCACHETYPE_DFSLINK;
+        strcpy(tsp->data, linkp);
+        *newRootScpp = NULL;
+        code = CM_ERROR_PATH_NOT_COVERED;
     } else if (*linkp == '\\' || *linkp == '/') {
+#if 0   
         /* formerly, this was considered to be from the AFS root,
          * but this seems to create problems.  instead, we will just
          * reject the link */
-#if 0   
         strcpy(tsp->data, linkp+1);
-        *newRootScpp = cm_rootSCachep;
-        cm_HoldSCache(cm_rootSCachep);
+        *newRootScpp = cm_data.rootSCachep;
+        cm_HoldSCache(cm_data.rootSCachep);
 #else
+        /* we still copy the link data into the response so that 
+         * the user can see what the link points to
+         */
+        linkScp->fileType = CM_SCACHETYPE_INVALID;
+        strcpy(tsp->data, linkp);
+        *newRootScpp = NULL;
         code = CM_ERROR_NOSUCHPATH;
-        goto done;
 #endif  
-    }
-    else {
+    } else {
         /* a relative link */
         strcpy(tsp->data, linkp);
         *newRootScpp = NULL;
@@ -1328,34 +1531,45 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
         strcat(tsp->data, pathSuffixp);
     }
     *newSpaceBufferp = tsp;
-    code = 0;
 
   done:
     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 */
     char tc;                   /* temp char */
     int haveComponent;         /* has new component started? */
-    char component[256];               /* this is the new component */
+    char component[256];       /* this is the new component */
     char *cp;                  /* component name being assembled */
     cm_scache_t *tscp;         /* current location in the hierarchy */
     cm_scache_t *nscp;         /* next dude down */
-    cm_scache_t *dirScp;               /* last dir we searched */
-    cm_scache_t *linkScp;              /* new root for the symlink we just
+    cm_scache_t *dirScp;       /* last dir we searched */
+    cm_scache_t *linkScp;      /* new root for the symlink we just
     * looked up */
     cm_space_t *psp;           /* space for current path, if we've hit
     * any symlinks */
     cm_space_t *tempsp;                /* temp vbl */
-    char *restp;                       /* rest of the pathname to interpret */
+    char *restp;               /* rest of the pathname to interpret */
     int symlinkCount;          /* count of # of symlinks traversed */
-    int extraFlag;                     /* avoid chasing mt pts for dir cmd */
-    int phase = 1;                     /* 1 = tidPathp, 2 = pathp */
+    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) {
@@ -1370,6 +1584,9 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     tscp = rootSCachep;
     cm_HoldSCache(tscp);
     symlinkCount = 0;
+    dirScp = NULL;
+
+
     while (1) {
         tc = *tp++;
 
@@ -1380,9 +1597,9 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
             tc = '\\';
 
         if (!haveComponent) {
-            if (tc == '\\') 
+            if (tc == '\\') {
                 continue;
-            else if (tc == 0) {
+            } else if (tc == 0) {
                 if (phase == 1) {
                     phase = 2;
                     tp = pathp;
@@ -1390,14 +1607,12 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                 }
                 code = 0;
                 break;
-            }
-            else {
+            } else {
                 haveComponent = 1;
                 cp = component;
                 *cp++ = tc;
             }
-        }
-        else {
+        } else {
             /* we have a component here */
             if (tc == 0 || tc == '\\') {
                 /* end of the component; we're at the last
@@ -1405,25 +1620,46 @@ 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 (psp) 
-                        cm_FreeSpace(psp);
-                    return code;
-                }
-                haveComponent = 0;     /* component done */
-                dirScp = tscp;         /* for some symlinks */
-                tscp = nscp;   /* already held */
-                if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
+               if (!strcmp(".",component)) {
                     code = 0;
-                    cm_ReleaseSCache(dirScp);
+                    if (dirScp) {
+                        cm_ReleaseSCache(dirScp);
+                        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 = NULL;
+                    }
                     break;
                 }
 
@@ -1437,17 +1673,28 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                 if (code) {
                     lock_ReleaseMutex(&tscp->mx);
                     cm_ReleaseSCache(tscp);
-                    cm_ReleaseSCache(dirScp);
+                    tscp = NULL;
+                    if (dirScp) {
+                        cm_ReleaseSCache(dirScp);
+                        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);
-                        cm_ReleaseSCache(dirScp);
+                        tscp = NULL;
+                        if (dirScp) {
+                            cm_ReleaseSCache(dirScp);
+                            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) 
@@ -1458,7 +1705,11 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     if (code) {
                         /* something went wrong */
                         cm_ReleaseSCache(tscp);
-                        cm_ReleaseSCache(dirScp);
+                        tscp = NULL;
+                        if (dirScp) {
+                            cm_ReleaseSCache(dirScp);
+                            dirScp = NULL;
+                        }
                         break;
                     }
 
@@ -1475,7 +1726,8 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     psp = tempsp;
                     tp = psp->data;
                     cm_ReleaseSCache(tscp);
-                    tscp = linkScp;    
+                    tscp = linkScp;
+                    linkScp = NULL;
                     /* already held
                      * by AssembleLink
                      * now, if linkScp is null, that's
@@ -1487,11 +1739,10 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                      * dir hierarchy.
                      */
                     if (tscp == NULL) {
-                        cm_HoldSCache(dirScp);
                         tscp = dirScp;
+                        dirScp = NULL;
                     }
-                }      /* if we have a sym link */
-                else {
+                } else {
                     /* not a symlink, we may be done */
                     lock_ReleaseMutex(&tscp->mx);
                     if (tc == 0) {
@@ -1500,22 +1751,38 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                             tp = pathp;
                             continue;
                         }
-                        cm_ReleaseSCache(dirScp);
+                        if (dirScp) {
+                            cm_ReleaseSCache(dirScp);
+                            dirScp = NULL;
+                        }
                         code = 0;
                         break;
                     }
                 }
-                cm_ReleaseSCache(dirScp);
+                if (dirScp) {
+                    cm_ReleaseSCache(dirScp);
+                    dirScp = NULL;
+                }
             } /* end of a component */
-            else *cp++ = tc;
+            else 
+                *cp++ = tc;
         } /* we have a component */
     } /* big while loop over all components */
 
     /* already held */
+    if (dirScp)
+        cm_ReleaseSCache(dirScp);
     if (psp) 
         cm_FreeSpace(psp);
     if (code == 0) 
         *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;
 }
 
@@ -1541,7 +1808,7 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
     cm_space_t *spacep;
     cm_scache_t *newRootScp;
 
-    osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
+    osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
 
     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
     if (code) 
@@ -1559,6 +1826,9 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
                      userp, NULL, reqp, outScpp);
 
+    if (code == CM_ERROR_NOSUCHFILE)
+        code = CM_ERROR_NOSUCHPATH;
+
     /* this stuff is allocated no matter what happened on the namei call,
      * so free it */
     cm_FreeSpace(spacep);
@@ -1571,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 {
@@ -1606,7 +1876,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     if (bsp->counter >= CM_BULKMAX)
         return CM_ERROR_STOPNOW;
 
-    thyper.LowPart = buf_bufferSize;
+    thyper.LowPart = cm_data.buf_blockSize;
     thyper.HighPart = 0;
     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
 
@@ -1631,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.
@@ -1669,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;
@@ -1688,22 +1959,25 @@ 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%x", (long) dscp);
+    osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
 
     /* should be on a buffer boundary */
-    osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
+    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);
     /* first, assemble the file IDs we need to stat */
     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
-    lock_ObtainMutex(&dscp->mx);
 
     /* if we failed, bail out early */
-    if (code && code != CM_ERROR_STOPNOW) return;
+    if (code && code != CM_ERROR_STOPNOW) {
+        lock_ObtainMutex(&dscp->mx);
+        return code;
+    }
 
     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
      * make the calls to create the entries.  Handle AFSCBMAX files at a
@@ -1712,7 +1986,8 @@ void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     filex = 0;
     while (filex < bb.counter) {
         filesThisCall = bb.counter - filex;
-        if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
+        if (filesThisCall > AFSCBMAX) 
+            filesThisCall = AFSCBMAX;
 
         fidStruct.AFSCBFids_len = filesThisCall;
         fidStruct.AFSCBFids_val = &bb.fids[filex];
@@ -1728,24 +2003,40 @@ 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);
-
-        osi_Log0(afsd_logp, "CALL BulkStatus DONE");
+        if (code)
+            osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
+                     inlinebulk ? "Inline" : "", code);
+        else
+            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.
          */
-        if (code) 
+        if (code) {
+            cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
             break;
+        }
 
         /* otherwise, we should do the merges */
-        for(i = 0; i<filesThisCall; i++) {
+        for (i = 0; i<filesThisCall; i++) {
             j = filex + i;
             tfid.cell = dscp->fid.cell;
             tfid.volume = bb.fids[j].Volume;
@@ -1769,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
@@ -1777,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);
@@ -1789,7 +2079,24 @@ 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 */
-    osi_Log0(afsd_logp, "END cm_TryBulkStat");
+    lock_ObtainMutex(&dscp->mx);
+
+#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)
@@ -1850,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;
@@ -1909,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);
@@ -1921,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;
@@ -1933,24 +2244,24 @@ 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);
 
-    lock_ReleaseMutex(&scp->mx);
-    if (code) 
-        return code;
-
-    /* now make the RPC */
-    osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
     tfid.Volume = scp->fid.volume;
     tfid.Vnode = scp->fid.vnode;
     tfid.Unique = scp->fid.unique;
+       lock_ReleaseMutex(&scp->mx);
+
+    /* now make the RPC */
+    osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
     do {
         code = cm_Conn(&scp->fid, userp, reqp, &connp);
         if (code) 
@@ -1965,7 +2276,10 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
                          &scp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
-    osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
+    if (code)
+        osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
 
     lock_ObtainMutex(&scp->mx);
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
@@ -2024,6 +2338,7 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) 
@@ -2044,6 +2359,11 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
                          &dscp->fid, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
         
+    if (code)
+        osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
@@ -2064,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);
@@ -2092,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;
@@ -2146,6 +2472,7 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) 
@@ -2166,6 +2493,11 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
                          &dscp->fid, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
         
+    if (code)
+        osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
@@ -2230,6 +2562,8 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
     if (code)
         return code;
 
+    /* try the RPC now */
+    osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) continue;
@@ -2246,13 +2580,18 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
             &newLinkStatus, &updatedDirStatus, &volSync);
         rx_PutConnection(callp);
-        osi_Log1(smb_logp,"  RXAFS_Link returns %d", code);
+        osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
 
     } while (cm_Analyze(connp, userp, reqp,
         &dscp->fid, &volSync, NULL, NULL, code));
 
     code = cm_MapRPCError(code, reqp);
 
+    if (code)
+        osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL Link SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
@@ -2292,6 +2631,7 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
+    osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) 
@@ -2311,6 +2651,11 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
                          &dscp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
         
+    if (code)
+        osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
@@ -2368,6 +2713,7 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
     didEnd = 0;
 
     /* try the RPC now */
+    osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
     do {
         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
         if (code) 
@@ -2385,11 +2731,16 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
     } while (cm_Analyze(connp, userp, reqp,
                          &dscp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCErrorRmdir(code, reqp);
-        
+
+    if (code)
+        osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
+
     lock_ObtainMutex(&dscp->mx);
-    cm_dnlcRemove(dscp, namep); 
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
+        cm_dnlcRemove(dscp, namep); 
         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
     }
     lock_ReleaseMutex(&dscp->mx);
@@ -2476,8 +2827,10 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
                 lock_ReleaseMutex(&newDscp->mx);
                 if (code) {
                     /* cleanup first one */
+                    lock_ObtainMutex(&oldDscp->mx);
                     cm_SyncOpDone(oldDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
+                    lock_ReleaseMutex(&oldDscp->mx);
                 }       
             }
         }
@@ -2496,8 +2849,10 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
                 lock_ReleaseMutex(&oldDscp->mx);
                 if (code) {
                     /* cleanup first one */
+                    lock_ObtainMutex(&newDscp->mx);
                     cm_SyncOpDone(newDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
+                    lock_ReleaseMutex(&newDscp->mx);
                 }       
             }
         }
@@ -2509,6 +2864,8 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
     didEnd = 0;
 
     /* try the RPC now */
+    osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
+              oldDscp, newDscp);
     do {
         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
         if (code) 
@@ -2532,6 +2889,11 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
                          &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
         
+    if (code)
+        osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
+    else
+        osi_Log0(afsd_logp, "CALL Rename SUCCESS");
+
     /* update the individual stat cache entries for the directories */
     lock_ObtainMutex(&oldDscp->mx);
     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
@@ -2556,162 +2918,1369 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
     return code;
 }
 
-long cm_Lock(cm_scache_t *scp, unsigned char LockType,
-              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
-              u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
-              void **lockpp)
-{
-    long code;
-    int Which = ((LockType & 0x1) ? LockRead : LockWrite);
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
-    cm_file_lock_t *fileLock;
-    osi_queue_t *q;
-    int found = 0;
-    struct rx_connection * callp;
+/* Byte range locks:
+
+   The OpenAFS Windows client has to fake byte range locks given no
+   server side support for such locks.  This is implemented as keyed
+   byte range locks on the cache manager.
+
+   Keyed byte range locks:
+
+   Each cm_scache_t structure keeps track of a list of keyed locks.
+   The key for a lock identifies an owner of a set of locks (referred
+   to as a client).  Each key is represented by a value.  The set of
+   key values used within a specific cm_scache_t structure form a
+   namespace that has a scope of just that cm_scache_t structure.  The
+   same key value can be used with another cm_scache_t structure and
+   correspond to a completely different client.  However it is
+   advantageous for the SMB or IFS layer to make sure that there is a
+   1-1 mapping between client and keys over all cm_scache_t objects.
+
+   Assume a client C has key Key(C) (although, since the scope of the
+   key is a cm_scache_t, the key can be Key(C,S), where S is the
+   cm_scache_t.  But assume a 1-1 relation between keys and clients).
+   A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
+   inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
+   through cm_generateKey() function for both SMB and IFS.
+
+   The list of locks for a cm_scache_t object S is maintained in
+   S->fileLocks.  The cache manager will set a lock on the AFS file
+   server in order to assert the locks in S->fileLocks.  If only
+   shared locks are in place for S, then the cache manager will obtain
+   a LockRead lock, while if there are any exclusive locks, it will
+   obtain a LockWrite lock.  If the exclusive locks are all released
+   while the shared locks remain, then the cache manager will
+   downgrade the lock from LockWrite to LockRead.  Similarly, if an
+   exclusive lock is obtained when only shared locks exist, then the
+   cache manager will try to upgrade the lock from LockRead to
+   LockWrite.
+
+   Each lock L owned by client C maintains a key L->key such that
+   L->key == Key(C), the effective range defined by L->LOffset and
+   L->LLength such that the range of bytes affected by the lock is
+   (L->LOffset, +L->LLength), a type maintained in L->LockType which
+   is either exclusive or shared.
+
+   Lock states:
+
+   A lock exists iff it is in S->fileLocks for some cm_scache_t
+   S. Existing locks are in one of the following states: ACTIVE,
+   WAITLOCK, WAITUNLOCK, LOST, DELETED.
+
+   The following sections describe each lock and the associated
+   transitions.
+
+   1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
+      the lock with the AFS file server.  This type of lock can be
+      exercised by a client to read or write to the locked region (as
+      the lock allows).
+
+      1.1 ACTIVE->LOST: When the AFS file server fails to extend a
+        server lock that was required to assert the lock.  Before
+        marking the lock as lost, the cache manager checks if the file
+        has changed on the server.  If the file has not changed, then
+        the cache manager will attempt to obtain a new server lock
+        that is sufficient to assert the client side locks for the
+        file.  If any of these fail, the lock is marked as LOST.
+        Otherwise, it is left as ACTIVE.
+
+      1.2 ACTIVE->DELETED: Lock is released.
+
+   2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
+      grants the lock but the lock is yet to be asserted with the AFS
+      file server.  Once the file server grants the lock, the state
+      will transition to an ACTIVE lock.
 
-    /* Look for a conflict.  Also, if we are asking for a shared lock,
-     * look for another shared lock, so we don't have to do an RPC.
-     */
-    q = scp->fileLocks;
-    while (q) {
-        fileLock = (cm_file_lock_t *)
-            ((char *) q - offsetof(cm_file_lock_t, fileq));
-        if ((fileLock->flags &
-              (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
-             == 0) {
-            if ((LockType & 0x1) == 0
-                 || (fileLock->LockType & 0x1) == 0)
-                return CM_ERROR_WOULDBLOCK;
-            found = 1;
-        }
-        q = osi_QNext(q);
-    }
+      2.1 WAITLOCK->ACTIVE: The server granted the lock.
 
-    if (found)
-        code = 0;
-    else {
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        lock_ReleaseMutex(&scp->mx);
-        do {
-            code = cm_Conn(&scp->fid, userp, reqp, &connp);
-            if (code) 
-                break;
+      2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
+        waiting.
 
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_SetLock(callp, &tfid, Which,
-                                  &volSync);
-            rx_PutConnection(callp);
+      2.3 WAITLOCK->LOST: One or more locks from this client were
+        marked as LOST.  No further locks will be granted to this
+        client until all lost locks are removed.
 
-        } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
-                             NULL, NULL, code));
-        lock_ObtainMutex(&scp->mx);
-        code = cm_MapRPCError(code, reqp);
-    }
+   3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
+      receives a request for a lock that conflicts with an existing
+      ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
+      and will be granted at such time the conflicting locks are
+      removed, at which point the state will transition to either
+      WAITLOCK or ACTIVE.
+
+      3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
+        current serverLock is sufficient to assert this lock, or a
+        sufficient serverLock is obtained.
+
+      3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
+        however the required serverLock is yet to be asserted with the
+        server.
 
-    if (code == 0 || Timeout != 0) {
-        fileLock = malloc(sizeof(cm_file_lock_t));
-        fileLock->LockType = LockType;
-        cm_HoldUser(userp);
-        fileLock->userp = userp;
-        fileLock->fid = scp->fid;
-        fileLock->LOffset = LOffset;
-        fileLock->LLength = LLength;
-        fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
-        osi_QAdd(&scp->fileLocks, &fileLock->fileq);
-        lock_ObtainWrite(&cm_scacheLock);
-        osi_QAdd(&cm_allFileLocks, &fileLock->q);
-        lock_ReleaseWrite(&cm_scacheLock);
-        if (code != 0) 
-            *lockpp = fileLock;
-    }
-    return code;
-}
+      3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
+        released.
 
-long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
-                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
-                cm_user_t *userp, cm_req_t *reqp)
-{
-    long code = 0;
-    int Which = ((LockType & 0x1) ? LockRead : LockWrite);
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
-    cm_file_lock_t *fileLock, *ourLock;
-    osi_queue_t *q, *qq;
-    int anotherReader = 0;
-    int smallLock = 0;
-    int found = 0;
-    struct rx_connection * callp;
+      3.5 WAITUNLOCK->LOST: One or more locks from this client were
+        marked as LOST.  No further locks will be granted to this
+        client until all lost locks are removed.
+
+   4. LOST: A lock L is LOST if the server lock that was required to
+      assert the lock could not be obtained or if it could not be
+      extended, or if other locks by the same client were LOST.
+      Essentially, once a lock is LOST, the contract between the cache
+      manager and that specific client is no longer valid.
+
+      The cache manager rechecks the server lock once every minute and
+      extends it as appropriate.  If this is not done for 5 minutes,
+      the AFS file server will release the lock (the 5 minute timeout
+      is based on current file server code and is fairly arbitrary).
+      Once released, the lock cannot be re-obtained without verifying
+      that the contents of the file hasn't been modified since the
+      time the lock was released.  Re-obtaining the lock without
+      verifying this may lead to data corruption.  If the lock can not
+      be obtained safely, then all active locks for the cm_scache_t
+      are marked as LOST.
 
-    if (LargeIntegerLessThan(LLength, scp->length))
-        smallLock = 1;
+      4.1 LOST->DELETED: The lock is released.
 
-    /* Look for our own lock on the list, so as to remove it.
-     * Also, determine if we're the last reader; if not, avoid an RPC.
-     */
-    q = scp->fileLocks;
-    while (q) {
-        fileLock = (cm_file_lock_t *)
-            ((char *) q - offsetof(cm_file_lock_t, fileq));
-        if (!found
-             && fileLock->userp == userp
-             && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
-             && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
-            found = 1;
-            ourLock = fileLock;
-            qq = q;
-        }
-        else if (fileLock->LockType & 0x1)
-            anotherReader = 1;
-        q = osi_QNext(q);
-    }
+   5. DELETED: The lock is no longer relevant.  Eventually, it will
+      get removed from the cm_scache_t. In the meantime, it will be
+      treated as if it does not exist.
 
-    /* ignore byte ranges */
-    if (smallLock && !found)
-        return 0;
+      5.1 DELETED->not exist: The lock is removed from the
+        cm_scache_t.
 
-    /* don't try to unlock other people's locks */
-    if (!found)
-        return CM_ERROR_WOULDBLOCK;
+   The following are classifications of locks based on their state.
 
-    /* discard lock record */
-    osi_QRemove(&scp->fileLocks, qq);
-    /*
-     * Don't delete it here; let the daemon delete it, to simplify
-     * the daemon's traversal of the list.
-     */
-    lock_ObtainWrite(&cm_scacheLock);
-    ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
-    cm_ReleaseUser(ourLock->userp);
+   6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
+      have been accepted by the cache manager, but may or may not have
+      been granted back to the client.
+
+   7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
+
+   8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
+
+   Lock operation:
+
+   A client C can READ range (Offset,+Length) of a file represented by
+   cm_scache_t S iff (1):
+
+   1. for all _a_ in (Offset,+Length), all of the following is true:
+
+       1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
+         (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
+         shared.
+
+       1.2 For each LOST lock L in S->fileLocks such that _a_ in
+         (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
+         Key(C)
+
+       (When locks are lost on an cm_scache_t, all locks are lost.  By
+       4.2 (below), if there is an exclusive LOST lock, then there
+       can't be any overlapping ACTIVE locks.)
+
+   A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
+
+   2. for all _a_ in (Offset,+Length), one of the following is true:
+
+       2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
+         does not exist a LOST lock L such that _a_ in
+         (L->LOffset,+L->LLength).
+
+       2.2 Byte _a_ of S is owned by C under lock L (as specified in
+         1.2) AND L->LockType is exclusive.
+
+   A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
+
+   3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
+      true:
+
+       3.1 If L->LockType is exclusive then there does NOT exist a
+         ACCEPTED lock M in S->fileLocks such that _a_ in
+         (M->LOffset,+M->LLength).
+
+         (If we count all QUEUED locks then we hit cases such as
+         cascading waiting locks where the locks later on in the queue
+         can be granted without compromising file integrity.  On the
+         other hand if only ACCEPTED locks are considered, then locks
+         that were received earlier may end up waiting for locks that
+         were received later to be unlocked. The choice of ACCEPTED
+         locks was made to mimic the Windows byte range lock
+         semantics.)
+
+       3.2 If L->LockType is shared then for each ACCEPTED lock M in
+         S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
+         M->LockType is shared.
+
+   4. For all LOST locks M in S->fileLocks, ALL of the following are true:
+
+       4.1 M->key != Key(C)
+
+       4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
+         and (M->LOffset,+M->LLength) do not intersect.
+
+         (Note: If a client loses a lock, it loses all locks.
+         Subsequently, it will not be allowed to obtain any more locks
+         until all existing LOST locks that belong to the client are
+         released.  Once all locks are released by a single client,
+         there exists no further contract between the client and AFS
+         about the contents of the file, hence the client can then
+         proceed to obtain new locks and establish a new contract.
+
+         This doesn't quite work as you think it should, because most
+         applications aren't built to deal with losing locks they
+         thought they once had.  For now, we don't have a good
+         solution to lost locks.
+
+         Also, for consistency reasons, we have to hold off on
+         granting locks that overlap exclusive LOST locks.)
+
+   A client C can only unlock locks L in S->fileLocks which have
+   L->key == Key(C).
+
+   The representation and invariants are as follows:
+
+   - Each cm_scache_t structure keeps:
+
+       - A queue of byte-range locks (cm_scache_t::fileLocks) which
+         are of type cm_file_lock_t.
+
+       - A record of the highest server-side lock that has been
+         obtained for this object (cm_scache_t::serverLock), which is
+         one of (-1), LockRead, LockWrite.
+
+       - A count of ACCEPTED exclusive and shared locks that are in the
+         queue (cm_scache_t::sharedLocks and
+         cm_scache_t::exclusiveLocks)
+
+   - Each cm_file_lock_t structure keeps:
+
+       - The type of lock (cm_file_lock_t::LockType)
+
+       - The key associated with the lock (cm_file_lock_t::key)
+
+       - The offset and length of the lock (cm_file_lock_t::LOffset
+         and cm_file_lock_t::LLength)
+
+       - The state of the lock.
+
+       - Time of issuance or last successful extension
+
+   Semantic invariants:
+
+       I1. The number of ACCEPTED locks in S->fileLocks are
+           (S->sharedLocks + S->exclusiveLocks)
+
+   External invariants:
+
+       I3. S->serverLock is the lock that we have asserted with the
+           AFS file server for this cm_scache_t.
+
+       I4. S->serverLock == LockRead iff there is at least one ACTIVE
+           shared lock, but no ACTIVE exclusive locks.
+
+       I5. S->serverLock == LockWrite iff there is at least one ACTIVE
+           exclusive lock.
+
+       I6. If L is a LOST lock, then for each lock M in S->fileLocks,
+           M->key == L->key IMPLIES M is LOST or DELETED.
+
+   --asanka
+ */
+
+#define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
+
+#define IS_LOCK_WAITLOCK(lockp)   (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
+
+#define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
+
+#define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
+
+#define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
+
+/* unsafe */
+#define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
+
+/* unsafe */
+#define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
+
+/* unsafe */
+#define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
+
+/* unsafe */
+#define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
+
+#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)
+{
+    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);
+
+    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) {
+            pos->length = int_begin - pos->offset;
+        }
+
+        /* We only subtract ranges if the resulting range is
+           contiguous.  If we try to support non-contigous ranges, we
+           aren't actually improving performance. */
+    }
+}
+
+/* Called with scp->mx held.  Returns 0 if all is clear to read the
+   specified range by the client identified by key.
+ */
+long cm_LockCheckRead(cm_scache_t *scp, 
+                      LARGE_INTEGER LOffset, 
+                      LARGE_INTEGER LLength, 
+                      cm_key_t key)
+{
+#ifndef ADVISORY_LOCKS
+
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q;
+    long code = 0;
+    cm_range_t range;
+    int substract_ranges = FALSE;
+
+    range.offset = LOffset.QuadPart;
+    range.length = LLength.QuadPart;
+
+    /*
+
+     1. for all _a_ in (Offset,+Length), all of the following is true:
+
+       1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
+         (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
+         shared.
+
+       1.2 For each LOST lock L in S->fileLocks such that _a_ in
+         (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
+         Key(C)
+
+    */
+
+    lock_ObtainRead(&cm_scacheLock);
+
+    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 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) {
+                        code = CM_ERROR_LOCK_CONFLICT;
+                        break;
+                    }
+
+                    /* even if the entire range is locked for reading,
+                       we still can't grant the lock at this point
+                       because the client may have lost locks. That
+                       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)
+                        cm_LockRangeSubtract(&range, &fileLock->range);
+                }
+            } else if (IS_LOCK_LOST(fileLock) &&
+                      (fileLock->key == key || fileLock->lockType == LockWrite)) {
+                code = CM_ERROR_BADFD;
+                break;
+            }
+        }
+    }
+
+    lock_ReleaseRead(&cm_scacheLock);
+
+    osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
+             scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
+
+    return code;
+
+#else
+
+    return 0;
+
+#endif
+}
+
+/* Called with scp->mx held.  Returns 0 if all is clear to write the
+   specified range by the client identified by key.
+ */
+long cm_LockCheckWrite(cm_scache_t *scp,
+                       LARGE_INTEGER LOffset,
+                       LARGE_INTEGER LLength,
+                       cm_key_t key)
+{
+#ifndef ADVISORY_LOCKS
+
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q;
+    long code = 0;
+    cm_range_t range;
+
+    range.offset = LOffset.QuadPart;
+    range.length = LLength.QuadPart;
+
+    /*
+   A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
+
+   2. for all _a_ in (Offset,+Length), one of the following is true:
+
+       2.1 Byte _a_ of S is unowned AND there does not exist a LOST
+         lock L such that _a_ in (L->LOffset,+L->LLength).
+
+       2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
+         exclusive.
+    */
+
+    lock_ObtainRead(&cm_scacheLock);
+
+    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 there is an active lock for this client, it
+                           is safe to substract ranges */
+                        cm_LockRangeSubtract(&range, &fileLock->range);
+                    } else {
+                        code = CM_ERROR_LOCK_CONFLICT;
+                        break;
+                    }
+                } else {
+                    code = CM_ERROR_LOCK_CONFLICT;
+                    break;
+                }
+            } else if (IS_LOCK_LOST(fileLock)) {
+                code = CM_ERROR_BADFD;
+                break;
+            }
+        }
+    }
+
+    lock_ReleaseRead(&cm_scacheLock);
+
+    osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
+             scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
+
+    return code;
+
+#else
+
+    return 0;
+
+#endif
+}
+
+/* Forward dcl. */
+static void cm_LockMarkSCacheLost(cm_scache_t * scp);
+
+/* Called with cm_scacheLock write locked */
+static cm_file_lock_t * cm_GetFileLock(void) {
+    cm_file_lock_t * l;
+
+    l = (cm_file_lock_t *) cm_freeFileLocks;
+    if (l) {
+        osi_QRemove(&cm_freeFileLocks, &l->q);
+    } else {
+        l = malloc(sizeof(cm_file_lock_t));
+        osi_assert(l);
+    }
+
+    memset(l, 0, sizeof(cm_file_lock_t));
+
+    return l;
+}
+
+/* Called with cm_scacheLock write locked */
+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,
+             cm_key_t key,
+             int allowWait, cm_user_t *userp, cm_req_t *reqp,
+             cm_file_lock_t **lockpp)
+{
+    long code = 0;
+    int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q;
+    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);
+    osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait, 
+             (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
+
+    /*
+   A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
+
+   3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
+      true:
+
+       3.1 If L->LockType is exclusive then there does NOT exist a
+         ACCEPTED lock M in S->fileLocks such that _a_ in
+         (M->LOffset,+M->LLength).
+
+       3.2 If L->LockType is shared then for each ACCEPTED lock M in
+         S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
+         M->LockType is shared.
+
+   4. For all LOST locks M in S->fileLocks, ALL of the following are true:
+
+       4.1 M->key != Key(C)
+
+       4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
+         and (M->LOffset,+M->LLength) do not intersect.
+    */
+
+    range.offset = LOffset.QuadPart;
+    range.length = LLength.QuadPart;
+
+    lock_ObtainRead(&cm_scacheLock);
+
+    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 (fileLock->key == key) {
+                code = CM_ERROR_BADFD;
+                break;
+            } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
+                code = CM_ERROR_WOULDBLOCK;
+                wait_unlock = TRUE;
+                break;
+            }
+        }
+
+        /* we don't need to check for deleted locks here since deleted
+           locks are dequeued from scp->fileLocks */
+        if (IS_LOCK_ACCEPTED(fileLock) &&
+           INTERSECT_RANGE(range, fileLock->range)) {
+
+            if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
+                fileLock->lockType != LockRead) {
+                wait_unlock = TRUE;
+                code = CM_ERROR_WOULDBLOCK;
+                break;
+            }
+        }
+    }
+
+    lock_ReleaseRead(&cm_scacheLock);
+
+    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 = 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);
+
+            /* 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 {
+            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.");
+                osi_Log1(afsd_logp,
+                         "   dataVersion on scp: %d", scp->dataVersion);
+
+                /* 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;
+        
+                code = cm_IntReleaseLock(scp, userp, reqp);
+
+                if (code) {
+                    /* We couldn't release the lock */
+                    goto check_code;
+                } else {
+                    scp->serverLock = -1;
+                }
+            }
+
+            /* We need to obtain a server lock of type Which in order
+               to assert this file lock */
+#ifndef AGGRESSIVE_LOCKS
+            newLock = Which;
+#else
+            newLock = LockWrite;
+#endif
+            code = cm_IntSetLock(scp, userp, newLock, reqp);
+
+            if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
+                /* we wanted LockRead.  We tried LockWrite. Now try
+                   LockRead again */
+                newLock = Which;
+
+                /* am I sane? */
+                osi_assert(newLock == LockRead);
+
+                code = cm_IntSetLock(scp, userp, newLock, reqp);
+            }
+
+            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. */
+
+                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);
+
+                code = cm_IntReleaseLock(scp, userp, reqp);
+
+                scp->serverLock = -1;
+
+                code = CM_ERROR_INVAL;
+            } else if (code == 0) {
+                scp->serverLock = newLock;
+                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");
+    }
+
+ check_code:
+
+    if (code != 0 && !force_client_lock) {
+        /* Special case error translations
+
+           Applications don't expect certain errors from a
+           LockFile/UnlockFile call.  We need to translate some error
+           code to codes that apps expect and handle. */
+
+        /* We shouldn't actually need to handle this case since we
+           simulate locks for RO scps anyway. */
+        if (code == CM_ERROR_READONLY) {
+            osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
+            code = CM_ERROR_NOACCESS;
+        }
+    }
+
+    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();
+        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 : 
+                           ((wait_unlock)?
+                            CM_FILELOCK_FLAG_WAITUNLOCK :
+                            CM_FILELOCK_FLAG_WAITLOCK));
+
+        if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
+            fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
+
+        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);
+        lock_ReleaseWrite(&cm_scacheLock);
+
+        if (code != 0) {
+            *lockpp = fileLock;
+        }
+
+        if (IS_LOCK_CLIENTONLY(fileLock)) {
+            scp->clientLocks++;
+        } else if (IS_LOCK_ACCEPTED(fileLock)) {
+            if (Which == LockRead)
+                scp->sharedLocks++;
+            else
+                scp->exclusiveLocks++;
+        }
+
+        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;
+}
+
+static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
+
+/* Called with scp->mx held */
+long cm_UnlockByKey(cm_scache_t * scp,
+                   cm_key_t key,
+                   int 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:%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) {
+        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_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)) {
+            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_assert(FALSE);
+        }
+#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");
+    osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
+
+    if (!SERVERLOCKS_ENABLED(scp)) {
+        osi_Log0(afsd_logp, "  Skipping server lock 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");
+
+        /* 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);
+
+        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) {
+            /* 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);
+            
+            code = cm_IntReleaseLock(scp, userp, reqp);
+
+            code = CM_ERROR_INVAL;
+            scp->serverLock = -1;
+        }
+
+        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 */
+
+        code = cm_IntReleaseLock(scp, userp, reqp);
+
+        if (code == 0)
+            scp->serverLock = (-1);
+    }
+
+ done:
+
+    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);
+
+    return code;
+}
+
+long cm_Unlock(cm_scache_t *scp, 
+               unsigned char sLockType,
+               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
+               cm_key_t key, 
+               cm_user_t *userp, 
+               cm_req_t *reqp)
+{
+    long code = 0;
+    int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q;
+    int release_userp = FALSE;
+
+    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);
+    osi_Log2(afsd_logp, "... key 0x%x:%x",
+             (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
+
+    lock_ObtainRead(&cm_scacheLock);
+
+    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)) {
+            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_assert(FALSE);
+        }
+#endif
+        if (!IS_LOCK_DELETED(fileLock) &&
+            fileLock->key == key &&
+            fileLock->range.offset == LOffset.QuadPart &&
+            fileLock->range.length == LLength.QuadPart) {
+            break;
+        }
+    }
+
+    if (!q) {
+        osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
+        
+        lock_ReleaseRead(&cm_scacheLock);
+
+        /* The lock didn't exist anyway. *shrug* */
+        return 0;
+    }
+
+    lock_ReleaseRead(&cm_scacheLock);
+
+    /* discard lock record */
+    lock_ObtainWrite(&cm_scacheLock);
+    if (scp->fileLocksT == q)
+        scp->fileLocksT = osi_QPrev(q);
+    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.
+     */
+
+    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;
+    if (userp != NULL) {
+        cm_ReleaseUser(fileLock->userp);
+    } else {
+        userp = fileLock->userp;
+        release_userp = TRUE;
+    }
+    fileLock->userp = NULL;
+    cm_ReleaseSCacheNoLock(scp);
+    fileLock->scp = NULL;
     lock_ReleaseWrite(&cm_scacheLock);
 
-    if (!anotherReader) {
-        tfid.Volume = scp->fid.volume;
-        tfid.Vnode = scp->fid.vnode;
-        tfid.Unique = scp->fid.unique;
-        lock_ReleaseMutex(&scp->mx);
-        do {
-            code = cm_Conn(&scp->fid, userp, reqp, &connp);
-            if (code) 
-                break;
+    if (!SERVERLOCKS_ENABLED(scp)) {
+        osi_Log0(afsd_logp, "   Skipping server locks for scp");
+        goto done;
+    }
 
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-            rx_PutConnection(callp);
+    /* 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.
+     */
 
-        } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
-                             NULL, NULL, code));
-        code = cm_MapRPCError(code, reqp);
-        lock_ObtainMutex(&scp->mx);
+    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");
+
+        /* 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);
+
+        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=%d, after=%d",
+                     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 */
+
+        code = cm_IntReleaseLock(scp, userp, reqp);
+
+        if (code == 0) {
+            scp->serverLock = (-1);
+        }
     }
 
+    if (release_userp)
+        cm_ReleaseUser(userp);
+
+ done:
+
+    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;
 }
 
+/* called with scp->mx held */
+static void cm_LockMarkSCacheLost(cm_scache_t * scp)
+{
+    cm_file_lock_t *fileLock;
+    osi_queue_t *q;
+
+    osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
+
+#ifdef DEBUG
+    /* With the current code, we can't lose a lock on a RO scp */
+    osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
+#endif
+
+    /* cm_scacheLock needed because we are modifying fileLock->flags */
+    lock_ObtainWrite(&cm_scacheLock);
+
+    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) &&
+            !IS_LOCK_CLIENTONLY(fileLock)) {
+            if (fileLock->lockType == LockRead)
+                scp->sharedLocks--;
+            else
+                scp->exclusiveLocks--;
+
+            fileLock->flags |= CM_FILELOCK_FLAG_LOST;
+        }
+    }
+
+    scp->serverLock = -1;
+    scp->lockDataVersion = -1;
+    lock_ReleaseWrite(&cm_scacheLock);
+}
+
+/* Called with no relevant locks held */
 void cm_CheckLocks()
 {
     osi_queue_t *q, *nq;
@@ -2722,128 +4291,503 @@ void cm_CheckLocks()
     cm_conn_t *connp;
     long code;
     struct rx_connection * callp;
+    cm_scache_t * scp;
 
     cm_InitReq(&req);
 
     lock_ObtainWrite(&cm_scacheLock);
-    q = cm_allFileLocks;
-    while (q) {
+
+    cm_lockRefreshCycle++;
+
+    osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
+
+    for (q = cm_allFileLocks; q; q = nq) {
         fileLock = (cm_file_lock_t *) q;
         nq = osi_QNext(q);
-        if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
+       code = -1;
+
+        if (IS_LOCK_DELETED(fileLock)) {
+
             osi_QRemove(&cm_allFileLocks, q);
-            free(fileLock);
-        }
-        else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
-            tfid.Volume = fileLock->fid.volume;
-            tfid.Vnode = fileLock->fid.vnode;
-            tfid.Unique = fileLock->fid.unique;
-            lock_ReleaseWrite(&cm_scacheLock);
-            do {
-                code = cm_Conn(&fileLock->fid, fileLock->userp,
-                                &req, &connp);
-                if (code) 
-                    break;
+            cm_PutFileLock(fileLock);
+
+        } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
+
+            /* Server locks must have been enabled for us to have
+               received an active non-client-only lock. */
+            osi_assert(cm_enableServerLocks);
+
+            scp = fileLock->scp;
+            osi_assert(scp != NULL);
+
+            cm_HoldSCacheNoLock(scp);
+
+#ifdef DEBUG
+            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_assert(FALSE);
+            }
+#endif
+            /* Server locks are extended once per scp per refresh
+               cycle. */
+            if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
+
+                int scp_done = FALSE;
+
+                osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
+
+                lock_ReleaseWrite(&cm_scacheLock);
+                lock_ObtainMutex(&scp->mx);
+
+                /* did the lock change while we weren't holding the lock? */
+                if (!IS_LOCK_ACTIVE(fileLock))
+                    goto post_syncopdone;
+
+                code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
+                                 CM_SCACHESYNC_NEEDCALLBACK
+                                 | CM_SCACHESYNC_GETSTATUS
+                                 | CM_SCACHESYNC_LOCK);
+
+                if (code) {
+                    osi_Log1(smb_logp,
+                             "cm_CheckLocks SyncOp failure code 0x%x", code);
+                    goto post_syncopdone;
+                }
+
+                /* cm_SyncOp releases scp->mx during which the lock
+                   may get released. */
+                if (!IS_LOCK_ACTIVE(fileLock))
+                    goto pre_syncopdone;
+
+                if (scp->serverLock != -1) {
+                    cm_fid_t cfid;
+                    cm_user_t * userp;
+
+                    tfid.Volume = scp->fid.volume;
+                    tfid.Vnode = scp->fid.vnode;
+                    tfid.Unique = scp->fid.unique;
+                    cfid = scp->fid;
+                    userp = fileLock->userp;
+                    
+                    osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d", 
+                             fileLock,
+                             scp,
+                             (int) scp->serverLock);
+
+                    lock_ReleaseMutex(&scp->mx);
+
+                    do {
+                        code = cm_Conn(&cfid, userp,
+                                       &req, &connp);
+                        if (code) 
+                            break;
+
+                        callp = cm_GetRxConn(connp);
+                        code = RXAFS_ExtendLock(callp, &tfid,
+                                                &volSync);
+                        rx_PutConnection(callp);
+
+                        osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
+
+                    } while (cm_Analyze(connp, userp, &req,
+                                        &cfid, &volSync, NULL, NULL,
+                                        code));
+
+                    code = cm_MapRPCError(code, &req);
+
+                    lock_ObtainMutex(&scp->mx);
+
+                    if (code) {
+                        osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
+                    } 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
+                       serverLock */
+                    cm_LockMarkSCacheLost(scp);
+                }
+
+                scp_done = TRUE;
+
+            pre_syncopdone:
+
+                cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+            post_syncopdone:
+                lock_ReleaseMutex(&scp->mx);
+
+                lock_ObtainWrite(&cm_scacheLock);
+
+                if (code == 0) {
+                    fileLock->lastUpdate = time(NULL);
+                }
+                
+                if (scp_done)
+                    scp->lastRefreshCycle = cm_lockRefreshCycle;
+
+            } else {
+                /* we have already refreshed the locks on this scp */
+                fileLock->lastUpdate = time(NULL);
+            }
 
-                callp = cm_GetRxConn(connp);
-                code = RXAFS_ExtendLock(callp, &tfid,
-                                         &volSync);
-                rx_PutConnection(callp);
+            cm_ReleaseSCacheNoLock(scp);
 
-            } while (cm_Analyze(connp, fileLock->userp, &req,
-                                 &fileLock->fid, &volSync, NULL, NULL,
-                                 code));
-            code = cm_MapRPCError(code, &req);
-            lock_ObtainWrite(&cm_scacheLock);
+        } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
+            /* TODO: Check callbacks */
         }
-        q = nq;
     }
+
     lock_ReleaseWrite(&cm_scacheLock);
-}       
+    osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
+}
 
-long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
+/* NOT called with scp->mx held. */
+long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 {
-    long code;
-    int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
-    cm_scache_t *scp;
-    AFSFid tfid;
-    AFSVolSync volSync;
-    cm_conn_t *connp;
+    long code = 0;
+    cm_scache_t *scp = NULL;
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
     cm_req_t req;
-    int found = 0;
-    struct rx_connection * callp;
+    int newLock = -1;
+    int force_client_lock = FALSE;
+
+    cm_InitReq(&req);
 
-    if (vcp_is_dead) {
+    if (client_is_dead) {
         code = CM_ERROR_TIMEDOUT;
-        goto handleCode;
+        goto updateLock;
     }
 
-    cm_InitReq(&req);
+    lock_ObtainRead(&cm_scacheLock);
+
+    osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
+    osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
+             (unsigned)(oldFileLock->range.offset >> 32),
+             (unsigned)(oldFileLock->range.offset & 0xffffffff),
+             (unsigned)(oldFileLock->range.length >> 32),
+             (unsigned)(oldFileLock->range.length & 0xffffffff));
+    osi_Log3(afsd_logp, "    key(%x:%x) flags=%x",
+             (unsigned)(oldFileLock->key >> 32),
+             (unsigned)(oldFileLock->key & 0xffffffff),
+             (unsigned)(oldFileLock->flags));
+
+    /* if the lock has already been granted, then we have nothing to do */
+    if (IS_LOCK_ACTIVE(oldFileLock)) {
+        lock_ReleaseRead(&cm_scacheLock);
+        osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
+        return 0;
+    }
 
-    /* Look for a conflict.  Also, if we are asking for a shared lock,
-     * look for another shared lock, so we don't have to do an RPC.
-     */
-    code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
-    if (code)
+    /* we can't do anything with lost or deleted locks at the moment. */
+    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);
+        goto updateLock;
+    }
+
+    scp = oldFileLock->scp;
+
+    osi_assert(scp != NULL);
+
+    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;
+    }
 
-    q = scp->fileLocks;
-    while (q) {
-        fileLock = (cm_file_lock_t *)
-            ((char *) q - offsetof(cm_file_lock_t, fileq));
-        if ((fileLock->flags &
-              (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
-             == 0) {
-            if ((oldFileLock->LockType & 0x1) == 0
-                 || (fileLock->LockType & 0x1) == 0) {
-                cm_ReleaseSCache(scp);
-                return CM_ERROR_WOULDBLOCK;
+    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)) {
+
+        oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
+
+        if (SERVERLOCKS_ENABLED(scp)) {
+            osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
+                     (int) scp->serverLock);
+        } else {
+            osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
+        }
+
+        lock_ReleaseWrite(&cm_scacheLock);
+        lock_ReleaseMutex(&scp->mx);
+
+        return 0;
+    }
+
+    if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
+
+        /* check if the conflicting locks have dissappeared already */
+        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 (fileLock->key == oldFileLock->key) {
+                    code = CM_ERROR_BADFD;
+                    oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
+                    osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
+                             fileLock);
+                    break;
+                } else if (fileLock->lockType == LockWrite &&
+                           INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
+                    osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
+                    code = CM_ERROR_WOULDBLOCK;
+                    break;
+                }
+            }
+
+            if (IS_LOCK_ACCEPTED(fileLock) &&
+                INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
+
+                if (oldFileLock->lockType != LockRead ||
+                   fileLock->lockType != LockRead) {
+
+                    osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
+                    code = CM_ERROR_WOULDBLOCK;
+                    break;
+                }
             }
-            found = 1;
         }
-        q = osi_QNext(q);
     }
 
-    if (found)
-        code = 0;
-    else {
-        tfid.Volume = oldFileLock->fid.volume;
-        tfid.Vnode = oldFileLock->fid.vnode;
-        tfid.Unique = oldFileLock->fid.unique;
-        do {
-            code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
-                            &req, &connp);
-            if (code) 
-                break;
+    if (code != 0) {
+        lock_ReleaseWrite(&cm_scacheLock);
+        lock_ReleaseMutex(&scp->mx);
 
-            callp = cm_GetRxConn(connp);
-            code = RXAFS_SetLock(callp, &tfid, Which,
-                                  &volSync);
-            rx_PutConnection(callp);
+        goto handleCode;
+    }
+
+    /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
+       If it is WAITUNLOCK, then we didn't find any conflicting lock
+       but we haven't verfied whether the serverLock is sufficient to
+       assert it.  If it is WAITLOCK, then the serverLock is
+       insufficient to assert it. Eitherway, we are ready to accept
+       the lock as either ACTIVE or WAITLOCK depending on the
+       serverLock. */
+
+    /* First, promote the WAITUNLOCK to a WAITLOCK */
+    if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
+        if (oldFileLock->lockType == LockRead)
+            scp->sharedLocks++;
+        else
+            scp->exclusiveLocks++;
+
+        oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
+        oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
+    }
+
+    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_user_t * userp;
+
+        code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
+                         CM_SCACHESYNC_NEEDCALLBACK
+                        | CM_SCACHESYNC_GETSTATUS
+                        | CM_SCACHESYNC_LOCK);
+        if (code) {
+            osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
+            lock_ReleaseWrite(&cm_scacheLock);
+            goto post_syncopdone;
+        }
+
+        if (!IS_LOCK_WAITLOCK(oldFileLock))
+            goto pre_syncopdone;
+
+        userp = oldFileLock->userp;
+
+#ifndef AGGRESSIVE_LOCKS
+        newLock = oldFileLock->lockType;
+#else
+        newLock = LockWrite;
+#endif
+
+        lock_ReleaseWrite(&cm_scacheLock);
 
-        } while (cm_Analyze(connp, oldFileLock->userp, &req,
-                             &oldFileLock->fid, &volSync,
-                             NULL, NULL, code));
-        code = cm_MapRPCError(code, &req);
+        code = cm_IntSetLock(scp, userp, newLock, &req);
+
+    pre_syncopdone:
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+    post_syncopdone:
+        ;
     }
 
   handleCode:
     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
-        lock_ObtainMutex(&scp->mx);
-        osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
-        lock_ReleaseMutex(&scp->mx);
+       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);
+    } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
+        scp->serverLock = newLock;
     }
+    lock_ReleaseMutex(&scp->mx);
+
+  updateLock:
     lock_ObtainWrite(&cm_scacheLock);
-    if (code == 0)
-        oldFileLock->flags = 0;
-    else if (code != CM_ERROR_WOULDBLOCK) {
-        oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
+    if (code == 0) {
+        oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
+    } else if (code != CM_ERROR_WOULDBLOCK) {
+        oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
         cm_ReleaseUser(oldFileLock->userp);
         oldFileLock->userp = NULL;
+        if (oldFileLock->scp) {
+            cm_ReleaseSCacheNoLock(oldFileLock->scp);
+            oldFileLock->scp = NULL;
+        }
     }
     lock_ReleaseWrite(&cm_scacheLock);
 
     return code;
 }
+
+cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int 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)
+{
+    if (flags & CM_UNLOCK_BY_FID) {
+        return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
+    } else {
+        return (k1 == k2);
+    }
+}
+
+void cm_ReleaseAllLocks(void)
+{
+    cm_scache_t *scp;
+    cm_req_t req;
+    cm_user_t *userp;
+    cm_key_t   key;
+    cm_file_lock_t *fileLock;
+    unsigned int i;
+
+    for (i = 0; i < cm_data.hashTableSize; i++)
+    {
+       for ( scp = cm_data.hashTablep[i]; scp; scp = scp->nextp ) {
+           while (scp->fileLocksH != NULL) {
+               lock_ObtainMutex(&scp->mx);
+               lock_ObtainWrite(&cm_scacheLock);
+               if (!scp->fileLocksH) {
+                   lock_ReleaseWrite(&cm_scacheLock);
+                   lock_ReleaseMutex(&scp->mx);
+                   break;
+               }
+               fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
+               userp = fileLock->userp;
+               cm_HoldUser(userp);
+               key = fileLock->key;
+               cm_HoldSCacheNoLock(scp);
+               lock_ReleaseWrite(&cm_scacheLock);
+               cm_UnlockByKey(scp, key, 0, userp, &req);
+               cm_ReleaseSCache(scp);
+               cm_ReleaseUser(userp);
+               lock_ReleaseMutex(&scp->mx);
+           }
+       }
+    }
+}