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);