Windows: Add cm_req_t parameter to buf_Get* functions
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index c3464b1..71163be 100644 (file)
 #include <osi.h>
 
 #include "afsd.h"
+#include "smb.h"
 #include "cm_btree.h"
 
+#include <strsafe.h>
+
 #ifdef DEBUG
 extern void afsi_log(char *pattern, ...);
 #endif
@@ -96,156 +99,7 @@ int cm_stricmp(const char *str1, const char *str2)
     }
 }
 
-/* characters that are legal in an 8.3 name */
-/*
- * We used to have 1's for all characters from 128 to 254.  But
- * the NT client behaves better if we create an 8.3 name for any
- * name that has a character with the high bit on, and if we
- * delete those characters from 8.3 names.  In particular, see
- * Sybase defect 10859.
- */
-char cm_LegalChars[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
 
-/* return true iff component is a valid 8.3 name */
-int cm_Is8Dot3(char *namep)
-{
-    int sawDot = 0;
-    unsigned char tc;
-    int charCount = 0;
-        
-    /*
-     * can't have a leading dot;
-     * special case for . and ..
-     */
-    if (namep[0] == '.') {
-        if (namep[1] == 0)
-            return 1;
-        if (namep[1] == '.' && namep[2] == 0)
-            return 1;
-        return 0;
-    }
-    while (tc = *namep++) {
-        if (tc == '.') {
-            /* saw another dot */
-            if (sawDot) return 0;      /* second dot */
-            sawDot = 1;
-            charCount = 0;
-            continue;
-        }
-        if (cm_LegalChars[tc] == 0)
-            return 0;
-        charCount++;
-        if (!sawDot && charCount > 8)
-            /* more than 8 chars in name */
-            return 0;
-        if (sawDot && charCount > 3)
-            /* more than 3 chars in extension */
-            return 0;
-    }
-    return 1;
-}
-
-/*
- * Number unparsing map for generating 8.3 names;
- * The version taken from DFS was on drugs.  
- * You can't include '&' and '@' in a file name.
- */
-char cm_8Dot3Mapping[42] =
-{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '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);
-
-void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
-                        char *shortName, char **shortNameEndp)
-{
-    char number[12];
-    int i, nsize = 0;
-    int vnode = ntohl(pfid->vnode);
-    char *lastDot;
-    int validExtension = 0;
-    char tc, *temp;
-    const char *name;
-
-    /* Unparse the file's vnode number to get a "uniquifier" */
-    do {
-        number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
-        nsize++;
-        vnode /= cm_8Dot3MapSize;
-    } while (vnode);
-
-    /*
-     * Look for valid extension.  There has to be a dot, and
-     * at least one of the characters following has to be legal.
-     */
-    lastDot = strrchr(longname, '.');
-    if (lastDot) {
-        temp = lastDot; temp++;
-        while (tc = *temp++)
-            if (cm_LegalChars[tc])
-                break;
-        if (tc)
-            validExtension = 1;
-    }
-
-    /* Copy name characters */
-    for (i = 0, name = longname;
-          i < (7 - nsize) && name != lastDot; ) {
-        tc = *name++;
-
-        if (tc == 0)
-            break;
-        if (!cm_LegalChars[tc])
-            continue;
-        i++;
-        *shortName++ = toupper(tc);
-    }
-
-    /* tilde */
-    *shortName++ = '~';
-
-    /* Copy uniquifier characters */
-    memcpy(shortName, number, nsize);
-    shortName += nsize;
-
-    if (validExtension) {
-        /* Copy extension characters */
-        *shortName++ = *lastDot++;     /* copy dot */
-        for (i = 0, tc = *lastDot++;
-              i < 3 && tc;
-              tc = *lastDot++) {
-            if (cm_LegalChars[tc]) {
-                i++;
-                *shortName++ = toupper(tc);
-            }
-        }
-    }
-
-    /* Trailing null */
-    *shortName = 0;
-
-    if (shortNameEndp)
-        *shortNameEndp = shortName;
-}       
 
 /* return success if we can open this file in this mode */
 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
@@ -260,7 +114,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
@@ -293,7 +147,7 @@ long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
 
         if (code == 0) {
-            cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
+            cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
         } else {
             /* In this case, we allow the file open to go through even
                though we can't enforce mandatory locking on the
@@ -324,7 +178,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;
 }
@@ -346,7 +200,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
@@ -356,7 +210,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
@@ -434,25 +288,26 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
         goto _done;
     }
 
-    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);
+    lock_ObtainWrite(&scp->rw);
     if (*ldpp) {
-       lock_ObtainMutex(&scp->mx);
        cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
-                 (*ldpp)->key, userp, reqp);
-       lock_ReleaseMutex(&scp->mx);
+                 (*ldpp)->key, 0, userp, reqp);
        free(*ldpp);
        *ldpp = NULL;
     }
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+    lock_ReleaseWrite(&scp->rw);
     return 0;
 }
 /*
@@ -478,13 +333,15 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
     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);
+    if (!code)
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
     if (code)
         return code;
 
@@ -494,21 +351,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);
+    code = buf_Get(scp, &thyper, reqp, &bufferp);
     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;
@@ -516,14 +372,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).
@@ -555,7 +414,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;
 }       
 
@@ -568,8 +428,8 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
  * cm_lookupSearch_t object.  
  */
 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
-                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
-                  cm_scache_t **retscp)
+                 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
+                 cm_scache_t **retscp)
 {
     char *tp;
     long code;
@@ -588,10 +448,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;
         
@@ -639,7 +499,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 if (code == 0) {
 
 #ifdef USE_BPLUS
-                    code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
+                    code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
                     if (code != EINVAL)
                         usedBplus = 1;
                     else 
@@ -730,51 +590,32 @@ 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);
+            code = buf_Get(scp, &thyper, reqp, &bufferp);
             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);
             bufferOffset = thyper;
 
             /* 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;
                 }
 
@@ -782,7 +623,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;
@@ -858,18 +699,18 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
     return code;
 }
 
-int cm_NoneUpper(char *s)
+int cm_NoneUpper(normchar_t *s)
 {
-    char c;
+    normchar_t c;
     while (c = *s++)
         if (c >= 'A' && c <= 'Z')
             return 0;
     return 1;
 }
 
-int cm_NoneLower(char *s)
+int cm_NoneLower(normchar_t *s)
 {
-    char c;
+    normchar_t c;
     while (c = *s++)
         if (c >= 'a' && c <= 'z')
             return 0;
@@ -877,30 +718,35 @@ int cm_NoneLower(char *s)
 }
 
 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
-                          osi_hyper_t *offp)
+                         osi_hyper_t *offp)
 {
     cm_lookupSearch_t *sp;
     int match;
-    char shortName[13];
-    char *matchName;
+    normchar_t matchName[MAX_PATH];
+    int looking_for_short_name = FALSE;
 
     sp = (cm_lookupSearch_t *) rockp;
 
-    matchName = dep->name;
+    if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
+        /* Can't normalize FS string. */
+        return 0;
+    }
+
     if (sp->caseFold)
-        match = cm_stricmp(matchName, sp->searchNamep);
+        match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
     else
-        match = strcmp(matchName, sp->searchNamep);
+        match = cm_NormStrCmp(matchName, sp->nsearchNamep);
 
     if (match != 0
-         && sp->hasTilde
-         && !cm_Is8Dot3(dep->name)) {
-        matchName = shortName;
-        cm_Gen8Dot3Name(dep, shortName, NULL);
+        && sp->hasTilde
+        && !cm_Is8Dot3(matchName)) {
+
+        cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
         if (sp->caseFold)
-            match = cm_stricmp(matchName, sp->searchNamep);
+            match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
         else
-            match = strcmp(matchName, sp->searchNamep);
+            match = cm_NormStrCmp(matchName, sp->nsearchNamep);
+        looking_for_short_name = TRUE;
     }
 
     if (match != 0)
@@ -910,9 +756,8 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     if (!sp->caseFold) 
         sp->ExactFound = 1;
 
-    if (!sp->caseFold || matchName == shortName) {
-        sp->fid.vnode = ntohl(dep->fid.vnode);
-        sp->fid.unique = ntohl(dep->fid.unique);
+    if (!sp->caseFold || looking_for_short_name) {
+        cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
         return CM_ERROR_STOPNOW;
     }
 
@@ -924,11 +769,10 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
      */
 
     /* Exact matches are the best. */
-    match = strcmp(matchName, sp->searchNamep);
+    match = cm_NormStrCmp(matchName, sp->nsearchNamep);
     if (match == 0) {
         sp->ExactFound = 1;
-        sp->fid.vnode = ntohl(dep->fid.vnode);
-        sp->fid.unique = ntohl(dep->fid.unique);
+        cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
         return CM_ERROR_STOPNOW;
     }
 
@@ -954,109 +798,119 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     sp->NCfound = 1;
 
   inexact:
-    sp->fid.vnode = ntohl(dep->fid.vnode);
-    sp->fid.unique = ntohl(dep->fid.unique);
+    cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
     return 0;
 }       
 
 /* 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)
 {
     long code;
-    cm_buf_t *bufp;
+    cm_buf_t *bufp = NULL;
     osi_hyper_t thyper;
     int tlen;
 
     if (scp->mountPointStringp[0]) 
         return 0;
         
-    /* otherwise, we have to read it in */
-    lock_ReleaseMutex(&scp->mx);
-
-    lock_ObtainRead(&scp->bufCreateLock);
-    thyper.LowPart = thyper.HighPart = 0;
-    code = buf_Get(scp, &thyper, &bufp);
-    lock_ReleaseRead(&scp->bufCreateLock);
+#ifdef AFS_FREELANCE_CLIENT
+    /* File servers do not have data for freelance entries */
+    if (cm_freelanceEnabled &&
+        scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+    {       
+        code = cm_FreelanceFetchMountPointString(scp);
+    } else 
+#endif /* AFS_FREELANCE_CLIENT */        
+    {
+        /* otherwise, we have to read it in */
+        lock_ReleaseWrite(&scp->rw);
 
-    lock_ObtainMutex(&scp->mx);
-    if (code)
-        return code;
+        thyper.LowPart = thyper.HighPart = 0;
+        code = buf_Get(scp, &thyper, reqp, &bufp);
 
-    while (1) {
-        code = cm_SyncOp(scp, bufp, userp, reqp, 0,
-                          CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
+        lock_ObtainWrite(&scp->rw);
         if (code)
-            goto done;
+            return code;
 
-       cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+        while (1) {
+            code = cm_SyncOp(scp, bufp, userp, reqp, 0,
+                              CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
+            if (code)
+                goto done;
 
-        if (cm_HaveBuffer(scp, bufp, 0)) 
-            break;
+            cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
 
-        /* otherwise load buffer */
-        code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
-        if (code)
+            if (cm_HaveBuffer(scp, bufp, 0)) 
+                break;
+
+            /* otherwise load buffer */
+            code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
+            if (code)
+                goto done;
+        }
+        /* locked, has callback, has valid data in buffer */
+        if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
+            return CM_ERROR_TOOBIG;
+        if (tlen <= 0) {
+            code = CM_ERROR_INVAL;
             goto done;
-    }
-    /* locked, has callback, has valid data in buffer */
-    if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
-        return CM_ERROR_TOOBIG;
-    if (tlen <= 0) {
-        code = CM_ERROR_INVAL;
-        goto done;
-    }
+        }
 
-    /* someone else did the work while we were out */
-    if (scp->mountPointStringp[0]) {
-        code = 0;
-        goto done;
-    }
+        /* someone else did the work while we were out */
+        if (scp->mountPointStringp[0]) {
+            code = 0;
+            goto done;
+        }
 
-    /* otherwise, copy out the link */
-    memcpy(scp->mountPointStringp, bufp->datap, tlen);
+        /* otherwise, copy out the link */
+        memcpy(scp->mountPointStringp, bufp->datap, tlen);
 
-    /* now make it null-terminated.  Note that the original contents of a
-     * link that is a mount point is "#volname." where "." is there just to
-     * be turned into a null.  That is, we can trash the last char of the
-     * link without damaging the vol name.  This is a stupid convention,
-     * but that's the protocol.
-     */
-    scp->mountPointStringp[tlen-1] = 0;
-    code = 0;
+        /* now make it null-terminated.  Note that the original contents of a
+         * link that is a mount point is "#volname." where "." is there just to
+         * be turned into a null.  That is, we can trash the last char of the
+         * link without damaging the vol name.  This is a stupid convention,
+         * but that's the protocol.
+         */
+        scp->mountPointStringp[tlen-1] = 0;
+        code = 0;
 
-  done:
-    if (bufp) 
-        buf_Release(bufp);
+      done:
+        if (bufp) 
+            buf_Release(bufp);
+    }
     return code;
 }
 
 
 /* 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)
 {
-    char *cellNamep;
-    char *volNamep;
+    fschar_t *cellNamep = NULL;
+    fschar_t *volNamep = NULL;
     int tlen;
-    long code;
-    char *cp;
-    char *mpNamep;
+    afs_uint32 code;
+    fschar_t *cp;
+    fschar_t *mpNamep;
     cm_volume_t *volp = NULL;
     cm_cell_t *cellp;
-    char mtType;
+    fschar_t mtType;
     cm_fid_t tfid;
     size_t vnLength;
     int targetType;
 
+    *outScpp = NULL;
+
     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;
     }
 
@@ -1064,27 +918,39 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     mpNamep = scp->mountPointStringp;
     if (!mpNamep[0])
        return CM_ERROR_NOSUCHPATH;
-    tlen = (int)strlen(scp->mountPointStringp);
+    tlen = cm_FsStrLen(scp->mountPointStringp);
     mtType = *scp->mountPointStringp;
-    cellNamep = malloc(tlen);
-    volNamep = malloc(tlen);
 
-    cp = strrchr(mpNamep, ':');
+    cp = cm_FsStrChr(mpNamep, _FS(':'));
     if (cp) {
         /* cellular mount point */
-        memset(cellNamep, 0, tlen);
-        strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
-        strcpy(volNamep, cp+1);
+        cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
+        cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
+        volNamep = cm_FsStrDup(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);
-    }
-    else {
+        lock_ObtainWrite(&scp->rw);
+    } else {
         /* normal mt pt */
-        strcpy(volNamep, mpNamep+1);
+        volNamep = cm_FsStrDup(mpNamep + 1);
 
-        cellp = cm_FindCellByID(scp->fid.cell, 0);
+#ifdef AFS_FREELANCE_CLIENT
+        /* 
+         * Mount points in the Freelance cell should default
+         * to the workstation cell.
+         */
+        if (cm_freelanceEnabled &&
+             scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+             scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+        {       
+            fschar_t rootCellName[256]="";
+            cm_GetRootCellName(rootCellName);
+            cellp = cm_GetCell(rootCellName, 0);
+        } else 
+#endif /* AFS_FREELANCE_CLIENT */        
+            cellp = cm_FindCellByID(scp->fid.cell, 0);
     }
 
     if (!cellp) {
@@ -1092,11 +958,11 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         goto done;
     }
 
-    vnLength = strlen(volNamep);
-    if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
+    vnLength = cm_FsStrLen(volNamep);
+    if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
         targetType = BACKVOL;
     else if (vnLength >= 10
-              && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
+             && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
         targetType = ROVOL;
     else
         targetType = RWVOL;
@@ -1110,28 +976,21 @@ 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, 
+        code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp, 
                                 CM_GETVOL_FLAG_CREATE, &volp);
     } else {
-        code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 
+        code = cm_FindVolumeByName(cellp, volNamep, userp, reqp, 
                                   CM_GETVOL_FLAG_CREATE, &volp);
     }
-    lock_ObtainMutex(&scp->mx);
+    lock_ObtainWrite(&scp->rw);
         
     if (code == 0) {
-        /* 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 
-         * scache entry lest the scache entry gets recycled 
-         * (defect 11489)
-         */
-        lock_ObtainMutex(&volp->mx);
-        volp->dotdotFid = dscp->fid;
-        lock_ReleaseMutex(&volp->mx);
+        afs_uint32 cell, volume;
+        cm_vol_state_t *statep;
 
-        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
@@ -1139,9 +998,9 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
          * instead of the read-write.
          */
         if (cm_followBackupPath && 
-            volp->bk.ID != 0 &&
+            volp->vol[BACKVOL].ID != 0 &&
             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
-            (targetType == RWVOL || targetType == ROVOL && volp->ro.ID == 0)
+            (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
             ) {
             targetType = BACKVOL;
         } 
@@ -1152,37 +1011,38 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
          */
         else if (mtType == '#' && targetType == RWVOL && 
                  (scp->flags & CM_SCACHEFLAG_PURERO) && 
-                 volp->ro.ID != 0) {
+                 volp->vol[ROVOL].ID != 0) {
             targetType = ROVOL;
         }
-        if (targetType == ROVOL)
-            scp->mountRootFid.volume = volp->ro.ID;
-        else if (targetType == BACKVOL)
-            scp->mountRootFid.volume = volp->bk.ID;
-        else
-            scp->mountRootFid.volume = volp->rw.ID;
+
+        lock_ObtainWrite(&volp->rw);
+        statep = cm_VolumeStateByType(volp, targetType);
+        volume = statep->ID;
+        statep->dotdotFid = dscp->fid;
+        lock_ReleaseWrite(&volp->rw);
 
         /* 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:
     if (volp)
        cm_PutVolume(volp);
-    free(cellNamep);
-    free(volNamep);
+    if (cellNamep)
+        free(cellNamep);
+    if (volNamep)
+        free(volNamep);
     return code;
 }       
 
-long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
-                       cm_req_t *reqp, cm_scache_t **outpScpp)
+long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
+                       cm_req_t *reqp, cm_scache_t **outScpp)
 {
     long code;
     int dnlcHit = 1;   /* did we hit in the dnlc? yes, we did */
@@ -1190,20 +1050,35 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
     cm_scache_t *mountedScp;
     cm_lookupSearch_t rock;
     int getroot;
+    normchar_t *nnamep = NULL;
+    fschar_t *fnamep = NULL;
+
+    *outScpp = NULL;
 
     memset(&rock, 0, sizeof(rock));
 
     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
-         && strcmp(namep, "..") == 0) {
+        && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
         if (dscp->dotdotFid.volume == 0)
             return CM_ERROR_NOSUCHVOLUME;
         rock.fid = dscp->dotdotFid;
         goto haveFid;
-    } else if (strcmp(namep, ".") == 0) {
+    } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
        rock.fid = dscp->fid;
        goto haveFid;
     }
 
+    nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
+    if (!nnamep) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
+    fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
+    if (!fnamep) {
+        code = CM_ERROR_NOSUCHFILE;
+        goto done;
+    }
+
     if (flags & CM_FLAG_NOMOUNTCHASE) {
         /* In this case, we should go and call cm_Dir* functions
            directly since the following cm_ApplyDir() function will
@@ -1217,12 +1092,12 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
         if (code == 0) {
 #ifdef USE_BPLUS
-            code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
+            code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
             if (code != EINVAL)
                 usedBplus = 1;
             else
 #endif
-                code = cm_DirLookup(&dirop, namep, &rock.fid);
+                code = cm_DirLookup(&dirop, fnamep, &rock.fid);
 
             cm_EndDirOp(&dirop);
         }
@@ -1248,13 +1123,14 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
 
     rock.fid.cell = dscp->fid.cell;
     rock.fid.volume = dscp->fid.volume;
-    rock.searchNamep = namep;
+    rock.searchNamep = fnamep;
+    rock.nsearchNamep = nnamep;
     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
-    rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
+    rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
 
     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
-                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
+                       (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
 
     /* code == 0 means we fell off the end of the dir, while stopnow means
      * that we stopped early, probably because we found the entry we're
@@ -1267,30 +1143,34 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
          */
         if (code == CM_ERROR_NOTDIR) {
             if (flags & CM_FLAG_CHECKPATH)
-                return CM_ERROR_NOSUCHPATH;
+                code = CM_ERROR_NOSUCHPATH;
             else
-                return CM_ERROR_NOSUCHFILE;
+                code = CM_ERROR_NOSUCHFILE;
         }
-        return code;
+        goto done;
     }
 
     getroot = (dscp==cm_data.rootSCachep) ;
     if (!rock.found) {
         if (!cm_freelanceEnabled || !getroot) {
             if (flags & CM_FLAG_CHECKPATH)
-                return CM_ERROR_NOSUCHPATH;
+                code = CM_ERROR_NOSUCHPATH;
             else
-                return CM_ERROR_NOSUCHFILE;
+                code = CM_ERROR_NOSUCHFILE;
+            goto done;
         }
-        else if (!strchr(namep, '#') && !strchr(namep, '%') &&
-                 strcmp(namep, "srvsvc") && strcmp(namep, "wkssvc") &&
-                 strcmp(namep, "ipc$")) {
+        else if (!cm_ClientStrChr(cnamep, '#') &&
+                 !cm_ClientStrChr(cnamep, '%') &&
+                 cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
+                 cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
+                 cm_ClientStrCmpI(cnamep, _C("ipc$"))) 
+        {
             /* nonexistent dir on freelance root, so add it */
-            char fullname[200] = ".";
+            fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
             int  found = 0;
 
-            osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
-                      osi_LogSaveString(afsd_logp,namep));
+            osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S", 
+                     osi_LogSaveClientString(afsd_logp,cnamep));
 
             /* 
              * There is an ugly behavior where a share name "foo" will be searched
@@ -1300,54 +1180,62 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
              */
 
             code = -1;
-            if (namep[0] == '.') {
-                if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
+            if (cnamep[0] == '.') {
+                if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
                     found = 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);
+                        code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.",
+                                                    1, &rock.fid);
+                    if ( cm_FsStrCmpI(&fnamep[1], &fullname[1]) && 
+                         !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                         !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
+                        code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
                 }
             } else {
-                if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
+                if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
                     found = 1;
                     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);
+                    if ( cm_FsStrCmpI(fnamep, fullname) && 
+                         !cm_FreelanceMountPointExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                         !cm_FreelanceSymlinkExists(fnamep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
+                        code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
                 }
             }
-            if (!found || code < 0) {   /* add mount point failed, so give up */
+            if (!found || code) {   /* add mount point failed, so give up */
                 if (flags & CM_FLAG_CHECKPATH)
-                    return CM_ERROR_NOSUCHPATH;
+                    code = CM_ERROR_NOSUCHPATH;
                 else
-                    return CM_ERROR_NOSUCHFILE;
+                    code = CM_ERROR_NOSUCHFILE;
+                goto done;
             }
             tscp = NULL;   /* to force call of cm_GetSCache */
+        } else {
+            if (flags & CM_FLAG_CHECKPATH)
+                code = CM_ERROR_NOSUCHPATH;
+            else
+                code = CM_ERROR_NOSUCHFILE;
+            goto done;
         }
     }
 
   haveFid:       
     if ( !tscp )    /* we did not find it in the dnlc */
     {
-        dnlcHit = 0;   
+        dnlcHit = 0; 
         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
         if (code) 
-            return code;
-    }       
+            goto done;
+    }
     /* 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;
+        goto done;
     }
     cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     /* tscp is now locked */
@@ -1360,82 +1248,100 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
         code = cm_ReadMountPoint(tscp, userp, reqp);
         if (code == 0)
             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
-                                        &mountedScp);
-        lock_ReleaseMutex(&tscp->mx);
+                                       &mountedScp);
+        lock_ReleaseWrite(&tscp->rw);
         cm_ReleaseSCache(tscp);
-        if (code) {
-            return code;
-        }
+        if (code)
+            goto done;
+
         tscp = mountedScp;
     }
     else {
-        lock_ReleaseMutex(&tscp->mx);
+        lock_ReleaseWrite(&tscp->rw);
     }
 
     /* copy back pointer */
-    *outpScpp = tscp;
+    *outScpp = tscp;
 
     /* 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);
-        if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
-            cm_dnlcEnter(dscp, namep, tscp);
-        lock_ReleaseMutex(&dscp->mx);
+        lock_ObtainRead(&dscp->rw);
+        if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
+            /* TODO: reuse nnamep from above */
+            if (nnamep) 
+                free(nnamep);
+            nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
+            if (nnamep)
+                cm_dnlcEnter(dscp, nnamep, tscp);
+        }
+        lock_ReleaseRead(&dscp->rw);
     }
 
     /* and return */
-    return 0;
+  done:
+    if (fnamep) {
+        free (fnamep);
+        fnamep = NULL;
+    }
+    if (nnamep) {
+        free (nnamep);
+        nnamep = NULL;
+    }
+
+    return code;
 }
 
-int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
+int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
 {
-    char *tp;
+    clientchar_t *tp;
     int prefixCount;
 
-    tp = strrchr(inp, '@');
+    tp = cm_ClientStrRChr(inp, '@');
     if (tp == NULL) 
         return 0;              /* no @sys */
 
-    if (strcmp(tp, "@sys") != 0) 
+    if (cm_ClientStrCmp(tp, _C("@sys")) != 0) 
         return 0;      /* no @sys */
 
     /* caller just wants to know if this is a valid @sys type of name */
     if (outp == NULL) 
         return 1;
 
-    if (index >= MAXNUMSYSNAMES)
+    if (index >= cm_sysNameCount)
         return -1;
 
     /* otherwise generate the properly expanded @sys name */
     prefixCount = (int)(tp - inp);
 
-    strncpy(outp, inp, prefixCount);   /* copy out "a." from "a.@sys" */
+    cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);      /* copy out "a." from "a.@sys" */
     outp[prefixCount] = 0;             /* null terminate the "a." */
-    strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
+    cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
     return 1;
 }   
 
-long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
-                                cm_req_t *reqp, cm_scache_t ** outpScpp)
+long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
+                                cm_req_t *reqp, cm_scache_t ** outScpp)
 {
-    long          code = 0;
-    char          cellName[CELL_MAXNAMELEN];
-    char          volumeName[VL_MAXNAMELEN];
+    afs_uint32    code = 0;
+    fschar_t      cellName[CELL_MAXNAMELEN];
+    fschar_t      volumeName[VL_MAXNAMELEN];
     size_t        len;
-    char *        cp;
-    char *        tp;
+    fschar_t *        cp;
+    fschar_t *        tp;
+    fschar_t *        fnamep = NULL;
 
     cm_cell_t *   cellp = NULL;
     cm_volume_t * volp = NULL;
     cm_fid_t      fid;
+    afs_uint32    volume;
     int           volType;
     int           mountType = RWVOL;
 
-    osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
-             osi_LogSaveString(afsd_logp, namep));
+    osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
+             osi_LogSaveClientString(afsd_logp, namep));
 
-    if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
+    if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
         goto _exit_invalid_path;
     }
 
@@ -1447,70 +1353,69 @@ long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
 
      */
 
-    cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
-    tp = strchr(cp, '%');
+    fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
+    cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
+    tp = cm_FsStrChr(cp, '%');
     if (tp == NULL)
-        tp = strchr(cp, '#');
+        tp = cm_FsStrChr(cp, '#');
     if (tp == NULL ||
         (len = tp - cp) == 0 ||
         len > CELL_MAXNAMELEN)
         goto _exit_invalid_path;
-    strncpy(cellName, cp, len);
-    cellName[len] = '\0';
+    cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
 
     if (*tp == '#')
         mountType = ROVOL;
 
     cp = tp+1;                  /* cp now points to volume, supposedly */
-    strncpy(volumeName, cp, VL_MAXNAMELEN-1);
-    volumeName[VL_MAXNAMELEN - 1] = 0;
+    cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
 
     /* OK, now we have the cell and the volume */
     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
-             osi_LogSaveString(afsd_logp, cellName),
-             osi_LogSaveString(afsd_logp, volumeName));
+             osi_LogSaveFsString(afsd_logp, cellName),
+             osi_LogSaveFsString(afsd_logp, volumeName));
 
     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
     if (cellp == NULL) {
         goto _exit_invalid_path;
     }
 
-    len = strlen(volumeName);
-    if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
+    len = cm_FsStrLen(volumeName);
+    if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
         volType = BACKVOL;
     else if (len >= 10 &&
-             strcmp(volumeName + len - 9, ".readonly") == 0)
+             cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
         volType = ROVOL;
     else
         volType = RWVOL;
 
     if (cm_VolNameIsID(volumeName)) {
-        code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
+        code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
                                 CM_GETVOL_FLAG_CREATE, &volp);
     } else {
-        code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
+        code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
                                   CM_GETVOL_FLAG_CREATE, &volp);
     }
 
     if (code != 0)
         goto _exit_cleanup;
 
-    fid.cell = cellp->cellID;
-
     if (volType == BACKVOL)
-        fid.volume = volp->bk.ID;
+        volume = volp->vol[BACKVOL].ID;
     else if (volType == ROVOL ||
-             (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
-        fid.volume = volp->ro.ID;
+             (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
+        volume = volp->vol[ROVOL].ID;
     else
-        fid.volume = volp->rw.ID;
+        volume = volp->vol[RWVOL].ID;
+
+    cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
 
-    fid.vnode = 1;
-    fid.unique = 1;
+    code = cm_GetSCache(&fid, outScpp, userp, reqp);
 
-    code = cm_GetSCache(&fid, outpScpp, userp, reqp);
+  _exit_cleanup:
+    if (fnamep)
+        free(fnamep);
 
- _exit_cleanup:
     if (volp)
         cm_PutVolume(volp);
 
@@ -1525,15 +1430,15 @@ long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
 }
 
 #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)
+long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
+               cm_req_t *reqp, cm_scache_t **outScpp, 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)
+long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
+               cm_req_t *reqp, cm_scache_t **outScpp)
 #endif
 {
     long code;
-    char tname[AFSPATHMAX];
+    clientchar_t tname[AFSPATHMAX];
     int sysNameIndex = 0;
     cm_scache_t *scp = NULL;
 
@@ -1542,7 +1447,7 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
 #endif
 
-    if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
+    if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
         if (flags & CM_FLAG_CHECKPATH)
             return CM_ERROR_NOSUCHPATH;
         else
@@ -1550,13 +1455,13 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
     }
 
     if (dscp == cm_data.rootSCachep &&
-        strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
-        return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
+        cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
+        return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
     }
 
     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
-            code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
+            code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
             if (code > 0) {
                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
 #ifdef DEBUG_REFCOUNT
@@ -1565,7 +1470,7 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
 #endif
 
                 if (code == 0) {
-                    *outpScpp = scp;
+                    *outScpp = scp;
                     return 0;
                 }
                 if (scp) {
@@ -1578,7 +1483,7 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
                 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;
+                *outScpp = scp;
                 return code;
             }
         }
@@ -1588,7 +1493,7 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
         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;
+        *outScpp = scp;
         return code;
     }
 
@@ -1599,7 +1504,28 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
         return CM_ERROR_NOSUCHFILE;
 }
 
-long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
+/*! \brief Unlink a file name
+
+  Encapsulates a call to RXAFS_RemoveFile().
+
+  \param[in] dscp cm_scache_t pointing at the directory containing the
+      name to be unlinked.
+
+  \param[in] fnamep Original name to be unlinked.  This is the
+      name that will be passed into the RXAFS_RemoveFile() call.
+      This parameter is optional.  If not provided, the value will
+      be looked up.
+
+  \param[in] came Client name to be unlinked.  This name will be used
+      to update the local directory caches.
+
+  \param[in] userp cm_user_t for the request.
+
+  \param[in] reqp Request tracker.
+ */
+long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
+               cm_user_t *userp, cm_req_t *reqp)
 {
     long code;
     cm_conn_t *connp;
@@ -1607,27 +1533,46 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     int sflags;
     AFSFetchStatus newDirStatus;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    cm_scache_t *scp = NULL;
+    int free_fnamep = FALSE;
+
+    if (fnamep == NULL) {
+        code = -1;
+#ifdef USE_BPLUS
+        code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+        if (code == 0) {
+            code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
+            if (code == 0)
+                free_fnamep = TRUE;
+            cm_EndDirOp(&dirop);
+        }
+#endif
+        if (code)
+            goto done;
+    }
 
 #ifdef AFS_FREELANCE_CLIENT
     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
         /* deleting a mount point from the root dir. */
-        code = cm_FreelanceRemoveMount(namep);
-        return code;
+        code = cm_FreelanceRemoveMount(fnamep);
+        goto done;
     }
 #endif  
 
+    code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
+
     /* 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;
+        goto done;
     }
 
     /* make the RPC */
@@ -1638,13 +1583,13 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
     do {
         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
-        if (code) 
+        if (code)
             continue;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_RemoveFile(callp, &afsFid, namep,
-                                 &newDirStatus, &volSync);
-        rx_PutConnection(callp);
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
+                                &newDirStatus, &volSync);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
@@ -1658,8 +1603,8 @@ 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);
-    cm_dnlcRemove(dscp, namep);
+    lock_ObtainWrite(&dscp->rw);
+    cm_dnlcRemove(dscp, cnamep);
     cm_SyncOpDone(dscp, NULL, sflags);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
@@ -1670,21 +1615,36 @@ 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);
+    if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
+        cm_DirDeleteEntry(&dirop, fnamep);
 #ifdef USE_BPLUS
-        cm_BPlusDirDeleteEntry(&dirop, namep);
+        cm_BPlusDirDeleteEntry(&dirop, cnamep);
 #endif
     }
     cm_EndDirOp(&dirop);
 
+    if (scp) {
+        cm_ReleaseSCache(scp);
+        if (code == 0) {
+           lock_ObtainWrite(&scp->rw);
+            if (--scp->linkCount == 0)
+                scp->flags |= CM_SCACHEFLAG_DELETED;
+            cm_DiscardSCache(scp);
+           lock_ReleaseWrite(&scp->rw);
+        }
+    }
+
+  done:
+    if (free_fnamep)
+        free(fnamep);
+
     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)
 {
@@ -1693,53 +1653,66 @@ 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);
-        thyper.LowPart = thyper.HighPart = 0;
-        code = buf_Get(linkScp, &thyper, &bufp);
-        lock_ObtainMutex(&linkScp->mx);
-        if (code) 
-            return code;
-        while (1) {
-            code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
-                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
-            if (code) {
-                buf_Release(bufp);
+        
+#ifdef AFS_FREELANCE_CLIENT
+       /* File servers do not have data for freelance entries */
+        if (cm_freelanceEnabled &&
+            linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+            linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+        {
+            code = cm_FreelanceFetchMountPointString(linkScp);
+        } else 
+#endif /* AFS_FREELANCE_CLIENT */        
+        {
+            /* read the link data from the file server*/
+            lock_ReleaseWrite(&linkScp->rw);
+            thyper.LowPart = thyper.HighPart = 0;
+            code = buf_Get(linkScp, &thyper, reqp, &bufp);
+            lock_ObtainWrite(&linkScp->rw);
+            if (code) 
                 return code;
-            }
-           cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+            while (1) {
+                code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
+                                  CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+                if (code) {
+                    buf_Release(bufp);
+                    return code;
+                }
+                cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
 
-            if (cm_HaveBuffer(linkScp, bufp, 0)) 
-                break;
+                if (cm_HaveBuffer(linkScp, bufp, 0)) 
+                    break;
 
-            code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
-            if (code) {
+                code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
+                if (code) {
+                    buf_Release(bufp);
+                    return code;
+                }
+            } /* while loop to get the data */
+
+            /* now if we still have no link read in,
+             * copy the data from the buffer */
+            if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
                 buf_Release(bufp);
-                return code;
+                return CM_ERROR_TOOBIG;
+            }       
+
+            /* otherwise, it fits; make sure it is still null (could have
+             * lost race with someone else referencing this link above),
+             * and if so, copy in the data.
+             */
+            if (!linkScp->mountPointStringp[0]) {
+                strncpy(linkScp->mountPointStringp, bufp->datap, temp);
+                linkScp->mountPointStringp[temp] = 0;  /* null terminate */
             }
-        } /* while loop to get the data */
-                
-        /* now if we still have no link read in,
-         * copy the data from the buffer */
-        if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
             buf_Release(bufp);
-            return CM_ERROR_TOOBIG;
         }
+        
+        if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
+            linkScp->fileType = CM_SCACHETYPE_DFSLINK;
 
-        /* otherwise, it fits; make sure it is still null (could have
-         * lost race with someone else referencing this link above),
-         * and if so, copy in the data.
-         */
-        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 */
 
     return 0;
@@ -1751,19 +1724,19 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
  * other than the directory containing the symbolic link, then the new root is
  * returned in *newRootScpp, otherwise a null is returned there.
  */
-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 cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
+                     cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
+                     cm_user_t *userp, cm_req_t *reqp)
 {
     long code = 0;
     long len;
-    char *linkp;
+    fschar_t *linkp;
     cm_space_t *tsp;
 
     *newRootScpp = NULL;
     *newSpaceBufferp = NULL;
 
-    lock_ObtainMutex(&linkScp->mx);
+    lock_ObtainWrite(&linkScp->rw);
     code = cm_HandleLink(linkScp, userp, reqp);
     if (code)
         goto done;
@@ -1772,15 +1745,17 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
      * bigger than max path length, so we don't really have to worry about
      * being a little conservative here.
      */
-    if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
-         >= CM_UTILS_SPACESIZE)
-        return CM_ERROR_TOOBIG;
+    if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
+        >= CM_UTILS_SPACESIZE) {
+        code = CM_ERROR_TOOBIG;
+        goto done;
+    }
 
     tsp = cm_GetSpace();
     linkp = linkScp->mountPointStringp;
     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
         if (strlen(linkp) > cm_mountRootLen)
-            strcpy(tsp->data, linkp+cm_mountRootLen+1);
+            StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
         else
             tsp->data[0] = 0;
         *newRootScpp = cm_data.rootSCachep;
@@ -1792,7 +1767,7 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
             if (strnicmp(p, "all", 3) == 0)
                 p += 4;
 
-            strcpy(tsp->data, p);
+            StringCbCopyA(tsp->data, sizeof(tsp->data), p);
             for (p = tsp->data; *p; p++) {
                 if (*p == '\\')
                     *p = '/';
@@ -1801,20 +1776,20 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
             cm_HoldSCache(cm_data.rootSCachep);
         } else {
             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
-            strcpy(tsp->data, linkp);
+            StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
             code = CM_ERROR_PATH_NOT_COVERED;
         }
     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
-        strcpy(tsp->data, linkp);
+        StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
         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 */
-        strcpy(tsp->data, linkp+1);
+        StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
         *newRootScpp = cm_data.rootSCachep;
         cm_HoldSCache(cm_data.rootSCachep);
 #else
@@ -1822,45 +1797,58 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
          * the user can see what the link points to
          */
         linkScp->fileType = CM_SCACHETYPE_INVALID;
-        strcpy(tsp->data, linkp);
+        StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
         code = CM_ERROR_NOSUCHPATH;
 #endif  
     } else {
         /* a relative link */
-        strcpy(tsp->data, linkp);
+        StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
     }
     if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
-        strcat(tsp->data, "\\");
-        strcat(tsp->data, pathSuffixp);
+        StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
+        StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
     }
-    if (code == 0)
+
+    if (code == 0) {
+        clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
+        if (cpath != NULL) {
+        cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
+        free(cpath);
         *newSpaceBufferp = tsp;
-    else {
+    } else {
+            code = CM_ERROR_NOSUCHPATH;
+        }
+    } 
+
+    if (code != 0) {
         cm_FreeSpace(tsp);
 
-        if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
+        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);
+ done:
+    lock_ReleaseWrite(&linkScp->rw);
     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)
+long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
+                 cm_user_t *userp, clientchar_t *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)
+long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
+              cm_user_t *userp, clientchar_t *tidPathp,
+              cm_req_t *reqp, cm_scache_t **outScpp)
 #endif
 {
     long code;
-    char *tp;                  /* ptr moving through input buffer */
-    char tc;                   /* temp char */
+    clientchar_t *tp;                  /* ptr moving through input buffer */
+    clientchar_t tc;                   /* temp char */
     int haveComponent;         /* has new component started? */
-    char component[AFSPATHMAX];        /* this is the new component */
-    char *cp;                  /* component name being assembled */
+    clientchar_t component[AFSPATHMAX];        /* this is the new component */
+    clientchar_t *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 */
@@ -1869,7 +1857,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     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 */
+    clientchar_t *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 */
@@ -1878,11 +1866,13 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     int fid_count = 0;          /* number of fids processed in this path walk */
     int i;
 
+    *outScpp = NULL;
+
 #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);
+    osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
+             rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>", 
+             flags);
 #endif
 
     tp = tidPathp;
@@ -1891,7 +1881,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
         phase = 2;
     }
     if (tp == NULL) {
-        tp = "";
+        tp = _C("");
     }
     haveComponent = 0;
     psp = NULL;
@@ -1938,16 +1928,18 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
                    extraFlag = CM_FLAG_NOMOUNTCHASE;
                code = cm_Lookup(tscp, component,
-                                 flags | extraFlag,
-                                 userp, reqp, &nscp);
+                                 flags | extraFlag,
+                                 userp, reqp, &nscp);
 
                 if (code == 0) {
-                    if (!strcmp(component,"..") || !strcmp(component,".")) {
+                    if (!cm_ClientStrCmp(component,_C("..")) ||
+                        !cm_ClientStrCmp(component,_C("."))) {
                         /* 
-                         * roll back the fid list until we find the fid 
-                         * that matches where we are now.  Its not necessarily
-                         * one or two fids because they might have been 
-                         * symlinks or mount points or both that were crossed.  
+                         * roll back the fid list until we find the
+                         * fid that matches where we are now.  Its not
+                         * necessarily one or two fids because they
+                         * might have been symlinks or mount points or
+                         * both that were crossed.
                          */
                         for ( i=fid_count-1; i>=0; i--) {
                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
@@ -1956,17 +1948,13 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                         fid_count = i+1;
                     } else {
                         /* add the new fid to the list */
-                        for ( i=0; i<fid_count; i++) {
-                            if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
-                                code = CM_ERROR_TOO_MANY_SYMLINKS;
-                                cm_ReleaseSCache(nscp);
-                                nscp = NULL;
-                                break;
-                            }
-                        }
-                        if (i == fid_count && fid_count < MAX_FID_COUNT) {
-                            fids[fid_count++] = nscp->fid;
-                        }
+                        if (fid_count == MAX_FID_COUNT) {
+                            code = CM_ERROR_TOO_MANY_SYMLINKS;
+                            cm_ReleaseSCache(nscp);
+                            nscp = NULL;
+                            break;
+                        }       
+                        fids[fid_count++] = nscp->fid;
                     }
                 }
 
@@ -1977,8 +1965,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                    if (psp) 
                        cm_FreeSpace(psp);
                    if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
-                         tscp->fileType == CM_SCACHETYPE_SYMLINK) 
-                    {
+                        tscp->fileType == CM_SCACHETYPE_SYMLINK) {
                        osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
                        return CM_ERROR_NOSUCHPATH;
                    } else {
@@ -2002,15 +1989,15 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     break;
                 }
 
-                /* now, if tscp is a symlink, we should follow
-                 * it and assemble the path again.
+                /* 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) {
@@ -2023,7 +2010,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;
@@ -2037,10 +2024,18 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                         return CM_ERROR_TOO_MANY_SYMLINKS;
                     }
                     if (tc == 0) 
-                        restp = "";
+                        restp = _C("");
                     else 
                         restp = tp;
-                    code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
+
+                    {
+                        fschar_t * frestp;
+
+                        /* TODO: make this better */
+                        frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
+                        code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
+                        free(frestp);
+                    }
 
                     if (code == 0 && linkScp != NULL) {
                         if (linkScp == cm_data.rootSCachep) 
@@ -2082,7 +2077,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     if (psp) 
                         cm_FreeSpace(psp);
                     psp = tempsp;
-                    tp = psp->data;
+                    tp = psp->wdata;
                     cm_ReleaseSCache(tscp);
                     tscp = linkScp;
                     linkScp = NULL;
@@ -2102,7 +2097,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;
@@ -2138,7 +2133,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
         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);
+    afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
 #endif
     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
     return code;
@@ -2166,6 +2161,8 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
     cm_space_t *spacep;
     cm_scache_t *newRootScp;
 
+    *outScpp = NULL;
+
     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
 
     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
@@ -2180,9 +2177,9 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
         cm_HoldSCache(dscp);
     }
 
-    code = cm_NameI(newRootScp, spacep->data,
-                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
-                     userp, NULL, reqp, outScpp);
+    code = cm_NameI(newRootScp, spacep->wdata,
+                    CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
+                    userp, NULL, reqp, outScpp);
 
     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
         code = CM_ERROR_NOSUCHPATH;
@@ -2201,23 +2198,6 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
     return code;
 }
 
-/* make this big enough so that one buffer of dir pages won't overflow.  We'll
- * check anyway, but we want to minimize the chance that we have to leave stuff
- * unstat'd.
- */
-#define CM_BULKMAX             (3 * AFSCBMAX)
-
-/* rock for bulk stat calls */
-typedef struct cm_bulkStat {
-    osi_hyper_t bufOffset;     /* only do it for things in this buffer page */
-
-    /* info for the actual call */
-    int counter;                       /* next free slot */
-    AFSFid fids[CM_BULKMAX];
-    AFSFetchStatus stats[CM_BULKMAX];
-    AFSCallBack callbacks[CM_BULKMAX];
-} cm_bulkStat_t;
-
 /* for a given entry, make sure that it isn't in the stat cache, and then
  * add it to the list of file IDs to be obtained.
  *
@@ -2257,24 +2237,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 */
@@ -2300,16 +2277,10 @@ 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
- * calls on all undeleted files in the page of the directory specified.
- */
 afs_int32
-cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
-              cm_req_t *reqp)
+cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
 {
-    long code;
-    cm_bulkStat_t bb;  /* this is *BIG*, probably 16K or so;
-                         * watch for stack problems */
+    afs_int32 code = 0;
     AFSCBFids fidStruct;
     AFSBulkStats statStruct;
     cm_conn_t *connp;
@@ -2322,43 +2293,24 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     long j;
     cm_scache_t *scp;
     cm_fid_t tfid;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     int inlinebulk = 0;                /* Did we use InlineBulkStatus RPC or not? */
-
-    osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
-
-    /* should be on a buffer boundary */
-    osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
-
-    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);
-
-    /* if we failed, bail out early */
-    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
      * time.
      */
-    filex = 0;
-    while (filex < bb.counter) {
-        filesThisCall = bb.counter - filex;
+    for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
+        filesThisCall = bbp->counter - filex;
         if (filesThisCall > AFSCBMAX) 
             filesThisCall = AFSCBMAX;
 
         fidStruct.AFSCBFids_len = filesThisCall;
-        fidStruct.AFSCBFids_val = &bb.fids[filex];
+        fidStruct.AFSCBFids_val = &bbp->fids[filex];
         statStruct.AFSBulkStats_len = filesThisCall;
-        statStruct.AFSBulkStats_val = &bb.stats[filex];
+        statStruct.AFSBulkStats_val = &bbp->stats[filex];
         callbackStruct.AFSCBs_len = filesThisCall;
-        callbackStruct.AFSCBs_val = &bb.callbacks[filex];
+        callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
         cm_StartCallbackGrantingCall(NULL, &cbReq);
         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
         do {
@@ -2366,9 +2318,9 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
             if (code) 
                 continue;
 
-            callp = cm_GetRxConn(connp);
+            rxconnp = cm_GetRxConn(connp);
            if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
-               code = RXAFS_InlineBulkStatus(callp, &fidStruct,
+               code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
                                      &statStruct, &callbackStruct, &volSync);
                if (code == RXGEN_OPCODE) {
                    cm_SetServerNoInlineBulk(connp->serverp, 0);
@@ -2377,10 +2329,10 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
                }
            }
            if (!inlinebulk) {
-               code = RXAFS_BulkStatus(callp, &fidStruct,
+               code = RXAFS_BulkStatus(rxconnp, &fidStruct,
                                        &statStruct, &callbackStruct, &volSync);
            }
-            rx_PutConnection(callp);
+            rx_PutConnection(rxconnp);
 
         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
                              &volSync, NULL, &cbReq, code));
@@ -2402,10 +2354,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, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
             code = cm_GetSCache(&tfid, &scp, userp, reqp);
             if (code != 0) 
                 continue;
@@ -2413,7 +2362,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
@@ -2430,30 +2379,65 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
                          | CM_SCACHEFLAG_STORING
                          | CM_SCACHEFLAG_SIZESTORING))) {
                 cm_EndCallbackGrantingCall(scp, &cbReq,
-                                            &bb.callbacks[j],
+                                            &bbp->callbacks[j],
                                             CM_CALLBACK_MAINTAINCOUNT);
-                cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
+                cm_MergeStatus(dscp, scp, &bbp->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,
          * after doing the vnode processing above */
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
-
-        filex += filesThisCall;
     }  /* while there are still more files to process */
-    lock_ObtainMutex(&dscp->mx);
 
     /* If we did the InlineBulk RPC pull out the return code and log it */
     if (inlinebulk) {
-       if ((&bb.stats[0])->errorCode) {
+       if ((&bbp->stats[0])->errorCode) {
            osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
-                    (&bb.stats[0])->errorCode);
+                    (&bbp->stats[0])->errorCode);
+            code = (&bbp->stats[0])->errorCode;
        }
     }
 
-    osi_Log0(afsd_logp, "END cm_TryBulkStat");
+    return code;
+}
+
+/* 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
+cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
+              cm_req_t *reqp)
+{
+    afs_int32 code;
+    cm_bulkStat_t *bbp;
+
+    osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
+
+    /* should be on a buffer boundary */
+    osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
+
+    bbp = malloc(sizeof(cm_bulkStat_t));
+    memset(bbp, 0, sizeof(cm_bulkStat_t));
+    bbp->bufOffset = *offsetp;
+
+    lock_ReleaseWrite(&dscp->rw);
+    /* first, assemble the file IDs we need to stat */
+    code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
+
+    /* if we failed, bail out early */
+    if (code && code != CM_ERROR_STOPNOW) {
+        free(bbp);
+        lock_ObtainWrite(&dscp->rw);
+        return code;
+    }
+
+    code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
+    osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
+
+    lock_ObtainWrite(&dscp->rw);
+    free(bbp);
     return 0;
 }       
 
@@ -2510,7 +2494,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) 
@@ -2528,9 +2512,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.
      */
@@ -2538,7 +2522,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.
@@ -2589,7 +2573,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;
@@ -2605,19 +2589,20 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
     cm_conn_t *connp;
     AFSFid tfid;
     AFSStoreStatus afsInStatus;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
 
     /* handle file length setting */
     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);
@@ -2625,7 +2610,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);
@@ -2634,10 +2619,10 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
         if (code) 
             continue;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_StoreStatus(callp, &tfid,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_StoreStatus(rxconnp, &tfid,
                                   &afsInStatus, &afsOutStatus, &volSync);
-        rx_PutConnection(callp);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
                          &scp->fid, &volSync, NULL, NULL, code));
@@ -2648,7 +2633,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,
@@ -2659,11 +2644,11 @@ 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;
 }       
 
-long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
+long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
 {       
     cm_conn_t *connp;
@@ -2679,13 +2664,14 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     AFSFetchStatus newFileStatus;
     AFSCallBack newFileCallback;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    fschar_t * fnamep = NULL;
 
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(namep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -2704,9 +2690,9 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
      * 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 {
@@ -2717,6 +2703,8 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     }
     didEnd = 0;
 
+    fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
+
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
@@ -2730,12 +2718,12 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         dirAFSFid.Vnode = dscp->fid.vnode;
         dirAFSFid.Unique = dscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
                                  &inStatus, &newAFSFid, &newFileStatus,
                                  &updatedDirStatus, &newFileCallback,
                                  &volSync);
-        rx_PutConnection(callp);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
                          &dscp->fid, &volSync, NULL, &cbReq, code));
@@ -2750,12 +2738,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, 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
@@ -2763,23 +2751,19 @@ 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,
-                                userp, 0);
+                               userp, 0);
                 cm_EndCallbackGrantingCall(scp, &cbReq,
-                                            &newFileCallback, 0);
+                                           &newFileCallback, 0);
                 didEnd = 1;     
             }       
-            lock_ReleaseMutex(&scp->mx);
-            *scpp = scp;
+            lock_ReleaseWrite(&scp->rw);
         }
     }
 
@@ -2788,13 +2772,22 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
 
     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
-        cm_DirCreateEntry(&dirop, namep, &newFid);
+        cm_DirCreateEntry(&dirop, fnamep, &newFid);
 #ifdef USE_BPLUS
-        cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+        cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
 #endif
     }
     cm_EndDirOp(&dirop);
 
+    if (fnamep)
+        free(fnamep);
+
+    if (scp) {
+        if (scpp)
+            *scpp = scp;
+        else
+            cm_ReleaseSCache(scp);
+    }
     return code;
 }       
 
@@ -2802,11 +2795,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
@@ -2818,13 +2809,13 @@ 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;
 }
 
-long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
-                 cm_user_t *userp, cm_req_t *reqp)
+long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
+                cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
 {
     cm_conn_t *connp;
     long code;
@@ -2839,13 +2830,14 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     AFSFetchStatus newDirStatus;
     AFSCallBack newDirCallback;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    fschar_t * fnamep = NULL;
 
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(namep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -2864,9 +2856,9 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
      * 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 {
@@ -2877,6 +2869,7 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     }
     didEnd = 0;
 
+    fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
@@ -2890,15 +2883,15 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         dirAFSFid.Vnode = dscp->fid.vnode;
         dirAFSFid.Unique = dscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
                               &inStatus, &newAFSFid, &newDirStatus,
                               &updatedDirStatus, &newDirCallback,
                               &volSync);
-        rx_PutConnection(callp);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, &volSync, NULL, &cbReq, code));
+                        &dscp->fid, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
         
     if (code)
@@ -2910,12 +2903,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, 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
@@ -2923,13 +2916,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);
@@ -2937,8 +2927,7 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
                                             &newDirCallback, 0);
                 didEnd = 1;             
             }
-            lock_ReleaseMutex(&scp->mx);
-            cm_ReleaseSCache(scp);
+            lock_ReleaseWrite(&scp->rw);
         }
     }
 
@@ -2947,18 +2936,27 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
 
     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
-        cm_DirCreateEntry(&dirop, namep, &newFid);
+        cm_DirCreateEntry(&dirop, fnamep, &newFid);
 #ifdef USE_BPLUS
-        cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+        cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
 #endif
     }
     cm_EndDirOp(&dirop);
 
+    free(fnamep);
+
+    if (scp) {
+        if (scpp)
+            *scpp = scp;
+        else
+            cm_ReleaseSCache(scp);
+    }
+
     /* and return error code */
     return code;
 }       
 
-long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
+long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
              cm_user_t *userp, cm_req_t *reqp)
 {
     cm_conn_t *connp;
@@ -2968,8 +2966,9 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
     AFSFetchStatus updatedDirStatus;
     AFSFetchStatus newLinkStatus;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    fschar_t * fnamep = NULL;
 
     if (dscp->fid.cell != sscp->fid.cell ||
         dscp->fid.volume != sscp->fid.volume) {
@@ -2977,15 +2976,17 @@ 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);
 
     if (code)
         return code;
 
+    fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
+
     /* try the RPC now */
     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
     do {
@@ -3000,11 +3001,11 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
         existingAFSFid.Vnode = sscp->fid.vnode;
         existingAFSFid.Unique = sscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
             &newLinkStatus, &updatedDirStatus, &volSync);
-        rx_PutConnection(callp);
-        osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
+        rx_PutConnection(rxconnp);
+        osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
 
     } while (cm_Analyze(connp, userp, reqp,
         &dscp->fid, &volSync, NULL, NULL, code));
@@ -3020,27 +3021,36 @@ 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, CM_MERGEFLAG_DIROP);
     }
-    lock_ReleaseMutex(&dscp->mx);
+    lock_ReleaseWrite(&dscp->rw);
 
     if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
-            cm_DirCreateEntry(&dirop, namep, &sscp->fid);
+            cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
 #ifdef USE_BPLUS
-            cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
+            cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
 #endif
         }
     }
     cm_EndDirOp(&dirop);
 
+    /* Update the linked object status */
+    if (code == 0) {
+        lock_ObtainWrite(&sscp->rw);
+        cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, 0);
+        lock_ReleaseWrite(&sscp->rw);
+    }
+
+    free(fnamep);
+
     return code;
 }
 
-long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
+long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
 {
     cm_conn_t *connp;
@@ -3053,23 +3063,26 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
     AFSFetchStatus updatedDirStatus;
     AFSFetchStatus newLinkStatus;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    fschar_t *fnamep = NULL;
 
     /* 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_EndDirOp(&dirop);
     if (code) {
         return code;
     }
 
+    fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
+
     cm_StatusFromAttr(&inStatus, NULL, attrp);
 
     /* try the RPC now */
@@ -3083,11 +3096,11 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
         dirAFSFid.Vnode = dscp->fid.vnode;
         dirAFSFid.Unique = dscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
                               &inStatus, &newAFSFid, &newLinkStatus,
                               &updatedDirStatus, &volSync);
-        rx_PutConnection(callp);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
                          &dscp->fid, &volSync, NULL, NULL, code));
@@ -3102,23 +3115,20 @@ 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, 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);
+            cm_DirCreateEntry(&dirop, fnamep, &newFid);
 #ifdef USE_BPLUS
-            cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+            cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
 #endif
         }
     }
@@ -3130,28 +3140,45 @@ 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);
         }
     }
+
+    free(fnamep);
        
     /* and return error code */
     return code;
 }
 
-long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
-                   cm_req_t *reqp)
+/*! \brief Remove a directory
+
+  Encapsulates a call to RXAFS_RemoveDir().
+
+  \param[in] dscp cm_scache_t for the directory containing the
+      directory to be removed.
+
+  \param[in] fnamep This will be the original name of the directory
+      as known to the file server.   It will be passed in to RXAFS_RemoveDir().
+      This parameter is optional.  If it is not provided the value
+      will be looked up.
+
+  \param[in] cnamep Normalized name used to update the local
+      directory caches.
+
+  \param[in] userp cm_user_t for the request.
+
+  \param[in] reqp Request tracker.
+*/
+long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
 {
     cm_conn_t *connp;
     long code;
@@ -3159,20 +3186,41 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
     int didEnd;
     AFSFetchStatus updatedDirStatus;
     AFSVolSync volSync;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t dirop;
+    cm_scache_t *scp = NULL;
+    int free_fnamep = FALSE;
+
+    if (fnamep == NULL) {
+        code = -1;
+#ifdef USE_BPLUS
+        code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+        if (code == 0) {
+            code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
+            if (code == 0)
+                free_fnamep = TRUE;
+            cm_EndDirOp(&dirop);
+        }
+#endif
+        if (code)
+            goto done;
+    }
+
+    code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
+    if (code)
+        goto done;
 
     /* 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) {
         cm_EndDirOp(&dirop);
-        return code;
+        goto done;
     }
     didEnd = 0;
 
@@ -3187,13 +3235,13 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
         dirAFSFid.Vnode = dscp->fid.vnode;
         dirAFSFid.Unique = dscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
-                                &updatedDirStatus, &volSync);
-        rx_PutConnection(callp);
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
+                               &updatedDirStatus, &volSync);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, &volSync, NULL, NULL, code));
+                        &dscp->fid, &volSync, NULL, NULL, code));
     code = cm_MapRPCErrorRmdir(code, reqp);
 
     if (code)
@@ -3205,24 +3253,37 @@ 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_dnlcRemove(dscp, cnamep); 
         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)) {
-            cm_DirDeleteEntry(&dirop, namep);
+        if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
+            cm_DirDeleteEntry(&dirop, fnamep);
 #ifdef USE_BPLUS
-            cm_BPlusDirDeleteEntry(&dirop, namep);
+            cm_BPlusDirDeleteEntry(&dirop, cnamep);
 #endif
         }
     }
     cm_EndDirOp(&dirop);
 
+    if (scp) {
+        cm_ReleaseSCache(scp);
+        if (code == 0) {
+           lock_ObtainWrite(&scp->rw);
+            scp->flags |= CM_SCACHEFLAG_DELETED;
+           lock_ReleaseWrite(&scp->rw);
+        }
+    }
+
+  done:
+    if (free_fnamep)
+        free(fnamep);
+
     /* and return error code */
     return code;
 }
@@ -3230,7 +3291,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 */
@@ -3239,14 +3300,39 @@ 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;
 }       
 
-long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
-                char *newNamep, cm_user_t *userp, cm_req_t *reqp)
+/*! \brief Rename a file or directory
+
+  Encapsulates a RXAFS_Rename() call.
+
+  \param[in] oldDscp cm_scache_t for the directory containing the old
+      name.
+
+  \param[in] oldNamep The original old name known to the file server.
+      This is the name that will be passed into the RXAFS_Rename().
+      If it is not provided, it will be looked up.
+
+  \param[in] normalizedOldNamep Normalized old name.  This is used for
+  updating local directory caches.
+
+  \param[in] newDscp cm_scache_t for the directory containing the new
+  name.
+
+  \param[in] newNamep New name. Normalized.
+
+  \param[in] userp cm_user_t for the request.
+
+  \param[in,out] reqp Request tracker.
+
+*/
+long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
+               cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
+               cm_req_t *reqp)
 {
     cm_conn_t *connp;
     long code;
@@ -3257,11 +3343,69 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
     AFSFetchStatus updatedNewDirStatus;
     AFSVolSync volSync;
     int oneDir;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_dirOp_t oldDirOp;
     cm_fid_t   fileFid;
     int        diropCode = -1;
     cm_dirOp_t newDirOp;
+    fschar_t * newNamep = NULL;
+    int free_oldNamep = FALSE;
+    cm_scache_t *oldScp = NULL, *newScp = NULL;
+
+    if (cOldNamep == NULL || cNewNamep == NULL ||
+        cm_ClientStrLen(cOldNamep) == 0 ||
+        cm_ClientStrLen(cNewNamep) == 0)
+        return CM_ERROR_INVAL;
+
+    /* 
+     * Before we permit the operation, make sure that we do not already have
+     * an object in the destination directory that has a case-insensitive match
+     * for this name UNLESS the matching object is the object we are renaming.
+     */
+    code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
+    if (code) {
+        osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed", 
+                 oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
+        goto done;
+    }
+
+    code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
+    if (code == 0) {
+        /* found a matching object with the new name */
+        if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
+            /* and they don't match so return an error */
+            osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists", 
+                      newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
+            code = CM_ERROR_EXISTS;
+        }
+        cm_ReleaseSCache(newScp);
+        newScp = NULL;
+    } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
+        code = CM_ERROR_EXISTS;
+    } else {
+        code = 0;
+    }
+    if (code) 
+        goto done;
+
+    if (oldNamep == NULL) {
+        code = -1;
+#ifdef USE_BPLUS
+        code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ, &oldDirOp);
+        if (code == 0) {
+            code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
+            if (code == 0)
+                free_oldNamep = TRUE;
+            cm_EndDirOp(&oldDirOp);
+        }
+#endif
+        if (code) {
+            osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed", 
+                      oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
+            goto done;
+        }
+    }
+
 
     /* 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
@@ -3270,17 +3414,21 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
      */
     if (oldDscp == newDscp) {
         /* check for identical names */
-        if (strcmp(oldNamep, newNamep) == 0)
-            return CM_ERROR_RENAME_IDENTICAL;
+        if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
+            osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL", 
+                      oldDscp, newDscp);
+            code = CM_ERROR_RENAME_IDENTICAL;
+            goto done;
+        }
 
         oneDir = 1;
         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
-        lock_ObtainMutex(&oldDscp->mx);
-        cm_dnlcRemove(oldDscp, oldNamep);
-        cm_dnlcRemove(oldDscp, newNamep);
+        lock_ObtainWrite(&oldDscp->rw);
+        cm_dnlcRemove(oldDscp, cOldNamep);
+        cm_dnlcRemove(oldDscp, cNewNamep);
         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);
         }
@@ -3289,40 +3437,48 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
         /* two distinct dir vnodes */
         oneDir = 0;
         if (oldDscp->fid.cell != newDscp->fid.cell ||
-             oldDscp->fid.volume != newDscp->fid.volume)
-            return CM_ERROR_CROSSDEVLINK;
+             oldDscp->fid.volume != newDscp->fid.volume) {
+            osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK", 
+                      oldDscp, newDscp);
+            code = CM_ERROR_CROSSDEVLINK;
+            goto done;
+        }
 
         /* shouldn't happen that we have distinct vnodes for two
          * different files, but could due to deliberate attack, or
          * stale info.  Avoid deadlocks and quit now.
          */
-        if (oldDscp->fid.vnode == newDscp->fid.vnode)
-            return CM_ERROR_CROSSDEVLINK;
+        if (oldDscp->fid.vnode == newDscp->fid.vnode) {
+            osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision", 
+                      oldDscp, newDscp);
+            code = CM_ERROR_CROSSDEVLINK;
+            goto done;
+        }
 
         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
-            lock_ObtainMutex(&oldDscp->mx);
-            cm_dnlcRemove(oldDscp, oldNamep);
+            lock_ObtainWrite(&oldDscp->rw);
+            cm_dnlcRemove(oldDscp, cOldNamep);
             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
-                              CM_SCACHESYNC_STOREDATA);
-            lock_ReleaseMutex(&oldDscp->mx);
+                             CM_SCACHESYNC_STOREDATA);
+            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);
-                cm_dnlcRemove(newDscp, newNamep);
+                lock_ObtainWrite(&newDscp->rw);
+                cm_dnlcRemove(newDscp, cNewNamep);
                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
-                                  CM_SCACHESYNC_STOREDATA);
-                lock_ReleaseMutex(&newDscp->mx);
+                                 CM_SCACHESYNC_STOREDATA);
+                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);
                 }       
             }
@@ -3330,39 +3486,41 @@ 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);
-            cm_dnlcRemove(newDscp, newNamep);
+            lock_ObtainWrite(&newDscp->rw);
+            cm_dnlcRemove(newDscp, cNewNamep);
             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);
-                cm_dnlcRemove(oldDscp, oldNamep);
+                lock_ObtainWrite(&oldDscp->rw);
+                cm_dnlcRemove(oldDscp, cOldNamep);
                 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);
                 }       
             }
         }
     }  /* two distinct vnodes */
 
-    if (code) {
-        return code;
-    }
+    if (code) 
+        goto done;
+
     didEnd = 0;
 
+    newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
+
     /* try the RPC now */
     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
               oldDscp, newDscp);
@@ -3378,12 +3536,12 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
         newDirAFSFid.Vnode = newDscp->fid.vnode;
         newDirAFSFid.Unique = newDscp->fid.unique;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
-                             &newDirAFSFid, newNamep,
-                             &updatedOldDirStatus, &updatedNewDirStatus,
-                             &volSync);
-        rx_PutConnection(callp);
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
+                            &newDirAFSFid, newNamep,
+                            &updatedOldDirStatus, &updatedNewDirStatus,
+                            &volSync);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
                          &volSync, NULL, NULL, code));
@@ -3399,39 +3557,36 @@ 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, CM_MERGEFLAG_DIROP);
-    lock_ReleaseMutex(&oldDscp->mx);
-
-    if (code == 0) {
-        if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
+                       userp, CM_MERGEFLAG_DIROP);
+    lock_ReleaseWrite(&oldDscp->rw);
 
+    if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
 #ifdef USE_BPLUS
-            diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
-            if (diropCode == CM_ERROR_INEXACT_MATCH)
-                diropCode = 0;
-            else if (diropCode == EINVAL)
+        diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
+        if (diropCode == CM_ERROR_INEXACT_MATCH)
+            diropCode = 0;
+        else if (diropCode == EINVAL)
 #endif
-                diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
+            diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
 
-            if (diropCode == 0) {
-                if (oneDir) {
-                    diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
-#ifdef USE_BPLUS
-                    cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
+        if (diropCode == 0) {
+            if (oneDir) {
+                diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
+#ifdef USE_BPLUS        
+                cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
 #endif
-                }
-
-                if (diropCode == 0) { 
-                    diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
+            }
+                
+            if (diropCode == 0) { 
+                diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
 #ifdef USE_BPLUS
-                    cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
-#endif
-                }
+                cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
+#endif  
             }
         }
     }
@@ -3443,13 +3598,24 @@ 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, CM_MERGEFLAG_DIROP);
-        lock_ReleaseMutex(&newDscp->mx);
-
+        lock_ReleaseWrite(&newDscp->rw);
+
+#if 0 
+        /* 
+         * The following optimization does not work.  
+         * When the file server processed a RXAFS_Rename() request the 
+         * FID of the object being moved between directories is not 
+         * preserved.  The client does not know the new FID nor the 
+         * version number of the target.  Not only can we not create
+         * the directory entry in the new directory, but we can't 
+         * preserve the cached data for the file.  It must be re-read
+         * from the file server.  - jaltman, 2009/02/20
+         */
         if (code == 0) {
             /* we only make the local change if we successfully made
                the change in the old directory AND there was only one
@@ -3457,13 +3623,32 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
 #ifdef USE_BPLUS
-                cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
+                cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
 #endif
             }
         }
+#endif /* 0 */
         cm_EndDirOp(&newDirOp);
     }
 
+    /* 
+     * After the rename the file server has invalidated the callbacks
+     * on the file that was moved nor do we have a directory reference 
+     * to it anymore.
+     */
+    lock_ObtainWrite(&oldScp->rw);
+    cm_DiscardSCache(oldScp);
+    lock_ReleaseWrite(&oldScp->rw);
+
+  done:
+    if (oldScp)
+        cm_ReleaseSCache(oldScp);
+
+    if (free_oldNamep)
+        free(oldNamep);
+
+    free(newNamep);
+
     /* and return error code */
     return code;
 }
@@ -3798,7 +3983,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, 
@@ -3839,7 +4024,7 @@ long cm_LockCheckRead(cm_scache_t *scp,
 
         if (INTERSECT_RANGE(range, fileLock->range)) {
             if (IS_LOCK_ACTIVE(fileLock)) {
-                if (fileLock->key == key) {
+                if (cm_KeyEquals(&fileLock->key, &key, 0)) {
 
                     /* If there is an active lock for this client, it
                        is safe to substract ranges.*/
@@ -3861,7 +4046,7 @@ long cm_LockCheckRead(cm_scache_t *scp,
                         cm_LockRangeSubtract(&range, &fileLock->range);
                 }
             } else if (IS_LOCK_LOST(fileLock) &&
-                      (fileLock->key == key || fileLock->lockType == LockWrite)) {
+                       (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
                 code = CM_ERROR_BADFD;
                 break;
             }
@@ -3882,7 +4067,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,
@@ -3920,7 +4105,7 @@ long cm_LockCheckWrite(cm_scache_t *scp,
 
         if (INTERSECT_RANGE(range, fileLock->range)) {
             if (IS_LOCK_ACTIVE(fileLock)) {
-                if (fileLock->key == key) {
+                if (cm_KeyEquals(&fileLock->key, &key, 0)) {
                     if (fileLock->lockType == LockWrite) {
 
                         /* if there is an active lock for this client, it
@@ -3955,9 +4140,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;
@@ -3980,7 +4162,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) {
@@ -3988,8 +4170,9 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
     AFSFid tfid;
     cm_fid_t cfid;
     cm_conn_t * connp;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     AFSVolSync volSync;
+    afs_uint32 reqflags = reqp->flags;
 
     tfid.Volume = scp->fid.volume;
     tfid.Vnode = scp->fid.vnode;
@@ -3998,17 +4181,18 @@ 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);
         if (code) 
             break;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_SetLock(callp, &tfid, lockType,
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_SetLock(rxconnp, &tfid, lockType,
                              &volSync);
-        rx_PutConnection(callp);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
                         NULL, NULL, code));
@@ -4020,19 +4204,19 @@ 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;
     AFSFid tfid;
     cm_fid_t cfid;
     cm_conn_t * connp;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     AFSVolSync volSync;
 
     tfid.Volume = scp->fid.volume;
@@ -4040,7 +4224,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);
 
@@ -4049,9 +4233,9 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
         if (code) 
             break;
 
-        callp = cm_GetRxConn(connp);
-        code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
-        rx_PutConnection(callp);
+        rxconnp = cm_GetRxConn(connp);
+        code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
+        rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
                         NULL, NULL, code));
@@ -4063,12 +4247,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:
@@ -4154,7 +4338,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,
@@ -4171,8 +4355,8 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
 
     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));
+    osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait, 
+             key.process_id, key.session_id, key.file_id);
 
     /*
    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
@@ -4206,7 +4390,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
 
         if (IS_LOCK_LOST(fileLock)) {
-            if (fileLock->key == key) {
+            if (cm_KeyEquals(&fileLock->key, &key, 0)) {
                 code = CM_ERROR_BADFD;
                 break;
             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
@@ -4519,9 +4703,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
     return code;
 }
 
-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,
@@ -4533,11 +4715,9 @@ long cm_UnlockByKey(cm_scache_t * scp,
     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);
+    osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
+             scp, key.process_id, key.session_id, key.file_id);
+    osi_Log1(afsd_logp, "    flags=0x%x", flags);
 
     lock_ObtainWrite(&cm_scacheLock);
 
@@ -4553,9 +4733,8 @@ long cm_UnlockByKey(cm_scache_t * scp,
                  (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),
+        osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
+                 fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
                  fileLock->flags);
 
         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
@@ -4575,7 +4754,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
 #endif
 
         if (!IS_LOCK_DELETED(fileLock) &&
-            cm_KeyEquals(fileLock->key, key, flags)) {
+            cm_KeyEquals(&fileLock->key, &key, flags)) {
             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
                     fileLock->range.offset,
                     fileLock->range.length,
@@ -4712,6 +4891,7 @@ long cm_Unlock(cm_scache_t *scp,
                unsigned char sLockType,
                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
                cm_key_t key, 
+               afs_uint32 flags,
                cm_user_t *userp, 
                cm_req_t *reqp)
 {
@@ -4720,12 +4900,19 @@ long cm_Unlock(cm_scache_t *scp,
     cm_file_lock_t *fileLock;
     osi_queue_t *q;
     int release_userp = FALSE;
+    int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
+    int lock_found  = 0;
+    LARGE_INTEGER RangeEnd;
 
     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));
+    osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
+             key.process_id, key.session_id, key.file_id, flags);
 
+    if (!exact_match)
+        RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
+
+  try_again:
     lock_ObtainRead(&cm_scacheLock);
 
     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
@@ -4748,27 +4935,43 @@ long cm_Unlock(cm_scache_t *scp,
             osi_assertx(FALSE, "invalid fid value");
         }
 #endif
-        if (!IS_LOCK_DELETED(fileLock) &&
-            fileLock->key == key &&
-            fileLock->range.offset == LOffset.QuadPart &&
-            fileLock->range.length == LLength.QuadPart) {
-            break;
+        if (exact_match) {
+            if (!IS_LOCK_DELETED(fileLock) &&
+                 cm_KeyEquals(&fileLock->key, &key, 0) &&
+                 fileLock->range.offset == LOffset.QuadPart &&
+                 fileLock->range.length == LLength.QuadPart) {
+                lock_found = 1;
+                break;
+            }
+        } else {
+
+            if (!IS_LOCK_DELETED(fileLock) &&
+                 cm_KeyEquals(&fileLock->key, &key, 0) &&
+                 fileLock->range.offset >= LOffset.QuadPart &&
+                 fileLock->range.offset < RangeEnd.QuadPart &&
+                 (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
+                lock_found = 1;
+                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 CM_ERROR_RANGE_NOT_LOCKED;
+        if (lock_found && !exact_match) {
+            code = 0;
+            goto done;
+        } else {
+            osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
+        
+            /* The lock didn't exist anyway. *shrug* */
+            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);
@@ -4889,8 +5092,13 @@ long cm_Unlock(cm_scache_t *scp,
         }
     }
 
-    if (release_userp)
+    if (release_userp) {
         cm_ReleaseUser(userp);
+        release_userp = FALSE;
+    }
+
+    if (!exact_match)
+        goto try_again;         /* might be more than one lock in the range */
 
  done:
 
@@ -4902,19 +5110,14 @@ 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;
 
     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_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
-#endif
-
     /* cm_scacheLock needed because we are modifying fileLock->flags */
     lock_ObtainWrite(&cm_scacheLock);
 
@@ -4934,7 +5137,7 @@ static void cm_LockMarkSCacheLost(cm_scache_t * scp)
     }
 
     scp->serverLock = -1;
-    scp->lockDataVersion = -1;
+    scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
     lock_ReleaseWrite(&cm_scacheLock);
 }
 
@@ -4948,7 +5151,7 @@ void cm_CheckLocks()
     AFSVolSync volSync;
     cm_conn_t *connp;
     long code;
-    struct rx_connection * callp;
+    struct rx_connection * rxconnp;
     cm_scache_t * scp;
 
     cm_InitReq(&req);
@@ -5005,7 +5208,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))
@@ -5017,17 +5220,17 @@ void cm_CheckLocks()
                                  | CM_SCACHESYNC_LOCK);
 
                 if (code) {
-                    osi_Log1(smb_logp,
+                    osi_Log1(afsd_logp,
                              "cm_CheckLocks SyncOp failure code 0x%x", code);
                     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;
 
@@ -5042,7 +5245,7 @@ void cm_CheckLocks()
                              scp,
                              (int) scp->serverLock);
 
-                    lock_ReleaseMutex(&scp->mx);
+                    lock_ReleaseWrite(&scp->rw);
 
                     do {
                         code = cm_ConnFromFID(&cfid, userp,
@@ -5050,10 +5253,10 @@ void cm_CheckLocks()
                         if (code) 
                             break;
 
-                        callp = cm_GetRxConn(connp);
-                        code = RXAFS_ExtendLock(callp, &tfid,
+                        rxconnp = cm_GetRxConn(connp);
+                        code = RXAFS_ExtendLock(rxconnp, &tfid,
                                                 &volSync);
-                        rx_PutConnection(callp);
+                        rx_PutConnection(rxconnp);
 
                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
 
@@ -5063,7 +5266,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);
@@ -5104,7 +5307,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);
                     }
 
@@ -5122,7 +5326,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);
 
@@ -5149,7 +5353,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;
@@ -5177,9 +5381,8 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
              (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),
+    osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
+             oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
              (unsigned)(oldFileLock->flags));
 
     /* if the lock has already been granted, then we have nothing to do */
@@ -5202,7 +5405,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,
@@ -5214,7 +5417,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;
     }
 
@@ -5237,7 +5440,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;
     }
@@ -5251,7 +5454,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
                 ((char *) q - offsetof(cm_file_lock_t, fileq));
 
             if (IS_LOCK_LOST(fileLock)) {
-                if (fileLock->key == oldFileLock->key) {
+                if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
                     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",
@@ -5281,7 +5484,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;
     }
@@ -5330,7 +5533,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;
 
@@ -5342,7 +5545,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
                         | CM_SCACHESYNC_GETSTATUS
                         | CM_SCACHESYNC_LOCK);
         if (code) {
-            osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
+            osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
             lock_ReleaseWrite(&cm_scacheLock);
             goto post_syncopdone;
         }
@@ -5433,7 +5636,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);
@@ -5453,27 +5656,21 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
     return code;
 }
 
-cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
+cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
 {
-#ifdef DEBUG
-    osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
-    osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
-    osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
-#endif
+    cm_key_t key;
 
-    return 
-        (((cm_key_t) (process_id & 0xffffffff)) << 32) |
-        (((cm_key_t) (session_id & 0xffff)) << 16) |
-        (((cm_key_t) (file_id & 0xffff)));
+    key.process_id = process_id;
+    key.session_id = session_id;
+    key.file_id = file_id;
+
+    return key;
 }
 
-static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
+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);
-    }
+    return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
+        ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
 }
 
 void cm_ReleaseAllLocks(void)
@@ -5489,11 +5686,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));
@@ -5505,7 +5702,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);
            }
        }
     }