diff --git a/build/VStudio/samples/mirror.vcxproj b/build/VStudio/testing/mirror.vcxproj
similarity index 95%
rename from build/VStudio/samples/mirror.vcxproj
rename to build/VStudio/testing/mirror.vcxproj
index 7d834c3a..a07cb233 100644
--- a/build/VStudio/samples/mirror.vcxproj
+++ b/build/VStudio/testing/mirror.vcxproj
@@ -101,7 +101,7 @@
Disabled
WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\..\inc;..\..\..\src
+ ..\..\..\src;..\..\..\inc
Console
@@ -116,7 +116,7 @@
Disabled
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\..\inc;..\..\..\src
+ ..\..\..\src;..\..\..\inc
Console
@@ -133,7 +133,7 @@
true
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\..\inc;..\..\..\src
+ ..\..\..\src;..\..\..\inc
Console
@@ -152,7 +152,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\..\inc;..\..\..\src
+ ..\..\..\src;..\..\..\inc
Console
diff --git a/build/VStudio/samples/mirror.vcxproj.filters b/build/VStudio/testing/mirror.vcxproj.filters
similarity index 100%
rename from build/VStudio/samples/mirror.vcxproj.filters
rename to build/VStudio/testing/mirror.vcxproj.filters
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj
new file mode 100644
index 00000000..bac3daa6
--- /dev/null
+++ b/build/VStudio/testing/winfsp-tests.vcxproj
@@ -0,0 +1,184 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}
+ Win32Proj
+ winfsptests
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)build\$(Configuration)\
+ $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\
+ $(ProjectName)-$(PlatformTarget)
+
+
+ true
+ $(SolutionDir)build\$(Configuration)\
+ $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\
+ $(ProjectName)-$(PlatformTarget)
+
+
+ false
+ $(SolutionDir)build\$(Configuration)\
+ $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\
+ $(ProjectName)-$(PlatformTarget)
+
+
+ false
+ $(SolutionDir)build\$(Configuration)\
+ $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\
+ $(ProjectName)-$(PlatformTarget)
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\src;..\..\..\inc;..\..\..\ext
+
+
+ Console
+ true
+
+
+
+
+
+
+ Level3
+ Disabled
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\src;..\..\..\inc;..\..\..\ext
+
+
+ Console
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\src;..\..\..\inc;..\..\..\ext
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\src;..\..\..\inc;..\..\..\ext
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ false
+ false
+ false
+ false
+ TurnOffAllWarnings
+ TurnOffAllWarnings
+ TurnOffAllWarnings
+ TurnOffAllWarnings
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters
new file mode 100644
index 00000000..a98cfe18
--- /dev/null
+++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters
@@ -0,0 +1,28 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {1b7df6fa-185e-4a53-bfac-a4bbf82bba70}
+
+
+
+
+ Source
+
+
+ Source
+
+
+ Source\tlib
+
+
+
+
+ Source\tlib
+
+
+
\ No newline at end of file
diff --git a/build/VStudio/winfsp.sln b/build/VStudio/winfsp.sln
index 328de8d2..a5976a97 100644
--- a/build/VStudio/winfsp.sln
+++ b/build/VStudio/winfsp.sln
@@ -7,9 +7,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp.dll", "winfsp_dll.vc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp.sys", "winfsp_sys.vcxproj", "{C85C26BA-8C22-4D30-83DA-46C3548E6332}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{69439FD1-C07D-4BF1-98DC-3CCFECE53A49}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testing", "testing", "{69439FD1-C07D-4BF1-98DC-3CCFECE53A49}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mirror", "samples\mirror.vcxproj", "{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mirror", "testing\mirror.vcxproj", "{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp-tests", "testing\winfsp-tests.vcxproj", "{262DF8CC-E7A8-4460-A22C-683CBA322C32}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -43,11 +45,20 @@ Global
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.Build.0 = Release|x64
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.ActiveCfg = Release|Win32
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.Build.0 = Release|Win32
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.ActiveCfg = Debug|x64
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.Build.0 = Debug|x64
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.ActiveCfg = Debug|Win32
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.Build.0 = Debug|Win32
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.ActiveCfg = Release|x64
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.Build.0 = Release|x64
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.ActiveCfg = Release|Win32
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AA7190E8-877F-4827-8CDD-E0D85F83C8C1} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49}
+ {262DF8CC-E7A8-4460-A22C-683CBA322C32} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49}
EndGlobalSection
EndGlobal
diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj
index cc07e05c..2bdac853 100644
--- a/build/VStudio/winfsp_dll.vcxproj
+++ b/build/VStudio/winfsp_dll.vcxproj
@@ -102,7 +102,7 @@
Disabled
WINFSP_DLL_INTERNAL;WIN32;_DEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions)
true
- ..\..\inc;..\..\src
+ ..\..\src;..\..\inc
Windows
@@ -117,7 +117,7 @@
Disabled
WINFSP_DLL_INTERNAL;_DEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions)
true
- ..\..\inc;..\..\src
+ ..\..\src;..\..\inc
Windows
@@ -134,7 +134,7 @@
true
WINFSP_DLL_INTERNAL;WIN32;NDEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions)
true
- ..\..\inc;..\..\src
+ ..\..\src;..\..\inc
Windows
@@ -153,7 +153,7 @@
true
WINFSP_DLL_INTERNAL;NDEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions)
true
- ..\..\inc;..\..\src
+ ..\..\src;..\..\inc
Windows
diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj
index 06f38c94..3c3e7ac6 100644
--- a/build/VStudio/winfsp_sys.vcxproj
+++ b/build/VStudio/winfsp_sys.vcxproj
@@ -96,7 +96,7 @@
- ..\..\inc;..\..\src;%(AdditionalIncludeDirectories)
+ ..\..\src;..\..\inc;%(AdditionalIncludeDirectories)
WINFSP_SYS_INTERNAL; _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)
@@ -105,7 +105,7 @@
- ..\..\inc;..\..\src;%(AdditionalIncludeDirectories)
+ ..\..\src;..\..\inc;%(AdditionalIncludeDirectories)
WINFSP_SYS_INTERNAL; _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)
@@ -114,7 +114,7 @@
- ..\..\inc;..\..\src;%(AdditionalIncludeDirectories)
+ ..\..\src;..\..\inc;%(AdditionalIncludeDirectories)
WINFSP_SYS_INTERNAL; _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)
@@ -123,7 +123,7 @@
- ..\..\inc;..\..\src;%(AdditionalIncludeDirectories)
+ ..\..\src;..\..\inc;%(AdditionalIncludeDirectories)
WINFSP_SYS_INTERNAL; _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)
diff --git a/ext/tlib/Commit.txt b/ext/tlib/Commit.txt
new file mode 100644
index 00000000..8a2253e6
--- /dev/null
+++ b/ext/tlib/Commit.txt
@@ -0,0 +1,2 @@
+Taked from secfs.core/src/tlib codebase:
+ commit 16296d2ac64a7d532b1c486dd3c44af5865d4851
diff --git a/ext/tlib/callstack.c b/ext/tlib/callstack.c
new file mode 100644
index 00000000..68392116
--- /dev/null
+++ b/ext/tlib/callstack.c
@@ -0,0 +1,75 @@
+/**
+ * @file tlib/callstack.c
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#include
+#include
+#include
+#if defined(_WIN32) || defined(_WIN64)
+#include
+#include
+#pragma comment(lib, "dbghelp.lib")
+#elif defined(__APPLE__) || defined(__linux__)
+#include
+#include
+#else
+#error tlib/callstack.c not implemented for this platform
+#endif
+
+void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack)
+{
+ if (count > TLIB_MAX_SYMRET)
+ count = TLIB_MAX_SYMRET;
+ size_t naddrs = skip + count;
+ if (naddrs > TLIB_MAX_SYMCAP)
+ naddrs = TLIB_MAX_SYMCAP;
+ memset((void *)stack->syms, 0, sizeof stack->syms);
+#if defined(_WIN32) || defined(_WIN64)
+ /* SymInitialize()/SymFromAddr() are not thread-safe. Furthermore SymInitialize() should
+ * not be called more than once per process.
+ */
+ HANDLE hproc = GetCurrentProcess();
+ static SYMBOL_INFO *syminfo = 0;
+ void *addrs[TLIB_MAX_SYMCAP];
+ size_t i = 0;
+ if (0 == syminfo)
+ {
+ syminfo = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO) + TLIB_MAX_SYMLEN);
+ if (0 == syminfo)
+ return;
+ SymInitialize(hproc, 0, TRUE);
+ }
+ memset(syminfo, 0, sizeof(SYMBOL_INFO));
+ syminfo->SizeOfStruct = sizeof(SYMBOL_INFO);
+ syminfo->MaxNameLen = TLIB_MAX_SYMLEN;
+ naddrs = CaptureStackBackTrace(0, naddrs, addrs, 0);
+ for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++)
+ {
+ DWORD64 displacement = 0;
+ if (SymFromAddr(hproc, (DWORD64)*p, &displacement, syminfo))
+ {
+ stack->syms[i] = stack->symbuf[i];
+ strncpy(stack->symbuf[i], syminfo->Name, TLIB_MAX_SYMLEN);
+ stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0';
+ i++;
+ }
+ }
+#elif defined(__APPLE__) || defined(__linux__)
+ void *addrs[TLIB_MAX_SYMCAP];
+ naddrs = backtrace(addrs, naddrs);
+ size_t i = 0;
+ Dl_info syminfo;
+ for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++)
+ {
+ if (dladdr(*p, &syminfo) && 0 != syminfo.dli_sname)
+ {
+ stack->syms[i] = stack->symbuf[i];
+ strncpy(stack->symbuf[i], syminfo.dli_sname, TLIB_MAX_SYMLEN);
+ stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0';
+ i++;
+ }
+ }
+#endif
+}
diff --git a/ext/tlib/callstack.h b/ext/tlib/callstack.h
new file mode 100644
index 00000000..78f53e13
--- /dev/null
+++ b/ext/tlib/callstack.h
@@ -0,0 +1,26 @@
+/**
+ * @file tlib/callstack.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#ifndef TLIB_CALLSTACK_H_INCLUDED
+#define TLIB_CALLSTACK_H_INCLUDED
+
+#include
+
+enum
+{
+ TLIB_MAX_SYMLEN = 63,
+ TLIB_MAX_SYMRET = 8,
+ TLIB_MAX_SYMCAP = 62, /* max number of frames to capture (Windows restriction) */
+};
+
+struct tlib_callstack_s
+{
+ const char *syms[TLIB_MAX_SYMRET + 1];
+ char symbuf[TLIB_MAX_SYMRET][TLIB_MAX_SYMLEN + 1];
+};
+void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack);
+
+#endif
diff --git a/ext/tlib/injected/allfunc.h b/ext/tlib/injected/allfunc.h
new file mode 100644
index 00000000..bf6a4aa3
--- /dev/null
+++ b/ext/tlib/injected/allfunc.h
@@ -0,0 +1,13 @@
+/**
+ * @file tlib/injected/allfunc.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#ifndef TLIB_INJECTED_ALLFUNC_H_INCLUDED
+#define TLIB_INJECTED_ALLFUNC_H_INCLUDED
+
+#include
+#include
+
+#endif
diff --git a/ext/tlib/injected/curlfunc.c b/ext/tlib/injected/curlfunc.c
new file mode 100644
index 00000000..4830897d
--- /dev/null
+++ b/ext/tlib/injected/curlfunc.c
@@ -0,0 +1,35 @@
+/**
+ * @file tlib/injected/curlfunc.c
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#include
+#define TLIB_INJECTIONS_ENABLED
+#include
+
+#undef curl_easy_init
+#undef curl_multi_init
+#undef curl_multi_add_handle
+#undef curl_multi_perform
+
+CURL *tlib_curl_easy_init(void)
+{
+ TLIB_INJECT("curl_easy_init", return 0);
+ return curl_easy_init();
+}
+CURLM *tlib_curl_multi_init(void)
+{
+ TLIB_INJECT("curl_multi_init", return 0);
+ return curl_multi_init();
+}
+CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh)
+{
+ TLIB_INJECT("curl_multi_add_handle", return CURLM_INTERNAL_ERROR);
+ return curl_multi_add_handle(mh, eh);
+}
+CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles)
+{
+ TLIB_INJECT("curl_multi_perform", return CURLM_INTERNAL_ERROR);
+ return curl_multi_perform(mh, running_handles);
+}
diff --git a/ext/tlib/injected/curlfunc.h b/ext/tlib/injected/curlfunc.h
new file mode 100644
index 00000000..a8435e1e
--- /dev/null
+++ b/ext/tlib/injected/curlfunc.h
@@ -0,0 +1,24 @@
+/**
+ * @file tlib/injected/curlfunc.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#ifndef TLIB_INJECTED_CURLFUNC_H_INCLUDED
+#define TLIB_INJECTED_CURLFUNC_H_INCLUDED
+
+#include
+
+#if defined(TLIB_INJECTIONS_ENABLED)
+#define curl_easy_init() tlib_curl_easy_init()
+#define curl_multi_init() tlib_curl_multi_init()
+#define curl_multi_add_handle(mh, eh) tlib_curl_multi_add_handle(mh, eh)
+#define curl_multi_perform(mh, rh) tlib_curl_multi_perform(mh, rh)
+#endif
+
+CURL *tlib_curl_easy_init(void);
+CURLM *tlib_curl_multi_init(void);
+CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh);
+CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles);
+
+#endif
diff --git a/ext/tlib/injected/stdfunc.c b/ext/tlib/injected/stdfunc.c
new file mode 100644
index 00000000..ce64c538
--- /dev/null
+++ b/ext/tlib/injected/stdfunc.c
@@ -0,0 +1,29 @@
+/**
+ * @file tlib/injected/stdfunc.c
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#include
+#define TLIB_INJECTIONS_ENABLED
+#include
+
+#undef calloc
+#undef malloc
+#undef realloc
+
+void *tlib_calloc(size_t count, size_t size)
+{
+ TLIB_INJECT("calloc", return 0);
+ return calloc(count, size);
+}
+void *tlib_malloc(size_t size)
+{
+ TLIB_INJECT("malloc", return 0);
+ return malloc(size);
+}
+void *tlib_realloc(void *ptr, size_t size)
+{
+ TLIB_INJECT("realloc", return 0);
+ return realloc(ptr, size);
+}
diff --git a/ext/tlib/injected/stdfunc.h b/ext/tlib/injected/stdfunc.h
new file mode 100644
index 00000000..8bbf93e1
--- /dev/null
+++ b/ext/tlib/injected/stdfunc.h
@@ -0,0 +1,23 @@
+/**
+ * @file tlib/injected/stdfunc.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#ifndef TLIB_INJECTED_STDFUNC_H_INCLUDED
+#define TLIB_INJECTED_STDFUNC_H_INCLUDED
+
+#include
+#include
+
+#if defined(TLIB_INJECTIONS_ENABLED)
+#define calloc(count, size) tlib_calloc(count, size)
+#define malloc(size) tlib_malloc(size)
+#define realloc(ptr, size) tlib_realloc(ptr, size)
+#endif
+
+void *tlib_calloc(size_t count, size_t size);
+void *tlib_malloc(size_t size);
+void *tlib_realloc(void *ptr, size_t size);
+
+#endif
diff --git a/ext/tlib/injection.c b/ext/tlib/injection.c
new file mode 100644
index 00000000..11e5adba
--- /dev/null
+++ b/ext/tlib/injection.c
@@ -0,0 +1,144 @@
+/**
+ * @file tlib/injection.c
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#undef NDEBUG
+#include
+
+#define NBUCKETS 256
+struct injection_cond_s
+{
+ struct injection_cond_s *cnext;
+ char *sym;
+ unsigned trigger, count;
+};
+struct injection_entry_s
+{
+ struct injection_entry_s *hnext;
+ char *name;
+ struct injection_cond_s *clist;
+};
+struct injection_htab_s
+{
+ struct injection_entry_s **buckets;
+};
+
+static inline size_t hash_chars(const char *s, size_t length)
+{
+ /* djb2: see http://www.cse.yorku.ca/~oz/hash.html */
+ size_t h = 5381;
+ for (const char *t = s + length; t > s; ++s)
+ h = 33 * h + *s;
+ return h;
+}
+static struct injection_htab_s *injection_htab()
+{
+ static struct injection_htab_s *htab;
+ if (0 == htab)
+ {
+ htab = calloc(1, sizeof *htab);
+ assert(0 != htab);
+ htab->buckets = calloc(NBUCKETS, sizeof(struct injection_entry_s *));
+ }
+ return htab;
+}
+static struct injection_entry_s *injection_lookup(const char *name)
+{
+ struct injection_htab_s *htab = injection_htab();
+ size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1);
+ for (struct injection_entry_s *entry = htab->buckets[i]; entry; entry = entry->hnext)
+ if (0 == strcmp(entry->name, name))
+ return entry;
+ return 0;
+}
+static struct injection_entry_s *injection_insert(const char *name)
+{
+ struct injection_htab_s *htab = injection_htab();
+ size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1);
+ struct injection_entry_s *entry = calloc(1, sizeof *entry);
+ assert(0 != entry);
+ entry->name = strdup(name);
+ entry->hnext = htab->buckets[i];
+ htab->buckets[i] = entry;
+ return entry;
+}
+static struct injection_cond_s *injection_cond_get(struct injection_entry_s *entry, const char **syms)
+{
+ struct injection_cond_s *deinjection_centry = 0;
+ for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext)
+ if ('*' == centry->sym[0] && '\0' == centry->sym[1])
+ deinjection_centry = centry;
+ else
+ {
+ for (const char *sym; 0 != (sym = *syms); syms++)
+ if (0 == strcmp(centry->sym, sym))
+ return centry;
+ }
+ return deinjection_centry;
+}
+static void injection_cond_set(struct injection_entry_s *entry, const char *sym, unsigned trigger)
+{
+ for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext)
+ if (0 == strcmp(centry->sym, sym))
+ {
+ centry->trigger = trigger;
+ return;
+ }
+ struct injection_cond_s *centry = calloc(1, sizeof *centry);
+ assert(0 != centry);
+ centry->sym = strdup(sym);
+ centry->trigger = trigger;
+ centry->cnext = entry->clist;
+ entry->clist = centry;
+}
+static void injection_cond_remove(struct injection_entry_s *entry, const char *sym)
+{
+ struct injection_cond_s **p = &entry->clist;
+ for (; *p; p = &(*p)->cnext)
+ if (0 == strcmp((*p)->sym, sym))
+ break;
+ if (*p) /* did we find the condition? */
+ {
+ struct injection_cond_s *q = *p;
+ *p = q->cnext;
+ free(q->sym);
+ free(q);
+ }
+}
+
+void *tlib_injection(const char *name)
+{
+ struct injection_entry_s *entry = injection_lookup(name);
+ if (0 == entry)
+ entry = injection_insert(name);
+ return entry;
+}
+int tlib_injection_trace(void *injection)
+{
+ if (0 == ((struct injection_entry_s *)injection)->clist)
+ return 0;
+ struct tlib_callstack_s stack;
+ tlib_callstack(2, TLIB_MAX_SYMRET, &stack);
+ struct injection_cond_s *centry = injection_cond_get(injection, stack.syms);
+ if (0 == centry)
+ return 0;
+ return centry->count++ == centry->trigger || ~0 == centry->trigger;
+}
+void tlib_injection_enable(const char *name, const char *sym, unsigned trigger)
+{
+ struct injection_entry_s *entry = tlib_injection(name);
+ injection_cond_set(entry, sym, trigger);
+}
+void tlib_injection_disable(const char *name, const char *sym)
+{
+ struct injection_entry_s *entry = tlib_injection(name);
+ injection_cond_remove(entry, sym);
+}
diff --git a/ext/tlib/injection.h b/ext/tlib/injection.h
new file mode 100644
index 00000000..df1cd6c7
--- /dev/null
+++ b/ext/tlib/injection.h
@@ -0,0 +1,30 @@
+/**
+ * @file tlib/injection.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+/* NOTE: This header may usefully be included multiple times.
+ * The TLIB_INJECT() macro will be redefined based on whether
+ * TLIB_INJECTIONS_ENABLED is defined.
+ */
+
+#undef TLIB_INJECT
+#if defined(TLIB_INJECTIONS_ENABLED)
+#define TLIB_INJECT(name, stmt) \
+ do\
+ {\
+ static void *injection = 0;\
+ if (0 == injection)\
+ injection = tlib_injection(name);\
+ if (tlib_injection_trace(injection))\
+ stmt;\
+ } while (0)
+#else
+#define TLIB_INJECT(name, stmt) do {} while (0)
+#endif
+
+void *tlib_injection(const char *name);
+int tlib_injection_trace(void *injection);
+void tlib_injection_enable(const char *name, const char *sym, unsigned trigger);
+void tlib_injection_disable(const char *name, const char *sym);
diff --git a/ext/tlib/testsuite.c b/ext/tlib/testsuite.c
new file mode 100644
index 00000000..5d9993cc
--- /dev/null
+++ b/ext/tlib/testsuite.c
@@ -0,0 +1,202 @@
+/**
+ * @file tlib/testsuite.c
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct test
+{
+ char name[64];
+ void (*fn)(void);
+ int optional;
+ struct test *next;
+};
+static struct test test_suite_sentinel = { .next = &test_suite_sentinel };
+static struct test *test_suite_tail = &test_suite_sentinel;
+static struct test test_sentinel = { .next = &test_sentinel };
+static struct test *test_tail = &test_sentinel;
+static void add_test_to_list(const char *name, void (*fn)(void), int optional, struct test **tail)
+{
+ struct test *test = calloc(1, sizeof *test);
+ strncpy(test->name, name, sizeof test->name - 1);
+ test->name[sizeof test->name - 1] = '\0';
+ test->fn = fn;
+ test->optional = optional;
+ test->next = (*tail)->next;
+ (*tail)->next = test;
+ (*tail) = test;
+}
+void tlib_add_test_suite(const char *name, void (*fn)(void))
+{
+ add_test_to_list(name, fn, 0, &test_suite_tail);
+}
+void tlib_add_test(const char *name, void (*fn)(void))
+{
+ add_test_to_list(name, fn, 0, &test_tail);
+}
+void tlib_add_test_opt(const char *name, void (*fn)(void))
+{
+ add_test_to_list(name, fn, 1, &test_tail);
+}
+
+static FILE *tlib_out, *tlib_err;
+static jmp_buf test_jmp_buf, *test_jmp;
+static char assert_buf[256];
+static void test_printf(const char *fmt, ...);
+static double run_test(struct test *test)
+{
+ time_t t0 = time(0);
+ test->fn();
+ time_t t1 = time(0);
+ return difftime(t1, t0);
+}
+static void do_test_default(struct test *test, int testno)
+{
+ if (0 != test)
+ {
+ snprintf(assert_buf, sizeof assert_buf, "KO\n ");
+ char dispname[39 + 1];
+ size_t displen = strlen(test->name);
+ if (displen > sizeof dispname - 1)
+ displen = sizeof dispname - 1;
+ memcpy(dispname, test->name, displen);
+ memset(dispname + displen, '.', sizeof dispname - 1 - displen);
+ dispname[sizeof dispname - 1] = '\0';
+ test_printf("%s ", dispname);
+ double d = run_test(test);
+ test_printf("OK %.0fs\n", d);
+ }
+ else
+ test_printf("--- COMPLETE ---\n");
+}
+static void do_test_list(struct test *test, int testno)
+{
+ if (0 != test)
+ test_printf("%s\n", test->name);
+}
+static void do_test_tap(struct test *test, int testno)
+{
+ if (0 != test)
+ {
+ snprintf(assert_buf, sizeof assert_buf, "not ok %d %s\n# ", testno + 1, test->name);
+ run_test(test);
+ test_printf("ok %d %s\n", testno + 1, test->name);
+ }
+ else
+ test_printf("1..%d\n", testno);
+}
+static void test_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ FILE *f = tlib_out ? tlib_out : stdout;
+ vfprintf(f, fmt, ap);
+ fflush(f);
+ va_end(ap);
+}
+void tlib_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ FILE *f = tlib_err ? tlib_err : stderr;
+ vfprintf(f, fmt, ap);
+ fflush(f);
+ va_end(ap);
+}
+void tlib_run_tests(int argc, char *argv[])
+{
+ argc--; argv++;
+ void (*do_test)(struct test *, int) = do_test_default;
+ int match_any = 1, no_abort = 0;
+ unsigned long repeat = 1;
+ for (char **ap = argv, **aendp = ap + argc; aendp > ap; ap++)
+ {
+ const char *a = *ap;
+ if ('-' == a[0])
+ {
+ if (0 == strcmp("--list", a))
+ do_test = do_test_list;
+ else if (0 == strcmp("--tap", a))
+ do_test = do_test_tap;
+ else if (0 == strcmp("--no-abort", a))
+ no_abort = 1;
+ else if (0 == strcmp("--repeat-forever", a))
+ repeat = ULONG_MAX;
+ }
+ else
+ match_any = 0;
+ }
+ for (struct test *test = test_suite_tail->next->next; 0 != test->fn; test = test->next)
+ test->fn();
+ while (repeat--)
+ {
+ int testno = 0;
+ for (struct test *test = test_tail->next->next; 0 != test->fn; test = test->next)
+ {
+ int match_arg = match_any && !test->optional;
+ for (char **ap = argv, **aendp = ap + argc; aendp > ap; aendp--)
+ {
+ const char *a = aendp[-1];
+ int sign = a[0];
+ if ('+' == sign)
+ a++;
+ else if ('-' == sign)
+ {
+ if ('-' == a[1])
+ continue;
+ a++;
+ }
+ size_t l = strlen(a);
+ if (0 == (0 < l && '*' == a[l - 1] ?
+ strncmp(test->name, a, l - 1) : strcmp(test->name, a)))
+ {
+ if ('+' == sign)
+ match_arg = 1;
+ else if ('-' == sign)
+ match_arg = 0;
+ else
+ match_arg = !test->optional;
+ break;
+ }
+ }
+ if (!match_arg)
+ continue;
+ assert_buf[0] = '\0';
+ if (no_abort)
+ {
+ test_jmp = &test_jmp_buf;
+ if (0 == setjmp(*test_jmp))
+ do_test(test, testno);
+ test_jmp = 0;
+ }
+ else
+ do_test(test, testno);
+ testno++;
+ }
+ do_test(0, testno);
+ }
+}
+void tlib__assert(const char *func, const char *file, int line, const char *expr)
+{
+#if defined(_WIN64) || defined(_WIN32)
+ const char *p = strrchr(file, '\\');
+#else
+ const char *p = strrchr(file, '/');
+#endif
+ file = 0 != p ? p + 1 : file;
+ if (0 == func)
+ test_printf("%sASSERT(%s) failed at: %s:%d\n", assert_buf, expr, file, line);
+ else
+ test_printf("%sASSERT(%s) failed at %s:%d:%s\n", assert_buf, expr, file, line, func);
+ if (0 != test_jmp)
+ longjmp(*test_jmp, 1);
+}
diff --git a/ext/tlib/testsuite.h b/ext/tlib/testsuite.h
new file mode 100644
index 00000000..db8d2ac7
--- /dev/null
+++ b/ext/tlib/testsuite.h
@@ -0,0 +1,112 @@
+/**
+ * @file tlib/testsuite.h
+ *
+ * @copyright 2014-2015 Bill Zissimopoulos
+ */
+
+#ifndef TLIB_TESTSUITE_H_INCLUDED
+#define TLIB_TESTSUITE_H_INCLUDED
+
+#include
+
+/**
+ * Assert macro
+ *
+ * This macro works similarly to the Standard C assert macro, except for the following differences:
+ *
+ *
+ * The macro always checks the specified expression regardless of the NDEBUG macro.
+ * The macro aborts the execution of the current test, but not necessarily the execution of the
+ * whole testsuite.
+ *
+ */
+#define ASSERT(expr)\
+ (!(expr) ? (tlib__assert(__func__, __FILE__, __LINE__, #expr), abort()) : (void)0)
+
+/**
+ * Register a test suite for execution.
+ *
+ * Test suites are simple functions with prototype void testsuite()
. When executed
+ * they register individual test cases for execution.
+ */
+#define TESTSUITE(fn)\
+ do\
+ {\
+ void fn(void);\
+ tlib_add_test_suite(#fn, fn);\
+ } while (0)
+
+/**
+ * Register a test case for execution.
+ *
+ * Test cases are simple functions with prototype void testcase()
.
+ */
+#define TEST(fn)\
+ do\
+ {\
+ void fn(void);\
+ tlib_add_test(#fn, fn);\
+ } while (0)
+
+/**
+ * Register an optional test case for execution.
+ *
+ * Test cases are simple functions with prototype void testcase()
.
+ * Optional tests are not executed by default.
+ */
+#define TEST_OPT(fn)\
+ do\
+ {\
+ void fn(void);\
+ tlib_add_test_opt(#fn, fn);\
+ } while (0)
+
+void tlib_add_test_suite(const char *name, void (*fn)(void));
+void tlib_add_test(const char *name, void (*fn)(void));
+void tlib_add_test_opt(const char *name, void (*fn)(void));
+
+/**
+ * Printf function.
+ *
+ * Use this to produce output in the appropriate tlib file stream. This function uses the local
+ * printf facilities and understands the same format strings.
+ */
+#if defined(__GNUC__)
+void tlib_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+#else
+void tlib_printf(const char *fmt, ...);
+#endif
+/**
+ * Run tests.
+ *
+ * This function will first execute all registered test suites, thus giving them the chance to
+ * register any test cases. It will then execute all registered test cases according to the
+ * command line arguments passed in argv. The command line syntax is a follows:
+ *
+ * Usage: testprog [--list][[--tap][--no-abort][--repeat-forever] [[+-]TESTNAME...]
+ *
+ *
+ * --list - list tests only
+ * --tap - produce output in TAP format
+ * --no-abort - do not abort all tests when an ASSERT fails
+ * (only the current test is aborted)
+ * --repeat-forever - repeat tests forever
+ *
+ *
+ * By default all test cases are executed unless specific test cases are named. By default optional
+ * test cases are not executed. To execute a specific test case specify its TESTNAME; if it is an
+ * optional test case specify +TESTNAME. To excluse a test case specify -TESTNAME.
+ *
+ * TESTNAME may also contain a single asterisk at the end; for example, mytest* will match all test
+ * cases that have names starting with "mytest".
+ *
+ * @param argc
+ * Argument count.
+ * @param argv
+ * Argument vector.
+ */
+void tlib_run_tests(int argc, char *argv[]);
+
+void tlib__assert(const char *func, const char *file, int line, const char *expr);
+
+#endif
diff --git a/tst/winfsp-tests/mount-test.c b/tst/winfsp-tests/mount-test.c
new file mode 100644
index 00000000..a0852ac9
--- /dev/null
+++ b/tst/winfsp-tests/mount-test.c
@@ -0,0 +1,11 @@
+#include
+#include
+
+void mount_test(void)
+{
+}
+
+void mount_tests(void)
+{
+ TEST(mount_test);
+}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
new file mode 100644
index 00000000..5169683e
--- /dev/null
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -0,0 +1,8 @@
+#include
+
+int main(int argc, char *argv[])
+{
+ TESTSUITE(mount_tests);
+ tlib_run_tests(argc, argv);
+ return 0;
+}