mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 09:22:57 -05:00
sys: notify implementation
This commit is contained in:
173
src/sys/volume.c
173
src/sys/volume.c
@ -52,6 +52,7 @@ NTSTATUS FspVolumeStop(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeNotify(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
static WORKER_THREAD_ROUTINE FspVolumeNotifyWork;
|
||||
NTSTATUS FspVolumeWork(
|
||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
|
||||
@ -71,6 +72,7 @@ NTSTATUS FspVolumeWork(
|
||||
#pragma alloc_text(PAGE, FspVolumeTransactFsext)
|
||||
#pragma alloc_text(PAGE, FspVolumeStop)
|
||||
#pragma alloc_text(PAGE, FspVolumeNotify)
|
||||
#pragma alloc_text(PAGE, FspVolumeNotifyWork)
|
||||
#pragma alloc_text(PAGE, FspVolumeWork)
|
||||
#endif
|
||||
|
||||
@ -1057,22 +1059,187 @@ NTSTATUS FspVolumeStop(
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PDEVICE_OBJECT FsvolDeviceObject;
|
||||
PVOID InputBuffer;
|
||||
ULONG InputBufferLength;
|
||||
WORK_QUEUE_ITEM WorkItem;
|
||||
} FSP_VOLUME_NOTIFY_WORK_ITEM;
|
||||
|
||||
NTSTATUS FspVolumeNotify(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/*
|
||||
* FspVolumeNotify processing requires multiple locks that cannot be acquired
|
||||
* synchronously or deadlocks are possible. (The reason is that FspVolumeNotify
|
||||
* may be called by the user mode file system while servicing a request that
|
||||
* has already acquired one of the required locks.)
|
||||
*
|
||||
* For this reason FspVolumeNotify does its processing asynchronously; it ships
|
||||
* its payload as a work item to a system worker thread, which will perform the
|
||||
* actual processing. See FspVolumeNotifyWork.
|
||||
*/
|
||||
|
||||
ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction);
|
||||
ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction);
|
||||
ASSERT(FSP_FSCTL_NOTIFY == IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||||
ASSERT(METHOD_NEITHER == (IrpSp->Parameters.FileSystemControl.FsControlCode & 3));
|
||||
ASSERT(0 != IrpSp->FileObject->FsContext2);
|
||||
|
||||
#if 0
|
||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
#endif
|
||||
PVOID InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
|
||||
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||||
FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = 0;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (!FspDeviceReference(FsvolDeviceObject))
|
||||
return STATUS_CANCELLED;
|
||||
|
||||
NotifyWorkItem = FspAllocNonPaged(sizeof *NotifyWorkItem);
|
||||
if (0 == NotifyWorkItem)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
NotifyWorkItem->FsvolDeviceObject = FsvolDeviceObject;
|
||||
|
||||
NotifyWorkItem->InputBuffer = FspAllocNonPaged(InputBufferLength);
|
||||
if (0 == NotifyWorkItem->InputBuffer)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProbeForRead(InputBuffer, InputBufferLength, 1);
|
||||
RtlCopyMemory(NotifyWorkItem->InputBuffer, InputBuffer, InputBufferLength);
|
||||
NotifyWorkItem->InputBufferLength = InputBufferLength;
|
||||
}
|
||||
except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
Result = GetExceptionCode();
|
||||
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ExInitializeWorkItem(&NotifyWorkItem->WorkItem, FspVolumeNotifyWork, NotifyWorkItem);
|
||||
ExQueueWorkItem(&NotifyWorkItem->WorkItem, DelayedWorkQueue);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
fail:
|
||||
if (0 != NotifyWorkItem)
|
||||
{
|
||||
if (0 != NotifyWorkItem->InputBuffer)
|
||||
FspFree(NotifyWorkItem->InputBuffer);
|
||||
FspFree(NotifyWorkItem);
|
||||
}
|
||||
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FsRtlEnterFileSystem();
|
||||
IoSetTopLevelIrp(0);
|
||||
|
||||
FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = NotifyWorkItem0;
|
||||
PDEVICE_OBJECT FsvolDeviceObject = NotifyWorkItem->FsvolDeviceObject;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo = NotifyWorkItem->InputBuffer;
|
||||
PUINT8 NotifyInfoEnd = (PUINT8)NotifyInfo + NotifyWorkItem->InputBufferLength;
|
||||
ULONG NotifyInfoSize;
|
||||
UNICODE_STRING FileName = { 0 }, StreamPart = { 0 }, AbsFileName = { 0 }, FullFileName = { 0 };
|
||||
ULONG StreamType = FspFileNameStreamTypeNone;
|
||||
NTSTATUS Result;
|
||||
|
||||
/*
|
||||
* Acquire the rename lock shared to disallow concurrent RENAME's.
|
||||
*
|
||||
* This guards against the race where a file that we want to invalidate
|
||||
* is being concurrently renamed to a different name. Thus we may think
|
||||
* that the file is not open and not invalidate its caches, whereas the
|
||||
* file has simply changed name.
|
||||
*/
|
||||
FspFsvolDeviceFileRenameAcquireShared(FsvolDeviceObject);
|
||||
|
||||
/* iterate over notify information and invalidate/notify each file */
|
||||
for (; (PUINT8)NotifyInfo + sizeof(NotifyInfo->Size) <= NotifyInfoEnd;
|
||||
NotifyInfo = (PVOID)((PUINT8)NotifyInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(NotifyInfoSize)))
|
||||
{
|
||||
NotifyInfoSize = NotifyInfo->Size;
|
||||
|
||||
if (sizeof(FSP_FSCTL_NOTIFY_INFO) > NotifyInfoSize)
|
||||
break;
|
||||
|
||||
FileName.Length =
|
||||
FileName.MaximumLength = (USHORT)(NotifyInfoSize - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FileName.Buffer = NotifyInfo->FileNameBuf;
|
||||
|
||||
if (!FspFileNameIsValid(&FileName, FsvolDeviceExtension->VolumeParams.MaxComponentLength,
|
||||
FsvolDeviceExtension->VolumeParams.NamedStreams ? &StreamPart : 0,
|
||||
&StreamType))
|
||||
continue;
|
||||
|
||||
if (sizeof(WCHAR) <= FileName.Length && L'\\' == FileName.Buffer[0])
|
||||
{
|
||||
/* absolute file names are used as-is */
|
||||
|
||||
AbsFileName = FileName;
|
||||
|
||||
FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject,
|
||||
&FileName, NotifyInfo->Filter, NotifyInfo->Action,
|
||||
TRUE);
|
||||
}
|
||||
else if (0 != AbsFileName.Length)
|
||||
{
|
||||
/* relative file names are considered relative to the last absolute file name */
|
||||
|
||||
if (0 == FullFileName.Buffer)
|
||||
{
|
||||
FullFileName.Buffer = FspAllocatePoolMustSucceed(
|
||||
NonPagedPool, FSP_FSCTL_TRANSACT_PATH_SIZEMAX, FSP_ALLOC_INTERNAL_TAG);
|
||||
FullFileName.MaximumLength = FSP_FSCTL_TRANSACT_PATH_SIZEMAX;
|
||||
}
|
||||
|
||||
FullFileName.Length = 0;
|
||||
Result = RtlAppendUnicodeStringToString(&FullFileName, &AbsFileName);
|
||||
if (NT_SUCCESS(Result))
|
||||
{
|
||||
if (sizeof(WCHAR) * 2/* not empty or root */ <= AbsFileName.Length &&
|
||||
L'\\' == AbsFileName.Buffer[AbsFileName.Length / sizeof(WCHAR) - 1])
|
||||
Result = RtlAppendUnicodeToString(&FullFileName, L"\\");
|
||||
}
|
||||
if (NT_SUCCESS(Result))
|
||||
Result = RtlAppendUnicodeStringToString(&FullFileName, &FileName);
|
||||
|
||||
if (NT_SUCCESS(Result))
|
||||
FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject,
|
||||
&FullFileName, NotifyInfo->Filter, NotifyInfo->Action,
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
|
||||
|
||||
if (0 != FullFileName.Buffer)
|
||||
FspFree(FullFileName.Buffer);
|
||||
|
||||
FspFree(NotifyWorkItem->InputBuffer);
|
||||
FspFree(NotifyWorkItem);
|
||||
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
FsRtlExitFileSystem();
|
||||
}
|
||||
|
||||
NTSTATUS FspVolumeWork(
|
||||
|
Reference in New Issue
Block a user