windows-buf-consistency-20080217
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index 9103162..3d6c771 100644 (file)
@@ -21,6 +21,7 @@
 #include <osi.h>
 
 #include "afsd.h"
+#include "cm_btree.h"
 
 #ifdef DEBUG
 extern void afsi_log(char *pattern, ...);
@@ -28,6 +29,8 @@ extern void afsi_log(char *pattern, ...);
 
 int cm_enableServerLocks = 1;
 
+int cm_followBackupPath = 0;
+
 /*
  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
  * I do not know anything more about it.
@@ -334,7 +337,7 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
     long rights;
     long code;
 
-    osi_assert(ldpp != NULL);
+    osi_assertx(ldpp != NULL, "null cm_lock_data_t");
     *ldpp = NULL;
 
     /* Always allow delete; the RPC will tell us if it's OK */
@@ -471,7 +474,7 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
     long code;
     osi_hyper_t thyper;
     cm_buf_t *bufferp;
-    cm_dirEntry_t *dep;
+    cm_dirEntry_t *dep = 0;
     unsigned short *hashTable;
     unsigned int i, idx;
     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
@@ -570,7 +573,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
 {
     char *tp;
     long code;
-    cm_dirEntry_t *dep;
+    cm_dirEntry_t *dep = 0;
     cm_buf_t *bufferp;
     long temp;
     osi_hyper_t dirLength;
@@ -588,15 +591,12 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
     lock_ObtainMutex(&scp->mx);
     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-    if (code) {
-        lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseMutex(&scp->mx);
+    if (code)
         return code;
-    }
         
-    if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
-        lock_ReleaseMutex(&scp->mx);
+    if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
         return CM_ERROR_NOTDIR;
-    }   
 
     if (retscp)                        /* if this is a lookup call */
     {
@@ -613,28 +613,39 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
 #else /* !AFS_FREELANCE_CLIENT */
             TRUE
 #endif
-            ) {
-        int casefold = sp->caseFold;
-        sp->caseFold = 0; /* we have a strong preference for exact matches */
-        if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
+            ) 
         {
+            int casefold = sp->caseFold;
+            sp->caseFold = 0; /* we have a strong preference for exact matches */
+            if ( *retscp = cm_dnlcLookup(scp, sp))     /* dnlc hit */
+            {
+                sp->caseFold = casefold;
+                return 0;
+            }
             sp->caseFold = casefold;
-            lock_ReleaseMutex(&scp->mx);
-            return 0;
-        }
-        sp->caseFold = casefold;
 
             /* see if we can find it using the directory hash tables.
                we can only do exact matches, since the hash is case
                sensitive. */
             {
                 cm_dirOp_t dirop;
+#ifdef USE_BPLUS
+                int usedBplus = 0;
+#endif
 
                 code = ENOENT;
 
-                code = cm_BeginDirOp(scp, userp, reqp, &dirop);
+                code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
                 if (code == 0) {
-                    code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
+
+#ifdef USE_BPLUS
+                    code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
+                    if (code != EINVAL)
+                        usedBplus = 1;
+                    else 
+#endif
+                        code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
+
                     cm_EndDirOp(&dirop);
                 }
 
@@ -642,14 +653,24 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                     /* found it */
                     sp->found = TRUE;
                     sp->ExactFound = TRUE;
-                    lock_ReleaseMutex(&scp->mx);
-
                     *retscp = NULL; /* force caller to call cm_GetSCache() */
-
                     return 0;
                 }
+#ifdef USE_BPLUS
+                if (usedBplus) {
+                    if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
+                        /* found it */
+                        sp->found = TRUE;
+                        sp->ExactFound = FALSE;
+                        *retscp = NULL; /* force caller to call cm_GetSCache() */
+                        return 0;
+                    }
+                    
+                    return CM_ERROR_BPLUS_NOMATCH;
+                }
+#endif 
             }
-    }
+        }
     }  
 
     /*
@@ -658,8 +679,6 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
      */
     dirLength = scp->length;
 
-    lock_ReleaseMutex(&scp->mx);
-
     bufferp = NULL;
     bufferOffset.LowPart = bufferOffset.HighPart = 0;
     if (startOffsetp)
@@ -1031,7 +1050,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     char mtType;
     cm_fid_t tfid;
     size_t vnLength;
-    int type;
+    int targetType;
 
     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
         tfid = scp->mountRootFid;
@@ -1057,13 +1076,15 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
         strcpy(volNamep, cp+1);
         /* now look up the cell */
+        lock_ReleaseMutex(&scp->mx);
         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
+        lock_ObtainMutex(&scp->mx);
     }
     else {
         /* normal mt pt */
         strcpy(volNamep, mpNamep+1);
 
-        cellp = cm_FindCellByID(scp->fid.cell);
+        cellp = cm_FindCellByID(scp->fid.cell, 0);
     }
 
     if (!cellp) {
@@ -1073,15 +1094,15 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
     vnLength = strlen(volNamep);
     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
-        type = BACKVOL;
+        targetType = BACKVOL;
     else if (vnLength >= 10
               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
-        type = ROVOL;
+        targetType = ROVOL;
     else
-        type = RWVOL;
+        targetType = RWVOL;
 
     /* check for backups within backups */
-    if (type == BACKVOL
+    if (targetType == BACKVOL
          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
          == CM_SCACHEFLAG_RO) {
         code = CM_ERROR_NOSUCHVOLUME;
@@ -1111,17 +1132,32 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
         lock_ReleaseMutex(&volp->mx);
 
         scp->mountRootFid.cell = cellp->cellID;
+        
+        /* if the mt pt originates in a .backup volume (not a .readonly)
+         * and FollowBackupPath is active, and if there is a .backup
+         * volume for the target, then use the .backup of the target
+         * instead of the read-write.
+         */
+        if (cm_followBackupPath && 
+            volp->bk.ID != 0 &&
+            (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
+            (targetType == RWVOL || targetType == ROVOL && volp->ro.ID == 0)
+            ) {
+            targetType = BACKVOL;
+        } 
         /* if the mt pt is in a read-only volume (not just a
          * backup), and if there is a read-only volume for the
-         * target, and if this is a type '#' mount point, use
+         * target, and if this is a targetType '#' mount point, use
          * the read-only, otherwise use the one specified.
          */
-        if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
-             && volp->ro.ID != 0 && type == RWVOL)
-            type = ROVOL;
-        if (type == ROVOL)
+        else if (mtType == '#' && targetType == RWVOL && 
+                 (scp->flags & CM_SCACHEFLAG_PURERO) && 
+                 volp->ro.ID != 0) {
+            targetType = ROVOL;
+        }
+        if (targetType == ROVOL)
             scp->mountRootFid.volume = volp->ro.ID;
-        else if (type == BACKVOL)
+        else if (targetType == BACKVOL)
             scp->mountRootFid.volume = volp->bk.ID;
         else
             scp->mountRootFid.volume = volp->rw.ID;
@@ -1174,22 +1210,40 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
            not. */
 
         cm_dirOp_t dirop;
+#ifdef USE_BPLUS
+        int usedBplus = 0;
+#endif
 
-        lock_ObtainMutex(&dscp->mx);
-
-        code = cm_BeginDirOp(dscp, userp, reqp, &dirop);
+        code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
         if (code == 0) {
-            code = cm_DirLookup(&dirop, namep, &rock.fid);
+#ifdef USE_BPLUS
+            code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
+            if (code != EINVAL)
+                usedBplus = 1;
+            else
+#endif
+                code = cm_DirLookup(&dirop, namep, &rock.fid);
+
             cm_EndDirOp(&dirop);
         }
 
-        lock_ReleaseMutex(&dscp->mx);
-
         if (code == 0) {
             /* found it */
             rock.found = TRUE;
             goto haveFid;
         }
+#ifdef USE_BPLUS
+        if (usedBplus) {
+            if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
+                /* found it */
+                code = 0;
+                rock.found = TRUE;
+                goto haveFid;
+            }
+            
+            return CM_ERROR_BPLUS_NOMATCH;
+        }
+#endif
     }
 
     rock.fid.cell = dscp->fid.cell;
@@ -1228,27 +1282,43 @@ long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *us
             else
                 return CM_ERROR_NOSUCHFILE;
         }
-        else {  /* nonexistent dir on freelance root, so add it */
+        else if (!strchr(namep, '#') && !strchr(namep, '%') &&
+                 strcmp(namep, "srvsvc") && strcmp(namep, "wkssvc") &&
+                 strcmp(namep, "ipc$")) {
+            /* nonexistent dir on freelance root, so add it */
             char fullname[200] = ".";
             int  found = 0;
 
             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
                       osi_LogSaveString(afsd_logp,namep));
+
+            /* 
+             * There is an ugly behavior where a share name "foo" will be searched
+             * for as "fo".  If the searched for name differs by an already existing
+             * symlink or mount point in the Freelance directory, do not add the 
+             * new value automatically.
+             */
+
+            code = -1;
             if (namep[0] == '.') {
                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
                     found = 1;
-                    if ( stricmp(&namep[1], &fullname[1]) )
+                    if (!cm_FreelanceMountPointExists(fullname, 0))
+                        code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
+                    if ( stricmp(&namep[1], &fullname[1]) && 
+                                               !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                                               !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
-                    else
-                        code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
                 }
             } else {
                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
                     found = 1;
-                    if ( stricmp(namep, fullname) )
+                    if (!cm_FreelanceMountPointExists(fullname, 0))
+                        code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
+                    if ( stricmp(namep, fullname) && 
+                                               !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+                                               !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
-                    else
-                        code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
                 }
             }
             if (!found || code < 0) {   /* add mount point failed, so give up */
@@ -1463,7 +1533,7 @@ long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
 #endif
 {
     long code;
-    char tname[256];
+    char tname[AFSPATHMAX];
     int sysNameIndex = 0;
     cm_scache_t *scp = NULL;
 
@@ -1549,10 +1619,9 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
 #endif  
 
     /* make sure we don't screw up the dir status during the merge */
-    lock_ObtainMutex(&dscp->mx);
-
-    code = cm_BeginDirOp(dscp, userp, reqp, &dirop);
+    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
 
+    lock_ObtainMutex(&dscp->mx);
     sflags = CM_SCACHESYNC_STOREDATA;
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
     lock_ReleaseMutex(&dscp->mx);
@@ -1585,14 +1654,15 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
     else
         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_dnlcRemove(dscp, namep);
     cm_SyncOpDone(dscp, NULL, sflags);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, 0);
-        if (cm_CheckDirOpForSingleChange(&dirop)) {
-            cm_DirDeleteEntry(&dirop, namep);
-        }
+        cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     } else if (code == CM_ERROR_NOSUCHFILE) {
        /* windows would not have allowed the request to delete the file 
         * if it did not believe the file existed.  therefore, we must 
@@ -1600,9 +1670,16 @@ long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
         */
        dscp->cbServerp = NULL;
     }
-    cm_EndDirOp(&dirop);
     lock_ReleaseMutex(&dscp->mx);
 
+    if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
+        cm_DirDeleteEntry(&dirop, namep);
+#ifdef USE_BPLUS
+        cm_BPlusDirDeleteEntry(&dirop, namep);
+#endif
+    }
+    cm_EndDirOp(&dirop);
+
     return code;
 }
 
@@ -1621,7 +1698,9 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
         /* read the link data */
         lock_ReleaseMutex(&linkScp->mx);
         thyper.LowPart = thyper.HighPart = 0;
+        lock_ObtainRead(&linkScp->bufCreateLock);
         code = buf_Get(linkScp, &thyper, &bufp);
+        lock_ReleaseRead(&linkScp->bufCreateLock);
         lock_ObtainMutex(&linkScp->mx);
         if (code) 
             return code;
@@ -1658,6 +1737,9 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
         if (!linkScp->mountPointStringp[0]) {
             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
             linkScp->mountPointStringp[temp] = 0;      /* null terminate */
+
+            if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
+                 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
         }
         buf_Release(bufp);
     }  /* don't have sym link contents cached */
@@ -1680,9 +1762,12 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
     char *linkp;
     cm_space_t *tsp;
 
+    *newRootScpp = NULL;
+    *newSpaceBufferp = NULL;
+
     lock_ObtainMutex(&linkScp->mx);
     code = cm_HandleLink(linkScp, userp, reqp);
-    if (code) 
+    if (code)
         goto done;
 
     /* if we may overflow the buffer, bail out; buffer is signficantly
@@ -1719,13 +1804,12 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
         } else {
             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
             strcpy(tsp->data, linkp);
-            *newRootScpp = NULL;
             code = CM_ERROR_PATH_NOT_COVERED;
         }
-    } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
+    } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
+                !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
         strcpy(tsp->data, linkp);
-        *newRootScpp = NULL;
         code = CM_ERROR_PATH_NOT_COVERED;
     } else if (*linkp == '\\' || *linkp == '/') {
 #if 0   
@@ -1741,19 +1825,24 @@ long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
          */
         linkScp->fileType = CM_SCACHETYPE_INVALID;
         strcpy(tsp->data, linkp);
-        *newRootScpp = NULL;
         code = CM_ERROR_NOSUCHPATH;
 #endif  
     } else {
         /* a relative link */
         strcpy(tsp->data, linkp);
-        *newRootScpp = NULL;
     }
     if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
         strcat(tsp->data, "\\");
         strcat(tsp->data, pathSuffixp);
     }
-    *newSpaceBufferp = tsp;
+    if (code == 0)
+        *newSpaceBufferp = tsp;
+    else {
+        cm_FreeSpace(tsp);
+
+        if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
+            cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
+    }
 
   done:
     lock_ReleaseMutex(&linkScp->mx);
@@ -1772,7 +1861,7 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
     char *tp;                  /* ptr moving through input buffer */
     char tc;                   /* temp char */
     int haveComponent;         /* has new component started? */
-    char component[256];       /* this is the new component */
+    char component[AFSPATHMAX];        /* this is the new component */
     char *cp;                  /* component name being assembled */
     cm_scache_t *tscp;         /* current location in the hierarchy */
     cm_scache_t *nscp;         /* next dude down */
@@ -1855,33 +1944,51 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                                  userp, reqp, &nscp);
 
                 if (code == 0) {
-                    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 (!strcmp(component,"..") || !strcmp(component,".")) {
+                        /* 
+                         * 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]))
+                                break;
+                        }
+                        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 (i == fid_count && fid_count < MAX_FID_COUNT) {
-                        fids[fid_count++] = nscp->fid;
                     }
                 }
-                
+
                 if (code) {
                    cm_ReleaseSCache(tscp);
                    if (dirScp)
                        cm_ReleaseSCache(dirScp);
                    if (psp) 
                        cm_FreeSpace(psp);
-                   if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK) {
+                   if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
+                         tscp->fileType == CM_SCACHETYPE_SYMLINK) 
+                    {
                        osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
                        return CM_ERROR_NOSUCHPATH;
                    } else {
                        osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
                        return code;
                    }
-               }       
+               }
+
                haveComponent = 0;      /* component done */
                if (dirScp)
                    cm_ReleaseSCache(dirScp);
@@ -1938,12 +2045,16 @@ long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
 
                     if (code == 0 && linkScp != NULL) {
-                        for ( i=0; i<fid_count; i++) {
-                            if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
-                                code = CM_ERROR_TOO_MANY_SYMLINKS;
-                                cm_ReleaseSCache(linkScp);
-                                nscp = NULL;
-                                break;
+                        if (linkScp == cm_data.rootSCachep) 
+                            fid_count = 0;
+                        else {
+                            for ( i=0; i<fid_count; i++) {
+                                if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
+                                    code = CM_ERROR_TOO_MANY_SYMLINKS;
+                                    cm_ReleaseSCache(linkScp);
+                                    nscp = NULL;
+                                    break;
+                                }
                             }
                         }
                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
@@ -2075,7 +2186,7 @@ long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
                      userp, NULL, reqp, outScpp);
 
-    if (code == CM_ERROR_NOSUCHFILE)
+    if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
         code = CM_ERROR_NOSUCHPATH;
 
     /* this stuff is allocated no matter what happened on the namei call,
@@ -2219,7 +2330,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
 
     /* should be on a buffer boundary */
-    osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
+    osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
 
     memset(&bb, 0, sizeof(bb));
     bb.bufOffset = *offsetp;
@@ -2580,19 +2691,29 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         return CM_ERROR_ATSYS;
     }
 
+#ifdef AFS_FREELANCE_CLIENT
+    /* Freelance root volume does not hold files */
+    if (cm_freelanceEnabled &&
+        dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+    {
+        return CM_ERROR_NOACCESS;
+    }
+#endif /* AFS_FREELANCE_CLIENT */
+
     /* before starting the RPC, mark that we're changing the file data, so
      * that someone who does a chmod will know to wait until our call
      * completes.
      */
+    cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
     lock_ObtainMutex(&dscp->mx);
-    cm_BeginDirOp(dscp, userp, reqp, &dirop);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseMutex(&dscp->mx);
     if (code == 0) {
         cm_StartCallbackGrantingCall(NULL, &cbReq);
     } else {
         cm_EndDirOp(&dirop);
     }
-    lock_ReleaseMutex(&dscp->mx);
     if (code) {
         return code;
     }
@@ -2627,10 +2748,14 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     else
         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
     lock_ReleaseMutex(&dscp->mx);
 
@@ -2664,12 +2789,13 @@ long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     if (!didEnd)
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
 
-    lock_ObtainMutex(&dscp->mx);
     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
         cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+        cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
     }
     cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
 
     return code;
 }       
@@ -2708,7 +2834,7 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     cm_callbackRequest_t cbReq;
     AFSFid newAFSFid;
     cm_fid_t newFid;
-    cm_scache_t *scp;
+    cm_scache_t *scp = NULL;
     int didEnd;
     AFSStoreStatus inStatus;
     AFSFetchStatus updatedDirStatus;
@@ -2725,19 +2851,29 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
         return CM_ERROR_ATSYS;
     }
 
+#ifdef AFS_FREELANCE_CLIENT
+    /* Freelance root volume does not hold subdirectories */
+    if (cm_freelanceEnabled &&
+        dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+    {
+        return CM_ERROR_NOACCESS;
+    }
+#endif /* AFS_FREELANCE_CLIENT */
+
     /* before starting the RPC, mark that we're changing the directory
      * data, so that someone who does a chmod on the dir will wait until
      * our call completes.
      */
+    cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
     lock_ObtainMutex(&dscp->mx);
-    cm_BeginDirOp(dscp, userp, reqp, &dirop);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseMutex(&dscp->mx);
     if (code == 0) {
         cm_StartCallbackGrantingCall(NULL, &cbReq);
     } else {
         cm_EndDirOp(&dirop);
     }
-    lock_ReleaseMutex(&dscp->mx);
     if (code) {
         return code;
     }
@@ -2772,10 +2908,14 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     else
         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
     lock_ReleaseMutex(&dscp->mx);
 
@@ -2808,12 +2948,13 @@ long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
     if (!didEnd)
         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
 
-    lock_ObtainMutex(&dscp->mx);
     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
         cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+        cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
     }
     cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
 
     /* and return error code */
     return code;
@@ -2837,12 +2978,12 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
         return CM_ERROR_CROSSDEVLINK;
     }
 
+    cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
     lock_ObtainMutex(&dscp->mx);
-    cm_BeginDirOp(dscp, userp, reqp, &dirop);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseMutex(&dscp->mx);
     if (code != 0)
         cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
 
     if (code)
         return code;
@@ -2877,16 +3018,26 @@ long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
     else
         osi_Log0(afsd_logp, "CALL Link SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
+    }
+    lock_ReleaseMutex(&dscp->mx);
+
+    if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
             cm_DirCreateEntry(&dirop, namep, &sscp->fid);
+#ifdef USE_BPLUS
+            cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
+#endif
         }
     }
     cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
 
     return code;
 }
@@ -2911,12 +3062,12 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
      * 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);
-    cm_BeginDirOp(dscp, userp, reqp, &dirop);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseMutex(&dscp->mx);
     if (code != 0)
         cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
     if (code) {
         return code;
     }
@@ -2949,10 +3100,18 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
     else
         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
+    }
+    lock_ReleaseMutex(&dscp->mx);
+
+    if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&dirop)) {
             newFid.cell = dscp->fid.cell;
             newFid.volume = dscp->fid.volume;
@@ -2960,10 +3119,12 @@ long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
             newFid.unique = newAFSFid.Unique;
 
             cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+            cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
         }
     }
     cm_EndDirOp(&dirop);
-    lock_ReleaseMutex(&dscp->mx);
 
     /* 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
@@ -3001,21 +3162,20 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
     AFSFetchStatus updatedDirStatus;
     AFSVolSync volSync;
     struct rx_connection * callp;
-    cm_dirOp_t dirOp;
+    cm_dirOp_t dirop;
 
     /* 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);
-    cm_BeginDirOp(dscp, userp, reqp, &dirOp);
     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+    lock_ReleaseMutex(&dscp->mx);
     if (code) {
-        cm_EndDirOp(&dirOp);
-        lock_ReleaseMutex(&dscp->mx);
+        cm_EndDirOp(&dirop);
         return code;
     }
-    lock_ReleaseMutex(&dscp->mx);
     didEnd = 0;
 
     /* try the RPC now */
@@ -3043,18 +3203,28 @@ long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
     else
         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
 
+    if (dirop.scp) {
+        lock_ObtainWrite(&dirop.scp->dirlock);
+        dirop.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&dscp->mx);
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     if (code == 0) {
         cm_dnlcRemove(dscp, namep); 
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, 0);
-        if (cm_CheckDirOpForSingleChange(&dirOp)) {
-            cm_DirDeleteEntry(&dirOp, namep);
-    }
+        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
     }
-    cm_EndDirOp(&dirOp);
     lock_ReleaseMutex(&dscp->mx);
 
+    if (code == 0) {
+        if (cm_CheckDirOpForSingleChange(&dirop)) {
+            cm_DirDeleteEntry(&dirop, namep);
+#ifdef USE_BPLUS
+            cm_BPlusDirDeleteEntry(&dirop, namep);
+#endif
+        }
+    }
+    cm_EndDirOp(&dirop);
+
     /* and return error code */
     return code;
 }
@@ -3106,16 +3276,16 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
             return CM_ERROR_RENAME_IDENTICAL;
 
         oneDir = 1;
+        cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
         lock_ObtainMutex(&oldDscp->mx);
         cm_dnlcRemove(oldDscp, oldNamep);
         cm_dnlcRemove(oldDscp, newNamep);
-        cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                           CM_SCACHESYNC_STOREDATA);
+        lock_ReleaseMutex(&oldDscp->mx);
         if (code != 0) {
             cm_EndDirOp(&oldDirOp);
         }
-        lock_ReleaseMutex(&oldDscp->mx);
     }
     else {
         /* two distinct dir vnodes */
@@ -3132,59 +3302,59 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
             return CM_ERROR_CROSSDEVLINK;
 
         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
+            cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
             lock_ObtainMutex(&oldDscp->mx);
-            cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
             cm_dnlcRemove(oldDscp, oldNamep);
             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                               CM_SCACHESYNC_STOREDATA);
+            lock_ReleaseMutex(&oldDscp->mx);
             if (code != 0)
                 cm_EndDirOp(&oldDirOp);
-            lock_ReleaseMutex(&oldDscp->mx);
             if (code == 0) {
+                cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
                 lock_ObtainMutex(&newDscp->mx);
-                cm_BeginDirOp(newDscp, userp, reqp, &newDirOp);
                 cm_dnlcRemove(newDscp, newNamep);
                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
                                   CM_SCACHESYNC_STOREDATA);
-                if (code != 0)
-                    cm_EndDirOp(&newDirOp);
                 lock_ReleaseMutex(&newDscp->mx);
                 if (code) {
+                    cm_EndDirOp(&newDirOp);
+
                     /* cleanup first one */
                     lock_ObtainMutex(&oldDscp->mx);
                     cm_SyncOpDone(oldDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
-                    cm_EndDirOp(&oldDirOp);
                     lock_ReleaseMutex(&oldDscp->mx);
+                    cm_EndDirOp(&oldDirOp);
                 }       
             }
         }
         else {
             /* lock the new vnode entry first */
+            cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
             lock_ObtainMutex(&newDscp->mx);
-            cm_BeginDirOp(newDscp, userp, reqp, &newDirOp);
             cm_dnlcRemove(newDscp, newNamep);
             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
                               CM_SCACHESYNC_STOREDATA);
+            lock_ReleaseMutex(&newDscp->mx);
             if (code != 0)
                 cm_EndDirOp(&newDirOp);
-            lock_ReleaseMutex(&newDscp->mx);
             if (code == 0) {
+                cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
                 lock_ObtainMutex(&oldDscp->mx);
-                cm_BeginDirOp(oldDscp, userp, reqp, &oldDirOp);
                 cm_dnlcRemove(oldDscp, oldNamep);
                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
                                   CM_SCACHESYNC_STOREDATA);
+                lock_ReleaseMutex(&oldDscp->mx);
                 if (code != 0)
                     cm_EndDirOp(&oldDirOp);
-                lock_ReleaseMutex(&oldDscp->mx);
                 if (code) {
                     /* cleanup first one */
                     lock_ObtainMutex(&newDscp->mx);
                     cm_SyncOpDone(newDscp, NULL,
                                    CM_SCACHESYNC_STOREDATA);
-                    cm_EndDirOp(&newDirOp);
                     lock_ReleaseMutex(&newDscp->mx);
+                    cm_EndDirOp(&newDirOp);
                 }       
             }
         }
@@ -3227,48 +3397,73 @@ long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
 
     /* update the individual stat cache entries for the directories */
+    if (oldDirOp.scp) {
+        lock_ObtainWrite(&oldDirOp.scp->dirlock);
+        oldDirOp.lockType = CM_DIRLOCK_WRITE;
+    }
     lock_ObtainMutex(&oldDscp->mx);
     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
 
-    if (code == 0) {
+    if (code == 0)
         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
-                        userp, 0);
+                        userp, CM_MERGEFLAG_DIROP);
+    lock_ReleaseMutex(&oldDscp->mx);
 
+    if (code == 0) {
         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
 
-            diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
+#ifdef USE_BPLUS
+            diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
+            if (diropCode == CM_ERROR_INEXACT_MATCH)
+                diropCode = 0;
+            else if (diropCode == EINVAL)
+#endif
+                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);
+#endif
                 }
 
-                if (diropCode == 0) {
+                if (diropCode == 0) { 
                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
-    }
+#ifdef USE_BPLUS
+                    cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
+#endif
+                }
             }
         }
     }
     cm_EndDirOp(&oldDirOp);
-    lock_ReleaseMutex(&oldDscp->mx);
 
     /* and update it for the new one, too, if necessary */
     if (!oneDir) {
+        if (newDirOp.scp) {
+            lock_ObtainWrite(&newDirOp.scp->dirlock);
+            newDirOp.lockType = CM_DIRLOCK_WRITE;
+        }
         lock_ObtainMutex(&newDscp->mx);
         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
-        if (code == 0) {
+        if (code == 0)
             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
-                            userp, 0);
+                            userp, CM_MERGEFLAG_DIROP);
+        lock_ReleaseMutex(&newDscp->mx);
 
+        if (code == 0) {
             /* we only make the local change if we successfully made
                the change in the old directory AND there was only one
                change in the new directory */
             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
+#ifdef USE_BPLUS
+                cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
+#endif
             }
         }
         cm_EndDirOp(&newDirOp);
-        lock_ReleaseMutex(&newDscp->mx);
     }
 
     /* and return error code */
@@ -3774,7 +3969,7 @@ static cm_file_lock_t * cm_GetFileLock(void) {
         osi_QRemove(&cm_freeFileLocks, &l->q);
     } else {
         l = malloc(sizeof(cm_file_lock_t));
-        osi_assert(l);
+        osi_assertx(l, "null cm_file_lock_t");
     }
 
     memset(l, 0, sizeof(cm_file_lock_t));
@@ -3917,7 +4112,7 @@ long cm_LockCheckPerms(cm_scache_t * scp,
         rights |= PRSFS_WRITE | PRSFS_LOCK;
     else {
         /* hmmkay */
-        osi_assert(FALSE);
+        osi_assertx(FALSE, "invalid lock type");
         return 0;
     }
 
@@ -4126,7 +4321,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                 osi_Log0(afsd_logp,
                          "   attempting to UPGRADE from LockRead to LockWrite.");
                 osi_Log1(afsd_logp,
-                         "   dataVersion on scp: %d", scp->dataVersion);
+                         "   dataVersion on scp: %I64d", scp->dataVersion);
 
                 /* we assume at this point (because scp->serverLock
                    was valid) that we had a valid server lock. */
@@ -4161,7 +4356,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                 newLock = Which;
 
                 /* am I sane? */
-                osi_assert(newLock == LockRead);
+                osi_assertx(newLock == LockRead, "lock type not read");
 
                 code = cm_IntSetLock(scp, userp, newLock, reqp);
             }
@@ -4217,7 +4412,7 @@ long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                 osi_Log0(afsd_logp,
                          "  Data version mismatch while upgrading lock.");
                 osi_Log2(afsd_logp,
-                         "  Data versions before=%d, after=%d",
+                         "  Data versions before=%I64d, after=%I64d",
                          scp->lockDataVersion,
                          scp->dataVersion);
                 osi_Log1(afsd_logp,
@@ -4377,7 +4572,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
                      fileLock->scp->fid.volume,
                      fileLock->scp->fid.vnode,
                      fileLock->scp->fid.unique);
-            osi_assert(FALSE);
+            osi_assertx(FALSE, "invalid fid value");
         }
 #endif
 
@@ -4451,7 +4646,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
         /* since scp->serverLock looked sane, we are going to assume
            that we have a valid server lock. */
         scp->lockDataVersion = scp->dataVersion;
-        osi_Log1(afsd_logp, "  dataVersion on scp = %d", scp->dataVersion);
+        osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
 
         code = cm_IntReleaseLock(scp, userp, reqp);
 
@@ -4473,7 +4668,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
                we have lost the lock we had during the transition. */
 
             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
-            osi_Log2(afsd_logp, "  Data versions before=%d, after=%d",
+            osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
                      scp->lockDataVersion,
                      scp->dataVersion);
             
@@ -4552,7 +4747,7 @@ long cm_Unlock(cm_scache_t *scp,
                      fileLock->scp->fid.volume,
                      fileLock->scp->fid.vnode,
                      fileLock->scp->fid.unique);
-            osi_assert(FALSE);
+            osi_assertx(FALSE, "invalid fid value");
         }
 #endif
         if (!IS_LOCK_DELETED(fileLock) &&
@@ -4569,7 +4764,7 @@ long cm_Unlock(cm_scache_t *scp,
         lock_ReleaseRead(&cm_scacheLock);
 
         /* The lock didn't exist anyway. *shrug* */
-        return 0;
+        return CM_ERROR_RANGE_NOT_LOCKED;
     }
 
     lock_ReleaseRead(&cm_scacheLock);
@@ -4628,7 +4823,7 @@ long cm_Unlock(cm_scache_t *scp,
         /* Since we already had a lock, we assume that there is a
            valid server lock. */
         scp->lockDataVersion = scp->dataVersion;
-        osi_Log1(afsd_logp, "   dataVersion on scp is %d", scp->dataVersion);
+        osi_Log1(afsd_logp, "   dataVersion on scp is %I64d", scp->dataVersion);
 
         /* before we downgrade, make sure that we have enough
            permissions to get the read lock. */
@@ -4663,7 +4858,7 @@ long cm_Unlock(cm_scache_t *scp,
             osi_Log0(afsd_logp,
                      "Data version mismatch while downgrading lock");
             osi_Log2(afsd_logp,
-                     "  Data versions before=%d, after=%d",
+                     "  Data versions before=%I64d, after=%I64d",
                      scp->lockDataVersion,
                      scp->dataVersion);
             
@@ -4719,7 +4914,7 @@ static void cm_LockMarkSCacheLost(cm_scache_t * scp)
 
 #ifdef DEBUG
     /* With the current code, we can't lose a lock on a RO scp */
-    osi_assert(!(scp->flags & CM_SCACHEFLAG_RO));
+    osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
 #endif
 
     /* cm_scacheLock needed because we are modifying fileLock->flags */
@@ -4780,10 +4975,10 @@ void cm_CheckLocks()
 
             /* Server locks must have been enabled for us to have
                received an active non-client-only lock. */
-            osi_assert(cm_enableServerLocks);
+            osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
 
             scp = fileLock->scp;
-            osi_assert(scp != NULL);
+            osi_assertx(scp != NULL, "null cm_scache_t");
 
             cm_HoldSCacheNoLock(scp);
 
@@ -4800,7 +4995,7 @@ void cm_CheckLocks()
                          fileLock->scp->fid.volume,
                          fileLock->scp->fid.vnode,
                          fileLock->scp->fid.unique);
-                osi_assert(FALSE);
+                osi_assertx(FALSE, "invalid fid");
             }
 #endif
             /* Server locks are extended once per scp per refresh
@@ -4901,7 +5096,7 @@ void cm_CheckLocks()
                                      "Data version mismatch on scp 0x%p",
                                      scp);
                             osi_Log2(afsd_logp,
-                                     "   Data versions: before=%d, after=%d",
+                                     "   Data versions: before=%I64d, after=%I64d",
                                      scp->lockDataVersion,
                                      scp->dataVersion);
 
@@ -5006,7 +5201,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 
     scp = oldFileLock->scp;
 
-    osi_assert(scp != NULL);
+    osi_assertx(scp != NULL, "null cm_scache_t");
 
     lock_ReleaseRead(&cm_scacheLock);
     lock_ObtainMutex(&scp->mx);
@@ -5112,7 +5307,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
     }
 
-    osi_assert(IS_LOCK_WAITLOCK(oldFileLock));
+    osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
 
     if (force_client_lock ||
         !SERVERLOCKS_ENABLED(scp) ||
@@ -5184,7 +5379,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 
         if (scp->serverLock == LockRead) {
 
-            osi_assert(newLock == LockWrite);
+            osi_assertx(newLock == LockWrite, "!LockWrite");
 
             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
 
@@ -5208,7 +5403,7 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
                 osi_Log0(afsd_logp,
                          "  Data version mismatch while upgrading lock.");
                 osi_Log2(afsd_logp,
-                         "  Data versions before=%d, after=%d",
+                         "  Data versions before=%I64d, after=%I64d",
                          scp->lockDataVersion,
                          scp->dataVersion);
                 osi_Log1(afsd_logp,
@@ -5263,9 +5458,9 @@ long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
 {
 #ifdef DEBUG
-    osi_assert((process_id & 0xffffffff) == process_id);
-    osi_assert((session_id & 0xffff) == session_id);
-    osi_assert((file_id & 0xffff) == file_id);
+    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
 
     return