diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 3a66a9ad..0964a148 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -67,7 +67,7 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = #define FSP_FSCTL_STOP \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'S', METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSP_FSCTL_NOTIFY \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'n', METHOD_BUFFERED, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'n', METHOD_NEITHER, FILE_ANY_ACCESS) /* fsctl internal device codes (usable only in-kernel) */ #define FSP_FSCTL_TRANSACT_INTERNAL \ diff --git a/src/sys/driver.h b/src/sys/driver.h index 2c249ebb..8abbe2ce 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -1361,8 +1361,10 @@ typedef struct FAST_MUTEX HeaderFastMutex; SECTION_OBJECT_POINTERS SectionObjectPointers; KSPIN_LOCK NpInfoSpinLock; /* allows to invalidate non-page Info w/o resources acquired */ + UINT64 Security; UINT64 DirInfo; UINT64 StreamInfo; + UINT64 Ea; } FSP_FILE_NODE_NONPAGED; typedef struct FSP_FILE_NODE { @@ -1393,11 +1395,9 @@ typedef struct FSP_FILE_NODE UINT64 ChangeTime; UINT32 EaSize; ULONG FileInfoChangeNumber; - UINT64 Security; ULONG SecurityChangeNumber; ULONG DirInfoChangeNumber; ULONG StreamInfoChangeNumber; - UINT64 Ea; ULONG EaChangeNumber; ULONG EaChangeCount; BOOLEAN TruncateOnClose; @@ -1542,6 +1542,7 @@ BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, P VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG SecurityChangeNumber); +VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode); static inline ULONG FspFileNodeSecurityChangeNumber(FSP_FILE_NODE *FileNode) { @@ -1575,6 +1576,7 @@ BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG EaChangeNumber); +VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode); static inline ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode) { @@ -1584,6 +1586,9 @@ ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode) } VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches); +VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject, + PUNICODE_STRING FileName, ULONG Filter, ULONG Action, + BOOLEAN InvalidateParentCaches); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc); VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc); diff --git a/src/sys/file.c b/src/sys/file.c index 67dadecc..aa072824 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -76,6 +76,7 @@ BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, P VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG SecurityChangeNumber); +VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode); BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize); VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, @@ -93,8 +94,12 @@ BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size); BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG EaChangeNumber); +VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode); VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches); +VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject, + PUNICODE_STRING FileName, ULONG Filter, ULONG Action, + BOOLEAN InvalidateParentCaches); NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp); static NTSTATUS FspFileNodeCompleteLockIrp(PVOID Context, PIRP Irp); NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc); @@ -149,9 +154,10 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); #pragma alloc_text(PAGE, FspFileNodeTrySetFileInfoAndSecurityOnOpen) #pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo) #pragma alloc_text(PAGE, FspFileNodeInvalidateFileInfo) -#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity) -#pragma alloc_text(PAGE, FspFileNodeSetSecurity) -#pragma alloc_text(PAGE, FspFileNodeTrySetSecurity) +// !#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity) +// !#pragma alloc_text(PAGE, FspFileNodeSetSecurity) +// !#pragma alloc_text(PAGE, FspFileNodeTrySetSecurity) +// !#pragma alloc_text(PAGE, FspFileNodeInvalidateSecurity) // !#pragma alloc_text(PAGE, FspFileNodeReferenceDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeSetDirInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetDirInfo) @@ -162,10 +168,12 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp); // !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo) // !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo) -#pragma alloc_text(PAGE, FspFileNodeReferenceEa) -#pragma alloc_text(PAGE, FspFileNodeSetEa) -#pragma alloc_text(PAGE, FspFileNodeTrySetEa) +// !#pragma alloc_text(PAGE, FspFileNodeReferenceEa) +// !#pragma alloc_text(PAGE, FspFileNodeSetEa) +// !#pragma alloc_text(PAGE, FspFileNodeTrySetEa) +// !#pragma alloc_text(PAGE, FspFileNodeInvalidateEa) #pragma alloc_text(PAGE, FspFileNodeNotifyChange) +#pragma alloc_text(PAGE, FspFileNodeInvalidateCachesAndNotifyChangeByName) #pragma alloc_text(PAGE, FspFileNodeProcessLockIrp) #pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp) #pragma alloc_text(PAGE, FspFileDescCreate) @@ -368,10 +376,10 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode) FsRtlTeardownPerStreamContexts(&FileNode->Header); - FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea); + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->NonPaged->Ea); FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo); FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo); - FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); + FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->NonPaged->Security); FspDeviceDereference(FileNode->FsvolDeviceObject); @@ -1905,38 +1913,54 @@ VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode) BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) { - PAGED_CODE(); + // !PAGED_CODE(); if (0 != FileNode->MainFileNode) FileNode = FileNode->MainFileNode; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + UINT64 Security; + + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + Security = NonPaged->Security; return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->SecurityCache, - FileNode->Security, PBuffer, PSize); + Security, PBuffer, PSize); } VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) { - PAGED_CODE(); + // !PAGED_CODE(); if (0 != FileNode->MainFileNode) FileNode = FileNode->MainFileNode; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 Security; - FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security); - FileNode->Security = 0 != Buffer ? + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + Security = NonPaged->Security; + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, Security); + Security = 0 != Buffer ? FspMetaCacheAddItem(FsvolDeviceExtension->SecurityCache, Buffer, Size) : 0; FileNode->SecurityChangeNumber++; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateSecurity */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + NonPaged->Security = Security; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); } BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG SecurityChangeNumber) { - PAGED_CODE(); + // !PAGED_CODE(); if (FspFileNodeSecurityChangeNumber(FileNode) != SecurityChangeNumber) return FALSE; @@ -1945,6 +1969,27 @@ BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG return TRUE; } +VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode) +{ + // !PAGED_CODE(); + + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 Security; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetSecurity */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + Security = NonPaged->Security; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, Security); +} + BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) { // !PAGED_CODE(); @@ -2130,38 +2175,54 @@ VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode) BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize) { - PAGED_CODE(); + // !PAGED_CODE(); if (0 != FileNode->MainFileNode) FileNode = FileNode->MainFileNode; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + UINT64 Ea; + + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + Ea = NonPaged->Ea; return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->EaCache, - FileNode->Ea, PBuffer, PSize); + Ea, PBuffer, PSize); } VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size) { - PAGED_CODE(); + // !PAGED_CODE(); if (0 != FileNode->MainFileNode) FileNode = FileNode->MainFileNode; FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 Ea; - FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea); - FileNode->Ea = 0 != Buffer ? + /* no need to acquire the NpInfoSpinLock as the FileNode is acquired */ + Ea = NonPaged->Ea; + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, Ea); + Ea = 0 != Buffer ? FspMetaCacheAddItem(FsvolDeviceExtension->EaCache, Buffer, Size) : 0; FileNode->EaChangeNumber++; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateEa */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + NonPaged->Ea = Ea; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); } BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, ULONG EaChangeNumber) { - PAGED_CODE(); + // !PAGED_CODE(); if (FspFileNodeEaChangeNumber(FileNode) != EaChangeNumber) return FALSE; @@ -2170,6 +2231,27 @@ BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size, return TRUE; } +VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode) +{ + // !PAGED_CODE(); + + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + + PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged; + KIRQL Irql; + UINT64 Ea; + + /* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetEa */ + KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql); + Ea = NonPaged->Ea; + KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql); + + FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, Ea); +} + VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action, BOOLEAN InvalidateCaches) { @@ -2232,6 +2314,120 @@ VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action } } +VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject, + PUNICODE_STRING FileName, ULONG Filter, ULONG Action, + BOOLEAN InvalidateParentCaches) +{ + PAGED_CODE(); + + FSP_FILE_NODE *FileNode; + + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + FileNode = FspFsvolDeviceLookupContextByName(FsvolDeviceObject, FileName); + if (0 != FileNode) + FspFileNodeReference(FileNode); + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + + if (0 != FileNode) + { + FspFileNodeAcquireExclusive(FileNode, Full); + + PFILE_OBJECT CcFileObject = + CcGetFileObjectFromSectionPtrsRef(&FileNode->NonPaged->SectionObjectPointers); + if (0 != CcFileObject) + { + IO_STATUS_BLOCK IoStatus; + CACHE_UNINITIALIZE_EVENT UninitializeEvent; + + FspCcFlushCache(CcFileObject->SectionObjectPointer, 0, 0, &IoStatus); + CcPurgeCacheSection(CcFileObject->SectionObjectPointer, 0, 0, TRUE); + if (0 != CcFileObject->SectionObjectPointer->SharedCacheMap) + { + UninitializeEvent.Next = 0; + KeInitializeEvent(&UninitializeEvent.Event, NotificationEvent, FALSE); + BOOLEAN CacheStopped = CcUninitializeCacheMap(CcFileObject, 0, &UninitializeEvent); + (VOID)CacheStopped; ASSERT(CacheStopped); + KeWaitForSingleObject(&UninitializeEvent.Event, Executive, KernelMode, FALSE, 0); + } + + ObDereferenceObject(CcFileObject); + } + + FspFileNodeInvalidateFileInfo(FileNode); + FspFileNodeInvalidateSecurity(FileNode); + FspFileNodeInvalidateDirInfo(FileNode); + FspFileNodeInvalidateStreamInfo(FileNode); + FspFileNodeInvalidateEa(FileNode); + + FspFileNodeNotifyChange(FileNode, Filter, Action, InvalidateParentCaches); + + FspFileNodeRelease(FileNode, Full); + FspFileNodeDereference(FileNode); + } + else + { + FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject); + UNICODE_STRING Parent, Suffix; + BOOLEAN IsStream; + + IsStream = FALSE; + for (PWSTR P = FileName->Buffer, EndP = P + FileName->Length / sizeof(WCHAR); EndP > P; P++) + if (L':' == *P) + { + IsStream = TRUE; + break; + } + + if (IsStream) + { + if (FlagOn(Filter, FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME)) + SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_NAME); + if (FlagOn(Filter, FILE_NOTIFY_CHANGE_SIZE)) + SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_SIZE); + if (FlagOn(Filter, FILE_NOTIFY_CHANGE_LAST_WRITE)) + SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_WRITE); + ClearFlag(Filter, ~(FILE_NOTIFY_CHANGE_STREAM_NAME | FILE_NOTIFY_CHANGE_STREAM_SIZE | + FILE_NOTIFY_CHANGE_STREAM_WRITE)); + + switch (Action) + { + case FILE_ACTION_ADDED: + Action = FILE_ACTION_ADDED_STREAM; + break; + case FILE_ACTION_REMOVED: + Action = FILE_ACTION_REMOVED_STREAM; + break; + case FILE_ACTION_MODIFIED: + Action = FILE_ACTION_MODIFIED_STREAM; + break; + } + } + + if (0 != Filter) + { + FspFileNameSuffix(FileName, &Parent, &Suffix); + + if (InvalidateParentCaches) + { + FspFsvolDeviceInvalidateVolumeInfo(FsvolDeviceObject); + if (!IsStream) + { + if (sizeof(WCHAR) == FileNode->FileName.Length && L'\\' == FileNode->FileName.Buffer[0]) + ; /* root does not have a parent */ + else + FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent); + } + } + + FspNotifyReportChange( + FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, + FileName, + (USHORT)((PUINT8)Suffix.Buffer - (PUINT8)FileName->Buffer), + 0, Filter, Action); + } + } +} + NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp) { PAGED_CODE(); diff --git a/src/sys/volume.c b/src/sys/volume.c index 81ebfdf3..8d35b7b0 100644 --- a/src/sys/volume.c +++ b/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(