Windows: cm_BPlusDirEnumBulkStatNext index error
[openafs.git] / src / WINNT / afsd / cm_btree.c
index a12c4d6..b6a9f0e 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright 2007-2008 Secure Endpoints Inc.  
- * 
+ * Copyright 2007-2008 Secure Endpoints Inc.
+ *
  * All Rights Reserved.
  *
  * This software has been released under the terms of the IBM Public
  * Thanks to Jan Jannink for B+ tree algorithms.
  */
 
+#include <afsconfig.h>
+#include <afs/param.h>
+#include <roken.h>
+
 #include <windows.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -128,10 +132,10 @@ static DWORD TlsDataIndex;
 
 long cm_InitBPlusDir(void)
 {
-    if ((TlsKeyIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
+    if ((TlsKeyIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
         return 0;
 
-    if ((TlsDataIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
+    if ((TlsDataIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
         return 0;
 
     return 1;
@@ -193,10 +197,10 @@ void freeBtree(Tree *B)
 /* access key and data values for B+tree methods */
 /* pass values to getSlot(), descend...() */
 static keyT getfunkey(Tree *B) {
-    keyT *tlsKey; 
-    // Retrieve a data pointer for the current thread. 
-    tlsKey = (keyT *) TlsGetValue(TlsKeyIndex); 
+    keyT *tlsKey;
+
+    // Retrieve a data pointer for the current thread.
+    tlsKey = (keyT *) TlsGetValue(TlsKeyIndex);
     if (tlsKey == NULL) {
         if (GetLastError() != ERROR_SUCCESS)
             osi_panic("TlsGetValue failed", __FILE__, __LINE__);
@@ -208,10 +212,10 @@ static keyT getfunkey(Tree *B) {
 }
 
 static dataT getfundata(Tree *B) {
-    dataT *tlsData; 
-    // Retrieve a data pointer for the current thread. 
-    tlsData = (dataT *) TlsGetValue(TlsDataIndex); 
+    dataT *tlsData;
+
+    // Retrieve a data pointer for the current thread.
+    tlsData = (dataT *) TlsGetValue(TlsDataIndex);
     if (tlsData == NULL) {
         if (GetLastError() != ERROR_SUCCESS)
             osi_panic("TlsGetValue failed", __FILE__, __LINE__);
@@ -225,14 +229,14 @@ static dataT getfundata(Tree *B) {
 static void setfunkey(Tree *B, keyT theKey) {
     keyT *tlsKey;
 
-    tlsKey = (keyT *) TlsGetValue(TlsKeyIndex); 
+    tlsKey = (keyT *) TlsGetValue(TlsKeyIndex);
     if (tlsKey == NULL) {
         if (GetLastError() != ERROR_SUCCESS)
             osi_panic("TlsGetValue failed", __FILE__, __LINE__);
 
         tlsKey = malloc(sizeof(keyT));
-        
-        if (!TlsSetValue(TlsKeyIndex, tlsKey)) 
+
+        if (!TlsSetValue(TlsKeyIndex, tlsKey))
             osi_panic("TlsSetValue failed", __FILE__, __LINE__);
     }
 
@@ -242,14 +246,14 @@ static void setfunkey(Tree *B, keyT theKey) {
 static void setfundata(Tree *B, dataT theData) {
     dataT *tlsData;
 
-    tlsData = (dataT *) TlsGetValue(TlsDataIndex); 
+    tlsData = (dataT *) TlsGetValue(TlsDataIndex);
     if (tlsData == NULL) {
         if (GetLastError() != ERROR_SUCCESS)
             osi_panic("TlsGetValue failed", __FILE__, __LINE__);
 
         tlsData = malloc(sizeof(dataT));
-        
-        if (!TlsSetValue(TlsDataIndex, tlsData)) 
+
+        if (!TlsSetValue(TlsDataIndex, tlsData))
             osi_panic("TlsSetValue failed", __FILE__, __LINE__);
     }
 
@@ -267,7 +271,7 @@ Nptr bplus_Lookup(Tree *B, keyT key)
     Nptr       leafNode;
 
 #ifdef DEBUG_BTREE
-    StringCbPrintfA(B->message, sizeof(B->message), "LOOKUP:  key %s.\n", key.name);
+    StringCbPrintfA(B->message, sizeof(B->message), "LOOKUP:  key %S.\n", key.name);
     OutputDebugString(B->message);
 #endif
 
@@ -287,13 +291,13 @@ Nptr bplus_Lookup(Tree *B, keyT key)
         dataNode = getnode(leafNode, slot);
         data = getdatavalue(dataNode);
 
-        StringCbPrintfA(B->message, sizeof(B->message), "LOOKUP: %s found on page %d value (%d.%d.%d).\n",
+        StringCbPrintfA(B->message, sizeof(B->message), "LOOKUP: %S found on page %d value (%d.%d.%d).\n",
                  key.name,
-                 getnodenumber(B, leafNode), 
-                 data.fid.volume, 
+                 getnodenumber(B, leafNode),
+                 data.fid.volume,
                  data.fid.vnode,
                  data.fid.unique);
-    } else 
+    } else
         StringCbPrintfA(B->message, sizeof(B->message), "LOOKUP: not found!\n");
     OutputDebugString(B->message);
 #endif
@@ -356,7 +360,7 @@ static int findKey(Tree *B, Nptr curr, int lo, int hi)
 
 #ifdef DEBUG_BTREE
         if (findslot == BTERROR) {
-            StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) Bad key ordering on node %d (0x%p)\n", 
+            StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) Bad key ordering on node %d (0x%p)\n",
                     lo, hi, getnodenumber(B, curr), curr);
             osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         }
@@ -373,7 +377,7 @@ static int findKey(Tree *B, Nptr curr, int lo, int hi)
                 findslot = findKey(B, curr, mid + 1, hi);
             break;
         case BTERROR:
-            StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) Bad key ordering on node %d (0x%p)\n", 
+            StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) Bad key ordering on node %d (0x%p)\n",
                     lo, hi, getnodenumber(B, curr), curr);
             osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         }
@@ -381,7 +385,7 @@ static int findKey(Tree *B, Nptr curr, int lo, int hi)
 
     if (isleaf(curr) && findslot == 0)
     {
-        StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) findslot %d is invalid for leaf nodes, bad key ordering on node %d (0x%p)\n", 
+        StringCbPrintfA(B->message, sizeof(B->message), "FINDKEY: (lo %d hi %d) findslot %d is invalid for leaf nodes, bad key ordering on node %d (0x%p)\n",
                 lo, hi, findslot, getnodenumber(B, curr), curr);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
     }
@@ -402,8 +406,8 @@ static int bestMatch(Tree *B, Nptr curr, int slot)
             if (isleaf(curr))
                  findslot = BTLOWER;   /* not found in the tree */
             else
-                 findslot = 0;                         
-        } 
+                 findslot = 0;
+        }
         else if ((comp = comparekeys(B)(getfunkey(B), getkey(curr, slot - 1), 0)) >= 0) {
             findslot = slot - 1;
         } else if (comp < diff) {
@@ -416,9 +420,9 @@ static int bestMatch(Tree *B, Nptr curr, int slot)
         }
     } else {                   /* or check following slot */
         if (slot == numentries(curr)) {
-            if (isleaf(curr) && numentries(curr) == getfanout(B)) 
+            if (isleaf(curr) && numentries(curr) == getfanout(B))
                 findslot = BTUPPER;
-            else 
+            else
                 findslot = slot;
         } else if ((comp = comparekeys(B)(getfunkey(B), getkey(curr, slot + 1), 0)) < 0) {
             findslot = slot;
@@ -436,7 +440,7 @@ static int bestMatch(Tree *B, Nptr curr, int slot)
 
     if (findslot == BTERROR || isleaf(curr) && findslot == 0)
     {
-        StringCbPrintfA(B->message, sizeof(B->message), "BESTMATCH: node %d (0x%p) slot %d diff %d comp %d findslot %d\n", 
+        StringCbPrintfA(B->message, sizeof(B->message), "BESTMATCH: node %d (0x%p) slot %d diff %d comp %d findslot %d\n",
                 getnodenumber(B, curr), curr, slot, diff, comp, findslot);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
     }
@@ -455,7 +459,7 @@ void insert(Tree *B, keyT key, dataT data)
     Nptr newNode;
 
 #ifdef DEBUG_BTREE
-    StringCbPrintfA(B->message, sizeof(B->message), "INSERT:  key %s.\n", key.name);
+    StringCbPrintfA(B->message, sizeof(B->message), "INSERT:  key %S.\n", key.name);
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
 #endif
 
@@ -469,7 +473,7 @@ void insert(Tree *B, keyT key, dataT data)
 
 
 /*****************   recurse down and split back up   ******************/
-static Nptr 
+static Nptr
 descendSplit(Tree *B, Nptr curr)
 {
     Nptr       downNode = NONODE, sibling = NONODE;
@@ -522,7 +526,7 @@ descendSplit(Tree *B, Nptr curr)
 }
 
 /***************   determine location of inserted key   ****************/
-static void 
+static void
 insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
 {
     int split, i, j, k, x, y;
@@ -552,7 +556,7 @@ insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
         k = (slot >= split ? 1 : 0);
 
         /*
-         * Move entries from the top half of the current node to 
+         * Move entries from the top half of the current node to
          * to the sibling node.
          * The number of entries to move is dependent upon where
          * the new entry is going to be added in relationship to
@@ -563,7 +567,7 @@ insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
          *
          * If the node that is being split is an internal node (i != 0)
          * then we move one less entry due to the extra down pointer
-         * when the split slot is not equal to the insertion slot 
+         * when the split slot is not equal to the insertion slot
          */
         for (x = split + k + j * i, y = 1; x <= getfanout(B); x++, y++) {
             xferentry(currNode, x, sibling, y);        /* copy entries to sibling */
@@ -611,7 +615,7 @@ insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
 
             /* set key separating nodes */
             if (isleaf(sibling))
-                key = getkey(sibling, 1); 
+                key = getkey(sibling, 1);
             else {
                 Nptr leaf = getfirstnode(sibling);
                 while ( isinternal(leaf) )
@@ -626,7 +630,7 @@ insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
 }
 
 /************   place key into appropriate node & slot   ***************/
-static void 
+static void
 placeEntry(Tree *B, Nptr node, int slot, Nptr downPtr)
 {
     int x;
@@ -660,7 +664,7 @@ placeEntry(Tree *B, Nptr node, int slot, Nptr downPtr)
 
 
 /*****************   split full node and set flags   *******************/
-static Nptr 
+static Nptr
 split(Tree *B, Nptr node)
 {
     Nptr sibling;
@@ -733,7 +737,7 @@ void delete(Tree *B, keyT key)
     Nptr newNode;
 
 #ifdef DEBUG_BTREE
-    StringCbPrintfA(B->message, sizeof(B->message), "DELETE:  key %s.\n", key.name);
+    StringCbPrintfA(B->message, sizeof(B->message), "DELETE:  key %S.\n", key.name);
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
 #endif
 
@@ -751,7 +755,7 @@ void delete(Tree *B, keyT key)
 
 
 /**********************   remove old root node   ***********************/
-static void 
+static void
 collapseRoot(Tree *B, Nptr oldRoot, Nptr newRoot)
 {
 
@@ -771,7 +775,7 @@ collapseRoot(Tree *B, Nptr oldRoot, Nptr newRoot)
 
 
 /****************   recurse down and balance back up   *****************/
-static Nptr 
+static Nptr
 descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc, Nptr parent)
 {
     Nptr       newMe=NONODE, myLeft=NONODE, myRight=NONODE, lAnchor=NONODE, rAnchor=NONODE, newNode=NONODE;
@@ -830,7 +834,7 @@ descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc,
         }
         newMe = descendBalance(B, newNode, myLeft, myRight, lAnchor, rAnchor, curr);
     }
-    else if ((slot > 0) && !comparekeys(B)(getfunkey(B), getkey(curr, slot), 0)) 
+    else if ((slot > 0) && !comparekeys(B)(getfunkey(B), getkey(curr, slot), 0))
     {
         Nptr        next;
         int         exact = 0;
@@ -840,7 +844,7 @@ descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc,
         next = getdatanext(newNode);
 
         /*
-         * We only delete exact matches.  
+         * We only delete exact matches.
          */
         if (!comparekeys(B)(getfunkey(B), getdatakey(newNode), EXACT_MATCH)) {
             /* exact match, free the first entry */
@@ -849,7 +853,7 @@ descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc,
             if (next == NONODE) {
                 /* delete this key as there are no more data values */
                 newMe = newNode;
-            } else { 
+            } else {
                 /* otherwise, there are more and we leave the key in place */
                 setnode(curr, slot, next);
                 putFreeNode(B, newNode);
@@ -859,14 +863,14 @@ descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc,
                 setmergepath(B, NONODE);
             }
         } else if (next == NONODE) {
-            /* 
+            /*
              * we didn't find an exact match and there are no more
              * choices.  so we leave it alone and remove nothing.
              */
             newMe = NONODE;
             setmergepath(B, NONODE);
         } else {
-            /* The first data node doesn't match but there are other 
+            /* The first data node doesn't match but there are other
              * options.  So we must determine if any of the next nodes
              * are the one we are looking for.
              */
@@ -882,13 +886,13 @@ descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc,
                 prev = next;
                 next = getdatanext(next);
             }
-            
+
             /* do not delete the key */
             newMe = NONODE;
             setmergepath(B, NONODE);
         }
     }
-    else 
+    else
     {
         newMe = NONODE;                /* no deletion possible, key not found */
         setmergepath(B, NONODE);
@@ -999,7 +1003,7 @@ removeEntry(Tree *B, Nptr curr, int slot)
 
 
 /*******   merge a node pair & set emptied node up for removal   *******/
-static Nptr 
+static Nptr
 merge(Tree *B, Nptr left, Nptr right, Nptr anchor)
 {
     int        x, y, z;
@@ -1056,15 +1060,15 @@ merge(Tree *B, Nptr left, Nptr right, Nptr anchor)
 
 
 /******   shift entries in a node pair & adjust anchor key value   *****/
-static Nptr 
+static Nptr
 shift(Tree *B, Nptr left, Nptr right, Nptr anchor)
 {
     int        i, x, y, z;
 
 #ifdef DEBUG_BTREE
-    StringCbPrintfA(B->message, sizeof(B->message), "SHIFT:  left %d, right %d, anchor %d.\n", 
-             getnodenumber(B, left), 
-             getnodenumber(B, right), 
+    StringCbPrintfA(B->message, sizeof(B->message), "SHIFT:  left %d, right %d, anchor %d.\n",
+             getnodenumber(B, left),
+             getnodenumber(B, right),
              getnodenumber(B, anchor));
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
     showNode(B, "pre-shift anchor", anchor);
@@ -1073,7 +1077,7 @@ shift(Tree *B, Nptr left, Nptr right, Nptr anchor)
 #endif
 
     i = isinternal(left);
-    
+
     if (numentries(left) < numentries(right)) {        /* shift entries to left */
         y = (numentries(right) - numentries(left)) >> 1;
         x = numentries(left) + y;
@@ -1116,7 +1120,7 @@ shift(Tree *B, Nptr left, Nptr right, Nptr anchor)
 
         for (z = numentries(right); z > 0; z--)        /* adjust increased node */
             pushentry(right, z, y);
-        
+
         setfunkey(B, getkey(left, x));                 /* set new anchor key value */
         z = getSlot(B, anchor);
         if (z <= BTERROR)
@@ -1145,7 +1149,7 @@ shift(Tree *B, Nptr left, Nptr right, Nptr anchor)
             xferentry(left, x, right, y);              /* transfer entries over */
             clrentry(left, x);
         }
-    } 
+    }
 #ifdef DEBUG_BTREE
     else {
         DebugBreak();
@@ -1185,7 +1189,7 @@ _clrentry(Nptr node, int entry)
 }
 
 static void
-_pushentry(Nptr node, int entry, int offset) 
+_pushentry(Nptr node, int entry, int offset)
 {
     if (getkey(node,entry + offset).name != NULL)
         free(getkey(node,entry + offset).name);
@@ -1246,7 +1250,7 @@ _setentry(Nptr node, int entry, keyT key, Nptr downNode)
 \***********************************************************************/
 
 /*********************   Set up initial pool of free nodes   ***********/
-static void 
+static void
 initFreeNodePool(Tree *B, int quantity)
 {
     int        i;
@@ -1271,7 +1275,7 @@ initFreeNodePool(Tree *B, int quantity)
     }
     setnextnode(p, NONODE);            /* indicates end of free node list */
     setallnode(p, NONODE);              /* indicates end of all node list */
-    
+
     setpoolsize(B, quantity);
 }
 
@@ -1310,7 +1314,7 @@ cleanupNodePool(Tree *B)
 }
 
 /**************   take a free B+tree node from the pool   **************/
-static Nptr 
+static Nptr
 getFreeNode(Tree *B)
 {
     Nptr newNode = getfirstfreenode(B);
@@ -1335,7 +1339,7 @@ getFreeNode(Tree *B)
 
 
 /*************   return a deleted B+tree node to the pool   ************/
-static void 
+static void
 putFreeNode(Tree *B, Nptr node)
 {
     int i;
@@ -1365,7 +1369,7 @@ putFreeNode(Tree *B, Nptr node)
 
 
 /*******   fill a free data node with a key and associated data   ******/
-static Nptr 
+static Nptr
 getDataNode(Tree *B, keyT key, dataT data)
 {
     Nptr       newNode = getFreeNode(B);
@@ -1408,7 +1412,7 @@ void showNode(Tree *B, const char * where, Nptr n)
     for (x = 1; x <= numentries(n); x++) {
         StringCbPrintfA(B->message, sizeof(B->message), "| entry %6d ", x);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
-        StringCbPrintfA(B->message, sizeof(B->message), "| key = %6s ", getkey(n, x).name);
+        StringCbPrintfA(B->message, sizeof(B->message), "| key = %6S ", getkey(n, x).name);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         StringCbPrintfA(B->message, sizeof(B->message), "| node = %6d  |\n", getnodenumber(B, getnode(n, x)));
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
@@ -1440,14 +1444,14 @@ void showBtree(Tree *B)
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
     StringCbPrintfA(B->message, sizeof(B->message), "|  theKey      %6s  |\n", getfunkey(B).name);
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
-    StringCbPrintfA(B->message, sizeof(B->message), "|  theData     %d.%d.%d |\n", getfundata(B).volume,
-             getfundata(B).vnode, getfundata(B).unique);
+    StringCbPrintfA(B->message, sizeof(B->message), "|  theData     %s (%d.%d.%d) |\n", getfundata(B).fsname, getfundata(B).fid.volume,
+             getfundata(B).fid.vnode, getfundata(B).fid.unique);
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
     StringCbPrintfA(B->message, sizeof(B->message), "-  --  --  --  --  --  -\n");
     osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
 }
 
-void 
+void
 listBtreeNodes(Tree *B, const char * parent_desc, Nptr node)
 {
     int i;
@@ -1458,17 +1462,17 @@ listBtreeNodes(Tree *B, const char * parent_desc, Nptr node)
         StringCbPrintfA(B->message, sizeof(B->message), "%s - NoNode!!!\n");
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         return;
-    } 
-    
+    }
+
     if (!isnode(node))
     {
         data = getdatavalue(node);
-        StringCbPrintfA(B->message, sizeof(B->message), "%s - data node %d (%d.%d.%d)\n", 
+        StringCbPrintfA(B->message, sizeof(B->message), "%s - data node %d (%d.%d.%d)\n",
                  parent_desc, getnodenumber(B, node),
                  data.fid.volume, data.fid.vnode, data.fid.unique);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         return;
-    } else 
+    } else
         showNode(B, parent_desc, node);
 
     if ( isinternal(node) || isroot(node) ) {
@@ -1482,7 +1486,7 @@ listBtreeNodes(Tree *B, const char * parent_desc, Nptr node)
 }
 
 /***********************   B+tree data printer   ***********************/
-void 
+void
 listBtreeValues(Tree *B, Nptr n, int num)
 {
     int slot;
@@ -1497,7 +1501,7 @@ listBtreeValues(Tree *B, Nptr n, int num)
         }
         prev = getkey(n, slot);
         data = getdatavalue(getnode(n, slot));
-        StringCbPrintfA(B->message, sizeof(B->message), "%8S (%d.%d.%d)\n", 
+        StringCbPrintfA(B->message, sizeof(B->message), "%8S (%d.%d.%d)\n",
                 prev.name, data.fid.volume, data.fid.vnode, data.fid.unique);
         osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
         if (++slot > numentries(n))
@@ -1508,14 +1512,14 @@ listBtreeValues(Tree *B, Nptr n, int num)
 }
 
 /********************   entire B+tree data printer   *******************/
-void 
+void
 listAllBtreeValues(Tree *B)
 {
     listBtreeValues(B, getleaf(B), BTERROR);
 }
 #endif /* DEBUG_BTREE */
 
-void 
+void
 findAllBtreeValues(Tree *B)
 {
     int num = -1;
@@ -1536,7 +1540,7 @@ findAllBtreeValues(Tree *B)
         if ( l != n ){
             if (l == NONODE)
                 StringCbPrintfA(B->message, sizeof(B->message),"BOMB %8S cannot be found\n", prev.name);
-            else 
+            else
                 StringCbPrintfA(B->message, sizeof(B->message),"BOMB lookup(%8S) finds wrong node\n", prev.name);
             osi_Log1(afsd_logp, "BPlus: %s", osi_LogSaveString(afsd_logp, B->message));
 #ifdef DEBUG_BTREE
@@ -1546,17 +1550,17 @@ findAllBtreeValues(Tree *B)
 
         if (++slot > numentries(n))
             n = getnextnode(n), slot = 1;
-    }   
+    }
 }
 
-/* 
+/*
  * the return must be -1, 0, or 1.  stricmp() in MSVC 8.0
  * does not return only those values.
  *
  * the sorting of the tree is by case insensitive sort order
  * therefore, unless the strings actually match via a case
  * insensitive search do we want to perform the case sensitive
- * match.  Otherwise, the search order might be considered 
+ * match.  Otherwise, the search order might be considered
  * to be inconsistent when the EXACT_MATCH flag is set.
  */
 int
@@ -1581,7 +1585,7 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
     fschar_t * fsname = NULL;
     normchar_t * entry = NULL;
 
-    if (op->scp->dirBplus == NULL || 
+    if (op->scp->dirBplus == NULL ||
         op->dataVersion > op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
@@ -1639,7 +1643,7 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
         } else {
             rc = CM_ERROR_AMBIGUOUS_FILENAME;
             bplus_lookup_ambiguous++;
-        } 
+        }
     } else {
         rc = ENOENT;
         bplus_lookup_misses++;
@@ -1677,7 +1681,7 @@ cm_BPlusDirLookup(cm_dirOp_t * op, clientchar_t * centry, cm_fid_t * cfid)
     Nptr leafNode = NONODE;
     LARGE_INTEGER start, end;
 
-    if (op->scp->dirBplus == NULL || 
+    if (op->scp->dirBplus == NULL ||
         op->dataVersion > op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
@@ -1735,7 +1739,7 @@ cm_BPlusDirLookup(cm_dirOp_t * op, clientchar_t * centry, cm_fid_t * cfid)
         } else {
             rc = CM_ERROR_AMBIGUOUS_FILENAME;
             bplus_lookup_ambiguous++;
-        } 
+        }
     } else {
             rc = ENOENT;
         bplus_lookup_misses++;
@@ -1753,7 +1757,7 @@ cm_BPlusDirLookup(cm_dirOp_t * op, clientchar_t * centry, cm_fid_t * cfid)
 }
 
 
-/* 
+/*
    On entry:
        op->scp->dirlock is write locked
 
@@ -1768,7 +1772,7 @@ long cm_BPlusDirCreateEntry(cm_dirOp_t * op, clientchar_t * entry, cm_fid_t * cf
     LARGE_INTEGER start, end;
     normchar_t * normalizedName = NULL;
 
-    if (op->scp->dirBplus == NULL || 
+    if (op->scp->dirBplus == NULL ||
         op->dataVersion != op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
@@ -1793,7 +1797,7 @@ long cm_BPlusDirCreateEntry(cm_dirOp_t * op, clientchar_t * entry, cm_fid_t * cf
 
     insert(op->scp->dirBplus, key, data);
 
-    if (!cm_Is8Dot3(entry)) {
+    if (cm_shortNames && !cm_Is8Dot3(entry)) {
         cm_dirFid_t dfid;
         clientchar_t wshortName[13];
 
@@ -1823,7 +1827,7 @@ long cm_BPlusDirCreateEntry(cm_dirOp_t * op, clientchar_t * entry, cm_fid_t * cf
     return rc;
 }
 
-/* 
+/*
    On entry:
        op->scp->dirlock is write locked
 
@@ -1838,7 +1842,7 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
     LARGE_INTEGER start, end;
     normchar_t * normalizedEntry = NULL;
 
-    if (op->scp->dirBplus == NULL || 
+    if (op->scp->dirBplus == NULL ||
         op->dataVersion != op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
@@ -1904,20 +1908,23 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
                 }
 
                 if (rc != CM_ERROR_AMBIGUOUS_FILENAME) {
-                    dfid.vnode = htonl(fid.vnode);
-                    dfid.unique = htonl(fid.unique);
-                    cm_Gen8Dot3NameIntW(centry, &dfid, shortName, NULL);
-
                     /* delete first the long name and then the short name */
                     delete(op->scp->dirBplus, key);
-                    key.name = shortName;
-                    delete(op->scp->dirBplus, key);
+
+                    if (cm_shortNames) {
+                        dfid.vnode = htonl(fid.vnode);
+                        dfid.unique = htonl(fid.unique);
+                        cm_Gen8Dot3NameIntW(centry, &dfid, shortName, NULL);
+
+                        key.name = shortName;
+                        delete(op->scp->dirBplus, key);
+                    }
                 }
             } /* !NONODE */
         } else {
             clientchar_t * cname = NULL;
 
-            /* We need to lookup the 8dot3 name to determine what the 
+            /* We need to lookup the 8dot3 name to determine what the
              * matching long name is
              */
             leafNode = bplus_Lookup(op->scp->dirBplus, key);
@@ -1959,7 +1966,7 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
                     rc = CM_ERROR_INEXACT_MATCH;
                 } else {
                     rc = CM_ERROR_AMBIGUOUS_FILENAME;
-                } 
+                }
             }
 
             if (rc != CM_ERROR_AMBIGUOUS_FILENAME) {
@@ -1977,7 +1984,7 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
             }
         }
     }
-    
+
     QueryPerformanceCounter(&end);
 
     bplus_remove_time += (end.QuadPart - start.QuadPart);
@@ -1987,7 +1994,7 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
         free(normalizedEntry);
 
     return rc;
-      
+
 }
 
 
@@ -2027,7 +2034,7 @@ int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
     /* the Write lock is held in cm_BPlusDirBuildTree() */
     insert(scp->dirBplus, key, data);
 
-    if (!cm_Is8Dot3(data.cname)) {
+    if (cm_shortNames && !cm_Is8Dot3(data.cname)) {
         cm_dirFid_t dfid;
         wchar_t wshortName[13];
 
@@ -2152,7 +2159,7 @@ void cm_BPlusDumpStats(void)
     afsi_log("             Free: %-16I64d", bplus_free_time);
 }
 
-static cm_direnum_t * 
+static cm_direnum_t *
 cm_BPlusEnumAlloc(afs_uint32 entries)
 {
     cm_direnum_t * enump;
@@ -2170,9 +2177,9 @@ cm_BPlusEnumAlloc(afs_uint32 entries)
     return enump;
 }
 
-long 
-cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
-                     afs_uint32 locked, clientchar_t * maskp, 
+long
+cm_BPlusDirEnumerate(cm_scache_t *dscp, cm_user_t *userp, cm_req_t *reqp,
+                     afs_uint32 locked, clientchar_t * maskp,
                      afs_uint32 fetchStatus, cm_direnum_t **enumpp)
 {
     afs_uint32 count = 0, slot, numentries;
@@ -2186,23 +2193,23 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
 
     /* Read lock the bplus tree so the data can't change */
     if (!locked)
-       lock_ObtainRead(&scp->dirlock);
+       lock_ObtainRead(&dscp->dirlock);
 
-    /* 
-     * Hold a reference to the directory so that it wont' be
-     * recycled while the enumeration is active. 
+    /*
+     * Hold a reference to the directory so that it won't be
+     * recycled while the enumeration is active.
      */
-    cm_HoldSCache(scp);
+    cm_HoldSCache(dscp);
     cm_HoldUser(userp);
 
-    if (scp->dirBplus == NULL) {
+    if (dscp->dirBplus == NULL) {
        osi_Log0(afsd_logp, "cm_BPlusDirEnumerate No BPlus Tree");
         rc = CM_ERROR_WOULDBLOCK;
        goto done;
     }
 
     /* Compute the number of entries */
-    for (count = 0, leafNode = getleaf(scp->dirBplus); leafNode; leafNode = nextLeafNode) {
+    for (count = 0, leafNode = getleaf(dscp->dirBplus); leafNode; leafNode = nextLeafNode) {
 
        for ( slot = 1, numentries = numentries(leafNode); slot <= numentries; slot++) {
            firstDataNode = getnode(leafNode, slot);
@@ -2240,7 +2247,7 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
     }
 
     /* Copy the name and fid for each cname entry into the enumeration */
-    for (count = 0, leafNode = getleaf(scp->dirBplus); leafNode; leafNode = nextLeafNode) {
+    for (count = 0, leafNode = getleaf(dscp->dirBplus); leafNode; leafNode = nextLeafNode) {
 
        for ( slot = 1, numentries = numentries(leafNode); slot <= numentries; slot++) {
            firstDataNode = getnode(leafNode, slot);
@@ -2272,17 +2279,19 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
                     enump->entry[count].name = name;
                     enump->entry[count].fid  = getdatavalue(dataNode).fid;
 
-                    if (!cm_Is8Dot3(name)) {
-                        cm_dirFid_t dfid;
+                    if (cm_shortNames) {
+                        if (!cm_Is8Dot3(name)) {
+                            cm_dirFid_t dfid;
 
-                        dfid.vnode = htonl(getdatavalue(dataNode).fid.vnode);
-                        dfid.unique = htonl(getdatavalue(dataNode).fid.unique);
+                            dfid.vnode = htonl(getdatavalue(dataNode).fid.vnode);
+                            dfid.unique = htonl(getdatavalue(dataNode).fid.unique);
 
-                        cm_Gen8Dot3NameIntW(name, &dfid, enump->entry[count].shortName, NULL);
-                    } else {
-                        StringCbCopyW(enump->entry[count].shortName,
-                                      sizeof(enump->entry[count].shortName),
-                                      name);
+                            cm_Gen8Dot3NameIntW(name, &dfid, enump->entry[count].shortName, NULL);
+                        } else {
+                            StringCbCopyW(enump->entry[count].shortName,
+                                          sizeof(enump->entry[count].shortName),
+                                          name);
+                        }
                     }
 
                     count++;
@@ -2292,23 +2301,31 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
        }
 
        nextLeafNode = getnextnode(leafNode);
-    }   
+    }
 
-    enump->dscp = scp;
+    enump->dscp = dscp;
     enump->userp = userp;
     enump->reqFlags = reqp->flags;
     enump->fetchStatus = fetchStatus;
+    enump->dataVersion = dscp->dirDataVersion;
 
   done:
     if (!locked)
-       lock_ReleaseRead(&scp->dirlock);
+       lock_ReleaseRead(&dscp->dirlock);
 
     /* if we failed, cleanup any mess */
     if (rc != 0) {
        osi_Log0(afsd_logp, "cm_BPlusDirEnumerate rc != 0");
-       
-        /* release the directory because we failed to generate an enumeration object */
-        cm_ReleaseSCache(scp);
+
+        /*
+         * release the directory because we failed to generate an enumeration object.
+         * adjust the directory position in the queue to ensure it doesn't get pushed
+         * out by the allocation of a large number of cm_scache objects.
+         */
+        lock_ObtainWrite(&cm_scacheLock);
+        cm_AdjustScacheLRU(dscp);
+        cm_ReleaseSCacheNoLock(dscp);
+        lock_ReleaseWrite(&cm_scacheLock);
         cm_ReleaseUser(userp);
         if (enump) {
            for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
@@ -2324,15 +2341,22 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
     return rc;
 }
 
-long 
+long
 cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
 {
     cm_scache_t *dscp = enump->dscp;
     cm_user_t   *userp = enump->userp;
-    cm_bulkStat_t *bsp;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32 ** bs_flagsp = NULL;
+    afs_uint32    dscp_errorCode = 0;
+    afs_uint32    dscp_flags = 0;
     afs_uint32 count;
     afs_uint32 code = 0;
     cm_req_t req;
+    int i;
+    cm_scache_t   *tscp;
+    afs_int32 nobulkstat = 0;
 
     cm_InitReq(&req);
     req.flags = enump->reqFlags;
@@ -2341,18 +2365,49 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
         return 0;
 
     bsp = malloc(sizeof(cm_bulkStat_t));
-    if (!bsp)
-        return ENOMEM;
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
     memset(bsp, 0, sizeof(cm_bulkStat_t));
+    bsp->userp = userp;
 
-    for ( count = 0; count < enump->count; count++ ) {
-        cm_scache_t   *tscp = cm_FindSCache(&enump->entry[count].fid);
-        int i;
+    bs_errorCodep = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
+    }
+
+    bs_flagsp = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_flagsp) {
+        code = ENOMEM;
+        goto done;
+    }
 
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bs_flagsp[0] = &dscp_flags;
+    bsp->counter++;
+
+  restart_stat:
+    for ( count = 0; count < enump->count; count++ ) {
+        if ( !wcscmp(L".", enump->entry[count].name) || !wcscmp(L"..", enump->entry[count].name) ) {
+            continue;
+        }
+        
+        tscp = cm_FindSCache(&enump->entry[count].fid);
         if (tscp) {
             if (lock_TryWrite(&tscp->rw)) {
                 /* we have an entry that we can look at */
-                if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
+                if (!cm_EAccesFindEntry(userp, &tscp->fid) && 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.
@@ -2360,8 +2415,20 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[count].errorCode = 0;
+                    continue;
+                }
+
+                if (nobulkstat) {
+                    code = cm_SyncOp(tscp, NULL, userp, &req, 0,
+                                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+                    lock_ReleaseWrite(&tscp->rw);
+                    cm_ReleaseSCache(tscp);
+                    enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[count].errorCode = code;
                     continue;
                 }
+
                 lock_ReleaseWrite(&tscp->rw);
             }  /* got lock */
             cm_ReleaseSCache(tscp);
@@ -2371,50 +2438,326 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
         bsp->fids[i].Volume = enump->entry[count].fid.volume;
         bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
         bsp->fids[i].Unique = enump->entry[count].fid.unique;
+        bs_errorCodep[i] = &enump->entry[count].errorCode;
+        bs_flagsp[bsp->counter] = &enump->entry[i].flags;
         enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
 
         if (bsp->counter == AFSCBMAX) {
             code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+            if (code == CM_ERROR_BULKSTAT_FAILURE) {
+                /*
+                 * If bulk stat cannot be used for this directory
+                 * we must perform individual fetch status calls.
+                 * Restart from the beginning of the enumeration.
+                 */
+                nobulkstat = 1;
+
+                for (i=0; i<bsp->counter; i++) {
+                    *(bs_flagsp[i]) &= ~CM_DIRENUM_FLAG_GOT_STATUS;
+                }
+                goto restart_stat;
+            }
+
+            if (code) {
+                /* on any other error, exit */
+                goto done;
+            }
+            for ( i=0; i<bsp->counter; i++) {
+                *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+            }
+
+            if (dscp_errorCode) {
+                code = dscp_errorCode;
+                goto done;
+            }
             memset(bsp, 0, sizeof(cm_bulkStat_t));
+            bsp->userp = userp;
+
+            /*
+             * In order to prevent the directory callback from expiring
+             * on really large directories with many symlinks to mount
+             * points such as /afs/andrew.cmu.edu/usr/, always include
+             * the directory fid in the search.
+             */
+            bsp->fids[0].Volume = dscp->fid.volume;
+            bsp->fids[0].Vnode = dscp->fid.vnode;
+            bsp->fids[0].Unique = dscp->fid.unique;
+            bs_errorCodep[0] = &dscp_errorCode;
+            bsp->counter++;
         }
     }
 
-    if (bsp->counter > 0)
+    if (bsp->counter > 0) {
         code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        if (code == CM_ERROR_BULKSTAT_FAILURE) {
+            /*
+             * If bulk stat cannot be used for this directory
+             * we must perform individual fetch status calls.
+             * Restart from the beginning of the enumeration.
+             */
+            nobulkstat = 1;
+
+            for (i=0; i<bsp->counter; i++) {
+                *(bs_flagsp[i]) &= ~CM_DIRENUM_FLAG_GOT_STATUS;
+            }
+            goto restart_stat;
+        }
+
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        if (dscp_errorCode) {
+            code = dscp_errorCode;
+            goto done;
+        }
+    }
+
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
+    if (bs_flagsp)
+        free(bs_flagsp);
 
-    free(bsp);
     return code;
 }
 
-static long 
-cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
+/*
+ * Similar to cm_BPlusDirEnumBulkStat() except that only
+ * one RPC is issued containing the provided scp FID and up to
+ * AFSCBMAX - 2 other FIDs for which the status info has yet
+ * to be obtained.
+ */
+long
+cm_BPlusDirEnumBulkStatOne(cm_direnum_t *enump, cm_scache_t *scp)
 {
     cm_scache_t *dscp = enump->dscp;
     cm_user_t   *userp = enump->userp;
-    cm_bulkStat_t *bsp;
-    afs_uint32 count;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32 ** bs_flagsp = NULL;
+    afs_uint32    dscp_errorCode = 0;
+    afs_uint32    dscp_flags = 0;
+    afs_uint32    scp_errorCode = 0;
+    afs_uint32    scp_flags = 0;
     afs_uint32 code = 0;
+    afs_uint32 i;
     cm_req_t req;
+    cm_scache_t   *tscp;
+
+    if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
+        return 0;
 
     cm_InitReq(&req);
     req.flags = enump->reqFlags;
 
+    bsp = malloc(sizeof(cm_bulkStat_t));
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
+    memset(bsp, 0, sizeof(cm_bulkStat_t));
+    bsp->userp = userp;
+
+    bs_errorCodep = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
+    }
+
+    bs_flagsp = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_flagsp) {
+        code = ENOMEM;
+        goto done;
+    }
+
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bs_flagsp[0] = &dscp_flags;
+    bsp->counter++;
+
+    /*
+     * There is an assumption that this FID is located
+     * within the directory enumeration but it could be
+     * the case that the enumeration is out of date and
+     * the FID is not listed.  So we explicitly add it
+     * after the directory FID and then skip it later
+     * if we find it.
+     */
+    bsp->fids[1].Volume = scp->fid.volume;
+    bsp->fids[1].Vnode = scp->fid.vnode;
+    bsp->fids[1].Unique = scp->fid.unique;
+    bs_errorCodep[1] = &scp_errorCode;
+    bs_flagsp[1] = &scp_flags;
+    bsp->counter++;
+
+    if (enump->count <= AFSCBMAX - 1) {
+        i = 0;
+    } else {
+        /*
+         * Find the requested FID in the enumeration and start from there.
+         */
+        for (i=0; i < enump->count && cm_FidCmp(&scp->fid, &enump->entry[i].fid); i++);
+    }
+
+    for ( ; bsp->counter < AFSCBMAX && i < enump->count; i++) {
+        if ( !wcscmp(L".", enump->entry[i].name) || !wcscmp(L"..", enump->entry[i].name) ) {
+            continue;
+        }
+
+        tscp = cm_FindSCache(&enump->entry[i].fid);
+        if (tscp) {
+            if (tscp == scp) {
+                cm_ReleaseSCache(tscp);
+                continue;
+            }
+
+            if (lock_TryWrite(&tscp->rw)) {
+                /* we have an entry that we can look at */
+                if (!cm_EAccesFindEntry(userp, &tscp->fid) && 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_ReleaseWrite(&tscp->rw);
+                    cm_ReleaseSCache(tscp);
+                    enump->entry[i].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[i].errorCode = 0;
+                    continue;
+                }
+                lock_ReleaseWrite(&tscp->rw);
+            }  /* got lock */
+            cm_ReleaseSCache(tscp);
+        } /* found entry */
+
+        bsp->fids[bsp->counter].Volume = enump->entry[i].fid.volume;
+        bsp->fids[bsp->counter].Vnode = enump->entry[i].fid.vnode;
+        bsp->fids[bsp->counter].Unique = enump->entry[i].fid.unique;
+        bs_errorCodep[bsp->counter] = &enump->entry[i].errorCode;
+        bs_flagsp[bsp->counter] = &enump->entry[i].flags;
+        enump->entry[i].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        bsp->counter++;
+    }
+
+    if (bsp->counter > 0) {
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        /* Now process any errors that might have occurred */
+        if (code == CM_ERROR_BULKSTAT_FAILURE) {
+            for (i=2; i<bsp->counter; i++) {
+                *(bs_flagsp[i]) &= ~CM_DIRENUM_FLAG_GOT_STATUS;
+            }
+
+            lock_ObtainWrite(&scp->rw);
+            code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            lock_ReleaseWrite(&scp->rw);
+            goto done;
+        }
+
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        /* Check if there was an error on the requested FID, if so return it */
+        if ( scp_errorCode ) {
+            code = scp_errorCode;
+            goto done;
+        }
+    }
+
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
+    if (bs_flagsp)
+        free(bs_flagsp);
+
+    return code;
+}
+
+static long
+cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
+{
+    cm_scache_t *dscp = enump->dscp;
+    cm_user_t   *userp = enump->userp;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32 ** bs_flagsp = NULL;
+    afs_uint32    dscp_errorCode = 0;
+    afs_uint32    dscp_flags = 0;
+    afs_uint32 count;
+    afs_uint32 code = 0;
+    cm_req_t req;
+    cm_scache_t   *tscp;
+    afs_int32     next = -1;
+    int i;
+
     if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
         return 0;
 
+    cm_InitReq(&req);
+    req.flags = enump->reqFlags;
+
     bsp = malloc(sizeof(cm_bulkStat_t));
-    if (!bsp)
-        return ENOMEM;
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
     memset(bsp, 0, sizeof(cm_bulkStat_t));
+    bsp->userp = userp;
 
-    for ( count = enump->next; count < enump->count; count++ ) {
-        cm_scache_t   *tscp = cm_FindSCache(&enump->entry[count].fid);
-        int i;
+    bs_errorCodep = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
+    }
 
+    bs_flagsp = malloc(sizeof(afs_uint32 *) * AFSCBMAX);
+    if (!bs_flagsp) {
+        code = ENOMEM;
+        goto done;
+    }
+
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bs_flagsp[0] = &dscp_flags;
+    bsp->counter++;
+
+    for ( count = enump->next; count < enump->count && bsp->counter < AFSCBMAX; count++ ) {
+        if ( !wcscmp(L".", enump->entry[count].name) || !wcscmp(L"..", enump->entry[count].name) ) {
+            continue;
+        }
+
+        tscp = cm_FindSCache(&enump->entry[count].fid);
         if (tscp) {
             if (lock_TryWrite(&tscp->rw)) {
                 /* we have an entry that we can look at */
-                if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
+                if (!cm_EAccesFindEntry(userp, &tscp->fid) && 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.
@@ -2422,6 +2765,7 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[count].errorCode = 0;
                     continue;
                 }
                 lock_ReleaseWrite(&tscp->rw);
@@ -2429,28 +2773,76 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
             cm_ReleaseSCache(tscp);
         }      /* found entry */
 
-        i = bsp->counter++;
-        bsp->fids[i].Volume = enump->entry[count].fid.volume;
-        bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
-        bsp->fids[i].Unique = enump->entry[count].fid.unique;
+        /* 'next' is the enump entry that is stored in the [bsp->counter == 1] element */
+        if (next == -1)
+            next = count;
+
+        bsp->fids[bsp->counter].Volume = enump->entry[count].fid.volume;
+        bsp->fids[bsp->counter].Vnode = enump->entry[count].fid.vnode;
+        bsp->fids[bsp->counter].Unique = enump->entry[count].fid.unique;
+        bs_errorCodep[bsp->counter] = &enump->entry[count].errorCode;
+        bs_flagsp[bsp->counter] = &enump->entry[count].flags;
         enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        bsp->counter++;
+    }
 
-        if (bsp->counter == AFSCBMAX) {
-            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
-            break;
+    if (bsp->counter > 0) {
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        /* Now process any errors that might have occurred */
+        if (code == CM_ERROR_BULKSTAT_FAILURE) {
+            for (i=0; i<bsp->counter; i++) {
+                *(bs_flagsp[i]) &= ~CM_DIRENUM_FLAG_GOT_STATUS;
+            }
+
+            /* if next == -1, there is no entry to update the status of */
+            if (next != -1) {
+                code = cm_GetSCache(&enump->entry[next].fid, NULL, &tscp, userp, &req);
+                if (code == 0) {
+                    if (lock_TryWrite(&tscp->rw)) {
+                        code = cm_SyncOp(tscp, NULL, userp, &req, 0,
+                                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+                        lock_ReleaseWrite(&tscp->rw);
+                        *(bs_errorCodep[1]) = code;
+                        *(bs_flagsp[1]) |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    }
+                    cm_ReleaseSCache(tscp);
+                } else {
+                    *(bs_errorCodep[1]) = code;
+                    *(bs_flagsp[1]) |= CM_DIRENUM_FLAG_GOT_STATUS;
+                }
+            }
+            goto done;
+        }
+
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        if (dscp_errorCode) {
+            code = dscp_errorCode;
+            goto done;
         }
     }
 
-    if (bsp->counter > 0 && bsp->counter < AFSCBMAX)
-        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
+    if (bs_flagsp)
+        free(bs_flagsp);
 
-    free(bsp);
     return code;
 }
 
 long
 cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
-{      
+{
+    long code = 0;
+
     if (enump == NULL || entrypp == NULL || enump->next >= enump->count) {
        if (entrypp)
            *entrypp = NULL;
@@ -2458,9 +2850,10 @@ cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
        return CM_ERROR_INVAL;
     }
 
-    if (enump->fetchStatus && 
-        !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS))
-        cm_BPlusDirEnumBulkStatNext(enump);
+    if (enump->fetchStatus &&
+               !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS)) {
+        code = cm_BPlusDirEnumBulkStatNext(enump);
+    }
 
     *entrypp = &enump->entry[enump->next++];
     if ( enump->next == enump->count ) {
@@ -2468,14 +2861,21 @@ cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
        return CM_ERROR_STOPNOW;
     }
     else {
-       osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry SUCCESS");
+        if (code) {
+            (*entrypp)->errorCode = code;
+            osi_Log1(afsd_logp, "cm_BPlusDirNextEnumEntry ERROR 0x%x", code);
+        } else {
+            osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry SUCCESS");
+        }
        return 0;
     }
 }
 
 long
 cm_BPlusDirPeekNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
-{      
+{
+    long code;
+
     if (enump == NULL || entrypp == NULL || enump->next >= enump->count) {
        if (entrypp)
            *entrypp = NULL;
@@ -2483,9 +2883,12 @@ cm_BPlusDirPeekNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
        return CM_ERROR_INVAL;
     }
 
-    if (enump->fetchStatus && 
-        !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS))
-        cm_BPlusDirEnumBulkStatNext(enump);
+    if (enump->fetchStatus &&
+        !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS)) {
+        code = cm_BPlusDirEnumBulkStatNext(enump);
+        if (code)
+            return code;
+    }
 
     *entrypp = &enump->entry[enump->next];
     if ( enump->next == enump->count ) {
@@ -2498,7 +2901,7 @@ cm_BPlusDirPeekNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
     }
 }
 
-long 
+long
 cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
 {
     afs_uint32 count;
@@ -2506,8 +2909,16 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
     osi_Log0(afsd_logp, "cm_BPlusDirFreeEnumeration");
 
     if (enump) {
-        /* Release the directory object */
-        cm_ReleaseSCache(enump->dscp);
+        /*
+         * Release the directory object but first adjust its position
+         * in the LRU queue to ensure that it does not get stuck at the
+         * end due to the allocation of a large number of cm_scache
+         * entries in the directory.
+         */
+        lock_ObtainWrite(&cm_scacheLock);
+        cm_AdjustScacheLRU(enump->dscp);
+        cm_ReleaseSCacheNoLock(enump->dscp);
+        lock_ReleaseWrite(&cm_scacheLock);
         cm_ReleaseUser(enump->userp);
 
        for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
@@ -2570,7 +2981,7 @@ cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_ui
                    entryp->name,
                    entryp->fid.cell, entryp->fid.volume, entryp->fid.vnode, entryp->fid.unique,
                    entryp->shortName,
-                   type, 
+                   type,
                    dv);
 
            osi_Log0(afsd_logp, osi_LogSaveString(afsd_logp, buffer));