mirror of
https://github.com/winfsp/winfsp.git
synced 2026-09-03 09:18:38 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fde790d8ea | ||
|
|
f97012ec46 | ||
|
|
43b9a6e222 | ||
|
|
f41f3afa07 | ||
|
|
d0bd2170bd | ||
|
|
57e0a24dd0 | ||
|
|
5b53285545 | ||
|
|
7ee0873f4b | ||
|
|
97e6d92b5b | ||
|
|
a744d6f24c | ||
|
|
1916d9bf06 | ||
|
|
e3ecc89bb7 | ||
|
|
83aa54b4ab | ||
|
|
e7c7ccc5c4 | ||
|
|
0953e76255 | ||
|
|
932e52769d | ||
|
|
9d1b37f0f1 | ||
|
|
169eb2c35d | ||
|
|
786b8e2707 | ||
|
|
2669a86a8e | ||
|
|
f7b313313f | ||
|
|
bd8b54c469 | ||
|
|
bdab233e92 | ||
|
|
1ad7d9935f | ||
|
|
82f59837f6 | ||
|
|
ff9e38c82d | ||
|
|
608e16761e |
@@ -1,6 +1,71 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## v2.2B4 (2026 Beta4)
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-3006 reported by Tay Kiat Loong (GitHub: @Owl4444).
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-7162 reported by Tay Kiat Loong (GitHub: @Owl4444) and uhg (GitHub: @UltimateHG).
|
||||
|
||||
- [FIX] Fixes vulnerability reported by Wencheng Xue (GitHub @panwnvda).
|
||||
|
||||
- [FIX] Fixes vulnerability reported by Abhinav Agarwal (GitHub @abhinavagarwal07).
|
||||
|
||||
- [FIX] Fixes deadlock during cached writes under memory pressure. See PR #677 for discussion.
|
||||
|
||||
- [FIX] Fixes deadlock in the file system notification mechanism with concurrent renames. See fix PR #669 by @yeonsh and test PR #672 by @Maksim-Isakau.
|
||||
|
||||
- [FIX] Fixes deadlock when closing named streams while the file system is being forcibly terminated. See issue #680.
|
||||
|
||||
- [FIX] Fixes another complicated deadlock when the file system is being forcibly terminated. See issue #682.
|
||||
|
||||
- [FIX] The WinFsp Network Provider provides improved shell support for network file systems not managed by the Launcher. See commit f021496.
|
||||
|
||||
- [FIX] Fixes a problem with mounting on a directory with a trailing backslash. See issue #674.
|
||||
|
||||
- [FUSE] Add FUSE option `FlushOnCleanup`. See PR #614 by @chenjie4255.
|
||||
|
||||
- [FUSE] FUSE now respects the `direct_io` flag. See PR #646 by @chenjie4255.
|
||||
|
||||
- [FUSE] Add FUSE option `AddWriteEaAccess`. See PR #648 by @chenjie4255.
|
||||
|
||||
|
||||
## v2.2B3 (2026 Beta3)
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-3006 discovered by Tay Kiat Loong (GitHub: @Owl4444).
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-7162 discovered by Tay Kiat Loong (GitHub: @Owl4444) and uhg (GitHub: @UltimateHG).
|
||||
|
||||
- [FIX] Fixes deadlock during cached writes under memory pressure. See PR #677 for discussion.
|
||||
|
||||
- [FIX] Fixes deadlock in the file system notification mechanism with concurrent renames. See fix PR #669 by @yeonsh and test PR #672 by @Maksim-Isakau.
|
||||
|
||||
- [FIX] The WinFsp Network Provider provides improved shell support for network file systems not managed by the Launcher. See commit f021496.
|
||||
|
||||
- [FUSE] Add FUSE option `FlushOnCleanup`. See PR #614 by @chenjie4255.
|
||||
|
||||
- [FUSE] FUSE now respects the `direct_io` flag. See PR #646 by @chenjie4255.
|
||||
|
||||
- [FUSE] Add FUSE option `AddWriteEaAccess`. See PR #648 by @chenjie4255.
|
||||
|
||||
|
||||
## v2.2B2 (2026 Beta2)
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-3006 discovered by Tay Kiat Loong (GitHub: @Owl4444).
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-7162 discovered by Tay Kiat Loong (GitHub: @Owl4444) and uhg (GitHub: @UltimateHG).
|
||||
|
||||
- [FIX] Fixes deadlock in the file system notification mechanism with concurrent renames. See fix PR #669 by @yeonsh and test PR #672 by @Maksim-Isakau.
|
||||
|
||||
- [FIX] The WinFsp Network Provider provides improved shell support for network file systems not managed by the Launcher. See commit f021496.
|
||||
|
||||
- [FUSE] Add FUSE option `FlushOnCleanup`. See PR #614 by @chenjie4255.
|
||||
|
||||
- [FUSE] FUSE now respects the `direct_io` flag. See PR #646 by @chenjie4255.
|
||||
|
||||
- [FUSE] Add FUSE option `AddWriteEaAccess`. See PR #648 by @chenjie4255.
|
||||
|
||||
|
||||
## v2.2B1 (2026 Beta1)
|
||||
|
||||
- [FIX] Fixes vulnerability CVE-2026-3006 discovered by Tay Kiat Loong.
|
||||
|
||||
@@ -68,12 +68,14 @@ CONTRIBUTOR LIST
|
||||
|John Oberschelp |john at oberschelp.net
|
||||
|John Tyner |jtyner at gmail.com
|
||||
|Konstantinos Karakostas |noiredev at protonmail.com
|
||||
|Max Isakov |max at resilio.com
|
||||
|Naoki Ikeguchi |me at s6n.jp
|
||||
|Paweł Wegner (Google LLC, https://google.com) |lemourin at google.com
|
||||
|Pedro Frejo (Arpa System, https://arpasystem.com) |pedro.frejo at arpasystem.com
|
||||
|Ronny Chan |ronny at ronnychan.ca
|
||||
|Sam Kelly (DuroSoft Technologies LLC, https://durosoft.com) |sam at durosoft.com
|
||||
|Santiago Ganis |sganis at gmail.com
|
||||
|Seunghoon Yeon (Bdrive Inc, https://www.bdrive.com) |ysh at bdrive.com
|
||||
|Thomas Gibson-Robinson |tom at cocotec.io
|
||||
|Tobias Urlaub |saibotu at outlook.de
|
||||
|Victor Gao |victgm at outlook.com
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<MyCanonicalVersion>2.2</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2026 Beta1</MyProductVersion>
|
||||
<MyProductVersion>2026 Beta4</MyProductVersion>
|
||||
<MyProductStage>Beta</MyProductStage>
|
||||
|
||||
<MyCrossCert>CrossCert.cer</MyCrossCert>
|
||||
|
||||
@@ -4,6 +4,7 @@ This document contains a list of known open-source file systems and file system
|
||||
|
||||
== File Systems
|
||||
|
||||
- https://github.com/dharmatech/9p-winfsp[9p-winfsp] - Experimental native Windows client for mounting Plan 9 9P exports.
|
||||
- https://github.com/wesley1975/blobfs-win[blobfs-win] - The native porting of the blobfs on the windows platform, blobfs can help you mount the Azure Blob storage as the local disk driver, no matter it is a Linux system or a Windows system.
|
||||
- https://github.com/buildbarn/bb-remote-execution[buildbarn] - A bazel-compatible remote execution service that uses WinFSP to provide a virtual directory for performing builds
|
||||
- https://github.com/cryptomator/cryptomator[Cryptomator] - Multi-platform transparent client-side encryption of your files in the cloud
|
||||
@@ -13,6 +14,7 @@ This document contains a list of known open-source file systems and file system
|
||||
- https://github.com/sganis/golddrive[golddrive] - Windows ssh network drive
|
||||
- https://github.com/winfsp/hubfs[hubfs] - File system for GitHub
|
||||
- https://github.com/juicedata/juicefs[JuiceFS] - a distributed POSIX file system built on top of Redis and S3
|
||||
- https://github.com/KeibiSoft/KeibiDrop[KeibiDrop] - End-to-end encrypted peer-to-peer filesystem with on-demand file streaming
|
||||
- https://github.com/FrKaram/KS2.Drive[KS2.Drive] - Mount a webDAV/AOS server as a local drive
|
||||
- https://github.com/winfsp/nfs-win[nfs-win] - NFS for Windows
|
||||
- https://github.com/winfsp/objfs[objfs] - Object Storage File System
|
||||
|
||||
@@ -147,6 +147,8 @@ FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_VOLUME_NAME_SIZEMAX <= 260 * sizeof(WCHAR),
|
||||
|
||||
#define FSP_FSCTL_DEVICECONTROL_SIZEMAX (4 * 1024) /* must be < FSP_FSCTL_TRANSACT_{REQ,RSP}_SIZEMAX */
|
||||
|
||||
#define FSP_FSCTL_NOTIFY_INFO_SIZEMAX (0x7fffffffU)
|
||||
|
||||
/* marshalling */
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4200 4201) /* zero-sized array in struct/union; nameless struct/union */
|
||||
|
||||
@@ -127,6 +127,11 @@ HANDLE FspCreateDirectoryFileW(
|
||||
Length = GetFullPathNameW(FileName, Length + 1, FullFileName, &FilePart);
|
||||
if (0 == Length)
|
||||
goto exit;
|
||||
if (0 == FilePart)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
FilePartChar = *FilePart;
|
||||
*FilePart = L'\0';
|
||||
|
||||
+2
-1
@@ -179,7 +179,8 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
||||
{
|
||||
DirInfoSize = DirInfo->Size;
|
||||
|
||||
if (sizeof(FSP_FSCTL_DIR_INFO) > DirInfoSize)
|
||||
if (sizeof(FSP_FSCTL_DIR_INFO) > DirInfoSize ||
|
||||
(SIZE_T)(DirInfoEnd - (PUINT8)DirInfo) < DirInfoSize)
|
||||
{
|
||||
if (0 == *PDestLen)
|
||||
return STATUS_NO_MORE_FILES;
|
||||
|
||||
+24
-2
@@ -698,14 +698,18 @@ NTSTATUS FspOplockFsctrl(
|
||||
/* utility: synchronous work queue */
|
||||
typedef struct
|
||||
{
|
||||
KEVENT Event;
|
||||
LONG RefCount;
|
||||
PWORKER_THREAD_ROUTINE Routine;
|
||||
PVOID Context;
|
||||
KEVENT Event;
|
||||
WORK_QUEUE_ITEM WorkQueueItem;
|
||||
} FSP_SYNCHRONOUS_WORK_ITEM;
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem(
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context);
|
||||
VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context);
|
||||
VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem);
|
||||
NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
BOOLEAN Alertable);
|
||||
|
||||
/* utility: delayed work queue */
|
||||
typedef struct
|
||||
@@ -1363,6 +1367,24 @@ BOOLEAN FspFsvolDeviceFileRenameTryAcquireExclusive(PDEVICE_OBJECT DeviceObject)
|
||||
return ExAcquireResourceExclusiveLite(&FsvolDeviceExtension->FileRenameResource, FALSE);
|
||||
}
|
||||
static inline
|
||||
VOID FspFsvolDeviceFileRenameAcquireSharedStarveExclusive(PDEVICE_OBJECT DeviceObject)
|
||||
{
|
||||
/*
|
||||
* Acquire the file rename resource shared, but starve (ignore) any QUEUED
|
||||
* exclusive waiter. Used by FspVolumeNotifyWork: a notify session opened by
|
||||
* FspFileSystemNotifyBegin already holds this resource shared (via owner
|
||||
* pointer) and intentionally defers renames until FspFileSystemNotifyEnd. A
|
||||
* plain ExAcquireResourceSharedLite here would honor a concurrently queued
|
||||
* exclusive rename waiter and block -- but the very work item that blocks is
|
||||
* the one that must run FspFileSystemNotifyEnd processing to release the
|
||||
* session and let that rename proceed, so the two self-deadlock. Starving the
|
||||
* queued exclusive waiter is safe: the rename stays blocked behind the shared
|
||||
* holders either way, so name stability across the notify is preserved.
|
||||
*/
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
||||
ExAcquireSharedStarveExclusive(&FsvolDeviceExtension->FileRenameResource, TRUE);
|
||||
}
|
||||
static inline
|
||||
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner)
|
||||
{
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
||||
|
||||
+33
-3
@@ -121,6 +121,7 @@ NTSTATUS FspMainFileOpen(
|
||||
NTSTATUS FspMainFileClose(
|
||||
HANDLE MainFileHandle,
|
||||
PFILE_OBJECT MainFileObject);
|
||||
static WORKER_THREAD_ROUTINE FspMainFileCloseSynchronousWork;
|
||||
VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp);
|
||||
VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
||||
|
||||
@@ -183,6 +184,7 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
||||
#pragma alloc_text(PAGE, FspFileDescSetDirectoryMarker)
|
||||
#pragma alloc_text(PAGE, FspMainFileOpen)
|
||||
#pragma alloc_text(PAGE, FspMainFileClose)
|
||||
#pragma alloc_text(PAGE, FspMainFileCloseSynchronousWork)
|
||||
#pragma alloc_text(PAGE, FspFileNodeOplockPrepare)
|
||||
#pragma alloc_text(PAGE, FspFileNodeOplockComplete)
|
||||
#endif
|
||||
@@ -2803,14 +2805,42 @@ NTSTATUS FspMainFileClose(
|
||||
|
||||
if (0 != MainFileHandle)
|
||||
{
|
||||
Result = ObCloseHandle(MainFileHandle, KernelMode);
|
||||
if (!NT_SUCCESS(Result))
|
||||
DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result));
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *WorkItem;
|
||||
|
||||
WorkItem = DEBUGTEST(90) ?
|
||||
FspAllocateSynchronousWorkItem(FspMainFileCloseSynchronousWork, MainFileHandle) :
|
||||
0;
|
||||
if (0 != WorkItem)
|
||||
/*
|
||||
* Fix issue #680:
|
||||
* Instead of closing the main file handle in this thread, close it in a system thread
|
||||
* and wait for that close to complete in an alertable manner. This avoids a deadlock
|
||||
* due to the file system process being forcibly terminated via NtTerminateProcess.
|
||||
*/
|
||||
Result = FspExecuteSynchronousWorkItem(WorkItem, TRUE);
|
||||
else
|
||||
/*
|
||||
* Best effort: Close the main file handle in this thread.
|
||||
* Small chance of deadlock if someone is killing our file system.
|
||||
*/
|
||||
FspMainFileCloseSynchronousWork(MainFileHandle);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspMainFileCloseSynchronousWork(PVOID Context)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
HANDLE MainFileHandle = Context;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = ObCloseHandle(MainFileHandle, KernelMode);
|
||||
if (!NT_SUCCESS(Result))
|
||||
DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result));
|
||||
}
|
||||
|
||||
VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
+2
-1
@@ -669,7 +669,8 @@ static NTSTATUS FspFsvolQueryStreamInformationCopy(
|
||||
{
|
||||
StreamInfoSize = StreamInfo->Size;
|
||||
|
||||
if (sizeof(FSP_FSCTL_STREAM_INFO) > StreamInfoSize)
|
||||
if (sizeof(FSP_FSCTL_STREAM_INFO) > StreamInfoSize ||
|
||||
(SIZE_T)(StreamInfoEnd - (PUINT8)StreamInfo) < StreamInfoSize)
|
||||
break;
|
||||
|
||||
StreamNameLength = StreamInfoSize - sizeof(FSP_FSCTL_STREAM_INFO) + STREAM_EXTRA_LENGTH;
|
||||
|
||||
+47
-7
@@ -115,9 +115,12 @@ NTSTATUS FspOplockFsctrl(
|
||||
POPLOCK Oplock,
|
||||
PIRP Irp,
|
||||
ULONG OpenCount);
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem(
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context);
|
||||
VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context);
|
||||
VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem);
|
||||
NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
BOOLEAN Alertable);
|
||||
static WORKER_THREAD_ROUTINE FspExecuteSynchronousWorkItemRoutine;
|
||||
VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem,
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context);
|
||||
@@ -167,6 +170,7 @@ LONG FspCompareUnicodeString(
|
||||
#pragma alloc_text(PAGE, FspCheckOplock)
|
||||
#pragma alloc_text(PAGE, FspCheckOplockEx)
|
||||
#pragma alloc_text(PAGE, FspOplockFsctrl)
|
||||
#pragma alloc_text(PAGE, FspAllocateSynchronousWorkItem)
|
||||
#pragma alloc_text(PAGE, FspInitializeSynchronousWorkItem)
|
||||
#pragma alloc_text(PAGE, FspExecuteSynchronousWorkItem)
|
||||
#pragma alloc_text(PAGE, FspExecuteSynchronousWorkItemRoutine)
|
||||
@@ -1123,27 +1127,56 @@ NTSTATUS FspOplockFsctrl(
|
||||
return Result;
|
||||
}
|
||||
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *FspAllocateSynchronousWorkItem(
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem;
|
||||
|
||||
SynchronousWorkItem = FspAllocNonPaged(sizeof *SynchronousWorkItem);
|
||||
if (0 != SynchronousWorkItem)
|
||||
{
|
||||
FspInitializeSynchronousWorkItem(SynchronousWorkItem, Routine, Context);
|
||||
SynchronousWorkItem->RefCount = 2; /* make it refcounted */
|
||||
}
|
||||
|
||||
return SynchronousWorkItem;
|
||||
}
|
||||
|
||||
VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
PWORKER_THREAD_ROUTINE Routine, PVOID Context)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
KeInitializeEvent(&SynchronousWorkItem->Event, NotificationEvent, FALSE);
|
||||
SynchronousWorkItem->RefCount = 0; /* initialize as non-refcounted */
|
||||
SynchronousWorkItem->Routine = Routine;
|
||||
SynchronousWorkItem->Context = Context;
|
||||
KeInitializeEvent(&SynchronousWorkItem->Event, NotificationEvent, FALSE);
|
||||
ExInitializeWorkItem(&SynchronousWorkItem->WorkQueueItem,
|
||||
FspExecuteSynchronousWorkItemRoutine, SynchronousWorkItem);
|
||||
}
|
||||
|
||||
VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem)
|
||||
NTSTATUS FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem,
|
||||
BOOLEAN Alertable)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
ExQueueWorkItem(&SynchronousWorkItem->WorkQueueItem, CriticalWorkQueue);
|
||||
|
||||
NTSTATUS Result;
|
||||
Result = KeWaitForSingleObject(&SynchronousWorkItem->Event, Executive, KernelMode, FALSE, 0);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
/* Alertable=TRUE requires item allocated by FspAllocateSynchronousWorkItem */
|
||||
ASSERT(!Alertable || 0 < SynchronousWorkItem->RefCount);
|
||||
|
||||
ExQueueWorkItem(&SynchronousWorkItem->WorkQueueItem, CriticalWorkQueue);
|
||||
Result = KeWaitForSingleObject(&SynchronousWorkItem->Event, Executive, KernelMode, Alertable, 0);
|
||||
|
||||
if (0 < SynchronousWorkItem->RefCount)
|
||||
{
|
||||
if (0 == InterlockedDecrement(&SynchronousWorkItem->RefCount))
|
||||
FspFree(SynchronousWorkItem);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspExecuteSynchronousWorkItemRoutine(PVOID Context)
|
||||
@@ -1151,8 +1184,15 @@ static VOID FspExecuteSynchronousWorkItemRoutine(PVOID Context)
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem = Context;
|
||||
|
||||
SynchronousWorkItem->Routine(SynchronousWorkItem->Context);
|
||||
KeSetEvent(&SynchronousWorkItem->Event, 1, FALSE);
|
||||
|
||||
if (0 < SynchronousWorkItem->RefCount)
|
||||
{
|
||||
if (0 == InterlockedDecrement(&SynchronousWorkItem->RefCount))
|
||||
FspFree(SynchronousWorkItem);
|
||||
}
|
||||
}
|
||||
|
||||
VOID FspInitializeDelayedWorkItem(FSP_DELAYED_WORK_ITEM *DelayedWorkItem,
|
||||
|
||||
+20
-5
@@ -432,6 +432,9 @@ VOID FspVolumeDelete(
|
||||
ULONG FileNodeCount, Index;
|
||||
NTSTATUS Result;
|
||||
|
||||
/* stop the I/O queue */
|
||||
FspIoqStop(FsvolDeviceExtension->Ioq, TRUE);
|
||||
|
||||
FspFsvolDeviceVolumeDeleteAcquireExclusive(FsvolDeviceObject);
|
||||
FsvolDeviceExtension->VolumeDeleted = TRUE;
|
||||
|
||||
@@ -486,9 +489,6 @@ static VOID FspVolumeDeleteNoLock(
|
||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
|
||||
/* stop the I/O queue */
|
||||
FspIoqStop(FsvolDeviceExtension->Ioq, TRUE);
|
||||
|
||||
/* do we have a virtual disk device or are we registered with fsmup? */
|
||||
if (0 != FsvolDeviceExtension->FsvrtDeviceObject)
|
||||
{
|
||||
@@ -1381,6 +1381,7 @@ NTSTATUS FspVolumeNotify(
|
||||
ASSERT(0 != IrpSp->FileObject->FsContext2);
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
PVOID InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
|
||||
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||||
FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = 0;
|
||||
@@ -1389,11 +1390,15 @@ NTSTATUS FspVolumeNotify(
|
||||
if (0 == InputBufferLength)
|
||||
return FspVolumeNotifyLock(FsvolDeviceObject);
|
||||
|
||||
if (FSP_FSCTL_NOTIFY_INFO_SIZEMAX < InputBufferLength)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!FspDeviceReference(FsvolDeviceObject))
|
||||
return STATUS_CANCELLED;
|
||||
|
||||
FspFsvolDeviceVolumeDeleteAcquireShared(FsvolDeviceObject);
|
||||
if (FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeDeleted)
|
||||
if (FsvolDeviceExtension->VolumeDeleted ||
|
||||
FspIoqStopped(FsvolDeviceExtension->Ioq))
|
||||
{
|
||||
Result = STATUS_CANCELLED;
|
||||
goto fail;
|
||||
@@ -1499,7 +1504,15 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0)
|
||||
BOOLEAN Unlock = FALSE;
|
||||
NTSTATUS Result;
|
||||
|
||||
FspFsvolDeviceFileRenameAcquireShared(FsvolDeviceObject);
|
||||
/*
|
||||
* Starve queued exclusive (rename) waiters here to avoid a self-deadlock:
|
||||
* the enclosing FspFileSystemNotifyBegin/End session already holds this
|
||||
* resource shared, and a rename that queues exclusive mid-session would
|
||||
* otherwise block this work item -- the same work item that must release
|
||||
* the session (FspVolumeNotifyLock count) and unblock that rename. See
|
||||
* FspFsvolDeviceFileRenameAcquireSharedStarveExclusive.
|
||||
*/
|
||||
FspFsvolDeviceFileRenameAcquireSharedStarveExclusive(FsvolDeviceObject);
|
||||
|
||||
/* iterate over notify information and invalidate/notify each file */
|
||||
for (; (PUINT8)NotifyInfo + sizeof(NotifyInfo->Size) <= NotifyInfoEnd;
|
||||
@@ -1512,6 +1525,8 @@ static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0)
|
||||
Unlock = TRUE;
|
||||
break;
|
||||
}
|
||||
if ((SIZE_T)(NotifyInfoEnd - (PUINT8)NotifyInfo) < NotifyInfoSize)
|
||||
break;
|
||||
|
||||
FileName.Length =
|
||||
FileName.MaximumLength = (USHORT)(NotifyInfoSize - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
|
||||
+6
-2
@@ -247,8 +247,13 @@ static NTSTATUS FspFsvolWriteCached(
|
||||
FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
|
||||
UINT64 WriteEndOffset;
|
||||
BOOLEAN ExtendingFile;
|
||||
BOOLEAN DeferWrite;
|
||||
BOOLEAN Success;
|
||||
|
||||
/* should we defer the write? */
|
||||
Success = DEBUGTEST(90) && CcCanIWrite(FileObject, WriteLength, CanWait, Retrying);
|
||||
DeferWrite = !Success;
|
||||
|
||||
/* try to acquire the FileNode Main exclusive */
|
||||
Success = DEBUGTEST(90) &&
|
||||
FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireMain, CanWait);
|
||||
@@ -306,8 +311,7 @@ static NTSTATUS FspFsvolWriteCached(
|
||||
}
|
||||
|
||||
/* should we defer the write? */
|
||||
Success = DEBUGTEST(90) && CcCanIWrite(FileObject, WriteLength, CanWait, Retrying);
|
||||
if (!Success)
|
||||
if (DeferWrite)
|
||||
{
|
||||
Result = FspWqCreateIrpWorkItem(Irp, FspFsvolWriteCached, 0);
|
||||
if (NT_SUCCESS(Result))
|
||||
|
||||
@@ -191,7 +191,7 @@ Ptfs::~Ptfs()
|
||||
|
||||
NTSTATUS Ptfs::SetPath(PWSTR Path)
|
||||
{
|
||||
WCHAR FullPath[MAX_PATH];
|
||||
WCHAR FullPath[FULLPATH_SIZE];
|
||||
ULONG Length;
|
||||
HANDLE Handle;
|
||||
FILETIME CreationTime;
|
||||
|
||||
@@ -626,7 +626,7 @@ static VOID PtfsDelete(PTFS *Ptfs);
|
||||
static NTSTATUS PtfsCreate(PWSTR Path, PWSTR VolumePrefix, PWSTR MountPoint, UINT32 DebugFlags,
|
||||
PTFS **PPtfs)
|
||||
{
|
||||
WCHAR FullPath[MAX_PATH];
|
||||
WCHAR FullPath[FULLPATH_SIZE];
|
||||
ULONG Length;
|
||||
HANDLE Handle;
|
||||
FILETIME CreationTime;
|
||||
|
||||
@@ -27,6 +27,37 @@
|
||||
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
static
|
||||
void notify_invalid_dotest(ULONG Flags)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 1);
|
||||
ASSERT(STATUS_ACCESS_VIOLATION == Result);
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, FSP_FSCTL_NOTIFY_INFO_SIZEMAX);
|
||||
ASSERT(STATUS_ACCESS_VIOLATION == Result || STATUS_INSUFFICIENT_RESOURCES == Result);
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, FSP_FSCTL_NOTIFY_INFO_SIZEMAX + 1);
|
||||
ASSERT(STATUS_INVALID_PARAMETER == Result);
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0xffffffffU);
|
||||
ASSERT(STATUS_INVALID_PARAMETER == Result);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_invalid_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_invalid_dotest(MemfsDisk);
|
||||
if (WinFspNetTests)
|
||||
notify_invalid_dotest(MemfsNet);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_abandon_dotest(ULONG Flags)
|
||||
{
|
||||
@@ -474,11 +505,101 @@ void notify_dirnotify_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
struct notify_rename_race_params
|
||||
{
|
||||
WCHAR Src[MAX_PATH];
|
||||
WCHAR Dst[MAX_PATH];
|
||||
HANDLE StartEvent;
|
||||
};
|
||||
|
||||
static
|
||||
unsigned __stdcall notify_rename_race_dotest_thread(void *Params0)
|
||||
{
|
||||
struct notify_rename_race_params *Params = Params0;
|
||||
|
||||
WaitForSingleObject(Params->StartEvent, INFINITE);
|
||||
|
||||
BOOL Success = MoveFileExW(Params->Src, Params->Dst, 0);
|
||||
|
||||
return Success ? 0 : GetLastError();
|
||||
}
|
||||
|
||||
static
|
||||
void notify_rename_race_dotest(ULONG Flags, PWSTR Prefix)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + 2 * sizeof(WCHAR)];
|
||||
} NotifyInfo;
|
||||
struct notify_rename_race_params Params;
|
||||
HANDLE Thread;
|
||||
DWORD ExitCode;
|
||||
NTSTATUS Result;
|
||||
HANDLE Handle;
|
||||
PWSTR FileName;
|
||||
|
||||
StringCbPrintfW(Params.Src, sizeof Params.Src, L"%s%s\\file0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
StringCbPrintfW(Params.Dst, sizeof Params.Dst, L"%s%s\\file0.new",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Handle = CreateFileW(Params.Src,
|
||||
GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
CloseHandle(Handle);
|
||||
|
||||
FileName = L"\\";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
|
||||
Params.StartEvent = CreateEventW(0, TRUE, FALSE, 0);
|
||||
ASSERT(0 != Params.StartEvent);
|
||||
|
||||
Thread = (HANDLE)_beginthreadex(0, 0, notify_rename_race_dotest_thread, &Params, 0, 0);
|
||||
ASSERT(0 != Thread);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 1000);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
SetEvent(Params.StartEvent);
|
||||
Sleep(1000); /* wait for rename IRP to enter the kernel and block */
|
||||
|
||||
Result = FspFileSystemNotify(FileSystem, &NotifyInfo.V, NotifyInfo.V.Size);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
Result = FspFileSystemNotifyEnd(FileSystem);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
WaitForSingleObject(Thread, INFINITE);
|
||||
GetExitCodeThread(Thread, &ExitCode);
|
||||
CloseHandle(Thread);
|
||||
ASSERT(STATUS_SUCCESS == ExitCode);
|
||||
|
||||
CloseHandle(Params.StartEvent);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_rename_race_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_rename_race_dotest(MemfsDisk, 0);
|
||||
if (WinFspNetTests)
|
||||
notify_rename_race_dotest(MemfsNet, L"\\\\memfs\\share");
|
||||
}
|
||||
|
||||
void notify_tests(void)
|
||||
{
|
||||
if (OptExternal || OptNotify)
|
||||
return;
|
||||
|
||||
TEST(notify_invalid_test);
|
||||
TEST(notify_abandon_test);
|
||||
TEST(notify_abandon_rename_test);
|
||||
/* OBSOLETE: it is now possible to have multiple outstanding NotifyBegin() calls. */
|
||||
@@ -489,4 +610,5 @@ void notify_tests(void)
|
||||
TEST(notify_change_test);
|
||||
TEST(notify_open_change_test);
|
||||
TEST(notify_dirnotify_test);
|
||||
TEST(notify_rename_race_test);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user