Windows: CM_SCACHEFLAG_DELETED use InterlockedOr
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
index 91dbcf1..62863ba 100644 (file)
@@ -9,17 +9,13 @@
 
 #include <afsconfig.h>
 #include <afs/param.h>
+#include <afs/stds.h>
 #include <roken.h>
 
-#include <afs/stds.h>
+#include <afs/unified_afs.h>
 
 #include <windows.h>
 #include <winsock2.h>
-#include <stddef.h>
-#include <malloc.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
 
 #include <osi.h>
 
@@ -364,7 +360,7 @@ long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
         return code;
 
     thyper.HighPart = 0; thyper.LowPart = 0;
-    code = buf_Get(scp, &thyper, reqp, &bufferp);
+    code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
     if (code)
         return code;
 
@@ -607,7 +603,7 @@ long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
                 bufferp = NULL;
             }
 
-            code = buf_Get(scp, &thyper, reqp, &bufferp);
+            code = buf_Get(scp, &thyper, reqp, 0, &bufferp);
             if (code) {
                 /* if buf_Get() fails we do not have a buffer object to lock */
                 bufferp = NULL;
@@ -771,6 +767,7 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
 
     if (match != 0
         && sp->hasTilde
+        && cm_shortNames
         && !cm_Is8Dot3(matchName)) {
 
         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
@@ -839,10 +836,17 @@ long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
  */
 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
-    long code;
+    long code = 0;
 
-    if (scp->mountPointStringp[0])
-        return 0;
+    code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_FETCHDATA);
+    if (code)
+        return code;
+
+    if (scp->mountPointStringp[0] &&
+         scp->mpDataVersion == scp->dataVersion) {
+        code = 0;
+        goto done;
+    }
 
 #ifdef AFS_FREELANCE_CLIENT
     /* File servers do not have data for freelance entries */
@@ -856,28 +860,38 @@ long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     {
         char temp[MOUNTPOINTLEN];
         osi_hyper_t offset;
+        afs_uint32 bytesRead = 0;
 
         /* otherwise, we have to read it in */
         offset.LowPart = offset.HighPart = 0;
-        code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
+        code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, &bytesRead, userp, reqp);
         if (code)
-            return code;
+            goto done;
 
         /*
          * scp->length is the actual length of the mount point string.
          * It is current because cm_GetData merged the most up to date
          * status info into scp and has not dropped the rwlock since.
          */
-        if (scp->length.LowPart > MOUNTPOINTLEN - 1)
-            return CM_ERROR_TOOBIG;
-        if (scp->length.LowPart == 0)
-            return CM_ERROR_INVAL;
+        if (scp->length.LowPart > MOUNTPOINTLEN - 1) {
+            code = CM_ERROR_TOOBIG;
+            goto done;
+        }
+
+        if (scp->length.LowPart == 0) {
+            code = CM_ERROR_INVAL;
+            goto done;
+        }
 
         /* convert the terminating dot to a NUL */
         temp[scp->length.LowPart - 1] = 0;
         memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
+        scp->mpDataVersion = scp->dataVersion;
     }
 
+  done:
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_FETCHDATA);
+
     return code;
 }
 
@@ -905,7 +919,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
         tfid = scp->mountRootFid;
         lock_ReleaseWrite(&scp->rw);
-        code = cm_GetSCache(&tfid, outScpp, userp, reqp);
+        code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
         lock_ObtainWrite(&scp->rw);
         return code;
     }
@@ -925,7 +939,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
         /* now look up the cell */
         lock_ReleaseWrite(&scp->rw);
-        cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
+        cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE|CM_FLAG_NOPROBE);
         lock_ObtainWrite(&scp->rw);
     } else {
         /* normal mt pt */
@@ -1022,7 +1036,7 @@ long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
 
         tfid = scp->mountRootFid;
         lock_ReleaseWrite(&scp->rw);
-        code = cm_GetSCache(&tfid, outScpp, userp, reqp);
+        code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
         lock_ObtainWrite(&scp->rw);
     }
 
@@ -1135,7 +1149,7 @@ retry_lookup:
      * that we stopped early, probably because we found the entry we're
      * looking for.  Any other non-zero code is an error.
      */
-    if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
+    if (code && code != CM_ERROR_STOPNOW) {
         /* if the cm_scache_t we are searching in is not a directory
          * we must return path not found because the error
          * is to describe the final component not an intermediary
@@ -1152,7 +1166,7 @@ retry_lookup:
 notfound:
     getroot = (dscp==cm_data.rootSCachep) ;
     if (!rock.found) {
-        if (!cm_freelanceEnabled || !getroot) {
+        if (!(cm_freelanceEnabled && cm_freelanceDiscovery) || !getroot) {
             if (flags & CM_FLAG_CHECKPATH)
                 code = CM_ERROR_NOSUCHPATH;
             else
@@ -1267,7 +1281,7 @@ notfound:
     if ( !tscp )    /* we did not find it in the dnlc */
     {
         dnlcHit = 0;
-        code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
+        code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
         if (code)
             goto done;
     }
@@ -1276,7 +1290,7 @@ notfound:
     lock_ObtainWrite(&tscp->rw);
 
     /*
-     * Do not get status if we do not already have a callback.
+     * Do not get status if we do not already have a callback or know the type.
      * The process of reading the mount point string will obtain status information
      * in a single RPC.  No reason to add a second round trip.
      *
@@ -1284,7 +1298,7 @@ notfound:
      * current cm_user_t is not the same as the one that obtained the
      * mount point string contents.
      */
-    if (cm_HaveCallback(tscp)) {
+    if (cm_HaveCallback(tscp) || tscp->fileType == CM_SCACHETYPE_UNKNOWN) {
         code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
         if (code) {
@@ -1355,7 +1369,7 @@ int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, lon
 #ifdef _WIN64
     int use_sysname64 = 0;
 
-    if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
+    if (cm_sysName64Count > 0 && reqp && !(reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
         use_sysname64 = 1;
 #endif
 
@@ -1483,7 +1497,7 @@ long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * us
 
     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
 
-    code = cm_GetSCache(&fid, outScpp, userp, reqp);
+    code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
 
   _exit_cleanup:
     if (fnamep)
@@ -1679,7 +1693,7 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
                                 &newDirStatus, &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
+    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &newDirStatus, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -1694,7 +1708,7 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
     lock_ObtainWrite(&dscp->rw);
     cm_dnlcRemove(dscp, cnamep);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         invalidate = 1;
         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
             lock_ReleaseWrite(&dscp->rw);
@@ -1727,11 +1741,10 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
                               dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
 
     if (scp) {
-        cm_ReleaseSCache(scp);
         if (code == 0) {
            lock_ObtainWrite(&scp->rw);
             if (--scp->linkCount == 0) {
-                scp->flags |= CM_SCACHEFLAG_DELETED;
+               _InterlockedOr(&scp->flags, CM_SCACHEFLAG_DELETED);
                lock_ObtainWrite(&cm_scacheLock);
                 cm_AdjustScacheLRU(scp);
                 cm_RemoveSCacheFromHashTable(scp);
@@ -1739,12 +1752,12 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
             }
             cm_DiscardSCache(scp);
            lock_ReleaseWrite(&scp->rw);
-            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
-                !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
-                                      scp->fid.unique, scp->fid.hash,
-                                      scp->fileType, AFS_INVALIDATE_DELETED))
-                buf_ClearRDRFlag(scp, "unlink");
+            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
+                RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                     scp->fid.unique, scp->fid.hash,
+                                     scp->fileType, AFS_INVALIDATE_DELETED);
         }
+        cm_ReleaseSCache(scp);
     }
 
   done:
@@ -1762,7 +1775,8 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
     long code = 0;
 
     lock_AssertWrite(&linkScp->rw);
-    if (!linkScp->mountPointStringp[0]) {
+    if (!linkScp->mountPointStringp[0] ||
+        linkScp->mpDataVersion != linkScp->dataVersion) {
 
 #ifdef AFS_FREELANCE_CLIENT
        /* File servers do not have data for freelance entries */
@@ -1776,10 +1790,11 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
         {
             char temp[MOUNTPOINTLEN];
             osi_hyper_t offset;
+            afs_uint32 bytesRead = 0;
 
             /* read the link data from the file server */
             offset.LowPart = offset.HighPart = 0;
-            code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
+            code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, &bytesRead, userp, reqp);
             if (code)
                 return code;
 
@@ -1796,6 +1811,7 @@ long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
             /* make sure we are NUL terminated */
             temp[linkScp->length.LowPart] = 0;
             memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
+            linkScp->mpDataVersion = linkScp->dataVersion;
         }
 
         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
@@ -1968,7 +1984,6 @@ long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
     int symlinkCount;          /* count of # of symlinks traversed */
     int extraFlag;             /* avoid chasing mt pts for dir cmd */
     int phase = 1;             /* 1 = tidPathp, 2 = pathp */
-#define MAX_FID_COUNT 512
     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
     int fid_count = 0;          /* number of fids processed in this path walk */
     int i;
@@ -2032,12 +2047,53 @@ long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
                  */
                 *cp++ = 0;     /* add null termination */
                extraFlag = 0;
-               if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
-                   extraFlag = CM_FLAG_NOMOUNTCHASE;
-               code = cm_Lookup(tscp, component,
-                                 flags | extraFlag,
-                                 userp, reqp, &nscp);
 
+                if (tscp == cm_RootSCachep(userp, reqp)) {
+                    code = cm_Lookup(tscp, component, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
+
+                    if ((code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE ||
+                         code == CM_ERROR_BPLUS_NOMATCH) &&
+                         tscp == cm_data.rootSCachep) {
+
+                        clientchar_t volref[AFSPATHMAX];
+
+                        if (wcschr(component, '%') != NULL || wcschr(component, '#') != NULL) {
+                            /*
+                             * A volume reference:  <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
+                             */
+                            cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
+                            cm_ClientStrCat(volref, AFSPATHMAX, component);
+
+                            code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
+                        }
+#ifdef AFS_FREELANCE_CLIENT
+                        else if (tscp->fid.cell == AFS_FAKE_ROOT_CELL_ID && tscp->fid.volume == AFS_FAKE_ROOT_VOL_ID &&
+                                  tscp->fid.vnode == 1 && tscp->fid.unique == 1) {
+                            /*
+                             * If this is the Freelance volume root directory then treat unrecognized
+                             * names as cell names and attempt to find the appropriate "root.cell".
+                             */
+                            cm_ClientStrCpyN(volref, AFSPATHMAX, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
+                            if (component[0] == L'.') {
+                                cm_ClientStrCat(volref, AFSPATHMAX, &component[1]);
+                                cm_ClientStrCatN(volref, AFSPATHMAX, L"%", sizeof(WCHAR));
+                            } else {
+                                cm_ClientStrCat(volref, AFSPATHMAX, component);
+                                cm_ClientStrCatN(volref, AFSPATHMAX, L"#", sizeof(WCHAR));
+                            }
+                            cm_ClientStrCatN(volref, AFSPATHMAX, L"root.cell", 9 * sizeof(WCHAR));
+
+                            code = cm_EvaluateVolumeReference(volref, CM_FLAG_CHECKPATH, userp, reqp, &nscp);
+                        }
+#endif
+                    }
+                } else {
+                    if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
+                        extraFlag = CM_FLAG_NOMOUNTCHASE;
+                    code = cm_Lookup(tscp, component,
+                                     flags | extraFlag,
+                                     userp, reqp, &nscp);
+                }
                 if (code == 0) {
                     if (!cm_ClientStrCmp(component,_C("..")) ||
                         !cm_ClientStrCmp(component,_C("."))) {
@@ -2350,7 +2406,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
     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(bsp->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.
@@ -2374,7 +2430,7 @@ long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
           !(tfid.vnode==0x1 && tfid.unique==0x1) )
     {
         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
-        return cm_GetSCache(&tfid, &tscp, NULL, NULL);
+        return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
     }
 #endif /* AFS_FREELANCE_CLIENT */
 
@@ -2441,16 +2497,27 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
                 continue;
 
             rxconnp = cm_GetRxConn(connp);
-           if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
+           if (SERVERHASINLINEBULK(connp)) {
                code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
                                               &statStruct, &callbackStruct, &volSync);
                if (code == RXGEN_OPCODE) {
-                   cm_SetServerNoInlineBulk(connp->serverp, 0);
+                   SET_SERVERHASNOINLINEBULK(connp);
                } else {
                    inlinebulk = 1;
                }
            }
            if (!inlinebulk) {
+                /*
+                 * It is important to note that RXAFS_BulkStatus is quite braindead.
+                 * The AFS 3.6 file server implementation returns arrays that are
+                 * sized to hold responses for all of the requested FIDs but it only
+                 * populates their contents up to the point where it detects an error.
+                 * Unfortunately, it does inform the caller which entries were filled
+                 * and which were not.  The caller has no ability to determine which
+                 * FID the RPC return code applies to or which of the FIDs valid status
+                 * info and callbacks have been issued for.  As a result, when an
+                 * error is returned, none of the data received can be trusted.
+                 */
                code = RXAFS_BulkStatus(rxconnp, &fidStruct,
                                        &statStruct, &callbackStruct, &volSync);
            }
@@ -2484,7 +2551,7 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
                         code = (&bbp->stats[0])->errorCode;
                 }
             }
-        } while (cm_Analyze(connp, userp, reqp, &tfid, 0, &volSync, NULL, &cbReq, code));
+        } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &bbp->stats[0], &volSync, NULL, &cbReq, code));
         code = cm_MapRPCError(code, reqp);
 
         /*
@@ -2494,6 +2561,17 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
         if (code) {
             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
                      inlinebulk ? "Inline" : "", code);
+            if (!inlinebulk) {
+                /*
+                 * Since an error occurred and it is impossible to determine
+                 * the context in which the returned error code should be
+                 * interpreted, we return the CM_ERROR_BULKSTAT_FAILURE error
+                 * which indicates that Bulk Stat cannot be used for the
+                 * current request.  The caller should fallback to using
+                 * individual RXAFS_FetchStatus calls.
+                 */
+                code = CM_ERROR_BULKSTAT_FAILURE;
+            }
             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
             break;
         }
@@ -2515,9 +2593,17 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
 
             if (inlinebulk && (&bbp->stats[j])->errorCode) {
                 cm_req_t treq = *reqp;
-                cm_Analyze(NULL, userp, &treq, &tfid, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
+                cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &bbp->stats[j], &volSync,
+                           NULL, &cbReq, (&bbp->stats[j])->errorCode);
+                switch ((&bbp->stats[j])->errorCode) {
+                case EACCES:
+                case UAEACCES:
+                case EPERM:
+                case UAEPERM:
+                    cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
+                }
             } else {
-                code = cm_GetSCache(&tfid, &scp, userp, reqp);
+                code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
                 if (code != 0)
                     continue;
 
@@ -2547,16 +2633,16 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
                 if ((scp->cbServerp == NULL &&
                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
-                     (scp->flags & CM_SCACHEFLAG_EACCESS))
+                     cm_EAccesFindEntry(userp, &scp->fid))
                 {
                     lock_ConvertRToW(&scp->rw);
                     lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
                                                           &bbp->callbacks[j],
                                                           &volSync,
-                                                          CM_CALLBACK_MAINTAINCOUNT);
+                                                          CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
                     InterlockedIncrement(&scp->activeRPCs);
                     if (!lostRace)
-                        cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
+                        code = cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
                     lock_ReleaseWrite(&scp->rw);
                 } else {
                     lock_ReleaseRead(&scp->rw);
@@ -2589,6 +2675,7 @@ cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
 
     bbp = malloc(sizeof(cm_bulkStat_t));
     memset(bbp, 0, sizeof(cm_bulkStat_t));
+    bbp->userp = userp;
     bbp->bufOffset = *offsetp;
 
     lock_ReleaseWrite(&dscp->rw);
@@ -2649,6 +2736,91 @@ void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *att
     statusp->Mask = mask;
 }
 
+int
+cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
+{
+    int spaceAvail = 1;
+    afs_uint32  code;
+    cm_conn_t *connp;
+    struct rx_connection * rxconnp;
+    AFSFetchVolumeStatus volStat;
+    cm_volume_t *volp = NULL;
+    afs_uint32   volType;
+    char *Name;
+    char *OfflineMsg;
+    char *MOTD;
+    char volName[32]="(unknown)";
+    char offLineMsg[256]="server temporarily inaccessible";
+    char motd[256]="server temporarily inaccessible";
+    osi_hyper_t freespace;
+    cm_fid_t    vfid;
+    cm_scache_t *vscp;
+
+    if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
+        fidp->volume==AFS_FAKE_ROOT_VOL_ID)
+    {
+        goto _done;
+    }
+
+    volp = cm_GetVolumeByFID(fidp);
+    if (!volp) {
+        spaceAvail = 0;
+        goto _done;
+    }
+    volType = cm_VolumeType(volp, fidp->volume);
+    if (volType == ROVOL || volType == BACKVOL) {
+        spaceAvail = 0;
+        goto _done;
+    }
+
+    cm_SetFid(&vfid, fidp->cell, fidp->volume, 1, 1);
+    code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
+    if (code == 0) {
+        lock_ObtainWrite(&vscp->rw);
+        code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&vscp->rw);
+        if (code == 0) {
+            Name = volName;
+            OfflineMsg = offLineMsg;
+            MOTD = motd;
+
+            do {
+                code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
+                if (code) continue;
+
+                rxconnp = cm_GetRxConn(connp);
+                code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
+                                             &volStat, &Name, &OfflineMsg, &MOTD);
+                rx_PutConnection(rxconnp);
+
+            } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
+            code = cm_MapRPCError(code, reqp);
+        }
+
+        lock_ObtainWrite(&vscp->rw);
+        cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&vscp->rw);
+        cm_ReleaseSCache(vscp);
+    }
+
+    if (code == 0) {
+        if (volStat.MaxQuota) {
+            freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
+        } else {
+            freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
+        }
+        spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
+    }
+    /* the rpc failed, assume there is space and we can fail it later. */
+
+  _done:
+    if (volp)
+        cm_PutVolume(volp);
+
+    return spaceAvail;
+}
+
 /* set the file size, and make sure that all relevant buffers have been
  * truncated.  Ensure that any partially truncated buffers have been zeroed
  * to the end of the buffer.
@@ -2658,6 +2830,7 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
 {
     long code;
     int shrinking;
+    int available;
 
     /* start by locking out buffer creation */
     lock_ObtainWrite(&scp->bufCreateLock);
@@ -2729,14 +2902,44 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
         scp->mask |= CM_SCACHEMASK_LENGTH;
     }
     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
-        /* really extending the file */
-        scp->length = *sizep;
-        scp->mask |= CM_SCACHEMASK_LENGTH;
+        /*
+         * Really extending the file so must check to see if we
+         * have sufficient quota.  cm_IsSpaceAvailable() obtains
+         * the cm_scache.rw lock on the volume root directory.
+         * vnode 1 < scp->fid.vnode therefore calling cm_IsSpaceAvailable
+         * while holding scp->rw is a lock order violation.
+         * Dropping it is ok because we are holding scp->bufCreateLock
+         * which prevents the size of the file from changing.
+         */
+        afs_uint64 nextChunk = scp->length.QuadPart;
+
+        nextChunk -= (nextChunk & 0xFFFFF);
+        nextChunk += 0x100000;
+
+        if (sizep->QuadPart > nextChunk) {
+            lock_ReleaseWrite(&scp->rw);
+            available = cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp);
+            lock_ObtainWrite(&scp->rw);
+        } else {
+            /*
+             * The file server permits 1MB quota overruns so only check
+             * when the file size increases by at least that much.
+             */
+            available = 1;
+        }
+        if (available) {
+            scp->length = *sizep;
+            scp->mask |= CM_SCACHEMASK_LENGTH;
+        } else {
+            code = CM_ERROR_SPACE;
+            goto syncopdone;
+        }
     }
 
     /* done successfully */
     code = 0;
 
+  syncopdone:
     cm_SyncOpDone(scp, NULL,
                   CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
                   | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
@@ -2805,7 +3008,7 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &scp->fid, 1, &volSync, NULL, NULL, code));
+                         &scp->fid, NULL, 1, &afsOutStatus, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -2815,8 +3018,8 @@ long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
 
     lock_ObtainWrite(&scp->rw);
     if (code == 0)
-        cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
-                        CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
+        code = cm_MergeStatus( NULL, scp, &afsOutStatus, &volSync, userp, reqp,
+                               CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
     else
         InterlockedDecrement(&scp->activeRPCs);
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
@@ -2917,7 +3120,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, 1, &volSync, NULL, &cbReq, code));
+                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -2931,7 +3134,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     }
     lock_ObtainWrite(&dscp->rw);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         if (cm_CheckDirOpForSingleChange(&dirop)) {
             lock_ReleaseWrite(&dscp->rw);
@@ -2953,7 +3156,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
      * info.
      */
     if (code == 0) {
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
            scp->creator = userp;               /* remember who created it */
@@ -2962,8 +3165,8 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
                                                       &newFileCallback, &volSync, 0);
                 InterlockedIncrement(&scp->activeRPCs);
                 if (!lostRace)
-                    cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
-                                   userp, reqp, 0);
+                    code = cm_MergeStatus( dscp, scp, &newFileStatus, &volSync,
+                                           userp, reqp, 0);
                 didEnd = 1;
             }
             lock_ReleaseWrite(&scp->rw);
@@ -3109,7 +3312,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                        &dscp->fid, 1, &volSync, NULL, &cbReq, code));
+                        &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -3123,7 +3326,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     }
     lock_ObtainWrite(&dscp->rw);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         if (cm_CheckDirOpForSingleChange(&dirop)) {
             lock_ReleaseWrite(&dscp->rw);
@@ -3145,7 +3348,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
      * info.
      */
     if (code == 0) {
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
@@ -3153,8 +3356,8 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
                                                       &newDirCallback, &volSync, 0);
                 InterlockedIncrement(&scp->activeRPCs);
                 if (!lostRace)
-                    cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
-                                   userp, reqp, 0);
+                    code = cm_MergeStatus( dscp, scp, &newDirStatus, &volSync,
+                                           userp, reqp, 0);
                 didEnd = 1;
             }
             lock_ReleaseWrite(&scp->rw);
@@ -3240,7 +3443,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
         rx_PutConnection(rxconnp);
         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
 
-    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, 1, &volSync, NULL, NULL, code));
+    } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
 
     code = cm_MapRPCError(code, reqp);
 
@@ -3255,7 +3458,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
     }
     lock_ObtainWrite(&dscp->rw);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         invalidate = 1;
 
         if (cm_CheckDirOpForSingleChange(&dirop)) {
@@ -3283,7 +3486,7 @@ long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long fl
     if (code == 0) {
         lock_ObtainWrite(&sscp->rw);
         InterlockedIncrement(&sscp->activeRPCs);
-        cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
+        code = cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
         lock_ReleaseWrite(&sscp->rw);
     }
 
@@ -3356,7 +3559,7 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                         &dscp->fid, 1, &volSync, NULL, NULL, code));
+                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -3370,7 +3573,7 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
     }
     lock_ObtainWrite(&dscp->rw);
     if (code == 0) {
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
         if (cm_CheckDirOpForSingleChange(&dirop)) {
             lock_ReleaseWrite(&dscp->rw);
@@ -3396,13 +3599,13 @@ long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, lo
      * info.
      */
     if (code == 0) {
-        code = cm_GetSCache(&newFid, &scp, userp, reqp);
+        code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
         if (code == 0) {
             lock_ObtainWrite(&scp->rw);
             if (!cm_HaveCallback(scp)) {
                 InterlockedIncrement(&scp->activeRPCs);
-                cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
-                                userp, reqp, 0);
+                code = cm_MergeStatus( dscp, scp, &newLinkStatus, &volSync,
+                                       userp, reqp, 0);
             }
             lock_ReleaseWrite(&scp->rw);
 
@@ -3513,7 +3716,7 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
         rx_PutConnection(rxconnp);
 
     } while (cm_Analyze(connp, userp, reqp,
-                        &dscp->fid, 1, &volSync, NULL, NULL, code));
+                        &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
     code = cm_MapRPCErrorRmdir(code, reqp);
 
     if (code)
@@ -3528,7 +3731,7 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
     lock_ObtainWrite(&dscp->rw);
     if (code == 0) {
         cm_dnlcRemove(dscp, cnamep);
-        cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
             lock_ReleaseWrite(&dscp->rw);
             cm_DirDeleteEntry(&dirop, fnamep);
@@ -3546,21 +3749,20 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
     cm_EndDirOp(&dirop);
 
     if (scp) {
-        cm_ReleaseSCache(scp);
         if (code == 0) {
            lock_ObtainWrite(&scp->rw);
-            scp->flags |= CM_SCACHEFLAG_DELETED;
+           _InterlockedOr(&scp->flags, CM_SCACHEFLAG_DELETED);
             lock_ObtainWrite(&cm_scacheLock);
             cm_AdjustScacheLRU(scp);
             cm_RemoveSCacheFromHashTable(scp);
             lock_ReleaseWrite(&cm_scacheLock);
            lock_ReleaseWrite(&scp->rw);
-            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
-                !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
-                                      scp->fid.unique, scp->fid.hash,
-                                      scp->fileType, AFS_INVALIDATE_DELETED))
-                buf_ClearRDRFlag(scp, "rmdir");
+            if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
+                RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                     scp->fid.unique, scp->fid.hash,
+                                     scp->fileType, AFS_INVALIDATE_DELETED);
         }
+        cm_ReleaseSCache(scp);
     }
 
   done:
@@ -3859,8 +4061,8 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
                             &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, 1,
-                         &volSync, NULL, NULL, code));
+    } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
+                        &updatedOldDirStatus, &volSync, NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
 
     if (code)
@@ -3877,8 +4079,8 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
 
     lock_ObtainWrite(&oldDscp->rw);
     if (code == 0) {
-        cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
-                       userp, reqp, CM_MERGEFLAG_DIROP);
+        code = cm_MergeStatus( NULL, oldDscp, &updatedOldDirStatus, &volSync,
+                               userp, reqp, CM_MERGEFLAG_DIROP);
         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
             lock_ReleaseWrite(&oldDscp->rw);
             if (bTargetExists && oneDir) {
@@ -3930,8 +4132,8 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
         }
         lock_ObtainWrite(&newDscp->rw);
         if (code == 0) {
-            cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
-                            userp, reqp, CM_MERGEFLAG_DIROP);
+            code = cm_MergeStatus( NULL, newDscp, &updatedNewDirStatus, &volSync,
+                                   userp, reqp, CM_MERGEFLAG_DIROP);
 
             /*
              * we only make the local change if we successfully made
@@ -4561,7 +4763,7 @@ long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
                              &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
+    } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
                         NULL, NULL, code));
 
     code = cm_MapRPCError(code, reqp);
@@ -4623,7 +4825,7 @@ long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
         rx_PutConnection(rxconnp);
 
-    } while (cm_Analyze(connp, userp, reqp, &cfid, 1, &volSync,
+    } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
                         NULL, NULL, code));
     code = cm_MapRPCError(code, reqp);
     if (code)
@@ -5617,7 +5819,7 @@ void cm_CheckLocks()
                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
 
                     } while (cm_Analyze(connp, userp, &req,
-                                        &cfid, 1, &volSync, NULL, NULL,
+                                        &cfid, NULL, 1, NULL, &volSync, NULL, NULL,
                                         code));
 
                     code = cm_MapRPCError(code, &req);