libwinpr-nt: implement NtCurrentTeb()
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Thu, 22 Aug 2013 14:18:38 +0000 (10:18 -0400)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Thu, 22 Aug 2013 14:18:38 +0000 (10:18 -0400)
15 files changed:
server/Sample/sfreerdp.c
winpr/include/winpr/nt.h [new file with mode: 0644]
winpr/libwinpr/error/CMakeLists.txt
winpr/libwinpr/error/error.c
winpr/libwinpr/error/test/.gitignore [new file with mode: 0644]
winpr/libwinpr/error/test/CMakeLists.txt [new file with mode: 0644]
winpr/libwinpr/error/test/TestErrorSetLastError.c [new file with mode: 0644]
winpr/libwinpr/nt/CMakeLists.txt [new file with mode: 0644]
winpr/libwinpr/nt/ModuleOptions.cmake [new file with mode: 0644]
winpr/libwinpr/nt/module.def [new file with mode: 0644]
winpr/libwinpr/nt/nt.c [new file with mode: 0644]
winpr/libwinpr/nt/test/.gitignore [new file with mode: 0644]
winpr/libwinpr/nt/test/CMakeLists.txt [new file with mode: 0644]
winpr/libwinpr/nt/test/TestNtCreateFile.c [new file with mode: 0644]
winpr/libwinpr/nt/test/TestNtCurrentTeb.c [new file with mode: 0644]

index 46615ec..6fc2b32 100644 (file)
@@ -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 (file)
index 0000000..7a6aae0
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Windows Native System Services
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * 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 <winpr/winpr.h>
+#include <winpr/wtypes.h>
+
+#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 */
+
index db67804..eb78993 100644 (file)
@@ -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()
index ff11daa..96c35cd 100644 (file)
 
 #include <winpr/error.h>
 
-/**
- * api-ms-win-core-errorhandling-l1-1-1.dll:
- * 
- * GetErrorMode
- * SetErrorMode
- * GetLastError
- * SetLastError
- * RestoreLastError
- * RaiseException
- * UnhandledExceptionFilter
- * SetUnhandledExceptionFilter
- * AddVectoredExceptionHandler
- * RemoveVectoredExceptionHandler
- * AddVectoredContinueHandler
- * RemoveVectoredContinueHandler
- */
-
 #ifndef _WIN32
 
+#include <stdio.h>
+
+#include <winpr/nt.h>
+
 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 (file)
index 0000000..8f1dcb1
--- /dev/null
@@ -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 (file)
index 0000000..ecec709
--- /dev/null
@@ -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 (file)
index 0000000..073417a
--- /dev/null
@@ -0,0 +1,89 @@
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+
+#include <winpr/error.h>
+
+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 (file)
index 0000000..b745524
--- /dev/null
@@ -0,0 +1,53 @@
+# WinPR: Windows Portable Runtime
+# libwinpr-nt cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# 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 (file)
index 0000000..750a8f5
--- /dev/null
@@ -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 (file)
index 0000000..b45c622
--- /dev/null
@@ -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 (file)
index 0000000..21f3a00
--- /dev/null
@@ -0,0 +1,205 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Windows Native System Services
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * 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 <winpr/nt.h>
+
+/**
+ * NtXxx Routines:
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff557720/
+ */
+
+#ifndef _WIN32
+
+#include <pthread.h>
+
+#include <winpr/crt.h>
+
+/**
+ * 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 (file)
index 0000000..1302271
--- /dev/null
@@ -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 (file)
index 0000000..b3fcb8b
--- /dev/null
@@ -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 (file)
index 0000000..c39a724
--- /dev/null
@@ -0,0 +1,8 @@
+
+#include <winpr/nt.h>
+
+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 (file)
index 0000000..43f46a7
--- /dev/null
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+
+#include <winpr/nt.h>
+
+int TestNtCurrentTeb(int argc, char* argv[])
+{
+       PTEB teb;
+
+       teb = NtCurrentTeb();
+
+       if (!teb)
+       {
+               printf("NtCurrentTeb() returned NULL\n");
+               return -1;
+       }
+
+       return 0;
+}
+