From 68ec10a9d94d9aef96ce6c1f930c529c36150a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 22 Aug 2013 10:18:38 -0400 Subject: [PATCH] libwinpr-nt: implement NtCurrentTeb() --- server/Sample/sfreerdp.c | 4 +- winpr/include/winpr/nt.h | 77 +++++++ winpr/libwinpr/error/CMakeLists.txt | 10 + winpr/libwinpr/error/error.c | 25 +-- winpr/libwinpr/error/test/.gitignore | 3 + winpr/libwinpr/error/test/CMakeLists.txt | 31 +++ .../error/test/TestErrorSetLastError.c | 89 ++++++++ winpr/libwinpr/nt/CMakeLists.txt | 53 +++++ winpr/libwinpr/nt/ModuleOptions.cmake | 8 + winpr/libwinpr/nt/module.def | 3 + winpr/libwinpr/nt/nt.c | 205 ++++++++++++++++++ winpr/libwinpr/nt/test/.gitignore | 3 + winpr/libwinpr/nt/test/CMakeLists.txt | 32 +++ winpr/libwinpr/nt/test/TestNtCreateFile.c | 8 + winpr/libwinpr/nt/test/TestNtCurrentTeb.c | 20 ++ 15 files changed, 550 insertions(+), 21 deletions(-) create mode 100644 winpr/include/winpr/nt.h create mode 100644 winpr/libwinpr/error/test/.gitignore create mode 100644 winpr/libwinpr/error/test/CMakeLists.txt create mode 100644 winpr/libwinpr/error/test/TestErrorSetLastError.c create mode 100644 winpr/libwinpr/nt/CMakeLists.txt create mode 100644 winpr/libwinpr/nt/ModuleOptions.cmake create mode 100644 winpr/libwinpr/nt/module.def create mode 100644 winpr/libwinpr/nt/nt.c create mode 100644 winpr/libwinpr/nt/test/.gitignore create mode 100644 winpr/libwinpr/nt/test/CMakeLists.txt create mode 100644 winpr/libwinpr/nt/test/TestNtCreateFile.c create mode 100644 winpr/libwinpr/nt/test/TestNtCurrentTeb.c diff --git a/server/Sample/sfreerdp.c b/server/Sample/sfreerdp.c index 46615ec3e..6fc2b3278 100644 --- a/server/Sample/sfreerdp.c +++ b/server/Sample/sfreerdp.c @@ -498,7 +498,7 @@ BOOL tf_peer_post_connect(freerdp_peer* client) { if (strncmp(client->settings->ChannelDefArray[i].Name, "rdpdbg", 6) == 0) { - context->debug_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpdbg", 0); + context->debug_channel = WTSVirtualChannelManagerOpenEx(context->vcm, "rdpdbg", 0); if (context->debug_channel != NULL) { @@ -580,7 +580,7 @@ void tf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) { if (context->debug_channel) { - WTSVirtualChannelWrite(context->debug_channel, (BYTE*) "test2", 5, NULL); + WTSVirtualChannelWrite(context->debug_channel, (PCHAR) "test2", 5, NULL); } } else if ((flags & 0x4000) && code == 0x2D) /* 'x' key */ diff --git a/winpr/include/winpr/nt.h b/winpr/include/winpr/nt.h new file mode 100644 index 000000000..7a6aae0f4 --- /dev/null +++ b/winpr/include/winpr/nt.h @@ -0,0 +1,77 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Native System Services + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_NT_H +#define WINPR_NT_H + +#include +#include + +#ifndef _WIN32 + +typedef struct _PEB PEB; +typedef struct _PEB* PPEB; + +typedef struct _TEB TEB; +typedef struct _TEB* PTEB; + +/** + * Process Environment Block + */ + +struct _THREAD_BLOCK_ID +{ + DWORD ThreadId; + TEB* ThreadEnvironmentBlock; +}; +typedef struct _THREAD_BLOCK_ID THREAD_BLOCK_ID; + +struct _PEB +{ + DWORD ThreadCount; + DWORD ThreadArraySize; + THREAD_BLOCK_ID* Threads; +}; + +/* + * Thread Environment Block + */ + +struct _TEB +{ + PEB* ProcessEnvironmentBlock; + + DWORD LastErrorValue; + PVOID TlsSlots[64]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API PTEB NtCurrentTeb(void); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* WINPR_NT_H */ + diff --git a/winpr/libwinpr/error/CMakeLists.txt b/winpr/libwinpr/error/CMakeLists.txt index db6780490..eb78993c6 100644 --- a/winpr/libwinpr/error/CMakeLists.txt +++ b/winpr/libwinpr/error/CMakeLists.txt @@ -31,10 +31,20 @@ add_complex_library(MODULE ${MODULE_NAME} TYPE "OBJECT" set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib") +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL + MODULE winpr + MODULES winpr-nt) + if(MONOLITHIC_BUILD) else() + target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR") + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/error/error.c b/winpr/libwinpr/error/error.c index ff11daa6b..96c35cd6c 100644 --- a/winpr/libwinpr/error/error.c +++ b/winpr/libwinpr/error/error.c @@ -23,25 +23,12 @@ #include -/** - * api-ms-win-core-errorhandling-l1-1-1.dll: - * - * GetErrorMode - * SetErrorMode - * GetLastError - * SetLastError - * RestoreLastError - * RaiseException - * UnhandledExceptionFilter - * SetUnhandledExceptionFilter - * AddVectoredExceptionHandler - * RemoveVectoredExceptionHandler - * AddVectoredContinueHandler - * RemoveVectoredContinueHandler - */ - #ifndef _WIN32 +#include + +#include + UINT GetErrorMode(void) { return 0; @@ -54,12 +41,12 @@ UINT SetErrorMode(UINT uMode) DWORD GetLastError(VOID) { - return 0; + return NtCurrentTeb()->LastErrorValue; } VOID SetLastError(DWORD dwErrCode) { - + NtCurrentTeb()->LastErrorValue = dwErrCode; } VOID RestoreLastError(DWORD dwErrCode) diff --git a/winpr/libwinpr/error/test/.gitignore b/winpr/libwinpr/error/test/.gitignore new file mode 100644 index 000000000..8f1dcb16a --- /dev/null +++ b/winpr/libwinpr/error/test/.gitignore @@ -0,0 +1,3 @@ +TestError +TestError.c + diff --git a/winpr/libwinpr/error/test/CMakeLists.txt b/winpr/libwinpr/error/test/CMakeLists.txt new file mode 100644 index 000000000..ecec7093f --- /dev/null +++ b/winpr/libwinpr/error/test/CMakeLists.txt @@ -0,0 +1,31 @@ + +set(MODULE_NAME "TestError") +set(MODULE_PREFIX "TEST_ERROR") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestErrorSetLastError.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE winpr + MODULES winpr-crt winpr-synch winpr-thread winpr-error) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/error/test/TestErrorSetLastError.c b/winpr/libwinpr/error/test/TestErrorSetLastError.c new file mode 100644 index 000000000..073417adc --- /dev/null +++ b/winpr/libwinpr/error/test/TestErrorSetLastError.c @@ -0,0 +1,89 @@ + +#include +#include +#include + +#include + +static int status = 0; + +static DWORD errors[4] = +{ + ERROR_INVALID_DATA, + ERROR_BROKEN_PIPE, + ERROR_INVALID_NAME, + ERROR_BAD_ARGUMENTS +}; + +static void* test_error_thread(void* arg) +{ + int id; + DWORD error; + + id = (int) (size_t) arg; + + error = errors[id]; + + SetLastError(error); + + Sleep(10); + + error = GetLastError(); + + if (error != errors[id]) + { + printf("GetLastError() failure (thread %d): Expected: 0x%04X, Actual: 0x%04X\n", + id, errors[id], error); + + if (!status) + status = -1; + + return NULL; + } + + return NULL; +} + +int TestErrorSetLastError(int argc, char* argv[]) +{ + DWORD error; + HANDLE threads[4]; + + SetLastError(ERROR_ACCESS_DENIED); + + error = GetLastError(); + + if (error != ERROR_ACCESS_DENIED) + { + printf("GetLastError() failure: Expected: 0x%04X, Actual: 0x%04X\n", + ERROR_ACCESS_DENIED, error); + return -1; + } + + threads[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 0, 0, NULL); + threads[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 1, 0, NULL); + threads[2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 2, 0, NULL); + threads[3] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 3, 0, NULL); + + WaitForSingleObject(threads[0], INFINITE); + WaitForSingleObject(threads[1], INFINITE); + WaitForSingleObject(threads[2], INFINITE); + WaitForSingleObject(threads[3], INFINITE); + + CloseHandle(threads[0]); + CloseHandle(threads[1]); + CloseHandle(threads[2]); + CloseHandle(threads[3]); + + error = GetLastError(); + + if (error != ERROR_ACCESS_DENIED) + { + printf("GetLastError() failure: Expected: 0x%04X, Actual: 0x%04X\n", + ERROR_ACCESS_DENIED, error); + return -1; + } + + return status; +} + diff --git a/winpr/libwinpr/nt/CMakeLists.txt b/winpr/libwinpr/nt/CMakeLists.txt new file mode 100644 index 000000000..b7455246e --- /dev/null +++ b/winpr/libwinpr/nt/CMakeLists.txt @@ -0,0 +1,53 @@ +# WinPR: Windows Portable Runtime +# libwinpr-nt cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-nt") +set(MODULE_PREFIX "WINPR_NT") + +set(${MODULE_PREFIX}_SRCS + nt.c) + +if(MSVC AND (NOT MONOLITHIC_BUILD)) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} module.def) +endif() + +add_complex_library(MODULE ${MODULE_NAME} TYPE "OBJECT" + MONOLITHIC ${MONOLITHIC_BUILD} + SOURCES ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib") + +set(${MODULE_PREFIX}_LIBS + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS}) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} rt) +endif() + +if(MONOLITHIC_BUILD) + set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE) +else() + target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR") + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/nt/ModuleOptions.cmake b/winpr/libwinpr/nt/ModuleOptions.cmake new file mode 100644 index 000000000..750a8f556 --- /dev/null +++ b/winpr/libwinpr/nt/ModuleOptions.cmake @@ -0,0 +1,8 @@ + +set(MINWIN_LAYER "0") +set(MINWIN_GROUP "none") +set(MINWIN_MAJOR_VERSION "0") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "nt") +set(MINWIN_LONG_NAME "Windows Native System Services") +set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}") diff --git a/winpr/libwinpr/nt/module.def b/winpr/libwinpr/nt/module.def new file mode 100644 index 000000000..b45c622b5 --- /dev/null +++ b/winpr/libwinpr/nt/module.def @@ -0,0 +1,3 @@ +LIBRARY "libwinpr-nt" +EXPORTS + diff --git a/winpr/libwinpr/nt/nt.c b/winpr/libwinpr/nt/nt.c new file mode 100644 index 000000000..21f3a00a7 --- /dev/null +++ b/winpr/libwinpr/nt/nt.c @@ -0,0 +1,205 @@ +/** + * WinPR: Windows Portable Runtime + * Windows Native System Services + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +/** + * NtXxx Routines: + * http://msdn.microsoft.com/en-us/library/windows/hardware/ff557720/ + */ + +#ifndef _WIN32 + +#include + +#include + +/** + * The current implementation of NtCurrentTeb() is not the most efficient + * but it's a starting point. Beware of potential performance bottlenecks + * caused by multithreaded usage of SetLastError/GetLastError. + */ + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static PPEB g_ProcessEnvironmentBlock = NULL; + +static void NtThreadEnvironmentBlockFree(PTEB teb); +static void NtProcessEnvironmentBlockFree(PPEB peb); + +static PTEB NtThreadEnvironmentBlockNew() +{ + PTEB teb = NULL; + pthread_key_t key; + + teb = (PTEB) malloc(sizeof(TEB)); + + if (teb) + { + ZeroMemory(teb, sizeof(TEB)); + + /** + * We are not really using the key, but it provides an automatic way + * of calling NtThreadEnvironmentBlockFree on thread termination for + * the current Thread Environment Block. + */ + + pthread_key_create(&key, (void (*)(void*)) NtThreadEnvironmentBlockFree); + pthread_setspecific(key, (void*) teb); + } + + return teb; +} + +static void NtThreadEnvironmentBlockFree(PTEB teb) +{ + DWORD index; + PPEB peb = NULL; + + peb = teb->ProcessEnvironmentBlock; + + pthread_mutex_lock(&mutex); + + for (index = 0; index < peb->ThreadArraySize; index++) + { + if (peb->Threads[index].ThreadEnvironmentBlock == teb) + { + peb->Threads[index].ThreadId = 0; + peb->Threads[index].ThreadEnvironmentBlock = NULL; + peb->ThreadCount--; + break; + } + } + + if (!peb->ThreadCount) + { + NtProcessEnvironmentBlockFree(peb); + } + + pthread_mutex_unlock(&mutex); + + free(teb); +} + +static PPEB NtProcessEnvironmentBlockNew() +{ + PPEB peb = NULL; + + peb = (PPEB) malloc(sizeof(PEB)); + + if (peb) + { + ZeroMemory(peb, sizeof(PEB)); + + peb->ThreadCount = 0; + peb->ThreadArraySize = 64; + peb->Threads = (THREAD_BLOCK_ID*) malloc(sizeof(THREAD_BLOCK_ID) * peb->ThreadArraySize); + + if (peb->Threads) + { + ZeroMemory(peb->Threads, sizeof(THREAD_BLOCK_ID) * peb->ThreadArraySize); + } + } + + return peb; +} + +static void NtProcessEnvironmentBlockFree(PPEB peb) +{ + if (peb) + { + free(peb->Threads); + free(peb); + } + + g_ProcessEnvironmentBlock = NULL; +} + +PPEB NtCurrentPeb(void) +{ + PPEB peb = NULL; + + pthread_mutex_lock(&mutex); + + if (!g_ProcessEnvironmentBlock) + g_ProcessEnvironmentBlock = NtProcessEnvironmentBlockNew(); + + peb = g_ProcessEnvironmentBlock; + + pthread_mutex_unlock(&mutex); + + return peb; +} + +PTEB NtCurrentTeb(void) +{ + DWORD index; + int freeIndex; + DWORD ThreadId; + PPEB peb = NULL; + PTEB teb = NULL; + + peb = NtCurrentPeb(); + + ThreadId = (DWORD) pthread_self(); + + freeIndex = -1; + + pthread_mutex_lock(&mutex); + + for (index = 0; index < peb->ThreadArraySize; index++) + { + if (!peb->Threads[index].ThreadId) + { + if (freeIndex < 0) + freeIndex = (int) index; + } + + if (peb->Threads[index].ThreadId == ThreadId) + { + teb = peb->Threads[index].ThreadEnvironmentBlock; + break; + } + } + + if (!teb) + { + if (freeIndex >= 0) + { + teb = NtThreadEnvironmentBlockNew(); + peb->Threads[freeIndex].ThreadEnvironmentBlock = teb; + peb->Threads[freeIndex].ThreadId = ThreadId; + peb->ThreadCount++; + + teb->ProcessEnvironmentBlock = peb; + teb->LastErrorValue = 0; + } + } + + pthread_mutex_unlock(&mutex); + + return teb; +} + +#endif + diff --git a/winpr/libwinpr/nt/test/.gitignore b/winpr/libwinpr/nt/test/.gitignore new file mode 100644 index 000000000..1302271b9 --- /dev/null +++ b/winpr/libwinpr/nt/test/.gitignore @@ -0,0 +1,3 @@ +TestNt +TestNt.c + diff --git a/winpr/libwinpr/nt/test/CMakeLists.txt b/winpr/libwinpr/nt/test/CMakeLists.txt new file mode 100644 index 000000000..b3fcb8bab --- /dev/null +++ b/winpr/libwinpr/nt/test/CMakeLists.txt @@ -0,0 +1,32 @@ + +set(MODULE_NAME "TestNt") +set(MODULE_PREFIX "TEST_NT") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestNtCreateFile.c + TestNtCurrentTeb.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE winpr + MODULES winpr-crt winpr-nt) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/nt/test/TestNtCreateFile.c b/winpr/libwinpr/nt/test/TestNtCreateFile.c new file mode 100644 index 000000000..c39a724e0 --- /dev/null +++ b/winpr/libwinpr/nt/test/TestNtCreateFile.c @@ -0,0 +1,8 @@ + +#include + +int TestNtCreateFile(int argc, char* argv[]) +{ + return 0; +} + diff --git a/winpr/libwinpr/nt/test/TestNtCurrentTeb.c b/winpr/libwinpr/nt/test/TestNtCurrentTeb.c new file mode 100644 index 000000000..43f46a77f --- /dev/null +++ b/winpr/libwinpr/nt/test/TestNtCurrentTeb.c @@ -0,0 +1,20 @@ + +#include + +#include + +int TestNtCurrentTeb(int argc, char* argv[]) +{ + PTEB teb; + + teb = NtCurrentTeb(); + + if (!teb) + { + printf("NtCurrentTeb() returned NULL\n"); + return -1; + } + + return 0; +} +