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

1046 lines
30 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
Server System Service
FILE: FileRepServerProc.cpp
PURPOSE: Remote procedures for server system service
FUNCTIONS:
COMMENTS:
*/
#include "common.h"
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <tchar.h>
#include <string.h>
#include <time.h>
// header file generated by MIDL compiler
#include "FileRepServer.h"
// Declarations for service routines.
#include "Service.h"
#ifdef DEBUG2
#include "DbgMsg.h"
#endif
extern HANDLE ServerCompletionPort;
extern LONG nThreadsAtServerCompletionPort;
#define ServerCompletionPortTimeout (20*1000)
typedef enum tSReqState {
StateArrived,
StateQueued,
StateActive
} SReqState;
typedef enum {
IoFileRep,
IoPipe,
IoFile
} IoCompletionType;
typedef enum {
Activate,
Read,
Push,
Wait,
} ActionType;
#ifdef DEBUG1
unsigned nServerReqs = 0;
#endif
//
// Packages up the variables to be passed
// to the processing thread.
//
typedef struct tSReq{
handle_t hFileRepClient;
HANDLE hTokenHandle;
HANDLE hLocalFile;
LPTSTR LocalFileName;
// Set when we are impersonating the client.
BOOL bImpersonating;
// Priority of this request.
UINT Pri;
// User SID.
PSID pSID;
SReqState 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 pbReadBuf[PULL_BUFSIZE];
LONG FileReadPos;
ULONG PushSize;
OVERLAPPED FileOl;
ActionType Action;
CRITICAL_SECTION Lock;
BOOL bPushOutstanding;
BOOL bReadOutstanding;
BOOL bBuf;
ULONG cbRead;
// Set when all the non-null pushes have been dispatched.
// This happens after reading the last segment of the file
// and calling a push for it. What remains to be done
// is waiting for notification for the last data push and issuing a
// NULL push.
BOOL bLastDataPushIssued;
// Set when all of the file reads have completed.
BOOL bReadsDone;
// Set when issuing the last push of 0 length.
BOOL bNullPush;
// Set when a pull fails and RPC runtime cleans up the call.
// There is no need to call Cancel after that.
BOOL fCallInvalidated;
// We use this wariable to set an error code so that
// ServerShutdownRequest can know whether an error has occurred.
// If no error occurred, the async call will be completed.
// Otherwise it will be cancelled.
DWORD Status;
} SReq;
//
// Handles the requests placed on req queue and active req queue
// and returns when no requests are available.
//
SReq* FindSReq(void) {
SReq *pReq = NULL;
#ifdef DEBUG2
TCHAR Msg[MSG_SIZE];
ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
int nCharWritten;
#endif
//
// 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 = (SReq *) QueueRemove(ServerReqQueues[pri-1]);
if (pReq != NULL) {
break;
}
}
if (pReq != NULL) {
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Took req %p off Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
if (CounterIncrement(pServerActiveReqCounters[pReq->Pri])) {
if (QueueHashIncrementCounter(ServerActiveReqHashCounters[pReq->Pri], pReq->pSID)) {
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Incremented ServerActiveReqCounters[%d] and ServerActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
// The request now resides on a new queue.
CounterDecrement(pServerReqCounters[pReq->Pri]);
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerReqCounters[%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(pServerActiveReqCounters[pReq->Pri]);
QueueAdd(ServerReqQueues[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, ServerReqQueues[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(ServerReqQueues[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, ServerReqQueues[pReq->Pri]);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
}
}
return NULL;
}
VOID FindAndActivateSReq(VOID) {
DWORD status;
tSReq *pReq = FindSReq();
if (pReq) {
#ifdef DEBUG1
QueueAdd(ServerActiveReqQueue, 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(ServerCompletionPort,
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.
//
VOID ServerShutdownRequest(SReq *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("-> ServerShutdownRequest\n"));
#endif
ASSERT(pReq != NULL);
#ifdef DEBUG1
nServerReqs--;
#endif
// We are actually deleting a request, pick a new one from the queue.
FindAndActivateSReq();
// Stop impersonating if we are.
// The first thing we do in handling a request is impersonating the client.
if (pReq->bImpersonating) {
rpcstatus = RpcRevertToSelf();
if (rpcstatus != RPC_S_OK) {
AddToMessageLogProcFailureEEInfo(TEXT("ServerShutdownRequest: RpcRevertToSelf"), rpcstatus);
}
}
// Complete or cancel the async call, depending on whether an error
// has occurred.
if (pReq->Status && !pReq->fCallInvalidated) {
RpcAsyncAbortCall(pReq->Async,
TRUE);
}
else {
RpcAsyncCompleteCall(pReq->Async,
&status);
}
// Check that hLocalFile has been initialized and that initialization
// was successful.
if (pReq->hLocalFile != NULL && pReq->hLocalFile != INVALID_HANDLE_VALUE) {
status = CloseHandle(pReq->hLocalFile);
ASSERT(status != 0);
}
if (pReq->LocalFileName != NULL) {
AutoHeapFree(pReq->LocalFileName);
}
// Check if any of the counters need to be decremented
// since we are removing the request.
if (pReq->State == StateQueued) {
CounterDecrement(pServerReqCounters[pReq->Pri]);
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerReqCounters[%d]\n"), pReq->Pri);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
}
if (pReq->State == StateActive) {
CounterDecrement(pServerActiveReqCounters[pReq->Pri]);
QueueHashDecrementCounter(ServerActiveReqHashCounters[pReq->Pri], pReq->pSID);
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerActiveReqCounters[%d] and ServerActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
#ifdef DEBUG1
QueueRemoveData(ServerActiveReqQueue, pReq);
#endif
}
if (pReq->hTokenHandle != NULL) {
status = CloseHandle(pReq->hTokenHandle);
ASSERT(status != NULL);
}
if (pReq->pSID != NULL) {
AutoHeapFree(pReq->pSID);
}
if (pReq->Lock.DebugInfo) {
DeleteCriticalSection(&pReq->Lock);
}
AutoHeapFree(pReq);
#ifdef DEBUG2
DbgMsgRecord(TEXT("<- ServerShutdownRequest\n"));
#endif
}
//
// Handles a request taken off req queue.
// Returns TRUE on sucess.
//
BOOL HandleSReq(tSReq *pReq) {
#ifndef NO_SEC
// 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();
ServerShutdownRequest(pReq);
AddToMessageLogProcFailure(TEXT("HandleReq: ImpersonateLoggedOnUser"), GetLastError());
return FALSE;
}
pReq->bImpersonating = TRUE;
#endif
// Attempt to open the local file.
if ((pReq->hLocalFile = CreateFile(pReq->LocalFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL)) == INVALID_HANDLE_VALUE) {
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddToMessageLogProcFailure(TEXT("HandleReq: CreateFile"), GetLastError());
return FALSE;
}
#ifndef NO_SEC
// We need to stop impersonating before putting this request back into
// a queue or quitting.
if (RevertToSelf() == 0) {
AddToMessageLogProcFailure(TEXT("HandleReq: RevertToSelf"), GetLastError());
}
pReq->bImpersonating = FALSE;
#endif
// Initialize the request fileds for throtteling the IO
pReq->FileOl.Offset = 0;
pReq->FileOl.OffsetHigh = 0;
pReq->FileOl.hEvent = NULL;
pReq->Async->u.IOC.dwCompletionKey = IoPipe;
pReq->Async->u.IOC.lpOverlapped = (LPOVERLAPPED) pReq;
pReq->bLastDataPushIssued = FALSE;
pReq->bReadsDone = FALSE;
pReq->bNullPush = FALSE;
pReq->Async->Flags = RPC_C_NOTIFY_ON_SEND_COMPLETE;
pReq->Async->UserInfo = pReq;
pReq->Async->NotificationType = RpcNotificationTypeIoc;
pReq->Async->u.IOC.hIOPort = ServerCompletionPort;
// Link this file to the client requests's IO completion port:
if ((ServerCompletionPort = CreateIoCompletionPort (pReq->hLocalFile,
ServerCompletionPort,
IoFile,
0)) == NULL) {
AddToMessageLogProcFailure(TEXT("HandleActiveReq: CreateIoCompletionPort"), GetLastError());
return FALSE;
}
pReq->bReadOutstanding = FALSE;
pReq->bPushOutstanding = FALSE;
pReq->bBuf = FALSE;
// Init the critsec.
if (InitializeCriticalSectionAndSpinCount(&pReq->Lock, 10) == 0) {
AddToMessageLogProcFailure(TEXT("HandleActiveReq: InitializeCriticalSectionAndSpinCount"), GetLastError());
return FALSE;
}
return TRUE;
}
//
// Handles a request taken off active req queue
//
VOID SServiceRequests(VOID) {
tSReq *pReq;
#ifdef DEBUG2
TCHAR Msg[MSG_SIZE];
ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
int nCharWritten;
#endif
DWORD dwNumberOfBytesTransferred;
DWORD dwCompletionKey;
OVERLAPPED *lpOverlapped;
ActionType Action = Wait;
while(TRUE) {
//
// Read data from file.
//
if (Action == Read) {
EnterCriticalSection(&pReq->Lock);
ASSERT(!pReq->bBuf);
ASSERT(!pReq->bReadOutstanding);
// We did not read anything yet.
pReq->cbRead = 0;
pReq->bReadOutstanding = TRUE;
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: reading file for req %p\n"), GetCurrentThreadId(), pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
if(!ReadFile(pReq->hLocalFile, pReq->pbReadBuf, PUSH_BUFSIZE, &pReq->cbRead, &pReq->FileOl)) {
if (GetLastError() != ERROR_IO_PENDING) {
AddToMessageLogProcFailure(TEXT("HandleActiveSReq: WriteFile"), GetLastError());
pReq->Status = GetLastError();
LeaveCriticalSection(&pReq->Lock);
ServerShutdownRequest(pReq);
Action = Wait;
}
}
Action = Wait;
LeaveCriticalSection(&pReq->Lock);
}
//
// Push the data to the client.
//
else if (Action == Push) {
EnterCriticalSection(&pReq->Lock);
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: pushing a buffer for req %p\n"), GetCurrentThreadId(), pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
// Doing a regular Push for the data read from the file.
if (!pReq->bNullPush) {
ASSERT(!pReq->bReadOutstanding);
ASSERT(!pReq->bPushOutstanding);
ASSERT(pReq->bBuf);
pReq->bPushOutstanding = TRUE;
pReq->Status = ((WINAPI_MY_PIPE_PUSH) pReq->OutPipe->push)(pReq->OutPipe->state, (char *)pReq->pbReadBuf, pReq->cbRead);
pReq->bBuf = FALSE;
if(pReq->Status != RPC_S_OK) {
pReq->bReadsDone = TRUE;
pReq->bPushOutstanding = FALSE;
pReq->fCallInvalidated = TRUE;
if (pReq->bReadOutstanding == FALSE) {
LeaveCriticalSection(&pReq->Lock);
ServerShutdownRequest(pReq);
Action = Wait;
}
else
LeaveCriticalSection(&pReq->Lock);
}
else {
if (!pReq->bReadsDone)
Action = Read;
else {
Action = Wait;
pReq->bLastDataPushIssued = TRUE;
}
LeaveCriticalSection(&pReq->Lock);
}
}
// Doing a "NULL" push to signal the end of the pipe out-stream.
else {
ASSERT(!pReq->bReadOutstanding);
ASSERT(!pReq->bPushOutstanding);
ASSERT(!pReq->bBuf);
pReq->bPushOutstanding = TRUE;
WINAPI_MY_PIPE_PUSH pPush = (WINAPI_MY_PIPE_PUSH) pReq->OutPipe->push;
pReq->Status = pPush(pReq->OutPipe->state, NULL, 0);
if(pReq->Status != RPC_S_OK) {
pReq->fCallInvalidated = TRUE;
LeaveCriticalSection(&pReq->Lock);
ServerShutdownRequest(pReq);
Action = Wait;
}
else {
Action = Wait;
LeaveCriticalSection(&pReq->Lock);
}
}
}
//
// Wait on the completion port.
//
else if (Action == Wait) {
InterlockedIncrement(&nThreadsAtServerCompletionPort);
ASSERT(nThreadsAtServerCompletionPort >= 0);
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: waiting on a completion port\n"), GetCurrentThreadId());
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
if (!GetQueuedCompletionStatus(ServerCompletionPort,
&dwNumberOfBytesTransferred,
(PULONG_PTR)&dwCompletionKey,
&lpOverlapped,
ServerCompletionPortTimeout)) {
LONG nThreads = InterlockedDecrement(&nThreadsAtServerCompletionPort);
if (GetLastError() == WAIT_TIMEOUT) {
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: timed out waiting on a completion port\n"), GetCurrentThreadId());
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
if (nThreads != 0) {
return;
}
else {
continue;
}
}
else {
AddToMessageLogProcFailure(TEXT("SServiceRequests: GetQueuedCompletionStatus"), GetLastError());
Action = Wait;
}
}
else {
// If this is the last thread to come off a completion port,
// spin up an extra worker thread.
if (InterlockedDecrement(&nThreadsAtServerCompletionPort) == 0) {
HANDLE hThread;
ULONG ThreadIdentifier;
DWORD status;
if ((hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) SServiceRequests,
NULL,
0,
&ThreadIdentifier)) == NULL) {
// If creating a thread fails there is really nothing that we
// we can do. Just continue.
AddToMessageLogProcFailure(TEXT("SServiceRequests: 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(nThreadsAtServerCompletionPort >= 0);
//
// Received file IO completion.
//
if (dwCompletionKey == IoFile) {
pReq = (SReq *) ((size_t)lpOverlapped - offsetof(SReq, FileOl));
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed File IO for req %p\n"), GetCurrentThreadId(), pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
EnterCriticalSection(&pReq->Lock);
pReq->cbRead = dwNumberOfBytesTransferred;
ASSERT(pReq->bReadOutstanding);
ASSERT(!pReq->bBuf);
pReq->bReadOutstanding = FALSE;
pReq->bBuf = TRUE;
ASSERT(!pReq->bLastDataPushIssued);
if(pReq->Status != RPC_S_OK) {
LeaveCriticalSection(&pReq->Lock);
ServerShutdownRequest(pReq);
Action = Wait;
}
else {
// If we read less then we asked, then EOF has been reached.
if (pReq->cbRead != PUSH_BUFSIZE) {
pReq->bReadsDone = TRUE;
// If we read 0 bytes, then the previous data push has been
// the last one. So the last data push has already been issued.
if (pReq->cbRead == 0) {
pReq->bLastDataPushIssued = TRUE;
// If there are no outstanding pushes, we can do a NULL push.
if (pReq->bPushOutstanding == FALSE) {
pReq->bNullPush = TRUE;
}
}
}
pReq->FileOl.Offset += pReq->cbRead;
// We have at most one outstanding push, since RPC runtime
// guarantees proper notification if there are no more then
// one push outstanding.
if (pReq->bPushOutstanding == TRUE)
Action = Wait;
else
Action = Push;
LeaveCriticalSection(&pReq->Lock);
}
}
//
// Received pipe IO completion.
//
else if(dwCompletionKey == IoPipe) {
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed Pipe IO for req %p\n"), GetCurrentThreadId(), pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
pReq = (SReq *) lpOverlapped;
EnterCriticalSection(&pReq->Lock);
if (pReq->Async->Event != RpcSendComplete) {
ASSERT(0);
}
ASSERT(pReq->bPushOutstanding);
pReq->bPushOutstanding = FALSE;
// The completion of the NULL push terminates the request.
// The reuest is now handled.
if (pReq->bNullPush) {
LeaveCriticalSection(&pReq->Lock);
ServerShutdownRequest(pReq);
Action = Wait;
}
// This is a regular data push.
else {
// If there is a read outstanding, wait for it to complete
// since we do not have any other data to push at present.
if (pReq->bReadOutstanding)
Action = Wait;
// If there is a buffer to send, push it.
else if (pReq->bBuf)
Action = Push;
// If the this is the completion of the last data push, then
// we just need to do the NULL push.
else if (pReq->bLastDataPushIssued) {
Action = Push;
pReq->bNullPush = TRUE;
}
else {
/*ASSERT(0);*/Action = Wait;
}
LeaveCriticalSection(&pReq->Lock);
}
}
//
// Received an internal FileRep IO completion.
//
else if(dwCompletionKey == IoFileRep) {
pReq = (SReq *) lpOverlapped;
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed FileRep internal IO for req %p\n"), GetCurrentThreadId(), pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
// This is a signal to handle a new request.
// If the request can be activated, then handle it.
if(HandleSReq(pReq)) {
Action = Read;
}
// Otherwise, do event.
else {
Action = Wait;
}
}
else {
ASSERT(0);
}
}
}
else {
ASSERT(0);
}
}
}
void s_RemoteReadAsyncPipe(PRPC_ASYNC_STATE pAsync,
handle_t hFileRepClient,
TCHAR FileName[128],
ASYNC_CHAR_PIPE_TYPE *OutPipe) {
int nReply = 1;
unsigned long ecount = 0;
SReq *pReq = NULL;
#ifdef DEBUG2
TCHAR Msg[MSG_SIZE];
ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
int nCharWritten;
DbgMsgRecord(TEXT("-> s_RemoteReadAsyncPipe\n"));
#endif
if((pReq = (SReq *) AutoHeapAlloc(sizeof(SReq))) == NULL) {
AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));
return;
}
// Set pReq' fields to NULL so that we will know
// in ServerShutdownRequest which ones have been initialized.
pReq->hFileRepClient = hFileRepClient;
pReq->hTokenHandle = NULL;
pReq->hLocalFile = NULL;
pReq->bImpersonating = FALSE;
pReq->Pri = 0;
pReq->State = StateArrived;
pReq->pSID = NULL;
pReq->Status = 0;
pReq->Lock.DebugInfo = NULL;
// The out-pipe we use to pull data from the server.
pReq->OutPipe = OutPipe;
// The async handle for the call to RemoteOpen.
pReq->Async = pAsync;
pReq->fCallInvalidated = FALSE;
#ifdef DEBUG1
nServerReqs++;
#endif
if ((pReq->LocalFileName = (LPTSTR) AutoHeapAlloc((_tcslen(FileName)+1) * sizeof(TCHAR))) == NULL) {
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));
return;
}
CopyMemory(pReq->LocalFileName, FileName, (_tcslen(FileName)+1) * sizeof(TCHAR));
//
// 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.
//
#ifndef NO_SEC
RPC_STATUS rpcstatus;
// Impersonate the caller so that we can get the caller's SID
if ((rpcstatus = RpcImpersonateClient(pReq->hFileRepClient)) != RPC_S_OK) {
pReq->Status = GetLastError();
ServerShutdownRequest(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();
ServerShutdownRequest(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();
// Stop impersonating. We got what we wanted.
if ((rpcstatus = RpcRevertToSelf()) != RPC_S_OK) {
pReq->bImpersonating = FALSE;
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcRevertToSelf failed"));
return;
}
pReq->bImpersonating = FALSE;
#else
pReq->Pri = 1;
unsigned SidLength = GetLengthSid(pAnonSID);
if ((pReq->pSID = AutoHeapAlloc(SidLength)) == NULL) {
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: AutoHeapAlloc failed"));
return;
}
if (CopySid(SidLength, pReq->pSID, pAnonSID) == 0) {
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: CopySID failed"));
return;
}
#endif
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Pri=%d for req %p\n"), pReq->Pri, pReq);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
// If there are no workers ot handle the request, create one.
HANDLE hThread;
ULONG ThreadIdentifier;
DWORD status;
if (nThreadsAtServerCompletionPort < 1) {
// Go service some requests, remember that you are
// an RPC thread.
if ((hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) SServiceRequests,
NULL,
0,
&ThreadIdentifier)) == NULL) {
pReq->Status = GetLastError();
ServerShutdownRequest(pReq);
AddToMessageLogProcFailure(TEXT("SServiceRequests: 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 place request onto the queue only after we know that there is someone
// who will actually be able to pick it up. Thus, we need to create the thread first.
// Increment the counter for the number of concurrent requests.
if (!CounterIncrement(pServerReqCounters[pReq->Pri])) {
pReq->Status = GetLastError();
ServerShutdownRequest(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"));
return;
}
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Incremented ServerReqCounters[%d]\n"), pReq->Pri);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
pReq->State = StateQueued;
#ifdef DEBUG2
nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
bufSize -= nCharWritten;
DbgMsgRecord(Msg);
#endif
// Place the request onto the queue.
QueueAdd(ServerReqQueues[pReq->Pri], pReq, TRUE);
// After this point the request can be deleted. Do not touch it.
// Now if we can, find a queued request and activate it.
FindAndActivateSReq();
#ifdef DEBUG2
DbgMsgRecord(TEXT("<- RequestFile\n"));
#endif
return;
}
// end FileRepServerProc.cpp