// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.


/*

    File Replication Sample
    Client System Service

    FILE: FileRepClientProc.cpp
    
    PURPOSE: Remote procedures for client system service
    
    FUNCTIONS:
        RequestFile() - receives file replication requests
        ThreadProcRequest() - processes file replication requests
    in an independent thread.

    COMMENTS:

*/

#include "common.h"

#include <stddef.h>
#include <Ntdsapi.h>
#include <time.h>

// header file generated by MIDL compiler
#include "FileRepClient.h"
#include "FileRepServer.h"

// Contains declarations for system service functions.
#include "Service.h"

#ifdef DEBUG2
#include "DbgMsg.h"
#endif

#ifdef PROF
#include "Prof.h"
#endif

extern HANDLE ClientCompletionPort;
extern LONG nThreadsAtClientCompletionPort;

// Files that are being written will be extended by this
// size to allow writes to be trully asyncronous.
#define FILE_SIZE_EXTENSION (1*1024*1024)

// Timeout for threads waiting on the client completion port
#define ClientCompletionPortTimeout (20*1000)


// The types of IO posted onto the completion port
typedef enum {
  IoFileRep, // Posted by FileRep
  IoPipe, // Posted by the RPC runtime
  IoFile // Posted by the File IO subsystem
} IoCompletionType;

// Action to be taken next for a given request
typedef enum {
  Activate,
  Pull,
  Wait
} ActionType;

// The state of the client request.
typedef enum tReqState {
    StateArrived,
    StateQueued,
    StateActive
} ReqState;

#ifdef DEBUG1
// Used to track the number of outstanding requests
unsigned nClientReqs = 0;
#endif

//
// Packages up the variables to be passed
// to the processing thread.
//
typedef struct tReq{

    // Binding handle to the server
    handle_t hFileRepServer;
    // Explicit binding to the client system service
    handle_t hFileRepClient;

    HANDLE hTokenHandle;
    HANDLE hLocalFile;

    RPC_STR ServerName;
    LPTSTR RemoteFileName;
    LPTSTR LocalFileName;

    // Set when we are impersonating the client
    BOOL bImpersonating;

    // Priority of this request
    UINT Pri;

    // User SID
    PSID pSID;

    // The state of the current request
    ReqState State;

    // The out-pipe we use to pull data from the server.
    ASYNC_CHAR_PIPE_TYPE OutPipe;

    // The async handle for the call to RemoteOpen.
    RPC_ASYNC_STATE Async;

    BYTE pbBuf[PULL_BUFSIZE];
  
    LONG FileWritePos;
    LONG CurrentExtendedSize;

    OVERLAPPED FileOl;

    CRITICAL_SECTION Lock;

    LONG nWritesOutstanding;
    LONG nBytesOutstanding;

    // There is an outstanding pull.
    BOOL bPullOutstanding;

    // There is a buffer that has been pulled
    // but haven't been written to the server yet.
    BOOL bBuf;

    // A receive complete notification has been received,
    // but no pulls have been done yet, and so the next pull
    // should succeed.
    BOOL bDataAvailable;

    // Set when all the pulls have completed.
    BOOL bAllPullsDone;

    BOOL bCallMade;
    BOOL bCallCancelled;

    // Set when call complete notification has been received or
    // a syncronous failure has been received from the pull routine.
    BOOL bCallComplete;

    // Set when we received a premature call completed notification and query
    // call status returned an error.
    BOOL bAsyncCallFailureReceived;

    // We use this wariable to set an error code so that
    // ClientShutdownRequest can know whether an error has occurred.
    DWORD Status;

    // Set after pull returned an error.
    BOOL bFailureFromPull;

    // Set after WriteFile returned an error.
    BOOL bFailureFromWrite;

#ifdef PROF
    // Used to track arrival and completion times of requests.
    ULONG nReqId;
#endif

} Req;


//
// Extends file associated with hFile to Size bytes.
// This is necessary to allow the async writes to be "trully"
// async.  Because of the inner workings of NTFS when writes extend
// the size of the file they may end up being syncronous.
//
VOID SetFileSize (HANDLE hFile, DWORD Size) {
    SetFilePointer(hFile,
                   Size,
                   NULL,
                   FILE_BEGIN);
    SetEndOfFile(hFile);
}

//
// Checks request queues in the order of decreasing priority.  If a request is found and
// there is space in the corresponding quota of active requests, then it is returned.
//
Req* FindCReq(void) {

#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
#endif

    Req *pReq = NULL;

    //
    // We will now go and check if the request or the active request sets of queues have anything that we
    // can service.  If they do not, then this thread can terminate.
    //

    // Check if the request queues have anything on them.  If they do,
    // pick a request off the highest priority queue and handle it, but only if we
    // are not handling enough of those requests already.
    for (UINT pri = NumPriGroups; pri > 0; pri--) {
      pReq = (Req *) QueueRemove(ClientReqQueues[pri-1]);
      if (pReq != NULL) {
        break;
      }
    }
    
    // We had found a request
    if (pReq != NULL) {
      
#ifdef DEBUG2
      nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Took req %p off Req queue %p\n"), pReq, ClientReqQueues[pReq->Pri]);
      bufSize -= nCharWritten;
      DbgMsgRecord(Msg);
#endif
      
      if (CounterIncrement(pClientActiveReqCounters[pReq->Pri])) {
        if (QueueHashIncrementCounter(ClientActiveReqHashCounters[pReq->Pri], pReq->pSID)) {
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Incremented ClientActiveReqCounters[%d] and ClientActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif
          // The request now resides on a new queue.
          CounterDecrement(pClientReqCounters[pReq->Pri]);
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ClientReqCounters[%d]\n"), pReq->Pri);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif            
          pReq->State = StateActive;
          
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Handling active req %p\n"), pReq);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif

          return pReq;
          
        }
        else {
          // There were too many requests for a given SID, place the request back
          // onto the queue.
          
          // Don't forget to decrement the counter for the group!
          CounterDecrement(pClientActiveReqCounters[pReq->Pri]);
          
          QueueAdd(ClientReqQueues[pReq->Pri], pReq, TRUE);
          
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Too many active requests for a SID\n"));
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ClientReqQueues[pReq->Pri]);
          bufSize -= nCharWritten;
		  DbgMsgRecord(Msg);
#endif
        }
      }
      else {
        // There were too many active requests for the user group, place the request back
        // onto the queue.
        
        QueueAdd(ClientReqQueues[pReq->Pri], pReq, TRUE);
        
#ifdef DEBUG2
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Too many active requests for priority %d\n"), pReq->Pri);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ClientReqQueues[pReq->Pri]);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
#endif
      }
    }

    return NULL;
}

//
// Attempts to find a queued request and post an IO completion
// packet telling the worker threads to begin processing the request.
//
VOID FindAndActivateCReq(VOID) {
    DWORD status;

    tReq *pReq = FindCReq();

    if (pReq) {

#ifdef DEBUG1
      QueueAdd(ClientActiveReqQueue, pReq, TRUE);
#endif

      // All that is requeired to activate the request is to queue a completion
      // packet with Activate key and the Req as Overlapped.
      status = PostQueuedCompletionStatus(ClientCompletionPort,
                                          0,
                                          IoFileRep,
                                          (LPOVERLAPPED)pReq);
      ASSERT(status);
    }
}

//
// Closes the file handle and frees all thread data if an error occured
// in one of the following functions.
//
// The proc may be called while holding pReq->Lock.  It will release it.
//
VOID ClientShutdownRequest(Req *pReq) {
  DWORD status;
  RPC_STATUS rpcstatus;

#ifdef DEBUG2
  TCHAR Msg[MSG_SIZE];
  ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
  int nCharWritten; 
  DbgMsgRecord(TEXT("-> ClientShutdownRequest\n"));
#endif

  ASSERT(pReq != NULL);


  /*
    The failuire handling for async pipe call is as follows:

      Pull returned failure:
      RPC has done all the clenaup. No need to do anything.

      Non-RPC failure occurred:
      Call RpcAsyncCalncelCall
      Wait for call-complete notification
      Call RpcAsyncCompleteCall

      Received call-complete with failure:
      Call RpcAsyncCompleteCall         

  */

  // Complete or cancel the async call, depending on whether
  // an error has occurred and whether complete is needed.
  if (pReq->bCallMade && !pReq->bFailureFromPull) {

    // If we are processing a syncronous failure and the call has not yet
    // been cancelled, then cancel the call and return.  We will later get a
    // call-complete event and will complete the call then.
    if (!pReq->bCallComplete && pReq->Status && !pReq->bAsyncCallFailureReceived && !pReq->bCallCancelled) {

      RpcAsyncCancelCall(&(pReq->Async),
                         TRUE);
      // After cancelling the RPC we return and wait for a
      // call-complete notification.  We will continue to clean up afterwards.
      pReq->bCallCancelled = TRUE;

      if (CriticalSectionOwned(&(pReq->Lock))) {
        LeaveCriticalSection(&(pReq->Lock));
      }
      return;
    }

    // Complete a call if either an error has occurred and we already received
    // a completion notification, or if we had already cancelled it, or
    // if the request has been handled.
    else {

      // If there is an outstanding pull, we should wait for it to complete.
      // This can only happen if WriteFile has failed.
      ASSERT(!pReq->bPullOutstanding || pReq->bFailureFromWrite);
      if (pReq->bPullOutstanding) {
        if (CriticalSectionOwned(&(pReq->Lock))) {
          LeaveCriticalSection(&pReq->Lock);
        }
        return;
      }


      RpcTryExcept {
        RpcAsyncCompleteCall(&(pReq->Async),
                             &status);
      }
      RpcExcept ( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
                    (RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
                    (RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
                    (RpcExceptionCode() != STATUS_ILLEGAL_INSTRUCTION) &&
                    (RpcExceptionCode() != STATUS_BREAKPOINT) )
                  ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
        ASSERT(pReq->Status != RPC_S_OK);
      }
      RpcEndExcept;
    }
  }


  // If there are outstanding writes we should wait for them to complete
  // before destroying the object.              
  if (pReq->nWritesOutstanding > 0) {
    if (CriticalSectionOwned(&(pReq->Lock))) {
      LeaveCriticalSection(&pReq->Lock);
    }
    return;
  }

  // If there have been no failures, then all the bytes written
  // should have been written and there must not be any
  // bytes unaccounted for.
  ASSERT(pReq->Status != RPC_S_OK || pReq->nBytesOutstanding == 0);
     

  if (CriticalSectionOwned(&(pReq->Lock))) {
    LeaveCriticalSection(&pReq->Lock);
  }

#ifdef DEBUG1
  nClientReqs--;
#endif


  // We are actually going to wipe out a request after this point
  // and need to bring in a new one from the queue.
  FindAndActivateCReq();

  // Stop impersonating if we are.
  // The first thing we do in handling a request is impersonating the client.
  if (pReq->bImpersonating) {
    if (RevertToSelf() == 0) {
      AddToMessageLogProcFailure(TEXT("ClientShutdownRequest: RevertToSelf"), GetLastError());
    }
    pReq->bImpersonating = FALSE;
  }

  if(pReq->hFileRepServer != NULL) {
    rpcstatus = RpcBindingFree(&(pReq->hFileRepServer));
    ASSERT(rpcstatus == RPC_S_OK);
  }
  // Check that hLocalFile has been initialized and that initialization
  // was successful.
  if(pReq->hLocalFile != NULL && pReq->hLocalFile != INVALID_HANDLE_VALUE) {

    // The file has been extended, truncate its size:
    if (pReq->FileWritePos < pReq->CurrentExtendedSize) {
      SetFileSize(pReq->hLocalFile, pReq->FileWritePos);
    }

    status = CloseHandle(pReq->hLocalFile);
    ASSERT(status != 0);
  }

  // Check if any of the counters need to be decremented
  // since we are removing the request.
  if (pReq->State == StateQueued) {
    CounterDecrement(pClientReqCounters[pReq->Pri]);
#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ClientReqCounters[%d]\n"), pReq->Pri);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif            
  }
  if (pReq->State == StateActive) {
    CounterDecrement(pClientActiveReqCounters[pReq->Pri]);
    QueueHashDecrementCounter(ClientActiveReqHashCounters[pReq->Pri], pReq->pSID);
#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ClientActiveReqCounters[%d] and ClientActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif

#ifdef DEBUG1
    QueueRemoveData(ClientActiveReqQueue, pReq);
#endif
  }

  if (pReq->hTokenHandle != NULL) {
    status = CloseHandle(pReq->hTokenHandle);
    ASSERT(status != NULL);
  }

  if (pReq->ServerName != NULL) {
    AutoHeapFree(pReq->ServerName);
  }
  if (pReq->LocalFileName != NULL) {
    AutoHeapFree(pReq->LocalFileName);
  }
  if (pReq->RemoteFileName != NULL) {
    AutoHeapFree(pReq->RemoteFileName);
  }
        
  if (pReq->pSID != NULL) {
    AutoHeapFree(pReq->pSID);
  }

  // If the critical section has been allocated, free it.
  if (pReq->Lock.DebugInfo) {
    DeleteCriticalSection(&pReq->Lock);
  }

#ifdef PROF
  ProfRecordTime(pReq->nReqId, TEXT("handled"));
#endif

  AutoHeapFree(pReq);

#ifdef DEBUG2
  DbgMsgRecord(TEXT("<- ClientShutdownRequest\n"));
#endif
}

//
// Handles a request taken off req queue.
// Returns TRUE on sucess.
//
BOOL HandleReq(tReq *pReq) {

    RPC_STATUS rpcstatus;

    // Default connection to server system service is over TCP/IP.
    RPC_STR DefaultProtocolSequence = (RPC_STR)TEXT("ncacn_ip_tcp");
    
    // An empty endpoint string is used, since we are going to
    // connect to the endpoint dynamically generated by the
    // RPC run-time library.  Server calls RpcServerUseProtseq to
    // obtain a binding hadnle and a dynamic endpoint.
    RPC_STR DefaultEndpoint = (RPC_STR)TEXT("");

    RPC_STR pszUuid = NULL;
    RPC_STR pszOptions = NULL;
    RPC_STR pszStringBinding = NULL;

    RPC_STR pszServerPrincipalName = NULL;

    //
    // If a request is picked of the request queue.
    // Then we need to set up binding, bind to the server,
    // and do the initial read.
    //
                
    // We need to impersonate the user that has issued the
    // remote call.
    // We want to impersonate as soon as possible to minimise
    // the amount of resources that can be consumed by an attack.
    if (ImpersonateLoggedOnUser(pReq->hTokenHandle) == 0) {      
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: ImpersonateLoggedOnUser"), GetLastError());
        return FALSE;
    }
    pReq->bImpersonating = TRUE;
                
    // Prepare the binding information.
    if ((rpcstatus = RpcStringBindingCompose(pszUuid,
                                             DefaultProtocolSequence,
                                             pReq->ServerName,
                                             DefaultEndpoint,
                                             pszOptions,
                                             &pszStringBinding)) != RPC_S_OK) {
        pReq->Status = rpcstatus;
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailureEEInfo(TEXT("HandleReq: RpcStringBindingCompose"), rpcstatus);
        return FALSE;
    }

    if ((rpcstatus = RpcBindingFromStringBinding(pszStringBinding, &(pReq->hFileRepServer))) != RPC_S_OK) {
        // The binding handle is invalid
        pReq->hFileRepServer = NULL;
                    
        // Deallocate the string binding.
        rpcstatus = RpcStringFree(&pszStringBinding);
        ASSERT(rpcstatus == RPC_S_OK);
                    
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailureEEInfo(TEXT("HandleReq: RpcBindingFromStringBinding"), rpcstatus);
        return FALSE;
    }
                
    // We need to delete the string binding, since it is no longer
    // necessary.  All the binding information is now contained in the
    // binding handle.
    rpcstatus = RpcStringFree(&pszStringBinding);
    ASSERT(rpcstatus == RPC_S_OK);    
                
#ifndef NO_SEC
    DWORD status;

    // Quality of service structure to ensure authentication.
    RPC_SECURITY_QOS SecurityQOS;

    // The length of the principal name and the principal name
    // for the client system service.
    TCHAR szSpn[MAX_PATH];
    ULONG ulSpn = sizeof(szSpn);

    // Generate a principal name for the local system service.
    if ((status = DsMakeSpn(SERVICENAME,
                            (LPCTSTR)pReq->ServerName,
                            NULL,
                            0,
                            0,
                            &ulSpn,
                            szSpn)) != ERROR_SUCCESS) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: DsMakeSpn"), GetLastError());
        return FALSE;
    }
    
    pszServerPrincipalName = (RPC_STR)szSpn;

    // Specify quality of service parameters.
    SecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
    SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
    // Use static identity tracking since we will be issuing multiple
    // RPC calls from this thread with different identities: while
    // impersonating, and while not.  This insures that all the calls will
    // go to server under the identity of the user who created the binding
    // handle.
    SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
    // The client system service needs to impersonate the security context
    // of the client utility on the remote systems where it may replicate
    // files from.  It will also need to obtain client's SID and hence
    // requires impersonation.
    SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
                
    // Set authentification and authorisation information for
    // the binding handle.
    if ((rpcstatus = RpcBindingSetAuthInfoEx(pReq->hFileRepServer, pszServerPrincipalName, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_KERBEROS, NULL, RPC_C_AUTHZ_NAME, &SecurityQOS)) != RPC_S_OK) {

        pReq->Status = rpcstatus;
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailureEEInfo(TEXT("HandleReq: RpcBindingSetAuthInfoEx"), rpcstatus);
        return FALSE;
    }
#endif

    // Initialize the async handle.
    RpcAsyncInitializeHandle(&(pReq->Async),
                             sizeof(RPC_ASYNC_STATE));
    pReq->Async.Flags = RPC_C_NOTIFY_ON_SEND_COMPLETE;
    pReq->Async.UserInfo = pReq;
    pReq->Async.NotificationType = RpcNotificationTypeIoc;
    pReq->Async.u.IOC.hIOPort = ClientCompletionPort;
    pReq->Async.u.IOC.dwCompletionKey = IoPipe;
    pReq->Async.u.IOC.lpOverlapped = (LPOVERLAPPED) pReq;

    // Initialize the request fileds for throtteling the overlapped structure.
    pReq->FileOl.Offset = 0;
    pReq->FileOl.OffsetHigh = 0;
    pReq->FileOl.hEvent = NULL;

    pReq->bPullOutstanding = FALSE;
    pReq->bBuf = FALSE;
    pReq->bDataAvailable = FALSE;
    pReq->bAllPullsDone = FALSE;
    pReq->bCallComplete = FALSE;
    pReq->bAsyncCallFailureReceived = FALSE;
    pReq->bFailureFromPull = FALSE;
    pReq->bFailureFromWrite = FALSE;

    pReq->FileWritePos = 0;
    pReq->CurrentExtendedSize = 0;

    // Attempt to open the local file.
    if ((pReq->hLocalFile = CreateFile(pReq->LocalFileName,
                                       GENERIC_WRITE,
                                       0,
                                       NULL,
                                       CREATE_ALWAYS,
                                       FILE_FLAG_OVERLAPPED,
                                       NULL)) == INVALID_HANDLE_VALUE) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: CreateFile"), GetLastError());
        return FALSE;
    }

    SetFileSize(pReq->hLocalFile, FILE_SIZE_EXTENSION);

    // Link this file to the client requests's IO completion port:
    if ((ClientCompletionPort = CreateIoCompletionPort (pReq->hLocalFile,
                                                        ClientCompletionPort,
                                                        IoFile,
                                                        0)) == NULL) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleActiveReq: CreateIoCompletionPort"), GetLastError());
        return FALSE;
    }

    // We need to enter critical section since right after the call is made
    // another thread can receive an asyncronous failure and clean up.
    EnterCriticalSection(&pReq->Lock);

#ifdef RETRY_EXCEPTION
    // When making aggressive calls to the server, this flag is
    // set when the call succeeded.  This makes the client service attempt
    // to contact the server even after an exception occured.
    BOOL bRetryCall = TRUE;
    while (bRetryCall) {
#endif

        // Attempt to open the remote file.
        RpcTryExcept {
            c_RemoteReadAsyncPipe(&(pReq->Async),
                                  pReq->hFileRepServer,
                                  pReq->RemoteFileName,
                                  &(pReq->OutPipe));

#ifdef RETRY_EXCEPTION
            bRetryCall = FALSE;
#endif
        }
        RpcExcept ( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
                      (RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
                      (RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
                      (RpcExceptionCode() != STATUS_ILLEGAL_INSTRUCTION) &&
                      (RpcExceptionCode() != STATUS_BREAKPOINT) )
                    ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
                        
#ifdef RETRY_EXCEPTION
            // This code attempts to make aggressive calls, retrying
            // if no endpoints are available.  It is used to put servers
            // under stress.
            if (RpcExceptionCode() != EPT_S_NOT_REGISTERED &&
                RpcExceptionCode() != RPC_S_SERVER_UNAVAILABLE) {
#endif
                // Deallocate all the thread data before failing.
                pReq->Status = RpcExceptionCode();

                ClientShutdownRequest(pReq);
                AddToMessageLogProcFailureEEInfo(TEXT("HandleReq: c_RemoteOpen"), RpcExceptionCode());

                return FALSE;
#ifdef RETRY_EXCEPTION
            }
#endif                    
        }
        RpcEndExcept;
#ifdef RETRY_EXCEPTION
    }
#endif

    pReq->bCallMade = true;

    // We need to stop impersonating before putting this request back into
    // a queue or quitting.
    if (RevertToSelf() == 0) {
        pReq->bImpersonating = FALSE;
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: RevertToSelf"), GetLastError());
    }
    pReq->bImpersonating = FALSE;

    LeaveCriticalSection(&pReq->Lock);

    return TRUE;
}

//
// Handles a request taken off active req queue
//
VOID ServiceRequests(VOID) {
    DWORD Status;

    // The request that we will be processing
    tReq *pReq;

#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
#endif

    // Number of bytes read by a pull
    ULONG cbRead;

    // Values returned after a wait on the completion port
    DWORD dwNumberOfBytesTransferred;
    DWORD dwCompletionKey;
    OVERLAPPED *lpOverlapped;

    // A worker thread starts of by waiting for IO
    ActionType Action = Wait;

    while(TRUE) {

      //
      // Make a pull to try and receive data.
      //
      if (Action == Pull) {

        EnterCriticalSection(&pReq->Lock);

        // This will be set if we have processed an async failure notification.
        // Do not issue any more pulls and just wait for all file
        // IO to complete.
        if (pReq->bAsyncCallFailureReceived) {

          LeaveCriticalSection(&pReq->Lock);

          Action = Wait;
          continue;
        }

        // This will be the case if an async write has failed and we want to
        // terminate the request.
        // Wait for all the writes to be completed.
        if (pReq->bFailureFromWrite == TRUE) {

          LeaveCriticalSection(&pReq->Lock);

          Action = Wait;
          continue;
        }

        // We did not read anything yet.
        cbRead = 0;

        RpcTryExcept {
        
          WINAPI_MY_PIPE_PULL pPull = (WINAPI_MY_PIPE_PULL)pReq->OutPipe.pull;

          pReq->bPullOutstanding = TRUE;


          Status = pPull (pReq->OutPipe.state,
                          (char *) pReq->pbBuf,
                          PULL_BUFSIZE,
                          &cbRead);
        }
        RpcExcept ( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
                      (RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
                      (RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
                      (RpcExceptionCode() != STATUS_ILLEGAL_INSTRUCTION) &&
                      (RpcExceptionCode() != STATUS_BREAKPOINT))
                    ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {

          Status = RpcExceptionCode();
          ASSERT(Status != RPC_S_OK && Status != RPC_S_ASYNC_CALL_PENDING);
        }
        RpcEndExcept;

        // The pull has failed either through and exception or an
        // error return code.
        if (Status != RPC_S_OK && Status != RPC_S_ASYNC_CALL_PENDING) {

          AddToMessageLogProcFailureEEInfo(TEXT("HandleActiveReq: c_RemoteRead"), Status);
          
          pReq->bFailureFromPull = TRUE;
          pReq->bAllPullsDone = TRUE;
          pReq->bPullOutstanding = FALSE;
          pReq->bCallComplete = TRUE;

          // Dump this request.
          pReq->Status = Status;
          ClientShutdownRequest(pReq);
          
          Action = Wait;
        }

        // The pull has succeeded.
        // We read some data.
        else if (Status == RPC_S_OK && cbRead > 0) {

          pReq->FileOl.Offset = pReq->FileWritePos;

          pReq->bDataAvailable = FALSE;
          pReq->bPullOutstanding = FALSE;

          pReq->nWritesOutstanding++;
          pReq->nBytesOutstanding+=cbRead;

          // Access check was done on opening when we were impersonating.
          // Thus we do not need to impersonate in this code.
          Status = WriteFile(pReq->hLocalFile, pReq->pbBuf, cbRead, NULL, &pReq->FileOl);

          // Write has failed.
          if(!Status && GetLastError() != ERROR_IO_PENDING) {

              AddToMessageLogProcFailure(TEXT("HandleActiveReq: WriteFile"), GetLastError());

              pReq->nWritesOutstanding--;

              pReq->nBytesOutstanding-=cbRead;

              // If we hit a failure on write we will
              // stop the pulls, wait for all outstanding IO to
              // complete and then finish.
              pReq->bFailureFromWrite = TRUE;
              
              // Dump this request.
              pReq->Status = GetLastError();      
              ClientShutdownRequest(pReq);

              Action = Wait;
          }

          // Write has suceeded.
          else {

            pReq->FileWritePos+=cbRead;
            
            if (pReq->FileWritePos > pReq->CurrentExtendedSize) {
              pReq->CurrentExtendedSize+=FILE_SIZE_EXTENSION;
              SetFileSize(pReq->hLocalFile, pReq->CurrentExtendedSize);
            }
            
            LeaveCriticalSection(&pReq->Lock);

            Action = Pull;
          }
        }

        // The pull has suceeded.
        // We did not get any data.
        // If the pipe is empty and pull returned 0 bytes, the transfer is complete.           
        else if (Status == RPC_S_OK && cbRead == 0) {
          
          pReq->bDataAvailable = FALSE;
          pReq->bPullOutstanding = FALSE;

          pReq->bAllPullsDone = TRUE;

          // If we have done all the pulls and received call-complete, finish.
          if(pReq->bAllPullsDone && pReq->bCallComplete) {
            ClientShutdownRequest(pReq);
          }
          // Otherwise, wait.
          else {
            LeaveCriticalSection(&pReq->Lock);
          }

          // We will either complete this request ot wait for all the file
          // IO to complete.  In either case, the next action will be Wait.
          Action = Wait;
        }

        // No data is present yet.  A notification will be issued later.
        else if (Status == RPC_S_ASYNC_CALL_PENDING && cbRead == 0) {

          ASSERT(pReq->bDataAvailable == FALSE);

          LeaveCriticalSection(&pReq->Lock);

          Action = Wait;          
        }

        // Should not get here
        else {

          LeaveCriticalSection(&pReq->Lock);

          ASSERT(0);
        }
      }

      //
      // Wait for IO completion
      //
      else if (Action == Wait) {

        InterlockedIncrement(&nThreadsAtClientCompletionPort);

        ASSERT(nThreadsAtClientCompletionPort >= 0);
        
        Status = GetQueuedCompletionStatus(ClientCompletionPort,
                                           &dwNumberOfBytesTransferred,
                                           (PULONG_PTR)&dwCompletionKey,
                                           &lpOverlapped,
                                           ClientCompletionPortTimeout);

        // Failure to dequeue an IO completion packet.
        if (!Status && lpOverlapped == NULL) {

          ULONG nThreads = InterlockedDecrement(&nThreadsAtClientCompletionPort);

          // Wait timed out.
          if (GetLastError() == WAIT_TIMEOUT) {


            // If wait timed out and there are other threads waiting on the completion port,
            // we can exit.
            if (nThreads != 0) {
              return;
            }
            // If there are no other threads, go back to wait for the IO.
            else {
              continue;
            }
          }

          // Failure occurred and we were unable to dequeue an IO completion
          // packet.
          else {
            AddToMessageLogProcFailure(TEXT("HandleActiveReq: GetQueuedCompletionStatus"), GetLastError());
            
            // Nothing we can do, just wait a little and re-try.
            Sleep(100);
            Action = Wait;
            continue;
          }
        }

        // If this is the last thread to come off a completion port,
        // spin up an extra worker thread.  Thi ensures that there is
        // always a waiter on the port.
        if (InterlockedDecrement(&nThreadsAtClientCompletionPort) == 0) {

          HANDLE hThread;
          ULONG ThreadIdentifier;
          DWORD status;
          
          if ((hThread = CreateThread(NULL,
                                      0,
                                      (LPTHREAD_START_ROUTINE) ServiceRequests,
                                      NULL,
                                      0,
                                      &ThreadIdentifier)) == NULL) {
            
            // If CreateThread has failed there is nothing that we can do.
            // Just continue...
            AddToMessageLogProcFailure(TEXT("ServiceRequests: CreateThread"), GetLastError());
          }
          else {
            // Unless we close a handle to the thread, it will remain in the
            // system even after its execution has terminated.
            status = CloseHandle(hThread);
            ASSERT(status != 0);
          }
        }

        ASSERT(nThreadsAtClientCompletionPort >= 0);

        // Dequeued an IO completion packet for a successful operation.
        // or for an unsucessful operation.  If Status != 0 then the
        // operation has suceeded.

        // There is at most one async WriteFile outstanding,
        // so only one thread may be executing here for a given request.      
        if (dwCompletionKey == IoFile) {
          pReq = (Req *) ((size_t)lpOverlapped - offsetof(Req, FileOl));

#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("HandleActiveReq: received File IO for req %p\n"), pReq);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif
          
          EnterCriticalSection(&pReq->Lock);


          // File IO suceeded.
          if (Status) {
          
            pReq->nWritesOutstanding--;

            pReq->nBytesOutstanding-=dwNumberOfBytesTransferred;
          
            ASSERT(pReq->nWritesOutstanding >= 0);
          
            Action = Wait;
          
            if(pReq->bAllPullsDone &&
               pReq->nWritesOutstanding <= 0 &&
               (pReq->bCallComplete || pReq->bAsyncCallFailureReceived || pReq->bFailureFromWrite)) {
            
              ClientShutdownRequest(pReq);
            
              Action = Wait;
            }
            else {
              LeaveCriticalSection(&pReq->Lock);
            }
          }

          // File IO has failed.
          else {
            AddToMessageLogProcFailure(TEXT("HandleActiveReq: WriteFile"), GetLastError());

            pReq->nWritesOutstanding--;

            pReq->nBytesOutstanding-=dwNumberOfBytesTransferred;

            pReq->bFailureFromWrite = TRUE;

            // We do not want to pull any more data once file IO has failed.
            pReq->bAllPullsDone = TRUE;

            ASSERT(pReq->nWritesOutstanding >= 0);

            // If we hit a failure on write we will
            // wait for the async IO to complete.
              
            // Dump this request.
            pReq->Status = GetLastError();
            if (pReq->Status == 0) {
                pReq->Status = ERROR_OPERATION_ABORTED;
            }

            ClientShutdownRequest(pReq);

            Action = Wait;
          }
        }
        
        // There is at most one async pull outstanding,
        // so at most one thread may be executing here for a given request.
        // The only possibility that we need to syncronize against is a pull and a write
        // completing simultaneously and racing.
        //
        // The IO completion packets posted by the RPC runtime correspond to
        // sucessful IO only.  Failures are reported as call-complete events.
        else if(dwCompletionKey == IoPipe) {
          pReq = (Req *) lpOverlapped;

#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("HandleActiveReq: received Pipe IO for req %p\n"), pReq);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif

      EnterCriticalSection(&pReq->Lock);  
          

          //
          // Received a call-complete notification from the RPC runtime.
          //
          if(pReq->Async.Event == RpcCallComplete) {
            
            // It is possible that the server terminated the call prematurely.
            // If this is the case find an error.
            pReq->Status = RpcAsyncGetCallStatus(&pReq->Async);
            
            ASSERT(pReq->bCallComplete == FALSE ||
                   pReq->Status == RPC_S_SERVER_UNAVAILABLE ||
                   pReq->Status == RPC_S_CALL_FAILED_DNE ||
                   pReq->Status == RPC_S_CALL_FAILED ||
                   pReq->Status == RPC_S_CALL_CANCELLED);

            // The call did in fact fail.            
            if (pReq->Status != RPC_S_OK) {
              
              // If the call has failed, forget about doing
              // more pulls.
              pReq->bAllPullsDone = TRUE;
              pReq->bPullOutstanding = FALSE;
              pReq->bAsyncCallFailureReceived = TRUE;
              
              // Try to dump the request.
              ClientShutdownRequest(pReq);
              
              Action = Wait;
            }

            // The call did not fail.  We received notification of it's
            // sucessful completion.
            else {
              
              ASSERT(pReq->bAsyncCallFailureReceived == FALSE);
              
              // Call complete should be the last notification
              // received.  We will note this, and wait for the file
              // IO to completion if necessary.
              pReq->bCallComplete = TRUE;
              
              Action = Wait;
              
              if(pReq->bAllPullsDone) {
                ClientShutdownRequest(pReq);
              }
              else {
                LeaveCriticalSection(&pReq->Lock);
              }
            }
          }

          //
          // Got a receive-complete notification from the RPC runtime.
          //
          else if(pReq->Async.Event == RpcReceiveComplete){
            
            Action = Pull;
            
            ASSERT(pReq->bCallComplete == FALSE);
            ASSERT(pReq->bPullOutstanding == TRUE);

            // If a pull has completed then there are no more
            // outstanding pulls.
            pReq->bPullOutstanding = FALSE;
            
            // If an async WriteFile has failed, we would have cancelled the
            // call.  After getting a call-complete notification we
            // will clean it up.
            if (pReq->bCallCancelled) {
              Action = Wait;
              LeaveCriticalSection(&pReq->Lock);
              continue;
            }

            // If the call is still a sucess, then there must be
            // data for us.
            pReq->bDataAvailable = TRUE;
            
            LeaveCriticalSection(&pReq->Lock);
          }

          // We should never be here.
          else {
            LeaveCriticalSection(&pReq->Lock);

            ASSERT(0);
          }
        }

        //
        // We got a FileRep internal packet to activate a new request.
        //
        else if(dwCompletionKey == IoFileRep) {
          pReq = (Req *) lpOverlapped;

          
          if(HandleReq(pReq)) {
            Action = Pull;
          }
          else {
            Action = Wait;
          }
        }

        else { // dwCompletionKey
          ASSERT(0);
        }
      }

      else { // Action
        ASSERT(0);
      }
    }
}

/*
    FUNCTIONS: IsSecureClient

    PURPOSE:
        Returns TRUE if the service is being contacted by a proper
      authenticated client.

    PARAMETERS:
        Self-explanatory.

    RETURN VALUE: TRUE if YES, FALSE otherwise or on error.
 
*/
BOOL IsSecureClient(handle_t hFileRepClient, Req *pReq) {

#ifndef NO_SEC
    RPC_AUTHZ_HANDLE hAuth;
    ULONG ulAuthnLevel;

    // Get client security info.
    if (RpcBindingInqAuthClient(pReq->hFileRepClient,
                                &hAuth,
                                NULL,
                                &ulAuthnLevel,
                                NULL,
                                NULL) != RPC_S_OK) {
        AddRpcEEInfo(GetLastError(), TEXT("RequestFile: RpcBindingInqAuthClient failed"));
        return FALSE;
    }
    
    // Make sure the client has adequate security measures.
    // Since this is LRPC, we get the security level of privacy
    // because it is the only available security level.
    // We do not interoperate with old versions of FileRep 
    // client utilities, since appropriate versions should be
    // available with the local system service.
    if (ulAuthnLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY) {
        AddRpcEEInfo(RPC_S_SEC_PKG_ERROR, TEXT("RequestFile: ulAuthnLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY"));
        return FALSE;
    }
#endif

    return TRUE;
}

/*
    FUNCTIONS: RequestFile

    PURPOSE:
        Called by FileRep to have the Client System
      Service request file replication from a Server System Service.
      The function processes rpc request and places it in a request
      queue.  It then checks the request and active request queues for
      requests to process, or exits.

    PARAMETERS:
        Self-explanatory.

    RETURN VALUE: none
 
*/
VOID RequestFile(handle_t hFileRepClient,
                 LPTSTR ServerName,
                 LPTSTR RemoteFileName,
                 LPTSTR LocalFileName) {

    RPC_STATUS rpcstatus;

    Req *pReq = NULL;

    // The string binding that is used to make sure clients contact the
    // service via an LRPC call.
    RPC_STR StringBinding;
    
    // The protocol sequence that gets extracted from the string binding.
    RPC_STR ProtSeq;

    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
  
#ifdef DEBUG2
    DbgMsgRecord(TEXT("-> RequestFile\n"));
#endif

    if((pReq = (Req *) AutoHeapAlloc(sizeof(Req))) == NULL) {
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));            
        return;
    }

    // Set pReq' fields to NULL so that we will know
    // in ClientShutdownRequest which ones have been initialized.
    pReq->hFileRepServer = NULL;

    pReq->hFileRepClient = hFileRepClient;

    pReq->hTokenHandle = NULL;

    pReq->hLocalFile = NULL;
    pReq->bImpersonating = FALSE;
    pReq->Pri = 0;
    pReq->State = StateArrived;

    pReq->ServerName = NULL;
    pReq->LocalFileName = NULL;
    pReq->RemoteFileName = NULL;

    pReq->pSID = NULL;

    pReq->Status = 0;

    pReq->bCallMade = FALSE;
    pReq->bCallCancelled = FALSE;

    pReq->Lock.DebugInfo = NULL;


    pReq->nWritesOutstanding = 0;
    pReq->nBytesOutstanding = 0;

#ifdef DEBUG1
    nClientReqs++;

#endif

    // Copy strings out since they may need to be allocated longer then
    // the arguments that live on the stack.
    // It is conceivable that this thread will put the
    // request on the queue and some other thread will
    // handle the request after this thread has exited.
    if ((pReq->ServerName = (RPC_STR) AutoHeapAlloc((_tcslen(ServerName)+1) * sizeof(TCHAR))) == NULL) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));            
        return;
    }
    CopyMemory(pReq->ServerName, ServerName, (_tcslen(ServerName)+1) * sizeof(TCHAR));
    if ((pReq->RemoteFileName = (LPTSTR) AutoHeapAlloc((_tcslen(RemoteFileName)+1) * sizeof(TCHAR))) == NULL) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));            
        return;
    }
    CopyMemory(pReq->RemoteFileName, RemoteFileName, (_tcslen(RemoteFileName)+1) * sizeof(TCHAR));
    if ((pReq->LocalFileName = (LPTSTR) AutoHeapAlloc((_tcslen(LocalFileName)+1) * sizeof(TCHAR))) == NULL) {
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));
        return;
    }
    CopyMemory(pReq->LocalFileName, LocalFileName, (_tcslen(LocalFileName)+1) * sizeof(TCHAR));

#ifdef PROF
    static ULONG nTotalId;

    EnterCriticalSection(&ProfCriticalSection);

    // Record the number of the current request.
    pReq->nReqId = nTotalId;
    // Increment the number of the request.
    nTotalId++;

    LeaveCriticalSection(&ProfCriticalSection);
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("name=\"%s\" arrived"), RemoteFileName);	
    bufSize -= nCharWritten;
	
    // Record the time of arrival of this request.
    ProfRecordTime(pReq->nReqId, Msg);
#endif

    // Make sure that the client utility is local.
    // Client system service only services LRPC calls.
    if ((rpcstatus = RpcBindingToStringBinding(pReq->hFileRepClient, &StringBinding)) != RPC_S_OK) {
        pReq->Status = rpcstatus;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcBindingToStringBinding failed"));
        return;
    }
    if ((rpcstatus = RpcStringBindingParse(StringBinding, NULL, &ProtSeq, NULL, NULL, NULL)) != RPC_S_OK) {
        pReq->Status = rpcstatus;

        // Free the string binding.
        rpcstatus = RpcStringFree(&StringBinding);
        ASSERT(rpcstatus == RPC_S_OK);

        rpcstatus = pReq->Status;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcStringBindingParse failed"));
        return;
    }
    if (_tcscmp((LPCTSTR)ProtSeq, TEXT("ncalrpc")) != 0) {

        // Free the string binding.
        rpcstatus = RpcStringFree(&StringBinding);
        ASSERT(rpcstatus == RPC_S_OK);
        // And the protocol sequence
        rpcstatus = RpcStringFree(&ProtSeq);
        ASSERT(rpcstatus == RPC_S_OK);

        pReq->Status = ERROR_ACCESS_DENIED;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(RPC_S_INVALID_RPC_PROTSEQ, TEXT("RequestFile: _tcscmp(ProtSeq, TEXT(\"ncalrpc\")) != 0"));
        return;
    }

    // Free the string binding.
    rpcstatus = RpcStringFree(&StringBinding);
    ASSERT(rpcstatus == RPC_S_OK);
    // And the protocol sequence.
    rpcstatus = RpcStringFree(&ProtSeq);
    ASSERT(rpcstatus == RPC_S_OK);

#ifndef NO_SEC
    if (!IsSecureClient(hFileRepClient, pReq)) {
        pReq->Status = ERROR_ACCESS_DENIED;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(RPC_S_SEC_PKG_ERROR, TEXT("RequestFile: IsSecureClient failed"));
        return;
    }
#endif

    //
    // Check that we are not exceeding the bound on the number
    // of concurrent requests for this user's priority group.
    // And update the number of such requests.
    //

    // Impersonate the caller so that we can get the caller's SID
    // Sometimes RpcImpersonateClient may fail under heavy load.
#ifdef RETRY_EXCEPTION
    rpcstatus = RPC_S_OK;
    unsigned tries = 0;
    do {
      if (rpcstatus != RPC_S_OK) {
        tries++;
        Sleep(100);
      }
      rpcstatus = RpcImpersonateClient(pReq->hFileRepClient);
    }
    while (rpcstatus != RPC_S_OK && tries < 10);
#else
    rpcstatus = RpcImpersonateClient(pReq->hFileRepClient);
#endif

    if (rpcstatus != RPC_S_OK) {      
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcImpersonateClient failed"));
        return;
    }
    pReq->bImpersonating = TRUE;

    // We need to write down the access token for the user so that
    // we can use it for impersonation later when the client binding handle
    // gets deallocated.
    if (OpenThreadToken(GetCurrentThread(),
                        TOKEN_QUERY | TOKEN_IMPERSONATE,
                        TRUE,
                        &pReq->hTokenHandle) == 0) {

        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: OpenThreadToken failed"));
        return;
    }

    // Determine current user's priority level.  This corresponds to the
    // priority level of the callee, since we are impersonating.
    pReq->Pri = GetCurrentUserPriority();

    // Write down the SID for the user.
    pReq->pSID = GetUserSID();
    if (pReq->pSID == NULL) {        
        pReq->Status = RPC_S_CALL_FAILED;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(RPC_S_CALL_FAILED, TEXT("RequestFile: GetUserSID failed"));
        return;
    }

    // Stop impersonating.  We got what we wanted.
    if ((rpcstatus = RpcRevertToSelf()) != RPC_S_OK) {
        pReq->Status = GetLastError();
        pReq->bImpersonating = FALSE;
        ClientShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcRevertToSelf failed"));
        return;
    }
    pReq->bImpersonating = FALSE;

    // Init the critsec.
    if (InitializeCriticalSectionAndSpinCount(&pReq->Lock, 10) == 0) {
        AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("ProfOpenLog: InitializeCriticalSectionAndSpinCount"));
        return;
    }

#ifdef DEBUG2
   nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Pri=%d for req %p\n"), pReq->Pri, pReq);
   bufSize -= nCharWritten;
   DbgMsgRecord(Msg);
#endif

    HANDLE hThread;
    ULONG ThreadIdentifier;
    DWORD status;

    // If the number of worker threads is lower then the maximum, create one.
    if (nThreadsAtClientCompletionPort < 1) {

      // Go service some requests, remember that you are
      // an RPC thread.
      if ((hThread = CreateThread(NULL,
                                  0,
                                  (LPTHREAD_START_ROUTINE) ServiceRequests,
                                  NULL,
                                  0,
                                  &ThreadIdentifier)) == NULL) {
        
        pReq->Status = GetLastError();
        ClientShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("ServiceRequests: CreateThread"), GetLastError());
        return;
      }
      
      // Unless we close a handle to the thread, it will remain in the
      // system even after its execution has terminated.
      status = CloseHandle(hThread);
      ASSERT(status != 0);  
    }

    // We need to wait putting the request onto the queue untill we know that we could create
    // a worker thread.

    // Increment the counter for the number of concurrent requests.
    if (!CounterIncrement(pClientReqCounters[pReq->Pri])) {

        pReq->Status = RPC_S_SERVER_TOO_BUSY;
        ClientShutdownRequest(pReq);
        // We have to raise exception after deallocating the data
        // since there is a possibility that a client thread
        // will retry and will attempt to open this local file
        // while it's previous request is still holding a lock.
        // This has been actually hit on an overloaded system.
        AddRpcEEInfoAndRaiseException(RPC_S_SERVER_TOO_BUSY, TEXT("RequestFile: CounterIncrement failed"));
#ifdef DEBUG2
        DbgMsgRecord(TEXT("<- RequestFile\n"));
#endif  
        return;
    }
#ifdef DEBUG2
   nCharWritten =  _stprintf_s(Msg, bufSize, TEXT("Incremented ClientReqCounters[%d]\n"), pReq->Pri);
   bufSize -= nCharWritten;
   DbgMsgRecord(Msg);
#endif            
    
    pReq->State = StateQueued;

    // Place the request onto the queue.
    // The request will be picked off the queue by a worker thread from the
    // completion port and activate later.
    QueueAdd(ClientReqQueues[pReq->Pri], pReq, TRUE);
#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ClientReqQueues[pReq->Pri]);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif    

    // Now if we can, find a queued request and activate it.
    FindAndActivateCReq();

#ifdef DEBUG2
    DbgMsgRecord(TEXT("<- RequestFile\n"));
#endif
    return;
}

// end FileRepClientProc.cpp
