/** * @file winfsp-tests.c * * @copyright 2015-2022 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 software * in accordance with the commercial license agreement provided in * conjunction with the software. The terms and conditions of any such * commercial license agreement shall govern, supersede, and render * ineffective any application of the GPLv3 license to this software, * notwithstanding of any reference thereto in the software or * associated repository. */ #include #include #include #include #include #include #define WINFSP_TESTS_NO_HOOKS #include "winfsp-tests.h" int NtfsTests = 0; int WinFspDiskTests = 1; int WinFspNetTests = 1; BOOLEAN OptExternal = FALSE; BOOLEAN OptFuseExternal = FALSE; BOOLEAN OptResilient = FALSE; BOOLEAN OptCaseInsensitiveCmp = FALSE; BOOLEAN OptCaseInsensitive = FALSE; BOOLEAN OptCaseRandomize = FALSE; BOOLEAN OptFlushAndPurgeOnCleanup = FALSE; BOOLEAN OptLegacyUnlinkRename = FALSE; BOOLEAN OptNotify = FALSE; WCHAR OptOplock = 0; WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint; WCHAR OptShareNameBuf[MAX_PATH], *OptShareName, *OptShareTarget; WCHAR OptShareComputer[MAX_PATH] = L"\\\\localhost\\"; ULONG OptSharePrefixLength; /* only counts single leading slash: \localhost\target\path */ HANDLE OptNoTraverseToken = 0; LUID OptNoTraverseLuid; static void exiting(void); int mywcscmp(PWSTR a, int alen, PWSTR b, int blen) { int len, res; if (-1 == alen) alen = (int)wcslen(a); if (-1 == blen) blen = (int)wcslen(b); len = alen < blen ? alen : blen; /* we should still be in the C locale */ if (OptCaseInsensitiveCmp) res = _wcsnicmp(a, b, len); else res = wcsncmp(a, b, len); if (0 == res) res = alen - blen; return res; } static unsigned myrandseed = 1; int myrand(void) { /* * This mimics MSVCRT rand(); we need our own version * as to not interfere with the program's rand(). */ myrandseed = myrandseed * 214013 + 2531011; return (myrandseed >> 16) & RAND_MAX; } VOID GetTestDirectoryEx(PWSTR DirBuf, ULONG DirBufSize, PWSTR DriveBuf) { DirBufSize /= sizeof(WCHAR); if (MAX_PATH > DirBufSize) ABORT("test directory buffer must be at least MAX_PATH long"); DWORD Result = GetCurrentDirectoryW(DirBufSize - 4, DirBuf + 4); if (0 == Result || Result >= DirBufSize - 4) ABORT("GetCurrentDirectoryW failed"); if (!testalpha(DirBuf[4]) || L':' != DirBuf[5]) ABORT("--ntfs/--external tests must be run from a drive"); DirBuf[0] = L'\\'; DirBuf[1] = L'\\'; DirBuf[2] = L'?'; DirBuf[3] = L'\\'; if (L'\\' == DirBuf[6] && L'\0' == DirBuf[7]) DirBuf[6] = L'\0'; if (0 != DriveBuf) { DriveBuf[0] = DirBuf[4]; DriveBuf[1] = L':'; DriveBuf[2] = L'\0'; } } static VOID DisableBackupRestorePrivileges(VOID) { union { TOKEN_PRIVILEGES P; UINT B[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; } Privileges; HANDLE Token; Privileges.P.PrivilegeCount = 2; Privileges.P.Privileges[0].Attributes = 0; Privileges.P.Privileges[1].Attributes = 0; if (!LookupPrivilegeValueW(0, SE_BACKUP_NAME, &Privileges.P.Privileges[0].Luid) || !LookupPrivilegeValueW(0, SE_RESTORE_NAME, &Privileges.P.Privileges[1].Luid)) ABORT("cannot lookup backup/restore privileges"); if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token)) ABORT("cannot open process token"); if (!AdjustTokenPrivileges(Token, FALSE, &Privileges.P, 0, 0, 0)) ABORT("cannot disable backup/restore privileges"); CloseHandle(Token); } static VOID AddNetShareIfNeeded(VOID) { if (!OptShareTarget) return; SHARE_INFO_2 ShareInfo = { 0 }; NET_API_STATUS NetStatus; ShareInfo.shi2_netname = OptShareName; ShareInfo.shi2_type = STYPE_DISKTREE; ShareInfo.shi2_permissions = ACCESS_ALL; ShareInfo.shi2_max_uses = -1; ShareInfo.shi2_path = OptShareTarget; NetShareDel(0, OptShareName, 0); NetStatus = NetShareAdd(0, 2, (PBYTE)&ShareInfo, 0); if (NERR_Success != NetStatus) ABORT("cannot add network share"); } static VOID RemoveNetShareIfNeeded(VOID) { if (!OptShareTarget) return; NetShareDel(0, OptShareName, 0); } static void abort_handler(int sig) { DWORD Error = GetLastError(); exiting(); SetLastError(Error); } LONG WINAPI UnhandledExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { if (0 != ExceptionInfo && 0 != ExceptionInfo->ExceptionRecord) { static CHAR OutBuf[128]; static union { SYMBOL_INFO V; UINT8 Buf[sizeof(SYMBOL_INFO) + 64]; } Info; LARGE_INTEGER Large; Info.V.SizeOfStruct = sizeof(SYMBOL_INFO); Info.V.MaxNameLen = 64; if (SymFromAddr(GetCurrentProcess(), (DWORD64)ExceptionInfo->ExceptionRecord->ExceptionAddress, &Large.QuadPart, &Info.V)) { wsprintfA(OutBuf, "\nEXCEPTION 0x%lX at %s+0x%lX(0x%p)\n", ExceptionInfo->ExceptionRecord->ExceptionCode, Info.V.Name, Large.LowPart, ExceptionInfo->ExceptionRecord->ExceptionAddress); } else { wsprintfA(OutBuf, "\nEXCEPTION 0x%lX at 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress); } WriteFile(GetStdHandle(STD_ERROR_HANDLE), OutBuf, lstrlenA(OutBuf), &Large.LowPart, 0); } exiting(); return EXCEPTION_EXECUTE_HANDLER; } #define rmarg(argv, argc, argi) \ argc--,\ memmove(argv + argi, argv + argi + 1, (argc - argi) * sizeof(char *)),\ argi--,\ argv[argc] = 0 int main(int argc, char *argv[]) { TESTSUITE(load_unload_tests); TESTSUITE(fuse_opt_tests); TESTSUITE(fuse_tests); TESTSUITE(posix_tests); TESTSUITE(uuid5_tests); TESTSUITE(eventlog_tests); TESTSUITE(path_tests); TESTSUITE(dirbuf_tests); TESTSUITE(version_tests); TESTSUITE(launch_tests); TESTSUITE(launcher_ptrans_tests); TESTSUITE(mount_tests); TESTSUITE(timeout_tests); TESTSUITE(memfs_tests); TESTSUITE(create_tests); TESTSUITE(info_tests); TESTSUITE(security_tests); TESTSUITE(rdwr_tests); TESTSUITE(flush_tests); TESTSUITE(lock_tests); TESTSUITE(dirctl_tests); TESTSUITE(exec_tests); TESTSUITE(devctl_tests); TESTSUITE(reparse_tests); TESTSUITE(ea_tests); TESTSUITE(stream_tests); TESTSUITE(oplock_tests); TESTSUITE(notify_tests); TESTSUITE(wsl_tests); TESTSUITE(volpath_tests); SymInitialize(GetCurrentProcess(), 0, TRUE); atexit(exiting); signal(SIGABRT, abort_handler); #pragma warning(suppress: 4996) if (0 == getenv("WINFSP_TESTS_EXCEPTION_FILTER_DISABLE")) SetUnhandledExceptionFilter(UnhandledExceptionHandler); for (int argi = 1; argc > argi; argi++) { const char *a = argv[argi]; if ('-' == a[0]) { if (0 == strcmp("--ntfs", a) || 0 == strcmp("--external", a)) { OptExternal = TRUE; NtfsTests = 1; WinFspDiskTests = 0; WinFspNetTests = 0; rmarg(argv, argc, argi); } else if (0 == strcmp("--fuse-external", a)) { OptExternal = TRUE; OptFuseExternal = TRUE; NtfsTests = 1; WinFspDiskTests = 0; WinFspNetTests = 0; rmarg(argv, argc, argi); } else if (0 == strcmp("--resilient", a)) { OptResilient = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--case-insensitive-cmp", a)) { OptCaseInsensitiveCmp = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--case-insensitive", a)) { OptCaseInsensitive = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--case-randomize", a)) { OptCaseRandomize = TRUE; OptCaseInsensitive = TRUE; OptCaseInsensitiveCmp = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--flush-and-purge-on-cleanup", a)) { OptFlushAndPurgeOnCleanup = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--legacy-unlink-rename", a)) { OptLegacyUnlinkRename = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--notify", a)) { OptNotify = TRUE; rmarg(argv, argc, argi); } else if (0 == strcmp("--oplock=batch", a)) { OptOplock = 'B'; rmarg(argv, argc, argi); } else if (0 == strcmp("--oplock=filter", a)) { OptOplock = 'F'; rmarg(argv, argc, argi); } else if (0 == strncmp("--mountpoint=", a, sizeof "--mountpoint=" - 1)) { if (0 != MultiByteToWideChar(CP_UTF8, 0, a + sizeof "--mountpoint=" - 1, -1, OptMountPointBuf, MAX_PATH)) { OptMountPoint = OptMountPointBuf; rmarg(argv, argc, argi); if (!(testalpha(OptMountPoint[0]) && L':' == OptMountPoint[1] && L'\0' == OptMountPoint[2])) WinFspNetTests = 0; } } else if (0 == strncmp("--share=", a, sizeof "--share=" - 1)) { if (0 != MultiByteToWideChar(CP_UTF8, 0, a + sizeof "--share=" - 1, -1, OptShareNameBuf, MAX_PATH)) { OptShareTarget = wcschr(OptShareNameBuf, L'='); if (OptShareTarget) { *OptShareTarget++ = L'\0'; OptShareName = OptShareNameBuf; } else if (L'\\' == OptShareNameBuf[0] && L'\\' == OptShareNameBuf[1]) { OptShareName = wcschr(OptShareNameBuf + 2, L'\\'); if (OptShareName) { OptShareName++; memcpy(OptShareComputer, OptShareNameBuf, (OptShareName - OptShareNameBuf) * sizeof(WCHAR)); OptShareComputer[OptShareName - OptShareNameBuf] = L'\0'; } } } if (OptShareName) { rmarg(argv, argc, argi); OptSharePrefixLength = (ULONG) ((wcslen(OptShareComputer) + wcslen(OptShareName) - 1) * sizeof(WCHAR)); WinFspDiskTests = 0; WinFspNetTests = 0; } } else if (0 == strncmp("--share-prefix=", a, sizeof "--share-prefix=" - 1)) { /* hack to allow name queries on network file systems with mapped drives */ WCHAR SharePrefixBuf[MAX_PATH]; if (0 != MultiByteToWideChar(CP_UTF8, 0, a + sizeof "--share-prefix=" - 1, -1, SharePrefixBuf, MAX_PATH)) { rmarg(argv, argc, argi); OptSharePrefixLength = (ULONG)(wcslen(SharePrefixBuf) * sizeof(WCHAR)); } } else if (0 == strcmp("--no-traverse", a)) { if (LookupPrivilegeValueW(0, SE_CHANGE_NOTIFY_NAME, &OptNoTraverseLuid) && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &OptNoTraverseToken)) { rmarg(argv, argc, argi); } } } } if (!NtfsTests && OptShareName) ABORT("option --share requires --ntfs/--external"); DisableBackupRestorePrivileges(); AddNetShareIfNeeded(); myrandseed = (unsigned)time(0); tlib_run_tests(argc, argv); return 0; } static void exiting(void) { OutputDebugStringA("winfsp-tests: exiting\n"); RemoveNetShareIfNeeded(); }