2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/notify.c
5 * PURPOSE: Change Notifications and Sync for File System Drivers
6 * PROGRAMMERS: Pierre Schweitzer
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
18 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange
,
22 FsRtlNotifySetCancelRoutine(IN PIRP Irp
,
23 IN PNOTIFY_CHANGE NotifyChange OPTIONAL
);
27 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject
,
30 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
38 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList
,
41 PLIST_ENTRY NextEntry
;
42 PNOTIFY_CHANGE NotifyChange
;
44 if (!IsListEmpty(NotifyList
))
46 /* Browse the notifications list to find the matching entry */
47 for (NextEntry
= NotifyList
->Flink
;
48 NextEntry
!= NotifyList
;
49 NextEntry
= NextEntry
->Flink
)
51 NotifyChange
= CONTAINING_RECORD(NextEntry
, NOTIFY_CHANGE
, NotifyList
);
52 /* If the current record matches with the given context, it's the good one */
53 if (NotifyChange
->FsContext
== FsContext
&& !IsListEmpty(&(NotifyChange
->NotifyIrps
)))
55 FsRtlNotifyCompleteIrpList(NotifyChange
, STATUS_DELETE_PENDING
);
62 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList
,
65 PLIST_ENTRY NextEntry
;
66 PNOTIFY_CHANGE NotifyChange
;
68 if (!IsListEmpty(NotifyList
))
70 /* Browse the notifications list to find the matching entry */
71 for (NextEntry
= NotifyList
->Flink
;
72 NextEntry
!= NotifyList
;
73 NextEntry
= NextEntry
->Flink
)
75 NotifyChange
= CONTAINING_RECORD(NextEntry
, NOTIFY_CHANGE
, NotifyList
);
76 /* If the current record matches with the given context, it's the good one */
77 if (NotifyChange
->FsContext
== FsContext
)
88 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync
)
90 ULONG_PTR CurrentThread
= (ULONG_PTR
)KeGetCurrentThread();
92 /* Only acquire fast mutex if it's not already acquired by the current thread */
93 if (RealNotifySync
->OwningThread
!= CurrentThread
)
95 ExAcquireFastMutexUnsafe(&(RealNotifySync
->FastMutex
));
96 RealNotifySync
->OwningThread
= CurrentThread
;
98 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
99 RealNotifySync
->OwnerCount
++;
106 FsRtlNotifyCompleteIrp(IN PIRP Irp
,
107 IN PNOTIFY_CHANGE NotifyChange
,
110 IN BOOLEAN SkipCompletion
)
113 PIO_STACK_LOCATION Stack
;
117 /* Check if we need to complete */
118 if (!FsRtlNotifySetCancelRoutine(Irp
, NotifyChange
) && SkipCompletion
)
123 /* No succes => no data to return just complete */
124 if (Status
!= STATUS_SUCCESS
)
129 /* Ensure there's something to return */
130 Stack
= IoGetCurrentIrpStackLocation(Irp
);
131 if (!DataLength
|| Stack
->Parameters
.NotifyDirectory
.Length
< DataLength
)
133 Status
= STATUS_NOTIFY_ENUM_DIR
;
137 /* Ensture there's a buffer where to find data */
138 if (!NotifyChange
->AllocatedBuffer
)
140 Irp
->IoStatus
.Information
= DataLength
;
141 NotifyChange
->Buffer
= NULL
;
145 /* Now, browse all the way to return data
146 * and find the one that will work. We will
147 * return data whatever happens
149 if (Irp
->AssociatedIrp
.SystemBuffer
)
151 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
152 goto CopyAndComplete
;
157 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
158 goto CopyAndComplete
;
161 if (!(Stack
->Control
& SL_PENDING_RETURNED
))
163 Buffer
= Irp
->UserBuffer
;
164 goto CopyAndComplete
;
167 Irp
->Flags
|= (IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_SYNCHRONOUS_PAGING_IO
);
168 Irp
->AssociatedIrp
.SystemBuffer
= NotifyChange
->AllocatedBuffer
;
169 /* Nothing to copy */
170 goto ReleaseAndComplete
;
175 RtlCopyMemory(Buffer
, NotifyChange
->AllocatedBuffer
, DataLength
);
177 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
184 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
186 /* Release buffer UNLESS it's used */
187 if (NotifyChange
->AllocatedBuffer
!= Irp
->AssociatedIrp
.SystemBuffer
&&
188 NotifyChange
->AllocatedBuffer
)
190 ExFreePoolWithTag(NotifyChange
->AllocatedBuffer
, 0);
193 /* Prepare for return */
194 NotifyChange
->AllocatedBuffer
= 0;
195 NotifyChange
->ThisBufferLength
= 0;
196 Irp
->IoStatus
.Information
= DataLength
;
197 NotifyChange
->Buffer
= NULL
;
199 /* Finally complete */
201 IoMarkIrpPending(Irp
);
202 Irp
->IoStatus
.Status
= Status
;
203 IoCompleteRequest(Irp
, EVENT_INCREMENT
);
210 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange
,
215 PLIST_ENTRY NextEntry
;
217 DataLength
= NotifyChange
->DataLength
;
219 NotifyChange
->Flags
&= (INVALIDATE_BUFFERS
| WATCH_TREE
);
220 NotifyChange
->DataLength
= 0;
221 NotifyChange
->LastEntry
= 0;
223 while (!IsListEmpty(&(NotifyChange
->NotifyIrps
)))
225 /* We take the first entry */
226 NextEntry
= RemoveHeadList(&(NotifyChange
->NotifyIrps
));
227 Irp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
229 FsRtlNotifyCompleteIrp(Irp
, NotifyChange
, DataLength
, Status
, TRUE
);
230 /* If we're notifying success, just notify first one */
231 if (Status
== STATUS_SUCCESS
)
238 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync
)
240 RealNotifySync
->OwnerCount
--;
241 /* Release the fast mutex only if no other instance needs it */
242 if (!RealNotifySync
->OwnerCount
)
244 ExReleaseFastMutexUnsafe(&(RealNotifySync
->FastMutex
));
245 RealNotifySync
->OwningThread
= (ULONG_PTR
)0;
253 FsRtlNotifySetCancelRoutine(IN PIRP Irp
,
254 IN PNOTIFY_CHANGE NotifyChange OPTIONAL
)
256 PDRIVER_CANCEL CancelRoutine
;
258 /* Acquire cancel lock */
259 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
261 /* If NotifyChange was given */
264 /* First get cancel routine */
265 CancelRoutine
= IoSetCancelRoutine(Irp
, NULL
);
266 Irp
->IoStatus
.Information
= 0;
267 /* Release cancel lock */
268 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
269 /* If there was a cancel routine */
272 /* Decrease reference count */
273 InterlockedDecrement((PLONG
)&NotifyChange
->ReferenceCount
);
274 /* Notify that we removed cancel routine */
280 /* If IRP is cancel, call FsRtl cancel routine */
283 FsRtlCancelNotify(NULL
, Irp
);
287 /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
288 IoSetCancelRoutine(Irp
, FsRtlCancelNotify
);
290 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
294 /* Return that we didn't removed cancel routine */
298 /* PUBLIC FUNCTIONS **********************************************************/
301 * @name FsRtlNotifyChangeDirectory
304 * Lets FSD know if changes occures in the specified directory.
305 * Directory will be reenumerated.
308 * Synchronization object pointer
311 * Used to identify the notify structure
313 * @param FullDirectoryName
314 * String (A or W) containing the full directory name
317 * Notify list pointer (to head)
320 * True to notify changes in subdirectories too
322 * @param CompletionFilter
323 * Used to define types of changes to notify
326 * IRP pointer to complete notify operation. It can be null
330 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
335 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
337 IN PSTRING FullDirectoryName
,
338 IN PLIST_ENTRY NotifyList
,
339 IN BOOLEAN WatchTree
,
340 IN ULONG CompletionFilter
,
343 FsRtlNotifyFilterChangeDirectory(NotifySync
,
357 * @name FsRtlNotifyCleanup
360 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
363 * Synchronization object pointer
366 * Notify list pointer (to head)
369 * Used to identify the notify structure
378 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync
,
379 IN PLIST_ENTRY NotifyList
,
382 PNOTIFY_CHANGE NotifyChange
;
383 PREAL_NOTIFY_SYNC RealNotifySync
;
384 PSECURITY_SUBJECT_CONTEXT SubjectContext
= NULL
;
386 /* Get real structure hidden behind the opaque pointer */
387 RealNotifySync
= (PREAL_NOTIFY_SYNC
)NotifySync
;
389 /* Acquire the fast mutex */
390 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
394 /* Find if there's a matching notification with the FsContext */
395 NotifyChange
= FsRtlIsNotifyOnList(NotifyList
, FsContext
);
398 /* Mark it as to know that cleanup is in process */
399 NotifyChange
->Flags
|= CLEANUP_IN_PROCESS
;
401 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
402 if (!IsListEmpty(&NotifyChange
->NotifyIrps
))
404 FsRtlNotifyCompleteIrpList(NotifyChange
, STATUS_NOTIFY_CLEANUP
);
407 /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
408 if (!InterlockedDecrement((PLONG
)&(NotifyChange
->ReferenceCount
)))
410 /* Remove it from the notifications list */
411 RemoveEntryList(&NotifyChange
->NotifyList
);
413 /* In case there was an allocated buffer, free it */
414 if (NotifyChange
->AllocatedBuffer
)
416 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
417 ExFreePool(NotifyChange
->AllocatedBuffer
);
420 /* In case there the string was set, get the captured subject security context */
421 if (NotifyChange
->FullDirectoryName
)
423 SubjectContext
= NotifyChange
->SubjectContext
;
426 /* Finally, free the notification, as it's not needed anymore */
427 ExFreePool(NotifyChange
);
433 /* Release fast mutex */
434 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
436 /* If the subject security context was captured, release and free it */
439 SeReleaseSubjectContext(SubjectContext
);
440 ExFreePool(SubjectContext
);
447 * @name FsRtlNotifyFilterChangeDirectory
461 * @param FullDirectoryName
467 * @param IgnoreBuffer
470 * @param CompletionFilter
476 * @param TraverseCallback
479 * @param SubjectContext
482 * @param FilterCallback
492 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
493 IN PLIST_ENTRY NotifyList
,
495 IN PSTRING FullDirectoryName
,
496 IN BOOLEAN WatchTree
,
497 IN BOOLEAN IgnoreBuffer
,
498 IN ULONG CompletionFilter
,
500 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL
,
501 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
,
502 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL
)
505 PIO_STACK_LOCATION Stack
;
506 PNOTIFY_CHANGE NotifyChange
;
507 PREAL_NOTIFY_SYNC RealNotifySync
;
511 DPRINT("FsRtlNotifyFilterChangeDirectory(): %p, %p, %p, %wZ, %d, %d, %u, %p, %p, %p, %p\n",
512 NotifySync
, NotifyList
, FsContext
, FullDirectoryName
, WatchTree
, IgnoreBuffer
, CompletionFilter
, NotifyIrp
,
513 TraverseCallback
, SubjectContext
, FilterCallback
);
515 /* Get real structure hidden behind the opaque pointer */
516 RealNotifySync
= (PREAL_NOTIFY_SYNC
)NotifySync
;
518 /* Acquire the fast mutex */
519 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
523 /* If we have no IRP, FSD is performing a cleanup */
527 FsRtlCheckNotifyForDelete(NotifyList
, FsContext
);
531 NotifyIrp
->IoStatus
.Status
= STATUS_SUCCESS
;
532 NotifyIrp
->IoStatus
.Information
= (ULONG_PTR
)NULL
;
534 Stack
= IoGetCurrentIrpStackLocation(NotifyIrp
);
535 /* If FileObject's been cleaned up, just return */
536 if (Stack
->FileObject
->Flags
& FO_CLEANUP_COMPLETE
)
538 IoMarkIrpPending(NotifyIrp
);
539 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_CLEANUP
;
540 IoCompleteRequest(NotifyIrp
, EVENT_INCREMENT
);
544 /* Try to find a matching notification has been already registered */
545 NotifyChange
= FsRtlIsNotifyOnList(NotifyList
, FsContext
);
548 /* If it's been found, and is cleaned up, immediatly complete */
549 if (NotifyChange
->Flags
& CLEANUP_IN_PROCESS
)
551 IoMarkIrpPending(NotifyIrp
);
552 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_CLEANUP
;
553 IoCompleteRequest(NotifyIrp
, EVENT_INCREMENT
);
555 /* Or if it's about to be deleted, complete */
556 else if (NotifyChange
->Flags
& DELETE_IN_PROCESS
)
558 IoMarkIrpPending(NotifyIrp
);
559 NotifyIrp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
560 IoCompleteRequest(NotifyIrp
, EVENT_INCREMENT
);
562 /* Complete if there is directory enumeration and no buffer available any more */
563 if ((NotifyChange
->Flags
& INVALIDATE_BUFFERS
) && (NotifyChange
->Flags
& ENUMERATE_DIR
))
565 NotifyChange
->Flags
&= ~INVALIDATE_BUFFERS
;
566 IoMarkIrpPending(NotifyIrp
);
567 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_ENUM_DIR
;
568 IoCompleteRequest(NotifyIrp
, EVENT_INCREMENT
);
570 /* If no data yet, or directory enumeration, handle */
571 else if (NotifyChange
->DataLength
== 0 || (NotifyChange
->Flags
& ENUMERATE_DIR
))
575 /* Else, just complete with we have */
578 SavedLength
= NotifyChange
->DataLength
;
579 NotifyChange
->DataLength
= 0;
580 FsRtlNotifyCompleteIrp(NotifyIrp
, NotifyChange
, SavedLength
, STATUS_SUCCESS
, FALSE
);
586 /* Allocate new notification */
587 NotifyChange
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
588 sizeof(NOTIFY_CHANGE
), 'FSrN');
589 RtlZeroMemory(NotifyChange
, sizeof(NOTIFY_CHANGE
));
591 /* Set basic information */
592 NotifyChange
->NotifySync
= NotifySync
;
593 NotifyChange
->FsContext
= FsContext
;
594 NotifyChange
->StreamID
= Stack
->FileObject
->FsContext
;
595 NotifyChange
->TraverseCallback
= TraverseCallback
;
596 NotifyChange
->SubjectContext
= SubjectContext
;
597 NotifyChange
->FullDirectoryName
= FullDirectoryName
;
598 NotifyChange
->FilterCallback
= FilterCallback
;
599 InitializeListHead(&(NotifyChange
->NotifyIrps
));
601 /* Keep trace of WatchTree */
604 NotifyChange
->Flags
|= WATCH_TREE
;
607 /* If string is empty, faulty to ANSI */
608 if (FullDirectoryName
->Length
== 0)
610 NotifyChange
->CharacterSize
= sizeof(CHAR
);
614 /* If it can't contain WCHAR, it's ANSI */
615 if (FullDirectoryName
->Length
< sizeof(WCHAR
))
617 NotifyChange
->CharacterSize
= sizeof(CHAR
);
619 /* First char is \, so in unicode, right part is 0
620 * whereas in ANSI it contains next char
622 else if (((CHAR
*)FullDirectoryName
->Buffer
)[1] == 0)
624 NotifyChange
->CharacterSize
= sizeof(WCHAR
);
628 NotifyChange
->CharacterSize
= sizeof(CHAR
);
631 /* Now, check is user is willing to watch root */
632 if (FullDirectoryName
->Length
== NotifyChange
->CharacterSize
)
634 NotifyChange
->Flags
|= WATCH_ROOT
;
638 NotifyChange
->CompletionFilter
= CompletionFilter
;
640 /* In case we have not to ignore buffer , keep its length */
643 NotifyChange
->BufferLength
= Stack
->Parameters
.NotifyDirectory
.Length
;
646 NotifyChange
->OwningProcess
= NotifyIrp
->Tail
.Overlay
.Thread
->ThreadsProcess
;
648 /* Insert the notification into the notification list */
649 InsertTailList(NotifyList
, &(NotifyChange
->NotifyList
));
651 NotifyChange
->ReferenceCount
= 1;
654 /* Associate the notification to the IRP */
655 NotifyIrp
->IoStatus
.Information
= (ULONG_PTR
)NotifyChange
;
656 /* The IRP is pending */
657 IoMarkIrpPending(NotifyIrp
);
658 /* Insert the IRP in the IRP list */
659 InsertTailList(&(NotifyChange
->NotifyIrps
), &(NotifyIrp
->Tail
.Overlay
.ListEntry
));
660 /* Increment reference count */
661 InterlockedIncrement((PLONG
)&(NotifyChange
->ReferenceCount
));
662 /* Set cancel routine to FsRtl one */
663 FsRtlNotifySetCancelRoutine(NotifyIrp
, NULL
);
667 /* Release fast mutex */
668 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
670 /* If the subject security context was captured and there's no notify */
671 if (SubjectContext
&& (!NotifyChange
|| FullDirectoryName
))
673 SeReleaseSubjectContext(SubjectContext
);
674 ExFreePool(SubjectContext
);
681 * @name FsRtlNotifyFilterReportChange
692 * @param FullTargetName
695 * @param TargetNameOffset
701 * @param NormalizedParentName
710 * @param TargetContext
713 * @param FilterContext
723 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync
,
724 IN PLIST_ENTRY NotifyList
,
725 IN PSTRING FullTargetName
,
726 IN USHORT TargetNameOffset
,
727 IN PSTRING StreamName OPTIONAL
,
728 IN PSTRING NormalizedParentName OPTIONAL
,
729 IN ULONG FilterMatch
,
731 IN PVOID TargetContext
,
732 IN PVOID FilterContext
)
734 KeBugCheck(FILE_SYSTEM
);
738 * @name FsRtlNotifyFullChangeDirectory
741 * Lets FSD know if changes occures in the specified directory.
744 * Synchronization object pointer
747 * Notify list pointer (to head)
750 * Used to identify the notify structure
752 * @param FullDirectoryName
753 * String (A or W) containing the full directory name
756 * True to notify changes in subdirectories too
758 * @param IgnoreBuffer
759 * True to reenumerate directory. It's ignored it NotifyIrp is null
761 * @param CompletionFilter
762 * Used to define types of changes to notify
765 * IRP pointer to complete notify operation. It can be null
767 * @param TraverseCallback
768 * Pointer to a callback function. It's called each time a change is
769 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
772 * @param SubjectContext
773 * Pointer to pass to SubjectContext member of TraverseCallback.
774 * It's freed after use. It's ignored it NotifyIrp is null
778 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
783 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
784 IN PLIST_ENTRY NotifyList
,
786 IN PSTRING FullDirectoryName
,
787 IN BOOLEAN WatchTree
,
788 IN BOOLEAN IgnoreBuffer
,
789 IN ULONG CompletionFilter
,
791 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL
,
792 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
)
794 FsRtlNotifyFilterChangeDirectory(NotifySync
,
808 * @name FsRtlNotifyFullReportChange
811 * Complets the pending notify IRPs.
814 * Synchronization object pointer
817 * Notify list pointer (to head)
819 * @param FullTargetName
820 * String (A or W) containing the full directory name that changed
822 * @param TargetNameOffset
823 * Offset, in FullTargetName, of the final component that is in the changed directory
826 * String (A or W) containing a stream name
828 * @param NormalizedParentName
829 * String (A or W) containing the full directory name that changed with long names
832 * Flags that will be compared to the completion filter
835 * Action code to store in user's buffer
837 * @param TargetContext
838 * Pointer to a callback function. It's called each time a change is
839 * done in a subdirectory of the main directory.
843 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
848 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync
,
849 IN PLIST_ENTRY NotifyList
,
850 IN PSTRING FullTargetName
,
851 IN USHORT TargetNameOffset
,
852 IN PSTRING StreamName OPTIONAL
,
853 IN PSTRING NormalizedParentName OPTIONAL
,
854 IN ULONG FilterMatch
,
856 IN PVOID TargetContext
)
858 FsRtlNotifyFilterReportChange(NotifySync
,
863 NormalizedParentName
,
871 * @name FsRtlNotifyInitializeSync
874 * Allocates the internal structure associated with notifications.
877 * Opaque pointer. It will receive the address of the allocated internal structure.
881 * @remarks This function raise an exception in case of a failure.
886 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC
*NotifySync
)
888 PREAL_NOTIFY_SYNC RealNotifySync
;
892 RealNotifySync
= ExAllocatePoolWithTag(NonPagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
893 sizeof(REAL_NOTIFY_SYNC
), 'FSNS');
894 ExInitializeFastMutex(&(RealNotifySync
->FastMutex
));
895 RealNotifySync
->OwningThread
= 0;
896 RealNotifySync
->OwnerCount
= 0;
898 *NotifySync
= RealNotifySync
;
902 * @name FsRtlNotifyReportChange
905 * Complets the pending notify IRPs.
908 * Synchronization object pointer
911 * Notify list pointer (to head)
913 * @param FullTargetName
914 * String (A or W) containing the full directory name that changed
916 * @param FileNamePartLength
917 * Length of the final component that is in the changed directory
920 * Flags that will be compared to the completion filter
924 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
929 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync
,
930 IN PLIST_ENTRY NotifyList
,
931 IN PSTRING FullTargetName
,
932 IN PUSHORT FileNamePartLength
,
933 IN ULONG FilterMatch
)
935 FsRtlNotifyFilterReportChange(NotifySync
,
938 FullTargetName
->Length
- *FileNamePartLength
,
948 * @name FsRtlNotifyUninitializeSync
951 * Uninitialize a NOTIFY_SYNC object
954 * Address of a pointer to a PNOTIFY_SYNC object previously
955 * initialized by FsRtlNotifyInitializeSync()
964 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC
*NotifySync
)
968 ExFreePoolWithTag(*NotifySync
, 'FSNS');