/*++ Copyright (c) 1990-2002 Microsoft Corporation Module Name: pool.cxx Abstract: Implementation for class ThreadPool. Author: Ali Naqvi (alinaqvi) 3-May-2002 Revision History: --*/ #include #include "pool.hxx" #pragma hdrstop #define SIZE_OF_TOKEN_INFORMATION \ sizeof( TOKEN_USER ) \ + sizeof( SID ) \ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES /*++ Name: TThreadPool::TThreadPool Description: Constructor Arguments: None Return Value: None --*/ TThreadPool::TThreadPool() { pHead = NULL; } /*++ Name: TThreadPool::~ThreadPool Description: Destructor Arguments: None Return Value: None --*/ TThreadPool::~TThreadPool() { PWIN32THREAD pElem = NULL; while (pHead != NULL) { pElem = pHead; pHead = pHead->pNext; FreeThread(pHead); } } /*++ Name: TThreadPool::CreateThreadEntry Description: Create and Initialize a WIN32THREAD object with provided printer name and printer defaults. Arguments: pName - Printer Name pDefaults - Printer Defaults ppThread - Out parameter pointer to the created WIN32THREAD Return Value: HRESULT --*/ HRESULT TThreadPool::CreateThreadEntry( LPWSTR pName, PPRINTER_DEFAULTSW pDefaults, PWIN32THREAD *ppThread ) { PWIN32THREAD pThread = NULL; HRESULT hReturn = E_FAIL; SplInSem(); hReturn = ppThread && pName ? S_OK : E_INVALIDARG; if (SUCCEEDED(hReturn)) { // // Create the thread wrapper object. // pThread = reinterpret_cast(AllocSplMem(sizeof(WIN32THREAD))); hReturn = pThread ? S_OK : E_OUTOFMEMORY; } if(SUCCEEDED(hReturn)) { pThread->signature = TP_SIGNATURE; pThread->pName = AllocSplStr(pName); hReturn = pThread->pName ? S_OK : E_OUTOFMEMORY; } if (SUCCEEDED(hReturn)) { hReturn = GetThreadSid(pThread); } if (SUCCEEDED(hReturn)) { pThread->hRpcHandle = NULL; pThread->hWaitValidHandle = CreateEvent(NULL, EVENT_RESET_MANUAL, EVENT_INITIAL_STATE_NOT_SIGNALED, NULL); pThread->dwStatus = THREAD_STATUS_EXECUTING; pThread->dwRpcOpenPrinterError = 0; pThread->pDefaults = reinterpret_cast(AllocSplMem(sizeof(PRINTER_DEFAULTSW))); hReturn = pThread->pDefaults ? S_OK : E_OUTOFMEMORY; } if (SUCCEEDED(hReturn)) { pThread->pDefaults->pDatatype = NULL; pThread->pDefaults->pDevMode = NULL; pThread->pDefaults->DesiredAccess = 0; pThread->bForegroundClose = FALSE; hReturn = CopypDefaults(pDefaults, pThread->pDefaults) ? S_OK : E_OUTOFMEMORY; pThread->pNext = NULL; } if (SUCCEEDED(hReturn)) { *ppThread = pThread; } else { FreeThread(pThread); *ppThread = NULL; } return hReturn; } /*++ Name: TThreadPool::GetThreadSid Description: Gets the SID from the thread token and makes a copy. Arguments: pThread - Pointer to the WIN32THREAD from which we get our user SID. Return Value: HRESULT --*/ HRESULT TThreadPool::GetThreadSid( PWIN32THREAD pThread ) { UCHAR ThreadTokenInformation[SIZE_OF_TOKEN_INFORMATION]; DWORD dwSidLength; ULONG ReturnLength; HRESULT hReturn = E_FAIL; hReturn = pThread ? S_OK : E_INVALIDARG; if (SUCCEEDED(hReturn)) { hReturn = OpenThreadToken(GetCurrentThread(), TOKEN_READ | TOKEN_IMPERSONATE, TRUE, &(pThread->hToken)) ? S_OK : GetLastErrorAsHResultAndFail(); } if (SUCCEEDED(hReturn)) { hReturn = GetTokenInformation(pThread->hToken, TokenUser, ThreadTokenInformation, sizeof(ThreadTokenInformation), &ReturnLength) ? S_OK : GetLastErrorAsHResultAndFail(); } if (SUCCEEDED(hReturn)) { dwSidLength = RtlLengthSid((reinterpret_cast(ThreadTokenInformation))->User.Sid); pThread->pSid = reinterpret_cast(AllocSplMem(dwSidLength)); hReturn = pThread->pSid ? S_OK : E_OUTOFMEMORY; } if (SUCCEEDED(hReturn)) { RtlCopySid( dwSidLength, pThread->pSid, (reinterpret_cast(ThreadTokenInformation))->User.Sid); } return hReturn; } /*++ Name: TThreadPool::DeleteThreadEntry Description: Delink thread item from the threadpool and delete it. Arguments: pThread - Pointer to the WIN322THREAD item to be deleted Return Value: HRESULT --*/ HRESULT TThreadPool::DeleteThreadEntry( PWIN32THREAD pThread ) { PWIN32THREAD *ppElem = &pHead; HRESULT hReturn = E_FAIL; SplInSem(); hReturn = pThread ? S_OK : E_INVALIDARG; SPLASSERT(IsValid(pThread)); if (SUCCEEDED(hReturn)) { // // If the thread is in the pool, delink it. // while (*ppElem && (*ppElem) != pThread) { ppElem = &((*ppElem)->pNext); } if (*ppElem) { (*ppElem) = (*ppElem)->pNext; } FreeThread(pThread); } return hReturn; } /*++ Name: TThreadPool::UseThread Description: If exists a thread item for the particular printer name in the pool, it delinks and returns a pointer to the thread item. Arguments: pName - Printer Name to be looked for ppThread - Pointer to the thread item if found Return Value: HRESULT S_OK - If thread item found S_FALSE - If thread is not found --*/ HRESULT TThreadPool::UseThread( LPWSTR pName, PWIN32THREAD *ppThread, ACCESS_MASK DesiredAccess ) { PWIN32THREAD *ppElem = NULL; HRESULT hReturn = E_FAIL; PVOID pUserTokenInformation = NULL; DWORD dwInformationLength = SIZE_OF_TOKEN_INFORMATION; SplInSem(); hReturn = ppThread ? S_OK : E_INVALIDARG; if (SUCCEEDED(hReturn)) { // // Get User information for current thread // pUserTokenInformation = reinterpret_cast(AllocSplMem(dwInformationLength)); hReturn = GetUserTokenInformation(&pUserTokenInformation, dwInformationLength); } if (SUCCEEDED(hReturn)) { *ppThread = NULL; // // If we dont find a thread we return S_FALSE // hReturn = S_FALSE; for (ppElem = &pHead; *ppElem; ppElem = &((*ppElem)->pNext)) { // // If we found the thread we return it and break. // if (wcscmp(pName, (*ppElem)->pName) == 0 && IsValidUser(*ppElem, pUserTokenInformation) && (*ppElem)->pDefaults->DesiredAccess == DesiredAccess && (*ppElem)->dwStatus == THREAD_STATUS_EXECUTING) { *ppThread = *ppElem; *ppElem = (*ppElem)->pNext; hReturn = S_OK; (*ppThread)->pNext = NULL; break; } } } FreeSplMem(pUserTokenInformation); return hReturn; } /*++ Name: TThreadPool::GetUserTokenInformation Description: Returns the current thread users Sid information in the out parameter. Arguments: pUserTokenInformation - Is set to the current user's Sid information Return Value: HRESULT --*/ HRESULT TThreadPool::GetUserTokenInformation( PVOID *ppUserTokenInformation, DWORD dwInformationLength ) { HANDLE UserTokenHandle; ULONG ReturnLength; HRESULT hResult = E_FAIL; hResult = ppUserTokenInformation ? S_OK : E_INVALIDARG; if (SUCCEEDED(hResult)) { hResult = OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, &UserTokenHandle) ? S_OK : E_HANDLE; } if (SUCCEEDED(hResult)) { hResult = GetTokenInformation( UserTokenHandle, TokenUser, *ppUserTokenInformation, dwInformationLength, &ReturnLength) ? S_OK : E_ACCESSDENIED; } if (UserTokenHandle) { CloseHandle(UserTokenHandle); } return hResult; } /*++ Name: TThreadPool::IsValidUser Description: Checks the Sid of the current user to that of the Sid token save in thread and determines if it is the same user. Other users cannot use the same RpcHandle therefore cannot use the same thread from the pool. Arguments: pThread - The thread whichs Sid we want to check against current user Return Value: BOOL - Returns true if it is the same user --*/ BOOL TThreadPool::IsValidUser( PWIN32THREAD pThread, PVOID pCurrentTokenInformation ) { BOOL bReturn = FALSE; bReturn = RtlEqualSid((reinterpret_cast(pCurrentTokenInformation))->User.Sid, pThread->pSid); return bReturn; } /*++ Name: TThreadPool::ReturnThread Description: Return thread item to the pool, signal event that we are done using the thread. Arguments: pThread - Thread item to be returned Return Value: HRESULT --*/ HRESULT TThreadPool::ReturnThread( PWIN32THREAD pThread ) { HRESULT hReturn = E_FAIL; SplInSem(); hReturn = pThread ? S_OK : E_INVALIDARG; SPLASSERT(IsValid(pThread)); if (SUCCEEDED(hReturn)) { // // We only return a thread from the foreground if it is actuall still // executing and not terminated. // SPLASSERT(pThread->dwStatus == THREAD_STATUS_EXECUTING); pThread->pNext = pHead; pHead = pThread; } return hReturn; } /*++ Name: TThreadPool::IsValid Description: Checks signature of the thread item to see if it is valid. Arguments: pThread - The thread to check for validity Return Value: BOOL - Returns true if it is the same user --*/ BOOL TThreadPool::IsValid( PWIN32THREAD pThread ) { return (pThread->signature == TP_SIGNATURE); } /*++ Name: TThreadPool::FreeThread Description: Frees the data in the given thread. Arguments: pThread - The thread to free Return Value: Nothing --*/ VOID TThreadPool::FreeThread( IN PWIN32THREAD pThread ) { if (pThread) { // // Now delete the thread. // pThread->hRpcHandle = NULL; if (pThread->pDefaults) { FreeSplStr(pThread->pDefaults->pDatatype); FreeSplMem(pThread->pDefaults->pDevMode); FreeSplMem(pThread->pDefaults); } if (pThread->hWaitValidHandle) { CloseHandle(pThread->hWaitValidHandle); } if( pThread->hToken != INVALID_HANDLE_VALUE ) { CloseHandle( pThread->hToken ); pThread->hToken = INVALID_HANDLE_VALUE; } if (pThread->pName) { FreeSplStr(pThread->pName); } if (pThread->pSid) { FreeSplMem(pThread->pSid); } FreeSplMem(pThread); } }