windows-afs-execute-only-20080309
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index edd7939..76aa2d6 100644 (file)
@@ -29,6 +29,8 @@ extern void afsi_log(char *pattern, ...);
 
 int cm_enableServerLocks = 1;
 
+int cm_followBackupPath = 0;
+
 /*
  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
  * I do not know anything more about it.
@@ -258,7 +260,7 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
     if (openMode == 1 || openMode == 2 || trunc) 
        rights |= PRSFS_WRITE;
         
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
                       CM_SCACHESYNC_GETSTATUS
@@ -322,7 +324,7 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
 
  _done:
 
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
     return code;
 }
@@ -344,7 +346,7 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
 
     rights = 0;
 
-    if (desiredAccess & AFS_ACCESS_READ)
+    if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
 
     /* We used to require PRSFS_WRITE if createDisp was 4
@@ -354,7 +356,7 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
     if (desiredAccess & AFS_ACCESS_WRITE)
         rights |= PRSFS_WRITE;
 
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
                       CM_SCACHESYNC_GETSTATUS
@@ -398,7 +400,7 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
            (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
            if (!*ldpp) {
                code = ENOMEM;
-               goto _done;
+               goto _syncopdone;
            }
 
            (*ldpp)->key = key;
@@ -432,22 +434,25 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
         goto _done;
     }
 
+  _syncopdone:
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
 
  _done:
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
+    osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
     return code;
 }
 
 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
                               cm_lock_data_t ** ldpp)
 {
+    osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
     if (*ldpp) {
-       lock_ObtainMutex(&scp->mx);
+       lock_ObtainWrite(&scp->rw);
        cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
                  (*ldpp)->key, userp, reqp);
-       lock_ReleaseMutex(&scp->mx);
+       lock_ReleaseWrite(&scp->rw);
        free(*ldpp);
        *ldpp = NULL;
     }
@@ -472,17 +477,18 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
     long code;
     osi_hyper_t thyper;
     cm_buf_t *bufferp;
-    cm_dirEntry_t *dep;
+    cm_dirEntry_t *dep = 0;
     unsigned short *hashTable;
     unsigned int i, idx;
     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
+    int releaseLock = 0;
 
     /* First check permissions */
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
     if (code)
         return code;
 
@@ -492,21 +498,20 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
         return code;
 
     thyper.HighPart = 0; thyper.LowPart = 0;
-    lock_ObtainRead(&scp->bufCreateLock);
     code = buf_Get(scp, &thyper, &bufferp);
-    lock_ReleaseRead(&scp->bufCreateLock);
     if (code)
         return code;
 
     lock_ObtainMutex(&bufferp->mx);
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
+    releaseLock = 1;
     while (1) {
         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
                           CM_SCACHESYNC_NEEDCALLBACK
                           | CM_SCACHESYNC_READ
                           | CM_SCACHESYNC_BUFLOCKED);
         if (code)
-            break;
+            goto done;
 
         if (cm_HaveBuffer(scp, bufferp, 1))
             break;
@@ -514,14 +519,17 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
         /* otherwise, load the buffer and try again */
         lock_ReleaseMutex(&bufferp->mx);
         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
         lock_ObtainMutex(&bufferp->mx);
-        lock_ObtainMutex(&scp->mx);
+        lock_ObtainWrite(&scp->rw);
        cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
         if (code)
-            break;
+            goto done;
     }
 
+    lock_ReleaseWrite(&scp->rw);
+    releaseLock = 0;
+
     /* We try to determine emptiness without looking beyond the first page,
      * and without assuming "." and ".." are present and are on the first
      * page (though these assumptions might, after all, be reasonable).
@@ -553,7 +561,8 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
   done:   
     lock_ReleaseMutex(&bufferp->mx);
     buf_Release(bufferp);
-    lock_ReleaseMutex(&scp->mx);
+    if (releaseLock)
+        lock_ReleaseWrite(&scp->rw);
     return code;
 }       
 
@@ -571,7 +580,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
 {
     char *tp;
     long code;
-    cm_dirEntry_t *dep;
+    cm_dirEntry_t *dep = 0;
     cm_buf_t *bufferp;
     long temp;
     osi_hyper_t dirLength;
@@ -586,10 +595,10 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
     int numDirChunks;  /* # of 32 byte dir chunks in this entry */
         
     /* get the directory size */
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
     if (code)
         return code;
         
@@ -728,9 +737,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 bufferp = NULL;
             }
 
-            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;
@@ -742,7 +749,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
               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);
+            lock_ObtainWrite(&scp->rw);
             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
                  && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
             {
@@ -751,7 +758,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
                 scp->bulkStatProgress = thyper;
             }
-            lock_ReleaseMutex(&scp->mx);
+            lock_ReleaseWrite(&scp->rw);
 #endif
 
             lock_ObtainMutex(&bufferp->mx);
@@ -759,20 +766,20 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
 
             /* now get the data in the cache */
             while (1) {
-                lock_ObtainMutex(&scp->mx);
+                lock_ObtainWrite(&scp->rw);
                 code = cm_SyncOp(scp, bufferp, userp, reqp,
                                   PRSFS_LOOKUP,
                                   CM_SCACHESYNC_NEEDCALLBACK
                                   | CM_SCACHESYNC_READ
                                   | CM_SCACHESYNC_BUFLOCKED);
                 if (code) {
-                    lock_ReleaseMutex(&scp->mx);
+                    lock_ReleaseWrite(&scp->rw);
                     break;
                 }
                cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
                                 
                 if (cm_HaveBuffer(scp, bufferp, 1)) {
-                    lock_ReleaseMutex(&scp->mx);
+                    lock_ReleaseWrite(&scp->rw);
                     break;
                 }
 
@@ -780,7 +787,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 lock_ReleaseMutex(&bufferp->mx);
                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
                                     reqp);
-                lock_ReleaseMutex(&scp->mx);
+                lock_ReleaseWrite(&scp->rw);
                 lock_ObtainMutex(&bufferp->mx);
                 if (code) 
                     break;
@@ -958,7 +965,7 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
 }       
 
 /* read the contents of a mount point into the appropriate string.
- * called with locked scp, and returns with locked scp.
+ * called with write locked scp, and returns with locked scp.
  */
 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
@@ -971,14 +978,12 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
         return 0;
         
     /* otherwise, we have to read it in */
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
-    lock_ObtainRead(&scp->bufCreateLock);
     thyper.LowPart = thyper.HighPart = 0;
     code = buf_Get(scp, &thyper, &bufp);
-    lock_ReleaseRead(&scp->bufCreateLock);
 
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     if (code)
         return code;
 
@@ -1032,7 +1037,7 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 
 
 /* called with a locked scp and chases the mount point, yielding outScpp.
- * scp remains locked, just for simplicity of describing the interface.
+ * scp remains write 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)
@@ -1048,13 +1053,13 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     char mtType;
     cm_fid_t tfid;
     size_t vnLength;
-    int type;
+    int targetType;
 
     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
         tfid = scp->mountRootFid;
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
-        lock_ObtainMutex(&scp->mx);
+        lock_ObtainWrite(&scp->rw);
         return code;
     }
 
@@ -1074,15 +1079,15 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
         strcpy(volNamep, cp+1);
         /* now look up the cell */
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
-        lock_ObtainMutex(&scp->mx);
+        lock_ObtainWrite(&scp->rw);
     }
     else {
         /* normal mt pt */
         strcpy(volNamep, mpNamep+1);
 
-        cellp = cm_FindCellByID(scp->fid.cell);
+        cellp = cm_FindCellByID(scp->fid.cell, 0);
     }
 
     if (!cellp) {
@@ -1092,15 +1097,15 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
     vnLength = strlen(volNamep);
     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
-        type = BACKVOL;
+        targetType = BACKVOL;
     else if (vnLength >= 10
               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
-        type = ROVOL;
+        targetType = ROVOL;
     else
-        type = RWVOL;
+        targetType = RWVOL;
 
     /* check for backups within backups */
-    if (type == BACKVOL
+    if (targetType == BACKVOL
          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
          == CM_SCACHEFLAG_RO) {
         code = CM_ERROR_NOSUCHVOLUME;
@@ -1108,7 +1113,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     }
 
     /* now we need to get the volume */
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
     if (cm_VolNameIsID(volNamep)) {
         code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp, 
                                 CM_GETVOL_FLAG_CREATE, &volp);
@@ -1116,9 +1121,11 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 
                                   CM_GETVOL_FLAG_CREATE, &volp);
     }
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
         
     if (code == 0) {
+        afs_uint32 cell, volume;
+
         /* save the parent of the volume root for this is the 
          * place where the volume is mounted and we must remember 
          * this in the volume structure rather than just in the 
@@ -1129,31 +1136,45 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         volp->dotdotFid = dscp->fid;
         lock_ReleaseMutex(&volp->mx);
 
-        scp->mountRootFid.cell = cellp->cellID;
+        cell = cellp->cellID;
+        
+        /* if the mt pt originates in a .backup volume (not a .readonly)
+         * and FollowBackupPath is active, and if there is a .backup
+         * volume for the target, then use the .backup of the target
+         * instead of the read-write.
+         */
+        if (cm_followBackupPath && 
+            volp->bk.ID != 0 &&
+            (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
+            (targetType == RWVOL || targetType == ROVOL && volp->ro.ID == 0)
+            ) {
+            targetType = BACKVOL;
+        } 
         /* 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
+         * target, and if this is a targetType '#' mount point, use
          * the read-only, otherwise use the one specified.
          */
-        if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
-             && volp->ro.ID != 0 && type == RWVOL)
-            type = ROVOL;
-        if (type == ROVOL)
-            scp->mountRootFid.volume = volp->ro.ID;
-        else if (type == BACKVOL)
-            scp->mountRootFid.volume = volp->bk.ID;
+        else if (mtType == '#' && targetType == RWVOL && 
+                 (scp->flags & CM_SCACHEFLAG_PURERO) && 
+                 volp->ro.ID != 0) {
+            targetType = ROVOL;
+        }
+        if (targetType == ROVOL)
+            volume = volp->ro.ID;
+        else if (targetType == BACKVOL)
+            volume = volp->bk.ID;
         else
-            scp->mountRootFid.volume = volp->rw.ID;
+            volume = volp->rw.ID;
 
         /* the rest of the fid is a magic number */
-        scp->mountRootFid.vnode = 1;
-        scp->mountRootFid.unique = 1;
+        cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
         scp->mountRootGen = cm_data.mountRootGen;
 
         tfid = scp->mountRootFid;
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
-        lock_ObtainMutex(&scp->mx);
+        lock_ObtainWrite(&scp->rw);
     }
 
   done:
@@ -1265,27 +1286,43 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
             else
                 return CM_ERROR_NOSUCHFILE;
         }
-        else {  /* nonexistent dir on freelance root, so add it */
+        else if (!strchr(namep, '#') && !strchr(namep, '%') &&
+                 strcmp(namep, "srvsvc") && strcmp(namep, "wkssvc") &&
+                 strcmp(namep, "ipc$")) {
+            /* nonexistent dir on freelance root, so add it */
             char fullname[200] = ".";
             int  found = 0;
 
             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
                       osi_LogSaveString(afsd_logp,namep));
+
+            /* 
+             * There is an ugly behavior where a share name "foo" will be searched
+             * for as "fo".  If the searched for name differs by an already existing
+             * symlink or mount point in the Freelance directory, do not add the 
+             * new value automatically.
+             */
+
+            code = -1;
             if (namep[0] == '.') {
                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
                     found = 1;
-                    if ( stricmp(&namep[1], &fullname[1]) )
+                    if (!cm_FreelanceMountPointExists(fullname, 0))
+                        code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
+                    if ( stricmp(&namep[1], &fullname[1]) && 
+                                               !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                                               !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
-                    else
-                        code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
                 }
             } else {
                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
                     found = 1;
-                    if ( stricmp(namep, fullname) )
+                    if (!cm_FreelanceMountPointExists(fullname, 0))
+                        code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
+                    if ( stricmp(namep, fullname) && 
+                                               !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                                               !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
                         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 */
@@ -1308,11 +1345,11 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
     }       
     /* tscp is now held */
 
-    lock_ObtainMutex(&tscp->mx);
+    lock_ObtainWrite(&tscp->rw);
     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
     if (code) { 
-        lock_ReleaseMutex(&tscp->mx);
+        lock_ReleaseWrite(&tscp->rw);
         cm_ReleaseSCache(tscp);
         return code;
     }
@@ -1328,7 +1365,7 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
         if (code == 0)
             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
                                         &mountedScp);
-        lock_ReleaseMutex(&tscp->mx);
+        lock_ReleaseWrite(&tscp->rw);
         cm_ReleaseSCache(tscp);
         if (code) {
             return code;
@@ -1336,7 +1373,7 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
         tscp = mountedScp;
     }
     else {
-        lock_ReleaseMutex(&tscp->mx);
+        lock_ReleaseWrite(&tscp->rw);
     }
 
     /* copy back pointer */
@@ -1345,10 +1382,10 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
     /* insert scache in dnlc */
     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
         /* lock the directory entry to prevent racing callback revokes */
-        lock_ObtainMutex(&dscp->mx);
+        lock_ObtainRead(&dscp->rw);
         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
             cm_dnlcEnter(dscp, namep, tscp);
-        lock_ReleaseMutex(&dscp->mx);
+        lock_ReleaseRead(&dscp->rw);
     }
 
     /* and return */
@@ -1371,7 +1408,7 @@ int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
     if (outp == NULL) 
         return 1;
 
-    if (index >= MAXNUMSYSNAMES)
+    if (index >= cm_sysNameCount)
         return -1;
 
     /* otherwise generate the properly expanded @sys name */
@@ -1396,6 +1433,7 @@ long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
     cm_cell_t *   cellp = NULL;
     cm_volume_t * volp = NULL;
     cm_fid_t      fid;
+    afs_uint32    volume;
     int           volType;
     int           mountType = RWVOL;
 
@@ -1462,18 +1500,15 @@ long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
     if (code != 0)
         goto _exit_cleanup;
 
-    fid.cell = cellp->cellID;
-
     if (volType == BACKVOL)
-        fid.volume = volp->bk.ID;
+        volume = volp->bk.ID;
     else if (volType == ROVOL ||
              (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
-        fid.volume = volp->ro.ID;
+        volume = volp->ro.ID;
     else
-        fid.volume = volp->rw.ID;
+        volume = volp->rw.ID;
 
-    fid.vnode = 1;
-    fid.unique = 1;
+    cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
 
     code = cm_GetSCache(&fid, outpScpp, userp, reqp);
 
@@ -1588,10 +1623,10 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     /* make sure we don't screw up the dir status during the merge */
     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
 
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     sflags = CM_SCACHESYNC_STOREDATA;
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code) {
         cm_EndDirOp(&dirop);
         return code;
@@ -1625,11 +1660,11 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_dnlcRemove(dscp, namep);
     cm_SyncOpDone(dscp, NULL, sflags);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     } else if (code == CM_ERROR_NOSUCHFILE) {
        /* windows would not have allowed the request to delete the file 
         * if it did not believe the file existed.  therefore, we must 
@@ -1637,7 +1672,7 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
         */
        dscp->cbServerp = NULL;
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
         cm_DirDeleteEntry(&dirop, namep);
@@ -1650,8 +1685,8 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     return code;
 }
 
-/* called with a locked vnode, and fills in the link info.
- * returns this the vnode still locked.
+/* called with a write locked vnode, and fills in the link info.
+ * returns this the vnode still write locked.
  */
 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
 {
@@ -1660,13 +1695,13 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
     long temp;
     osi_hyper_t thyper;
 
-    lock_AssertMutex(&linkScp->mx);
+    lock_AssertWrite(&linkScp->rw);
     if (!linkScp->mountPointStringp[0]) {
         /* read the link data */
-        lock_ReleaseMutex(&linkScp->mx);
+        lock_ReleaseWrite(&linkScp->rw);
         thyper.LowPart = thyper.HighPart = 0;
         code = buf_Get(linkScp, &thyper, &bufp);
-        lock_ObtainMutex(&linkScp->mx);
+        lock_ObtainWrite(&linkScp->rw);
         if (code) 
             return code;
         while (1) {
@@ -1702,6 +1737,9 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
         if (!linkScp->mountPointStringp[0]) {
             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
             linkScp->mountPointStringp[temp] = 0;      /* null terminate */
+
+            if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
+                 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
         }
         buf_Release(bufp);
     }  /* don't have sym link contents cached */
@@ -1724,9 +1762,12 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
     char *linkp;
     cm_space_t *tsp;
 
-    lock_ObtainMutex(&linkScp->mx);
+    *newRootScpp = NULL;
+    *newSpaceBufferp = NULL;
+
+    lock_ObtainWrite(&linkScp->rw);
     code = cm_HandleLink(linkScp, userp, reqp);
-    if (code) 
+    if (code)
         goto done;
 
     /* if we may overflow the buffer, bail out; buffer is signficantly
@@ -1734,8 +1775,10 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
      * being a little conservative here.
      */
     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
-         >= CM_UTILS_SPACESIZE)
-        return CM_ERROR_TOOBIG;
+               >= CM_UTILS_SPACESIZE) {
+        code = CM_ERROR_TOOBIG;
+               goto done;
+       }
 
     tsp = cm_GetSpace();
     linkp = linkScp->mountPointStringp;
@@ -1763,13 +1806,12 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
         } 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:"))) ) {
+    } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
+                !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   
@@ -1785,22 +1827,27 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
          */
         linkScp->fileType = CM_SCACHETYPE_INVALID;
         strcpy(tsp->data, linkp);
-        *newRootScpp = NULL;
         code = CM_ERROR_NOSUCHPATH;
 #endif  
     } else {
         /* a relative link */
         strcpy(tsp->data, linkp);
-        *newRootScpp = NULL;
     }
     if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
         strcat(tsp->data, "\\");
         strcat(tsp->data, pathSuffixp);
     }
-    *newSpaceBufferp = tsp;
+    if (code == 0)
+        *newSpaceBufferp = tsp;
+    else {
+        cm_FreeSpace(tsp);
+
+        if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
+            cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
+    }
 
   done:
-    lock_ReleaseMutex(&linkScp->mx);
+    lock_ReleaseWrite(&linkScp->rw);
     return code;
 }
 #ifdef DEBUG_REFCOUNT
@@ -1910,6 +1957,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
                                 break;
                         }
+                        fid_count = i+1;
                     } else {
                         /* add the new fid to the list */
                         for ( i=0; i<fid_count; i++) {
@@ -1961,12 +2009,12 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                 /* now, if tscp is a symlink, we should follow
                  * it and assemble the path again.
                  */
-                lock_ObtainMutex(&tscp->mx);
+                lock_ObtainWrite(&tscp->rw);
                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
                                   CM_SCACHESYNC_GETSTATUS
                                   | CM_SCACHESYNC_NEEDCALLBACK);
                 if (code) {
-                    lock_ReleaseMutex(&tscp->mx);
+                    lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     tscp = NULL;
                     if (dirScp) {
@@ -1979,7 +2027,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
 
                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
                     /* this is a symlink; assemble a new buffer */
-                    lock_ReleaseMutex(&tscp->mx);
+                    lock_ReleaseWrite(&tscp->rw);
                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
                         cm_ReleaseSCache(tscp);
                         tscp = NULL;
@@ -2058,7 +2106,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     }
                 } else {
                     /* not a symlink, we may be done */
-                    lock_ReleaseMutex(&tscp->mx);
+                    lock_ReleaseWrite(&tscp->rw);
                     if (tc == 0) {
                         if (phase == 1) {
                             phase = 2;
@@ -2213,24 +2261,21 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
         return 0;
 
-    tfid.cell = scp->fid.cell;
-    tfid.volume = scp->fid.volume;
-    tfid.vnode = ntohl(dep->fid.vnode);
-    tfid.unique = ntohl(dep->fid.unique);
+    cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
     tscp = cm_FindSCache(&tfid);
     if (tscp) {
-        if (lock_TryMutex(&tscp->mx)) {
+        if (lock_TryWrite(&tscp->rw)) {
             /* we have an entry that we can look at */
             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.
                  */
-                lock_ReleaseMutex(&tscp->mx);
+                lock_ReleaseWrite(&tscp->rw);
                 cm_ReleaseSCache(tscp);
                 return 0;
             }
-            lock_ReleaseMutex(&tscp->mx);
+            lock_ReleaseWrite(&tscp->rw);
         }      /* got lock */
         cm_ReleaseSCache(tscp);
     }  /* found entry */
@@ -2256,7 +2301,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     return 0;
 }       
 
-/* called with a locked scp and a pointer to a buffer.  Make bulk stat
+/* called with a write locked scp and a pointer to a buffer.  Make bulk stat
  * calls on all undeleted files in the page of the directory specified.
  */
 afs_int32
@@ -2289,13 +2334,13 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     memset(&bb, 0, sizeof(bb));
     bb.bufOffset = *offsetp;
 
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     /* first, assemble the file IDs we need to stat */
     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
 
     /* if we failed, bail out early */
     if (code && code != CM_ERROR_STOPNOW) {
-        lock_ObtainMutex(&dscp->mx);
+        lock_ObtainWrite(&dscp->rw);
         return code;
     }
 
@@ -2358,10 +2403,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
         /* otherwise, we should do the merges */
         for (i = 0; i<filesThisCall; i++) {
             j = filex + i;
-            tfid.cell = dscp->fid.cell;
-            tfid.volume = bb.fids[j].Volume;
-            tfid.vnode = bb.fids[j].Vnode;
-            tfid.unique = bb.fids[j].Unique;
+            cm_SetFid(&tfid, dscp->fid.cell, bb.fids[j].Volume, bb.fids[j].Vnode, bb.fids[j].Unique);
             code = cm_GetSCache(&tfid, &scp, userp, reqp);
             if (code != 0) 
                 continue;
@@ -2369,7 +2411,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
             /* otherwise, if this entry has no callback info, 
              * merge in this.
              */
-            lock_ObtainMutex(&scp->mx);
+            lock_ObtainWrite(&scp->rw);
             /* now, we have to be extra paranoid on merging in this
              * information, since we didn't use cm_SyncOp before
              * starting the fetch to make sure that no bad races
@@ -2390,7 +2432,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
                                             CM_CALLBACK_MAINTAINCOUNT);
                 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
             }       
-            lock_ReleaseMutex(&scp->mx);
+            lock_ReleaseWrite(&scp->rw);
             cm_ReleaseSCache(scp);
         } /* all files in the response */
         /* now tell it to drop the count,
@@ -2399,7 +2441,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
 
         filex += filesThisCall;
     }  /* while there are still more files to process */
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
 
     /* If we did the InlineBulk RPC pull out the return code and log it */
     if (inlinebulk) {
@@ -2466,7 +2508,7 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
     lock_ObtainWrite(&scp->bufCreateLock);
 
     /* verify that this is a file, not a dir or a symlink */
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     if (code) 
@@ -2484,9 +2526,9 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
     else
         shrinking = 0;
 
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
-    /* can't hold scp->mx lock here, since we may wait for a storeback to
+    /* can't hold scp->rw lock here, since we may wait for a storeback to
      * finish if the buffer package is cleaning a buffer by storing it to
      * the server.
      */
@@ -2494,7 +2536,7 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
         buf_Truncate(scp, userp, reqp, sizep);
 
     /* now ensure that file length is short enough, and update truncPos */
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     /* make sure we have a callback (so we have the right value for the
      * length), and wait for it to be safe to do a truncate.
@@ -2545,7 +2587,7 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
                   | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
 
   done:
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
     lock_ReleaseWrite(&scp->bufCreateLock);
 
     return code;
@@ -2567,13 +2609,14 @@ 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);
 
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     /* 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);
+       lock_ReleaseWrite(&scp->rw);
         return code;
     }
+    lock_ConvertWToR(&scp->rw);
 
     /* make the attr structure */
     cm_StatusFromAttr(&afsInStatus, scp, attrp);
@@ -2581,7 +2624,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     tfid.Volume = scp->fid.volume;
     tfid.Vnode = scp->fid.vnode;
     tfid.Unique = scp->fid.unique;
-       lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseRead(&scp->rw);
 
     /* now make the RPC */
     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
@@ -2604,7 +2647,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     else
         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
 
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
     if (code == 0)
         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
@@ -2615,7 +2658,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
      */
     if (afsInStatus.Mask & AFS_SETMODE) 
        cm_FreeAllACLEnts(scp);
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
     return code;
 }       
 
@@ -2645,14 +2688,24 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         return CM_ERROR_ATSYS;
     }
 
+#ifdef AFS_FREELANCE_CLIENT
+    /* Freelance root volume does not hold files */
+    if (cm_freelanceEnabled &&
+        dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+    {
+        return CM_ERROR_NOACCESS;
+    }
+#endif /* AFS_FREELANCE_CLIENT */
+
     /* before starting the RPC, mark that we're changing the file data, so
      * that someone who does a chmod will know to wait until our call
      * completes.
      */
     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code == 0) {
         cm_StartCallbackGrantingCall(NULL, &cbReq);
     } else {
@@ -2696,12 +2749,12 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     /* now try to create the file's entry, too, but be careful to 
      * make sure that we don't merge in old info.  Since we weren't locking
@@ -2709,13 +2762,10 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
      * info.
      */
     if (code == 0) {
-        newFid.cell = dscp->fid.cell;
-        newFid.volume = dscp->fid.volume;
-        newFid.vnode = newAFSFid.Vnode;
-        newFid.unique = newAFSFid.Unique;
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         code = cm_GetSCache(&newFid, &scp, userp, reqp);
         if (code == 0) {
-            lock_ObtainMutex(&scp->mx);
+            lock_ObtainWrite(&scp->rw);
            scp->creator = userp;               /* remember who created it */
             if (!cm_HaveCallback(scp)) {
                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
@@ -2724,7 +2774,7 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
                                             &newFileCallback, 0);
                 didEnd = 1;     
             }       
-            lock_ReleaseMutex(&scp->mx);
+            lock_ReleaseWrite(&scp->rw);
             *scpp = scp;
         }
     }
@@ -2748,11 +2798,9 @@ long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
     long code;
 
-    lock_ObtainWrite(&scp->bufCreateLock);
     code = buf_CleanVnode(scp, userp, reqp);
-    lock_ReleaseWrite(&scp->bufCreateLock);
     if (code == 0) {
-        lock_ObtainMutex(&scp->mx);
+        lock_ObtainWrite(&scp->rw);
 
         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
                           | CM_SCACHEMASK_CLIENTMODTIME
@@ -2764,7 +2812,7 @@ long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
            scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
        }
 
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
     }
     return code;
 }
@@ -2795,14 +2843,24 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         return CM_ERROR_ATSYS;
     }
 
+#ifdef AFS_FREELANCE_CLIENT
+    /* Freelance root volume does not hold subdirectories */
+    if (cm_freelanceEnabled &&
+        dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+    {
+        return CM_ERROR_NOACCESS;
+    }
+#endif /* AFS_FREELANCE_CLIENT */
+
     /* before starting the RPC, mark that we're changing the directory
      * data, so that someone who does a chmod on the dir will wait until
      * our call completes.
      */
     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code == 0) {
         cm_StartCallbackGrantingCall(NULL, &cbReq);
     } else {
@@ -2846,12 +2904,12 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     /* now try to create the new dir's entry, too, but be careful to 
      * make sure that we don't merge in old info.  Since we weren't locking
@@ -2859,13 +2917,10 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
      * info.
      */
     if (code == 0) {
-        newFid.cell = dscp->fid.cell;
-        newFid.volume = dscp->fid.volume;
-        newFid.vnode = newAFSFid.Vnode;
-        newFid.unique = newAFSFid.Unique;
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         code = cm_GetSCache(&newFid, &scp, userp, reqp);
         if (code == 0) {
-            lock_ObtainMutex(&scp->mx);
+            lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
                                 userp, 0);
@@ -2873,7 +2928,7 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
                                             &newDirCallback, 0);
                 didEnd = 1;             
             }
-            lock_ReleaseMutex(&scp->mx);
+            lock_ReleaseWrite(&scp->rw);
             cm_ReleaseSCache(scp);
         }
     }
@@ -2913,9 +2968,9 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
     }
 
     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code != 0)
         cm_EndDirOp(&dirop);
 
@@ -2956,12 +3011,12 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
@@ -2997,9 +3052,9 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
      * call completes.
      */
     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code != 0)
         cm_EndDirOp(&dirop);
     if (code) {
@@ -3038,19 +3093,16 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
-            newFid.cell = dscp->fid.cell;
-            newFid.volume = dscp->fid.volume;
-            newFid.vnode = newAFSFid.Vnode;
-            newFid.unique = newAFSFid.Unique;
+            cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
 
             cm_DirCreateEntry(&dirop, namep, &newFid);
 #ifdef USE_BPLUS
@@ -3066,18 +3118,15 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
      * info.
      */
     if (code == 0) {
-        newFid.cell = dscp->fid.cell;
-        newFid.volume = dscp->fid.volume;
-        newFid.vnode = newAFSFid.Vnode;
-        newFid.unique = newAFSFid.Unique;
+        cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         code = cm_GetSCache(&newFid, &scp, userp, reqp);
         if (code == 0) {
-            lock_ObtainMutex(&scp->mx);
+            lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
                                 userp, 0);
             }       
-            lock_ReleaseMutex(&scp->mx);
+            lock_ReleaseWrite(&scp->rw);
             cm_ReleaseSCache(scp);
         }
     }
@@ -3103,9 +3152,9 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
      * call completes.
      */
     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
     if (code) {
         cm_EndDirOp(&dirop);
         return code;
@@ -3141,13 +3190,13 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
         lock_ObtainWrite(&dirop.scp->dirlock);
         dirop.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&dscp->mx);
+    lock_ObtainWrite(&dscp->rw);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_dnlcRemove(dscp, namep); 
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
@@ -3166,7 +3215,7 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
 {
     /* grab mutex on contents */
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     /* reset the prefetch info */
     scp->prefetch.base.LowPart = 0;            /* base */
@@ -3175,7 +3224,7 @@ long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
     scp->prefetch.end.HighPart = 0;
 
     /* release mutex on contents */
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
     /* we're done */
     return 0;
@@ -3211,12 +3260,12 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
 
         oneDir = 1;
         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
-        lock_ObtainMutex(&oldDscp->mx);
+        lock_ObtainWrite(&oldDscp->rw);
         cm_dnlcRemove(oldDscp, oldNamep);
         cm_dnlcRemove(oldDscp, newNamep);
         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                           CM_SCACHESYNC_STOREDATA);
-        lock_ReleaseMutex(&oldDscp->mx);
+        lock_ReleaseWrite(&oldDscp->rw);
         if (code != 0) {
             cm_EndDirOp(&oldDirOp);
         }
@@ -3237,28 +3286,28 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
 
         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
-            lock_ObtainMutex(&oldDscp->mx);
+            lock_ObtainWrite(&oldDscp->rw);
             cm_dnlcRemove(oldDscp, oldNamep);
             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                               CM_SCACHESYNC_STOREDATA);
-            lock_ReleaseMutex(&oldDscp->mx);
+            lock_ReleaseWrite(&oldDscp->rw);
             if (code != 0)
                 cm_EndDirOp(&oldDirOp);
             if (code == 0) {
                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
-                lock_ObtainMutex(&newDscp->mx);
+                lock_ObtainWrite(&newDscp->rw);
                 cm_dnlcRemove(newDscp, newNamep);
                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
                                   CM_SCACHESYNC_STOREDATA);
-                lock_ReleaseMutex(&newDscp->mx);
+                lock_ReleaseWrite(&newDscp->rw);
                 if (code) {
                     cm_EndDirOp(&newDirOp);
 
                     /* cleanup first one */
-                    lock_ObtainMutex(&oldDscp->mx);
+                    lock_ObtainWrite(&oldDscp->rw);
                     cm_SyncOpDone(oldDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
-                    lock_ReleaseMutex(&oldDscp->mx);
+                    lock_ReleaseWrite(&oldDscp->rw);
                     cm_EndDirOp(&oldDirOp);
                 }       
             }
@@ -3266,28 +3315,28 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
         else {
             /* lock the new vnode entry first */
             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
-            lock_ObtainMutex(&newDscp->mx);
+            lock_ObtainWrite(&newDscp->rw);
             cm_dnlcRemove(newDscp, newNamep);
             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
                               CM_SCACHESYNC_STOREDATA);
-            lock_ReleaseMutex(&newDscp->mx);
+            lock_ReleaseWrite(&newDscp->rw);
             if (code != 0)
                 cm_EndDirOp(&newDirOp);
             if (code == 0) {
                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
-                lock_ObtainMutex(&oldDscp->mx);
+                lock_ObtainWrite(&oldDscp->rw);
                 cm_dnlcRemove(oldDscp, oldNamep);
                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                                   CM_SCACHESYNC_STOREDATA);
-                lock_ReleaseMutex(&oldDscp->mx);
+                lock_ReleaseWrite(&oldDscp->rw);
                 if (code != 0)
                     cm_EndDirOp(&oldDirOp);
                 if (code) {
                     /* cleanup first one */
-                    lock_ObtainMutex(&newDscp->mx);
+                    lock_ObtainWrite(&newDscp->rw);
                     cm_SyncOpDone(newDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
-                    lock_ReleaseMutex(&newDscp->mx);
+                    lock_ReleaseWrite(&newDscp->rw);
                     cm_EndDirOp(&newDirOp);
                 }       
             }
@@ -3335,13 +3384,13 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
         lock_ObtainWrite(&oldDirOp.scp->dirlock);
         oldDirOp.lockType = CM_DIRLOCK_WRITE;
     }
-    lock_ObtainMutex(&oldDscp->mx);
+    lock_ObtainWrite(&oldDscp->rw);
     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
 
     if (code == 0)
         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
-                        userp, 0);
-    lock_ReleaseMutex(&oldDscp->mx);
+                        userp, CM_MERGEFLAG_DIROP);
+    lock_ReleaseWrite(&oldDscp->rw);
 
     if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
@@ -3379,12 +3428,12 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
             lock_ObtainWrite(&newDirOp.scp->dirlock);
             newDirOp.lockType = CM_DIRLOCK_WRITE;
         }
-        lock_ObtainMutex(&newDscp->mx);
+        lock_ObtainWrite(&newDscp->rw);
         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
         if (code == 0)
             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
-                            userp, 0);
-        lock_ReleaseMutex(&newDscp->mx);
+                            userp, CM_MERGEFLAG_DIROP);
+        lock_ReleaseWrite(&newDscp->rw);
 
         if (code == 0) {
             /* we only make the local change if we successfully made
@@ -3734,7 +3783,7 @@ static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
     }
 }
 
-/* Called with scp->mx held.  Returns 0 if all is clear to read the
+/* Called with scp->rw 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, 
@@ -3818,7 +3867,7 @@ long cm_LockCheckRead(cm_scache_t *scp,
 #endif
 }
 
-/* Called with scp->mx held.  Returns 0 if all is clear to write the
+/* Called with scp->rw 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,
@@ -3891,9 +3940,6 @@ long cm_LockCheckWrite(cm_scache_t *scp,
 #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;
@@ -3916,7 +3962,7 @@ 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
+/* called with scp->rw 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) {
@@ -3926,6 +3972,7 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
     cm_conn_t * connp;
     struct rx_connection * callp;
     AFSVolSync volSync;
+    afs_uint32 reqflags = reqp->flags;
 
     tfid.Volume = scp->fid.volume;
     tfid.Vnode = scp->fid.vnode;
@@ -3934,7 +3981,8 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
 
     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
 
-    lock_ReleaseMutex(&scp->mx);
+    reqp->flags |= CM_REQ_NORETRY;
+    lock_ReleaseWrite(&scp->rw);
 
     do {
         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
@@ -3956,12 +4004,12 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
     }
 
-    lock_ObtainMutex(&scp->mx);
-
+    lock_ObtainWrite(&scp->rw);
+    reqp->flags = reqflags;
     return code;
 }
 
-/* called with scp->mx held.  Releases it during processing */
+/* called with scp->rw held.  Releases it during processing */
 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
                        cm_req_t * reqp) {
     long code = 0;
@@ -3976,7 +4024,7 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
     tfid.Unique = scp->fid.unique;
     cfid = scp->fid;
 
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
 
@@ -3999,12 +4047,12 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
         osi_Log0(afsd_logp,
                  "CALL ReleaseLock SUCCESS");
         
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     return code;
 }
 
-/* called with scp->mx held.  May release it during processing, but
+/* called with scp->rw held.  May release it during processing, but
    will exit with lock held.
 
    This will return:
@@ -4090,7 +4138,7 @@ long cm_LockCheckPerms(cm_scache_t * scp,
     return code;
 }
 
-/* called with scp->mx held */
+/* called with scp->rw held */
 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
              cm_key_t key,
@@ -4255,7 +4303,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                 osi_Log0(afsd_logp,
                          "   attempting to UPGRADE from LockRead to LockWrite.");
                 osi_Log1(afsd_logp,
-                         "   dataVersion on scp: %d", scp->dataVersion);
+                         "   dataVersion on scp: %I64d", scp->dataVersion);
 
                 /* we assume at this point (because scp->serverLock
                    was valid) that we had a valid server lock. */
@@ -4346,7 +4394,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                 osi_Log0(afsd_logp,
                          "  Data version mismatch while upgrading lock.");
                 osi_Log2(afsd_logp,
-                         "  Data versions before=%d, after=%d",
+                         "  Data versions before=%I64d, after=%I64d",
                          scp->lockDataVersion,
                          scp->dataVersion);
                 osi_Log1(afsd_logp,
@@ -4457,7 +4505,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
 
-/* Called with scp->mx held */
+/* Called with scp->rw held */
 long cm_UnlockByKey(cm_scache_t * scp,
                    cm_key_t key,
                    int flags,
@@ -4580,7 +4628,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
         /* 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);
+        osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
 
         code = cm_IntReleaseLock(scp, userp, reqp);
 
@@ -4602,7 +4650,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
                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",
+            osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
                      scp->lockDataVersion,
                      scp->dataVersion);
             
@@ -4698,13 +4746,11 @@ long cm_Unlock(cm_scache_t *scp,
         lock_ReleaseRead(&cm_scacheLock);
 
         /* The lock didn't exist anyway. *shrug* */
-        return 0;
+        return CM_ERROR_RANGE_NOT_LOCKED;
     }
 
-    lock_ReleaseRead(&cm_scacheLock);
-
     /* discard lock record */
-    lock_ObtainWrite(&cm_scacheLock);
+    lock_ConvertRToW(&cm_scacheLock);
     if (scp->fileLocksT == q)
         scp->fileLocksT = osi_QPrev(q);
     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
@@ -4757,7 +4803,7 @@ long cm_Unlock(cm_scache_t *scp,
         /* 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);
+        osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
 
         /* before we downgrade, make sure that we have enough
            permissions to get the read lock. */
@@ -4792,7 +4838,7 @@ long cm_Unlock(cm_scache_t *scp,
             osi_Log0(afsd_logp,
                      "Data version mismatch while downgrading lock");
             osi_Log2(afsd_logp,
-                     "  Data versions before=%d, after=%d",
+                     "  Data versions before=%I64d, after=%I64d",
                      scp->lockDataVersion,
                      scp->dataVersion);
             
@@ -4838,8 +4884,8 @@ long cm_Unlock(cm_scache_t *scp,
     return code;
 }
 
-/* called with scp->mx held */
-static void cm_LockMarkSCacheLost(cm_scache_t * scp)
+/* called with scp->rw held */
+void cm_LockMarkSCacheLost(cm_scache_t * scp)
 {
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
@@ -4941,7 +4987,7 @@ void cm_CheckLocks()
                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
 
                 lock_ReleaseWrite(&cm_scacheLock);
-                lock_ObtainMutex(&scp->mx);
+                lock_ObtainWrite(&scp->rw);
 
                 /* did the lock change while we weren't holding the lock? */
                 if (!IS_LOCK_ACTIVE(fileLock))
@@ -4958,12 +5004,12 @@ void cm_CheckLocks()
                     goto post_syncopdone;
                 }
 
-                /* cm_SyncOp releases scp->mx during which the lock
+                /* cm_SyncOp releases scp->rw during which the lock
                    may get released. */
                 if (!IS_LOCK_ACTIVE(fileLock))
                     goto pre_syncopdone;
 
-                if (scp->serverLock != -1) {
+                if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
                     cm_fid_t cfid;
                     cm_user_t * userp;
 
@@ -4978,7 +5024,7 @@ void cm_CheckLocks()
                              scp,
                              (int) scp->serverLock);
 
-                    lock_ReleaseMutex(&scp->mx);
+                    lock_ReleaseWrite(&scp->rw);
 
                     do {
                         code = cm_ConnFromFID(&cfid, userp,
@@ -4999,7 +5045,7 @@ void cm_CheckLocks()
 
                     code = cm_MapRPCError(code, &req);
 
-                    lock_ObtainMutex(&scp->mx);
+                    lock_ObtainWrite(&scp->rw);
 
                     if (code) {
                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
@@ -5030,7 +5076,7 @@ void cm_CheckLocks()
                                      "Data version mismatch on scp 0x%p",
                                      scp);
                             osi_Log2(afsd_logp,
-                                     "   Data versions: before=%d, after=%d",
+                                     "   Data versions: before=%I64d, after=%I64d",
                                      scp->lockDataVersion,
                                      scp->dataVersion);
 
@@ -5040,7 +5086,8 @@ void cm_CheckLocks()
                         }
                     }
 
-                    if (code == EINVAL || code == CM_ERROR_INVAL) {
+                    if (code == EINVAL || code == CM_ERROR_INVAL ||
+                        code == CM_ERROR_BADFD) {
                         cm_LockMarkSCacheLost(scp);
                     }
 
@@ -5058,7 +5105,7 @@ void cm_CheckLocks()
                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
 
             post_syncopdone:
-                lock_ReleaseMutex(&scp->mx);
+                lock_ReleaseWrite(&scp->rw);
 
                 lock_ObtainWrite(&cm_scacheLock);
 
@@ -5085,7 +5132,7 @@ void cm_CheckLocks()
     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
 }
 
-/* NOT called with scp->mx held. */
+/* NOT called with scp->rw held. */
 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 {
     long code = 0;
@@ -5138,7 +5185,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
     osi_assertx(scp != NULL, "null cm_scache_t");
 
     lock_ReleaseRead(&cm_scacheLock);
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
 
     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
                              oldFileLock->userp,
@@ -5150,7 +5197,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         }
         code = 0;
     } else if (code) {
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
         return code;
     }
 
@@ -5173,7 +5220,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         }
 
         lock_ReleaseWrite(&cm_scacheLock);
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
 
         return 0;
     }
@@ -5217,7 +5264,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 
     if (code != 0) {
         lock_ReleaseWrite(&cm_scacheLock);
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
 
         goto handleCode;
     }
@@ -5266,7 +5313,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         }
 
         lock_ReleaseWrite(&cm_scacheLock);
-        lock_ReleaseMutex(&scp->mx);
+        lock_ReleaseWrite(&scp->rw);
 
         return 0;
 
@@ -5337,7 +5384,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
                 osi_Log0(afsd_logp,
                          "  Data version mismatch while upgrading lock.");
                 osi_Log2(afsd_logp,
-                         "  Data versions before=%d, after=%d",
+                         "  Data versions before=%I64d, after=%I64d",
                          scp->lockDataVersion,
                          scp->dataVersion);
                 osi_Log1(afsd_logp,
@@ -5369,7 +5416,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
        lock_ReleaseWrite(&cm_scacheLock);
     }
-    lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseWrite(&scp->rw);
 
   updateLock:
     lock_ObtainWrite(&cm_scacheLock);
@@ -5425,11 +5472,11 @@ void cm_ReleaseAllLocks(void)
     {
        for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
            while (scp->fileLocksH != NULL) {
-               lock_ObtainMutex(&scp->mx);
+               lock_ObtainWrite(&scp->rw);
                lock_ObtainWrite(&cm_scacheLock);
                if (!scp->fileLocksH) {
                    lock_ReleaseWrite(&cm_scacheLock);
-                   lock_ReleaseMutex(&scp->mx);
+                   lock_ReleaseWrite(&scp->rw);
                    break;
                }
                fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
@@ -5441,7 +5488,7 @@ void cm_ReleaseAllLocks(void)
                cm_UnlockByKey(scp, key, 0, userp, &req);
                cm_ReleaseSCache(scp);
                cm_ReleaseUser(userp);
-               lock_ReleaseMutex(&scp->mx);
+               lock_ReleaseWrite(&scp->rw);
            }
        }
     }