diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj index 2d3b60bf..bd971532 100644 --- a/build/VStudio/winfsp_dll.vcxproj +++ b/build/VStudio/winfsp_dll.vcxproj @@ -45,6 +45,7 @@ + diff --git a/build/VStudio/winfsp_dll.vcxproj.filters b/build/VStudio/winfsp_dll.vcxproj.filters index 42c785ce..ab46ebdf 100644 --- a/build/VStudio/winfsp_dll.vcxproj.filters +++ b/build/VStudio/winfsp_dll.vcxproj.filters @@ -148,6 +148,9 @@ Source\fuse3 + + Source\fuse + diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 0a1f19b6..86ce2e48 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -17,9 +17,6 @@ #include -#define FSP_FUSE_SECTORSIZE_MIN 512 -#define FSP_FUSE_SECTORSIZE_MAX 4096 - struct fuse_chan { PWSTR MountPoint; @@ -223,251 +220,6 @@ 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 INIT_ONCE fsp_fuse_svconce = INIT_ONCE_STATIC_INIT; -static HANDLE fsp_fuse_svcthread; - -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_context *context; - struct fuse_conn_info conn; - NTSTATUS Result; - - context = fsp_fuse_get_context(f->env); - if (0 == context) - { - Result = STATUS_INSUFFICIENT_RESOURCES; - goto fail; - } - context->fuse = f; - context->private_data = f->data; - context->uid = -1; - context->gid = -1; - context->pid = -1; - - memset(&conn, 0, sizeof conn); - conn.proto_major = 7; /* pretend that we are FUSE kernel protocol 7.12 */ - conn.proto_minor = 12; /* which was current at the time of FUSE 2.8 */ - conn.async_read = 1; - conn.max_write = UINT_MAX; - conn.capable = - FUSE_CAP_ASYNC_READ | - //FUSE_CAP_POSIX_LOCKS | /* WinFsp handles locking in the FSD currently */ - //FUSE_CAP_ATOMIC_O_TRUNC | /* due to Windows/WinFsp design, no support */ - //FUSE_CAP_EXPORT_SUPPORT | /* not needed in Windows/WinFsp */ - FUSE_CAP_BIG_WRITES | - FUSE_CAP_DONT_MASK | - FSP_FUSE_CAP_READDIR_PLUS | - FSP_FUSE_CAP_READ_ONLY | - FSP_FUSE_CAP_STAT_EX | - FSP_FUSE_CAP_CASE_INSENSITIVE; - if (0 != f->ops.init) - { - context->private_data = f->data = f->ops.init(&conn); - f->VolumeParams.ReadOnlyVolume = 0 != (conn.want & FSP_FUSE_CAP_READ_ONLY); - f->VolumeParams.CaseSensitiveSearch = 0 == (conn.want & FSP_FUSE_CAP_CASE_INSENSITIVE); - if (!f->VolumeParams.CaseSensitiveSearch) - /* - * Disable GetDirInfoByName when file system is case-insensitive. - * The reason is that Windows always sends us queries with uppercase - * file names in GetDirInfoByName and we have no way in FUSE to normalize - * those file names when embedding them in FSP_FSCTL_DIR_INFO. - */ - f->VolumeParams.PassQueryDirectoryFileName = FALSE; - f->conn_want = conn.want; - } - f->fsinit = TRUE; - if (0 != f->ops.statfs) - { - struct fuse_statvfs stbuf; - int err; - - memset(&stbuf, 0, sizeof stbuf); - err = f->ops.statfs("/", &stbuf); - if (0 != err) - { - Result = fsp_fuse_ntstatus_from_errno(f->env, err); - goto fail; - } - - if (0 == f->VolumeParams.SectorSize && 0 != stbuf.f_frsize) - f->VolumeParams.SectorSize = (UINT16)stbuf.f_frsize; -#if 0 - if (0 == f->VolumeParams.SectorsPerAllocationUnit && 0 != stbuf.f_frsize) - f->VolumeParams.SectorsPerAllocationUnit = (UINT16)(stbuf.f_bsize / stbuf.f_frsize); -#endif - if (0 == f->VolumeParams.MaxComponentLength) - f->VolumeParams.MaxComponentLength = (UINT16)stbuf.f_namemax; - } - if (0 != f->ops.getattr) - { - struct fuse_stat_ex stbuf; - int err; - - memset(&stbuf, 0, sizeof stbuf); - err = f->ops.getattr("/", (void *)&stbuf); - if (0 != err) - { - Result = fsp_fuse_ntstatus_from_errno(f->env, err); - goto fail; - } - - if (0 == f->VolumeParams.VolumeCreationTime) - { - if (0 != stbuf.st_birthtim.tv_sec) - FspPosixUnixTimeToFileTime((void *)&stbuf.st_birthtim, - &f->VolumeParams.VolumeCreationTime); - else - if (0 != stbuf.st_ctim.tv_sec) - FspPosixUnixTimeToFileTime((void *)&stbuf.st_ctim, - &f->VolumeParams.VolumeCreationTime); - } - } - if (0 != f->ops.readlink) - { - char buf[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)]; - int err; - - /* this should always fail with ENOSYS or EINVAL */ - err = f->ops.readlink("/", buf, sizeof buf); - f->has_symlinks = -ENOSYS_(f->env) != err; - } - - /* the FSD does not currently limit these VolumeParams fields; do so here! */ - if (f->VolumeParams.SectorSize < FSP_FUSE_SECTORSIZE_MIN || - f->VolumeParams.SectorSize > FSP_FUSE_SECTORSIZE_MAX) - f->VolumeParams.SectorSize = FSP_FUSE_SECTORSIZE_MAX; - if (f->VolumeParams.SectorsPerAllocationUnit == 0) - f->VolumeParams.SectorsPerAllocationUnit = 1; - if (f->VolumeParams.MaxComponentLength > 255) - f->VolumeParams.MaxComponentLength = 255; - - if (0 == f->VolumeParams.VolumeCreationTime) - { - FILETIME FileTime; - GetSystemTimeAsFileTime(&FileTime); - f->VolumeParams.VolumeCreationTime = *(PUINT64)&FileTime; - } - if (0 == f->VolumeParams.VolumeSerialNumber) - f->VolumeParams.VolumeSerialNumber = - ((PLARGE_INTEGER)&f->VolumeParams.VolumeCreationTime)->HighPart ^ - ((PLARGE_INTEGER)&f->VolumeParams.VolumeCreationTime)->LowPart; - - Result = FspFileSystemCreate( - f->VolumeParams.Prefix[0] ? - L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, - &f->VolumeParams, &fsp_fuse_intf, - &f->FileSystem); - if (!NT_SUCCESS(Result)) - { - FspServiceLog(EVENTLOG_ERROR_TYPE, - L"Cannot create " FSP_FUSE_LIBRARY_NAME " file system."); - goto fail; - } - - f->FileSystem->UserContext = f; - FspFileSystemSetOperationGuard(f->FileSystem, fsp_fuse_op_enter, fsp_fuse_op_leave); - FspFileSystemSetOperationGuardStrategy(f->FileSystem, f->OpGuardStrategy); - FspFileSystemSetDebugLog(f->FileSystem, f->DebugLog); - - if (0 != f->MountPoint) - { - Result = FspFileSystemSetMountPoint(f->FileSystem, - L'*' == f->MountPoint[0] && L'\0' == f->MountPoint[1] ? 0 : f->MountPoint); - if (!NT_SUCCESS(Result)) - { - FspServiceLog(EVENTLOG_ERROR_TYPE, - L"Cannot set " FSP_FUSE_LIBRARY_NAME " file system mount point."); - goto fail; - } - } - - Result = FspFileSystemStartDispatcher(f->FileSystem, f->ThreadCount); - if (!NT_SUCCESS(Result)) - { - FspServiceLog(EVENTLOG_ERROR_TYPE, - L"Cannot start " FSP_FUSE_LIBRARY_NAME " file system dispatcher."); - goto fail; - } - - return STATUS_SUCCESS; - -fail: - fsp_fuse_loop_cleanup(f); - - return Result; -} - -static void fsp_fuse_loop_stop(struct fuse *f) -{ - FspFileSystemStopDispatcher(f->FileSystem); - - fsp_fuse_loop_cleanup(f); -} - -static void fsp_fuse_loop_cleanup(struct fuse *f) -{ - if (0 != f->FileSystem) - { - FspFileSystemDelete(f->FileSystem); - f->FileSystem = 0; - } - - if (f->fsinit) - { - if (f->ops.destroy) - f->ops.destroy(f->data); - f->fsinit = FALSE; - } -} - -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, struct fuse_args *outargs) { @@ -729,8 +481,6 @@ fail: FSP_FUSE_API void fsp_fuse_destroy(struct fsp_fuse_env *env, struct fuse *f) { - fsp_fuse_loop_cleanup(f); - if (0 != f->LoopEvent) CloseHandle(f->LoopEvent); @@ -739,20 +489,6 @@ FSP_FUSE_API void fsp_fuse_destroy(struct fsp_fuse_env *env, fsp_fuse_obj_free(f); } -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 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 NT_SUCCESS(fsp_fuse_loop_internal(f)) ? 0 : -1; -} - FSP_FUSE_API void fsp_fuse_exit(struct fsp_fuse_env *env, struct fuse *f) { @@ -817,10 +553,3 @@ FSP_FUSE_API int32_t fsp_fuse_ntstatus_from_errno(struct fsp_fuse_env *env, return STATUS_ACCESS_DENIED; } } - -/* Cygwin signal support */ - -FSP_FUSE_API void fsp_fuse_signal_handler(int sig) -{ - FspServiceConsoleCtrlHandler(CTRL_BREAK_EVENT); -} diff --git a/src/dll/fuse/fuse_loop.c b/src/dll/fuse/fuse_loop.c new file mode 100644 index 00000000..bae87be5 --- /dev/null +++ b/src/dll/fuse/fuse_loop.c @@ -0,0 +1,287 @@ +/** + * @file dll/fuse/fuse_loop.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 + +#define FSP_FUSE_SECTORSIZE_MIN 512 +#define FSP_FUSE_SECTORSIZE_MAX 4096 + +static INIT_ONCE fsp_fuse_svconce = INIT_ONCE_STATIC_INIT; +static HANDLE fsp_fuse_svcthread; + +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_context *context; + struct fuse_conn_info conn; + NTSTATUS Result; + + context = fsp_fuse_get_context(f->env); + if (0 == context) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto fail; + } + context->fuse = f; + context->private_data = f->data; + context->uid = -1; + context->gid = -1; + context->pid = -1; + + memset(&conn, 0, sizeof conn); + conn.proto_major = 7; /* pretend that we are FUSE kernel protocol 7.12 */ + conn.proto_minor = 12; /* which was current at the time of FUSE 2.8 */ + conn.async_read = 1; + conn.max_write = UINT_MAX; + conn.capable = + FUSE_CAP_ASYNC_READ | + //FUSE_CAP_POSIX_LOCKS | /* WinFsp handles locking in the FSD currently */ + //FUSE_CAP_ATOMIC_O_TRUNC | /* due to Windows/WinFsp design, no support */ + //FUSE_CAP_EXPORT_SUPPORT | /* not needed in Windows/WinFsp */ + FUSE_CAP_BIG_WRITES | + FUSE_CAP_DONT_MASK | + FSP_FUSE_CAP_READDIR_PLUS | + FSP_FUSE_CAP_READ_ONLY | + FSP_FUSE_CAP_STAT_EX | + FSP_FUSE_CAP_CASE_INSENSITIVE; + if (0 != f->ops.init) + { + context->private_data = f->data = f->ops.init(&conn); + f->VolumeParams.ReadOnlyVolume = 0 != (conn.want & FSP_FUSE_CAP_READ_ONLY); + f->VolumeParams.CaseSensitiveSearch = 0 == (conn.want & FSP_FUSE_CAP_CASE_INSENSITIVE); + if (!f->VolumeParams.CaseSensitiveSearch) + /* + * Disable GetDirInfoByName when file system is case-insensitive. + * The reason is that Windows always sends us queries with uppercase + * file names in GetDirInfoByName and we have no way in FUSE to normalize + * those file names when embedding them in FSP_FSCTL_DIR_INFO. + */ + f->VolumeParams.PassQueryDirectoryFileName = FALSE; + f->conn_want = conn.want; + } + f->fsinit = TRUE; + if (0 != f->ops.statfs) + { + struct fuse_statvfs stbuf; + int err; + + memset(&stbuf, 0, sizeof stbuf); + err = f->ops.statfs("/", &stbuf); + if (0 != err) + { + Result = fsp_fuse_ntstatus_from_errno(f->env, err); + goto fail; + } + + if (0 == f->VolumeParams.SectorSize && 0 != stbuf.f_frsize) + f->VolumeParams.SectorSize = (UINT16)stbuf.f_frsize; +#if 0 + if (0 == f->VolumeParams.SectorsPerAllocationUnit && 0 != stbuf.f_frsize) + f->VolumeParams.SectorsPerAllocationUnit = (UINT16)(stbuf.f_bsize / stbuf.f_frsize); +#endif + if (0 == f->VolumeParams.MaxComponentLength) + f->VolumeParams.MaxComponentLength = (UINT16)stbuf.f_namemax; + } + if (0 != f->ops.getattr) + { + struct fuse_stat_ex stbuf; + int err; + + memset(&stbuf, 0, sizeof stbuf); + err = f->ops.getattr("/", (void *)&stbuf); + if (0 != err) + { + Result = fsp_fuse_ntstatus_from_errno(f->env, err); + goto fail; + } + + if (0 == f->VolumeParams.VolumeCreationTime) + { + if (0 != stbuf.st_birthtim.tv_sec) + FspPosixUnixTimeToFileTime((void *)&stbuf.st_birthtim, + &f->VolumeParams.VolumeCreationTime); + else + if (0 != stbuf.st_ctim.tv_sec) + FspPosixUnixTimeToFileTime((void *)&stbuf.st_ctim, + &f->VolumeParams.VolumeCreationTime); + } + } + if (0 != f->ops.readlink) + { + char buf[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)]; + int err; + + /* this should always fail with ENOSYS or EINVAL */ + err = f->ops.readlink("/", buf, sizeof buf); + f->has_symlinks = -ENOSYS_(f->env) != err; + } + + /* the FSD does not currently limit these VolumeParams fields; do so here! */ + if (f->VolumeParams.SectorSize < FSP_FUSE_SECTORSIZE_MIN || + f->VolumeParams.SectorSize > FSP_FUSE_SECTORSIZE_MAX) + f->VolumeParams.SectorSize = FSP_FUSE_SECTORSIZE_MAX; + if (f->VolumeParams.SectorsPerAllocationUnit == 0) + f->VolumeParams.SectorsPerAllocationUnit = 1; + if (f->VolumeParams.MaxComponentLength > 255) + f->VolumeParams.MaxComponentLength = 255; + + if (0 == f->VolumeParams.VolumeCreationTime) + { + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + f->VolumeParams.VolumeCreationTime = *(PUINT64)&FileTime; + } + if (0 == f->VolumeParams.VolumeSerialNumber) + f->VolumeParams.VolumeSerialNumber = + ((PLARGE_INTEGER)&f->VolumeParams.VolumeCreationTime)->HighPart ^ + ((PLARGE_INTEGER)&f->VolumeParams.VolumeCreationTime)->LowPart; + + Result = FspFileSystemCreate( + f->VolumeParams.Prefix[0] ? + L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + &f->VolumeParams, &fsp_fuse_intf, + &f->FileSystem); + if (!NT_SUCCESS(Result)) + { + FspServiceLog(EVENTLOG_ERROR_TYPE, + L"Cannot create " FSP_FUSE_LIBRARY_NAME " file system."); + goto fail; + } + + f->FileSystem->UserContext = f; + FspFileSystemSetOperationGuard(f->FileSystem, fsp_fuse_op_enter, fsp_fuse_op_leave); + FspFileSystemSetOperationGuardStrategy(f->FileSystem, f->OpGuardStrategy); + FspFileSystemSetDebugLog(f->FileSystem, f->DebugLog); + + if (0 != f->MountPoint) + { + Result = FspFileSystemSetMountPoint(f->FileSystem, + L'*' == f->MountPoint[0] && L'\0' == f->MountPoint[1] ? 0 : f->MountPoint); + if (!NT_SUCCESS(Result)) + { + FspServiceLog(EVENTLOG_ERROR_TYPE, + L"Cannot set " FSP_FUSE_LIBRARY_NAME " file system mount point."); + goto fail; + } + } + + Result = FspFileSystemStartDispatcher(f->FileSystem, f->ThreadCount); + if (!NT_SUCCESS(Result)) + { + FspServiceLog(EVENTLOG_ERROR_TYPE, + L"Cannot start " FSP_FUSE_LIBRARY_NAME " file system dispatcher."); + goto fail; + } + + return STATUS_SUCCESS; + +fail: + fsp_fuse_loop_cleanup(f); + + return Result; +} + +static void fsp_fuse_loop_stop(struct fuse *f) +{ + FspFileSystemStopDispatcher(f->FileSystem); + + fsp_fuse_loop_cleanup(f); +} + +static void fsp_fuse_loop_cleanup(struct fuse *f) +{ + if (0 != f->FileSystem) + { + FspFileSystemDelete(f->FileSystem); + f->FileSystem = 0; + } + + if (f->fsinit) + { + if (f->ops.destroy) + f->ops.destroy(f->data); + f->fsinit = FALSE; + } +} + +static 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; +} + +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 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 NT_SUCCESS(fsp_fuse_loop_internal(f)) ? 0 : -1; +} + +/* Cygwin signal support */ + +FSP_FUSE_API void fsp_fuse_signal_handler(int sig) +{ + FspServiceConsoleCtrlHandler(CTRL_BREAK_EVENT); +}