mirror of
https://github.com/winfsp/winfsp.git
synced 2026-06-24 21:42:33 -05:00
sys: fix FileRenameResource self-deadlock between notify session and FspVolumeNotifyWork
Under heavy concurrent rename + change-notification load a volume can deadlock permanently: all renames (exclusive) and opens (shared) on the volume block, freezing the mount. FspFileSystemNotifyBegin (FspVolumeNotifyLock) acquires the per-volume FileRenameResource shared via an owner pointer (&VolumeNotifyCount) and holds it for the whole Begin/End session. If a rename queues as an exclusive waiter mid-session, the asynchronous FspVolumeNotifyWork then re-acquires the same resource shared with ExAcquireResourceSharedLite. Due to ERESOURCE writer-priority that shared acquire blocks behind the queued exclusive waiter (the worker thread is not the owner -- the owner is the &VolumeNotifyCount pointer). But that work item is the one that must process FspFileSystemNotifyEnd to drop VolumeNotifyCount to 0 and release the session, so it can never run: the session lock is never released and the rename waits forever, while VolumeNotifyCount runs away as Begin keeps incrementing it. Acquire the rename resource in FspVolumeNotifyWork with ExAcquireSharedStarveExclusive instead. The enclosing Begin/End session already holds the resource shared and already defers renames until End, so granting this redundant shared acquire ahead of the queued exclusive waiter preserves name-stability semantics while breaking the deadlock. A real exclusive holder still blocks the starve-exclusive acquire, so correctness is unchanged.
This commit is contained in:
@@ -1363,6 +1363,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);
|
||||
|
||||
+9
-1
@@ -1499,7 +1499,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;
|
||||
|
||||
Reference in New Issue
Block a user