diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj
index 2e906314..c1d7825e 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj
+++ b/build/VStudio/testing/winfsp-tests.vcxproj
@@ -189,6 +189,7 @@
+
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters
index 217f99ff..493a812a 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj.filters
+++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters
@@ -91,6 +91,9 @@
Source
+
+ Source
+
diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c
index 13c9eb3c..0a1f19b6 100644
--- a/src/dll/fuse/fuse.c
+++ b/src/dll/fuse/fuse.c
@@ -223,17 +223,29 @@ FSP_FUSE_API int fsp_fuse_is_lib_option(struct fsp_fuse_env *env,
return fsp_fuse_opt_match(env, fsp_fuse_core_opts, opt);
}
-static void fsp_fuse_cleanup(struct fuse *f);
+static INIT_ONCE fsp_fuse_svconce = INIT_ONCE_STATIC_INIT;
+static HANDLE fsp_fuse_svcthread;
-static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
+static DWORD WINAPI fsp_fuse_svcmain(PVOID Context)
+{
+ return FspServiceRun(FspDiagIdent(), 0, 0, 0);
+}
+
+static BOOL WINAPI fsp_fuse_svcinit(
+ PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
+{
+ fsp_fuse_svcthread = CreateThread(0, 0, fsp_fuse_svcmain, 0, 0, 0);
+ return TRUE;
+}
+
+static void fsp_fuse_loop_cleanup(struct fuse *f);
+
+static NTSTATUS fsp_fuse_loop_start(struct fuse *f)
{
- struct fuse *f = Service->UserContext;
struct fuse_context *context;
struct fuse_conn_info conn;
NTSTATUS Result;
- f->Service = Service;
-
context = fsp_fuse_get_context(f->env);
if (0 == context)
{
@@ -394,23 +406,19 @@ static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
return STATUS_SUCCESS;
fail:
- fsp_fuse_cleanup(f);
+ fsp_fuse_loop_cleanup(f);
return Result;
}
-static NTSTATUS fsp_fuse_svcstop(FSP_SERVICE *Service)
+static void fsp_fuse_loop_stop(struct fuse *f)
{
- struct fuse *f = Service->UserContext;
-
FspFileSystemStopDispatcher(f->FileSystem);
- fsp_fuse_cleanup(f);
-
- return STATUS_SUCCESS;
+ fsp_fuse_loop_cleanup(f);
}
-static void fsp_fuse_cleanup(struct fuse *f)
+static void fsp_fuse_loop_cleanup(struct fuse *f)
{
if (0 != f->FileSystem)
{
@@ -424,8 +432,40 @@ static void fsp_fuse_cleanup(struct fuse *f)
f->ops.destroy(f->data);
f->fsinit = FALSE;
}
+}
- f->Service = 0;
+FSP_FUSE_API NTSTATUS fsp_fuse_loop_internal(struct fuse *f)
+{
+ HANDLE WaitObjects[2];
+ DWORD WaitResult;
+ NTSTATUS Result;
+
+ Result = fsp_fuse_loop_start(f);
+ if (!NT_SUCCESS(Result))
+ {
+ /* emulate WinFsp-FUSE v1.3 behavior! */
+ FspServiceLog(EVENTLOG_ERROR_TYPE,
+ L"The service %s has failed to start (Status=%lx).", FspDiagIdent(), Result);
+ return Result;
+ }
+
+ InitOnceExecuteOnce(&fsp_fuse_svconce, fsp_fuse_svcinit, 0, 0);
+ if (0 == fsp_fuse_svcthread)
+ {
+ fsp_fuse_loop_stop(f);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* if either the service thread dies or our event gets signaled, stop the loop */
+ WaitObjects[0] = fsp_fuse_svcthread;
+ WaitObjects[1] = f->LoopEvent;
+ WaitResult = WaitForMultipleObjects(2, WaitObjects, FALSE, INFINITE);
+ if (WAIT_OBJECT_0 != WaitResult && WAIT_OBJECT_0 + 1 != WaitResult)
+ Result = FspNtStatusFromWin32(GetLastError());
+
+ fsp_fuse_loop_stop(f);
+
+ return Result;
}
static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
@@ -638,6 +678,10 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
goto fail;
memcpy(f->MountPoint, ch->MountPoint, Size);
+ f->LoopEvent = CreateEventW(0, TRUE, FALSE, 0);
+ if (0 == f->LoopEvent)
+ goto fail;
+
Result = FspFileSystemPreflight(
f->VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME,
'*' != f->MountPoint[0] || '\0' != f->MountPoint[1] ? f->MountPoint : 0);
@@ -685,7 +729,10 @@ fail:
FSP_FUSE_API void fsp_fuse_destroy(struct fsp_fuse_env *env,
struct fuse *f)
{
- fsp_fuse_cleanup(f);
+ fsp_fuse_loop_cleanup(f);
+
+ if (0 != f->LoopEvent)
+ CloseHandle(f->LoopEvent);
fsp_fuse_obj_free(f->MountPoint);
@@ -696,23 +743,20 @@ FSP_FUSE_API int fsp_fuse_loop(struct fsp_fuse_env *env,
struct fuse *f)
{
f->OpGuardStrategy = FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE;
- return 0 == FspServiceRunEx(FspDiagIdent(), fsp_fuse_svcstart, fsp_fuse_svcstop, 0, f) ?
- 0 : -1;
+ return NT_SUCCESS(fsp_fuse_loop_internal(f)) ? 0 : -1;
}
FSP_FUSE_API int fsp_fuse_loop_mt(struct fsp_fuse_env *env,
struct fuse *f)
{
f->OpGuardStrategy = FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE;
- return 0 == FspServiceRunEx(FspDiagIdent(), fsp_fuse_svcstart, fsp_fuse_svcstop, 0, f) ?
- 0 : -1;
+ return NT_SUCCESS(fsp_fuse_loop_internal(f)) ? 0 : -1;
}
FSP_FUSE_API void fsp_fuse_exit(struct fsp_fuse_env *env,
struct fuse *f)
{
- if (0 != f->Service)
- FspServiceStop(f->Service);
+ SetEvent(f->LoopEvent);
f->exited = 1;
}
diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h
index e381e01a..82a36b4d 100644
--- a/src/dll/fuse/library.h
+++ b/src/dll/fuse/library.h
@@ -61,8 +61,8 @@ struct fuse
UINT16 VolumeLabelLength;
WCHAR VolumeLabel[sizeof ((FSP_FSCTL_VOLUME_INFO *)0)->VolumeLabel / sizeof(WCHAR)];
PWSTR MountPoint;
+ HANDLE LoopEvent;
FSP_FILE_SYSTEM *FileSystem;
- FSP_SERVICE *Service; /* weak */
volatile int exited;
struct fuse3 *fuse3;
};
diff --git a/tst/winfsp-tests/fuse-test.c b/tst/winfsp-tests/fuse-test.c
new file mode 100644
index 00000000..272a39b8
--- /dev/null
+++ b/tst/winfsp-tests/fuse-test.c
@@ -0,0 +1,114 @@
+/**
+ * @file fuse-test.c
+ *
+ * @copyright 2015-2018 Bill Zissimopoulos
+ */
+/*
+ * This file is part of WinFsp.
+ *
+ * You can redistribute it and/or modify it under the terms of the GNU
+ * General Public License version 3 as published by the Free Software
+ * Foundation.
+ *
+ * Licensees holding a valid commercial license may use this file in
+ * accordance with the commercial license agreement provided with the
+ * software.
+ */
+
+#include
+#include
+#include
+
+#include "winfsp-tests.h"
+
+static unsigned __stdcall fuse_tests_thread(void *f)
+{
+ return fuse_loop_mt(f);
+}
+
+static void fuse_sequential_test(void)
+{
+ static struct fuse_operations ops;
+ char *argv[] = { "UNKNOWN" };
+ struct fuse_args args = FUSE_ARGS_INIT(1, argv);
+ struct fuse_chan *ch[10] = { 0 };
+ struct fuse *f[10];
+ HANDLE Thread[10];
+ DWORD ExitCode;
+
+ for (int i = 0; sizeof(f) / sizeof(f[0]) > i; i++)
+ {
+ ch[i] = fuse_mount("*", &args);
+ ASSERT(0 != ch[i]);
+
+ f[i] = fuse_new(ch[i], &args, &ops, sizeof ops, 0);
+ ASSERT(0 != f[i]);
+
+ Thread[i] = (HANDLE)_beginthreadex(0, 0, fuse_tests_thread, f[i], 0, 0);
+ ASSERT(0 != Thread[i]);
+
+ if (0 != i)
+ Sleep(i * 20);
+
+ fuse_exit(f[i]);
+
+ WaitForSingleObject(Thread[i], INFINITE);
+ GetExitCodeThread(Thread[i], &ExitCode);
+ CloseHandle(Thread[i]);
+
+ fuse_destroy(f[i]);
+
+ fuse_unmount("*", ch[i]);
+
+ ASSERT(0 == ExitCode);
+ }
+}
+
+static void fuse_parallel_test(void)
+{
+ static struct fuse_operations ops;
+ char *argv[] = { "UNKNOWN" };
+ struct fuse_args args = FUSE_ARGS_INIT(1, argv);
+ struct fuse_chan *ch[10] = { 0 };
+ struct fuse *f[10];
+ HANDLE Thread[10];
+ DWORD ExitCode;
+
+ for (int i = 0; sizeof(f) / sizeof(f[0]) > i; i++)
+ {
+ ch[i] = fuse_mount("*", &args);
+ ASSERT(0 != ch[i]);
+
+ f[i] = fuse_new(ch[i], &args, &ops, sizeof ops, 0);
+ ASSERT(0 != f[i]);
+
+ Thread[i] = (HANDLE)_beginthreadex(0, 0, fuse_tests_thread, f[i], 0, 0);
+ ASSERT(0 != Thread[i]);
+ }
+
+ Sleep(1000);
+
+ for (int i = 0; sizeof(f) / sizeof(f[0]) > i; i++)
+ {
+ fuse_exit(f[i]);
+
+ WaitForSingleObject(Thread[i], INFINITE);
+ GetExitCodeThread(Thread[i], &ExitCode);
+ CloseHandle(Thread[i]);
+
+ fuse_destroy(f[i]);
+
+ fuse_unmount("*", ch[i]);
+
+ ASSERT(0 == ExitCode);
+ }
+}
+
+void fuse_tests(void)
+{
+ if (OptExternal)
+ return;
+
+ TEST_OPT(fuse_sequential_test);
+ TEST_OPT(fuse_parallel_test);
+}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index 8d4a4437..d1547878 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -182,6 +182,7 @@ LONG WINAPI UnhandledExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
int main(int argc, char *argv[])
{
TESTSUITE(fuse_opt_tests);
+ TESTSUITE(fuse_tests);
TESTSUITE(posix_tests);
TESTSUITE(eventlog_tests);
TESTSUITE(path_tests);