diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj
index 28439205..3447dde0 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj
+++ b/build/VStudio/testing/winfsp-tests.vcxproj
@@ -177,6 +177,7 @@
TurnOffAllWarnings
+
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters
index a98cfe18..c56a1e40 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj.filters
+++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters
@@ -19,6 +19,9 @@
Source\tlib
+
+ Source
+
diff --git a/tst/winfsp-tests/timeout-test.c b/tst/winfsp-tests/timeout-test.c
new file mode 100644
index 00000000..38fa646f
--- /dev/null
+++ b/tst/winfsp-tests/timeout-test.c
@@ -0,0 +1,169 @@
+#include
+#include
+#include
+#include
+
+extern int WinFspDiskTests;
+extern int WinFspNetTests;
+
+static unsigned __stdcall timeout_pending_dotest_thread(void *FilePath)
+{
+ FspDebugLog(__FUNCTION__ ": \"%S\"\n", FilePath);
+
+ HANDLE Handle;
+ Handle = CreateFileW(FilePath,
+ FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ if (INVALID_HANDLE_VALUE == Handle)
+ return GetLastError();
+ CloseHandle(Handle);
+ return 0;
+}
+
+void timeout_pending_dotest(PWSTR DeviceName)
+{
+ NTSTATUS Result;
+ BOOL Success;
+ FSP_FSCTL_VOLUME_PARAMS Params = { 0 };
+ WCHAR VolumePath[MAX_PATH];
+ WCHAR FilePath[MAX_PATH];
+ HANDLE VolumeHandle;
+ HANDLE Thread;
+ DWORD ExitCode;
+
+ Params.SectorSize = 16384;
+ Params.SerialNumber = 0x12345678;
+ Params.IrpTimeout = FspFsctlIrpTimeoutDebug;
+ Result = FspFsctlCreateVolume(DeviceName, &Params, 0, VolumePath, sizeof VolumePath);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ StringCbPrintfW(FilePath, sizeof FilePath, L"\\\\?\\GLOBALROOT%s\\file0", VolumePath);
+ Thread = (HANDLE)_beginthreadex(0, 0, timeout_pending_dotest_thread, FilePath, 0, 0);
+ ASSERT(0 != Thread);
+
+ WaitForSingleObject(Thread, INFINITE);
+ GetExitCodeThread(Thread, &ExitCode);
+ CloseHandle(Thread);
+
+ ASSERT(ERROR_OPERATION_ABORTED == ExitCode);
+
+ Result = FspFsctlOpenVolume(VolumePath, &VolumeHandle);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ Result = FspFsctlDeleteVolume(VolumeHandle);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ Success = CloseHandle(VolumeHandle);
+ ASSERT(Success);
+}
+
+void timeout_pending_test(void)
+{
+ if (WinFspDiskTests)
+ timeout_pending_dotest(L"WinFsp.Disk");
+ if (WinFspNetTests)
+ timeout_pending_dotest(L"WinFsp.Net");
+}
+
+static unsigned __stdcall timeout_transact_dotest_thread(void *FilePath)
+{
+ FspDebugLog(__FUNCTION__ ": \"%S\"\n", FilePath);
+
+ Sleep(1000);
+
+ HANDLE Handle;
+ Handle = CreateFileW(FilePath,
+ FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ if (INVALID_HANDLE_VALUE == Handle)
+ return GetLastError();
+ CloseHandle(Handle);
+ return 0;
+}
+
+void timeout_transact_dotest(PWSTR DeviceName)
+{
+ NTSTATUS Result;
+ BOOL Success;
+ FSP_FSCTL_VOLUME_PARAMS Params = { 0 };
+ WCHAR VolumePath[MAX_PATH];
+ WCHAR FilePath[MAX_PATH];
+ HANDLE VolumeHandle;
+ HANDLE Thread;
+ DWORD ExitCode;
+
+ Params.SectorSize = 16384;
+ Params.SerialNumber = 0x12345678;
+ Params.TransactTimeout = 2000;
+ Params.IrpTimeout = FspFsctlIrpTimeoutDebug;
+ Result = FspFsctlCreateVolume(DeviceName, &Params, 0, VolumePath, sizeof VolumePath);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ StringCbPrintfW(FilePath, sizeof FilePath, L"\\\\?\\GLOBALROOT%s\\file0", VolumePath);
+ Thread = (HANDLE)_beginthreadex(0, 0, timeout_transact_dotest_thread, FilePath, 0, 0);
+ ASSERT(0 != Thread);
+
+ Result = FspFsctlOpenVolume(VolumePath, &VolumeHandle);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ FSP_FSCTL_DECLSPEC_ALIGN UINT8 RequestBuf[FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMIN];
+ FSP_FSCTL_DECLSPEC_ALIGN UINT8 ResponseBuf[FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMIN];
+ UINT8 *RequestBufEnd;
+ UINT8 *ResponseBufEnd = ResponseBuf + sizeof ResponseBuf;
+ SIZE_T RequestBufSize;
+ SIZE_T ResponseBufSize;
+ FSP_FSCTL_TRANSACT_REQ *Request = (PVOID)RequestBuf, *NextRequest;
+ FSP_FSCTL_TRANSACT_RSP *Response = (PVOID)ResponseBuf;
+
+ ResponseBufSize = 0;
+ RequestBufSize = sizeof RequestBuf;
+ Result = FspFsctlTransact(VolumeHandle, ResponseBuf, ResponseBufSize, RequestBuf, &RequestBufSize);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ RequestBufEnd = RequestBuf + RequestBufSize;
+
+ NextRequest = FspFsctlTransactConsumeRequest(Request, RequestBufEnd);
+ ASSERT(0 != NextRequest);
+
+ ASSERT(0 == Request->Version);
+ ASSERT(FSP_FSCTL_TRANSACT_REQ_SIZEMAX >= Request->Size);
+ ASSERT(0 != Request->Hint);
+ ASSERT(FspFsctlTransactCreateKind == Request->Kind);
+ ASSERT(FILE_CREATE == ((Request->Req.Create.CreateOptions >> 24) & 0xff));
+ ASSERT(FILE_ATTRIBUTE_NORMAL == Request->Req.Create.FileAttributes);
+ ASSERT(0 == Request->Req.Create.SecurityDescriptor.Offset);
+ ASSERT(0 == Request->Req.Create.SecurityDescriptor.Size);
+ ASSERT(0 == Request->Req.Create.AllocationSize);
+ ASSERT(FILE_GENERIC_READ == Request->Req.Create.DesiredAccess);
+ ASSERT((FILE_SHARE_READ | FILE_SHARE_WRITE) == Request->Req.Create.ShareAccess);
+ ASSERT(0 == Request->Req.Create.Ea.Offset);
+ ASSERT(0 == Request->Req.Create.Ea.Size);
+ ASSERT(Request->Req.Create.UserMode);
+ ASSERT(Request->Req.Create.HasTraversePrivilege);
+ ASSERT(!Request->Req.Create.OpenTargetDirectory);
+ ASSERT(!Request->Req.Create.CaseSensitive);
+
+ WaitForSingleObject(Thread, INFINITE);
+ GetExitCodeThread(Thread, &ExitCode);
+ CloseHandle(Thread);
+
+ ASSERT(ERROR_OPERATION_ABORTED == ExitCode);
+
+ Result = FspFsctlDeleteVolume(VolumeHandle);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ Success = CloseHandle(VolumeHandle);
+ ASSERT(Success);
+}
+
+void timeout_transact_test(void)
+{
+ if (WinFspDiskTests)
+ timeout_transact_dotest(L"WinFsp.Disk");
+ if (WinFspNetTests)
+ timeout_transact_dotest(L"WinFsp.Net");
+}
+
+void timeout_tests(void)
+{
+ TEST(timeout_pending_test);
+ TEST(timeout_transact_test);
+}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index 1d567bb8..a27c79b3 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -6,6 +6,8 @@ int WinFspNetTests = 0;
int main(int argc, char *argv[])
{
TESTSUITE(mount_tests);
+ TESTSUITE(timeout_tests);
+
tlib_run_tests(argc, argv);
return 0;
}