Windows: Redirector interface for afsd_service.exe
authorJeffrey Altman <jaltman@your-file-system.com>
Thu, 15 Sep 2011 05:11:15 +0000 (01:11 -0400)
committerJeffrey Altman <jaltman@openafs.org>
Thu, 15 Sep 2011 18:25:13 +0000 (11:25 -0700)
Over the last three years the afsd_service sources have been
gradually separated into distinct layers for the SMB server
and the AFS cache.  The eventual goal of this work was to
permit the addition of alternative interfaces to the cache
manager in parallel.

This patchset implements the first alternative interface,
a reverse ioctl model that communicates with a native IFS
redirector driver.  The driver will be submitted in a
subsequent patchset.

Although it is possible to run afsd_service with both the
SMB and RDR interfaces active at the same time.  In practice
it is somewhat impractical because it destroys the uniformity
of the \\AFS name space.  The RDR loads at boot time and claims
all of \\AFS.  The SMB interface if active at the same time
must use the old \\%HOSTNAME%-AFS.  As implemented, if the RDR
interface is functional the SMB interface is not started.  Only
if the RDR interface fails will the SMB interface be activated.

The afsd_service.exe maintains all of its primary responsibilities
for communicating with the AFS servers, processing callbacks,
enforcing permissions, handling afs path ioctls, Windows RPC
service simulation, and object management.  The biggest change
is in the cm_buf_t management.  Data is exchanged with the
RDR by passing control over cm_buf_t->data buffers in the form
of Windows File Extents.  This avoids data copies across a
communication channel which significantly improves performance
at a substantial complexity cost.

Credential management is switched from a Windows username binding
to a GUID binding where the GUIDs represent authentication groups
that are managed by the RDR.

This patchset includes additional changes to support integrated
logon in conjunction with the RDR.  In particular, adding support
for authentication groups.

Change-Id: I7135489421c67a429ec3b2acd4c8ae08b8329f6d
Reviewed-on: http://gerrit.openafs.org/5432
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Tested-by: Rod Widdowson <rdw@steadingsoftware.com>
Reviewed-by: Jeffrey Altman <jaltman@openafs.org>
Tested-by: Jeffrey Altman <jaltman@openafs.org>

42 files changed:
src/WINNT/afsd/NTMakefile
src/WINNT/afsd/afsd.h
src/WINNT/afsd/afsd_init.c
src/WINNT/afsd/afsd_service.c
src/WINNT/afsd/afslogon.c
src/WINNT/afsd/afslogon.h
src/WINNT/afsd/cm.h
src/WINNT/afsd/cm_aclent.c
src/WINNT/afsd/cm_buf.c
src/WINNT/afsd/cm_buf.h
src/WINNT/afsd/cm_callback.c
src/WINNT/afsd/cm_callback.h
src/WINNT/afsd/cm_conn.c
src/WINNT/afsd/cm_conn.h
src/WINNT/afsd/cm_daemon.c
src/WINNT/afsd/cm_dcache.c
src/WINNT/afsd/cm_freelance.c
src/WINNT/afsd/cm_ioctl.c
src/WINNT/afsd/cm_ioctl.h
src/WINNT/afsd/cm_memmap.c
src/WINNT/afsd/cm_memmap.h
src/WINNT/afsd/cm_performance.c
src/WINNT/afsd/cm_rdr.h [new file with mode: 0644]
src/WINNT/afsd/cm_scache.c
src/WINNT/afsd/cm_scache.h
src/WINNT/afsd/cm_server.c
src/WINNT/afsd/cm_vnodeops.c
src/WINNT/afsd/cm_vnodeops.h
src/WINNT/afsd/cm_volstat.c
src/WINNT/afsd/cm_volstat.h
src/WINNT/afsd/cm_volume.c
src/WINNT/afsd/fs.c
src/WINNT/afsd/logon_ad.cpp
src/WINNT/afsd/smb.c
src/WINNT/afsd/smb3.c
src/WINNT/afsrdr/user/RDRFunction.c [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRInit.cpp [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRIoctl.c [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRIoctl.h [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRPipe.c [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRPipe.h [new file with mode: 0644]
src/WINNT/afsrdr/user/RDRPrototypes.h [new file with mode: 0644]

index 77b0a0c..525fcbb 100644 (file)
@@ -7,7 +7,7 @@
 
 AFSDEV_AUXCDEFINES = $(AFSDEV_AUXCDEFINES) /D"_AFXDLL" /DSMB_UNICODE -I..\kfw\inc\loadfuncs \
         -I..\kfw\inc\krb5 -I..\kfw\inc\leash -I$(DESTDIR)\include\afs \
-        -I$(DESTDIR)\include\rx
+        -I$(DESTDIR)\include\rx -I..\afsrdr\common -I..\afsrdr\user
 AFSDEV_NETGUI = 1
 RELDIR=WINNT\afsd
 !INCLUDE ..\..\config\NTMakefile.$(SYS_NAME)
@@ -153,10 +153,26 @@ AFSDOBJS=\
        $(OUT)\ms-wkssvc_s.obj \
        $(OUT)\rpc_wkssvc.obj \
        $(OUT)\rpc_srvsvc.obj \
-        $(OUT)\AFS_component_version_number.obj
+        $(OUT)\AFS_component_version_number.obj \
+       $(OUT)\RDRInit.obj \
+       $(OUT)\RDRFunction.obj \
+        $(OUT)\RDRIoctl.obj \
+        $(OUT)\RDRPipe.obj
 
 $(AFSDOBJS):
 
+$(OUT)\RDRInit.obj: ..\afsrdr\user\RDRInit.cpp ..\afsrdr\user\RDRPrototypes.h ..\afsrdr\user\RDRIoctl.h
+       $(CPP2OBJ) ..\afsrdr\user\RDRInit.cpp
+
+$(OUT)\RDRFunction.obj: ..\afsrdr\user\RDRFunction.c ..\afsrdr\user\RDRPrototypes.h ..\afsrdr\user\RDRIoctl.h
+       $(C2OBJ)  ..\afsrdr\user\RDRFunction.c
+
+$(OUT)\RDRIoctl.obj: ..\afsrdr\user\RDRIoctl.c ..\afsrdr\user\RDRIoctl.h
+       $(C2OBJ)  ..\afsrdr\user\RDRIoctl.c
+
+$(OUT)\RDRPipe.obj: ..\afsrdr\user\RDRPipe.c ..\afsrdr\user\RDRPipe.h
+       $(C2OBJ)  ..\afsrdr\user\RDRPipe.c
+
 $(OUT)\cm_conn.obj: cm_conn.c
        $(C2OBJ) -DAFS_PTHREAD_ENV /Fo$@ $**
 
@@ -317,7 +333,8 @@ LOGON_DLLSDKLIBS =\
        activeds.lib \
        user32.lib \
         userenv.lib \
-        shell32.lib
+        shell32.lib \
+        rpcrt4.lib
 
 $(LOGON_DLLFILE): $(LOGON_DLLOBJS) $(LOGON_DLLLIBS)
        $(DLLGUILINK) $(LOGONLINKFLAGS) -def:afslogon.def $(LOGON_DLLSDKLIBS)
@@ -450,12 +467,14 @@ AFSD_EXELIBS =\
        $(DESTDIR)\lib\afs\mtafsvldb.lib \
        $(DESTDIR)\lib\afs\mtafsint.lib \
        $(DESTDIR)\lib\afsrpc.lib \
+        $(DESTDIR)\lib\afsrxkad.lib \
        $(DESTDIR)\lib\afs\mtafsutil.lib \
        $(DESTDIR)\lib\afsauthent.lib \
        $(DESTDIR)\lib\libafsconf.lib \
        $(DESTDIR)\lib\afs\afsreg.lib \
        $(DESTDIR)\lib\afspthread.lib \
        $(DESTDIR)\lib\afsroken.lib \
+        $(DESTDIR)\lib\afshcrypto.lib \
         $(LANAHELPERLIB)
 
 $(AFSD_EXEFILE): $(OUT)\afsd.obj $(AFSDOBJS) $(OUT)\afsd.res  $(RXOBJS) $(AFSD_EXELIBS)
index 00ffa12..516e22c 100644 (file)
@@ -60,6 +60,7 @@ BOOL APIENTRY About(HWND, unsigned int, unsigned int, long);
 #include "cm_memmap.h"
 #include "cm_freelance.h"
 #include "cm_performance.h"
+#include "cm_rdr.h"
 #include "afsd_init.h"
 #include "afsd_eventlog.h"
 
index 3db3f32..dcf1004 100644 (file)
@@ -103,9 +103,10 @@ BOOL reportSessionStartups = FALSE;
 
 cm_initparams_v1 cm_initParams;
 
-clientchar_t *cm_sysName = 0;
 unsigned int  cm_sysNameCount = 0;
 clientchar_t *cm_sysNameList[MAXNUMSYSNAMES];
+unsigned int  cm_sysName64Count = 0;
+clientchar_t *cm_sysName64List[MAXNUMSYSNAMES];
 
 DWORD TraceOption = 0;
 
@@ -917,12 +918,15 @@ afsd_InitCM(char **reasonP)
     for ( i=0; i < MAXNUMSYSNAMES; i++ ) {
         cm_sysNameList[i] = osi_Alloc(MAXSYSNAME * sizeof(clientchar_t));
         cm_sysNameList[i][0] = '\0';
+        cm_sysName64List[i] = osi_Alloc(MAXSYSNAME * sizeof(clientchar_t));
+        cm_sysName64List[i][0] = '\0';
     }
-    cm_sysName = cm_sysNameList[0];
 
+    /* Process SysName lists from the registry */
     {
         clientchar_t *p, *q;
         clientchar_t * cbuf = (clientchar_t *) buf;
+
         dummyLen = sizeof(buf);
         code = RegQueryValueExW(parmKey, L"SysName", NULL, NULL, (LPBYTE) cbuf, &dummyLen);
         if (code != ERROR_SUCCESS || !cbuf[0]) {
@@ -934,7 +938,7 @@ afsd_InitCM(char **reasonP)
             cm_ClientStrCpy(cbuf, lengthof(buf), _C("x86_win32 i386_w2k i386_nt40"));
 #endif
         }
-        afsi_log("Sys name %S", cbuf);
+        afsi_log("Sys name list: %S", cbuf);
 
         /* breakup buf into individual search string entries */
         for (p = q = cbuf; p < cbuf + dummyLen; p++) {
@@ -944,16 +948,49 @@ afsd_InitCM(char **reasonP)
                 cm_sysNameCount++;
                 do {
                     if (*p == '\0')
-                        goto done_sysname;
+                        goto done_sysname32;
                     p++;
                 } while (*p == '\0' || isspace(*p));
                 q = p;
                 p--;
             }
         }
+      done_sysname32:
+        ;
+
+#ifdef _WIN64
+        /*
+         * If there is a 64-bit list, process it.  Otherwise, we will leave
+         * it undefined which implies that the 32-bit list be used for both.
+         * The 64-bit list is only used for the native file system driver.
+         * The SMB redirector interface does not provide any means of indicating
+         * the source of the request.
+         */
+        dummyLen = sizeof(buf);
+        code = RegQueryValueExW(parmKey, L"SysName64", NULL, NULL, (LPBYTE) cbuf, &dummyLen);
+        if (code == ERROR_SUCCESS && cbuf[0]) {
+            afsi_log("Sys name 64 list: %S", cbuf);
+
+            /* breakup buf into individual search string entries */
+            for (p = q = cbuf; p < cbuf + dummyLen; p++) {
+                if (*p == '\0' || iswspace(*p)) {
+                    memcpy(cm_sysName64List[cm_sysName64Count],q,(p-q) * sizeof(clientchar_t));
+                    cm_sysName64List[cm_sysName64Count][p-q] = '\0';
+                    cm_sysName64Count++;
+                    do {
+                        if (*p == '\0')
+                            goto done_sysname64;
+                        p++;
+                    } while (*p == '\0' || isspace(*p));
+                    q = p;
+                    p--;
+                }
+            }
+        }
+      done_sysname64:
+        ;
+#endif
     }
-  done_sysname:
-    cm_ClientStrCpy(cm_sysName, MAXSYSNAME, cm_sysNameList[0]);
 
     dummyLen = sizeof(cryptall);
     code = RegQueryValueEx(parmKey, "SecurityLevel", NULL, NULL,
@@ -1527,11 +1564,11 @@ int afsd_InitSMB(char **reasonP, void *aMBfunc)
     }
 
     if ( smb_Enabled ) {
-    /* Do this last so that we don't handle requests before init is done.
-     * Here we initialize the SMB listener.
-     */
-    smb_Init(afsd_logp, smb_UseV3, numSvThreads, aMBfunc);
-    afsi_log("smb_Init complete");
+        /* Do this last so that we don't handle requests before init is done.
+         * Here we initialize the SMB listener.
+         */
+        smb_Init(afsd_logp, smb_UseV3, numSvThreads, aMBfunc);
+        afsi_log("smb_Init complete");
     } else {
         afsi_log("smb_Init skipped");
     }
index 873e6d0..532f956 100644 (file)
@@ -1327,6 +1327,21 @@ afsd_Main(DWORD argc, LPTSTR *argv)
         /* Notify any volume status handlers that the cache manager has started */
         cm_VolStatus_Service_Started();
 
+        code = RDR_Initialize();
+        RDR_Initialized = !code;
+        afsi_log("RDR_Initialize returned: (code = %d)", code);
+
+        if (RDR_Initialized) {
+            if (cm_sysNameCount)
+                RDR_SysName( AFS_SYSNAME_ARCH_32BIT, cm_sysNameCount, cm_sysNameList );
+#ifdef _WIN64
+            if (cm_sysName64Count)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysName64Count, cm_sysName64List );
+            else if (cm_sysNameCount)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysNameCount, cm_sysNameList );
+#endif
+        }
+
         /*
          * Set the default for the SMB interface based upon the state of the
          * Redirector interface.
@@ -1464,11 +1479,15 @@ afsd_Main(DWORD argc, LPTSTR *argv)
     DismountGlobalDrives();
     afsi_log("Global Drives dismounted");
 
+    if (RDR_Initialized) {
+        RDR_ShutdownNotify();
+        cm_VolStatus_SetRDRNotifications(FALSE);
+        afsi_log("RDR notified of shutdown");
+    }
+
     smb_Shutdown();
     afsi_log("smb shutdown complete");
 
-    RpcShutdown();
-
     cm_ReleaseAllLocks();
 
     cm_DaemonShutdown();
@@ -1479,8 +1498,15 @@ afsd_Main(DWORD argc, LPTSTR *argv)
 
     afsd_ShutdownCM();
 
+    RpcShutdown();
+
     cm_ShutdownMappedMemory();
 
+    if (RDR_Initialized) {
+        RDR_ShutdownFinal();
+        afsi_log("RDR shutdown complete");
+    }
+
     rx_Finalize();
     afsi_log("rx finalization complete");
 
index ccac524..38f7499 100644 (file)
 #include <afs/param.h>
 #include <roken.h>
 
-#include "afslogon.h"
-
 #include <io.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
 
 #include <winsock2.h>
+#include <winioctl.h>
+#define SECURITY_WIN32
+#include <sspi.h>
 #include <lm.h>
 #include <nb30.h>
+#include <sddl.h>
+
+#include "afslogon.h"
 
 #include <afs/stds.h>
 #include <afs/pioctl_nt.h>
@@ -810,7 +814,8 @@ UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOu
     return FALSE;
 }  // UnicodeStringToANSI
 
-DWORD APIENTRY NPLogonNotify(
+DWORD APIENTRY
+NPLogonNotify(
        PLUID lpLogonId,
        LPCWSTR lpAuthentInfoType,
        LPVOID lpAuthentInfo,
@@ -850,6 +855,8 @@ DWORD APIENTRY NPLogonNotify(
     int retryInterval;
     int sleepInterval;
 
+    CtxtHandle LogonContext;
+
     /* Are we interactive? */
     interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);
 
@@ -952,7 +959,7 @@ DWORD APIENTRY NPLogonNotify(
         /* Get cell name if doing integrated logon.
            We might overwrite this if we are logging into an AD realm and we find out that
            the user's home dir is in some other cell. */
-        DebugEvent("About to call cm_GetRootCellName()");
+        DebugEvent0("About to call cm_GetRootCellName()");
         code = cm_GetRootCellName(cell);
         if (code < 0) {
             DebugEvent0("Unable to obtain Root Cell");
@@ -972,7 +979,8 @@ DWORD APIENTRY NPLogonNotify(
         }
     }
 
-    /* loop until AFS is started. */
+    AFSCreatePAG(lpLogonId);
+
     if (afsWillAutoStart) {
         /*
          * If the service is configured for auto start but hasn't started yet,
@@ -981,12 +989,12 @@ DWORD APIENTRY NPLogonNotify(
         if (!(IsServiceRunning() || IsServiceStartPending()))
             StartTheService();
 
+        /* loop until AFS is started or fails. */
         while ( IsServiceStartPending() ) {
             Sleep(10);
         }
 
         while (IsServiceRunning() && code != KTC_NOCM && code != KTC_NOCMRPC && code != KTC_NOCELL) {
-
             DebugEvent("while(autostart) LogonOption[%x], Service AutoStart[%d]",
                        opt.LogonOption,afsWillAutoStart);
 
@@ -1004,6 +1012,9 @@ DWORD APIENTRY NPLogonNotify(
            /* if Integrated Logon  */
            if (ISLOGONINTEGRATED(opt.LogonOption))
            {
+                LogonSSP(lpLogonId, &LogonContext);
+                ImpersonateSecurityContext(&LogonContext);
+
                if ( KFW_is_available() ) {
                    SetEnvironmentVariable(DO_NOT_REGISTER_VARNAME, "");
                     if (opt.realm) {
@@ -1077,6 +1088,10 @@ DWORD APIENTRY NPLogonNotify(
                    DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2 Code[%x] uname[%s] smbname=[%s] Cell[%s] PwExp=[%d] Reason=[%s]",
                                code,uname,opt.smbName,cell,pw_exp,reason?reason:"");
                }
+
+                RevertSecurityContext(&LogonContext);
+                DeleteSecurityContext(&LogonContext);
+
                if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && !lowercased_name ) {
                    for ( ctemp = uname; *ctemp ; ctemp++) {
                        *ctemp = tolower(*ctemp);
@@ -1128,8 +1143,8 @@ DWORD APIENTRY NPLogonNotify(
            Sleep(sleepInterval * 1000);
            retryInterval -= sleepInterval;
        }
+        DebugEvent0("while loop exited");
     }
-    DebugEvent0("while loop exited");
 
     /* remove any kerberos 5 tickets currently held by the SYSTEM account
      * for this user
index 44a0bae..bbac07d 100644 (file)
@@ -26,7 +26,6 @@ SOFTWARE.
 /* We only support VC 1200 and above anyway */
 #pragma once
 
-#include <windows.h>
 #include <objbase.h>
 #include <npapi.h>
 #if (_WIN32_WINNT < 0x0501)
@@ -132,6 +131,10 @@ DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonO
 DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWSTR domain);
 BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain);
 
+void AFSCreatePAG(PLUID lpLogonId);
+
+DWORD LogonSSP(PLUID lpLogonId, PCtxtHandle outCtx);
+
 #ifdef __cplusplus
 }
 #endif
index e80848c..c55c19a 100644 (file)
@@ -45,6 +45,7 @@
 
 #define LOCK_HIERARCHY_IGNORE                    0
 
+#define LOCK_HIERARCHY_RDR_GLOBAL               10
 #define LOCK_HIERARCHY_SMB_STARTED              20
 #define LOCK_HIERARCHY_SMB_LISTENER             30
 #define LOCK_HIERARCHY_SMB_DIRWATCH             40
@@ -87,5 +88,8 @@
 #define LOCK_HIERARCHY_AFSDBSBMT_GLOBAL       1000
 #define LOCK_HIERARCHY_TOKEN_EVENT_GLOBAL     2000
 #define LOCK_HIERARCHY_SYSCFG_GLOBAL          3000
+
+#define LOCK_HIERARCHY_RDR_EXTENTS               0
 #endif /*  OPENAFS_WINNT_AFSD_CM_H */
 
+
index 65e3563..88f8829 100644 (file)
@@ -338,6 +338,9 @@ void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
             aclp->userp = NULL;
             aclp->backp = (struct cm_scache *) 0;
             found = 1;
+            if (RDR_Initialized && cm_HaveCallback(scp))
+                RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                     scp->fid.hash, scp->fileType, AFS_INVALIDATE_CREDS);
             break;
         }
     }
@@ -351,6 +354,7 @@ void cm_InvalidateACLUser(cm_scache_t *scp, cm_user_t *userp)
 void
 cm_ResetACLCache(cm_cell_t *cellp, cm_user_t *userp)
 {
+    cm_volume_t *volp, *nextVolp;
     cm_scache_t *scp, *nextScp;
     afs_uint32 hash;
 
@@ -369,6 +373,40 @@ cm_ResetACLCache(cm_cell_t *cellp, cm_user_t *userp)
         }
     }
     lock_ReleaseRead(&cm_scacheLock);
+
+    if (RDR_Initialized) {
+        lock_ObtainRead(&cm_volumeLock);
+        for (hash = 0; hash < cm_data.volumeHashTableSize; hash++) {
+            for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[RWVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[RWVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[RWVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+            for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[ROVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[ROVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[ROVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+            for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = nextVolp) {
+                nextVolp = volp->vol[BACKVOL].nextp;
+                if ((cellp == NULL || cellp->cellID == volp->cellp->cellID) &&
+                    volp->vol[BACKVOL].ID) {
+                    lock_ReleaseRead(&cm_volumeLock);
+                    RDR_InvalidateVolume(volp->cellp->cellID, volp->vol[BACKVOL].ID, AFS_INVALIDATE_CREDS);
+                    lock_ObtainRead(&cm_volumeLock);
+                }
+            }
+        }
+        lock_ReleaseRead(&cm_volumeLock);
+    }
 }
 
 
index 73d64f8..d34281e 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <strsafe.h>
 #include <math.h>
+#include <hcrypto\md5.h>
 
 #include "afsd.h"
 #include "cm_memmap.h"
@@ -60,6 +61,10 @@ osi_log_t *buf_logp = NULL;
 /* Global lock protecting hash tables and free lists */
 osi_rwlock_t buf_globalLock;
 
+/* Global lock used to limit the number of RDR Release
+ * Extents requests to one. */
+osi_mutex_t buf_rdrReleaseExtentsLock;
+
 /* ptr to head of the free list (most recently used) and the
  * tail (the guy to remove first).  We use osi_Q* functions
  * to put stuff in buf_freeListp, and maintain the end
@@ -163,11 +168,12 @@ void buf_ReleaseLocked(cm_buf_t *bp, afs_uint32 writeLocked)
             lock_ConvertRToW(&buf_globalLock);
 
         if (bp->refCount == 0 &&
-            !(bp->qFlags & CM_BUF_QINLRU)) {
+            !(bp->qFlags & (CM_BUF_QINLRU|CM_BUF_QREDIR))) {
             osi_QAddH( (osi_queue_t **) &cm_data.buf_freeListp,
                        (osi_queue_t **) &cm_data.buf_freeListEndp,
                        &bp->q);
             _InterlockedOr(&bp->qFlags, CM_BUF_QINLRU);
+            buf_IncrementFreeCount();
         }
 
         if (!writeLocked)
@@ -201,11 +207,12 @@ void buf_Release(cm_buf_t *bp)
     if (refCount == 0) {
         lock_ObtainWrite(&buf_globalLock);
         if (bp->refCount == 0 &&
-            !(bp->qFlags & CM_BUF_QINLRU)) {
+            !(bp->qFlags & (CM_BUF_QINLRU|CM_BUF_QREDIR))) {
             osi_QAddH( (osi_queue_t **) &cm_data.buf_freeListp,
                        (osi_queue_t **) &cm_data.buf_freeListEndp,
                        &bp->q);
             _InterlockedOr(&bp->qFlags, CM_BUF_QINLRU);
+            buf_IncrementFreeCount();
         }
         lock_ReleaseWrite(&buf_globalLock);
     }
@@ -224,13 +231,26 @@ buf_Sync(int quitOnShutdown)
         if (quitOnShutdown && buf_ShutdownFlag)
             break;
 
+        /*
+         * If the buffer is held be the redirector we must fetch
+         * it back in order to determine whether or not it is in
+         * fact dirty.
+         */
+        if (bp->qFlags & CM_BUF_QREDIR) {
+            osi_Log1(buf_logp,"buf_Sync buffer held by redirector bp 0x%p", bp);
+
+            /* Request single buffer from the redirector */
+            buf_RDRShakeAnExtentFree(bp, &req);
+        }
+
         lock_ReleaseRead(&buf_globalLock);
-        /* all dirty buffers are held when they are added to the
-        * dirty list.  No need for an additional hold.
-        */
+        /*
+         * all dirty buffers are held when they are added to the
+         * dirty list.  No need for an additional hold.
+         */
         lock_ObtainMutex(&bp->mx);
 
-        if (bp->flags & CM_BUF_DIRTY && !(bp->qFlags & CM_BUF_QREDIR)) {
+        if ((bp->flags & CM_BUF_DIRTY)) {
             /* start cleaning the buffer; don't touch log pages since
              * the log code counts on knowing exactly who is writing
              * a log page at any given instant.
@@ -339,7 +359,7 @@ long
 buf_ValidateBuffers(void)
 {
     cm_buf_t * bp, *bpf, *bpa, *bpb;
-    afs_uint64 countb = 0, countf = 0, counta = 0;
+    afs_uint64 countb = 0, countf = 0, counta = 0, countr = 0;
 
     if (cm_data.buf_freeListp == NULL && cm_data.buf_freeListEndp != NULL ||
          cm_data.buf_freeListp != NULL && cm_data.buf_freeListEndp == NULL) {
@@ -380,6 +400,20 @@ buf_ValidateBuffers(void)
         }
     }
 
+    for ( bp = cm_data.buf_redirListp; bp; bp = (cm_buf_t *) osi_QNext(&bp->q)) {
+        if (!(bp->qFlags & CM_BUF_QREDIR)) {
+            afsi_log("CM_BUF_QREDIR not set on cm_buf_t in buf_redirListp");
+            fprintf(stderr, "CM_BUF_QREDIR not set on cm_buf_t in buf_redirListp");
+            return -9;
+        }
+        countr++;
+        if (countr > cm_data.buf_nbuffers) {
+            afsi_log("cm_ValidateBuffers failure: countr > cm_data.buf_nbuffers");
+            fprintf(stderr, "cm_ValidateBuffers failure: countr > cm_data.buf_nbuffers\n");
+            return -10;
+        }
+    }
+
     for (bp = cm_data.buf_allp; bp; bp=bp->allp) {
         if (bp->magic != CM_BUF_MAGIC) {
             afsi_log("cm_ValidateBuffers failure: bp->magic != CM_BUF_MAGIC");
@@ -447,6 +481,7 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
     if (osi_Once(&once)) {
         /* initialize global locks */
         lock_InitializeRWLock(&buf_globalLock, "Global buffer lock", LOCK_HIERARCHY_BUF_GLOBAL);
+        lock_InitializeMutex(&buf_rdrReleaseExtentsLock, "RDR Release Extents lock", LOCK_HIERARCHY_RDR_EXTENTS);
 
         if ( newFile ) {
             /* remember this for those who want to reset it */
@@ -483,6 +518,7 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
                            (osi_queue_t **) &cm_data.buf_freeListEndp,
                            &bp->q);
                 _InterlockedOr(&bp->qFlags, CM_BUF_QINLRU);
+                buf_IncrementFreeCount();
                 lock_InitializeMutex(&bp->mx, "Buffer mutex", LOCK_HIERARCHY_BUFFER);
 
                 /* grab appropriate number of bytes from aligned zone */
@@ -508,8 +544,49 @@ long buf_Init(int newFile, cm_buf_ops_t *opsp, afs_uint64 nbuffers)
                 bp->waitCount = 0;
                 bp->waitRequests = 0;
                 _InterlockedAnd(&bp->flags, ~CM_BUF_WAITING);
+                bp->error = 0;
+                if (bp->qFlags & CM_BUF_QREDIR) {
+                    /*
+                     * extent was not returned by the file system driver.
+                     * clean up the mess.
+                     */
+                    bp->dataVersion = CM_BUF_VERSION_BAD;
+                    _InterlockedAnd(&bp->qFlags, ~CM_BUF_QREDIR);
+                    osi_QRemoveHT( (osi_queue_t **) &cm_data.buf_redirListp,
+                                   (osi_queue_t **) &cm_data.buf_redirListEndp,
+                                   &bp->q);
+                    buf_DecrementRedirCount();
+                    bp->redirq.nextp = bp->redirq.prevp = NULL;
+                    bp->redirLastAccess = 0;
+                    bp->redirReleaseRequested = 0;
+                    buf_Release(bp);
+                }
                 bp++;
             }
+
+            /*
+             * There should be nothing left in cm_data.buf_redirListp
+             * but double check just to be sure.
+             */
+            for ( bp = cm_data.buf_redirListp;
+                  bp;
+                  bp = cm_data.buf_redirListp)
+            {
+                /*
+                 * extent was not returned by the file system driver.
+                 * clean up the mess.
+                 */
+                bp->dataVersion = CM_BUF_VERSION_BAD;
+                _InterlockedAnd(&bp->qFlags, ~CM_BUF_QREDIR);
+                osi_QRemoveHT( (osi_queue_t **) &cm_data.buf_redirListp,
+                               (osi_queue_t **) &cm_data.buf_redirListEndp,
+                               &bp->q);
+                buf_DecrementRedirCount();
+                bp->redirq.nextp = bp->redirq.prevp = NULL;
+                bp->redirLastAccess = 0;
+                bp->redirReleaseRequested = 0;
+                buf_Release(bp);
+            }
         }
 
 #ifdef TESTING
@@ -868,6 +945,8 @@ void buf_Recycle(cm_buf_t *bp)
 
     osi_assertx(bp->magic == CM_BUF_MAGIC, "invalid cm_buf_t magic");
 
+    osi_assertx(!(bp->qFlags & CM_BUF_QREDIR), "can't recycle redir held buffers");
+
     /* if we get here, we know that the buffer still has a 0 ref count,
      * and that it is clean and has no currently pending I/O.  This is
      * the dude to return.
@@ -924,6 +1003,253 @@ void buf_Recycle(cm_buf_t *bp)
     bp->dataVersion = CM_BUF_VERSION_BAD;      /* unknown so far */
 }
 
+
+/*
+ * buf_RDRShakeAnExtentFree
+ * called with buf_globalLock read locked
+ */
+afs_uint32
+buf_RDRShakeAnExtentFree(cm_buf_t *rbp, cm_req_t *reqp)
+{
+    afs_uint32 code = 0;
+    LARGE_INTEGER heldExtents = {0,0};
+    AFSFileExtentCB extentList[1];
+    DWORD extentCount = 0;
+    BOOL locked = FALSE;
+
+    if (!(rbp->qFlags & CM_BUF_QREDIR))
+        return 0;
+
+    lock_ReleaseRead(&buf_globalLock);
+
+    if (!lock_TryMutex(&buf_rdrReleaseExtentsLock)) {
+        osi_Log0(afsd_logp, "Waiting for prior RDR_RequestExtentRelease request to complete");
+        if (reqp->flags & CM_REQ_NORETRY) {
+            code = CM_ERROR_WOULDBLOCK;
+            goto done;
+        }
+
+        lock_ObtainMutex(&buf_rdrReleaseExtentsLock);
+    }
+
+    extentList[0].Flags = 0;
+    extentList[0].Length = cm_data.blockSize;
+    extentList[0].FileOffset.QuadPart = rbp->offset.QuadPart;
+    extentList[0].CacheOffset.QuadPart = rbp->datap - cm_data.baseAddress;
+    extentCount = 1;
+
+    code = RDR_RequestExtentRelease(&rbp->fid, heldExtents, extentCount, extentList);
+
+    lock_ReleaseMutex(&buf_rdrReleaseExtentsLock);
+
+  done:
+    lock_ObtainRead(&buf_globalLock);
+    return code;
+}
+
+/*
+ * buf_RDRShakeFileExtentsFree
+ * requests all extents held by the redirector to be returned for
+ * the specified cm_scache_t.  This function is called with no
+ * locks held.
+ */
+afs_uint32
+buf_RDRShakeFileExtentsFree(cm_scache_t *rscp, cm_req_t *reqp)
+{
+    afs_uint32 code = 0;
+    afs_uint64 n_redir = 0;
+
+    if (!lock_TryMutex(&buf_rdrReleaseExtentsLock)) {
+        osi_Log0(afsd_logp, "Waiting for prior RDR_RequestExtentRelease request to complete");
+        if (reqp->flags & CM_REQ_NORETRY)
+            return CM_ERROR_WOULDBLOCK;
+
+        lock_ObtainMutex(&buf_rdrReleaseExtentsLock);
+    }
+
+    for ( code = CM_ERROR_RETRY; code == CM_ERROR_RETRY; ) {
+        LARGE_INTEGER heldExtents = {0,0};
+        AFSFileExtentCB extentList[1024];
+        DWORD extentCount = 0;
+        cm_buf_t *srbp;
+        time_t now;
+
+        /* only retry if a call to RDR_RequestExtentRelease says to */
+        code = 0;
+        lock_ObtainWrite(&buf_globalLock);
+
+        if (rscp->redirBufCount == 0)
+        {
+            lock_ReleaseWrite(&buf_globalLock);
+            break;
+        }
+
+        time(&now);
+        for ( srbp = redirq_to_cm_buf_t(rscp->redirQueueT);
+              srbp;
+              srbp = ((code == 0 && extentCount == 0) ? redirq_to_cm_buf_t(rscp->redirQueueT) :
+                       redirq_to_cm_buf_t(osi_QPrev(&srbp->redirq))))
+        {
+            extentList[extentCount].Flags = 0;
+            extentList[extentCount].Length = cm_data.blockSize;
+            extentList[extentCount].FileOffset.QuadPart = srbp->offset.QuadPart;
+            extentList[extentCount].CacheOffset.QuadPart = srbp->datap - cm_data.baseAddress;
+            srbp->redirReleaseRequested = now;
+            extentCount++;
+
+            if (extentCount == 1024) {
+                lock_ReleaseWrite(&buf_globalLock);
+                heldExtents.QuadPart = cm_data.buf_redirCount;
+                code = RDR_RequestExtentRelease(&rscp->fid, heldExtents, extentCount, extentList);
+                if (code) {
+                    if (code == CM_ERROR_RETRY) {
+                        /*
+                         * The redirector either is not holding the extents or cannot let them
+                         * go because they are otherwise in use.  At the moment, do nothing.
+                         */
+                    } else
+                        break;
+                }
+                extentCount = 0;
+                lock_ObtainWrite(&buf_globalLock);
+            }
+        }
+        lock_ReleaseWrite(&buf_globalLock);
+
+        if (code == 0 && extentCount > 0) {
+            heldExtents.QuadPart = cm_data.buf_redirCount;
+            code = RDR_RequestExtentRelease(&rscp->fid, heldExtents, extentCount, extentList);
+        }
+
+        if ((code == CM_ERROR_RETRY) && (reqp->flags & CM_REQ_NORETRY)) {
+            code = CM_ERROR_WOULDBLOCK;
+            break;
+        }
+    }
+    lock_ReleaseMutex(&buf_rdrReleaseExtentsLock);
+    return code;
+}
+
+afs_uint32
+buf_RDRShakeSomeExtentsFree(cm_req_t *reqp, afs_uint32 oneFid, afs_uint32 minage)
+{
+    afs_uint32 code = 0;
+
+    if (!lock_TryMutex(&buf_rdrReleaseExtentsLock)) {
+        if (reqp->flags & CM_REQ_NORETRY)
+            return CM_ERROR_WOULDBLOCK;
+
+        osi_Log0(afsd_logp, "Waiting for prior RDR_RequestExtentRelease request to complete");
+        lock_ObtainMutex(&buf_rdrReleaseExtentsLock);
+    }
+
+    for ( code = CM_ERROR_RETRY; code == CM_ERROR_RETRY; ) {
+        LARGE_INTEGER heldExtents;
+        AFSFileExtentCB extentList[1024];
+        DWORD extentCount = 0;
+        cm_buf_t *rbp, *srbp;
+        cm_scache_t *rscp;
+        time_t now;
+        BOOL locked = FALSE;
+
+        /* only retry if a call to RDR_RequestExtentRelease says to */
+        code = 0;
+        lock_ObtainWrite(&buf_globalLock);
+        locked = TRUE;
+
+        for ( rbp = cm_data.buf_redirListEndp;
+              code == 0 && rbp && (!oneFid || extentCount == 0);
+              rbp = (cm_buf_t *) osi_QPrev(&rbp->q))
+        {
+            if (!oneFid)
+                extentCount = 0;
+
+            if (rbp->redirLastAccess >= rbp->redirReleaseRequested) {
+                rscp = cm_FindSCache(&rbp->fid);
+                if (!rscp)
+                    continue;
+
+                time(&now);
+                for ( srbp = redirq_to_cm_buf_t(rscp->redirQueueT);
+                      srbp && extentCount < 1024;
+                      srbp = redirq_to_cm_buf_t(osi_QPrev(&srbp->redirq)))
+                {
+                    /*
+                     * Do not request a release if we have already done so
+                     * or if the extent was delivered to windows less than
+                     * 'minage' seconds ago.
+                     */
+                    if (srbp->redirLastAccess >= srbp->redirReleaseRequested &&
+                         srbp->redirLastAccess < now - minage) {
+                        extentList[extentCount].Flags = 0;
+                        extentList[extentCount].Length = cm_data.blockSize;
+                        extentList[extentCount].FileOffset.QuadPart = srbp->offset.QuadPart;
+                        extentList[extentCount].CacheOffset.QuadPart = srbp->datap - cm_data.baseAddress;
+                        srbp->redirReleaseRequested = now;
+                        extentCount++;
+                    }
+                }
+                cm_ReleaseSCache(rscp);
+            }
+
+            if ( !oneFid && extentCount > 0) {
+                if (locked) {
+                    lock_ReleaseWrite(&buf_globalLock);
+                    locked = FALSE;
+                }
+                heldExtents.QuadPart = cm_data.buf_redirCount;
+                code = RDR_RequestExtentRelease(&rbp->fid, heldExtents, extentCount, extentList);
+            }
+            if (!locked) {
+                lock_ObtainWrite(&buf_globalLock);
+                locked = TRUE;
+            }
+        }
+        if (locked)
+            lock_ReleaseWrite(&buf_globalLock);
+        if (code == 0) {
+            if (oneFid) {
+                heldExtents.QuadPart = cm_data.buf_redirCount;
+                if (rbp && extentCount)
+                    code = RDR_RequestExtentRelease(&rbp->fid, heldExtents, extentCount, extentList);
+                else
+                    code = RDR_RequestExtentRelease(NULL, heldExtents, 1024, NULL);
+            } else {
+                code = 0;
+            }
+        }
+
+        if ((code == CM_ERROR_RETRY) && (reqp->flags & CM_REQ_NORETRY)) {
+            code = CM_ERROR_WOULDBLOCK;
+            break;
+        }
+    }
+    lock_ReleaseMutex(&buf_rdrReleaseExtentsLock);
+    return code;
+}
+
+/* returns 0 if the buffer does not exist, and non-0 if it does */
+static long
+buf_ExistsLocked(struct cm_scache *scp, osi_hyper_t *offsetp)
+{
+    cm_buf_t *bp;
+
+    if (bp = buf_FindLocked(&scp->fid, offsetp)) {
+        /* Do not call buf_ReleaseLocked() because we
+         * do not want to allow the buffer to be added
+         * to the free list.
+         */
+        afs_int32 refCount = InterlockedDecrement(&bp->refCount);
+#ifdef DEBUG_REFCOUNT
+        osi_Log2(afsd_logp,"buf_ExistsLocked bp 0x%p ref %d", bp, refCount);
+        afsi_log("%s:%d buf_ExistsLocked bp 0x%p, ref %d", __FILE__, __LINE__, bp, refCount);
+#endif
+        return CM_BUF_EXISTS;
+    }
+
+    return 0;
+}
+
 /* recycle a buffer, removing it from the free list, hashing in its new identity
  * and returning it write-locked so that no one can use it.  Called without
  * any locks held, and can return an error if it loses the race condition and
@@ -940,6 +1266,7 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
     cm_buf_t *bp;      /* buffer we're dealing with */
     cm_buf_t *nextBp;  /* next buffer in file hash chain */
     afs_uint32 i;      /* temp */
+    afs_uint64 n_bufs, n_nonzero, n_busy, n_dirty, n_own;
 
 #ifdef TESTING
     buf_ValidateBufQueues();
@@ -947,24 +1274,19 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
 
     while(1) {
       retry:
+        n_bufs = 0;
+        n_nonzero = 0;
+        n_own = 0;
+        n_busy = 0;
+        n_dirty = 0;
+
         lock_ObtainRead(&scp->bufCreateLock);
         lock_ObtainWrite(&buf_globalLock);
         /* check to see if we lost the race */
-        if (scp) {
-            if (bp = buf_FindLocked(&scp->fid, offsetp)) {
-               /* Do not call buf_ReleaseLocked() because we
-                * do not want to allow the buffer to be added
-                * to the free list.
-                */
-                afs_int32 refCount = InterlockedDecrement(&bp->refCount);
-#ifdef DEBUG_REFCOUNT
-                osi_Log2(afsd_logp,"buf_GetNewLocked bp 0x%p ref %d", bp, refCount);
-                afsi_log("%s:%d buf_GetNewLocked bp 0x%p, ref %d", __FILE__, __LINE__, bp, refCount);
-#endif
-                lock_ReleaseWrite(&buf_globalLock);
-                lock_ReleaseRead(&scp->bufCreateLock);
-                return CM_BUF_EXISTS;
-            }
+        if (buf_ExistsLocked(scp, offsetp)) {
+            lock_ReleaseWrite(&buf_globalLock);
+            lock_ReleaseRead(&scp->bufCreateLock);
+            return CM_BUF_EXISTS;
         }
 
        /* does this fix the problem below?  it's a simple solution. */
@@ -972,6 +1294,10 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
        {
            lock_ReleaseWrite(&buf_globalLock);
             lock_ReleaseRead(&scp->bufCreateLock);
+
+            if ( RDR_Initialized )
+                goto rdr_release;
+
            osi_Log0(afsd_logp, "buf_GetNewLocked: Free Buffer List is empty - sleeping 200ms");
            Sleep(200);
            goto retry;
@@ -989,28 +1315,30 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
          * starting cleaning I/O for those which are dirty.  If we find
          * a clean buffer, we rehash it, lock it and return it.
          */
-        for(bp = cm_data.buf_freeListEndp; bp; bp=(cm_buf_t *) osi_QPrev(&bp->q)) {
+        for (bp = cm_data.buf_freeListEndp; bp; bp=(cm_buf_t *) osi_QPrev(&bp->q)) {
+            n_bufs++;
+
             /* check to see if it really has zero ref count.  This
              * code can bump refcounts, at least, so it may not be
              * zero.
              */
-            if (bp->refCount > 0)
+            if (bp->refCount > 0) {
+                n_nonzero++;
                 continue;
+            }
 
             /* we don't have to lock buffer itself, since the ref
              * count is 0 and we know it will stay zero as long as
              * we hold the global lock.
              */
 
-            /* Don't recycle a buffer held by the redirector. */
-            if (bp->qFlags & CM_BUF_QREDIR)
-                continue;
-
             /* don't recycle someone in our own chunk */
             if (!cm_FidCmp(&bp->fid, &scp->fid)
                  && (bp->offset.LowPart & (-cm_chunkSize))
-                 == (offsetp->LowPart & (-cm_chunkSize)))
+                 == (offsetp->LowPart & (-cm_chunkSize))) {
+                n_own++;
                 continue;
+            }
 
             /* if this page is being filled (!) or cleaned, see if
              * the I/O has completed.  If not, skip it, otherwise
@@ -1021,10 +1349,17 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
                  * holding the big lock?  Watch for contention
                  * here.
                  */
+                n_busy++;
                 continue;
             }
 
             if (bp->flags & CM_BUF_DIRTY) {
+                n_dirty++;
+
+                /* leave the buffer alone if held by the redirector */
+                if (bp->qFlags & CM_BUF_QREDIR)
+                    continue;
+
                 /* if the buffer is dirty, start cleaning it and
                  * move on to the next buffer.  We do this with
                  * just the lock required to minimize contention
@@ -1047,9 +1382,28 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
 
                 /* now put it back and go around again */
                 buf_Release(bp);
-                goto retry;
+
+                /* but first obtain the locks we gave up
+                 * before the buf_CleanAsync() call */
+                lock_ObtainRead(&scp->bufCreateLock);
+                lock_ObtainWrite(&buf_globalLock);
+
+                /*
+                 * Since we dropped the locks we need to verify that
+                 * another thread has not allocated the buffer for us.
+                 */
+                if (buf_ExistsLocked(scp, offsetp)) {
+                    lock_ReleaseWrite(&buf_globalLock);
+                    lock_ReleaseRead(&scp->bufCreateLock);
+                    return CM_BUF_EXISTS;
+                }
+                continue;
             }
 
+            osi_Log3(afsd_logp, "buf_GetNewLocked: scp 0x%p examined %u buffers before recycling bufp 0x%p",
+                     scp, n_bufs, bp);
+            osi_Log4(afsd_logp, "... nonzero %u; own %u; busy %u; dirty %u", n_nonzero, n_own, n_busy, n_dirty);
+
             /* if we get here, we know that the buffer still has a 0
              * ref count, and that it is clean and has no currently
              * pending I/O.  This is the dude to return.
@@ -1093,6 +1447,7 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
                            (osi_queue_t **) &cm_data.buf_freeListEndp,
                            &bp->q);
             _InterlockedAnd(&bp->qFlags, ~CM_BUF_QINLRU);
+            buf_DecrementFreeCount();
 
             /* prepare to return it.  Give it a refcount */
             bp->refCount = 1;
@@ -1122,7 +1477,23 @@ long buf_GetNewLocked(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *req
         } /* for all buffers in lru queue */
         lock_ReleaseWrite(&buf_globalLock);
         lock_ReleaseRead(&scp->bufCreateLock);
-       osi_Log0(afsd_logp, "buf_GetNewLocked: Free Buffer List has no buffers with a zero refcount - sleeping 100ms");
+
+       osi_Log1(afsd_logp, "buf_GetNewLocked: Free Buffer List has %u buffers none free", n_bufs);
+        osi_Log4(afsd_logp, "... nonzero %u; own %u; busy %u; dirty %u", n_nonzero, n_own, n_busy, n_dirty);
+
+        if (RDR_Initialized) {
+            afs_uint32 code;
+          rdr_release:
+            code = buf_RDRShakeSomeExtentsFree(reqp, TRUE, 2 /* seconds */);
+            switch (code) {
+            case CM_ERROR_RETRY:
+            case 0:
+                goto retry;
+            case CM_ERROR_WOULDBLOCK:
+                return CM_ERROR_WOULDBLOCK;
+            }
+        }
+
        Sleep(100);             /* give some time for a buffer to be freed */
     }  /* while loop over everything */
     /* not reached */
@@ -1215,6 +1586,8 @@ long buf_Get(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *reqp, cm_buf
 
         if (code != 0) {
             /* failure or queued */
+
+            /* unless cm_BufRead() is altered, this path cannot be hit */
             if (code != ERROR_IO_PENDING) {
                 bp->error = code;
                 _InterlockedOr(&bp->flags, CM_BUF_ERROR);
@@ -1269,6 +1642,7 @@ long buf_Get(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *reqp, cm_buf
                        (osi_queue_t **) &cm_data.buf_freeListEndp,
                        &bp->q);
         _InterlockedAnd(&bp->qFlags, ~CM_BUF_QINLRU);
+        buf_DecrementFreeCount();
     }
     lock_ReleaseWrite(&buf_globalLock);
 
@@ -1280,30 +1654,6 @@ long buf_Get(struct cm_scache *scp, osi_hyper_t *offsetp, cm_req_t *reqp, cm_buf
     return 0;
 }
 
-/* count # of elements in the free list;
- * we don't bother doing the proper locking for accessing dataVersion or flags
- * since it is a pain, and this is really just an advisory call.  If you need
- * to do better at some point, rewrite this function.
- */
-long buf_CountFreeList(void)
-{
-    long count;
-    cm_buf_t *bufp;
-
-    count = 0;
-    lock_ObtainRead(&buf_globalLock);
-    for(bufp = cm_data.buf_freeListp; bufp; bufp = (cm_buf_t *) osi_QNext(&bufp->q)) {
-        /* if the buffer doesn't have an identity, or if the buffer
-         * has been invalidate (by having its DV stomped upon), then
-         * count it as free, since it isn't really being utilized.
-         */
-        if (!(bufp->qFlags & CM_BUF_QINHASH) || bufp->dataVersion == CM_BUF_VERSION_BAD)
-            count++;
-    }
-    lock_ReleaseRead(&buf_globalLock);
-    return count;
-}
-
 /* clean a buffer synchronously */
 afs_uint32 buf_CleanAsync(cm_scache_t *scp, cm_buf_t *bp, cm_req_t *reqp, afs_uint32 flags, afs_uint32 *pisdirty)
 {
@@ -1375,7 +1725,14 @@ void buf_SetDirty(cm_buf_t *bp, cm_req_t *reqp, afs_uint32 offset, afs_uint32 le
         bp->dirty_offset = offset;
         bp->dirty_length = length;
 
-        /* and add to the dirty list.
+        /*
+         * if the request is not from the afs redirector,
+         * add to the dirty list.  The redirector interface ensures
+         * that a background store operation is queued for each and
+         * every dirty extent that is released.  Therefore, the
+         * buf_IncrSyncer thread is not required to ensure that
+         * dirty buffers are written to the file server.
+         *
          * we obtain a hold on the buffer for as long as it remains
          * in the list.  buffers are only removed from the list by
          * the buf_IncrSyncer function regardless of when else the
@@ -1385,19 +1742,21 @@ void buf_SetDirty(cm_buf_t *bp, cm_req_t *reqp, afs_uint32 offset, afs_uint32 le
          * elsewhere, never add to the dirty list if the buffer is
          * already there.
          */
-        lock_ObtainWrite(&buf_globalLock);
-        if (!(bp->qFlags & CM_BUF_QINDL)) {
-            buf_HoldLocked(bp);
-            if (!cm_data.buf_dirtyListp) {
-                cm_data.buf_dirtyListp = cm_data.buf_dirtyListEndp = bp;
-            } else {
-                cm_data.buf_dirtyListEndp->dirtyp = bp;
-                cm_data.buf_dirtyListEndp = bp;
+        if (!(reqp->flags & CM_REQ_SOURCE_REDIR)) {
+            lock_ObtainWrite(&buf_globalLock);
+            if (!(bp->qFlags & CM_BUF_QINDL)) {
+                buf_HoldLocked(bp);
+                if (!cm_data.buf_dirtyListp) {
+                    cm_data.buf_dirtyListp = cm_data.buf_dirtyListEndp = bp;
+                } else {
+                    cm_data.buf_dirtyListEndp->dirtyp = bp;
+                    cm_data.buf_dirtyListEndp = bp;
+                }
+                bp->dirtyp = NULL;
+                _InterlockedOr(&bp->qFlags, CM_BUF_QINDL);
             }
-            bp->dirtyp = NULL;
-            _InterlockedOr(&bp->qFlags, CM_BUF_QINDL);
+            lock_ReleaseWrite(&buf_globalLock);
         }
-        lock_ReleaseWrite(&buf_globalLock);
     }
 
     /* and record the last writer */
@@ -1438,6 +1797,13 @@ long buf_CleanAndReset(void)
     lock_ObtainRead(&buf_globalLock);
     for(i=0; i<cm_data.buf_hashSize; i++) {
         for(bp = cm_data.buf_scacheHashTablepp[i]; bp; bp = bp->hashp) {
+            if (bp->qFlags & CM_BUF_QREDIR) {
+                osi_Log1(buf_logp,"buf_CleanAndReset buffer held by redirector bp 0x%p", bp);
+
+                /* Request single extent from the redirector */
+                buf_RDRShakeAnExtentFree(bp, &req);
+            }
+
             if ((bp->flags & CM_BUF_DIRTY) == CM_BUF_DIRTY) {
                 buf_HoldLocked(bp);
                 lock_ReleaseRead(&buf_globalLock);
@@ -1547,6 +1913,7 @@ long buf_Truncate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
 
     buf_HoldLocked(bufp);
     lock_ReleaseRead(&buf_globalLock);
+
     while (bufp) {
         lock_ObtainMutex(&bufp->mx);
 
@@ -1585,7 +1952,29 @@ long buf_Truncate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
              */
             if (LargeIntegerLessThanOrEqualTo(*sizep, bufp->offset)) {
                 /* truncating the entire page */
+                if (reqp->flags & CM_REQ_SOURCE_REDIR) {
+                    /*
+                     * Implicitly clear the redirector flag
+                     * and release the matching hold.
+                     */
+                    if (bufp->qFlags & CM_BUF_QREDIR) {
+                        osi_Log4(buf_logp,"buf_Truncate taking from file system bufp 0x%p vno 0x%x foffset 0x%x:%x",
+                                 bufp, bufp->fid.vnode, bufp->offset.HighPart, bufp->offset.LowPart);
+                        lock_ObtainWrite(&buf_globalLock);
+                        if (bufp->qFlags & CM_BUF_QREDIR) {
+                            buf_RemoveFromRedirQueue(scp, bufp);
+                            buf_ReleaseLocked(bufp, TRUE);
+                        }
+                        lock_ReleaseWrite(&buf_globalLock);
+                    }
+                } else {
+                    if (RDR_Initialized)
+                        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                             scp->fid.unique, scp->fid.hash,
+                                             scp->fileType, AFS_INVALIDATE_SMB);
+                }
                 _InterlockedAnd(&bufp->flags, ~CM_BUF_DIRTY);
+                bufp->error = 0;
                 bufp->dirty_offset = 0;
                 bufp->dirty_length = 0;
                 bufp->dataVersion = CM_BUF_VERSION_BAD;        /* known bad */
@@ -1701,15 +2090,17 @@ long buf_FlushCleanPages(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
             /* actually, we only know that buffer is clean if ref
              * count is 1, since we don't have buffer itself locked.
              */
-            if (!(bp->flags & CM_BUF_DIRTY)) {
+            if (!(bp->flags & CM_BUF_DIRTY) && !(bp->qFlags & CM_BUF_QREDIR)) {
                 lock_ObtainWrite(&buf_globalLock);
-                if (bp->refCount == 1) {       /* bp is held above */
-                    nbp = bp->fileHashp;
-                    if (nbp)
-                        buf_HoldLocked(nbp);
-                    buf_ReleaseLocked(bp, TRUE);
-                    didRelease = 1;
-                    buf_Recycle(bp);
+                if (!(bp->flags & CM_BUF_DIRTY) && !(bp->qFlags & CM_BUF_QREDIR)) {
+                    if (bp->refCount == 1) {   /* bp is held above */
+                        nbp = bp->fileHashp;
+                        if (nbp)
+                            buf_HoldLocked(nbp);
+                        buf_ReleaseLocked(bp, TRUE);
+                        didRelease = 1;
+                        buf_Recycle(bp);
+                    }
                 }
                 lock_ReleaseWrite(&buf_globalLock);
             }
@@ -1774,6 +2165,11 @@ long buf_CleanVnode(struct cm_scache *scp, cm_user_t *userp, cm_req_t *reqp)
     cm_buf_t *nbp;             /* next one */
     afs_uint32 i;
 
+    if (RDR_Initialized && scp->redirBufCount > 0) {
+        /* Retrieve all extents for this file from the redirector */
+        buf_RDRShakeFileExtentsFree(scp, reqp);
+    }
+
     i = BUF_FILEHASH(&scp->fid);
 
     lock_ObtainRead(&buf_globalLock);
@@ -1784,8 +2180,22 @@ long buf_CleanVnode(struct cm_scache *scp, cm_user_t *userp, cm_req_t *reqp)
     for (; bp; bp = nbp) {
         /* clean buffer synchronously */
         if (cm_FidCmp(&bp->fid, &scp->fid) == 0) {
+            /*
+             * If the buffer is held by the redirector we must fetch
+             * it back in order to determine whether or not it is in
+             * fact dirty.
+             */
+            lock_ObtainRead(&buf_globalLock);
+            if (bp->qFlags & CM_BUF_QREDIR) {
+                osi_Log1(buf_logp,"buf_CleanVnode buffer held by redirector bp 0x%p", bp);
+
+                /* Retrieve single extent from the redirector */
+                buf_RDRShakeAnExtentFree(bp, reqp);
+            }
+            lock_ReleaseRead(&buf_globalLock);
+
             lock_ObtainMutex(&bp->mx);
-            if (bp->flags & CM_BUF_DIRTY) {
+            if ((bp->flags & CM_BUF_DIRTY)) {
                 if (userp && userp != bp->userp) {
                     cm_HoldUser(userp);
                     if (bp->userp)
@@ -1992,13 +2402,84 @@ long buf_DirtyBuffersExist(cm_fid_t *fidp)
     cm_buf_t *bp;
     afs_uint32 bcount = 0;
     afs_uint32 i;
+    long found = 0;
 
     i = BUF_FILEHASH(fidp);
 
+    lock_ObtainRead(&buf_globalLock);
     for (bp = cm_data.buf_fileHashTablepp[i]; bp; bp=bp->fileHashp, bcount++) {
-       if (!cm_FidCmp(fidp, &bp->fid) && (bp->flags & CM_BUF_DIRTY))
-           return 1;
+       if (!cm_FidCmp(fidp, &bp->fid) && (bp->flags & CM_BUF_DIRTY)) {
+           found = 1;
+            break;
+        }
     }
+    lock_ReleaseRead(&buf_globalLock);
+    return 0;
+}
+
+long buf_RDRBuffersExist(cm_fid_t *fidp)
+{
+    cm_buf_t *bp;
+    afs_uint32 bcount = 0;
+    afs_uint32 i;
+    long found = 0;
+
+    if (!RDR_Initialized)
+        return 0;
+
+    i = BUF_FILEHASH(fidp);
+
+    lock_ObtainRead(&buf_globalLock);
+    for (bp = cm_data.buf_fileHashTablepp[i]; bp; bp=bp->fileHashp, bcount++) {
+       if (!cm_FidCmp(fidp, &bp->fid) && (bp->qFlags & CM_BUF_QREDIR)) {
+           found = 1;
+            break;
+        }
+    }
+    lock_ReleaseRead(&buf_globalLock);
+    return 0;
+}
+
+long buf_ClearRDRFlag(cm_scache_t *scp, char *reason)
+{
+    cm_fid_t *fidp = &scp->fid;
+    cm_buf_t *bp;
+    afs_uint32 bcount = 0;
+    afs_uint32 i;
+
+    i = BUF_FILEHASH(fidp);
+
+    lock_ObtainWrite(&scp->rw);
+    lock_ObtainRead(&buf_globalLock);
+    for (bp = cm_data.buf_fileHashTablepp[i]; bp; bp=bp->fileHashp, bcount++) {
+       if (!cm_FidCmp(fidp, &bp->fid) && (bp->qFlags & CM_BUF_QREDIR)) {
+            lock_ConvertRToW(&buf_globalLock);
+            if (bp->qFlags & CM_BUF_QREDIR) {
+                osi_Log4(buf_logp,"buf_ClearRDRFlag taking from file system bp 0x%p vno 0x%x foffset 0x%x:%x",
+                          bp, bp->fid.vnode, bp->offset.HighPart, bp->offset.LowPart);
+                buf_RemoveFromRedirQueue(scp, bp);
+                buf_ReleaseLocked(bp, TRUE);
+            }
+            lock_ConvertWToR(&buf_globalLock);
+        }
+    }
+
+    /* Confirm that there are none left */
+    lock_ConvertRToW(&buf_globalLock);
+    for ( bp = redirq_to_cm_buf_t(scp->redirQueueT);
+          bp;
+          bp = redirq_to_cm_buf_t(scp->redirQueueT))
+    {
+        if (bp->qFlags & CM_BUF_QREDIR) {
+            osi_Log4(buf_logp,"buf_ClearRDRFlag taking from file system bufp 0x%p vno 0x%x foffset 0x%x:%x",
+                      bp, bp->fid.vnode, bp->offset.HighPart, bp->offset.LowPart);
+            buf_RemoveFromRedirQueue(scp, bp);
+            buf_ReleaseLocked(bp, TRUE);
+        }
+
+    }
+    lock_ReleaseWrite(&buf_globalLock);
+    lock_ReleaseWrite(&scp->rw);
     return 0;
 }
 
@@ -2032,3 +2513,132 @@ long buf_CleanDirtyBuffers(cm_scache_t *scp)
     return 0;
 }
 #endif
+
+/*
+ * The following routines will not be used on a
+ * regular basis but are very useful in a variety
+ * of scenarios when debugging data corruption.
+ */
+const char *
+buf_HexCheckSum(cm_buf_t * bp)
+{
+    int i, k;
+    static char buf[33];
+    static char tr[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+    for (i=0;i<16;i++) {
+        k = bp->md5cksum[i];
+
+        buf[i*2] = tr[k / 16];
+        buf[i*2+1] = tr[k % 16];
+    }
+    buf[32] = '\0';
+
+    return buf;
+}
+
+void
+buf_ComputeCheckSum(cm_buf_t * bp)
+{
+    MD5_CTX md5;
+
+    MD5_Init(&md5);
+    MD5_Update(&md5, bp->datap, cm_data.blockSize);
+    MD5_Final(bp->md5cksum, &md5);
+
+    osi_Log4(buf_logp, "CheckSum bp 0x%p md5 %s, dirty: offset %u length %u",
+             bp, osi_LogSaveString(buf_logp, buf_HexCheckSum(bp)),
+             bp->dirty_offset, bp->dirty_length);
+}
+
+int
+buf_ValidateCheckSum(cm_buf_t * bp)
+{
+    MD5_CTX md5;
+    unsigned char tmp[16];
+
+    MD5_Init(&md5);
+    MD5_Update(&md5, bp->datap, cm_data.blockSize);
+    MD5_Final(tmp, &md5);
+
+    if (memcmp(tmp, bp->md5cksum, 16) == 0)
+        return 1;
+    return 0;
+}
+
+void
+buf_InsertToRedirQueue(cm_scache_t *scp, cm_buf_t *bufp)
+{
+    lock_AssertWrite(&buf_globalLock);
+    if (scp)
+        lock_AssertWrite(&scp->rw);
+
+    if (bufp->qFlags & CM_BUF_QINLRU) {
+        _InterlockedAnd(&bufp->qFlags, ~CM_BUF_QINLRU);
+        osi_QRemoveHT( (osi_queue_t **) &cm_data.buf_freeListp,
+                       (osi_queue_t **) &cm_data.buf_freeListEndp,
+                       &bufp->q);
+        buf_DecrementFreeCount();
+    }
+    _InterlockedOr(&bufp->qFlags, CM_BUF_QREDIR);
+    osi_QAddH( (osi_queue_t **) &cm_data.buf_redirListp,
+               (osi_queue_t **) &cm_data.buf_redirListEndp,
+               &bufp->q);
+    buf_IncrementRedirCount();
+    bufp->redirLastAccess = time(NULL);
+    if (scp) {
+        osi_QAddH( (osi_queue_t **) &scp->redirQueueH,
+                   (osi_queue_t **) &scp->redirQueueT,
+                   &bufp->redirq);
+        scp->redirLastAccess = bufp->redirLastAccess;
+        InterlockedIncrement(&scp->redirBufCount);
+    }
+}
+
+void
+buf_RemoveFromRedirQueue(cm_scache_t *scp, cm_buf_t *bufp)
+{
+    lock_AssertWrite(&buf_globalLock);
+    if (scp)
+        lock_AssertWrite(&scp->rw);
+
+    _InterlockedAnd(&bufp->qFlags, ~CM_BUF_QREDIR);
+    osi_QRemoveHT( (osi_queue_t **) &cm_data.buf_redirListp,
+                   (osi_queue_t **) &cm_data.buf_redirListEndp,
+                   &bufp->q);
+    buf_DecrementRedirCount();
+    if (scp) {
+        osi_QRemoveHT( (osi_queue_t **) &scp->redirQueueH,
+                       (osi_queue_t **) &scp->redirQueueT,
+                       &bufp->redirq);
+        InterlockedDecrement(&scp->redirBufCount);
+    }
+}
+
+void
+buf_MoveToHeadOfRedirQueue(cm_scache_t *scp, cm_buf_t *bufp)
+{
+    lock_AssertWrite(&buf_globalLock);
+    osi_assertx(bufp->qFlags & CM_BUF_QREDIR,
+                 "buf_MoveToHeadOfRedirQueue buffer not held by redirector");
+
+    if (scp)
+        lock_AssertWrite(&scp->rw);
+
+    osi_QRemoveHT( (osi_queue_t **) &cm_data.buf_redirListp,
+                   (osi_queue_t **) &cm_data.buf_redirListEndp,
+                   &bufp->q);
+    osi_QAddH( (osi_queue_t **) &cm_data.buf_redirListp,
+               (osi_queue_t **) &cm_data.buf_redirListEndp,
+               &bufp->q);
+    bufp->redirLastAccess = time(NULL);
+    if (scp) {
+        osi_QRemoveHT( (osi_queue_t **) &scp->redirQueueH,
+                       (osi_queue_t **) &scp->redirQueueT,
+                       &bufp->redirq);
+        osi_QAddH( (osi_queue_t **) &scp->redirQueueH,
+                   (osi_queue_t **) &scp->redirQueueT,
+                   &bufp->redirq);
+        scp->redirLastAccess = bufp->redirLastAccess;
+    }
+}
index 0c57905..3c5e293 100644 (file)
@@ -42,8 +42,8 @@ extern int buf_cacheType;
 
 /* represents a single buffer */
 typedef struct cm_buf {
-    osi_queue_t q;             /* queue of all zero-refcount buffers */
-    afs_uint32 qFlags;         /* queue/hash state flags - buf_globalLock */
+    osi_queue_t    q;          /* queue: buf_freeList and buf_redirList */
+    afs_uint32     qFlags;     /* queue/hash state flags - buf_globalLock */
     afs_uint32     magic;
     struct cm_buf *allp;       /* next in all list */
     struct cm_buf *hashp;      /* hash bucket pointer */
@@ -77,7 +77,7 @@ typedef struct cm_buf {
     afs_uint32 waitRequests;    /* num of thread wait requests */
 
     afs_uint32 dirty_offset;    /* offset from beginning of buffer containing dirty bytes */
-    afs_uint32 dirty_length;      /* number of dirty bytes within the buffer */
+    afs_uint32 dirty_length;    /* number of dirty bytes within the buffer */
 
 #ifdef DISKCACHE95
     cm_diskcache_t *dcp;        /* diskcache structure */
@@ -88,8 +88,17 @@ typedef struct cm_buf {
 #else
     void * dummy;
 #endif
+
+    /* redirector state - protected by buf_globalLock */
+    osi_queue_t redirq;         /* queue: cm_scache_t redirList */
+    time_t      redirLastAccess;/* last time redir accessed the buffer */
+    time_t      redirReleaseRequested;
+
+    unsigned char md5cksum[16]; /* md5 checksum of the block pointed to by datap */
 } cm_buf_t;
 
+#define redirq_to_cm_buf_t(q) ((q) ? (cm_buf_t *)((char *) (q) - offsetof(cm_buf_t, redirq)) : NULL)
+
 /* values for cmFlags */
 #define CM_BUF_CMFETCHING      1       /* fetching this buffer */
 #define CM_BUF_CMSTORING       2       /* storing this buffer */
@@ -132,8 +141,6 @@ extern long buf_Init(int newFile, cm_buf_ops_t *, afs_uint64 nbuffers);
 
 extern void buf_Shutdown(void);
 
-extern long buf_CountFreeList(void);
-
 #ifdef DEBUG_REFCOUNT
 extern void buf_ReleaseDbg(cm_buf_t *, char *, long);
 
@@ -212,10 +219,51 @@ extern long buf_DirtyBuffersExist(cm_fid_t * fidp);
 
 extern long buf_CleanDirtyBuffers(cm_scache_t *scp);
 
+extern long buf_RDRBuffersExist(cm_fid_t *fidp);
+
+extern long buf_ClearRDRFlag(cm_scache_t *scp, char * reason);
+
 extern long buf_ForceDataVersion(cm_scache_t * scp, afs_uint64 fromVersion, afs_uint64 toVersion);
 
 extern int cm_DumpBufHashTable(FILE *outputFile, char *cookie, int lock);
 
+extern void buf_ComputeCheckSum(cm_buf_t *bp);
+
+extern int  buf_ValidateCheckSum(cm_buf_t *bp);
+
+extern const char *buf_HexCheckSum(cm_buf_t * bp);
+
+extern afs_uint32
+buf_RDRShakeSomeExtentsFree(cm_req_t *reqp, afs_uint32 oneFid, afs_uint32 minage);
+
+extern afs_uint32
+buf_RDRShakeAnExtentFree(cm_buf_t *bufp, cm_req_t *reqp);
+
+extern afs_uint32
+buf_RDRShakeFileExtentsFree(cm_scache_t *scp, cm_req_t *reqp);
+
+extern void
+buf_InsertToRedirQueue(cm_scache_t *scp, cm_buf_t *bufp);
+
+extern void
+buf_RemoveFromRedirQueue(cm_scache_t *scp, cm_buf_t *bufp);
+
+extern void
+buf_MoveToHeadOfRedirQueue(cm_scache_t *scp, cm_buf_t *bufp);
+
+#ifdef _M_IX86
+#define buf_IncrementRedirCount()  InterlockedIncrement(&cm_data.buf_redirCount)
+#define buf_DecrementRedirCount()  InterlockedDecrement(&cm_data.buf_redirCount)
+#define buf_IncrementFreeCount()   InterlockedIncrement(&cm_data.buf_freeCount)
+#define buf_DecrementFreeCount()   InterlockedDecrement(&cm_data.buf_freeCount)
+#else
+#define buf_IncrementRedirCount()  InterlockedIncrement64(&cm_data.buf_redirCount)
+#define buf_DecrementRedirCount()  InterlockedDecrement64(&cm_data.buf_redirCount)
+#define buf_IncrementFreeCount()   InterlockedIncrement64(&cm_data.buf_freeCount)
+#define buf_DecrementFreeCount()   InterlockedDecrement64(&cm_data.buf_freeCount)
+#endif
+
 /* error codes */
 #define CM_BUF_EXISTS  1       /* buffer exists, and shouldn't */
+
 #endif /* OPENAFS_WINNT_AFSD_BUF_H */
index 9b56a0d..b85b4f8 100644 (file)
@@ -197,6 +197,10 @@ void cm_RevokeCallback(struct rx_call *callp, cm_cell_t * cellp, AFSFid *fidp)
             osi_Log4(afsd_logp, "RevokeCallback Discarding SCache scp 0x%p vol %u vn %u uniq %u",
                      scp, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
 
+            if (RDR_Initialized)
+                RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                     scp->fid.hash, scp->fileType, AFS_INVALIDATE_CALLBACK);
+
             lock_ObtainWrite(&scp->rw);
             cm_DiscardSCache(scp);
             lock_ReleaseWrite(&scp->rw);
@@ -241,7 +245,7 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
     cm_scache_t *scp;
     cm_fid_t tfid;
 
-    osi_Log1(afsd_logp, "RevokeVolumeCallback vol %d", fidp->Volume);
+    osi_Log1(afsd_logp, "RevokeVolumeCallback vol %u", fidp->Volume);
 
     /* do this first, so that if we're executing a callback granting call
      * at this moment, we kill it before it can be merged in.  Otherwise,
@@ -251,7 +255,6 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
     tfid.cell = cellp ? cellp->cellID : 0;
     tfid.volume = fidp->Volume;
     tfid.vnode = tfid.unique = 0;
-
     cm_RecordRacingRevoke(&tfid, CM_RACINGFLAG_CANCELVOL);
 
     lock_ObtainWrite(&cm_scacheLock);
@@ -265,11 +268,19 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
                 lock_ReleaseWrite(&cm_scacheLock);
 
                 lock_ObtainWrite(&scp->rw);
-                osi_Log4(afsd_logp, "RevokeVolumeCallback Discarding SCache scp 0x%p vol %u vn %u uniq %u",
-                          scp, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
+                osi_Log5(afsd_logp, "RevokeVolumeCallback Discarding SCache scp 0x%p vol %u vn %u uniq %u",
+                          scp, scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
+                osi_Log2(afsd_logp, ".... dv 0x%x:%x",
+                          (afs_uint32)((scp->dataVersion >> 32) & 0xFFFFFFFF),
+                          (afs_uint32)(scp->dataVersion & 0xFFFFFFFF));
+
                 cm_DiscardSCache(scp);
                 lock_ReleaseWrite(&scp->rw);
 
+                if (RDR_Initialized)
+                    RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                         scp->fid.hash, scp->fileType, AFS_INVALIDATE_CALLBACK);
+
                 cm_CallbackNotifyChange(scp);
                 lock_ObtainWrite(&cm_scacheLock);
                 cm_ReleaseSCacheNoLock(scp);
@@ -281,6 +292,9 @@ void cm_RevokeVolumeCallback(struct rx_call *callp, cm_cell_t *cellp, AFSFid *fi
 
     lock_ReleaseWrite(&cm_scacheLock);
 
+    if (cellp && RDR_Initialized)
+        RDR_InvalidateVolume(cellp->cellID, fidp->Volume, AFS_INVALIDATE_CALLBACK);
+
     osi_Log1(afsd_logp, "RevokeVolumeCallback Complete vol %d", fidp->Volume);
 }
 
@@ -1063,8 +1077,12 @@ SRXAFSCB_InitCallBackState3(struct rx_call *callp, afsUUID* serverUuid)
                     }
                 }
                 lock_ReleaseWrite(&scp->rw);
-                if (discarded)
+                if (discarded) {
                     cm_CallbackNotifyChange(scp);
+                    if (RDR_Initialized)
+                        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                              scp->fid.hash, scp->fileType, AFS_INVALIDATE_EXPIRED);
+                }
                 lock_ObtainWrite(&cm_scacheLock);
                 cm_ReleaseSCacheNoLock(scp);
 
@@ -1557,7 +1575,8 @@ int cm_HaveCallback(cm_scache_t *scp)
                     break;
                 }
             }
-            if (!haveCB &&
+            if (cm_readonlyVolumeVersioning &&
+                !haveCB &&
                 volp->creationDateRO == scp->volumeCreationDate &&
                 volp->cbServerpRO != NULL) {
                 haveCB = 1;
@@ -1699,6 +1718,9 @@ void cm_EndCallbackGrantingCall(cm_scache_t *scp, cm_callbackRequest_t *cbrp,
         cm_DiscardSCache(scp);
         lock_ReleaseWrite(&scp->rw);
         cm_CallbackNotifyChange(scp);
+        if (RDR_Initialized)
+            RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                 scp->fid.hash, scp->fileType, AFS_INVALIDATE_CALLBACK);
         lock_ObtainWrite(&scp->rw);
     } else {
         if (scp && scp->flags & CM_SCACHEFLAG_PURERO) {
@@ -1993,6 +2015,10 @@ void cm_CheckCBExpiration(void)
             cm_DiscardSCache(scp);
             lock_ReleaseWrite(&scp->rw);
 
+            if (RDR_Initialized)
+                RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                      scp->fid.hash, scp->fileType, AFS_INVALIDATE_EXPIRED);
+
             cm_CallbackNotifyChange(scp);
 
           scp_complete:
index bfb6119..ebc4c24 100644 (file)
@@ -79,4 +79,5 @@ extern afs_int32 cm_OfflineROIsValid;
 extern afs_int32 cm_giveUpAllCBs;
 
 extern afs_int32 cm_shutdown;
+
 #endif /*  OPENAFS_WINNT_AFSD_CM_CALLBACK_H */
index 958f8fd..c737b20 100644 (file)
@@ -156,7 +156,7 @@ void cm_InitConn(void)
             if (HardDeadtimeout == 0) {
                 HardDeadtimeout = (unsigned short) (RDRtimeout > 125 ? 120 : (RDRtimeout - 5));
                 afsi_log("HardDeadTimeout is %d", HardDeadtimeout);
-            }       
+            }
             if (IdleDeadtimeout == 0) {
                 IdleDeadtimeout = (unsigned short) ConnDeadtimeout;
                 afsi_log("IdleDeadTimeout is %d", IdleDeadtimeout);
@@ -696,15 +696,23 @@ cm_Analyze(cm_conn_t *connp, cm_user_t *userp, cm_req_t *reqp,
                lock_ReleaseWrite(&cm_scacheLock);
                 cm_LockMarkSCacheLost(scp);
                lock_ReleaseWrite(&scp->rw);
+                if (RDR_Initialized)
+                    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);
 
                if (pscp) {
                    if (cm_HaveCallback(pscp)) {
                        lock_ObtainWrite(&pscp->rw);
                        cm_DiscardSCache(pscp);
-                       lock_ReleaseWrite(&pscp->rw);
-                   }
-                   cm_ReleaseSCache(pscp);
+                       lock_ReleaseWrite(&pscp->rw);
+
+                        if (RDR_Initialized)
+                            RDR_InvalidateObject(pscp->fid.cell, pscp->fid.volume, pscp->fid.vnode, pscp->fid.unique,
+                                                 pscp->fid.hash, pscp->fileType, AFS_INVALIDATE_EXPIRED);
+
+                   }
+                   cm_ReleaseSCache(pscp);
                }
            }
        } else {
index ab6031b..a1ddb1a 100644 (file)
@@ -53,8 +53,8 @@ typedef struct cm_conn {
  * to the cache manager functions.
  */
 typedef struct cm_req {
-    DWORD startTime;           /* Quit before RDR times us out */
-    int rpcError;                      /* RPC error code */
+    DWORD startTime;           /* GetTickCount() when this struct was initialized */
+    int rpcError;              /* RPC error code */
     int volumeError;           /* volume error code */
     int accessError;           /* access error code */
     struct cm_server * tokenIdleErrorServp;  /* server that reported a token/idle error other than expired */
index 0b70dab..cdcfbf2 100644 (file)
@@ -40,6 +40,7 @@ long cm_daemonTokenCheckInterval = 180;
 long cm_daemonCheckOfflineVolInterval = 600;
 long cm_daemonPerformanceTuningInterval = 0;
 long cm_daemonRankServerInterval = 600;
+long cm_daemonRDRShakeExtentsInterval = 0;
 
 osi_rwlock_t cm_daemonLock;
 
@@ -164,7 +165,8 @@ void cm_BkgDaemon(void * parm)
         case CM_ERROR_ALLDOWN:
         case CM_ERROR_ALLOFFLINE:
         case CM_ERROR_PARTIALWRITE:
-            if (rp->procp == cm_BkgStore) {
+            if (rp->procp == cm_BkgStore ||
+                rp->procp == RDR_BkgFetch) {
                 osi_Log2(afsd_logp,
                          "cm_BkgDaemon re-queueing failed request 0x%p code 0x%x",
                          rp, code);
@@ -350,6 +352,13 @@ cm_DaemonCheckInit(void)
     afsi_log("daemonCheckOfflineVolInterval is %d", cm_daemonCheckOfflineVolInterval);
 
     dummyLen = sizeof(DWORD);
+    code = RegQueryValueEx(parmKey, "daemonRDRShakeExtentsInterval", NULL, NULL,
+                           (BYTE *) &dummy, &dummyLen);
+    if (code == ERROR_SUCCESS && dummy)
+       cm_daemonRDRShakeExtentsInterval = dummy;
+    afsi_log("daemonRDRShakeExtentsInterval is %d", cm_daemonRDRShakeExtentsInterval);
+
+    dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonPerformanceTuningInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
     if (code == ERROR_SUCCESS)
@@ -418,6 +427,7 @@ void cm_Daemon(long parm)
     time_t lastBusyVolCheck;
     time_t lastPerformanceCheck;
     time_t lastServerRankCheck;
+    time_t lastRDRShakeExtents;
     char thostName[200];
     unsigned long code;
     struct hostent *thp;
@@ -472,6 +482,8 @@ void cm_Daemon(long parm)
     if (cm_daemonPerformanceTuningInterval)
         lastPerformanceCheck = now - cm_daemonPerformanceTuningInterval/2 * (rand() % cm_daemonPerformanceTuningInterval);
     lastServerRankCheck = now - cm_daemonRankServerInterval/2 * (rand() % cm_daemonRankServerInterval);
+    if (cm_daemonRDRShakeExtentsInterval)
+        lastRDRShakeExtents = now - cm_daemonRDRShakeExtentsInterval/2 * (rand() % cm_daemonRDRShakeExtentsInterval);
 
     while (daemon_ShutdownFlag == 0) {
         if (powerStateSuspended) {
@@ -624,6 +636,20 @@ void cm_Daemon(long parm)
            now = osi_Time();
         }
 
+        if (cm_daemonRDRShakeExtentsInterval &&
+            now > lastRDRShakeExtents + cm_daemonRDRShakeExtentsInterval &&
+            daemon_ShutdownFlag == 0 &&
+            powerStateSuspended == 0) {
+            cm_req_t req;
+            cm_InitReq(&req);
+            lastRDRShakeExtents = now;
+            if (cm_data.buf_redirCount > cm_data.buf_freeCount)
+                buf_RDRShakeSomeExtentsFree(&req, FALSE, 10 /* seconds */);
+            if (daemon_ShutdownFlag == 1)
+                break;
+           now = osi_Time();
+        }
+
         /* allow an exit to be called prior to stopping the service */
         hHookDll = cm_LoadAfsdHookLib();
         if (hHookDll)
index d14c8f8..5e65ab1 100644 (file)
@@ -22,6 +22,7 @@
 #include <osi.h>
 
 #include "afsd.h"
+#include "smb.h"
 
 #ifdef DEBUG
 extern void afsi_log(char *pattern, ...);
@@ -732,12 +733,12 @@ cm_BkgStore(cm_scache_t *scp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_u
     osi_hyper_t toffset;
     long length;
     long code = 0;
+    afs_uint32 req_flags = reqp->flags;
 
     if (scp->flags & CM_SCACHEFLAG_DELETED) {
        osi_Log4(afsd_logp, "Skipping BKG store - Deleted scp 0x%p, offset 0x%x:%08x, length 0x%x", scp, p2, p1, p3);
     } else {
        /* Retries will be performed by the BkgDaemon thread if appropriate */
-        afs_uint32 req_flags = reqp->flags;
        reqp->flags |= CM_REQ_NORETRY;
 
        toffset.LowPart = p1;
@@ -1420,6 +1421,7 @@ void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore, long code, int scp_locked)
     osi_queueData_t *qdp;
     osi_queueData_t *nqdp;
     int flags;
+    int reportErrorToRedir = 0;
 
     /* Give back reserved buffers */
     if (biop->reserved)
@@ -1477,6 +1479,7 @@ void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore, long code, int scp_locked)
                         bufp->error = code;
                         bufp->dataVersion = CM_BUF_VERSION_BAD;
                         bufp->dirtyCounter++;
+                        reportErrorToRedir = 1;
                         break;
                     case CM_ERROR_TIMEDOUT:
                     case CM_ERROR_ALLDOWN:
@@ -1501,6 +1504,12 @@ void cm_ReleaseBIOD(cm_bulkIO_t *biop, int isStore, long code, int scp_locked)
            buf_Release(bufp);
            bufp = NULL;
        }
+
+        if (RDR_Initialized && reportErrorToRedir) {
+            DWORD status;
+            smb_MapNTError(cm_MapRPCError(code, biop->reqp), &status, TRUE);
+            RDR_SetFileStatus( &scp->fid, status);
+        }
     } else {
        if (!scp_locked)
             lock_ObtainWrite(&scp->rw);
index 171c389..4384342 100644 (file)
@@ -397,6 +397,15 @@ int cm_noteLocalMountPointChange(afs_int32 locked) {
         lock_ObtainMutex(&cm_Freelance_Lock);
     cm_data.fakeDirVersion++;
     cm_localMountPointChangeFlag = 1;
+
+    if (RDR_Initialized) {
+        cm_fid_t fid;
+        cm_FakeRootFid(&fid);
+        RDR_InvalidateObject( fid.cell, fid.volume, fid.vnode, fid.unique, fid.hash,
+                              CM_SCACHETYPE_DIRECTORY,
+                              AFS_INVALIDATE_DATA_VERSION);
+    }
+
     if (!locked)
         lock_ReleaseMutex(&cm_Freelance_Lock);
     return 1;
@@ -459,6 +468,11 @@ int cm_reInitLocalMountPoints() {
                 lock_ReleaseWrite(&scp->rw);
                 lock_ReleaseWrite(&cm_scacheLock);
                 cm_CallbackNotifyChange(scp);
+
+                if (RDR_Initialized)
+                    RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                         scp->fid.hash, scp->fileType, AFS_INVALIDATE_CALLBACK);
+
                 lock_ObtainWrite(&cm_scacheLock);
                 cm_ReleaseSCacheNoLock(scp);
                 lock_ObtainMutex(&cm_Freelance_Lock);
index 66a625c..d9bbc60 100644 (file)
@@ -78,7 +78,12 @@ cm_CleanFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
 {
     long code;
 
-    code = cm_FSync(scp, userp, reqp, FALSE);
+    if (RDR_Initialized &&
+        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                             scp->fid.hash, scp->fileType, AFS_INVALIDATE_FLUSHED))
+        code = CM_ERROR_WOULDBLOCK;
+    else
+        code = cm_FSync(scp, userp, reqp, FALSE);
     if (!code) {
         lock_ObtainWrite(&scp->rw);
         cm_DiscardSCache(scp);
@@ -104,7 +109,16 @@ cm_FlushFile(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
     }
 #endif
 
-    code = buf_FlushCleanPages(scp, userp, reqp);
+    /*
+     * The file system will forget all knowledge of the object
+     * when it receives this message.
+     */
+    if (RDR_Initialized &&
+        RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                             scp->fid.hash, scp->fileType, AFS_INVALIDATE_FLUSHED))
+        code = CM_ERROR_WOULDBLOCK;
+    else
+        code = buf_FlushCleanPages(scp, userp, reqp);
 
     if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
         lock_ObtainWrite(&scp->dirlock);
@@ -530,6 +544,10 @@ cm_IoctlSetACL(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *scp,
         lock_ObtainWrite(&scp->rw);
         cm_DiscardSCache(scp);
         lock_ReleaseWrite(&scp->rw);
+
+        if (RDR_Initialized)
+            RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                 scp->fid.hash, scp->fileType, AFS_INVALIDATE_CREDS);
     }
 
     return code;
@@ -1185,6 +1203,11 @@ cm_IoctlDeleteMountPoint(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scac
     lock_ReleaseWrite(&scp->rw);
     cm_ReleaseSCache(scp);
 
+    if (RDR_Initialized &&
+        !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, "deleted mp");
+
   done3:
     if (originalName != NULL)
         free(originalName);
@@ -1409,7 +1432,7 @@ cm_IoctlGetCacheParms(struct cm_ioctl *ioctlp, struct cm_user *userp)
     /* and then the actual # of buffers in use (not in the free list, I guess,
      * will be what we do).
      */
-    parms.parms[1] = (cm_data.buf_nbuffers - buf_CountFreeList()) * (cm_data.buf_blockSize / 1024);
+    parms.parms[1] = (cm_data.buf_nbuffers - cm_data.buf_freeCount) * (cm_data.buf_blockSize / 1024);
 
     memcpy(ioctlp->outDatap, &parms, sizeof(parms));
     ioctlp->outDatap += sizeof(parms);
@@ -1679,6 +1702,13 @@ cm_IoctlGetWsCell(cm_ioctl_t *ioctlp, cm_user_t *userp)
  * VIOC_AFS_SYSNAME internals.
  *
  * Assumes that pioctl path has been parsed or skipped.
+ *
+ * In order to support both 32-bit and 64-bit sysname lists
+ * we will treat bit-31 of the setSysName value as a flag
+ * indicating which architecture is being indicated.  If unset
+ * the architecture is 32-bit and if set the architecture is
+ * 64-bit.  This change is backward compatible with cache
+ * managers that do not support this extension.
  */
 afs_int32
 cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
@@ -1688,10 +1718,14 @@ cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
     clientchar_t *inname = NULL;
     int t;
     unsigned int count;
+    int arch64 = 0;
 
     memcpy(&setSysName, ioctlp->inDatap, sizeof(afs_uint32));
     ioctlp->inDatap += sizeof(afs_uint32);
 
+    arch64 = (setSysName & 0x8000000) ? 1 : 0;
+    setSysName &= 0x7FFFFFF;
+
     if (setSysName) {
         /* check my args */
         if ( setSysName < 0 || setSysName > MAXNUMSYSNAMES )
@@ -1716,46 +1750,57 @@ cm_IoctlSysName(struct cm_ioctl *ioctlp, struct cm_user *userp)
     }
 
     /* Not xlating, so local case */
-    if (!cm_sysName)
-        osi_panic("cm_IoctlSysName: !cm_sysName\n", __FILE__, __LINE__);
-
     if (setSysName) {
         /* Local guy; only root can change sysname */
         /* clear @sys entries from the dnlc, once afs_lookup can
          * do lookups of @sys entries and thinks it can trust them */
         /* privs ok, store the entry, ... */
 
-        cm_ClientStrCpy(cm_sysName, lengthof(cm_sysName), inname);
-        cm_ClientStrCpy(cm_sysNameList[0], MAXSYSNAME, inname);
+        cm_ClientStrCpy(arch64 ? cm_sysName64List[0] : cm_sysNameList[0], MAXSYSNAME, inname);
 
         if (setSysName > 1) {       /* ... or list */
             for (count = 1; count < setSysName; ++count) {
                 clientchar_t * newsysname;
 
-                if (!cm_sysNameList[count])
+                if (!(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]))
                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to write\n",
                               __FILE__, __LINE__);
 
                 newsysname = cm_ParseIoctlStringAlloc(ioctlp, NULL);
-                cm_ClientStrCpy(cm_sysNameList[count], MAXSYSNAME, newsysname);
+                cm_ClientStrCpy(arch64 ? cm_sysName64List[count] : cm_sysNameList[count], MAXSYSNAME, newsysname);
                 free(newsysname);
             }
         }
-        cm_sysNameCount = setSysName;
+        if ( arch64 ) {
+            cm_sysName64Count = setSysName;
+            if (cm_sysName64Count)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysName64Count, cm_sysName64List );
+            else if (cm_sysNameCount)
+                RDR_SysName( AFS_SYSNAME_ARCH_64BIT, cm_sysNameCount, cm_sysNameList );
+        } else {
+            cm_sysNameCount = setSysName;
+            RDR_SysName( AFS_SYSNAME_ARCH_32BIT, cm_sysNameCount, cm_sysNameList );
+        }
     } else {
         afs_uint32 i32;
 
-        /* return the sysname to the caller */
-        i32 = cm_sysNameCount;
+        /* return the sysname list to the caller.
+         * if there is no 64-bit list and 64-bit is requested, use the 32-bit list.
+         */
+        if ( arch64 && cm_sysName64Count == 0 )
+            arch64 = 0;
+
+        i32 = arch64 ? cm_sysName64Count : cm_sysNameCount;
         memcpy(ioctlp->outDatap, &i32, sizeof(afs_int32));
         ioctlp->outDatap += sizeof(afs_int32); /* skip found flag */
 
-        if (cm_sysNameCount) {
-            for ( count=0; count < cm_sysNameCount ; ++count) {   /* ... or list */
-                if ( !cm_sysNameList[count] || *cm_sysNameList[count] == _C('\0'))
+        if (i32) {
+            for ( count=0; count < i32 ; ++count) {   /* ... or list */
+                if ( !(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]) ||
+                     *(arch64 ? cm_sysName64List[count] : cm_sysNameList[count]) == _C('\0'))
                     osi_panic("cm_IoctlSysName: no cm_sysNameList entry to read\n",
                               __FILE__, __LINE__);
-                cm_UnparseIoctlString(ioctlp, NULL, cm_sysNameList[count], -1);
+                cm_UnparseIoctlString(ioctlp, NULL, arch64 ? cm_sysName64List[count] : cm_sysNameList[count], -1);
             }
         }
     }
@@ -2346,6 +2391,11 @@ cm_IoctlDeletelink(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scache_t *
     lock_ReleaseWrite(&scp->rw);
     cm_ReleaseSCache(scp);
 
+    if (RDR_Initialized &&
+        !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, "deleted link");
+
   done3:
     free(clientp);
 
@@ -2529,8 +2579,14 @@ cm_IoctlSetToken(struct cm_ioctl *ioctlp, struct cm_user *userp)
     }
 
     if (flags & PIOCTL_LOGON) {
-        userp = smb_FindCMUserByName(smbname, ioctlp->fidp->vcp->rname,
+        clientchar_t *cname;
+
+        cname = cm_FsStringToClientStringAlloc(smbname, -1, NULL);
+
+        userp = smb_FindCMUserByName(cname, ioctlp->fidp->vcp->rname,
                                     SMB_FLAG_CREATE|SMB_FLAG_AFSLOGON);
+        if (cname)
+            free(cname);
        release_userp = 1;
     }
 
index 048683c..605f5e7 100644 (file)
@@ -118,9 +118,10 @@ typedef struct cm_IoctlQueryOptions {
 
 #define MAXNUMSYSNAMES    16      /* max that current constants allow */
 #define   MAXSYSNAME      128     /* max sysname (i.e. @sys) size */
-extern clientchar_t  *cm_sysName;
 extern unsigned int   cm_sysNameCount;
 extern clientchar_t  *cm_sysNameList[MAXNUMSYSNAMES];
+extern unsigned int   cm_sysName64Count;
+extern clientchar_t  *cm_sysName64List[MAXNUMSYSNAMES];
 
 /* Paths that are passed into pioctl calls can be specified using
    UTF-8.  These strings are prefixed with UTF8_PREFIX defined below.
index b7bc9ca..d437280 100644 (file)
@@ -266,15 +266,15 @@ cm_ShutdownMappedMemory(void)
         HeapDestroy(hCacheHeap);
         afsi_log("Memory Heap has been destroyed");
     } else {
-    if (cm_ValidateCache == 2)
-        dirty = !cm_IsCacheValid();
+        if (cm_ValidateCache == 2)
+            dirty = !cm_IsCacheValid();
 
-    *config_data_p = cm_data;
-    config_data_p->dirty = dirty;
-    UnmapViewOfFile(config_data_p);
-    CloseHandle(hMemoryMappedFile);
-    hMemoryMappedFile = NULL;
-    afsi_log("Memory Mapped File has been closed");
+        *config_data_p = cm_data;
+        config_data_p->dirty = dirty;
+        UnmapViewOfFile(config_data_p);
+        CloseHandle(hMemoryMappedFile);
+        hMemoryMappedFile = NULL;
+        afsi_log("Memory Mapped File has been closed");
     }
     return 0;
 }
@@ -794,44 +794,44 @@ cm_InitMappedMemory(DWORD virtualCache, char * cachePath, DWORD stats, DWORD max
             CloseHandle(hm);
         }
 
-    hm = CreateFileMapping( hf,
-                            NULL,
-                            PAGE_READWRITE,
-                           (DWORD)(mappingSize >> 32),
-                           (DWORD)(mappingSize & 0xFFFFFFFF),
-                            NULL);
-    if (hm == NULL) {
-        if (GetLastError() == ERROR_DISK_FULL) {
-            afsi_log("Error creating file mapping for \"%s\": disk full [2]",
-                      cachePath);
-            return CM_ERROR_TOOMANYBUFS;
-        }
-        afsi_log("Error creating file mapping for \"%s\": %d",
-                  cachePath, GetLastError());
-        return CM_ERROR_INVAL;
-    }
-    baseAddress = MapViewOfFileEx( hm,
-                                   FILE_MAP_ALL_ACCESS,
-                                   0,
-                                  0,
-                                  (SIZE_T)mappingSize,
-                                   baseAddress );
-    if (baseAddress == NULL) {
-        afsi_log("Error mapping view of file: %d", GetLastError());
-        baseAddress = MapViewOfFile( hm,
-                                     FILE_MAP_ALL_ACCESS,
-                                     0,
-                                    0,
-                                    (SIZE_T)mappingSize);
-        if (baseAddress == NULL) {
-            if (hf != INVALID_HANDLE_VALUE)
-                CloseHandle(hf);
-            CloseHandle(hm);
+        hm = CreateFileMapping( hf,
+                                NULL,
+                                PAGE_READWRITE,
+                                (DWORD)(mappingSize >> 32),
+                                (DWORD)(mappingSize & 0xFFFFFFFF),
+                                NULL);
+        if (hm == NULL) {
+            if (GetLastError() == ERROR_DISK_FULL) {
+                afsi_log("Error creating file mapping for \"%s\": disk full [2]",
+                          cachePath);
+                return CM_ERROR_TOOMANYBUFS;
+            }
+            afsi_log("Error creating file mapping for \"%s\": %d",
+                      cachePath, GetLastError());
             return CM_ERROR_INVAL;
         }
+        baseAddress = MapViewOfFileEx( hm,
+                                       FILE_MAP_ALL_ACCESS,
+                                       0,
+                                       0,
+                                       (SIZE_T)mappingSize,
+                                       baseAddress );
+        if (baseAddress == NULL) {
+            afsi_log("Error mapping view of file: %d", GetLastError());
+            baseAddress = MapViewOfFile( hm,
+                                         FILE_MAP_ALL_ACCESS,
+                                         0,
+                                         0,
+                                         (SIZE_T)mappingSize);
+            if (baseAddress == NULL) {
+                if (hf != INVALID_HANDLE_VALUE)
+                    CloseHandle(hf);
+                CloseHandle(hm);
+                return CM_ERROR_INVAL;
+            }
             newCache = 1;
-    }
-    CloseHandle(hm);
+        }
+        CloseHandle(hm);
         hMemoryMappedFile = hf;
     }
 
@@ -936,10 +936,18 @@ cm_InitMappedMemory(DWORD virtualCache, char * cachePath, DWORD stats, DWORD max
         cm_data.bufEndOfData = (char *) baseAddress;
        cm_data.buf_dirtyListp = NULL;
        cm_data.buf_dirtyListEndp = NULL;
-        cm_data.fakeDirVersion = 0x8;
+        /* Make sure the fakeDirVersion is always increasing */
+        cm_data.fakeDirVersion = time(NULL);
+        cm_data.fakeUnique = 0;
         UuidCreate((UUID *)&cm_data.Uuid);
        cm_data.volSerialNumber = volumeSerialNumber;
        memcpy(cm_data.Sid, machineSid, sizeof(machineSid));
+
+        /*
+         * make sure that the file is fully allocated
+         * by writing a non-zero byte to the end
+         */
+        cm_data.baseAddress[mappingSize-1] = 0xFF;
     } else {
        int gennew = 0;
 
index 62eb225..5f8b4ce 100644 (file)
@@ -83,17 +83,30 @@ typedef struct cm_config_data {
     cm_buf_t    *       buf_freeListEndp;
     cm_buf_t   *       buf_dirtyListp;
     cm_buf_t    *       buf_dirtyListEndp;
+    cm_buf_t    *       buf_redirListp;
+    cm_buf_t    *       buf_redirListEndp;
     cm_buf_t   **      buf_scacheHashTablepp;
     cm_buf_t   **      buf_fileHashTablepp;
     cm_buf_t   *       buf_allp;
-    afs_uint64         buf_nbuffers;
     afs_uint32         buf_blockSize;
     afs_uint32         buf_hashSize;
+#ifdef _M_IX86
+    afs_uint32         buf_nbuffers;
+    afs_uint32         buf_nOrigBuffers;
+    afs_uint32          buf_reservedBufs;
+    afs_uint32          buf_maxReservedBufs;
+    afs_uint32          buf_reserveWaiting;
+    afs_uint32          buf_freeCount;
+    afs_uint32          buf_redirCount;
+#else
+    afs_uint64         buf_nbuffers;
     afs_uint64         buf_nOrigBuffers;
     afs_uint64          buf_reservedBufs;
     afs_uint64          buf_maxReservedBufs;
     afs_uint64          buf_reserveWaiting;
-
+    afs_uint64          buf_freeCount;
+    afs_uint64          buf_redirCount;
+#endif
     time_t              mountRootGen;
     afsUUID             Uuid;
     DWORD              volSerialNumber;
index 6563dab..41343e5 100644 (file)
@@ -537,7 +537,7 @@ void cm_PerformancePrintReport(void)
                         "SIZEs- 0kb=%u 1kb=%u 4kb=%u 64kb=%u 1mb=%u 20m=%u 100mb=%u 1gb=%u 2gb=%u larger=%u\r\n\r\n",
                          t,
                          cm_data.currentSCaches, cm_data.maxSCaches, cm_data.currentVolumes, cm_data.maxVolumes,
-                         cm_data.buf_nbuffers - buf_CountFreeList(), cm_data.buf_nbuffers,
+                         cm_data.buf_nbuffers - cm_data.buf_freeCount, cm_data.buf_nbuffers,
                          fid_cnt, fid_w_vol, fid_w_scache, fid_w_callbacks, fid_w_buffers,
                          fid_w_scache_no_vol, fid_w_vol_no_scache, fid_w_scache_no_buf, fid_w_buf_no_scache,
                          rw_vols, ro_vols, bk_vols,
diff --git a/src/WINNT/afsd/cm_rdr.h b/src/WINNT/afsd/cm_rdr.h
new file mode 100644 (file)
index 0000000..955ad55
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2008 Secure Endpoints Inc.
+ * Copyright (c) 2009-2011 Your File System Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Secure Endpoints Inc nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission from Secure Endpoints Inc and
+ *   Your File System Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef CM_RDR_H
+#define CM_RDR_H
+
+#include <..\afsrdr\common\AFSUserDefines.h>
+#include <..\afsrdr\common\AFSUserStructs.h>
+#include <..\afsrdr\common\AFSUserPrototypes.h>
+
+#endif /* CM_RDR_H */
+
index 687b0cd..e107571 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "afsd.h"
 #include "cm_btree.h"
+#include <afs/unified_afs.h>
 
 /*extern void afsi_log(char *pattern, ...);*/
 
@@ -154,6 +155,11 @@ long cm_RecycleSCache(cm_scache_t *scp, afs_int32 flags)
        return -1;
     }
 
+    if (scp->redirBufCount != 0) {
+        return -1;
+    }
+
+    cm_RemoveSCacheFromHashTable(scp);
 
     /* invalidate so next merge works fine;
      * also initialize some flags */
@@ -233,36 +239,64 @@ cm_scache_t *cm_GetNewSCache(void)
        /* There were no deleted scache objects that we could use.  Try to find
         * one that simply hasn't been used in a while.
         */
-        for ( scp = cm_data.scacheLRULastp;
-              scp;
-              scp = (cm_scache_t *) osi_QPrev(&scp->q))
-        {
-            /* It is possible for the refCount to be zero and for there still
-             * to be outstanding dirty buffers.  If there are dirty buffers,
-             * we must not recycle the scp. */
-            if (scp->refCount == 0 && scp->bufReadsp == NULL && scp->bufWritesp == NULL) {
-                if (!buf_DirtyBuffersExist(&scp->fid)) {
-                    if (!lock_TryWrite(&scp->rw))
-                        continue;
-
-                    if (!cm_RecycleSCache(scp, 0)) {
-                        /* we found an entry, so return it */
-                        /* now remove from the LRU queue and put it back at the
-                         * head of the LRU queue.
-                         */
-                        cm_AdjustScacheLRU(scp);
-
-                        /* and we're done */
-                        return scp;
+        for (retry = 0 ; retry < 2; retry++) {
+            for ( scp = cm_data.scacheLRULastp;
+                  scp;
+                  scp = (cm_scache_t *) osi_QPrev(&scp->q))
+            {
+                /* It is possible for the refCount to be zero and for there still
+                 * to be outstanding dirty buffers.  If there are dirty buffers,
+                 * we must not recycle the scp.
+                 *
+                 * If the object is in use by the redirector, then avoid recycling
+                 * it unless we have to.
+                 */
+                if (scp->refCount == 0 && scp->bufReadsp == NULL && scp->bufWritesp == NULL) {
+                    if (!buf_DirtyBuffersExist(&scp->fid) && !buf_RDRBuffersExist(&scp->fid)) {
+                        cm_fid_t   fid;
+                        afs_uint32 fileType;
+
+                        if (!lock_TryWrite(&scp->rw))
+                            continue;
+
+                        /* Found a likely candidate.  Save type and fid in case we succeed */
+                        fid = scp->fid;
+                        fileType = scp->fileType;
+
+                        if (!cm_RecycleSCache(scp, 0)) {
+                            /* we found an entry, so return it.
+                             * remove from the LRU queue and put it back at the
+                             * head of the LRU queue.
+                             */
+                            cm_AdjustScacheLRU(scp);
+
+                            if (RDR_Initialized) {
+                                /*
+                                 * We drop the cm_scacheLock because it may be required to
+                                 * satisfy an ioctl request from the redirector.  It should
+                                 * be safe to hold the scp->rw lock here because at this
+                                 * point (a) the object has just been recycled so the fid
+                                 * is nul and there are no requests that could possibly
+                                 * be issued by the redirector that would depend upon it.
+                                 */
+                                lock_ReleaseWrite(&cm_scacheLock);
+                                RDR_InvalidateObject( fid.cell, fid.volume, fid.vnode,
+                                                      fid.unique, fid.hash,
+                                                      fileType, AFS_INVALIDATE_EXPIRED);
+                                lock_ObtainWrite(&cm_scacheLock);
+                            }
+
+                            /* and we're done */
+                            return scp;
+                        }
+                        lock_ReleaseWrite(&scp->rw);
+                    } else {
+                        osi_Log1(afsd_logp,"GetNewSCache dirty buffers exist scp 0x%p", scp);
                     }
-                    lock_ReleaseWrite(&scp->rw);
-                } else {
-                    osi_Log1(afsd_logp,"GetNewSCache dirty buffers exist scp 0x%x", scp);
                 }
             }
+            osi_Log1(afsd_logp, "GetNewSCache all scache entries in use (retry = %d)", retry);
         }
-        osi_Log1(afsd_logp, "GetNewSCache all scache entries in use (retry = %d)", retry);
-
         return NULL;
     }
 
@@ -567,7 +601,11 @@ void cm_InitSCache(int newFile, long maxSCaches)
                 scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
 #endif
                 scp->waitQueueT = NULL;
-                _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_WAITING);
+                _InterlockedAnd(&scp->flags, ~(CM_SCACHEFLAG_CALLBACK | CM_SCACHEFLAG_WAITING | CM_SCACHEFLAG_RDR_IN_USE));
+
+                scp->redirBufCount = 0;
+                scp->redirQueueT = NULL;
+                scp->redirQueueH = NULL;
             }
         }
         cm_allFileLocks = NULL;
@@ -981,6 +1019,9 @@ int cm_SyncOpCheckContinue(cm_scache_t * scp, afs_int32 flags, cm_buf_t * bufp)
  * possibly resulting in a bogus truncation.  The simplest way to avoid this
  * is to serialize all StoreData RPC's.  This is the reason we defined
  * CM_SCACHESYNC_STOREDATA_EXCL and CM_SCACHEFLAG_DATASTORING.
+ *
+ * CM_SCACHESYNC_BULKREAD is used to permit synchronization of multiple bulk
+ * readers which may be requesting overlapping ranges.
  */
 long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *reqp,
                afs_uint32 rights, afs_uint32 flags)
@@ -1208,6 +1249,14 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
             }
         }
 
+        if (flags & CM_SCACHESYNC_BULKREAD) {
+            /* Don't allow concurrent fiddling with lock lists */
+            if (scp->flags & CM_SCACHEFLAG_BULKREADING) {
+                osi_Log1(afsd_logp, "CM SyncOp scp 0x%p is BULKREADING want BULKREAD", scp);
+                goto sleep;
+            }
+        }
+
         /* if we get here, we're happy */
         break;
 
@@ -1276,6 +1325,8 @@ long cm_SyncOp(cm_scache_t *scp, cm_buf_t *bufp, cm_user_t *userp, cm_req_t *req
         _InterlockedOr(&scp->flags, CM_SCACHEFLAG_ASYNCSTORING);
     if (flags & CM_SCACHESYNC_LOCK)
         _InterlockedOr(&scp->flags, CM_SCACHEFLAG_LOCKING);
+    if (flags & CM_SCACHESYNC_BULKREAD)
+        _InterlockedOr(&scp->flags, CM_SCACHEFLAG_BULKREADING);
 
     /* now update the buffer pointer */
     if (bufp && (flags & CM_SCACHESYNC_FETCHDATA)) {
@@ -1347,6 +1398,8 @@ void cm_SyncOpDone(cm_scache_t *scp, cm_buf_t *bufp, afs_uint32 flags)
         _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_ASYNCSTORING);
     if (flags & CM_SCACHESYNC_LOCK)
         _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_LOCKING);
+    if (flags & CM_SCACHESYNC_BULKREAD)
+        _InterlockedAnd(&scp->flags, ~CM_SCACHEFLAG_BULKREADING);
 
     /* now update the buffer pointer */
     if (bufp && (flags & CM_SCACHESYNC_FETCHDATA)) {
@@ -1468,10 +1521,20 @@ void cm_MergeStatus(cm_scache_t *dscp,
 #endif /* AFS_FREELANCE_CLIENT */
 
     if (statusp->errorCode != 0) {
-        _InterlockedOr(&scp->flags, CM_SCACHEFLAG_EACCESS);
-       osi_Log2(afsd_logp, "Merge, Failure scp 0x%p code 0x%x", scp, statusp->errorCode);
+       _InterlockedOr(&scp->flags, CM_SCACHEFLAG_EACCESS);
+        switch (statusp->errorCode) {
+        case EACCES:
+        case UAEACCES:
+        case EPERM:
+        case UAEPERM:
+            _InterlockedOr(&scp->flags, CM_SCACHEFLAG_EACCESS);
+        }
+        osi_Log2(afsd_logp, "Merge, Failure scp 0x%p code 0x%x", scp, statusp->errorCode);
 
-       scp->fileType = 0;      /* unknown */
+        if (scp->fid.vnode & 0x1)
+            scp->fileType = CM_SCACHETYPE_DIRECTORY;
+        else
+            scp->fileType = 0; /* unknown */
 
        scp->serverModTime = 0;
        scp->clientModTime = 0;
@@ -1634,7 +1697,8 @@ void cm_MergeStatus(cm_scache_t *dscp,
             if (cm_FidCmp(&scp->fid, &bp->fid) == 0 &&
                  lock_TryMutex(&bp->mx)) {
                 if (bp->refCount == 0 &&
-                    !(bp->flags & CM_BUF_READING | CM_BUF_WRITING | CM_BUF_DIRTY)) {
+                    !(bp->flags & (CM_BUF_READING | CM_BUF_WRITING | CM_BUF_DIRTY)) &&
+                    !(bp->qFlags & CM_BUF_QREDIR)) {
                     prevBp = bp->fileHashBackp;
                     bp->fileHashBackp = bp->fileHashp = NULL;
                     if (prevBp)
@@ -1671,9 +1735,19 @@ void cm_MergeStatus(cm_scache_t *dscp,
      * does not update a mountpoint or symlink by altering the contents of
      * the file data; but the Unix CM does.
      */
-    if (scp->dataVersion != dataVersion && !(flags & CM_MERGEFLAG_FETCHDATA))
+    if (scp->dataVersion != dataVersion && !(flags & CM_MERGEFLAG_FETCHDATA)) {
         scp->mountPointStringp[0] = '\0';
 
+        osi_Log5(afsd_logp, "cm_MergeStatus data version change scp 0x%p cell %u vol %u vn %u uniq %u",
+                 scp, scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
+
+        osi_Log4(afsd_logp, ".... oldDV 0x%x:%x -> newDV 0x%x:%x",
+                 (afs_uint32)((scp->dataVersion >> 32) & 0xFFFFFFFF),
+                 (afs_uint32)(scp->dataVersion & 0xFFFFFFFF),
+                 (afs_uint32)((dataVersion >> 32) & 0xFFFFFFFF),
+                 (afs_uint32)(dataVersion & 0xFFFFFFFF));
+    }
+
     /* We maintain a range of buffer dataVersion values which are considered
      * valid.  This avoids the need to update the dataVersion on each buffer
      * object during an uncontested storeData operation.  As a result this
@@ -1685,6 +1759,19 @@ void cm_MergeStatus(cm_scache_t *dscp,
          scp->bufDataVersionLow == 0)
         scp->bufDataVersionLow = dataVersion;
 
+    if (RDR_Initialized && scp->dataVersion != CM_SCACHE_VERSION_BAD) {
+        if ( ( !(reqp->flags & CM_REQ_SOURCE_REDIR) || !(flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA))) &&
+             scp->dataVersion != dataVersion ) {
+            RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                 scp->fid.unique, scp->fid.hash,
+                                 scp->fileType, AFS_INVALIDATE_DATA_VERSION);
+        } else if ( (reqp->flags & CM_REQ_SOURCE_REDIR) && (flags & (CM_MERGEFLAG_DIROP|CM_MERGEFLAG_STOREDATA)) &&
+                    dataVersion - scp->dataVersion > 1) {
+            RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
+                                 scp->fid.unique, scp->fid.hash,
+                                 scp->fileType, AFS_INVALIDATE_DATA_VERSION);
+        }
+    }
     scp->dataVersion = dataVersion;
 
     /*
@@ -1735,7 +1822,7 @@ void cm_DiscardSCache(cm_scache_t *scp)
     }
     scp->cbExpires = 0;
     scp->volumeCreationDate = 0;
-    _InterlockedAnd(&scp->flags, ~(CM_SCACHEFLAG_CALLBACK | CM_SCACHEFLAG_LOCAL));
+    _InterlockedAnd(&scp->flags, ~(CM_SCACHEFLAG_CALLBACK | CM_SCACHEFLAG_LOCAL | CM_SCACHEFLAG_RDR_IN_USE));
     cm_dnlcPurgedp(scp);
     cm_dnlcPurgevp(scp);
     cm_FreeAllACLEnts(scp);
@@ -1965,7 +2052,7 @@ int cm_DumpSCache(FILE *outputFile, char *cookie, int lock)
             WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
 
             for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
-                cm_file_lock_t * lockp = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
+                cm_file_lock_t * lockp = fileq_to_cm_file_lock_t(q);
                 sprintf(output, "  %s lockp=0x%p scp=0x%p, cm_userp=0x%p offset=0x%I64x len=0x%08I64x type=0x%x "
                         "key=0x%I64x flags=0x%x update=0x%I64u\r\n",
                         cookie, lockp, lockp->scp, lockp->userp, lockp->range.offset, lockp->range.length,
index d14f398..f0ab19a 100644 (file)
@@ -62,6 +62,8 @@ typedef struct cm_file_lock {
                                  * cm_scacheLock] */
 } cm_file_lock_t;
 
+#define fileq_to_cm_file_lock_t(q) ((cm_file_lock_t *)((char *) (q) - offsetof(cm_file_lock_t, fileq)))
+
 #define CM_FILELOCK_FLAG_DELETED         0x01
 #define CM_FILELOCK_FLAG_LOST            0x02
 
@@ -223,8 +225,16 @@ typedef struct cm_scache {
                                        Holds queue of
                                        cm_scache_waiter_t
                                        objects. Protected by
-                                       cm_cacheLock. */
+                                       cm_scacheLock. */
     osi_queue_t * waitQueueT;       /* locked by cm_scacheLock */
+
+    /* redirector state - protected by scp->rw */
+    osi_queue_t * redirQueueH;      /* LRU queue of buffers for this
+                                       file that are assigned to the
+                                       afsredir kernel module. */
+    osi_queue_t * redirQueueT;
+    afs_uint32    redirBufCount;    /* Number of buffers held by the redirector */
+    time_t        redirLastAccess;  /* last time redir accessed the vnode */
 } cm_scache_t;
 
 /* dataVersion */
@@ -277,6 +287,8 @@ typedef struct cm_scache {
 #define CM_SCACHEFLAG_EACCESS           0x200000 /* Bulk Stat returned EACCES */
 #define CM_SCACHEFLAG_SMB_FID          0x400000
 #define CM_SCACHEFLAG_LOCAL             0x800000 /* Locally modified */
+#define CM_SCACHEFLAG_BULKREADING       0x1000000/* Bulk read in progress */
+#define CM_SCACHEFLAG_RDR_IN_USE        0x2000000/* in use by Redirector; advisory */
 
 /* sync flags for calls to the server.  The CM_SCACHEFLAG_FETCHING,
  * CM_SCACHEFLAG_STORING and CM_SCACHEFLAG_SIZESTORING flags correspond to the
@@ -314,6 +326,8 @@ typedef struct cm_scache {
 #define CM_SCACHESYNC_FORCECB          0x200000/* when calling cm_GetCallback()
                                                  * set the force flag */
 
+#define CM_SCACHESYNC_BULKREAD          0x400000/* reading many buffers */
+
 /* flags for cm_RecycleSCache  */
 #define CM_SCACHE_RECYCLEFLAG_DESTROY_BUFFERS  0x1
 
index 10f022b..32eeff8 100644 (file)
@@ -172,13 +172,14 @@ cm_PingServer(cm_server_t *tsp)
     }  /* got an unauthenticated connection to this server */
 
     lock_ObtainMutex(&tsp->mx);
-    if (code >= 0 || code == RXGEN_OPCODE) {
+    if (code >= 0 || code == RXGEN_OPCODE || code == CM_RX_RETRY_BUSY_CALL) {
        /* mark server as up */
        _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
         tsp->downTime = 0;
 
        /* we currently handle 32-bits of capabilities */
-       if (code != RXGEN_OPCODE && caps.Capabilities_len > 0) {
+       if (code != RXGEN_OPCODE && code != CM_RX_RETRY_BUSY_CALL &&
+            caps.Capabilities_len > 0) {
            tsp->capabilities = caps.Capabilities_val[0];
            xdr_free((xdrproc_t) xdr_Capabilities, &caps);
            caps.Capabilities_len = 0;
@@ -455,13 +456,15 @@ static void cm_CheckServersMulti(afs_uint32 flags, cm_cell_t *cellp)
             lock_ObtainMutex(&tsp->mx);
             wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
 
-            if (results[i] >= 0 || results[i] == RXGEN_OPCODE)  {
+            if (results[i] >= 0 || results[i] == RXGEN_OPCODE ||
+                results[i] == CM_RX_RETRY_BUSY_CALL)  {
                 /* mark server as up */
                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
                 tsp->downTime = 0;
 
                 /* we currently handle 32-bits of capabilities */
-                if (results[i] != RXGEN_OPCODE && caps[i].Capabilities_len > 0) {
+                if (results[i] != RXGEN_OPCODE && results[i] != CM_RX_RETRY_BUSY_CALL &&
+                    caps[i].Capabilities_len > 0) {
                     tsp->capabilities = caps[i].Capabilities_val[0];
                     xdr_free((xdrproc_t) xdr_Capabilities, &caps[i]);
                     caps[i].Capabilities_len = 0;
@@ -620,7 +623,7 @@ static void cm_CheckServersMulti(afs_uint32 flags, cm_cell_t *cellp)
             lock_ObtainMutex(&tsp->mx);
             wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
 
-            if (results[i] >= 0)  {
+            if (results[i] >= 0 || results[i] == CM_RX_RETRY_BUSY_CALL)  {
                 /* mark server as up */
                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
                 tsp->downTime = 0;
index d0c04e9..aff45a8 100644 (file)
@@ -197,6 +197,10 @@ long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
     if (desiredAccess == DELETE)
         goto done_2;
 
+    /* Always allow reading attributes (Hidden, System, Readonly, ...) */
+    if (desiredAccess == FILE_READ_ATTRIBUTES)
+        goto done_2;
+
     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
 
@@ -1327,10 +1331,16 @@ notfound:
     return code;
 }
 
-int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
+int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
 {
     clientchar_t *tp;
     int prefixCount;
+#ifdef _WIN64
+    int use_sysname64 = 0;
+
+    if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
+        use_sysname64 = 1;
+#endif
 
     tp = cm_ClientStrRChr(inp, '@');
     if (tp == NULL)
@@ -1343,6 +1353,11 @@ int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, uns
     if (outp == NULL)
         return 1;
 
+#ifdef _WIN64
+    if (use_sysname64 && index >= cm_sysName64Count)
+        return -1;
+    else
+#endif
     if (index >= cm_sysNameCount)
         return -1;
 
@@ -1350,8 +1365,14 @@ int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, uns
     prefixCount = (int)(tp - inp);
 
     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);      /* copy out "a." from "a.@sys" */
-    outp[prefixCount] = 0;             /* null terminate the "a." */
-    cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
+    outp[prefixCount] = 0;                                     /* null terminate the "a." */
+#ifdef _WIN64
+    if (use_sysname64)
+        cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
+    else
+#endif
+        cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
+
     return 1;
 }
 
@@ -1494,9 +1515,9 @@ long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *us
         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
     }
 
-    if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
+    if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
-            code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
+            code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
             if (code > 0) {
                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
 #ifdef DEBUG_REFCOUNT
@@ -1654,6 +1675,11 @@ long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
     cm_dnlcRemove(dscp, cnamep);
     if (code == 0) {
         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
+        if (RDR_Initialized &&
+            scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
+            RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
+                                 dscp->fid.unique, dscp->fid.hash,
+                                 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
     } else if (code == CM_ERROR_NOSUCHFILE) {
        /* windows would not have allowed the request to delete the file
         * if it did not believe the file existed.  therefore, we must
@@ -1685,6 +1711,11 @@ 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");
         }
     }
 
@@ -2789,7 +2820,7 @@ long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *a
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -2973,7 +3004,7 @@ long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *
     /* can't create names with @sys in them; must expand it manually first.
      * return "invalid request" if they try.
      */
-    if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
+    if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
         return CM_ERROR_ATSYS;
     }
 
@@ -3171,6 +3202,10 @@ 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);
+        if (RDR_Initialized)
+            RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
+                                 dscp->fid.unique, dscp->fid.hash,
+                                 dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
     }
     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
     lock_ReleaseWrite(&dscp->rw);
@@ -3452,6 +3487,11 @@ long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_
             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");
         }
     }
 
@@ -3830,6 +3870,9 @@ long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep
     cm_DiscardSCache(oldScp);
     lock_ReleaseWrite(&oldScp->rw);
 
+    if (RDR_Initialized)
+        RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
+                              oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
   done:
     if (oldScp)
         cm_ReleaseSCache(oldScp);
@@ -5108,6 +5151,12 @@ long cm_UnlockByKey(cm_scache_t * scp,
 
             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
 
+            cm_ReleaseUser(fileLock->userp);
+            cm_ReleaseSCacheNoLock(scp);
+
+            fileLock->userp = NULL;
+            fileLock->scp = NULL;
+
             n_unlocks++;
         }
     }
@@ -5122,9 +5171,9 @@ long cm_UnlockByKey(cm_scache_t * scp,
         return 0;
     }
 
-    osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
-
+    code = cm_IntUnlock(scp, userp, reqp);
     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
+
     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
              (int)(signed char) scp->serverLock);
@@ -5132,6 +5181,7 @@ long cm_UnlockByKey(cm_scache_t * scp,
     return code;
 }
 
+/* Called with scp->rw held */
 long cm_Unlock(cm_scache_t *scp,
                unsigned char sLockType,
                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
@@ -5234,8 +5284,20 @@ long cm_Unlock(cm_scache_t *scp,
     }
 
     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
+
+    if (userp != NULL) {
+        cm_ReleaseUser(fileLock->userp);
+    } else {
+        userp = fileLock->userp;
+        release_userp = TRUE;
+    }
+    cm_ReleaseSCacheNoLock(scp);
+    fileLock->userp = NULL;
+    fileLock->scp = NULL;
     lock_ReleaseWrite(&cm_scacheLock);
 
+    code = cm_IntUnlock(scp, userp, reqp);
+
     if (release_userp) {
         cm_ReleaseUser(userp);
         release_userp = FALSE;
@@ -5321,15 +5383,16 @@ void cm_CheckLocks()
             fileLock->userp = NULL;
             fileLock->scp = NULL;
 
-            lock_ReleaseWrite(&cm_scacheLock);
-            lock_ObtainWrite(&scp->rw);
-            code = cm_IntUnlock(scp, userp, &req);
-            lock_ReleaseWrite(&scp->rw);
-
-            cm_ReleaseUser(fileLock->userp);
-            lock_ObtainWrite(&cm_scacheLock);
-            cm_ReleaseSCacheNoLock(scp);
+            if (scp && userp) {
+                lock_ReleaseWrite(&cm_scacheLock);
+                lock_ObtainWrite(&scp->rw);
+                code = cm_IntUnlock(scp, userp, &req);
+                lock_ReleaseWrite(&scp->rw);
 
+                cm_ReleaseUser(userp);
+                lock_ObtainWrite(&cm_scacheLock);
+                cm_ReleaseSCacheNoLock(scp);
+            }
             osi_QRemove(&cm_allFileLocks, q);
             cm_PutFileLock(fileLock);
 
@@ -5830,7 +5893,7 @@ cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16
 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
 {
     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
-        ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
+        ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
 }
 
 void cm_ReleaseAllLocks(void)
index fcabb74..b251f4a 100644 (file)
@@ -149,7 +149,7 @@ extern long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
                             cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
                             cm_user_t *userp, cm_req_t *reqp);
 
-extern int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch,
+extern int cm_ExpandSysName(cm_req_t *reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch,
                             unsigned int sysNameIndex);
 
 extern long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp);
@@ -197,15 +197,14 @@ extern long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
                     int allowWait, cm_user_t *userp, cm_req_t *reqp,
                     cm_file_lock_t **lockpp);
 
-#define CM_UNLOCK_BY_FID       0x0001
-
 extern long cm_UnlockByKey(cm_scache_t * scp,
                            cm_key_t key,
                            afs_uint32 flags,
                            cm_user_t * userp,
                            cm_req_t * reqp);
 
-#define CM_UNLOCK_FLAG_MATCH_RANGE      0x01
+#define CM_UNLOCK_FLAG_BY_FID          0x0001
+#define CM_UNLOCK_FLAG_MATCH_RANGE      0x0002
 
 extern long cm_Unlock(cm_scache_t *scp, unsigned char sLockType,
                       LARGE_INTEGER LOffset, LARGE_INTEGER LLength, cm_key_t key,
index bb15846..2097ab3 100644 (file)
 #include "smb.h"
 #include <WINNT/afsreg.h>
 
+extern DWORD RDR_NetworkAddrChange(void);
+extern DWORD RDR_VolumeStatus(ULONG cellID, ULONG volID, BOOLEAN online);
+extern DWORD RDR_NetworkStatus(BOOLEAN status);
+
 HMODULE hVolStatus = NULL;
 dll_VolStatus_Funcs_t dll_funcs;
 cm_VolStatus_Funcs_t cm_funcs;
 
 static char volstat_NetbiosName[64] = "";
 
+static DWORD RDR_Notifications = 0;
+
+rdr_volstat_evt_t *rdr_evtH = NULL;
+rdr_volstat_evt_t *rdr_evtT = NULL;
+
+static EVENT_HANDLE rdr_q_event = NULL;
+
+static osi_mutex_t rdr_evt_lock;
+
+void
+cm_VolStatus_SetRDRNotifications(DWORD onoff)
+{
+    RDR_Notifications = onoff;
+}
+
 afs_uint32
 cm_VolStatus_Active(void)
 {
@@ -77,16 +96,21 @@ cm_VolStatus_Initialization(void)
         code = RegQueryValueEx(parmKey, "VolStatusHandler", NULL, NULL,
                                 (BYTE *) &wd, &dummyLen);
 
-        if (code == 0) {
+        if (code == ERROR_SUCCESS) {
             dummyLen = sizeof(volstat_NetbiosName);
             code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,
                                    (BYTE *)volstat_NetbiosName, &dummyLen);
         }
+        if (code == ERROR_SUCCESS && wd[0])
+            hVolStatus = LoadLibrary(wd);
+
+        dummyLen = sizeof(wd);
+        code = RegQueryValueEx(parmKey, "RDRVolStatNotify", NULL, NULL,
+                                (BYTE *) &RDR_Notifications, &dummyLen);
+
         RegCloseKey (parmKey);
     }
 
-    if (code == ERROR_SUCCESS && wd[0])
-        hVolStatus = LoadLibrary(wd);
     if (hVolStatus) {
         (FARPROC) dll_VolStatus_Initialization = GetProcAddress(hVolStatus, "@VolStatus_Initialization@8");
         if (dll_VolStatus_Initialization) {
@@ -106,6 +130,23 @@ cm_VolStatus_Initialization(void)
         }
     }
 
+    if (RDR_Initialized && RDR_Notifications) {
+        long pid;
+        thread_t phandle;
+
+        lock_InitializeMutex(&rdr_evt_lock, "rdr_evt_lock", LOCK_HIERARCHY_IGNORE);
+
+        phandle = thrd_Create((SecurityAttrib) NULL, 0,
+                                       (ThreadFunc) cm_VolStatus_DeliverNotifications,
+                                       0, 0, &pid, "cm_VolStatus_DeliverNotifications");
+        osi_assertx(phandle != NULL, "cm_VolStatus_DeliverNotifications thread creation failure");
+        thrd_CloseHandle(phandle);
+
+        rdr_q_event = thrd_CreateEvent(NULL, TRUE, TRUE, "rdr_q_event");
+        if ( GetLastError() == ERROR_ALREADY_EXISTS )
+            afsi_log("Event Object Already Exists: rdr_q_event");
+    }
+
     osi_Log1(afsd_logp,"cm_VolStatus_Initialization 0x%x", code);
 
     return code;
@@ -119,6 +160,10 @@ cm_VolStatus_Finalize(void)
 {
     osi_Log1(afsd_logp,"cm_VolStatus_Finalize handle 0x%x", hVolStatus);
 
+    if ( RDR_Initialized && RDR_Notifications ) {
+        CloseHandle(rdr_q_event);
+    }
+
     if (hVolStatus == NULL)
         return 0;
 
@@ -180,6 +225,18 @@ cm_VolStatus_Network_Started(const char * netbios32)
 {
     long code = 0;
 
+    if (RDR_Initialized && RDR_Notifications) {
+        rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
+        evp->type = netstatus;
+        evp->netstatus_data.status = TRUE;
+
+        lock_ObtainMutex(&rdr_evt_lock);
+        osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+        lock_ReleaseMutex(&rdr_evt_lock);
+
+        thrd_SetEvent(rdr_q_event);
+    }
+
     if (hVolStatus == NULL)
         return 0;
 
@@ -205,6 +262,18 @@ cm_VolStatus_Network_Stopped(const char * netbios32)
 {
     long code = 0;
 
+    if (RDR_Initialized && RDR_Notifications) {
+        rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
+        evp->type = netstatus;
+        evp->netstatus_data.status = FALSE;
+
+        lock_ObtainMutex(&rdr_evt_lock);
+        osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+        lock_ReleaseMutex(&rdr_evt_lock);
+
+        thrd_SetEvent(rdr_q_event);
+    }
+
     if (hVolStatus == NULL)
         return 0;
 
@@ -227,6 +296,17 @@ cm_VolStatus_Network_Addr_Change(void)
 {
     long code = 0;
 
+    if (RDR_Initialized && RDR_Notifications) {
+        rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
+        evp->type = addrchg;
+
+        lock_ObtainMutex(&rdr_evt_lock);
+        osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+        lock_ReleaseMutex(&rdr_evt_lock);
+
+        thrd_SetEvent(rdr_q_event);
+    }
+
     if (hVolStatus == NULL)
         return 0;
 
@@ -243,6 +323,34 @@ cm_VolStatus_Change_Notification(afs_uint32 cellID, afs_uint32 volID, enum volst
 {
     long code = 0;
 
+    if (RDR_Initialized && RDR_Notifications) {
+        rdr_volstat_evt_t *evp = (rdr_volstat_evt_t *)malloc(sizeof(rdr_volstat_evt_t));
+        switch (status) {
+        case vl_alldown:
+        case vl_offline:
+            evp->type = volstatus;
+            evp->volstatus_data.cellID = cellID;
+            evp->volstatus_data.volID = volID;
+            evp->volstatus_data.online = FALSE;
+
+            lock_ObtainMutex(&rdr_evt_lock);
+            osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+            lock_ReleaseMutex(&rdr_evt_lock);
+            break;
+        default:
+            evp->type = volstatus;
+            evp->volstatus_data.cellID = cellID;
+            evp->volstatus_data.volID = volID;
+            evp->volstatus_data.online = TRUE;
+
+            lock_ObtainMutex(&rdr_evt_lock);
+            osi_QAddH((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+            lock_ReleaseMutex(&rdr_evt_lock);
+        }
+
+        thrd_SetEvent(rdr_q_event);
+    }
+
     if (hVolStatus == NULL)
         return 0;
 
@@ -446,3 +554,38 @@ cm_VolStatus_Path_To_DFSlink(const char * share, const char * path, afs_uint32 *
     osi_Log1(afsd_logp,"cm_VolStatus_Path_To_DFSlink code 0x%x",code);
     return code;
 }
+
+void
+cm_VolStatus_DeliverNotifications(void * dummy)
+{
+    rdr_volstat_evt_t *evp, *evprev;
+    afs_uint32 code;
+
+    while ( TRUE ) {
+        code = thrd_WaitForSingleObject_Event( rdr_q_event, INFINITE );
+
+        lock_ObtainMutex(&rdr_evt_lock);
+        for (evp = rdr_evtT; evp; evp = evprev)
+        {
+            evprev = (rdr_volstat_evt_t *) osi_QPrev(&evp->q);
+            osi_QRemoveHT((osi_queue_t **) &rdr_evtH, (osi_queue_t **) &rdr_evtT, &evp->q);
+            lock_ReleaseMutex(&rdr_evt_lock);
+
+            switch ( evp->type ) {
+            case addrchg:
+                RDR_NetworkAddrChange();
+                break;
+            case volstatus:
+                RDR_VolumeStatus(evp->volstatus_data.cellID, evp->volstatus_data.volID, evp->volstatus_data.online);
+                break;
+            case netstatus:
+                RDR_NetworkStatus(evp->netstatus_data.status);
+                break;
+            }
+
+            free(evp);
+            lock_ObtainMutex(&rdr_evt_lock);
+        }
+        lock_ReleaseMutex(&rdr_evt_lock);
+    }
+}
index 245fcfc..533e596 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007 Secure Endpoints Inc.
+ * Copyright (c) 2007-2011 Secure Endpoints Inc.
  *
  * All rights reserved.
  *
@@ -73,6 +73,11 @@ extern long cm_VolStatus_Notify_DFS_Mapping(cm_scache_t *scp,
 
 extern long cm_VolStatus_Invalidate_DFS_Mapping(cm_scache_t *scp);
 
+extern void cm_VolStatus_DeliverNotifications(void * dummy);
+
+extern void cm_VolStatus_SetRDRNotifications(DWORD onoff);
+
+
 #define DLL_VOLSTATUS_FUNCS_VERSION 2
 typedef struct dll_VolStatus_Funcs {
     afs_uint32          version;
@@ -112,3 +117,22 @@ struct VolStatTest {
 #define VOLSTAT_TEST_NETWORK_UP      4
 #define VOLSTAT_TEST_NETWORK_DOWN    8
 
+/* redirector - native file system */
+
+enum rdr_event_type { addrchg, volstatus, netstatus };
+
+typedef struct rdr_volstat_evt {
+    osi_queue_t q;
+    enum rdr_event_type type;
+    union {
+        struct {
+            ULONG cellID;
+            ULONG volID;
+            BOOLEAN online;
+        } volstatus_data;
+        struct {
+            BOOLEAN status;
+        } netstatus_data;
+    };
+} rdr_volstat_evt_t;
+
index d0cfdc3..5efb9f3 100644 (file)
@@ -312,7 +312,7 @@ long cm_UpdateVolumeLocation(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *
         lock_ReleaseWrite(&volp->rw);
 
         if (cellp->flags & CM_CELLFLAG_VLSERVER_INVALID)
-            cm_UpdateCell(cellp, 0);
+             cm_UpdateCell(cellp, 0);
 
         /* now we have volume structure locked and held; make RPC to fill it */
         code = cm_GetEntryByName(cellp, volp->namep, &vldbEntry, &nvldbEntry,
index 8423fb9..3a99343 100644 (file)
@@ -4839,7 +4839,7 @@ ChGrpCmd(struct cmd_syndesc *as, void *arock)
     for(ti=as->parms[1].items; ti; ti=ti->next) {
         cm_fid_t fid;
         afs_uint32 filetype;
-       char cell[CELL_MAXNAMELEN];
+        char cell[CELL_MAXNAMELEN];
 
         /* once per file */
         memset(&fid, 0, sizeof(fid));
index f69c003..88638b6 100644 (file)
@@ -24,17 +24,21 @@ SOFTWARE.
 */
 
 
+#include <afsconfig.h>
+#include <afs/param.h>
+#include <roken.h>
+
+
 //#pragma keyword("interface",on)
 //#define interface struct
 #define SECURITY_WIN32
-#include "afslogon.h"
-
 #if (_WIN32_WINNT < 0x0500)
 #error _WIN32_WINNT < 0x0500
 #endif
 
 /**/
 #include <security.h>
+#include <winioctl.h>
 #include <sddl.h>
 #include <unknwn.h>
 #include <oaidl.h>
@@ -42,171 +46,177 @@ SOFTWARE.
 #include <adshlp.h>
 /**/
 
+#include "afslogon.h"
+
+#include "..\afsrdr\common\AFSUserDefines.h"
+#include "..\afsrdr\common\AFSUserIoctl.h"
+#include "..\afsrdr\common\AFSUserStructs.h"
+#include "..\afsrdr\common\AFSProvider.h"
+
 #define SEC_ERR_VALUE(v) if (status==v) return #v
 
 char * _get_sec_err_text(SECURITY_STATUS status) {
-       SEC_ERR_VALUE(SEC_E_OK);
-       SEC_ERR_VALUE(SEC_I_CONTINUE_NEEDED);
-       SEC_ERR_VALUE(SEC_I_COMPLETE_NEEDED);
-       SEC_ERR_VALUE(SEC_I_COMPLETE_AND_CONTINUE);
-       SEC_ERR_VALUE(SEC_E_INCOMPLETE_MESSAGE);
-       SEC_ERR_VALUE(SEC_I_INCOMPLETE_CREDENTIALS);
-       SEC_ERR_VALUE(SEC_E_INVALID_HANDLE);
-       SEC_ERR_VALUE(SEC_E_TARGET_UNKNOWN);
-       SEC_ERR_VALUE(SEC_E_LOGON_DENIED);
-       SEC_ERR_VALUE(SEC_E_INTERNAL_ERROR);
-       SEC_ERR_VALUE(SEC_E_NO_CREDENTIALS);
-       SEC_ERR_VALUE(SEC_E_NO_AUTHENTICATING_AUTHORITY);
-       SEC_ERR_VALUE(SEC_E_INSUFFICIENT_MEMORY);
-       SEC_ERR_VALUE(SEC_E_INVALID_TOKEN);
-       SEC_ERR_VALUE(SEC_E_UNSUPPORTED_FUNCTION);
-       SEC_ERR_VALUE(SEC_E_WRONG_PRINCIPAL);
-       return "Unknown";
+    SEC_ERR_VALUE(SEC_E_OK);
+    SEC_ERR_VALUE(SEC_I_CONTINUE_NEEDED);
+    SEC_ERR_VALUE(SEC_I_COMPLETE_NEEDED);
+    SEC_ERR_VALUE(SEC_I_COMPLETE_AND_CONTINUE);
+    SEC_ERR_VALUE(SEC_E_INCOMPLETE_MESSAGE);
+    SEC_ERR_VALUE(SEC_I_INCOMPLETE_CREDENTIALS);
+    SEC_ERR_VALUE(SEC_E_INVALID_HANDLE);
+    SEC_ERR_VALUE(SEC_E_TARGET_UNKNOWN);
+    SEC_ERR_VALUE(SEC_E_LOGON_DENIED);
+    SEC_ERR_VALUE(SEC_E_INTERNAL_ERROR);
+    SEC_ERR_VALUE(SEC_E_NO_CREDENTIALS);
+    SEC_ERR_VALUE(SEC_E_NO_AUTHENTICATING_AUTHORITY);
+    SEC_ERR_VALUE(SEC_E_INSUFFICIENT_MEMORY);
+    SEC_ERR_VALUE(SEC_E_INVALID_TOKEN);
+    SEC_ERR_VALUE(SEC_E_UNSUPPORTED_FUNCTION);
+    SEC_ERR_VALUE(SEC_E_WRONG_PRINCIPAL);
+    return "Unknown";
 }
 
 #undef SEC_ERR_VALUE
 
-DWORD LogonSSP(PLUID lpLogonId, PCtxtHandle outCtx) {
-       DWORD code = 1;
+DWORD
+LogonSSP(PLUID lpLogonId, PCtxtHandle outCtx) {
+    DWORD code = 1;
     SECURITY_STATUS status;
-       CredHandle creds;
-       CtxtHandle ctxclient,ctxserver;
-       TimeStamp expiry;
-       BOOL cont = TRUE;
-       BOOL first = TRUE;
-       SecBufferDesc sdescc,sdescs;
-       SecBuffer stokc,stoks;
-       ULONG cattrs,sattrs;
-       int iters = 10;
-
-       outCtx->dwLower = 0;
-       outCtx->dwUpper = 0;
-
-       cattrs = 0;
-       sattrs = 0;
-
-       status = AcquireCredentialsHandle(
-               NULL,
-               "Negotiate",
-               SECPKG_CRED_BOTH,
-               lpLogonId,
-               NULL,
-               NULL,
-               NULL,
-               &creds,
-               &expiry);
-
-       if (status != SEC_E_OK) {
-               DebugEvent("AcquireCredentialsHandle failed: %lX", status);
-               goto ghp_0;
-       }
-
-       sdescc.cBuffers = 1;
-       sdescc.pBuffers = &stokc;
-       sdescc.ulVersion = SECBUFFER_VERSION;
-
-       stokc.BufferType = SECBUFFER_TOKEN;
-       stokc.cbBuffer = 0;
-       stokc.pvBuffer = NULL;
-
-       sdescs.cBuffers = 1;
-       sdescs.pBuffers = &stoks;
-       sdescs.ulVersion = SECBUFFER_VERSION;
-
-       stoks.BufferType = SECBUFFER_TOKEN;
-       stoks.cbBuffer = 0;
-       stoks.pvBuffer = NULL;
-
-       do {
-               status = InitializeSecurityContext(
-                       &creds,
-                       ((first)? NULL:&ctxclient),
-            NULL,
-                       ISC_REQ_DELEGATE | ISC_REQ_ALLOCATE_MEMORY,
-                       0,
-                       SECURITY_NATIVE_DREP,
-                       ((first)?NULL:&sdescs),
-                       0,
-                       &ctxclient,
-                       &sdescc,
-                       &cattrs,
-                       &expiry
-                       );
-
-               DebugEvent("InitializeSecurityContext returns status[%lX](%s)",status,_get_sec_err_text(status));
-
-               if (!first) FreeContextBuffer(stoks.pvBuffer);
-
-               if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
-                       CompleteAuthToken(&ctxclient, &sdescc);
-               }
-
-               if (status != SEC_I_CONTINUE_NEEDED && status != SEC_I_COMPLETE_AND_CONTINUE) {
-                       cont = FALSE;
-               }
-
-               if (!stokc.cbBuffer && !cont) {
-                       DebugEvent("Breaking out after InitializeSecurityContext");
-                       break;
-               }
-
-               status = AcceptSecurityContext(
-                       &creds,
-                       ((first)?NULL:&ctxserver),
-                       &sdescc,
-                       ASC_REQ_DELEGATE | ASC_REQ_ALLOCATE_MEMORY,
-                       SECURITY_NATIVE_DREP,
-                       &ctxserver,
-                       &sdescs,
-                       &sattrs,
-                       &expiry);
-
-               DebugEvent("AcceptSecurityContext returns status[%lX](%s)", status, _get_sec_err_text(status));
-
-               FreeContextBuffer(stokc.pvBuffer);
-
-               if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
-                       CompleteAuthToken(&ctxserver,&sdescs);
-               }
-
-               if (status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
-                       cont = TRUE;
-               }
-
-               if (!cont)
-                       FreeContextBuffer(stoks.pvBuffer);
-
-               first = FALSE;
-               iters--; /* just in case, hard limit on loop */
-       } while (cont && iters);
-
-       if (sattrs & ASC_RET_DELEGATE) {
-               DebugEvent("Received delegate context");
-               *outCtx = ctxserver;
-               code = 0;
-       } else {
-               DebugEvent("Didn't receive delegate context");
-               outCtx->dwLower = 0;
-               outCtx->dwUpper = 0;
-               DeleteSecurityContext(&ctxserver);
-       }
-
-       DeleteSecurityContext(&ctxclient);
+    CredHandle creds;
+    CtxtHandle ctxclient,ctxserver;
+    TimeStamp expiry;
+    BOOL cont = TRUE;
+    BOOL first = TRUE;
+    SecBufferDesc sdescc,sdescs;
+    SecBuffer stokc,stoks;
+    ULONG cattrs,sattrs;
+    int iters = 10;
+
+    outCtx->dwLower = 0;
+    outCtx->dwUpper = 0;
+
+    cattrs = 0;
+    sattrs = 0;
+
+    status = AcquireCredentialsHandle( NULL,
+                                       "Negotiate",
+                                       SECPKG_CRED_BOTH,
+                                       lpLogonId,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &creds,
+                                       &expiry);
+
+    if (status != SEC_E_OK) {
+        DebugEvent("AcquireCredentialsHandle failed: %lX", status);
+        goto ghp_0;
+    }
+
+    sdescc.cBuffers = 1;
+    sdescc.pBuffers = &stokc;
+    sdescc.ulVersion = SECBUFFER_VERSION;
+
+    stokc.BufferType = SECBUFFER_TOKEN;
+    stokc.cbBuffer = 0;
+    stokc.pvBuffer = NULL;
+
+    sdescs.cBuffers = 1;
+    sdescs.pBuffers = &stoks;
+    sdescs.ulVersion = SECBUFFER_VERSION;
+
+    stoks.BufferType = SECBUFFER_TOKEN;
+    stoks.cbBuffer = 0;
+    stoks.pvBuffer = NULL;
+
+    do {
+        status = InitializeSecurityContext( &creds,
+                                            ((first)? NULL:&ctxclient),
+                                            NULL,
+                                            ISC_REQ_DELEGATE | ISC_REQ_ALLOCATE_MEMORY,
+                                            0,
+                                            SECURITY_NATIVE_DREP,
+                                            ((first)?NULL:&sdescs),
+                                            0,
+                                            &ctxclient,
+                                            &sdescc,
+                                            &cattrs,
+                                            &expiry
+                                            );
+
+        DebugEvent("InitializeSecurityContext returns status[%lX](%s)",status,_get_sec_err_text(status));
+
+        if (!first) FreeContextBuffer(stoks.pvBuffer);
+
+        if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
+            CompleteAuthToken(&ctxclient, &sdescc);
+        }
+
+        if (status != SEC_I_CONTINUE_NEEDED && status != SEC_I_COMPLETE_AND_CONTINUE) {
+            cont = FALSE;
+        }
+
+        if (!stokc.cbBuffer && !cont) {
+            DebugEvent("Breaking out after InitializeSecurityContext");
+            break;
+        }
+
+        status = AcceptSecurityContext( &creds,
+                                        ((first)?NULL:&ctxserver),
+                                        &sdescc,
+                                        ASC_REQ_DELEGATE | ASC_REQ_ALLOCATE_MEMORY,
+                                        SECURITY_NATIVE_DREP,
+                                        &ctxserver,
+                                        &sdescs,
+                                        &sattrs,
+                                        &expiry);
+
+        DebugEvent("AcceptSecurityContext returns status[%lX](%s)", status, _get_sec_err_text(status));
+
+        FreeContextBuffer(stokc.pvBuffer);
+
+        if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
+            CompleteAuthToken(&ctxserver,&sdescs);
+        }
+
+        if (status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
+            cont = TRUE;
+        }
+
+        if (!cont)
+            FreeContextBuffer(stoks.pvBuffer);
+
+        first = FALSE;
+        iters--; /* just in case, hard limit on loop */
+    } while (cont && iters);
+
+    if (sattrs & ASC_RET_DELEGATE) {
+        DebugEvent("Received delegate context");
+        *outCtx = ctxserver;
+        code = 0;
+    } else {
+        DebugEvent("Didn't receive delegate context");
+        outCtx->dwLower = 0;
+        outCtx->dwUpper = 0;
+        DeleteSecurityContext(&ctxserver);
+    }
+
+    DeleteSecurityContext(&ctxclient);
     FreeCredentialsHandle(&creds);
-ghp_0:
-       return code;
+  ghp_0:
+    return code;
 }
 
-DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWSTR domain) {
-       DWORD code = 1; /* default is failure */
-       NTSTATUS rv = 0;
-       HRESULT hr = S_OK;
-       LPWSTR p = NULL;
-       WCHAR adsPath[MAX_PATH] = L"";
-       BOOL coInitialized = FALSE;
+DWORD
+QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWSTR domain) {
+    DWORD code = 1; /* default is failure */
+    NTSTATUS rv = 0;
+    HRESULT hr = S_OK;
+    LPWSTR p = NULL;
+    WCHAR adsPath[MAX_PATH] = L"";
+    BOOL coInitialized = FALSE;
     CHAR ansidomain[256], *a;
 
-       homePath[0] = '\0';
+    homePath[0] = '\0';
 
     /* I trust this is an ASCII domain name */
     for ( p=domain, a=ansidomain; *a = (CHAR)*p; p++, a++);
@@ -227,7 +237,9 @@ DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWS
                                IID_IADsNameTranslate,
                                (void**)&pNto);
 
-        if (FAILED(hr)) { DebugEvent("Can't create nametranslate object"); }
+        if (FAILED(hr)) {
+            DebugEvent("Can't create nametranslate object");
+        }
         else {
             hr = pNto->Init(ADS_NAME_INITTYPE_GC,L"");
             if (FAILED(hr)) {
@@ -242,7 +254,9 @@ DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWS
 
             if (!FAILED(hr)) {
                 hr = pNto->Set(ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, p);
-                if (FAILED(hr)) { DebugEvent("Can't set sid string"); }
+                if (FAILED(hr)) {
+                    DebugEvent("Can't set sid string");
+                }
                 else {
                     BSTR bstr;
 
@@ -262,103 +276,104 @@ DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWS
         }
 
         LocalFree(p);
-
     } else {
         DebugEvent("Can't convert sid to string");
     }
 
-       if (adsPath[0]) {
-               WCHAR fAdsPath[MAX_PATH];
-               IADsUser *pAdsUser;
-               BSTR bstHomeDir = NULL;
+    if (adsPath[0]) {
+        WCHAR fAdsPath[MAX_PATH];
+        IADsUser *pAdsUser;
+        BSTR bstHomeDir = NULL;
 
-               hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath);
-               if (hr != S_OK) {
-                       DebugEvent("Can't format full adspath");
-                       goto cleanup;
-               }
+        hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath);
+        if (hr != S_OK) {
+            DebugEvent("Can't format full adspath");
+            goto cleanup;
+        }
 
-               DebugEvent("Trying adsPath=[%S]", fAdsPath);
+        DebugEvent("Trying adsPath=[%S]", fAdsPath);
 
-               hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser);
-               if (hr != S_OK) {
-                       DebugEvent("Can't open IADs object");
-                       goto cleanup;
-               }
+        hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser);
+        if (hr != S_OK) {
+            DebugEvent("Can't open IADs object");
+            goto cleanup;
+        }
 
         hr = pAdsUser->get_Profile(&bstHomeDir);
-               if (hr != S_OK) {
-                       DebugEvent("Can't get profile directory");
-                       goto cleanup_homedir_section;
-               }
+        if (hr != S_OK) {
+            DebugEvent("Can't get profile directory");
+            goto cleanup_homedir_section;
+        }
 
-               wcstombs(homePath, bstHomeDir, homePathLen);
+        wcstombs(homePath, bstHomeDir, homePathLen);
 
-               DebugEvent("Got homepath [%s]", homePath);
+        DebugEvent("Got homepath [%s]", homePath);
 
-               SysFreeString(bstHomeDir);
+        SysFreeString(bstHomeDir);
 
-               code = 0;
+        code = 0;
 
-cleanup_homedir_section:
-               pAdsUser->Release();
-       }
+      cleanup_homedir_section:
+        pAdsUser->Release();
+    }
 
-cleanup:
-       if (coInitialized)
-               CoUninitialize();
+  cleanup:
+    if (coInitialized)
+        CoUninitialize();
 
-       return code;
+    return code;
 }
 
 /* Try to determine the user's AD home path.  *homePath is assumed to be at least MAXPATH bytes.
    If successful, opt.flags is updated with LOGON_FLAG_AD_REALM to indicate that we are dealing with
    an AD realm. */
-DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonOptions_t * opt) {
-       CtxtHandle ctx;
-       DWORD code = 0;
-       SECURITY_STATUS status;
+DWORD
+GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonOptions_t * opt) {
+    CtxtHandle ctx;
+    DWORD code = 0;
+    SECURITY_STATUS status;
 
-       homePath[0] = '\0';
+    homePath[0] = '\0';
 
-       if (LogonSSP(lpLogonId,&ctx)) {
+    if (LogonSSP(lpLogonId,&ctx)) {
         DebugEvent("Failed LogonSSP");
-               return 1;
-    } else {
-               status = ImpersonateSecurityContext(&ctx);
-               if (status == SEC_E_OK) {
-                   PSECURITY_LOGON_SESSION_DATA plsd;
-            NTSTATUS rv;
-
-            rv = LsaGetLogonSessionData(lpLogonId, &plsd);
-            if (rv == 0) {
-                PWSTR domain;
-
-                domain = (PWSTR)malloc(sizeof(WCHAR) * (plsd->LogonDomain.Length+1));
-                memcpy(domain, plsd->LogonDomain.Buffer, sizeof(WCHAR) * (plsd->LogonDomain.Length));
-                domain[plsd->LogonDomain.Length] = 0;
-
-                if (!QueryAdHomePathFromSid(homePath,homePathLen,plsd->Sid,domain)) {
-                    DebugEvent("Returned home path [%s]",homePath);
-                    opt->flags |= LOGON_FLAG_AD_REALM;
-                }
-                free(domain);
-                LsaFreeReturnBuffer(plsd);
-            } else {
-                DebugEvent("LsaGetLogonSessionData failed [%lX]", rv);
+        return 1;
+    }
+
+    status = ImpersonateSecurityContext(&ctx);
+    if (status == SEC_E_OK) {
+        PSECURITY_LOGON_SESSION_DATA plsd;
+        NTSTATUS rv;
+
+        rv = LsaGetLogonSessionData(lpLogonId, &plsd);
+        if (rv == 0) {
+            PWSTR domain;
+
+            domain = (PWSTR)malloc(sizeof(WCHAR) * (plsd->LogonDomain.Length+1));
+            memcpy(domain, plsd->LogonDomain.Buffer, sizeof(WCHAR) * (plsd->LogonDomain.Length));
+            domain[plsd->LogonDomain.Length] = 0;
+
+            if (!QueryAdHomePathFromSid(homePath,homePathLen,plsd->Sid,domain)) {
+                DebugEvent("Returned home path [%s]",homePath);
+                opt->flags |= LOGON_FLAG_AD_REALM;
             }
-            RevertSecurityContext(&ctx);
-               } else {
-                       DebugEvent("Can't impersonate context [%lX]",status);
-                       code = 1;
-               }
-
-        DeleteSecurityContext(&ctx);
-               return code;
-       }
+            free(domain);
+            LsaFreeReturnBuffer(plsd);
+        } else {
+            DebugEvent("LsaGetLogonSessionData failed [%lX]", rv);
+        }
+        RevertSecurityContext(&ctx);
+    } else {
+        DebugEvent("Can't impersonate context [%lX]",status);
+        code = 1;
+    }
+
+    DeleteSecurityContext(&ctx);
+    return code;
 }
 
-BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain)
+BOOL
+GetLocalShortDomain(PWSTR Domain, DWORD cbDomain)
 {
     HRESULT hr;
     IADsADSystemInfo *pADsys;
@@ -369,7 +384,7 @@ BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain)
     if (SUCCEEDED(hr))
         coInitialized = TRUE;
 
-    hr = CoCreateInstance(CLSID_ADSystemInfo,
+    hr = CoCreateInstance( CLSID_ADSystemInfo,
                            NULL,
                            CLSCTX_INPROC_SERVER,
                            IID_IADsADSystemInfo,
@@ -388,8 +403,260 @@ BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain)
         pADsys->Release();
     }
 
-       if (coInitialized)
-               CoUninitialize();
+    if (coInitialized)
+        CoUninitialize();
 
     return retval;
 }
+
+static HANDLE
+OpenRedirector(void)
+{
+    HANDLE hControlDevice = NULL;
+
+    hControlDevice = CreateFileW( AFS_SYMLINK_W,
+                                 GENERIC_READ | GENERIC_WRITE,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                 NULL,
+                                 OPEN_EXISTING,
+                                 0,
+                                 NULL );
+
+    if( hControlDevice == INVALID_HANDLE_VALUE)
+    {
+        hControlDevice = NULL;
+        DebugEvent("OpenRedirector Failed to open control device error: %d",
+                    GetLastError());
+    }
+
+    return hControlDevice;
+}
+
+void
+AFSCreatePAG(PLUID lpLogonId)
+{
+    BOOLEAN             bRet = FALSE;
+    HANDLE              hControlDevice = NULL;
+    DWORD               dwCopyBytes = 0;
+    AFSAuthGroupRequestCB *pAuthGroup = NULL;
+    WCHAR *             pwchSIDString = NULL;
+    CtxtHandle          ctx;
+    SECURITY_STATUS     status;
+    PSECURITY_LOGON_SESSION_DATA plsd = NULL;
+    NTSTATUS            rv;
+    BOOLEAN             bImpersonated = FALSE;
+
+    GUID stAuthGroup;
+    unsigned char *pchGUID = NULL;
+    DWORD bytesReturned;
+
+    if (LogonSSP(lpLogonId, &ctx)) {
+        DebugEvent("AFSCreatePAG unable to obtain LogonSSP context");
+        return;
+    }
+
+    status = ImpersonateSecurityContext(&ctx);
+    if (status == SEC_E_OK)
+    {
+        bImpersonated = TRUE;
+
+        rv = LsaGetLogonSessionData(lpLogonId, &plsd);
+        if (rv == 0)
+        {
+            if( !ConvertSidToStringSidW( plsd->Sid,
+                                         &pwchSIDString))
+            {
+                DebugEvent("AFSCreatePAG Failed to convert sid to string Error %08X", GetLastError());
+                goto cleanup;
+            }
+
+            pAuthGroup = (AFSAuthGroupRequestCB *)LocalAlloc( LPTR, 0x1000);
+
+            if( pAuthGroup == NULL)
+            {
+                DebugEvent0("AFSCreatePAG Failed auth group allocation");
+                goto cleanup;
+            }
+
+            memset( pAuthGroup, 0, 0x1000);
+
+            pAuthGroup->SIDLength = (USHORT) (wcslen( pwchSIDString) * sizeof( WCHAR));
+            pAuthGroup->SessionId = plsd->Session;
+
+            memcpy( pAuthGroup->SIDString,
+                    pwchSIDString,
+                    pAuthGroup->SIDLength);
+
+            RevertSecurityContext(&ctx);
+
+            bImpersonated = FALSE;
+
+            hControlDevice = OpenRedirector();
+
+            if( hControlDevice == NULL)
+            {
+                DebugEvent0("AFSCreatePAG Failed to open redirector");
+                goto cleanup;
+            }
+
+            bRet = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_AUTHGROUP_SID_QUERY,
+                                   NULL,
+                                   0,
+                                   &stAuthGroup,
+                                   sizeof( GUID),
+                                   &bytesReturned,
+                                   NULL);
+
+            if( bRet == FALSE)
+            {
+                DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Error 0x%08X", GetLastError());
+            }
+            else
+            {
+                if( UuidToString( (UUID *)&stAuthGroup,
+                                  &pchGUID) == RPC_S_OK)
+                {
+                    DebugEvent("AFSCreatePAG Initial AuthGroup %s\n", pchGUID);
+                    RpcStringFree( &pchGUID);
+                }
+                else
+                {
+                    DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n");
+                }
+            }
+
+            ImpersonateSecurityContext(&ctx); bImpersonated = TRUE;
+
+            bRet = DeviceIoControl( hControlDevice,
+                                    IOCTL_AFS_AUTHGROUP_SID_QUERY,
+                                    NULL,
+                                    0,
+                                    &stAuthGroup,
+                                    sizeof( GUID),
+                                    &bytesReturned,
+                                    NULL);
+
+            if( bRet == FALSE)
+            {
+                DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Impersonation Error 0x%08X", GetLastError());
+            }
+            else
+            {
+                if( UuidToString( (UUID *)&stAuthGroup,
+                                  &pchGUID) == RPC_S_OK)
+                {
+                    DebugEvent("AFSCreatePAG Initial Impersonation AuthGroup %s\n", pchGUID);
+                    RpcStringFree( &pchGUID);
+                }
+                else
+                {
+                    DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n");
+                }
+            }
+
+            RevertSecurityContext(&ctx); bImpersonated = FALSE;
+
+
+            bRet = DeviceIoControl( hControlDevice,
+                                    IOCTL_AFS_AUTHGROUP_LOGON_CREATE,
+                                    pAuthGroup,
+                                    sizeof( AFSAuthGroupRequestCB) + pAuthGroup->SIDLength - 1,
+                                    NULL,
+                                    0,
+                                    &dwCopyBytes,
+                                    NULL);
+
+            if( bRet == FALSE)
+            {
+                DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_CREATE Error 0x%08X", GetLastError());
+            }
+            else
+            {
+                bRet = DeviceIoControl( hControlDevice,
+                                        IOCTL_AFS_AUTHGROUP_SID_QUERY,
+                                        NULL,
+                                        0,
+                                        &stAuthGroup,
+                                        sizeof( GUID),
+                                        &bytesReturned,
+                                        NULL);
+
+                if( bRet == FALSE)
+                {
+                    DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Error 0x%08X", GetLastError());
+                }
+                else
+                {
+                    if( UuidToString( (UUID *)&stAuthGroup,
+                                      &pchGUID) == RPC_S_OK)
+                    {
+                        DebugEvent("AFSCreatePAG New AuthGroup %s\n", pchGUID);
+                        RpcStringFree( &pchGUID);
+                    }
+                    else
+                    {
+                        DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n");
+                    }
+                }
+            }
+
+            ImpersonateSecurityContext(&ctx);
+
+            bRet = DeviceIoControl( hControlDevice,
+                                    IOCTL_AFS_AUTHGROUP_SID_QUERY,
+                                    NULL,
+                                    0,
+                                    &stAuthGroup,
+                                    sizeof( GUID),
+                                    &bytesReturned,
+                                    NULL);
+
+            if( bRet == FALSE)
+            {
+                DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Impersonation Error 0x%08X", GetLastError());
+            }
+            else
+            {
+                if( UuidToString( (UUID *)&stAuthGroup,
+                                  &pchGUID) == RPC_S_OK)
+                {
+                    DebugEvent("AFSCreatePAG New Impersonation AuthGroup %s\n", pchGUID);
+                    RpcStringFree( &pchGUID);
+                }
+                else
+                {
+                    DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n");
+                }
+            }
+
+            RevertSecurityContext(&ctx);
+        }
+        else
+        {
+            DebugEvent("AFSCreatePAG LsaGetLogonSessionData failed [%lX]", rv);
+        }
+
+    }
+    else
+    {
+        DebugEvent("AFSCreatePAG cannot impersonate context [%lX]", status);
+    }
+
+
+  cleanup:
+
+    if (bImpersonated)
+        RevertSecurityContext(&ctx);
+
+    DeleteSecurityContext(&ctx);
+
+    if (plsd != NULL)
+        LsaFreeReturnBuffer(plsd);
+
+    if ( hControlDevice != NULL)
+        CloseHandle( hControlDevice);
+
+    if( pwchSIDString != NULL)
+        LocalFree( pwchSIDString);
+}
index 24cd62a..2b7df22 100644 (file)
@@ -3117,11 +3117,11 @@ void smb_MapNTError(long code, unsigned long *NTStatusp, afs_uint32 redir)
             NTStatus = 0xC000022DL;    /* Retry */
         else {
 #ifdef COMMENT
-        NTStatus = 0xC000022DL;        /* Retry */
+            NTStatus = 0xC000022DL;    /* Retry */
 #else
-        NTStatus = 0xC00000B5L; /* I/O Timeout */
+            NTStatus = 0xC00000B5L; /* I/O Timeout */
 #endif
-    }
+        }
     }
     else if (code == CM_ERROR_NOACCESS) {
         NTStatus = 0xC0000022L;        /* Access denied */
@@ -3281,7 +3281,10 @@ void smb_MapNTError(long code, unsigned long *NTStatusp, afs_uint32 redir)
     else if (code == CM_ERROR_RPC_MOREDATA) {
        NTStatus = 0x80000005L; /* Buffer overflow */
     }
-    else  {
+    else {
+        char foo[256];
+        sprintf(foo, "No mapping for 0x%X using 0xC0982001\r\n", code);
+        OutputDebugString(foo);
         NTStatus = 0xC0982001L;        /* SMB non-specific error */
     }
 
@@ -6932,8 +6935,10 @@ long smb_CloseFID(smb_vc_t *vcp, smb_fid_t *fidp, cm_user_t *userp,
 
         lock_ReleaseMutex(&fidp->mx);
 
-        /* CM_UNLOCK_BY_FID doesn't look at the process ID.  We pass
-              * in zero. */
+        /*
+         * CM_UNLOCK_FLAG_BY_FID doesn't look at the process ID.
+         * We pass in zero.
+         */
         key = cm_GenerateKey(vcp->vcID, 0, fidp->fid);
         lock_ObtainWrite(&scp->rw);
 
@@ -6948,7 +6953,7 @@ long smb_CloseFID(smb_vc_t *vcp, smb_fid_t *fidp, cm_user_t *userp,
             goto post_syncopdone;
         }
 
-        cm_UnlockByKey(scp, key, CM_UNLOCK_BY_FID, userp, &req);
+        cm_UnlockByKey(scp, key, CM_UNLOCK_FLAG_BY_FID, userp, &req);
 
        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_LOCK);
 
index af59a38..7c453f3 100644 (file)
@@ -1087,6 +1087,8 @@ long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
             * be freed when the refCount returns to zero.
             */
            unp->flags &= ~SMB_USERNAMEFLAG_AFSLOGON;
+            if (usernIsSID)
+                unp->flags |= SMB_USERNAMEFLAG_SID;
        }
     if (usernIsSID)
         unp->flags |= SMB_USERNAMEFLAG_SID;
diff --git a/src/WINNT/afsrdr/user/RDRFunction.c b/src/WINNT/afsrdr/user/RDRFunction.c
new file mode 100644 (file)
index 0000000..eb71773
--- /dev/null
@@ -0,0 +1,5334 @@
+/*
+ * Copyright (c) 2008 Secure Endpoints, Inc.
+ * Copyright (c) 2009-2011 Your File System, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Secure Endpoints Inc. nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software without
+ *   specific prior written permission from Secure Endpoints, Inc. and
+ *   Your File System, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#define _CRT_SECURE_NO_DEPRECATE
+#define _CRT_NON_CONFORMING_SWPRINTFS
+#define INITGUID        /* define AFS_AUTH_GUID_NO_PAG */
+
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+
+#include <roken.h>
+
+#include <afs/stds.h>
+
+#include <ntsecapi.h>
+#include <sddl.h>
+#pragma warning(push)
+#pragma warning(disable: 4005)
+
+#include <devioctl.h>
+
+#include "..\\Common\\AFSUserDefines.h"
+#include "..\\Common\\AFSUserStructs.h"
+
+#pragma warning(pop)
+
+#include <tchar.h>
+#include <wchar.h>
+#include <winbase.h>
+#include <winreg.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <strsafe.h>
+
+#include "afsd.h"
+#include "smb.h"
+#include "cm_btree.h"
+#include "msrpc.h"
+#include <RDRPrototypes.h>
+#include <RDRIoctl.h>
+#include <RDRPipe.h>
+
+static CHAR * RDR_extentBaseAddress = NULL;
+
+void
+RDR_InitReq(cm_req_t *reqp)
+{
+    cm_InitReq(reqp);
+    reqp->flags |= CM_REQ_SOURCE_REDIR;
+}
+
+void
+RDR_fid2FID( cm_fid_t *fid, AFSFileID *FileId)
+{
+    FileId->Cell = fid->cell;
+    FileId->Volume = fid->volume;
+    FileId->Vnode = fid->vnode;
+    FileId->Unique = fid->unique;
+    FileId->Hash = fid->hash;
+}
+
+void
+RDR_FID2fid( AFSFileID *FileId, cm_fid_t *fid)
+{
+    fid->cell = FileId->Cell;
+    fid->volume = FileId->Volume;
+    fid->vnode = FileId->Vnode;
+    fid->unique = FileId->Unique;
+    fid->hash = FileId->Hash;
+}
+
+DWORD
+RDR_SetInitParams( OUT AFSRedirectorInitInfo **ppRedirInitInfo, OUT DWORD * pRedirInitInfoLen )
+{
+    extern char cm_CachePath[];
+    extern cm_config_data_t cm_data;
+    extern int smb_hideDotFiles;
+    size_t cm_CachePathLen = strlen(cm_CachePath);
+    size_t err;
+    DWORD TempPathLen = ExpandEnvironmentStringsW(L"%TEMP%", NULL, 0);
+    MEMORYSTATUSEX memStatus;
+    DWORD maxMemoryCacheSize;
+
+    memStatus.dwLength = sizeof(memStatus);
+    if (GlobalMemoryStatusEx(&memStatus)) {
+        /*
+         * Use the memory extent interface in the afs redirector
+         * whenever the cache size is less than equal to 10% of
+         * physical memory.  Do not use too much because this memory
+         * will be locked by the redirector so it can't be swapped
+         * out.
+         */
+        maxMemoryCacheSize = (DWORD)(memStatus.ullTotalPhys / 1024 / 10);
+    } else {
+        /*
+         * If we can't determine the amount of physical memory
+         * in the system, be conservative and limit the use of
+         * memory extent interface to 64MB data caches.
+         */
+        maxMemoryCacheSize = 65536;
+    }
+
+    *pRedirInitInfoLen = (DWORD) (sizeof(AFSRedirectorInitInfo) + (cm_CachePathLen + TempPathLen) * sizeof(WCHAR));
+    *ppRedirInitInfo = (AFSRedirectorInitInfo *)malloc(*pRedirInitInfoLen);
+    (*ppRedirInitInfo)->Flags = smb_hideDotFiles ? AFS_REDIR_INIT_FLAG_HIDE_DOT_FILES : 0;
+    (*ppRedirInitInfo)->MaximumChunkLength = cm_data.chunkSize;
+    (*ppRedirInitInfo)->GlobalFileId.Cell   = cm_data.rootFid.cell;
+    (*ppRedirInitInfo)->GlobalFileId.Volume = cm_data.rootFid.volume;
+    (*ppRedirInitInfo)->GlobalFileId.Vnode  = cm_data.rootFid.vnode;
+    (*ppRedirInitInfo)->GlobalFileId.Unique = cm_data.rootFid.unique;
+    (*ppRedirInitInfo)->GlobalFileId.Hash   = cm_data.rootFid.hash;
+    (*ppRedirInitInfo)->ExtentCount.QuadPart = cm_data.buf_nbuffers;
+    (*ppRedirInitInfo)->CacheBlockSize = cm_data.blockSize;
+    (*ppRedirInitInfo)->MaxPathLinkCount = 512; /* this needs to become a registry value */
+    (*ppRedirInitInfo)->NameArrayLength = 32;   /* this needs to become a registry value */
+    if (cm_virtualCache || cm_data.bufferSize <= maxMemoryCacheSize) {
+        osi_Log0(afsd_logp, "RDR_SetInitParams Initializing Memory Extent Interface");
+        (*ppRedirInitInfo)->MemoryCacheOffset.QuadPart = (LONGLONG)cm_data.bufDataBaseAddress;
+        (*ppRedirInitInfo)->MemoryCacheLength.QuadPart = cm_data.bufEndOfData - cm_data.bufDataBaseAddress;
+        (*ppRedirInitInfo)->CacheFileNameLength = 0;
+        RDR_extentBaseAddress = cm_data.bufDataBaseAddress;
+    } else {
+        (*ppRedirInitInfo)->MemoryCacheOffset.QuadPart = 0;
+        (*ppRedirInitInfo)->MemoryCacheLength.QuadPart = 0;
+        (*ppRedirInitInfo)->CacheFileNameLength = (ULONG) (cm_CachePathLen * sizeof(WCHAR));
+        err = mbstowcs((*ppRedirInitInfo)->CacheFileName, cm_CachePath, (cm_CachePathLen + 1) *sizeof(WCHAR));
+        if (err == -1) {
+            free(*ppRedirInitInfo);
+            osi_Log0(afsd_logp, "RDR_SetInitParams Invalid Object Name");
+            return STATUS_OBJECT_NAME_INVALID;
+        }
+        RDR_extentBaseAddress = cm_data.baseAddress;
+    }
+    (*ppRedirInitInfo)->DumpFileLocationOffset = FIELD_OFFSET(AFSRedirectorInitInfo, CacheFileName) + (*ppRedirInitInfo)->CacheFileNameLength;
+    (*ppRedirInitInfo)->DumpFileLocationLength = (TempPathLen - 1) * sizeof(WCHAR);
+    ExpandEnvironmentStringsW(L"%TEMP%",
+                              (LPWSTR)(((PBYTE)(*ppRedirInitInfo)) + (*ppRedirInitInfo)->DumpFileLocationOffset),
+                              TempPathLen);
+
+    osi_Log0(afsd_logp,"RDR_SetInitParams Success");
+    return 0;
+}
+
+cm_user_t *
+RDR_GetLocalSystemUser( void)
+{
+    smb_username_t *unp;
+    cm_user_t *userp = NULL;
+    wchar_t cname[MAX_COMPUTERNAME_LENGTH+1];
+    int cnamelen = MAX_COMPUTERNAME_LENGTH+1;
+
+    GetComputerNameW(cname, &cnamelen);
+    _wcsupr(cname);
+
+    unp = smb_FindUserByName(NTSID_LOCAL_SYSTEM, cname, SMB_FLAG_CREATE);
+    lock_ObtainMutex(&unp->mx);
+    if (!unp->userp)
+        unp->userp = cm_NewUser();
+    unp->flags |= SMB_USERNAMEFLAG_SID;
+    lock_ReleaseMutex(&unp->mx);
+    userp = unp->userp;
+    cm_HoldUser(userp);
+    smb_ReleaseUsername(unp);
+
+    if (!userp) {
+        userp = cm_rootUserp;
+        cm_HoldUser(userp);
+    }
+
+    return userp;
+}
+
+cm_user_t *
+RDR_UserFromCommRequest( IN AFSCommRequest *RequestBuffer)
+{
+
+    return RDR_UserFromAuthGroup( &RequestBuffer->AuthGroup);
+}
+
+cm_user_t *
+RDR_UserFromAuthGroup( IN GUID *pGuid)
+{
+    smb_username_t *unp;
+    cm_user_t * userp = NULL;
+    RPC_WSTR UuidString = NULL;
+    wchar_t cname[MAX_COMPUTERNAME_LENGTH+1];
+    int cnamelen = MAX_COMPUTERNAME_LENGTH+1;
+
+    if (UuidToStringW((UUID *)pGuid, &UuidString) != RPC_S_OK)
+        goto done;
+
+    GetComputerNameW(cname, &cnamelen);
+    _wcsupr(cname);
+
+    unp = smb_FindUserByName(UuidString, cname, SMB_FLAG_CREATE);
+    lock_ObtainMutex(&unp->mx);
+    if (!unp->userp)
+        unp->userp = cm_NewUser();
+    unp->flags |= SMB_USERNAMEFLAG_SID;
+    lock_ReleaseMutex(&unp->mx);
+    userp = unp->userp;
+    cm_HoldUser(userp);
+    smb_ReleaseUsername(unp);
+
+  done:
+    if (!userp) {
+        userp = cm_rootUserp;
+        cm_HoldUser(userp);
+    }
+
+    osi_Log2(afsd_logp, "RDR_UserFromCommRequest Guid %S userp = 0x%p",
+             osi_LogSaveStringW(afsd_logp, UuidString),
+             userp);
+
+    if (UuidString)
+        RpcStringFreeW(&UuidString);
+
+    return userp;
+}
+
+void
+RDR_ReleaseUser( IN cm_user_t *userp )
+{
+    osi_Log1(afsd_logp, "RDR_ReleaseUser userp = 0x%p", userp);
+    cm_ReleaseUser(userp);
+}
+
+
+/*
+ * RDR_FlagScpInUse flags the scp with CM_SCACHEFLAG_RDR_IN_USE
+ */
+static void
+RDR_FlagScpInUse( IN cm_scache_t *scp, IN BOOL bLocked )
+{
+    if (!bLocked)
+        lock_ObtainWrite(&scp->rw);
+
+    lock_AssertWrite(&scp->rw);
+    scp->flags |= CM_SCACHEFLAG_RDR_IN_USE;
+
+    if (!bLocked)
+        lock_ReleaseWrite(&scp->rw);
+}
+
+/*
+ * Obtain the status information for the specified object and
+ *
+ */
+static afs_uint32
+RDR_BulkStatLookup( cm_scache_t *dscp,
+                    cm_scache_t *scp,
+                    cm_user_t   *userp,
+                    cm_req_t    *reqp)
+{
+    cm_direnum_t *      enump = NULL;
+    afs_uint32  code = 0;
+    cm_dirOp_t    dirop;
+
+    code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, CM_DIROP_FLAG_NONE, &dirop);
+    if (code == 0) {
+        code = cm_BPlusDirEnumerate(dscp, userp, reqp, TRUE, NULL, TRUE, &enump);
+        if (code) {
+            osi_Log1(afsd_logp, "RDR_BulkStatLookup cm_BPlusDirEnumerate failure code=0x%x",
+                      code);
+        }
+        cm_EndDirOp(&dirop);
+    } else {
+        osi_Log1(afsd_logp, "RDR_BulkStatLookup cm_BeginDirOp failure code=0x%x",
+                  code);
+    }
+
+
+    if (enump)
+    {
+        code = cm_BPlusDirEnumBulkStatOne(enump, scp);
+        if (code) {
+            osi_Log1(afsd_logp, "RDR_BulkStatLookup cm_BPlusDirEnumBulkStatOne failure code=0x%x",
+                      code);
+        }
+        cm_BPlusDirFreeEnumeration(enump);
+    }
+
+    return code;
+}
+
+
+#define RDR_POP_FOLLOW_MOUNTPOINTS 0x01
+#define RDR_POP_EVALUATE_SYMLINKS  0x02
+#define RDR_POP_WOW64              0x04
+#define RDR_POP_NO_GETSTATUS       0x08
+
+afs_uint32
+RDR_PopulateCurrentEntry( IN  AFSDirEnumEntry * pCurrentEntry,
+                          IN  DWORD             dwMaxEntryLength,
+                          IN  cm_scache_t     * dscp,
+                          IN  cm_scache_t     * scp,
+                          IN  cm_user_t       * userp,
+                          IN  cm_req_t        * reqp,
+                          IN  wchar_t         * name,
+                          IN  wchar_t         * shortName,
+                          IN  DWORD             dwFlags,
+                          OUT AFSDirEnumEntry **ppNextEntry,
+                          OUT DWORD           * pdwRemainingLength)
+{
+    FILETIME ft;
+    WCHAR *  wname, *wtarget;
+    size_t   len;
+    DWORD      dwEntryLength;
+    afs_uint32 code = 0, code2 = 0;
+    BOOL          bMustFake = FALSE;
+
+    osi_Log5(afsd_logp, "RDR_PopulateCurrentEntry dscp=0x%p scp=0x%p name=%S short=%S flags=0x%x",
+             dscp, scp, osi_LogSaveStringW(afsd_logp, name),
+             osi_LogSaveStringW(afsd_logp, shortName), dwFlags);
+    osi_Log1(afsd_logp, "... maxLength=%d", dwMaxEntryLength);
+
+    if (dwMaxEntryLength < sizeof(AFSDirEnumEntry) + (MAX_PATH + MOUNTPOINTLEN) * sizeof(wchar_t)) {
+        if (ppNextEntry)
+            *ppNextEntry = pCurrentEntry;
+        if (pdwRemainingLength)
+            *pdwRemainingLength = dwMaxEntryLength;
+        osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry Not Enough Room for Entry %d < %d",
+                 dwMaxEntryLength, sizeof(AFSDirEnumEntry) + (MAX_PATH + MOUNTPOINTLEN) * sizeof(wchar_t));
+        return CM_ERROR_TOOBIG;
+    }
+
+    if (!name)
+        name = L"";
+    if (!shortName)
+        shortName = L"";
+
+    dwEntryLength = sizeof(AFSDirEnumEntry);
+
+    lock_ObtainWrite(&scp->rw);
+    if (dwFlags & RDR_POP_NO_GETSTATUS) {
+        if (!cm_HaveCallback(scp))
+            bMustFake = TRUE;
+    } else {
+#ifdef AFS_FREELANCE_CLIENT
+        if (scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID) {
+            /*
+             * If the FID is from the Freelance Local Root always perform
+             * a single item status check.
+             */
+            code = cm_SyncOp( scp, NULL, userp, reqp, 0,
+                              CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+            if (code) {
+                lock_ReleaseWrite(&scp->rw);
+                osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry cm_SyncOp failed for scp=0x%p code=0x%x",
+                         scp, code);
+                return code;
+            }
+        } else
+#endif
+        {
+            /*
+             * For non-Freelance objects, check to see if we have current
+             * status information.  If not, perform a bulk status lookup of multiple
+             * entries in order to reduce the number of RPCs issued to the file server.
+             */
+            if ((scp->flags & CM_SCACHEFLAG_EACCESS))
+                bMustFake = TRUE;
+            else if (!cm_HaveCallback(scp)) {
+                lock_ReleaseWrite(&scp->rw);
+                code = RDR_BulkStatLookup(dscp, scp, userp, reqp);
+                if (code) {
+                    osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry RXR_BulkStatLookup failed for scp=0x%p code=0x%x",
+                             scp, code);
+                    return code;
+                }
+                lock_ObtainWrite(&scp->rw);
+                /*
+                 * RDR_BulkStatLookup can succeed but it may be the case that there
+                 * still is not valid status info.  If we get this far, generate fake
+                 * status info.
+                 */
+                if (!cm_HaveCallback(scp))
+                    bMustFake = TRUE;
+            }
+        }
+
+    }
+
+    /* Populate the real or fake data */
+    pCurrentEntry->FileId.Cell = scp->fid.cell;
+    pCurrentEntry->FileId.Volume = scp->fid.volume;
+    pCurrentEntry->FileId.Vnode = scp->fid.vnode;
+    pCurrentEntry->FileId.Unique = scp->fid.unique;
+    pCurrentEntry->FileId.Hash = scp->fid.hash;
+
+    pCurrentEntry->FileType = scp->fileType;
+
+    pCurrentEntry->DataVersion.QuadPart = scp->dataVersion;
+
+    if (scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+        scp->fid.volume==AFS_FAKE_ROOT_VOL_ID) {
+        cm_LargeSearchTimeFromUnixTime(&ft, MAX_AFS_UINT32);
+    } else {
+        cm_LargeSearchTimeFromUnixTime(&ft, scp->cbExpires);
+    }
+    pCurrentEntry->Expiration.LowPart = ft.dwLowDateTime;
+    pCurrentEntry->Expiration.HighPart = ft.dwHighDateTime;
+
+    if (bMustFake) {
+        /* 1969-12-31 23:59:59 +00 */
+        ft.dwHighDateTime = 0x19DB200;
+        ft.dwLowDateTime = 0x5BB78980;
+    } else
+        cm_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
+    pCurrentEntry->CreationTime.LowPart = ft.dwLowDateTime;
+    pCurrentEntry->CreationTime.HighPart = ft.dwHighDateTime;
+    pCurrentEntry->LastAccessTime = pCurrentEntry->CreationTime;
+    pCurrentEntry->LastWriteTime = pCurrentEntry->CreationTime;
+    pCurrentEntry->ChangeTime = pCurrentEntry->CreationTime;
+
+    pCurrentEntry->EndOfFile = scp->length;
+    pCurrentEntry->AllocationSize = scp->length;
+
+    if (bMustFake) {
+        switch (scp->fileType) {
+        case CM_SCACHETYPE_DIRECTORY:
+        case CM_SCACHETYPE_MOUNTPOINT:
+        case CM_SCACHETYPE_INVALID:
+            pCurrentEntry->FileAttributes = SMB_ATTR_DIRECTORY;
+            break;
+        case CM_SCACHETYPE_SYMLINK:
+            if (cm_TargetPerceivedAsDirectory(scp->mountPointStringp))
+                pCurrentEntry->FileAttributes = SMB_ATTR_DIRECTORY;
+            else
+                pCurrentEntry->FileAttributes = SMB_ATTR_NORMAL;
+            break;
+        default:
+            /* if we get here we either have a normal file
+            * or we have a file for which we have never
+            * received status info.  In this case, we can
+            * check the even/odd value of the entry's vnode.
+            * odd means it is to be treated as a directory
+            * and even means it is to be treated as a file.
+            */
+            if (scp->fid.vnode & 0x1)
+                pCurrentEntry->FileAttributes = SMB_ATTR_DIRECTORY;
+            else
+                pCurrentEntry->FileAttributes = SMB_ATTR_NORMAL;
+        }
+    } else
+        pCurrentEntry->FileAttributes = smb_ExtAttributes(scp);
+    pCurrentEntry->EaSize = 0;
+    pCurrentEntry->Links = scp->linkCount;
+
+    len = wcslen(shortName);
+    wcsncpy(pCurrentEntry->ShortName, shortName, len);
+    pCurrentEntry->ShortNameLength = (CCHAR)(len * sizeof(WCHAR));
+
+    pCurrentEntry->FileNameOffset = sizeof(AFSDirEnumEntry);
+    len = wcslen(name);
+    wname = (WCHAR *)((PBYTE)pCurrentEntry + pCurrentEntry->FileNameOffset);
+    wcsncpy(wname, name, len);
+    pCurrentEntry->FileNameLength = (ULONG)(sizeof(WCHAR) * len);
+
+    osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry scp=0x%p fileType=%d",
+              scp, scp->fileType);
+
+    if (!(dwFlags & RDR_POP_NO_GETSTATUS))
+        cm_SyncOpDone( scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    if ((dwFlags & RDR_POP_NO_GETSTATUS) || !cm_HaveCallback(scp)) {
+        pCurrentEntry->TargetNameOffset = 0;
+        pCurrentEntry->TargetNameLength = 0;
+    }
+    else
+    switch (scp->fileType) {
+    case CM_SCACHETYPE_MOUNTPOINT:
+        if (dwFlags & RDR_POP_FOLLOW_MOUNTPOINTS) {
+            if ((code2 = cm_ReadMountPoint(scp, userp, reqp)) == 0) {
+                cm_scache_t *targetScp = NULL;
+
+                pCurrentEntry->TargetNameOffset = pCurrentEntry->FileNameOffset + pCurrentEntry->FileNameLength;
+                len = strlen(scp->mountPointStringp);
+                wtarget = (WCHAR *)((PBYTE)pCurrentEntry + pCurrentEntry->TargetNameOffset);
+
+#ifdef UNICODE
+                cch = MultiByteToWideChar( CP_UTF8, 0, scp->mountPointStringp,
+                                           len * sizeof(char),
+                                           wtarget,
+                                           len * sizeof(WCHAR));
+#else
+                mbstowcs(wtarget, scp->mountPointStringp, len);
+#endif
+                pCurrentEntry->TargetNameLength = (ULONG)(sizeof(WCHAR) * len);
+
+                code2 = cm_FollowMountPoint(scp, dscp, userp, reqp, &targetScp);
+
+                if (code2 == 0) {
+                    pCurrentEntry->TargetFileId.Cell = targetScp->fid.cell;
+                    pCurrentEntry->TargetFileId.Volume = targetScp->fid.volume;
+                    pCurrentEntry->TargetFileId.Vnode = targetScp->fid.vnode;
+                    pCurrentEntry->TargetFileId.Unique = targetScp->fid.unique;
+                    pCurrentEntry->TargetFileId.Hash = targetScp->fid.hash;
+
+                    osi_Log4(afsd_logp, "RDR_PopulateCurrentEntry target FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+                              pCurrentEntry->TargetFileId.Cell, pCurrentEntry->TargetFileId.Volume,
+                              pCurrentEntry->TargetFileId.Vnode, pCurrentEntry->TargetFileId.Unique);
+
+                    cm_ReleaseSCache(targetScp);
+                } else {
+                    osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry cm_FollowMountPoint failed scp=0x%p code=0x%x",
+                              scp, code2);
+                }
+            } else {
+                osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry cm_ReadMountPoint failed scp=0x%p code=0x%x",
+                          scp, code2);
+            }
+        }
+        break;
+    case CM_SCACHETYPE_SYMLINK:
+    case CM_SCACHETYPE_DFSLINK:
+        {
+            pCurrentEntry->TargetNameOffset = pCurrentEntry->FileNameOffset + pCurrentEntry->FileNameLength;
+            wtarget = (WCHAR *)((PBYTE)pCurrentEntry + pCurrentEntry->TargetNameOffset);
+
+            if (dwFlags & RDR_POP_EVALUATE_SYMLINKS) {
+                char * mp;
+
+                code2 = cm_HandleLink(scp, userp, reqp);
+                if (code2 == 0) {
+                    mp = scp->mountPointStringp;
+                    len = strlen(mp);
+                    if ( len != 0 ) {
+                        /* Strip off the msdfs: prefix from the target name for the file system */
+                        if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
+                            osi_Log0(afsd_logp, "RDR_PopulateCurrentEntry DFSLink Detected");
+                            pCurrentEntry->FileType = scp->fileType;
+
+                            if (!strncmp("msdfs:", mp, 6)) {
+                                mp += 6;
+                                len -= 6;
+                            }
+                        }
+                        /* only send one slash to the redirector */
+                        if (mp[0] == '\\' && mp[1] == '\\') {
+                            mp++;
+                            len--;
+                        }
+#ifdef UNICODE
+                        cch = MultiByteToWideChar( CP_UTF8, 0, mp,
+                                                   len * sizeof(char),
+                                                   wtarget,
+                                                   len * sizeof(WCHAR));
+#else
+                        mbstowcs(wtarget, mp, len);
+#endif
+                    }
+                    pCurrentEntry->TargetNameLength = (ULONG)(sizeof(WCHAR) * len);
+                } else {
+                    osi_Log2(afsd_logp, "RDR_PopulateCurrentEntry cm_HandleLink failed scp=0x%p code=0x%x",
+                             scp, code2);
+                }
+            }
+
+        }
+        break;
+
+    default:
+        pCurrentEntry->TargetNameOffset = 0;
+        pCurrentEntry->TargetNameLength = 0;
+    }
+    lock_ReleaseWrite(&scp->rw);
+
+    dwEntryLength += pCurrentEntry->FileNameLength + pCurrentEntry->TargetNameLength;
+    dwEntryLength += (dwEntryLength % 8) ? 8 - (dwEntryLength % 8) : 0;   /* quad align */
+    if (ppNextEntry)
+        *ppNextEntry = (AFSDirEnumEntry *)((PBYTE)pCurrentEntry + dwEntryLength);
+    if (pdwRemainingLength)
+        *pdwRemainingLength = dwMaxEntryLength - dwEntryLength;
+
+    osi_Log3(afsd_logp, "RDR_PopulateCurrentEntry Success FileNameLength=%d TargetNameLength=%d RemainingLength=%d",
+              pCurrentEntry->FileNameLength, pCurrentEntry->TargetNameLength, *pdwRemainingLength);
+
+    return code;
+}
+
+afs_uint32
+RDR_PopulateCurrentEntryNoScp( IN  AFSDirEnumEntry * pCurrentEntry,
+                               IN  DWORD             dwMaxEntryLength,
+                               IN  cm_scache_t     * dscp,
+                               IN  cm_fid_t        * fidp,
+                               IN  cm_user_t       * userp,
+                               IN  cm_req_t        * reqp,
+                               IN  wchar_t         * name,
+                               IN  wchar_t         * shortName,
+                               IN  DWORD             dwFlags,
+                               OUT AFSDirEnumEntry **ppNextEntry,
+                               OUT DWORD           * pdwRemainingLength)
+{
+    FILETIME ft;
+    WCHAR *  wname;
+    size_t   len;
+    DWORD      dwEntryLength;
+    afs_uint32 code = 0, code2 = 0;
+
+    osi_Log4(afsd_logp, "RDR_PopulateCurrentEntryNoEntry dscp=0x%p name=%S short=%S flags=0x%x",
+             dscp, osi_LogSaveStringW(afsd_logp, name),
+             osi_LogSaveStringW(afsd_logp, shortName), dwFlags);
+    osi_Log1(afsd_logp, "... maxLength=%d", dwMaxEntryLength);
+
+    if (dwMaxEntryLength < sizeof(AFSDirEnumEntry) + (MAX_PATH + MOUNTPOINTLEN) * sizeof(wchar_t)) {
+        if (ppNextEntry)
+            *ppNextEntry = pCurrentEntry;
+        if (pdwRemainingLength)
+            *pdwRemainingLength = dwMaxEntryLength;
+        osi_Log2(afsd_logp, "RDR_PopulateCurrentEntryNoEntry Not Enough Room for Entry %d < %d",
+                 dwMaxEntryLength, sizeof(AFSDirEnumEntry) + (MAX_PATH + MOUNTPOINTLEN) * sizeof(wchar_t));
+        return CM_ERROR_TOOBIG;
+    }
+
+    if (!name)
+        name = L"";
+    if (!shortName)
+        shortName = L"";
+
+    dwEntryLength = sizeof(AFSDirEnumEntry);
+
+    pCurrentEntry->FileId.Cell = fidp->cell;
+    pCurrentEntry->FileId.Volume = fidp->volume;
+    pCurrentEntry->FileId.Vnode = fidp->vnode;
+    pCurrentEntry->FileId.Unique = fidp->unique;
+    pCurrentEntry->FileId.Hash = fidp->hash;
+
+    pCurrentEntry->FileType = CM_SCACHETYPE_UNKNOWN;
+
+    pCurrentEntry->DataVersion.QuadPart = CM_SCACHE_VERSION_BAD;
+
+    cm_LargeSearchTimeFromUnixTime(&ft, 0);
+    pCurrentEntry->Expiration.LowPart = ft.dwLowDateTime;
+    pCurrentEntry->Expiration.HighPart = ft.dwHighDateTime;
+
+    cm_LargeSearchTimeFromUnixTime(&ft, 0);
+    pCurrentEntry->CreationTime.LowPart = ft.dwLowDateTime;
+    pCurrentEntry->CreationTime.HighPart = ft.dwHighDateTime;
+    pCurrentEntry->LastAccessTime = pCurrentEntry->CreationTime;
+    pCurrentEntry->LastWriteTime = pCurrentEntry->CreationTime;
+    pCurrentEntry->ChangeTime = pCurrentEntry->CreationTime;
+
+    pCurrentEntry->EndOfFile.QuadPart = 0;
+    pCurrentEntry->AllocationSize.QuadPart = 0;
+    pCurrentEntry->FileAttributes = 0;
+    pCurrentEntry->EaSize = 0;
+    pCurrentEntry->Links = 0;
+
+    len = wcslen(shortName);
+    wcsncpy(pCurrentEntry->ShortName, shortName, len);
+    pCurrentEntry->ShortNameLength = (CCHAR)(len * sizeof(WCHAR));
+
+    pCurrentEntry->FileNameOffset = sizeof(AFSDirEnumEntry);
+    len = wcslen(name);
+    wname = (WCHAR *)((PBYTE)pCurrentEntry + pCurrentEntry->FileNameOffset);
+    wcsncpy(wname, name, len);
+    pCurrentEntry->FileNameLength = (ULONG)(sizeof(WCHAR) * len);
+
+    pCurrentEntry->TargetNameOffset = 0;
+    pCurrentEntry->TargetNameLength = 0;
+
+    dwEntryLength += pCurrentEntry->FileNameLength + pCurrentEntry->TargetNameLength;
+    dwEntryLength += (dwEntryLength % 8) ? 8 - (dwEntryLength % 8) : 0;   /* quad align */
+    if (ppNextEntry)
+        *ppNextEntry = (AFSDirEnumEntry *)((PBYTE)pCurrentEntry + dwEntryLength);
+    if (pdwRemainingLength)
+        *pdwRemainingLength = dwMaxEntryLength - dwEntryLength;
+
+    osi_Log3(afsd_logp, "RDR_PopulateCurrentEntryNoScp Success FileNameLength=%d TargetNameLength=%d RemainingLength=%d",
+              pCurrentEntry->FileNameLength, pCurrentEntry->TargetNameLength, *pdwRemainingLength);
+
+    return code;
+}
+
+void
+RDR_EnumerateDirectory( IN cm_user_t *userp,
+                        IN AFSFileID DirID,
+                        IN AFSDirQueryCB *QueryCB,
+                        IN BOOL bWow64,
+                        IN BOOL bSkipStatus,
+                        IN DWORD ResultBufferLength,
+                        IN OUT AFSCommResult **ResultCB)
+{
+    DWORD status;
+    cm_direnum_t *      enump = NULL;
+    AFSDirEnumResp  * pDirEnumResp;
+    AFSDirEnumEntry * pCurrentEntry;
+    size_t size = ResultBufferLength ? sizeof(AFSCommResult) + ResultBufferLength - 1 : sizeof(AFSCommResult);
+    DWORD             dwMaxEntryLength;
+    afs_uint32  code = 0;
+    cm_fid_t      fid;
+    cm_scache_t * dscp = NULL;
+    cm_req_t      req;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    osi_Log4(afsd_logp, "RDR_EnumerateDirectory FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+             DirID.Cell, DirID.Volume, DirID.Vnode, DirID.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc(size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_EnumerateDirectory Out of Memory");
+       return;
+    }
+
+    memset(*ResultCB, 0, size);
+
+    if (QueryCB->EnumHandle == (ULONG_PTR)-1) {
+        osi_Log0(afsd_logp, "RDR_EnumerateDirectory No More Entries");
+        (*ResultCB)->ResultStatus = STATUS_NO_MORE_ENTRIES;
+        (*ResultCB)->ResultBufferLength = 0;
+        return;
+    }
+
+    (*ResultCB)->ResultBufferLength = dwMaxEntryLength = ResultBufferLength;
+    if (ResultBufferLength) {
+        pDirEnumResp = (AFSDirEnumResp *)&(*ResultCB)->ResultData;
+        pCurrentEntry = (AFSDirEnumEntry *)&pDirEnumResp->Entry;
+        dwMaxEntryLength -= FIELD_OFFSET( AFSDirEnumResp, Entry);      /* AFSDirEnumResp */
+    }
+
+    if (DirID.Cell != 0) {
+        fid.cell   = DirID.Cell;
+        fid.volume = DirID.Volume;
+        fid.vnode  = DirID.Vnode;
+        fid.unique = DirID.Unique;
+        fid.hash   = DirID.Hash;
+
+        code = cm_GetSCache(&fid, &dscp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EnumerateDirectory cm_GetSCache failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_NAME_INVALID;
+        osi_Log0(afsd_logp, "RDR_EnumerateDirectory Object Name Invalid - Cell = 0");
+        return;
+    }
+
+    /* get the directory size */
+    lock_ObtainWrite(&dscp->rw);
+    code = cm_SyncOp(dscp, NULL, userp, &req, PRSFS_LOOKUP,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log2(afsd_logp, "RDR_EnumerateDirectory cm_SyncOp failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_EnumerateDirectory Not a Directory dscp=0x%p",
+                 dscp);
+        return;
+    }
+
+    /*
+     * If there is no enumeration handle, then this is a new query
+     * and we must perform an enumeration for the specified object
+     */
+    if (QueryCB->EnumHandle == (ULONG_PTR)NULL) {
+        cm_dirOp_t    dirop;
+
+        code = cm_BeginDirOp(dscp, userp, &req, CM_DIRLOCK_READ, CM_DIROP_FLAG_NONE, &dirop);
+        if (code == 0) {
+            code = cm_BPlusDirEnumerate(dscp, userp, &req, TRUE, NULL, !bSkipStatus, &enump);
+            if (code) {
+                osi_Log1(afsd_logp, "RDR_EnumerateDirectory cm_BPlusDirEnumerate failure code=0x%x",
+                          code);
+            }
+            cm_EndDirOp(&dirop);
+        } else {
+            osi_Log1(afsd_logp, "RDR_EnumerateDirectory cm_BeginDirOp failure code=0x%x",
+                      code);
+        }
+    } else {
+        enump = (cm_direnum_t *)QueryCB->EnumHandle;
+    }
+
+    if (enump && ResultBufferLength) {
+        cm_direnum_entry_t * entryp = NULL;
+
+      getnextentry:
+        if (dwMaxEntryLength < sizeof(AFSDirEnumEntry) + (MAX_PATH + MOUNTPOINTLEN) * sizeof(wchar_t)) {
+            osi_Log0(afsd_logp, "RDR_EnumerateDirectory out of space, returning");
+            goto outofspace;
+        }
+
+        code = cm_BPlusDirNextEnumEntry(enump, &entryp);
+
+        if ((code == 0 || code == CM_ERROR_STOPNOW) && entryp) {
+            cm_scache_t *scp;
+            int stopnow = (code == CM_ERROR_STOPNOW);
+
+            if ( !wcscmp(L".", entryp->name) || !wcscmp(L"..", entryp->name) ) {
+                osi_Log0(afsd_logp, "RDR_EnumerateDirectory skipping . or ..");
+                if (stopnow)
+                    goto outofspace;
+                goto getnextentry;
+            }
+
+            if ( FALSE /* bSkipStatus */) {
+                scp = cm_FindSCache(&entryp->fid);
+                code = 0;
+            } else {
+                code = cm_GetSCache(&entryp->fid, &scp, userp, &req);
+            }
+
+            if (!code) {
+                if (scp) {
+                    code = RDR_PopulateCurrentEntry(pCurrentEntry, dwMaxEntryLength,
+                                                     dscp, scp, userp, &req,
+                                                     entryp->name, entryp->shortName,
+                                                     (bWow64 ? RDR_POP_WOW64 : 0) |
+                                                     (bSkipStatus ? RDR_POP_NO_GETSTATUS : 0),
+                                                     &pCurrentEntry, &dwMaxEntryLength);
+                    cm_ReleaseSCache(scp);
+                } else {
+                    code = RDR_PopulateCurrentEntryNoScp( pCurrentEntry, dwMaxEntryLength,
+                                                          dscp, &entryp->fid, userp, &req,
+                                                          entryp->name, entryp->shortName,
+                                                          (bWow64 ? RDR_POP_WOW64 : 0),
+                                                          &pCurrentEntry, &dwMaxEntryLength);
+                }
+                if (stopnow)
+                    goto outofspace;
+                goto getnextentry;
+            } else {
+                osi_Log2(afsd_logp, "RDR_EnumerateDirectory cm_GetSCache failure scp=0x%p code=0x%x",
+                          scp, code);
+                if (stopnow)
+                    goto outofspace;
+                goto getnextentry;
+            }
+        }
+    }
+
+    if (enump && ResultBufferLength == 0) {
+        code = cm_BPlusDirEnumBulkStat(enump);
+        if (code) {
+            osi_Log1(afsd_logp, "RDR_EnumerateDirectory cm_BPlusDirEnumBulkStat failure code=0x%x",
+                      code);
+        }
+    }
+  outofspace:
+
+    if (code || enump->next == enump->count || ResultBufferLength == 0) {
+        cm_BPlusDirFreeEnumeration(enump);
+        enump = (cm_direnum_t *)(ULONG_PTR)-1;
+    }
+
+    if (code == 0 || code == CM_ERROR_STOPNOW) {
+        (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+        osi_Log0(afsd_logp, "RDR_EnumerateDirectory SUCCESS");
+    } else {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_EnumerateDirectory Failure code=0x%x status=0x%x",
+                  code, status);
+    }
+
+    if (ResultBufferLength) {
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwMaxEntryLength;
+
+        pDirEnumResp->EnumHandle = (ULONG_PTR) enump;
+    }
+
+    if (dscp)
+        cm_ReleaseSCache(dscp);
+
+    return;
+}
+
+void
+RDR_EvaluateNodeByName( IN cm_user_t *userp,
+                        IN AFSFileID ParentID,
+                        IN WCHAR   *FileNameCounted,
+                        IN DWORD    FileNameLength,
+                        IN BOOL     CaseSensitive,
+                        IN BOOL     bWow64,
+                        IN BOOL     bHoldFid,
+                        IN BOOL     bNoFollow,
+                        IN DWORD    ResultBufferLength,
+                        IN OUT AFSCommResult **ResultCB)
+{
+    AFSDirEnumEntry * pCurrentEntry;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    afs_uint32  code = 0;
+    cm_scache_t * scp = NULL;
+    cm_scache_t * dscp = NULL;
+    cm_req_t      req;
+    cm_fid_t      parentFid;
+    DWORD         status;
+    DWORD         dwRemaining;
+    WCHAR       * wszName = NULL;
+    size_t        cbName;
+    BOOL          bVol = FALSE;
+    wchar_t       FileName[260];
+
+    StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    osi_Log4(afsd_logp, "RDR_EvaluateNodeByName parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+             ParentID.Cell, ParentID.Volume, ParentID.Vnode, ParentID.Unique);
+
+    /* Allocate enough room to add a volume prefix if necessary */
+    cbName = FileNameLength + (CM_PREFIX_VOL_CCH + 1) * sizeof(WCHAR);
+    wszName = malloc(cbName);
+    if (!wszName) {
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByName Out of Memory");
+       return;
+    }
+    StringCbCopyNW(wszName, cbName, FileName, FileNameLength);
+    osi_Log1(afsd_logp, "... name=%S", osi_LogSaveStringW(afsd_logp, wszName));
+
+    *ResultCB = (AFSCommResult *)malloc(size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByName Out of Memory");
+        free(wszName);
+        return;
+    }
+
+    memset(*ResultCB, 0, size);
+    (*ResultCB)->ResultBufferLength = ResultBufferLength;
+    if (ResultBufferLength)
+        pCurrentEntry = (AFSDirEnumEntry *)&(*ResultCB)->ResultData;
+
+    if (ParentID.Cell != 0) {
+        parentFid.cell   = ParentID.Cell;
+        parentFid.volume = ParentID.Volume;
+        parentFid.vnode  = ParentID.Vnode;
+        parentFid.unique = ParentID.Unique;
+        parentFid.hash   = ParentID.Hash;
+
+        code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EvaluateNodeByName cm_GetSCache parentFID failure code=0x%x status=0x%x",
+                      code, status);
+            free(wszName);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_NAME_INVALID;
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByName Object Name Invalid - Cell = 0");
+        return;
+    }
+
+    /* get the directory size */
+    lock_ObtainWrite(&dscp->rw);
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                     CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_EvaluateNodeByName cm_SyncOp failure dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        free(wszName);
+        return;
+    }
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_EvaluateNodeByName Not a Directory dscp=0x%p",
+                 dscp);
+        free(wszName);
+        return;
+    }
+
+    code = cm_Lookup(dscp, wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+
+    if ((code == CM_ERROR_NOSUCHPATH || code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
+         (wcschr(wszName, '%') != NULL || wcschr(wszName, '#') != NULL)) {
+        /*
+         * A volume reference:  <cell>{%,#}<volume> -> @vol:<cell>{%,#}<volume>
+         */
+        StringCchCopyNW(wszName, cbName, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH);
+        StringCbCatNW(wszName, cbName, FileName, FileNameLength);
+        cm_strlwr_utf16(wszName);
+        bVol = TRUE;
+
+        code = cm_EvaluateVolumeReference(wszName, CM_FLAG_CHECKPATH, userp, &req, &scp);
+    }
+
+    if (code == 0 && scp) {
+        wchar_t shortName[13]=L"";
+
+        if (bVol) {
+            cm_Gen8Dot3VolNameW(scp->fid.cell, scp->fid.volume, shortName, NULL);
+        } else if (!cm_Is8Dot3(wszName)) {
+            cm_dirFid_t dfid;
+
+            dfid.vnode = htonl(scp->fid.vnode);
+            dfid.unique = htonl(scp->fid.unique);
+
+            cm_Gen8Dot3NameIntW(FileName, &dfid, shortName, NULL);
+        } else {
+            shortName[0] = '\0';
+        }
+
+        code = RDR_PopulateCurrentEntry(pCurrentEntry, ResultBufferLength,
+                                        dscp, scp, userp, &req,
+                                        FileName, shortName,
+                                        (bWow64 ? RDR_POP_WOW64 : 0) |
+                                        (bNoFollow ? 0 : (RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS)),
+                                        NULL, &dwRemaining);
+        if (bHoldFid)
+            RDR_FlagScpInUse( scp, FALSE );
+        cm_ReleaseSCache(scp);
+
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EvaluateNodeByName FAILURE code=0x%x status=0x%x",
+                      code, status);
+        } else {
+            (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+            (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+            osi_Log0(afsd_logp, "RDR_EvaluateNodeByName SUCCESS");
+        }
+    } else if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_EvaluateNodeByName FAILURE code=0x%x status=0x%x",
+                 code, status);
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_NO_SUCH_FILE;
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByName No Such File");
+    }
+    cm_ReleaseSCache(dscp);
+    free(wszName);
+
+    return;
+}
+
+void
+RDR_EvaluateNodeByID( IN cm_user_t *userp,
+                      IN AFSFileID ParentID,            /* not used */
+                      IN AFSFileID SourceID,
+                      IN BOOL      bWow64,
+                      IN BOOL      bNoFollow,
+                      IN BOOL      bHoldFid,
+                      IN DWORD     ResultBufferLength,
+                      IN OUT AFSCommResult **ResultCB)
+{
+    AFSDirEnumEntry * pCurrentEntry;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    afs_uint32  code = 0;
+    cm_scache_t * scp = NULL;
+    cm_scache_t * dscp = NULL;
+    cm_req_t      req;
+    cm_fid_t      Fid;
+    cm_fid_t      parentFid;
+    DWORD         status;
+    DWORD         dwRemaining;
+
+    osi_Log4(afsd_logp, "RDR_EvaluateNodeByID source FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              SourceID.Cell, SourceID.Volume, SourceID.Vnode, SourceID.Unique);
+    osi_Log4(afsd_logp, "... parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              ParentID.Cell, ParentID.Volume, ParentID.Vnode, ParentID.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc(size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByID Out of Memory");
+       return;
+    }
+
+    memset(*ResultCB, 0, size);
+    (*ResultCB)->ResultBufferLength = ResultBufferLength;
+    dwRemaining = ResultBufferLength;
+    if (ResultBufferLength)
+        pCurrentEntry = (AFSDirEnumEntry *)&(*ResultCB)->ResultData;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    if (SourceID.Cell != 0) {
+        Fid.cell   = SourceID.Cell;
+        Fid.volume = SourceID.Volume;
+        Fid.vnode  = SourceID.Vnode;
+        Fid.unique = SourceID.Unique;
+        Fid.hash   = SourceID.Hash;
+
+        code = cm_GetSCache(&Fid, &scp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EvaluateNodeByID cm_GetSCache SourceFID failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_NAME_INVALID;
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByID Object Name Invalid - Cell = 0");
+        return;
+    }
+
+    if (ParentID.Cell != 0) {
+        cm_SetFid(&parentFid, ParentID.Cell, ParentID.Volume, ParentID.Vnode, ParentID.Unique);
+        code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+        if (code) {
+            cm_ReleaseSCache(scp);
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EvaluateNodeByID cm_GetSCache parentFID failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else if (SourceID.Vnode == 1) {
+        dscp = scp;
+        cm_HoldSCache(dscp);
+    } else if (scp->parentVnode) {
+        cm_SetFid(&parentFid, SourceID.Cell, SourceID.Volume, scp->parentVnode, scp->parentUnique);
+        code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+        if (code) {
+            cm_ReleaseSCache(scp);
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_EvaluateNodeByID cm_GetSCache parentFID failure code=0x%x status=0x%x",
+                      code, status);
+            return;
+        }
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_OBJECT_PATH_INVALID;
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByID Object Path Invalid - Unknown Parent");
+        return;
+    }
+
+    /* Make sure the directory is current */
+    lock_ObtainWrite(&dscp->rw);
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        cm_ReleaseSCache(scp);
+        osi_Log3(afsd_logp, "RDR_EvaluateNodeByID cm_SyncOp failure dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        cm_ReleaseSCache(scp);
+        osi_Log1(afsd_logp, "RDR_EvaluateNodeByID Not a Directory dscp=0x%p", dscp);
+        return;
+    }
+
+    code = RDR_PopulateCurrentEntry(pCurrentEntry, ResultBufferLength,
+                                    dscp, scp, userp, &req, NULL, NULL,
+                                    (bWow64 ? RDR_POP_WOW64 : 0) |
+                                    (bNoFollow ? 0 : (RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS)),
+                                    NULL, &dwRemaining);
+
+    if (bHoldFid)
+        RDR_FlagScpInUse( scp, FALSE );
+    cm_ReleaseSCache(scp);
+    cm_ReleaseSCache(dscp);
+
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_EvaluateNodeByID FAILURE code=0x%x status=0x%x",
+                 code, status);
+    } else {
+        (*ResultCB)->ResultStatus = STATUS_SUCCESS;
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+        osi_Log0(afsd_logp, "RDR_EvaluateNodeByID SUCCESS");
+    }
+    return;
+}
+
+void
+RDR_CreateFileEntry( IN cm_user_t *userp,
+                     IN WCHAR *FileNameCounted,
+                     IN DWORD FileNameLength,
+                     IN AFSFileCreateCB *CreateCB,
+                     IN BOOL bWow64,
+                     IN BOOL bHoldFid,
+                     IN DWORD ResultBufferLength,
+                     IN OUT AFSCommResult **ResultCB)
+{
+    AFSFileCreateResultCB *pResultCB = NULL;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    cm_fid_t            parentFid;
+    afs_uint32          code;
+    cm_scache_t *       dscp = NULL;
+    afs_uint32          flags = 0;
+    cm_attr_t           setAttr;
+    cm_scache_t *       scp = NULL;
+    cm_req_t            req;
+    DWORD               status;
+    wchar_t             FileName[260];
+
+    StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
+
+    osi_Log4(afsd_logp, "RDR_CreateFileEntry parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              CreateCB->ParentId.Cell, CreateCB->ParentId.Volume,
+              CreateCB->ParentId.Vnode, CreateCB->ParentId.Unique);
+    osi_Log1(afsd_logp, "... name=%S", osi_LogSaveStringW(afsd_logp, FileName));
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+    memset(&setAttr, 0, sizeof(cm_attr_t));
+
+    *ResultCB = (AFSCommResult *)malloc(size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_CreateFileEntry out of memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    parentFid.cell   = CreateCB->ParentId.Cell;
+    parentFid.volume = CreateCB->ParentId.Volume;
+    parentFid.vnode  = CreateCB->ParentId.Vnode;
+    parentFid.unique = CreateCB->ParentId.Unique;
+    parentFid.hash   = CreateCB->ParentId.Hash;
+
+    code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_CreateFileEntry cm_GetSCache ParentFID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&dscp->rw);
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_CreateFileEntry cm_SyncOp failure (1) dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_CreateFileEntry Not a Directory dscp=0x%p",
+                 dscp);
+        return;
+    }
+
+    /* Use current time */
+    setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+    setAttr.clientModTime = time(NULL);
+
+    if (CreateCB->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (smb_unixModeDefaultDir) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = smb_unixModeDefaultDir;
+            if (CreateCB->FileAttributes & FILE_ATTRIBUTE_READONLY)
+                setAttr.unixModeBits &= ~0222;          /* disable the write bits */
+        }
+
+        code = cm_MakeDir(dscp, FileName, flags, &setAttr, userp, &req, &scp);
+    } else {
+        if (smb_unixModeDefaultFile) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = smb_unixModeDefaultFile;
+            if (CreateCB->FileAttributes & FILE_ATTRIBUTE_READONLY)
+                setAttr.unixModeBits &= ~0222;          /* disable the write bits */
+        }
+
+        setAttr.mask |= CM_ATTRMASK_LENGTH;
+        setAttr.length.LowPart = CreateCB->AllocationSize.LowPart;
+        setAttr.length.HighPart = CreateCB->AllocationSize.HighPart;
+        code = cm_Create(dscp, FileName, flags, &setAttr, &scp, userp, &req);
+    }
+    if (code == 0) {
+        wchar_t shortName[13]=L"";
+        cm_dirFid_t dfid;
+        DWORD dwRemaining;
+
+        (*ResultCB)->ResultStatus = 0;  // We will be able to fit all the data in here
+
+        (*ResultCB)->ResultBufferLength = sizeof( AFSFileCreateResultCB);
+
+        pResultCB = (AFSFileCreateResultCB *)(*ResultCB)->ResultData;
+
+        dwRemaining = ResultBufferLength - sizeof( AFSFileCreateResultCB) + sizeof( AFSDirEnumEntry);
+
+        lock_ObtainWrite(&dscp->rw);
+        code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            lock_ReleaseWrite(&dscp->rw);
+            cm_ReleaseSCache(dscp);
+            cm_ReleaseSCache(scp);
+            osi_Log3(afsd_logp, "RDR_CreateFileEntry cm_SyncOp failure (2) dscp=0x%p code=0x%x status=0x%x",
+                      dscp, code, status);
+            return;
+        }
+
+        pResultCB->ParentDataVersion.QuadPart = dscp->dataVersion;
+
+        cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&dscp->rw);
+
+        dfid.vnode = htonl(scp->fid.vnode);
+        dfid.unique = htonl(scp->fid.unique);
+
+        if (!cm_Is8Dot3(FileName))
+            cm_Gen8Dot3NameIntW(FileName, &dfid, shortName, NULL);
+        else
+            shortName[0] = '\0';
+        code = RDR_PopulateCurrentEntry(&pResultCB->DirEnum, dwRemaining,
+                                        dscp, scp, userp, &req, FileName, shortName,
+                                        RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS,
+                                        NULL, &dwRemaining);
+
+        if (bHoldFid)
+            RDR_FlagScpInUse( scp, FALSE );
+        cm_ReleaseSCache(scp);
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+        osi_Log0(afsd_logp, "RDR_CreateFileEntry SUCCESS");
+    } else {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log2(afsd_logp, "RDR_CreateFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    }
+
+    cm_ReleaseSCache(dscp);
+
+    return;
+}
+
+void
+RDR_UpdateFileEntry( IN cm_user_t *userp,
+                     IN AFSFileID FileId,
+                     IN AFSFileUpdateCB *UpdateCB,
+                     IN BOOL bWow64,
+                     IN DWORD ResultBufferLength,
+                     IN OUT AFSCommResult **ResultCB)
+{
+    AFSFileUpdateResultCB *pResultCB = NULL;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    cm_fid_t            Fid;
+    cm_fid_t            parentFid;
+    afs_uint32          code;
+    afs_uint32          flags = 0;
+    cm_attr_t           setAttr;
+    cm_scache_t *       scp = NULL;
+    cm_scache_t *       dscp = NULL;
+    cm_req_t            req;
+    time_t              clientModTime;
+    FILETIME            ft;
+    DWORD               status;
+    BOOL                bScpLocked = FALSE;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+    memset(&setAttr, 0, sizeof(cm_attr_t));
+
+    osi_Log4(afsd_logp, "RDR_UpdateFileEntry parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              UpdateCB->ParentId.Cell, UpdateCB->ParentId.Volume,
+              UpdateCB->ParentId.Vnode, UpdateCB->ParentId.Unique);
+    osi_Log4(afsd_logp, "... object FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc( size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_UpdateFileEntry Out of Memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    parentFid.cell   = UpdateCB->ParentId.Cell;
+    parentFid.volume = UpdateCB->ParentId.Volume;
+    parentFid.vnode  = UpdateCB->ParentId.Vnode;
+    parentFid.unique = UpdateCB->ParentId.Unique;
+    parentFid.hash   = UpdateCB->ParentId.Hash;
+
+    code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_UpdateFileEntry cm_GetSCache ParentFID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&dscp->rw);
+    bScpLocked = TRUE;
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_UpdateFileEntry cm_SyncOp failure dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+    bScpLocked = FALSE;
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_UpdateFileEntry Not a Directory dscp=0x%p",
+                 dscp);
+        return;
+    }
+
+    Fid.cell   = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode  = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash   = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        cm_ReleaseSCache(dscp);
+        osi_Log2(afsd_logp, "RDR_UpdateFileEntry cm_GetSCache object FID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    bScpLocked = TRUE;
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
+    if (code) {
+        lock_ReleaseWrite(&scp->rw);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        cm_ReleaseSCache(dscp);
+        cm_ReleaseSCache(scp);
+        osi_Log3(afsd_logp, "RDR_UpdateFileEntry cm_SyncOp failure scp=0x%p code=0x%x status=0x%x",
+                 scp, code, status);
+        return;
+    }
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    if (UpdateCB->ChangeTime.QuadPart) {
+
+        if (scp->fileType == CM_SCACHETYPE_FILE) {
+            /* Do not set length and other attributes at the same time */
+            if (scp->length.QuadPart != UpdateCB->AllocationSize.QuadPart) {
+                osi_Log2(afsd_logp, "RDR_UpdateFileEntry Length Change 0x%x -> 0x%x",
+                          (afs_uint32)scp->length.QuadPart, (afs_uint32)UpdateCB->AllocationSize.QuadPart);
+                setAttr.mask |= CM_ATTRMASK_LENGTH;
+                setAttr.length.LowPart = UpdateCB->AllocationSize.LowPart;
+                setAttr.length.HighPart = UpdateCB->AllocationSize.HighPart;
+                lock_ReleaseWrite(&scp->rw);
+                bScpLocked = FALSE;
+                code = cm_SetAttr(scp, &setAttr, userp, &req);
+                if (code)
+                    goto on_error;
+                setAttr.mask = 0;
+            }
+        }
+
+        if (!bScpLocked) {
+            lock_ObtainWrite(&scp->rw);
+            bScpLocked = TRUE;
+        }
+        if ((scp->unixModeBits & 0200) && (UpdateCB->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = scp->unixModeBits & ~0222;
+        } else if (!(scp->unixModeBits & 0200) && !(UpdateCB->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = scp->unixModeBits | 0222;
+        }
+    }
+
+    if (UpdateCB->LastWriteTime.QuadPart) {
+        ft.dwLowDateTime = UpdateCB->LastWriteTime.LowPart;
+        ft.dwHighDateTime = UpdateCB->LastWriteTime.HighPart;
+
+        cm_UnixTimeFromLargeSearchTime(& clientModTime, &ft);
+
+        if (!bScpLocked) {
+            lock_ObtainWrite(&scp->rw);
+            bScpLocked = TRUE;
+        }
+        if (scp->clientModTime != clientModTime) {
+            setAttr.mask |= CM_ATTRMASK_CLIENTMODTIME;
+            setAttr.clientModTime = clientModTime;
+        }
+
+        /* call setattr */
+        if (setAttr.mask) {
+            lock_ReleaseWrite(&scp->rw);
+            bScpLocked = FALSE;
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
+        } else
+            code = 0;
+    }
+
+  on_error:
+    if (bScpLocked) {
+        lock_ReleaseWrite(&scp->rw);
+    }
+
+    if (code == 0) {
+        DWORD dwRemaining = ResultBufferLength - sizeof( AFSFileUpdateResultCB) + sizeof( AFSDirEnumEntry);
+
+        pResultCB = (AFSFileUpdateResultCB *)(*ResultCB)->ResultData;
+
+        code = RDR_PopulateCurrentEntry(&pResultCB->DirEnum, dwRemaining,
+                                        dscp, scp, userp, &req, NULL, NULL,
+                                        RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS,
+                                        NULL, &dwRemaining);
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+        osi_Log0(afsd_logp, "RDR_UpdateFileEntry SUCCESS");
+    } else {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log2(afsd_logp, "RDR_UpdateFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    }
+    cm_ReleaseSCache(scp);
+    cm_ReleaseSCache(dscp);
+
+    return;
+}
+
+void
+RDR_CleanupFileEntry( IN cm_user_t *userp,
+                      IN AFSFileID FileId,
+                      IN WCHAR *FileNameCounted,
+                      IN DWORD FileNameLength,
+                      IN AFSFileCleanupCB *CleanupCB,
+                      IN BOOL bWow64,
+                      IN BOOL bLastHandle,
+                      IN BOOL bDeleteFile,
+                      IN BOOL bUnlockFile,
+                      IN DWORD ResultBufferLength,
+                      IN OUT AFSCommResult **ResultCB)
+{
+    size_t size = sizeof(AFSCommResult);
+    cm_fid_t            Fid;
+    cm_fid_t            parentFid;
+    afs_uint32          code = 0;
+    afs_uint32          flags = 0;
+    cm_attr_t           setAttr;
+    cm_scache_t *       scp = NULL;
+    cm_scache_t *       dscp = NULL;
+    cm_req_t            req;
+    time_t              clientModTime;
+    FILETIME            ft;
+    DWORD               status;
+    BOOL                bScpLocked = FALSE;
+    BOOL                bDscpLocked = FALSE;
+    BOOL                bFlushFile = FALSE;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+    memset(&setAttr, 0, sizeof(cm_attr_t));
+
+    osi_Log4(afsd_logp, "RDR_CleanupFileEntry parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              CleanupCB->ParentId.Cell, CleanupCB->ParentId.Volume,
+              CleanupCB->ParentId.Vnode, CleanupCB->ParentId.Unique);
+    osi_Log4(afsd_logp, "... object FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc( size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_CleanupFileEntry Out of Memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    parentFid.cell   = CleanupCB->ParentId.Cell;
+    parentFid.volume = CleanupCB->ParentId.Volume;
+    parentFid.vnode  = CleanupCB->ParentId.Vnode;
+    parentFid.unique = CleanupCB->ParentId.Unique;
+    parentFid.hash   = CleanupCB->ParentId.Hash;
+
+    if (parentFid.cell) {
+        code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            osi_Log2(afsd_logp, "RDR_CleanupFileEntry cm_GetSCache ParentFID failure code=0x%x status=0x%x",
+                     code, status);
+            return;
+        }
+
+        lock_ObtainWrite(&dscp->rw);
+        bDscpLocked = TRUE;
+        code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                         CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if (code) {
+            osi_Log2(afsd_logp, "RDR_CleanupFileEntry cm_SyncOp failure dscp=0x%p code=0x%x",
+                    dscp, code);
+            if (code)
+                goto on_error;
+        }
+
+        cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&dscp->rw);
+        bDscpLocked = FALSE;
+
+        if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+            (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+            cm_ReleaseSCache(dscp);
+            osi_Log1(afsd_logp, "RDR_CleanupFileEntry Not a Directory dscp=0x%p",
+                     dscp);
+            if (code)
+                goto on_error;
+        }
+    }
+
+    Fid.cell   = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode  = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash   = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        osi_Log1(afsd_logp, "RDR_CleanupFileEntry cm_GetSCache object FID failure code=0x%x",
+                 code);
+        goto on_error;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    bScpLocked = TRUE;
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
+    if (code) {
+        osi_Log2(afsd_logp, "RDR_CleanupFileEntry cm_SyncOp failure scp=0x%p code=0x%x",
+                 scp, code);
+        goto on_error;
+    }
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+    if (scp->redirBufCount > 0) {
+        LARGE_INTEGER heldExtents;
+        AFSFileExtentCB extentList[1024];
+        DWORD extentCount = 0;
+        cm_buf_t *srbp;
+        time_t now;
+
+        time(&now);
+        heldExtents.QuadPart = 0;
+
+        for ( srbp = redirq_to_cm_buf_t(scp->redirQueueT);
+              srbp;
+              srbp = redirq_to_cm_buf_t(osi_QPrev(&srbp->redirq)))
+        {
+            extentList[extentCount].Flags = 0;
+            extentList[extentCount].Length = cm_data.blockSize;
+            extentList[extentCount].FileOffset.QuadPart = srbp->offset.QuadPart;
+            extentList[extentCount].CacheOffset.QuadPart = srbp->datap - RDR_extentBaseAddress;
+            lock_ObtainWrite(&buf_globalLock);
+            srbp->redirReleaseRequested = now;
+            lock_ReleaseWrite(&buf_globalLock);
+            extentCount++;
+
+            if (extentCount == 1024) {
+                lock_ReleaseWrite(&scp->rw);
+                code = RDR_RequestExtentRelease(&scp->fid, heldExtents, extentCount, extentList);
+                if (code) {
+                    if (code == CM_ERROR_RETRY) {
+                        /*
+                         * The redirector either is not holding the extents or cannot let them
+                         * go because they are otherwise in use.  At the moment, do nothing.
+                         */
+                    } else
+                        break;
+                }
+                extentCount = 0;
+                bFlushFile = TRUE;
+                lock_ObtainWrite(&scp->rw);
+            }
+        }
+
+        if (code == 0 && extentCount > 0) {
+            if (bScpLocked) {
+                lock_ReleaseWrite(&scp->rw);
+                bScpLocked = FALSE;
+            }
+            code = RDR_RequestExtentRelease(&scp->fid, heldExtents, extentCount, extentList);
+            bFlushFile = TRUE;
+        }
+    }
+
+    /* No longer in use by redirector */
+    if (!bScpLocked) {
+        lock_ObtainWrite(&scp->rw);
+        bScpLocked = TRUE;
+    }
+
+    if (bLastHandle) {
+        lock_AssertWrite(&scp->rw);
+        scp->flags &= ~CM_SCACHEFLAG_RDR_IN_USE;
+    }
+
+    if (bLastHandle || bFlushFile) {
+        if (bScpLocked) {
+            lock_ReleaseWrite(&scp->rw);
+            bScpLocked = FALSE;
+        }
+        code = buf_CleanVnode(scp, userp, &req);
+        if (bLastHandle && code)
+            goto on_error;
+    }
+
+    if (bUnlockFile || bDeleteFile) {
+        cm_key_t    key;
+
+        if (!bScpLocked) {
+            lock_ObtainWrite(&scp->rw);
+            bScpLocked = TRUE;
+        }
+        code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_LOCK);
+        if (code) {
+            osi_Log2(afsd_logp, "RDR_CleanupFileEntry cm_SyncOp (2) failure scp=0x%p code=0x%x",
+                     scp, code);
+            goto on_error;
+        }
+
+        /* the scp is now locked and current */
+        key = cm_GenerateKey(CM_SESSION_IFS, CleanupCB->ProcessId, 0);
+
+        code = cm_UnlockByKey(scp, key,
+                              bDeleteFile ? CM_UNLOCK_FLAG_BY_FID : 0,
+                              userp, &req);
+
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_LOCK);
+
+        if (code)
+            goto on_error;
+    }
+
+    if (CleanupCB->ChangeTime.QuadPart) {
+
+        if (scp->fileType == CM_SCACHETYPE_FILE) {
+            /* Do not set length and other attributes at the same time */
+            if (scp->length.QuadPart != CleanupCB->AllocationSize.QuadPart) {
+                osi_Log2(afsd_logp, "RDR_CleanupFileEntry Length Change 0x%x -> 0x%x",
+                          (afs_uint32)scp->length.QuadPart, (afs_uint32)CleanupCB->AllocationSize.QuadPart);
+                setAttr.mask |= CM_ATTRMASK_LENGTH;
+                setAttr.length.LowPart = CleanupCB->AllocationSize.LowPart;
+                setAttr.length.HighPart = CleanupCB->AllocationSize.HighPart;
+
+                if (bScpLocked) {
+                    lock_ReleaseWrite(&scp->rw);
+                    bScpLocked = FALSE;
+                }
+                code = cm_SetAttr(scp, &setAttr, userp, &req);
+                if (code)
+                    goto on_error;
+                setAttr.mask = 0;
+            }
+        }
+
+        if (!bScpLocked) {
+            lock_ObtainWrite(&scp->rw);
+            bScpLocked = TRUE;
+        }
+
+        if ((scp->unixModeBits & 0200) && (CleanupCB->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = scp->unixModeBits & ~0222;
+        } else if (!(scp->unixModeBits & 0200) && !(CleanupCB->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
+            setAttr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+            setAttr.unixModeBits = scp->unixModeBits | 0222;
+        }
+    }
+
+    if (CleanupCB->LastWriteTime.QuadPart) {
+        ft.dwLowDateTime = CleanupCB->LastWriteTime.LowPart;
+        ft.dwHighDateTime = CleanupCB->LastWriteTime.HighPart;
+
+        cm_UnixTimeFromLargeSearchTime(&clientModTime, &ft);
+        if (scp->clientModTime != clientModTime) {
+            setAttr.mask |= CM_ATTRMASK_CLIENTMODTIME;
+            setAttr.clientModTime = clientModTime;
+        }
+    }
+
+    /* call setattr */
+    if (setAttr.mask) {
+        lock_ReleaseWrite(&scp->rw);
+        bScpLocked = FALSE;
+        code = cm_SetAttr(scp, &setAttr, userp, &req);
+    } else
+        code = 0;
+
+  on_error:
+    if (bDscpLocked)
+        lock_ReleaseWrite(&dscp->rw);
+    if (bScpLocked)
+        lock_ReleaseWrite(&scp->rw);
+
+    if (dscp && bDeleteFile) {
+        WCHAR FileName[260];
+
+        StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
+
+        if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
+            code = cm_RemoveDir(dscp, NULL, FileName, userp, &req);
+        else
+            code = cm_Unlink(dscp, NULL, FileName, userp, &req);
+    }
+
+    if (code == 0) {
+        (*ResultCB)->ResultStatus = 0;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log0(afsd_logp, "RDR_CleanupFileEntry SUCCESS");
+    } else {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log2(afsd_logp, "RDR_CleanupFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    }
+    if (scp)
+        cm_ReleaseSCache(scp);
+    if (dscp)
+        cm_ReleaseSCache(dscp);
+
+    return;
+}
+
+void
+RDR_DeleteFileEntry( IN cm_user_t *userp,
+                     IN AFSFileID ParentId,
+                     IN ULONGLONG ProcessId,
+                     IN WCHAR *FileNameCounted,
+                     IN DWORD FileNameLength,
+                     IN BOOL bWow64,
+                     IN BOOL bCheckOnly,
+                     IN DWORD ResultBufferLength,
+                     IN OUT AFSCommResult **ResultCB)
+{
+
+    AFSFileDeleteResultCB *pResultCB = NULL;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    cm_fid_t            parentFid;
+    afs_uint32          code;
+    cm_scache_t *       dscp = NULL;
+    cm_scache_t *       scp = NULL;
+    afs_uint32          flags = 0;
+    cm_attr_t           setAttr;
+    cm_req_t            req;
+    DWORD               status;
+    wchar_t             FileName[260];
+    cm_key_t            key;
+
+    StringCchCopyNW(FileName, 260, FileNameCounted, FileNameLength / sizeof(WCHAR));
+
+    osi_Log4(afsd_logp, "RDR_DeleteFileEntry parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              ParentId.Cell,  ParentId.Volume,
+              ParentId.Vnode, ParentId.Unique);
+    osi_Log2(afsd_logp, "... name=%S checkOnly=%x",
+             osi_LogSaveStringW(afsd_logp, FileName),
+             bCheckOnly);
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+    memset(&setAttr, 0, sizeof(cm_attr_t));
+
+    *ResultCB = (AFSCommResult *)malloc( size);
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_DeleteFileEntry out of memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    parentFid.cell   = ParentId.Cell;
+    parentFid.volume = ParentId.Volume;
+    parentFid.vnode  = ParentId.Vnode;
+    parentFid.unique = ParentId.Unique;
+    parentFid.hash   = ParentId.Hash;
+
+    code = cm_GetSCache(&parentFid, &dscp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_DeleteFileEntry cm_GetSCache ParentFID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&dscp->rw);
+
+    code = cm_SyncOp(dscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        lock_ReleaseWrite(&dscp->rw);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_DeleteFileEntry cm_SyncOp failure dscp=0x%p code=0x%x status=0x%x",
+                 dscp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&dscp->rw);
+
+    if (dscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(dscp);
+        osi_Log1(afsd_logp, "RDR_DeleteFileEntry Not a Directory dscp=0x%p",
+                 dscp);
+        return;
+    }
+
+    code = cm_Lookup(dscp, FileName, 0, userp, &req, &scp);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        cm_ReleaseSCache(dscp);
+        osi_Log2(afsd_logp, "RDR_DeleteFileEntry cm_Lookup failure code=0x%x status=0x%x",
+                 code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_DELETE,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        cm_ReleaseSCache(dscp);
+        osi_Log3(afsd_logp, "RDR_DeleteFileEntry cm_SyncOp failure scp=0x%p code=0x%x status=0x%x",
+                 scp, code, status);
+        return;
+    }
+
+    if (!bCheckOnly) {
+        /* Drop all locks since the file is being deleted */
+        code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                         CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_LOCK);
+        if (code) {
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            (*ResultCB)->ResultBufferLength = 0;
+            lock_ReleaseWrite(&scp->rw);
+            cm_ReleaseSCache(scp);
+            cm_ReleaseSCache(dscp);
+            osi_Log3(afsd_logp, "RDR_DeleteFileEntry cm_SyncOp Lock failure scp=0x%p code=0x%x status=0x%x",
+                     scp, code, status);
+        }
+
+        /* the scp is now locked and current */
+        key = cm_GenerateKey(CM_SESSION_IFS, ProcessId, 0);
+
+        code = cm_UnlockByKey(scp, key,
+                              CM_UNLOCK_FLAG_BY_FID,
+                              userp, &req);
+
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_LOCK);
+        lock_ReleaseWrite(&scp->rw);
+
+        if (scp->fileType == CM_SCACHETYPE_DIRECTORY)
+            code = cm_RemoveDir(dscp, NULL, FileName, userp, &req);
+        else
+            code = cm_Unlink(dscp, NULL, FileName, userp, &req);
+    } else {
+        lock_ReleaseWrite(&scp->rw);
+    }
+
+    if (code == 0) {
+        (*ResultCB)->ResultStatus = 0;  // We will be able to fit all the data in here
+
+        (*ResultCB)->ResultBufferLength = sizeof( AFSFileDeleteResultCB);
+
+        pResultCB = (AFSFileDeleteResultCB *)(*ResultCB)->ResultData;
+
+        pResultCB->ParentDataVersion.QuadPart = dscp->dataVersion;
+        osi_Log0(afsd_logp, "RDR_DeleteFileEntry SUCCESS");
+    } else {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+        osi_Log2(afsd_logp, "RDR_DeleteFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    }
+
+    cm_ReleaseSCache(dscp);
+    cm_ReleaseSCache(scp);
+
+    return;
+}
+
+void
+RDR_RenameFileEntry( IN cm_user_t *userp,
+                     IN WCHAR    *SourceFileNameCounted,
+                     IN DWORD     SourceFileNameLength,
+                     IN AFSFileID SourceFileId,
+                     IN AFSFileRenameCB *pRenameCB,
+                     IN BOOL bWow64,
+                     IN DWORD ResultBufferLength,
+                     IN OUT AFSCommResult **ResultCB)
+{
+
+    AFSFileRenameResultCB *pResultCB = NULL;
+    size_t size = sizeof(AFSCommResult) + ResultBufferLength - 1;
+    AFSFileID              SourceParentId   = pRenameCB->SourceParentId;
+    AFSFileID              TargetParentId   = pRenameCB->TargetParentId;
+    WCHAR *                TargetFileNameCounted = pRenameCB->TargetName;
+    DWORD                  TargetFileNameLength = pRenameCB->TargetNameLength;
+    cm_fid_t               SourceParentFid;
+    cm_fid_t               TargetParentFid;
+    cm_scache_t *          oldDscp;
+    cm_scache_t *          newDscp;
+    wchar_t                shortName[13];
+    wchar_t                SourceFileName[260];
+    wchar_t                TargetFileName[260];
+    cm_dirFid_t            dfid;
+    cm_req_t               req;
+    afs_uint32             code;
+    DWORD                  status;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    StringCchCopyNW(SourceFileName, 260, SourceFileNameCounted, SourceFileNameLength / sizeof(WCHAR));
+    StringCchCopyNW(TargetFileName, 260, TargetFileNameCounted, TargetFileNameLength / sizeof(WCHAR));
+
+    osi_Log4(afsd_logp, "RDR_RenameFileEntry Source Parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              SourceParentId.Cell,  SourceParentId.Volume,
+              SourceParentId.Vnode, SourceParentId.Unique);
+    osi_Log2(afsd_logp, "... Source Name=%S Length %u", osi_LogSaveStringW(afsd_logp, SourceFileName), SourceFileNameLength);
+    osi_Log4(afsd_logp, "... Target Parent FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              TargetParentId.Cell,  TargetParentId.Volume,
+              TargetParentId.Vnode, TargetParentId.Unique);
+    osi_Log2(afsd_logp, "... Target Name=%S Length %u", osi_LogSaveStringW(afsd_logp, TargetFileName), TargetFileNameLength);
+
+    *ResultCB = (AFSCommResult *)malloc( size);
+    if (!(*ResultCB))
+       return;
+
+    memset( *ResultCB,
+            '\0',
+            size);
+
+    pResultCB = (AFSFileRenameResultCB *)(*ResultCB)->ResultData;
+
+    if (SourceFileNameLength == 0 || TargetFileNameLength == 0)
+    {
+        osi_Log2(afsd_logp, "RDR_RenameFileEntry Invalid Name Length: src %u target %u",
+                 SourceFileNameLength, TargetFileNameLength);
+        (*ResultCB)->ResultStatus = STATUS_INVALID_PARAMETER;
+        return;
+    }
+
+    SourceParentFid.cell   = SourceParentId.Cell;
+    SourceParentFid.volume = SourceParentId.Volume;
+    SourceParentFid.vnode  = SourceParentId.Vnode;
+    SourceParentFid.unique = SourceParentId.Unique;
+    SourceParentFid.hash   = SourceParentId.Hash;
+
+    TargetParentFid.cell   = TargetParentId.Cell;
+    TargetParentFid.volume = TargetParentId.Volume;
+    TargetParentFid.vnode  = TargetParentId.Vnode;
+    TargetParentFid.unique = TargetParentId.Unique;
+    TargetParentFid.hash   = TargetParentId.Hash;
+
+    code = cm_GetSCache(&SourceParentFid, &oldDscp, userp, &req);
+    if (code) {
+        osi_Log1(afsd_logp, "RDR_RenameFileEntry cm_GetSCache source parent failed code 0x%x", code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        return;
+    }
+
+    lock_ObtainWrite(&oldDscp->rw);
+    code = cm_SyncOp(oldDscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        osi_Log2(afsd_logp, "RDR_RenameFileEntry cm_SyncOp oldDscp 0x%p failed code 0x%x", oldDscp, code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&oldDscp->rw);
+        cm_ReleaseSCache(oldDscp);
+        return;
+    }
+
+    cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&oldDscp->rw);
+
+
+    if (oldDscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        osi_Log1(afsd_logp, "RDR_RenameFileEntry oldDscp 0x%p not a directory", oldDscp);
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(oldDscp);
+        return;
+    }
+
+    code = cm_GetSCache(&TargetParentFid, &newDscp, userp, &req);
+    if (code) {
+        osi_Log1(afsd_logp, "RDR_RenameFileEntry cm_GetSCache target parent failed code 0x%x", code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        cm_ReleaseSCache(oldDscp);
+        return;
+    }
+
+    lock_ObtainWrite(&newDscp->rw);
+    code = cm_SyncOp(newDscp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        osi_Log2(afsd_logp, "RDR_RenameFileEntry cm_SyncOp newDscp 0x%p failed code 0x%x", newDscp, code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&newDscp->rw);
+        cm_ReleaseSCache(oldDscp);
+        cm_ReleaseSCache(newDscp);
+        return;
+    }
+
+    cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&newDscp->rw);
+
+
+    if (newDscp->fileType != CM_SCACHETYPE_DIRECTORY) {
+        osi_Log1(afsd_logp, "RDR_RenameFileEntry newDscp 0x%p not a directory", newDscp);
+        (*ResultCB)->ResultStatus = STATUS_NOT_A_DIRECTORY;
+        cm_ReleaseSCache(oldDscp);
+        cm_ReleaseSCache(newDscp);
+        return;
+    }
+
+    code = cm_Rename( oldDscp, NULL, SourceFileName,
+                      newDscp, TargetFileName, userp, &req);
+    if (code == 0) {
+        cm_dirOp_t dirop;
+        cm_fid_t   targetFid;
+        cm_scache_t *scp = 0;
+        DWORD dwRemaining;
+
+        (*ResultCB)->ResultBufferLength = ResultBufferLength;
+        dwRemaining = ResultBufferLength - sizeof( AFSFileRenameResultCB) + sizeof( AFSDirEnumEntry);
+        (*ResultCB)->ResultStatus = 0;
+
+        pResultCB->SourceParentDataVersion.QuadPart = oldDscp->dataVersion;
+        pResultCB->TargetParentDataVersion.QuadPart = newDscp->dataVersion;
+
+        osi_Log2(afsd_logp, "RDR_RenameFileEntry cm_Rename oldDscp 0x%p newDscp 0x%p SUCCESS",
+                 oldDscp, newDscp);
+
+        code = cm_BeginDirOp( newDscp, userp, &req, CM_DIRLOCK_READ, CM_DIROP_FLAG_NONE, &dirop);
+        if (code == 0) {
+            code = cm_BPlusDirLookup(&dirop, TargetFileName, &targetFid);
+            cm_EndDirOp(&dirop);
+        }
+
+        if (code != 0) {
+            osi_Log1(afsd_logp, "RDR_RenameFileEntry cm_BPlusDirLookup failed code 0x%x",
+                     code);
+            (*ResultCB)->ResultStatus = STATUS_OBJECT_PATH_INVALID;
+            cm_ReleaseSCache(oldDscp);
+            cm_ReleaseSCache(newDscp);
+            return;
+        }
+
+        osi_Log4(afsd_logp, "RDR_RenameFileEntry Target FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+                  targetFid.cell,  targetFid.volume,
+                  targetFid.vnode, targetFid.unique);
+
+        code = cm_GetSCache(&targetFid, &scp, userp, &req);
+        if (code) {
+            osi_Log1(afsd_logp, "RDR_RenameFileEntry cm_GetSCache target failed code 0x%x", code);
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            cm_ReleaseSCache(oldDscp);
+            cm_ReleaseSCache(newDscp);
+            return;
+        }
+
+        /* Make sure the source vnode is current */
+        lock_ObtainWrite(&scp->rw);
+        code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if (code) {
+            osi_Log2(afsd_logp, "RDR_RenameFileEntry cm_SyncOp scp 0x%p failed code 0x%x", scp, code);
+            smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+            (*ResultCB)->ResultStatus = status;
+            lock_ReleaseWrite(&scp->rw);
+            cm_ReleaseSCache(oldDscp);
+            cm_ReleaseSCache(newDscp);
+            cm_ReleaseSCache(scp);
+            return;
+        }
+
+        cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseWrite(&scp->rw);
+
+        dfid.vnode = htonl(scp->fid.vnode);
+        dfid.unique = htonl(scp->fid.unique);
+
+        if (!cm_Is8Dot3(TargetFileName))
+            cm_Gen8Dot3NameIntW(TargetFileName, &dfid, shortName, NULL);
+        else
+            shortName[0] = '\0';
+        RDR_PopulateCurrentEntry(&pResultCB->DirEnum, dwRemaining,
+                                 newDscp, scp, userp, &req, TargetFileName, shortName,
+                                 RDR_POP_FOLLOW_MOUNTPOINTS | RDR_POP_EVALUATE_SYMLINKS,
+                                 NULL, &dwRemaining);
+        (*ResultCB)->ResultBufferLength = ResultBufferLength - dwRemaining;
+        cm_ReleaseSCache(scp);
+
+        osi_Log0(afsd_logp, "RDR_RenameFileEntry SUCCESS");
+    } else {
+        osi_Log3(afsd_logp, "RDR_RenameFileEntry cm_Rename oldDscp 0x%p newDscp 0x%p failed code 0x%x",
+                 oldDscp, newDscp, code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        (*ResultCB)->ResultBufferLength = 0;
+    }
+
+    cm_ReleaseSCache(oldDscp);
+    cm_ReleaseSCache(newDscp);
+    return;
+}
+
+void
+RDR_FlushFileEntry( IN cm_user_t *userp,
+                    IN AFSFileID FileId,
+                    IN BOOL bWow64,
+                    IN DWORD ResultBufferLength,
+                    IN OUT AFSCommResult **ResultCB)
+{
+    cm_scache_t *scp = NULL;
+    cm_fid_t    Fid;
+    afs_uint32  code;
+    cm_req_t    req;
+    DWORD       status;
+#ifdef ODS_DEBUG
+    char        dbgstr[1024];
+#endif
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    osi_Log4(afsd_logp, "RDR_FlushFileEntry File FID cell 0x%x vol 0x%x vno 0x%x uniq 0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+#ifdef ODS_DEBUG
+    snprintf( dbgstr, 1024,
+              "RDR_FlushFileEntry File FID cell 0x%x vol 0x%x vno 0x%x uniq 0x%x\n",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+    OutputDebugStringA( dbgstr);
+#endif
+
+    *ResultCB = (AFSCommResult *)malloc( sizeof( AFSCommResult));
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_FlushFileEntry out of memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            sizeof( AFSCommResult));
+
+    /* Process the release */
+    Fid.cell = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_FlushFileEntry cm_GetSCache FID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    if (scp->flags & CM_SCACHEFLAG_DELETED) {
+        lock_ReleaseWrite(&scp->rw);
+        (*ResultCB)->ResultStatus = STATUS_INVALID_HANDLE;
+        return;
+    }
+
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log3(afsd_logp, "RDR_FlushFileEntry cm_SyncOp failure scp=0x%p code=0x%x status=0x%x",
+                 scp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    code = cm_FSync(scp, userp, &req, FALSE);
+    cm_ReleaseSCache(scp);
+
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_FlushFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    } else {
+        (*ResultCB)->ResultStatus = 0;
+        osi_Log0(afsd_logp, "RDR_FlushFileEntry SUCCESS");
+    }
+    (*ResultCB)->ResultBufferLength = 0;
+
+    return;
+}
+
+afs_uint32
+RDR_CheckAccess( IN cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
+                 ULONG access,
+                 ULONG *granted)
+{
+    ULONG afs_acc, afs_gr;
+    BOOLEAN file, dir;
+    afs_uint32 code = 0;
+
+    file = (scp->fileType == CM_SCACHETYPE_FILE);
+    dir = !file;
+
+    /* access definitions from prs_fs.h */
+    afs_acc = 0;
+    if (access & FILE_READ_DATA)
+       afs_acc |= PRSFS_READ;
+    if (access & FILE_READ_EA || access & FILE_READ_ATTRIBUTES)
+       afs_acc |= PRSFS_READ;
+    if (file && ((access & FILE_WRITE_DATA) || (access & FILE_APPEND_DATA)))
+       afs_acc |= PRSFS_WRITE;
+    if (access & FILE_WRITE_EA || access & FILE_WRITE_ATTRIBUTES)
+       afs_acc |= PRSFS_WRITE;
+    if (dir && ((access & FILE_ADD_FILE) || (access & FILE_ADD_SUBDIRECTORY)))
+       afs_acc |= PRSFS_INSERT;
+    if (dir && (access & FILE_LIST_DIRECTORY))
+       afs_acc |= PRSFS_LOOKUP;
+    if (file && (access & FILE_EXECUTE))
+       afs_acc |= PRSFS_WRITE;
+    if (dir && (access & FILE_TRAVERSE))
+       afs_acc |= PRSFS_READ;
+    if (dir && (access & FILE_DELETE_CHILD))
+       afs_acc |= PRSFS_DELETE;
+    if ((access & DELETE))
+       afs_acc |= PRSFS_DELETE;
+
+    /* check ACL with server */
+    lock_ObtainWrite(&scp->rw);
+    while (1)
+    {
+       if (cm_HaveAccessRights(scp, userp, reqp, afs_acc, &afs_gr))
+        {
+            break;
+        }
+       else
+        {
+            /* we don't know the required access rights */
+            code = cm_GetAccessRights(scp, userp, reqp);
+            if (code)
+                break;
+            continue;
+        }
+    }
+    lock_ReleaseWrite(&(scp->rw));
+
+    if (code == 0) {
+        *granted = 0;
+        if (afs_gr & PRSFS_READ)
+            *granted |= FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES | FILE_EXECUTE;
+        if (afs_gr & PRSFS_WRITE)
+            *granted |= FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE;
+        if (afs_gr & PRSFS_INSERT)
+            *granted |= (dir ? FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY : 0) | (file ? FILE_ADD_SUBDIRECTORY : 0);
+        if (afs_gr & PRSFS_LOOKUP)
+            *granted |= (dir ? FILE_LIST_DIRECTORY : 0);
+        if (afs_gr & PRSFS_DELETE)
+            *granted |= FILE_DELETE_CHILD | DELETE;
+        if (afs_gr & PRSFS_LOCK)
+            *granted |= 0;
+        if (afs_gr & PRSFS_ADMINISTER)
+            *granted |= 0;
+
+        *granted |= SYNCHRONIZE | READ_CONTROL;
+
+        /* don't give more access than what was requested */
+        *granted &= access;
+        osi_Log3(afsd_logp, "RDR_CheckAccess SUCCESS scp=0x%p requested=0x%x granted=0x%x", scp, access, *granted);
+    } else
+        osi_Log2(afsd_logp, "RDR_CheckAccess FAILURE scp=0x%p code=0x%x",
+                 scp, code);
+
+    return code;
+}
+
+void
+RDR_OpenFileEntry( IN cm_user_t *userp,
+                   IN AFSFileID FileId,
+                   IN AFSFileOpenCB *OpenCB,
+                   IN BOOL bWow64,
+                   IN BOOL bHoldFid,
+                   IN DWORD ResultBufferLength,
+                   IN OUT AFSCommResult **ResultCB)
+{
+    AFSFileOpenResultCB *pResultCB = NULL;
+    cm_scache_t *scp = NULL;
+    cm_user_t   *sysUserp = NULL;
+    cm_fid_t    Fid;
+    cm_lock_data_t      *ldp = NULL;
+    afs_uint32  code;
+    cm_req_t    req;
+    DWORD       status;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    osi_Log4(afsd_logp, "RDR_OpenFileEntry File FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc( sizeof( AFSCommResult) + sizeof( AFSFileOpenResultCB));
+    if (!(*ResultCB)) {
+        osi_Log0(afsd_logp, "RDR_OpenFileEntry out of memory");
+       return;
+    }
+
+    memset( *ResultCB,
+            '\0',
+            sizeof( AFSCommResult) + sizeof( AFSFileOpenResultCB));
+
+    pResultCB = (AFSFileOpenResultCB *)(*ResultCB)->ResultData;
+
+    /* Process the release */
+    Fid.cell = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_OpenFileEntry cm_GetSCache FID failure code=0x%x status=0x%x",
+                  code, status);
+        return;
+    }
+
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        lock_ReleaseWrite(&scp->rw);
+        cm_ReleaseSCache(scp);
+        osi_Log3(afsd_logp, "RDR_OpenFileEntry cm_SyncOp failure scp=0x%p code=0x%x status=0x%x",
+                 scp, code, status);
+        return;
+    }
+
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+
+    sysUserp = RDR_GetLocalSystemUser();
+
+    /*
+     * Skip the open check if the request is coming from the local system account.
+     * The local system has no tokens and therefore any requests sent to a file
+     * server will fail.  Unfortunately, there are special system processes that
+     * perform actions on files and directories in preparation for memory mapping
+     * executables.  If the open check fails, the real request from the user process
+     * will never be issued.
+     *
+     * Permitting the file system to allow subsequent operations to proceed does
+     * not compromise security.  All requests to obtain file data or directory
+     * enumerations will subsequently fail if they are not submitted under the
+     * context of a process for that have access to the necessary credentials.
+     */
+
+    if ( userp == sysUserp)
+    {
+        osi_Log1(afsd_logp, "RDR_OpenFileEntry LOCAL_SYSTEM access check skipped scp=0x%p",
+                 scp);
+        pResultCB->GrantedAccess = OpenCB->DesiredAccess;
+        code = 0;
+    } else {
+        int count = 0;
+        do {
+            if (count++ > 0) {
+                Sleep(350);
+                osi_Log3(afsd_logp,
+                         "RDR_OpenFileEntry repeating open check scp=0x%p userp=0x%p code=0x%x",
+                         scp, userp, code);
+            }
+            code = cm_CheckNTOpen(scp, OpenCB->DesiredAccess, OPEN_ALWAYS, userp, &req, &ldp);
+            if (code == 0)
+                code = RDR_CheckAccess(scp, userp, &req, OpenCB->DesiredAccess, &pResultCB->GrantedAccess);
+            cm_CheckNTOpenDone(scp, userp, &req, &ldp);
+        } while (count < 100 && (code == CM_ERROR_RETRY || code == CM_ERROR_WOULDBLOCK));
+    }
+
+    cm_ReleaseUser(sysUserp);
+    if (bHoldFid)
+        RDR_FlagScpInUse( scp, FALSE );
+    cm_ReleaseSCache(scp);
+
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_OpenFileEntry FAILURE code=0x%x status=0x%x",
+                  code, status);
+    } else {
+        (*ResultCB)->ResultStatus = 0;
+        (*ResultCB)->ResultBufferLength = sizeof( AFSFileOpenResultCB);
+        osi_Log0(afsd_logp, "RDR_OpenFileEntry SUCCESS");
+    }
+    return;
+}
+
+static const char *
+HexCheckSum(unsigned char * buf, int buflen, unsigned char * md5cksum)
+{
+    int i, k;
+    static char tr[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+    if (buflen < 33)
+        return "buffer length too small to HexCheckSum";
+
+    for (i=0;i<16;i++) {
+        k = md5cksum[i];
+
+        buf[i*2] = tr[k / 16];
+        buf[i*2+1] = tr[k % 16];
+    }
+    buf[32] = '\0';
+
+    return buf;
+}
+
+/*
+ * Extent requests from the file system are triggered when a file
+ * page is not resident in the Windows cache.  The file system is
+ * responsible for loading the page but cannot block the request
+ * while doing so.  The AFS Redirector forwards the requests to
+ * the AFS cache manager while indicating to Windows that the page
+ * is not yet available.  A polling operation will then ensue with
+ * the AFS Redirector issuing a RDR_RequestFileExtentsXXX call for
+ * each poll attempt.  As each request is received and processed
+ * by a separate worker thread in the service, this can lead to
+ * contention by multiple threads attempting to claim the same
+ * cm_buf_t objects.  Therefore, it is important that
+ *
+ *  (a) the service avoid processing more than one overlapping
+ *      extent request at a time
+ *  (b) background daemon processing be used to avoid blocking
+ *      of ioctl threads
+ *
+ * Beginning with the 20091122 build of the redirector, the redirector
+ * will not issue an additional RDR_RequestFileExtentsXXX call for
+ * each poll request.  Instead, afsd_service is required to track
+ * the requests and return them to the redirector or fail the
+ * portions of the request that cannot be satisfied.
+ *
+ * The request processing returns any extents that can be returned
+ * immediately to the redirector.  The rest of the requested range(s)
+ * are queued as background operations using RDR_BkgFetch().
+ */
+
+/* do the background fetch. */
+afs_int32
+RDR_BkgFetch(cm_scache_t *scp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4,
+             cm_user_t *userp, cm_req_t *reqp)
+{
+    osi_hyper_t length;
+    osi_hyper_t base;
+    osi_hyper_t offset;
+    osi_hyper_t end;
+    osi_hyper_t fetched;
+    osi_hyper_t tblocksize;
+    afs_int32 code;
+    int rwheld = 0;
+    cm_buf_t *bufp = NULL;
+    DWORD dwResultBufferLength;
+    AFSSetFileExtentsCB *pResultCB;
+    DWORD status;
+    afs_uint32 count=0;
+    AFSFileID FileId;
+    int reportErrorToRedir = 0;
+    int force_retry = 0;
+
+    FileId.Cell = scp->fid.cell;
+    FileId.Volume = scp->fid.volume;
+    FileId.Vnode = scp->fid.vnode;
+    FileId.Unique = scp->fid.unique;
+    FileId.Hash = scp->fid.hash;
+
+    if ((GetTickCount() - reqp->startTime) / 1000 > HardDeadtimeout * 5) {
+        RDR_SetFileStatus( &scp->fid, STATUS_IO_TIMEOUT);
+        return 0;
+    }
+
+    fetched.LowPart = 0;
+    fetched.HighPart = 0;
+    tblocksize = ConvertLongToLargeInteger(cm_data.buf_blockSize);
+    base.LowPart = p1;
+    base.HighPart = p2;
+    length.LowPart = p3;
+    length.HighPart = p4;
+
+    end = LargeIntegerAdd(base, length);
+
+    osi_Log5(afsd_logp, "Starting BKG Fetch scp 0x%p offset 0x%x:%x length 0x%x:%x",
+             scp, p2, p1, p4, p3);
+
+    /*
+     * Make sure we have a callback.
+     * This is necessary so that we can return access denied
+     * if a callback cannot be granted.
+     */
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_READ,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) {
+        lock_ReleaseWrite(&scp->rw);
+        osi_Log2(afsd_logp, "RDR_BkgFetch cm_SyncOp failure scp=0x%p code=0x%x",
+                 scp, code);
+        smb_MapNTError(cm_MapRPCError(code, reqp), &status, TRUE);
+        RDR_SetFileStatus( &scp->fid, status);
+        return code;
+    }
+    lock_ReleaseWrite(&scp->rw);
+
+    dwResultBufferLength = (DWORD)(sizeof( AFSSetFileExtentsCB) + sizeof( AFSSetFileExtentsCB) * (length.QuadPart / cm_data.blockSize + 1));
+    pResultCB = (AFSSetFileExtentsCB *)malloc( dwResultBufferLength );
+    if (!pResultCB)
+        return CM_ERROR_RETRY;
+
+    memset( pResultCB, '\0', dwResultBufferLength );
+    pResultCB->FileId = FileId;
+
+    for ( code = 0, offset = base;
+          code == 0 && LargeIntegerLessThan(offset, end);
+          offset = LargeIntegerAdd(offset, tblocksize) )
+    {
+        int bBufRelease = TRUE;
+
+        if (rwheld) {
+            lock_ReleaseWrite(&scp->rw);
+            rwheld = 0;
+        }
+
+        code = buf_Get(scp, &offset, reqp, &bufp);
+        if (code) {
+            /*
+             * any error from buf_Get() is non-fatal.
+             * we need to re-queue this extent fetch.
+             */
+            force_retry = 1;
+            continue;
+        }
+
+        if (!rwheld) {
+            lock_ObtainWrite(&scp->rw);
+            rwheld = 1;
+        }
+
+        code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
+        if (code == 0) {
+            if (bufp->flags & CM_BUF_DIRTY) {
+                if (rwheld) {
+                    lock_ReleaseWrite(&scp->rw);
+                    rwheld = 0;
+                }
+                cm_BufWrite(scp, &bufp->offset, cm_chunkSize, 0, userp, reqp);
+            }
+
+            if (!(bufp->qFlags & CM_BUF_QREDIR)) {
+#ifdef VALIDATE_CHECK_SUM
+#ifdef ODS_DEBUG
+                char md5dbg[33];
+                char dbgstr[1024];
+#endif
+#endif
+                if (!rwheld) {
+                    lock_ObtainWrite(&scp->rw);
+                    rwheld = 1;
+                }
+                lock_ObtainWrite(&buf_globalLock);
+                if (!(bufp->flags & CM_BUF_DIRTY) &&
+                    bufp->cmFlags == 0 &&
+                    !(bufp->qFlags & CM_BUF_QREDIR)) {
+                    buf_InsertToRedirQueue(scp, bufp);
+                    lock_ReleaseWrite(&buf_globalLock);
+                    lock_ReleaseWrite(&scp->rw);
+                    rwheld = 0;
+
+#ifdef VALIDATE_CHECK_SUM
+                    buf_ComputeCheckSum(bufp);
+#endif
+                    pResultCB->FileExtents[count].Flags = 0;
+                    pResultCB->FileExtents[count].FileOffset.QuadPart = bufp->offset.QuadPart;
+                    pResultCB->FileExtents[count].CacheOffset.QuadPart = bufp->datap - RDR_extentBaseAddress;
+                    pResultCB->FileExtents[count].Length = cm_data.blockSize;
+                    count++;
+                    fetched = LargeIntegerAdd(fetched, tblocksize);
+                    bBufRelease = FALSE;
+
+#ifdef VALIDATE_CHECK_SUM
+#ifdef ODS_DEBUG
+                    HexCheckSum(md5dbg, sizeof(md5dbg), bufp->md5cksum);
+                    snprintf( dbgstr, 1024,
+                              "RDR_BkgFetch md5 %s vol 0x%x vno 0x%x uniq 0x%x foffset 0x%x:%x coffset 0x%x:%x\n",
+                              md5dbg,
+                              scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                              pResultCB->FileExtents[count].FileOffset.HighPart,
+                              pResultCB->FileExtents[count].FileOffset.LowPart,
+                              pResultCB->FileExtents[count].CacheOffset.HighPart,
+                              pResultCB->FileExtents[count].CacheOffset.LowPart);
+                    OutputDebugStringA( dbgstr);
+#endif
+#endif
+                    osi_Log4(afsd_logp, "RDR_BkgFetch Extent2FS bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                              bufp, bufp->offset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+                } else {
+                    lock_ReleaseWrite(&buf_globalLock);
+                    if ((bufp->cmFlags != 0) || (bufp->flags & CM_BUF_DIRTY)) {
+                        /* An I/O operation is already in progress */
+                        force_retry = 1;
+                        osi_Log4(afsd_logp, "RDR_BkgFetch Extent2FS Not delivering to Redirector Dirty or Busy bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                                  bufp, bufp->offset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+                    } else {
+                        osi_Log4(afsd_logp, "RDR_BkgFetch Extent2FS Already held by Redirector bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                                  bufp, bufp->offset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+                    }
+                }
+            } else {
+                osi_Log4(afsd_logp, "RDR_BkgFetch Extent2FS Already held by Redirector bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                          bufp, bufp->offset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+            }
+
+            if (rwheld) {
+                lock_ReleaseWrite(&scp->rw);
+                rwheld = 0;
+            }
+
+        } else {
+            /*
+             * depending on what the error from cm_GetBuffer is
+             * it may or may not be fatal.  Only return fatal errors.
+             * Re-queue a request for others.
+             */
+            osi_Log5(afsd_logp, "RDR_BkgFetch Extent2FS FAILURE bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x code 0x%x",
+                      bufp, offset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize, code);
+            switch (code) {
+            case CM_ERROR_NOACCESS:
+            case CM_ERROR_NOSUCHFILE:
+            case CM_ERROR_NOSUCHPATH:
+            case CM_ERROR_NOSUCHVOLUME:
+            case CM_ERROR_NOSUCHCELL:
+            case CM_ERROR_INVAL:
+            case CM_ERROR_BADFD:
+            case CM_ERROR_CLOCKSKEW:
+            case RXKADNOAUTH:
+            case CM_ERROR_QUOTA:
+            case CM_ERROR_LOCK_CONFLICT:
+                /*
+                 * these are fatal errors.  deliver what we can
+                 * and halt.
+                 */
+                reportErrorToRedir = 1;
+                break;
+            default:
+                /*
+                 * non-fatal errors.  re-queue the exent
+                 */
+                code = CM_ERROR_RETRY;
+            }
+        }
+
+        if (bBufRelease)
+            buf_Release(bufp);
+    }
+
+    if (!rwheld) {
+        lock_ObtainWrite(&scp->rw);
+        rwheld = 1;
+    }
+
+    /* wakeup anyone who is waiting */
+    if (scp->flags & CM_SCACHEFLAG_WAITING) {
+        osi_Log1(afsd_logp, "RDR Bkg Fetch Waking scp 0x%p", scp);
+        osi_Wakeup((LONG_PTR) &scp->flags);
+    }
+    lock_ReleaseWrite(&scp->rw);
+
+    if (count > 0) {
+        pResultCB->ExtentCount = count;
+        RDR_SetFileExtents( pResultCB, dwResultBufferLength);
+    }
+    free(pResultCB);
+
+    if (reportErrorToRedir) {
+        smb_MapNTError(cm_MapRPCError(code, reqp), &status, TRUE);
+        RDR_SetFileStatus( &scp->fid, status);
+    }
+
+    osi_Log4(afsd_logp, "Ending BKG Fetch scp 0x%p code 0x%x fetched 0x%x:%x",
+             scp, code, fetched.HighPart, fetched.LowPart);
+
+    return force_retry ? CM_ERROR_RETRY : code;
+}
+
+
+BOOL
+RDR_RequestFileExtentsAsync( IN cm_user_t *userp,
+                             IN AFSFileID FileId,
+                             IN AFSRequestExtentsCB *RequestExtentsCB,
+                             IN BOOL bWow64,
+                             IN OUT DWORD * ResultBufferLength,
+                             IN OUT AFSSetFileExtentsCB **ResultCB)
+{
+    AFSSetFileExtentsCB *pResultCB = NULL;
+    DWORD Length;
+    DWORD count;
+    DWORD status;
+    cm_scache_t *scp = NULL;
+    cm_fid_t    Fid;
+    cm_buf_t    *bufp;
+    afs_uint32  code = 0;
+    osi_hyper_t thyper;
+    LARGE_INTEGER ByteOffset, BeginOffset, EndOffset, QueueOffset;
+    afs_uint32  QueueLength;
+    cm_req_t    req;
+    BOOLEAN     bBufRelease = TRUE;
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+    req.flags |= CM_REQ_NORETRY;
+
+    osi_Log4(afsd_logp, "RDR_RequestFileExtentsAsync File FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+    osi_Log4(afsd_logp, "... Flags 0x%x ByteOffset 0x%x:%x Length 0x%x",
+             RequestExtentsCB->Flags,
+             RequestExtentsCB->ByteOffset.HighPart, RequestExtentsCB->ByteOffset.LowPart,
+             RequestExtentsCB->Length);
+    Length = sizeof( AFSSetFileExtentsCB) + sizeof( AFSFileExtentCB) * (RequestExtentsCB->Length / cm_data.blockSize + 1);
+
+    pResultCB = *ResultCB = (AFSSetFileExtentsCB *)malloc( Length );
+    if (*ResultCB == NULL) {
+        *ResultBufferLength = 0;
+        return FALSE;
+    }
+    *ResultBufferLength = Length;
+
+    memset( pResultCB, '\0', Length );
+    pResultCB->FileId = FileId;
+
+    Fid.cell = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        osi_Log1(afsd_logp, "RDR_RequestFileExtentsAsync cm_GetSCache FID failure code=0x%x",
+                  code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        RDR_SetFileStatus( &scp->fid, status);
+        return FALSE;
+    }
+
+    /*
+     * Make sure we have a callback.
+     * This is necessary so that we can return access denied
+     * if a callback cannot be granted.
+     */
+    lock_ObtainWrite(&scp->rw);
+    code = cm_SyncOp(scp, NULL, userp, &req, PRSFS_READ,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    lock_ReleaseWrite(&scp->rw);
+    if (code) {
+        cm_ReleaseSCache(scp);
+        osi_Log2(afsd_logp, "RDR_RequestFileExtentsAsync cm_SyncOp failure scp=0x%p code=0x%x",
+                 scp, code);
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        RDR_SetFileStatus( &scp->fid, status);
+        return FALSE;
+    }
+
+    /* Allocate the extents from the buffer package */
+    for ( count = 0,
+          ByteOffset = BeginOffset = RequestExtentsCB->ByteOffset,
+          EndOffset.QuadPart = ByteOffset.QuadPart + RequestExtentsCB->Length;
+          code == 0 && ByteOffset.QuadPart < EndOffset.QuadPart;
+          ByteOffset.QuadPart += cm_data.blockSize)
+    {
+        BOOL bHaveBuffer = FALSE;
+
+        QueueLength = 0;
+        thyper.QuadPart = ByteOffset.QuadPart;
+
+        code = buf_Get(scp, &thyper, &req, &bufp);
+        if (code == 0) {
+            lock_ObtainMutex(&bufp->mx);
+            bBufRelease = TRUE;
+
+            if (bufp->qFlags & CM_BUF_QREDIR) {
+                bHaveBuffer = TRUE;
+            } else if (bufp->flags & CM_BUF_DIRTY) {
+                bHaveBuffer = FALSE;
+#if 0
+                code = buf_CleanAsyncLocked(scp, bufp, &req, 0, NULL);
+                switch (code) {
+                case 0:
+                    bHaveBuffer = TRUE;
+                    break;
+                case CM_ERROR_RETRY:
+                    /* Couldn't flush it, obtain it asynchronously so we don't block the thread. */
+                    bHaveBuffer = FALSE;
+                    code = 0;
+                    break;
+                default:
+                    smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+                    RDR_SetFileStatus(&FileId, status);
+                    bHaveBuffer = FALSE;
+                    code = 0;
+                }
+#endif
+            } else {
+                osi_hyper_t minLength;  /* effective end of file */
+
+                lock_ObtainRead(&scp->rw);
+                bHaveBuffer = cm_HaveBuffer(scp, bufp, TRUE);
+
+                if (LargeIntegerGreaterThan(scp->length, scp->serverLength))
+                    minLength = scp->serverLength;
+                else
+                    minLength = scp->length;
+
+                if (LargeIntegerGreaterThanOrEqualTo(bufp->offset, minLength)) {
+                    memset(bufp->datap, 0, cm_data.buf_blockSize);
+                    bufp->dataVersion = scp->dataVersion;
+                    bHaveBuffer = TRUE;
+                }
+                lock_ReleaseRead(&scp->rw);
+
+                if ((RequestExtentsCB->Flags & AFS_EXTENT_FLAG_CLEAN) &&
+                     ByteOffset.QuadPart <= bufp->offset.QuadPart &&
+                     EndOffset.QuadPart >= bufp->offset.QuadPart + cm_data.blockSize) {
+                    memset(bufp->datap, 0, cm_data.blockSize);
+                    bufp->dataVersion = scp->dataVersion;
+                    buf_SetDirty(bufp, &req, 0, cm_data.blockSize, userp);
+                    bHaveBuffer = TRUE;
+                }
+            }
+
+            /*
+             * if this buffer is already up to date, skip it.
+             */
+            if (bHaveBuffer) {
+                if (ByteOffset.QuadPart == BeginOffset.QuadPart) {
+                    BeginOffset.QuadPart += cm_data.blockSize;
+                } else {
+                    QueueLength = (afs_uint32)(ByteOffset.QuadPart - BeginOffset.QuadPart);
+                    QueueOffset = BeginOffset;
+                    BeginOffset = ByteOffset;
+                }
+
+                if (!(bufp->qFlags & CM_BUF_QREDIR)) {
+#ifdef VALIDATE_CHECK_SUM
+#ifdef ODS_DEBUG
+                    char md5dbg[33];
+                    char dbgstr[1024];
+#endif
+#endif
+                    lock_ObtainWrite(&scp->rw);
+                    lock_ObtainWrite(&buf_globalLock);
+                    if (!(bufp->qFlags & CM_BUF_QREDIR)) {
+                        buf_InsertToRedirQueue(scp, bufp);
+                        lock_ReleaseWrite(&buf_globalLock);
+                        lock_ReleaseWrite(&scp->rw);
+
+#ifdef VALIDATE_CHECK_SUM
+                        buf_ComputeCheckSum(bufp);
+#endif
+                        /* we already have the buffer, return it now */
+                        pResultCB->FileExtents[count].Flags = 0;
+                        pResultCB->FileExtents[count].FileOffset = ByteOffset;
+                        pResultCB->FileExtents[count].CacheOffset.QuadPart = bufp->datap - RDR_extentBaseAddress;
+                        pResultCB->FileExtents[count].Length = cm_data.blockSize;
+                        count++;
+
+                        bBufRelease = FALSE;
+
+#ifdef VALIDATE_CHECK_SUM
+#ifdef ODS_DEBUG
+                        HexCheckSum(md5dbg, sizeof(md5dbg), bufp->md5cksum);
+                        snprintf( dbgstr, 1024,
+                                  "RDR_RequestFileExtentsAsync md5 %s vol 0x%x vno 0x%x uniq 0x%x foffset 0x%x:%x coffset 0x%x:%x\n",
+                                  md5dbg,
+                                  scp->fid.volume, scp->fid.vnode, scp->fid.unique,
+                                  pResultCB->FileExtents[count].FileOffset.HighPart,
+                                  pResultCB->FileExtents[count].FileOffset.LowPart,
+                                  pResultCB->FileExtents[count].CacheOffset.HighPart,
+                                  pResultCB->FileExtents[count].CacheOffset.LowPart);
+                        OutputDebugStringA( dbgstr);
+#endif
+#endif
+                        osi_Log4(afsd_logp, "RDR_RequestFileExtentsAsync Extent2FS bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                                 bufp, ByteOffset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+                    } else {
+                        lock_ReleaseWrite(&buf_globalLock);
+                        lock_ReleaseWrite(&scp->rw);
+                    }
+                } else {
+                    if (bBufRelease) {
+                        /*
+                         * The service is not handing off the extent to the redirector in this pass.
+                         * However, we know the buffer is in recent use so move the buffer to the
+                         * front of the queue
+                         */
+                        lock_ObtainWrite(&scp->rw);
+                        lock_ObtainWrite(&buf_globalLock);
+                        buf_MoveToHeadOfRedirQueue(scp, bufp);
+                        lock_ReleaseWrite(&buf_globalLock);
+                        lock_ReleaseWrite(&scp->rw);
+
+                        osi_Log4(afsd_logp, "RDR_RequestFileExtentsAsync Extent2FS Already held by Redirector bufp 0x%p foffset 0x%p coffset 0x%p len 0x%x",
+                                 bufp, ByteOffset.QuadPart, bufp->datap - RDR_extentBaseAddress, cm_data.blockSize);
+                    }
+                }
+            }
+            lock_ReleaseMutex(&bufp->mx);
+            if (bBufRelease)
+                buf_Release(bufp);
+
+            if (QueueLength) {
+                cm_QueueBKGRequest(scp, RDR_BkgFetch, QueueOffset.LowPart, QueueOffset.HighPart,
+                                   QueueLength, 0, userp, &req);
+                osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
+                         QueueOffset.HighPart, QueueOffset.LowPart, QueueLength);
+            }
+        } else {
+            /* No error from buf_Get() can be fatal */
+            osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync buf_Get FAILURE offset 0x%x:%x code 0x%x",
+                     BeginOffset.HighPart, BeginOffset.LowPart, code);
+        }
+    }
+
+    if (BeginOffset.QuadPart != EndOffset.QuadPart) {
+        afs_uint32 length = (afs_uint32)(EndOffset.QuadPart - BeginOffset.QuadPart);
+
+        cm_QueueBKGRequest(scp, RDR_BkgFetch, BeginOffset.LowPart, BeginOffset.HighPart,
+                           length, 0, userp, &req);
+        osi_Log3(afsd_logp, "RDR_RequestFileExtentsAsync Queued a Background Fetch offset 0x%x:%x length 0x%x",
+                  BeginOffset.HighPart, BeginOffset.LowPart, length);
+    }
+    cm_ReleaseSCache(scp);
+
+    (*ResultCB)->ExtentCount = count;
+    osi_Log1(afsd_logp, "RDR_RequestFileExtentsAsync replying with 0x%x extent records", count);
+    return FALSE;
+}
+
+/*
+ * When processing an extent release the extents must be accepted back by
+ * the service even if there is an error condition returned to the redirector.
+ * For example, there may no longer be a callback present or the file may
+ * have been deleted on the file server.  Regardless, the extents must be
+ * put back into the pool.
+ */
+void
+RDR_ReleaseFileExtents( IN cm_user_t *userp,
+                        IN AFSFileID FileId,
+                        IN AFSReleaseExtentsCB *ReleaseExtentsCB,
+                        IN BOOL bWow64,
+                        IN DWORD ResultBufferLength,
+                        IN OUT AFSCommResult **ResultCB)
+{
+    DWORD count;
+    cm_scache_t *scp = NULL;
+    cm_fid_t    Fid;
+    cm_buf_t    *bufp;
+    afs_uint32  code;
+    osi_hyper_t thyper;
+    cm_req_t    req;
+    int         dirty = 0;
+    int         released = 0;
+    DWORD       status;
+#ifdef ODS_DEBUG
+#ifdef VALIDATE_CHECK_SUM
+    char md5dbg[33], md5dbg2[33], md5dbg3[33];
+#endif
+    char dbgstr[1024];
+#endif
+
+    RDR_InitReq(&req);
+    if ( bWow64 )
+        req.flags |= CM_REQ_WOW64;
+
+    osi_Log4(afsd_logp, "RDR_ReleaseFileExtents File FID cell=0x%x vol=0x%x vn=0x%x uniq=0x%x",
+              FileId.Cell, FileId.Volume,
+              FileId.Vnode, FileId.Unique);
+
+    *ResultCB = (AFSCommResult *)malloc( sizeof( AFSCommResult));
+    if (!(*ResultCB))
+       return;
+
+    memset( *ResultCB,
+            '\0',
+            sizeof( AFSCommResult));
+
+    /* Process the release */
+    Fid.cell = FileId.Cell;
+    Fid.volume = FileId.Volume;
+    Fid.vnode = FileId.Vnode;
+    Fid.unique = FileId.Unique;
+    Fid.hash = FileId.Hash;
+
+    code = cm_GetSCache(&Fid, &scp, userp, &req);
+    if (code) {
+        smb_MapNTError(cm_MapRPCError(code, &req), &status, TRUE);
+        (*ResultCB)->ResultStatus = status;
+        osi_Log2(afsd_logp, "RDR_ReleaseFileExtents cm_GetSCache FID failure code=0x%x status=0x%x",
+                  code, status);
+    }
+
+    /*
+     * We do not stop processing as a result of being unable to find the cm_scache object.
+     * If this occurs something really bad has happened since the cm_scache object must have
+     * been recycled while extents were held by the redirector.  However, we will be resilient
+     * and carry on without it.
+     */
+    if (scp && ReleaseExtentsCB->AllocationSize.QuadPart != scp->length.QuadPart) {
+        cm_attr_t setAttr;
+
+        memset(&setAttr, 0, sizeof(cm_attr_t));
+        lock_ObtainWrite(&scp->rw);
+        if (ReleaseExtentsCB->AllocationSize.QuadPart != scp->length.QuadPart) {
+
+            osi_Log4(afsd_logp, "RDR_ReleaseFileExtents new length fid vol 0x%x vno 0x%x length 0x%x:%x",
+                      scp->fid.volume, scp->fid.vnode,
+                      ReleaseExtentsCB->AllocationSize.HighPart,
+                      ReleaseExtentsCB->AllocationSize.LowPart);
+
+            setAttr.mask |= CM_ATTRMASK_LENGTH;
+            setAttr.length.LowPart = ReleaseExtentsCB->AllocationSize.LowPart;
+            setAttr.length.HighPart = ReleaseExtentsCB->AllocationSize.HighPart;
+        }
+        lock_ReleaseWrite(&scp->rw);
+        if (setAttr.mask)
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
+    }
+
+    for ( count = 0; count < ReleaseExtentsCB->ExtentCount; count++) {
+        AFSFileExtentCB * pExtent = &ReleaseExtentsCB->FileExtents[count];
+
+        thyper.QuadPart = pExtent->FileOffset.QuadPart;
+
+        bufp = buf_Find(&Fid, &thyper);
+        if (bufp) {
+            if (pExtent->Flags & AFS_EXTENT_FLAG_UNKNOWN) {
+                if (!(bufp->qFlags & CM_BUF_QREDIR)) {
+                    osi_Log4(afsd_logp, "RDR_ReleaseFileExtents extent vol 0x%x vno 0x%x foffset 0x%x:%x",
+                              Fid.volume, Fid.vnode,
+                              pExtent->FileOffset.HighPart,
+                              pExtent->FileOffset.LowPart);
+                    osi_Log2(afsd_logp, "... coffset 0x%x:%x UNKNOWN to redirector; previously released",
+                              pExtent->CacheOffset.HighPart,
+                              pExtent->CacheOffset.LowPart);
+                } else {
+                    osi_Log4(afsd_logp, "RDR_ReleaseFileExtents extent vol 0x%x vno 0x%x foffset 0x%x:%x",
+                              Fid.volume, Fid.vnode,
+                              pExtent->FileOffset.HighPart,
+                              pExtent->FileOffset.LowPart);
+                    osi_Log2(afsd_logp, "... coffset 0x%x:%x UNKNOWN to redirector; owned by redirector",
+                              pExtent->CacheOffset.HighPart,
+                              pExtent->CacheOffset.LowPart);
+                }
+                buf_Release(bufp);
+                continue;
+            }
+
+            if (pExtent->Flags & AFS_EXTENT_FLAG_IN_USE) {
+                osi_Log4(afsd_logp, "RDR_ReleaseFileExtents extent vol 0x%x vno 0x%x foffset 0x%x:%x",
+                          Fid.volume, Fid.vnode,
+                          pExtent->FileOffset.HighPart,
+                          pExtent->FileOffset.LowPart);
+                osi_Log2(afsd_logp, "... coffset 0x%x:%x IN_USE by file system",
+                          pExtent->CacheOffset.HighPart,
+                          pExtent->CacheOffset.LowPart);
+
+                /* Move the buffer to the front of the queue */
+                if (scp)
+                    lock_ObtainWrite(&scp->rw);
+                lock_ObtainWrite(&buf_globalLock);
+                buf_MoveToHeadOfRedirQueue(scp, bufp);
+                lock_ReleaseWrite(&buf_globalLock);
+                if (scp)
+