2025-11-28 00:35:46 +09:00

1630 lines
51 KiB
C++

// 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