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 /* INLINED FUNCTIONS *********************************************************/
22 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync
)
24 ULONG_PTR CurrentThread
= (ULONG_PTR
)KeGetCurrentThread();
26 /* Only acquire fast mutex if it's not already acquired by the current thread */
27 if (RealNotifySync
->OwningThread
!= CurrentThread
)
29 ExAcquireFastMutexUnsafe(&(RealNotifySync
->FastMutex
));
30 RealNotifySync
->OwningThread
= CurrentThread
;
32 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
33 RealNotifySync
->OwnerCount
++;
41 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync
)
43 RealNotifySync
->OwnerCount
--;
44 /* Release the fast mutex only if no other instance needs it */
45 if (!RealNotifySync
->OwnerCount
)
47 ExReleaseFastMutexUnsafe(&(RealNotifySync
->FastMutex
));
48 RealNotifySync
->OwningThread
= (ULONG_PTR
)0;
52 #define FsRtlNotifyGetLastPartOffset(FullLen, TargLen, Type, Chr) \
53 for (FullPosition = 0; FullPosition < FullLen; ++FullPosition) \
54 if (((Type)NotifyChange->FullDirectoryName->Buffer)[FullPosition] == Chr) \
55 ++FullNumberOfParts; \
56 for (LastPartOffset = 0; LastPartOffset < TargLen; ++LastPartOffset) { \
57 if ( ((Type)TargetDirectory.Buffer)[LastPartOffset] == Chr) { \
58 ++TargetNumberOfParts; \
59 if (TargetNumberOfParts == FullNumberOfParts) \
64 /* PRIVATE FUNCTIONS *********************************************************/
67 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange
,
71 FsRtlNotifySetCancelRoutine(IN PIRP Irp
,
72 IN PNOTIFY_CHANGE NotifyChange OPTIONAL
);
79 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject
,
85 PIO_STACK_LOCATION Stack
;
86 PNOTIFY_CHANGE NotifyChange
;
87 PREAL_NOTIFY_SYNC RealNotifySync
;
88 PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext
= NULL
;
90 /* Get the NOTIFY_CHANGE struct and reset it */
91 NotifyChange
= (PNOTIFY_CHANGE
)Irp
->IoStatus
.Information
;
92 Irp
->IoStatus
.Information
= 0;
93 /* Reset the cancel routine */
94 IoSetCancelRoutine(Irp
, NULL
);
95 /* And release lock */
96 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
97 /* Get REAL_NOTIFY_SYNC struct */
98 RealNotifySync
= NotifyChange
->NotifySync
;
100 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
104 /* Remove the IRP from the notifications list and mark it pending */
105 RemoveEntryList(&(Irp
->Tail
.Overlay
.ListEntry
));
106 IoMarkIrpPending(Irp
);
108 /* Now, the tricky part - let's find a buffer big enough to hold the return data */
109 if (NotifyChange
->Buffer
&& NotifyChange
->AllocatedBuffer
== NULL
&&
110 ((Irp
->MdlAddress
&& MmGetSystemAddressForMdl(Irp
->MdlAddress
) == NotifyChange
->Buffer
) ||
111 NotifyChange
->Buffer
== Irp
->AssociatedIrp
.SystemBuffer
))
113 /* Assume we didn't find any */
117 /* If we don't have IRPs, check if current buffer is big enough */
118 if (IsListEmpty(&NotifyChange
->NotifyIrps
))
120 if (NotifyChange
->BufferLength
>= NotifyChange
->DataLength
)
122 BufferLength
= NotifyChange
->BufferLength
;
127 /* Otherwise, try to look at next IRP available */
128 NotifyIrp
= CONTAINING_RECORD(NotifyChange
->NotifyIrps
.Flink
, IRP
, Tail
.Overlay
.ListEntry
);
129 Stack
= IoGetCurrentIrpStackLocation(NotifyIrp
);
131 /* If its buffer is big enough, get it */
132 if (Stack
->Parameters
.NotifyDirectory
.Length
>= NotifyChange
->BufferLength
)
135 if (NotifyIrp
->AssociatedIrp
.SystemBuffer
== NULL
)
137 if (NotifyIrp
->MdlAddress
!= NULL
)
139 Buffer
= MmGetSystemAddressForMdl(NotifyIrp
->MdlAddress
);
144 Buffer
= NotifyIrp
->AssociatedIrp
.MasterIrp
;
147 /* Backup our accepted buffer length */
148 BufferLength
= Stack
->Parameters
.NotifyDirectory
.Length
;
149 if (BufferLength
> NotifyChange
->BufferLength
)
151 BufferLength
= NotifyChange
->BufferLength
;
156 /* At that point, we *may* have a buffer */
158 /* If it has null length, then note that we won't use it */
159 if (BufferLength
== 0)
161 NotifyChange
->Flags
|= NOTIFY_IMMEDIATELY
;
165 /* If we have a buffer length, but no buffer then allocate one */
168 PsChargePoolQuota(NotifyChange
->OwningProcess
, PagedPool
, BufferLength
);
169 Buffer
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, BufferLength
, TAG_FS_NOTIFICATIONS
);
170 NotifyChange
->AllocatedBuffer
= Buffer
;
173 /* Copy data in that buffer */
174 RtlCopyMemory(Buffer
, NotifyChange
->Buffer
, NotifyChange
->DataLength
);
175 NotifyChange
->ThisBufferLength
= BufferLength
;
176 NotifyChange
->Buffer
= Buffer
;
179 /* If we have to notify immediately, ensure that any buffer is 0-ed out */
180 if (NotifyChange
->Flags
& NOTIFY_IMMEDIATELY
)
182 NotifyChange
->Buffer
= 0;
183 NotifyChange
->AllocatedBuffer
= 0;
184 NotifyChange
->LastEntry
= 0;
185 NotifyChange
->DataLength
= 0;
186 NotifyChange
->ThisBufferLength
= 0;
190 /* It's now time to complete - data are ready */
192 /* Set appropriate status and complete */
193 Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
194 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
196 /* If that notification isn't referenced any longer, drop it */
197 if (!InterlockedDecrement((PLONG
)&(NotifyChange
->ReferenceCount
)))
199 /* If it had an allocated buffer, delete */
200 if (NotifyChange
->AllocatedBuffer
)
202 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
203 ExFreePoolWithTag(NotifyChange
->AllocatedBuffer
, TAG_FS_NOTIFICATIONS
);
206 /* In case of full name, remember subject context for later deletion */
207 if (NotifyChange
->FullDirectoryName
)
209 SubjectContext
= NotifyChange
->SubjectContext
;
212 /* We mustn't have ANY change left anymore */
213 ASSERT(NotifyChange
->NotifyList
.Flink
== NULL
);
214 ExFreePoolWithTag(NotifyChange
, 0);
219 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
221 /* If the subject security context was captured, release and free it */
224 SeReleaseSubjectContext(SubjectContext
);
225 ExFreePool(SubjectContext
);
235 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList
,
238 PLIST_ENTRY NextEntry
;
239 PNOTIFY_CHANGE NotifyChange
;
241 if (!IsListEmpty(NotifyList
))
243 /* Browse the notifications list to find the matching entry */
244 for (NextEntry
= NotifyList
->Flink
;
245 NextEntry
!= NotifyList
;
246 NextEntry
= NextEntry
->Flink
)
248 NotifyChange
= CONTAINING_RECORD(NextEntry
, NOTIFY_CHANGE
, NotifyList
);
249 /* If the current record matches with the given context, it's the good one */
250 if (NotifyChange
->FsContext
== FsContext
&& !IsListEmpty(&(NotifyChange
->NotifyIrps
)))
252 FsRtlNotifyCompleteIrpList(NotifyChange
, STATUS_DELETE_PENDING
);
262 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList
,
265 PLIST_ENTRY NextEntry
;
266 PNOTIFY_CHANGE NotifyChange
;
268 if (!IsListEmpty(NotifyList
))
270 /* Browse the notifications list to find the matching entry */
271 for (NextEntry
= NotifyList
->Flink
;
272 NextEntry
!= NotifyList
;
273 NextEntry
= NextEntry
->Flink
)
275 NotifyChange
= CONTAINING_RECORD(NextEntry
, NOTIFY_CHANGE
, NotifyList
);
276 /* If the current record matches with the given context, it's the good one */
277 if (NotifyChange
->FsContext
== FsContext
)
290 FsRtlNotifyCompleteIrp(IN PIRP Irp
,
291 IN PNOTIFY_CHANGE NotifyChange
,
294 IN BOOLEAN SkipCompletion
)
297 PIO_STACK_LOCATION Stack
;
301 /* Check if we need to complete */
302 if (!FsRtlNotifySetCancelRoutine(Irp
, NotifyChange
) && SkipCompletion
)
307 /* No succes => no data to return just complete */
308 if (Status
!= STATUS_SUCCESS
)
313 /* Ensure there's something to return */
314 Stack
= IoGetCurrentIrpStackLocation(Irp
);
315 if (!DataLength
|| Stack
->Parameters
.NotifyDirectory
.Length
< DataLength
)
317 Status
= STATUS_NOTIFY_ENUM_DIR
;
321 /* Ensture there's a buffer where to find data */
322 if (!NotifyChange
->AllocatedBuffer
)
324 Irp
->IoStatus
.Information
= DataLength
;
325 NotifyChange
->Buffer
= NULL
;
329 /* Now, browse all the way to return data
330 * and find the one that will work. We will
331 * return data whatever happens
333 if (Irp
->AssociatedIrp
.SystemBuffer
)
335 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
336 goto CopyAndComplete
;
341 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
342 goto CopyAndComplete
;
345 if (!(Stack
->Control
& SL_PENDING_RETURNED
))
347 Buffer
= Irp
->UserBuffer
;
348 goto CopyAndComplete
;
351 Irp
->Flags
|= (IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_SYNCHRONOUS_PAGING_IO
);
352 Irp
->AssociatedIrp
.SystemBuffer
= NotifyChange
->AllocatedBuffer
;
353 /* Nothing to copy */
354 goto ReleaseAndComplete
;
359 RtlCopyMemory(Buffer
, NotifyChange
->AllocatedBuffer
, DataLength
);
361 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
368 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
370 /* Release buffer UNLESS it's used */
371 if (NotifyChange
->AllocatedBuffer
!= Irp
->AssociatedIrp
.SystemBuffer
&&
372 NotifyChange
->AllocatedBuffer
)
374 ExFreePoolWithTag(NotifyChange
->AllocatedBuffer
, 0);
377 /* Prepare for return */
378 NotifyChange
->AllocatedBuffer
= 0;
379 NotifyChange
->ThisBufferLength
= 0;
380 Irp
->IoStatus
.Information
= DataLength
;
381 NotifyChange
->Buffer
= NULL
;
383 /* Finally complete */
385 IoMarkIrpPending(Irp
);
386 Irp
->IoStatus
.Status
= Status
;
387 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
394 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange
,
399 PLIST_ENTRY NextEntry
;
401 DataLength
= NotifyChange
->DataLength
;
403 NotifyChange
->Flags
&= (NOTIFY_IMMEDIATELY
| WATCH_TREE
);
404 NotifyChange
->DataLength
= 0;
405 NotifyChange
->LastEntry
= 0;
407 while (!IsListEmpty(&(NotifyChange
->NotifyIrps
)))
409 /* We take the first entry */
410 NextEntry
= RemoveHeadList(&(NotifyChange
->NotifyIrps
));
411 Irp
= CONTAINING_RECORD(NextEntry
, IRP
, Tail
.Overlay
.ListEntry
);
413 FsRtlNotifyCompleteIrp(Irp
, NotifyChange
, DataLength
, Status
, TRUE
);
414 /* If we're notifying success, just notify first one */
415 if (Status
== STATUS_SUCCESS
)
424 FsRtlNotifySetCancelRoutine(IN PIRP Irp
,
425 IN PNOTIFY_CHANGE NotifyChange OPTIONAL
)
427 PDRIVER_CANCEL CancelRoutine
;
429 /* Acquire cancel lock */
430 IoAcquireCancelSpinLock(&Irp
->CancelIrql
);
432 /* If NotifyChange was given */
435 /* First get cancel routine */
436 CancelRoutine
= IoSetCancelRoutine(Irp
, NULL
);
437 Irp
->IoStatus
.Information
= 0;
438 /* Release cancel lock */
439 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
440 /* If there was a cancel routine */
443 /* Decrease reference count */
444 InterlockedDecrement((PLONG
)&NotifyChange
->ReferenceCount
);
445 /* Notify that we removed cancel routine */
451 /* If IRP is cancel, call FsRtl cancel routine */
454 FsRtlCancelNotify(NULL
, Irp
);
458 /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
459 IoSetCancelRoutine(Irp
, FsRtlCancelNotify
);
461 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
465 /* Return that we didn't removed cancel routine */
473 FsRtlNotifyUpdateBuffer(OUT PFILE_NOTIFY_INFORMATION OutputBuffer
,
475 IN PSTRING ParentName
,
476 IN PSTRING TargetName
,
477 IN PSTRING StreamName
,
478 IN BOOLEAN IsUnicode
,
481 /* Unless there's an issue with buffers, there's no reason to fail */
482 BOOLEAN Succeed
= TRUE
;
483 ULONG AlreadyWritten
= 0, ResultSize
;
487 /* Update user buffer with the change that occured
488 * First copy parent name if any
489 * Then copy target name, there's always one
490 * And finally, copy stream name if any
491 * If these names aren't unicode, then convert first
495 OutputBuffer
->NextEntryOffset
= 0;
496 OutputBuffer
->Action
= Action
;
497 OutputBuffer
->FileNameLength
= DataLength
- FIELD_OFFSET(FILE_NOTIFY_INFORMATION
, FileName
);
500 if (ParentName
->Length
)
502 RtlCopyMemory(OutputBuffer
->FileName
, ParentName
->Buffer
, ParentName
->Length
);
503 OutputBuffer
->FileName
[ParentName
->Length
/ sizeof(WCHAR
)] = L
'\\';
504 AlreadyWritten
= ParentName
->Length
+ sizeof(WCHAR
);
506 RtlCopyMemory((PVOID
)((ULONG_PTR
)OutputBuffer
->FileName
+ AlreadyWritten
),
507 TargetName
->Buffer
, TargetName
->Length
);
510 AlreadyWritten
+= TargetName
->Length
;
511 OutputBuffer
->FileName
[AlreadyWritten
/ sizeof(WCHAR
)] = L
':';
512 RtlCopyMemory((PVOID
)((ULONG_PTR
)OutputBuffer
->FileName
+ AlreadyWritten
+ sizeof(WCHAR
)),
513 StreamName
->Buffer
, StreamName
->Length
);
518 if (!ParentName
->Length
)
521 RtlCopyMemory(OutputBuffer
->FileName
, StreamName
->Buffer
, StreamName
->Length
);
525 RtlOemToUnicodeN(OutputBuffer
->FileName
, OutputBuffer
->FileNameLength
,
526 &ResultSize
, ParentName
->Buffer
,
528 OutputBuffer
->FileName
[ResultSize
/ sizeof(WCHAR
)] = L
'\\';
529 AlreadyWritten
= ResultSize
+ sizeof(WCHAR
);
531 RtlOemToUnicodeN((PVOID
)((ULONG_PTR
)OutputBuffer
->FileName
+ AlreadyWritten
),
532 OutputBuffer
->FileNameLength
, &ResultSize
,
533 TargetName
->Buffer
, TargetName
->Length
);
537 AlreadyWritten
+= ResultSize
;
538 OutputBuffer
->FileName
[AlreadyWritten
/ sizeof(WCHAR
)] = L
':';
539 RtlOemToUnicodeN((PVOID
)((ULONG_PTR
)OutputBuffer
->FileName
+ AlreadyWritten
+ sizeof(WCHAR
)),
540 OutputBuffer
->FileNameLength
, &ResultSize
,
541 StreamName
->Buffer
, StreamName
->Length
);
546 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
555 /* PUBLIC FUNCTIONS **********************************************************/
558 * @name FsRtlNotifyChangeDirectory
561 * Lets FSD know if changes occures in the specified directory.
562 * Directory will be reenumerated.
565 * Synchronization object pointer
568 * Used to identify the notify structure
570 * @param FullDirectoryName
571 * String (A or W) containing the full directory name
574 * Notify list pointer (to head)
577 * True to notify changes in subdirectories too
579 * @param CompletionFilter
580 * Used to define types of changes to notify
583 * IRP pointer to complete notify operation. It can be null
587 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
592 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
594 IN PSTRING FullDirectoryName
,
595 IN PLIST_ENTRY NotifyList
,
596 IN BOOLEAN WatchTree
,
597 IN ULONG CompletionFilter
,
600 FsRtlNotifyFilterChangeDirectory(NotifySync
,
614 * @name FsRtlNotifyCleanup
617 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
620 * Synchronization object pointer
623 * Notify list pointer (to head)
626 * Used to identify the notify structure
635 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync
,
636 IN PLIST_ENTRY NotifyList
,
639 PNOTIFY_CHANGE NotifyChange
;
640 PREAL_NOTIFY_SYNC RealNotifySync
;
641 PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext
= NULL
;
643 /* Get real structure hidden behind the opaque pointer */
644 RealNotifySync
= (PREAL_NOTIFY_SYNC
)NotifySync
;
646 /* Acquire the fast mutex */
647 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
651 /* Find if there's a matching notification with the FsContext */
652 NotifyChange
= FsRtlIsNotifyOnList(NotifyList
, FsContext
);
655 /* Mark it as to know that cleanup is in process */
656 NotifyChange
->Flags
|= CLEANUP_IN_PROCESS
;
658 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
659 if (!IsListEmpty(&NotifyChange
->NotifyIrps
))
661 FsRtlNotifyCompleteIrpList(NotifyChange
, STATUS_NOTIFY_CLEANUP
);
664 /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
665 if (!InterlockedDecrement((PLONG
)&(NotifyChange
->ReferenceCount
)))
667 /* Remove it from the notifications list */
668 RemoveEntryList(&NotifyChange
->NotifyList
);
670 /* In case there was an allocated buffer, free it */
671 if (NotifyChange
->AllocatedBuffer
)
673 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
674 ExFreePool(NotifyChange
->AllocatedBuffer
);
677 /* In case there the string was set, get the captured subject security context */
678 if (NotifyChange
->FullDirectoryName
)
680 SubjectContext
= NotifyChange
->SubjectContext
;
683 /* Finally, free the notification, as it's not needed anymore */
684 ExFreePoolWithTag(NotifyChange
, 'FSrN');
690 /* Release fast mutex */
691 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
693 /* If the subject security context was captured, release and free it */
696 SeReleaseSubjectContext(SubjectContext
);
697 ExFreePool(SubjectContext
);
704 * @name FsRtlNotifyFilterChangeDirectory
718 * @param FullDirectoryName
724 * @param IgnoreBuffer
727 * @param CompletionFilter
733 * @param TraverseCallback
736 * @param SubjectContext
739 * @param FilterCallback
749 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
750 IN PLIST_ENTRY NotifyList
,
752 IN PSTRING FullDirectoryName
,
753 IN BOOLEAN WatchTree
,
754 IN BOOLEAN IgnoreBuffer
,
755 IN ULONG CompletionFilter
,
757 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL
,
758 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
,
759 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL
)
762 PIO_STACK_LOCATION Stack
;
763 PNOTIFY_CHANGE NotifyChange
= NULL
;
764 PREAL_NOTIFY_SYNC RealNotifySync
;
768 DPRINT("FsRtlNotifyFilterChangeDirectory(): %p, %p, %p, %wZ, %u, %u, %u, %p, %p, %p, %p\n",
769 NotifySync
, NotifyList
, FsContext
, FullDirectoryName
, WatchTree
, IgnoreBuffer
, CompletionFilter
, NotifyIrp
,
770 TraverseCallback
, SubjectContext
, FilterCallback
);
772 /* Get real structure hidden behind the opaque pointer */
773 RealNotifySync
= (PREAL_NOTIFY_SYNC
)NotifySync
;
775 /* Acquire the fast mutex */
776 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
780 /* If we have no IRP, FSD is performing a cleanup */
784 FsRtlCheckNotifyForDelete(NotifyList
, FsContext
);
788 NotifyIrp
->IoStatus
.Status
= STATUS_SUCCESS
;
789 NotifyIrp
->IoStatus
.Information
= (ULONG_PTR
)NULL
;
791 Stack
= IoGetCurrentIrpStackLocation(NotifyIrp
);
792 /* If FileObject's been cleaned up, just return */
793 if (Stack
->FileObject
->Flags
& FO_CLEANUP_COMPLETE
)
795 IoMarkIrpPending(NotifyIrp
);
796 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_CLEANUP
;
797 IoCompleteRequest(NotifyIrp
, IO_DISK_INCREMENT
);
801 /* Try to find a matching notification has been already registered */
802 NotifyChange
= FsRtlIsNotifyOnList(NotifyList
, FsContext
);
805 /* If it's been found, and is cleaned up, immediatly complete */
806 if (NotifyChange
->Flags
& CLEANUP_IN_PROCESS
)
808 IoMarkIrpPending(NotifyIrp
);
809 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_CLEANUP
;
810 IoCompleteRequest(NotifyIrp
, IO_DISK_INCREMENT
);
812 /* Or if it's about to be deleted, complete */
813 else if (NotifyChange
->Flags
& DELETE_IN_PROCESS
)
815 IoMarkIrpPending(NotifyIrp
);
816 NotifyIrp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
817 IoCompleteRequest(NotifyIrp
, IO_DISK_INCREMENT
);
819 /* Complete now if asked to (and not asked to notify later on) */
820 if ((NotifyChange
->Flags
& NOTIFY_IMMEDIATELY
) && !(NotifyChange
->Flags
& NOTIFY_LATER
))
822 NotifyChange
->Flags
&= ~NOTIFY_IMMEDIATELY
;
823 IoMarkIrpPending(NotifyIrp
);
824 NotifyIrp
->IoStatus
.Status
= STATUS_NOTIFY_ENUM_DIR
;
825 IoCompleteRequest(NotifyIrp
, IO_DISK_INCREMENT
);
827 /* If no data yet, or asked to notify later on, handle */
828 else if (NotifyChange
->DataLength
== 0 || (NotifyChange
->Flags
& NOTIFY_LATER
))
832 /* Else, just complete with we have */
835 SavedLength
= NotifyChange
->DataLength
;
836 NotifyChange
->DataLength
= 0;
837 FsRtlNotifyCompleteIrp(NotifyIrp
, NotifyChange
, SavedLength
, STATUS_SUCCESS
, FALSE
);
843 /* Allocate new notification */
844 NotifyChange
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
845 sizeof(NOTIFY_CHANGE
), 'FSrN');
846 RtlZeroMemory(NotifyChange
, sizeof(NOTIFY_CHANGE
));
848 /* Set basic information */
849 NotifyChange
->NotifySync
= NotifySync
;
850 NotifyChange
->FsContext
= FsContext
;
851 NotifyChange
->StreamID
= Stack
->FileObject
->FsContext
;
852 NotifyChange
->TraverseCallback
= TraverseCallback
;
853 NotifyChange
->SubjectContext
= SubjectContext
;
854 NotifyChange
->FullDirectoryName
= FullDirectoryName
;
855 NotifyChange
->FilterCallback
= FilterCallback
;
856 InitializeListHead(&(NotifyChange
->NotifyIrps
));
858 /* Keep trace of WatchTree */
861 NotifyChange
->Flags
|= WATCH_TREE
;
864 /* If string is empty, faulty to ANSI */
865 if (FullDirectoryName
->Length
== 0)
867 NotifyChange
->CharacterSize
= sizeof(CHAR
);
871 /* If it can't contain WCHAR, it's ANSI */
872 if (FullDirectoryName
->Length
< sizeof(WCHAR
) || ((CHAR
*)FullDirectoryName
->Buffer
)[1] != 0)
874 NotifyChange
->CharacterSize
= sizeof(CHAR
);
878 NotifyChange
->CharacterSize
= sizeof(WCHAR
);
881 /* Now, check is user is willing to watch root */
882 if (FullDirectoryName
->Length
== NotifyChange
->CharacterSize
)
884 NotifyChange
->Flags
|= WATCH_ROOT
;
888 NotifyChange
->CompletionFilter
= CompletionFilter
;
890 /* In case we have not to ignore buffer , keep its length */
893 NotifyChange
->BufferLength
= Stack
->Parameters
.NotifyDirectory
.Length
;
896 NotifyChange
->OwningProcess
= NotifyIrp
->Tail
.Overlay
.Thread
->ThreadsProcess
;
898 /* Insert the notification into the notification list */
899 InsertTailList(NotifyList
, &(NotifyChange
->NotifyList
));
901 NotifyChange
->ReferenceCount
= 1;
904 /* Associate the notification to the IRP */
905 NotifyIrp
->IoStatus
.Information
= (ULONG_PTR
)NotifyChange
;
906 /* The IRP is pending */
907 IoMarkIrpPending(NotifyIrp
);
908 /* Insert the IRP in the IRP list */
909 InsertTailList(&(NotifyChange
->NotifyIrps
), &(NotifyIrp
->Tail
.Overlay
.ListEntry
));
910 /* Increment reference count */
911 InterlockedIncrement((PLONG
)&(NotifyChange
->ReferenceCount
));
912 /* Set cancel routine to FsRtl one */
913 FsRtlNotifySetCancelRoutine(NotifyIrp
, NULL
);
917 /* Release fast mutex */
918 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
920 /* If the subject security context was captured and there's no notify */
921 if (SubjectContext
&& (!NotifyChange
|| NotifyChange
->FullDirectoryName
))
923 SeReleaseSubjectContext(SubjectContext
);
924 ExFreePool(SubjectContext
);
931 * @name FsRtlNotifyFilterReportChange
942 * @param FullTargetName
945 * @param TargetNameOffset
951 * @param NormalizedParentName
960 * @param TargetContext
963 * @param FilterContext
973 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync
,
974 IN PLIST_ENTRY NotifyList
,
975 IN PSTRING FullTargetName
,
976 IN USHORT TargetNameOffset
,
977 IN PSTRING StreamName OPTIONAL
,
978 IN PSTRING NormalizedParentName OPTIONAL
,
979 IN ULONG FilterMatch
,
981 IN PVOID TargetContext
,
982 IN PVOID FilterContext
)
987 PLIST_ENTRY NextEntry
;
988 PIO_STACK_LOCATION Stack
;
989 PNOTIFY_CHANGE NotifyChange
;
990 PREAL_NOTIFY_SYNC RealNotifySync
;
991 PFILE_NOTIFY_INFORMATION FileNotifyInfo
;
992 BOOLEAN IsStream
, IsParent
, PoolQuotaCharged
;
993 STRING TargetDirectory
, TargetName
, ParentName
, IntNormalizedParentName
;
994 ULONG NumberOfBytes
, TargetNumberOfParts
, FullNumberOfParts
, LastPartOffset
, ParentNameOffset
, ParentNameLength
;
995 ULONG DataLength
, AlignedDataLength
;
997 TargetDirectory
.Length
= 0;
998 TargetDirectory
.MaximumLength
= 0;
999 TargetDirectory
.Buffer
= NULL
;
1000 TargetName
.Length
= 0;
1001 TargetName
.MaximumLength
= 0;
1002 TargetName
.Buffer
= NULL
;
1003 ParentName
.Length
= 0;
1004 ParentName
.MaximumLength
= 0;
1005 ParentName
.Buffer
= NULL
;
1010 DPRINT("FsRtlNotifyFilterReportChange(%p, %p, %p, %u, %p, %p, %p, %x, %x, %p, %p)\n",
1011 NotifySync
, NotifyList
, FullTargetName
, TargetNameOffset
, StreamName
, NormalizedParentName
,
1012 FilterMatch
, Action
, TargetContext
, FilterContext
);
1014 /* We need offset in name */
1015 if (!TargetNameOffset
&& FullTargetName
)
1020 /* Get real structure hidden behind the opaque pointer */
1021 RealNotifySync
= (PREAL_NOTIFY_SYNC
)NotifySync
;
1022 /* Acquire lock - will be released in finally block */
1023 FsRtlNotifyAcquireFastMutex(RealNotifySync
);
1026 /* Browse all the registered notifications we have */
1027 for (NextEntry
= NotifyList
->Flink
; NextEntry
!= NotifyList
;
1028 NextEntry
= NextEntry
->Flink
)
1030 /* Try to find an entry matching our change */
1031 NotifyChange
= CONTAINING_RECORD(NextEntry
, NOTIFY_CHANGE
, NotifyList
);
1032 if (FullTargetName
!= NULL
)
1034 ASSERT(NotifyChange
->FullDirectoryName
!= NULL
);
1035 if (!NotifyChange
->FullDirectoryName
->Length
)
1040 if (!(FilterMatch
& NotifyChange
->CompletionFilter
))
1045 /* If no normalized name provided, construct it from full target name */
1046 if (NormalizedParentName
== NULL
)
1048 IntNormalizedParentName
.Buffer
= FullTargetName
->Buffer
;
1049 if (TargetNameOffset
!= NotifyChange
->CharacterSize
)
1051 IntNormalizedParentName
.MaximumLength
=
1052 IntNormalizedParentName
.Length
= TargetNameOffset
- NotifyChange
->CharacterSize
;
1056 IntNormalizedParentName
.MaximumLength
=
1057 IntNormalizedParentName
.Length
= TargetNameOffset
;
1059 NormalizedParentName
= &IntNormalizedParentName
;
1062 /* heh? Watched directory bigger than changed file? */
1063 if (NormalizedParentName
->Length
< NotifyChange
->FullDirectoryName
->Length
)
1068 /* Same len => parent */
1069 if (NormalizedParentName
->Length
== NotifyChange
->FullDirectoryName
->Length
)
1073 /* If not, then, we have to be watching the tree, otherwise we don't have to report such changes */
1074 else if (!(NotifyChange
->Flags
& WATCH_TREE
))
1078 /* And finally, we've to check we're properly \-terminated */
1081 if (!(NotifyChange
->Flags
& WATCH_ROOT
))
1083 if (NotifyChange
->CharacterSize
== sizeof(CHAR
))
1085 if (((PSTR
)NormalizedParentName
->Buffer
)[NotifyChange
->FullDirectoryName
->Length
] != '\\')
1092 if (((PWSTR
)NormalizedParentName
->Buffer
)[NotifyChange
->FullDirectoryName
->Length
/ sizeof (WCHAR
)] != L
'\\')
1102 /* If len matches, then check that both name are equal */
1103 if (!RtlEqualMemory(NormalizedParentName
->Buffer
, NotifyChange
->FullDirectoryName
->Buffer
,
1104 NotifyChange
->FullDirectoryName
->Length
))
1109 /* Call traverse callback (only if we have to traverse ;-)) */
1111 && NotifyChange
->TraverseCallback
!= NULL
1112 && !NotifyChange
->TraverseCallback(NotifyChange
->FsContext
,
1114 NotifyChange
->SubjectContext
))
1119 /* And then, filter callback if provided */
1120 if (NotifyChange
->FilterCallback
!= NULL
1121 && FilterContext
!= NULL
1122 && !NotifyChange
->FilterCallback(NotifyChange
->FsContext
, FilterContext
))
1127 /* We have a stream! */
1130 ASSERT(NotifyChange
->FullDirectoryName
== NULL
);
1131 if (TargetContext
!= NotifyChange
->SubjectContext
)
1136 ParentName
.Buffer
= NULL
;
1137 ParentName
.Length
= 0;
1142 /* If we don't have to notify immediately, prepare for output */
1143 if (!(NotifyChange
->Flags
& NOTIFY_IMMEDIATELY
))
1145 /* If we have something to output... */
1146 if (NotifyChange
->BufferLength
)
1148 /* Get size of the output */
1151 if (!NotifyChange
->ThisBufferLength
)
1153 if (IsListEmpty(&NotifyChange
->NotifyIrps
))
1155 NumberOfBytes
= NotifyChange
->BufferLength
;
1159 Irp
= CONTAINING_RECORD(NotifyChange
->NotifyIrps
.Flink
, IRP
, Tail
.Overlay
.ListEntry
);
1160 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1161 NumberOfBytes
= Stack
->Parameters
.NotifyDirectory
.Length
;
1166 NumberOfBytes
= NotifyChange
->ThisBufferLength
;
1169 /* If we're matching parent, we don't care about parent (redundant) */
1172 ParentName
.Length
= 0;
1176 /* If we don't deal with streams, some more work is required */
1179 if (NotifyChange
->Flags
& WATCH_ROOT
||
1180 (NormalizedParentName
->Buffer
!= FullTargetName
->Buffer
))
1182 /* Construct TargetDirectory if we don't have it yet */
1183 if (TargetDirectory
.Buffer
== NULL
)
1185 TargetDirectory
.Buffer
= FullTargetName
->Buffer
;
1186 TargetDirectory
.Length
= TargetNameOffset
;
1187 if (TargetNameOffset
!= NotifyChange
->CharacterSize
)
1189 TargetDirectory
.Length
= TargetNameOffset
- NotifyChange
->CharacterSize
;
1191 TargetDirectory
.MaximumLength
= TargetDirectory
.Length
;
1193 /* Now, we start looking for matching parts (unless we watch root) */
1194 TargetNumberOfParts
= 0;
1195 if (!(NotifyChange
->Flags
& WATCH_ROOT
))
1197 FullNumberOfParts
= 1;
1198 if (NotifyChange
->CharacterSize
== sizeof(CHAR
))
1200 FsRtlNotifyGetLastPartOffset(NotifyChange
->FullDirectoryName
->Length
,
1201 TargetDirectory
.Length
, PSTR
, '\\');
1205 FsRtlNotifyGetLastPartOffset(NotifyChange
->FullDirectoryName
->Length
/ sizeof(WCHAR
),
1206 TargetDirectory
.Length
/ sizeof(WCHAR
), PWSTR
, L
'\\');
1207 LastPartOffset
*= NotifyChange
->CharacterSize
;
1211 /* Then, we can construct proper parent name */
1212 ParentNameOffset
= NotifyChange
->CharacterSize
+ LastPartOffset
;
1213 ParentName
.Buffer
= &TargetDirectory
.Buffer
[ParentNameOffset
];
1214 ParentNameLength
= TargetDirectory
.Length
;
1218 /* Construct parent name even for streams */
1219 ParentName
.Buffer
= &NormalizedParentName
->Buffer
[NotifyChange
->FullDirectoryName
->Length
] + NotifyChange
->CharacterSize
;
1220 ParentNameLength
= NormalizedParentName
->Length
- NotifyChange
->FullDirectoryName
->Length
;
1221 ParentNameOffset
= NotifyChange
->CharacterSize
;
1223 ParentNameLength
-= ParentNameOffset
;
1224 ParentName
.Length
= ParentNameLength
;
1225 ParentName
.MaximumLength
= ParentNameLength
;
1229 /* Start to count amount of data to write, we've first the structure itself */
1230 DataLength
= FIELD_OFFSET(FILE_NOTIFY_INFORMATION
, FileName
);
1232 /* If stream, we'll just append stream name */
1235 ASSERT(StreamName
!= NULL
);
1236 DataLength
+= StreamName
->Length
;
1240 /* If not parent, we've to append parent name */
1243 if (NotifyChange
->CharacterSize
== sizeof(CHAR
))
1245 DataLength
+= RtlOemStringToCountedUnicodeSize(&ParentName
);
1249 DataLength
+= ParentName
.Length
;
1251 DataLength
+= sizeof(WCHAR
);
1254 /* Look for target name & construct it, if required */
1255 if (TargetName
.Buffer
== NULL
)
1257 TargetName
.Buffer
= &FullTargetName
->Buffer
[TargetNameOffset
];
1259 TargetName
.MaximumLength
= FullTargetName
->Length
- TargetNameOffset
;
1262 /* Then, we will append it as well */
1263 if (NotifyChange
->CharacterSize
== sizeof(CHAR
))
1265 DataLength
+= RtlOemStringToCountedUnicodeSize(&TargetName
);
1269 DataLength
+= TargetName
.Length
;
1272 /* If we also had a stream name, then we can append it as well */
1273 if (StreamName
!= NULL
)
1275 if (NotifyChange
->CharacterSize
== sizeof(WCHAR
))
1277 DataLength
+= StreamName
->Length
+ sizeof(WCHAR
);
1281 DataLength
= DataLength
+ RtlOemStringToCountedUnicodeSize(&TargetName
) + sizeof(CHAR
);
1286 /* Get the position where we can put our data (aligned!) */
1287 AlignedDataLength
= ROUND_UP(NotifyChange
->DataLength
, sizeof(ULONG
));
1288 /* If it's higher than buffer length, then, bail out without outputing */
1289 if (DataLength
> NumberOfBytes
|| AlignedDataLength
+ DataLength
> NumberOfBytes
)
1291 NotifyChange
->Flags
|= NOTIFY_IMMEDIATELY
;
1295 OutputBuffer
= NULL
;
1296 FileNotifyInfo
= NULL
;
1297 /* If we already had a buffer, update last entry position */
1298 if (NotifyChange
->Buffer
!= NULL
)
1300 FileNotifyInfo
= (PVOID
)((ULONG_PTR
)NotifyChange
->Buffer
+ NotifyChange
->LastEntry
);
1301 FileNotifyInfo
->NextEntryOffset
= AlignedDataLength
- NotifyChange
->LastEntry
;
1302 NotifyChange
->LastEntry
= AlignedDataLength
;
1303 /* And get our output buffer */
1304 OutputBuffer
= (PVOID
)((ULONG_PTR
)NotifyChange
->Buffer
+ AlignedDataLength
);
1306 /* If we hadn't buffer, try to find one */
1307 else if (Irp
!= NULL
)
1309 if (Irp
->AssociatedIrp
.SystemBuffer
!= NULL
)
1311 OutputBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
1313 else if (Irp
->MdlAddress
!= NULL
)
1315 OutputBuffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
1318 NotifyChange
->Buffer
= OutputBuffer
;
1319 NotifyChange
->ThisBufferLength
= NumberOfBytes
;
1322 /* If we couldn't find one, then allocate one */
1323 if (NotifyChange
->Buffer
== NULL
)
1325 PoolQuotaCharged
= FALSE
;
1328 PsChargePoolQuota(NotifyChange
->OwningProcess
, PagedPool
, NumberOfBytes
);
1329 PoolQuotaCharged
= TRUE
;
1330 OutputBuffer
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
1331 NumberOfBytes
, TAG_FS_NOTIFICATIONS
);
1332 NotifyChange
->Buffer
= OutputBuffer
;
1333 NotifyChange
->AllocatedBuffer
= OutputBuffer
;
1335 /* If something went wrong during allocation, notify immediately instead of outputing */
1336 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1338 if (PoolQuotaCharged
)
1340 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NumberOfBytes
);
1342 NotifyChange
->Flags
|= NOTIFY_IMMEDIATELY
;
1347 /* Finally, if we have a buffer, fill it in! */
1348 if (OutputBuffer
!= NULL
)
1350 if (FsRtlNotifyUpdateBuffer((FILE_NOTIFY_INFORMATION
*)OutputBuffer
,
1351 Action
, &ParentName
, &TargetName
,
1352 StreamName
, NotifyChange
->CharacterSize
== sizeof(WCHAR
),
1355 NotifyChange
->DataLength
= DataLength
+ AlignedDataLength
;
1357 /* If it failed, notify immediately */
1360 NotifyChange
->Flags
|= NOTIFY_IMMEDIATELY
;
1365 /* If we have to notify right now (something went wrong?) */
1366 if (NotifyChange
->Flags
& NOTIFY_IMMEDIATELY
)
1368 /* Ensure that all our buffers are NULL */
1369 if (NotifyChange
->Buffer
!= NULL
)
1371 if (NotifyChange
->AllocatedBuffer
!= NULL
)
1373 PsReturnProcessPagedPoolQuota(NotifyChange
->OwningProcess
, NotifyChange
->ThisBufferLength
);
1374 ExFreePoolWithTag(NotifyChange
->AllocatedBuffer
, TAG_FS_NOTIFICATIONS
);
1377 NotifyChange
->Buffer
= NULL
;
1378 NotifyChange
->AllocatedBuffer
= NULL
;
1379 NotifyChange
->LastEntry
= 0;
1380 NotifyChange
->DataLength
= 0;
1381 NotifyChange
->ThisBufferLength
= 0;
1387 /* If asking for old name in case of a rename, notify later on,
1388 * so that we can wait for new name.
1389 * http://msdn.microsoft.com/en-us/library/dn392331.aspx
1391 if (Action
== FILE_ACTION_RENAMED_OLD_NAME
)
1393 NotifyChange
->Flags
|= NOTIFY_LATER
;
1397 NotifyChange
->Flags
&= ~NOTIFY_LATER
;
1398 if (!IsListEmpty(&NotifyChange
->NotifyIrps
))
1400 FsRtlNotifyCompleteIrpList(NotifyChange
, STATUS_SUCCESS
);
1407 FsRtlNotifyReleaseFastMutex(RealNotifySync
);
1413 * @name FsRtlNotifyFullChangeDirectory
1416 * Lets FSD know if changes occures in the specified directory.
1419 * Synchronization object pointer
1422 * Notify list pointer (to head)
1425 * Used to identify the notify structure
1427 * @param FullDirectoryName
1428 * String (A or W) containing the full directory name
1431 * True to notify changes in subdirectories too
1433 * @param IgnoreBuffer
1434 * True to reenumerate directory. It's ignored it NotifyIrp is null
1436 * @param CompletionFilter
1437 * Used to define types of changes to notify
1440 * IRP pointer to complete notify operation. It can be null
1442 * @param TraverseCallback
1443 * Pointer to a callback function. It's called each time a change is
1444 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
1447 * @param SubjectContext
1448 * Pointer to pass to SubjectContext member of TraverseCallback.
1449 * It's freed after use. It's ignored it NotifyIrp is null
1453 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
1458 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync
,
1459 IN PLIST_ENTRY NotifyList
,
1461 IN PSTRING FullDirectoryName
,
1462 IN BOOLEAN WatchTree
,
1463 IN BOOLEAN IgnoreBuffer
,
1464 IN ULONG CompletionFilter
,
1466 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL
,
1467 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
)
1469 FsRtlNotifyFilterChangeDirectory(NotifySync
,
1483 * @name FsRtlNotifyFullReportChange
1486 * Complets the pending notify IRPs.
1489 * Synchronization object pointer
1492 * Notify list pointer (to head)
1494 * @param FullTargetName
1495 * String (A or W) containing the full directory name that changed
1497 * @param TargetNameOffset
1498 * Offset, in FullTargetName, of the final component that is in the changed directory
1501 * String (A or W) containing a stream name
1503 * @param NormalizedParentName
1504 * String (A or W) containing the full directory name that changed with long names
1506 * @param FilterMatch
1507 * Flags that will be compared to the completion filter
1510 * Action code to store in user's buffer
1512 * @param TargetContext
1513 * Pointer to a callback function. It's called each time a change is
1514 * done in a subdirectory of the main directory.
1518 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1523 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync
,
1524 IN PLIST_ENTRY NotifyList
,
1525 IN PSTRING FullTargetName
,
1526 IN USHORT TargetNameOffset
,
1527 IN PSTRING StreamName OPTIONAL
,
1528 IN PSTRING NormalizedParentName OPTIONAL
,
1529 IN ULONG FilterMatch
,
1531 IN PVOID TargetContext
)
1533 FsRtlNotifyFilterReportChange(NotifySync
,
1538 NormalizedParentName
,
1546 * @name FsRtlNotifyInitializeSync
1549 * Allocates the internal structure associated with notifications.
1552 * Opaque pointer. It will receive the address of the allocated internal structure.
1556 * @remarks This function raise an exception in case of a failure.
1561 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC
*NotifySync
)
1563 PREAL_NOTIFY_SYNC RealNotifySync
;
1567 RealNotifySync
= ExAllocatePoolWithTag(NonPagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
1568 sizeof(REAL_NOTIFY_SYNC
), 'FSNS');
1569 ExInitializeFastMutex(&(RealNotifySync
->FastMutex
));
1570 RealNotifySync
->OwningThread
= 0;
1571 RealNotifySync
->OwnerCount
= 0;
1573 *NotifySync
= RealNotifySync
;
1577 * @name FsRtlNotifyReportChange
1580 * Complets the pending notify IRPs.
1583 * Synchronization object pointer
1586 * Notify list pointer (to head)
1588 * @param FullTargetName
1589 * String (A or W) containing the full directory name that changed
1591 * @param FileNamePartLength
1592 * Length of the final component that is in the changed directory
1594 * @param FilterMatch
1595 * Flags that will be compared to the completion filter
1599 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1604 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync
,
1605 IN PLIST_ENTRY NotifyList
,
1606 IN PSTRING FullTargetName
,
1607 IN PUSHORT FileNamePartLength
,
1608 IN ULONG FilterMatch
)
1610 FsRtlNotifyFilterReportChange(NotifySync
,
1613 FullTargetName
->Length
- *FileNamePartLength
,
1623 * @name FsRtlNotifyUninitializeSync
1626 * Uninitialize a NOTIFY_SYNC object
1629 * Address of a pointer to a PNOTIFY_SYNC object previously
1630 * initialized by FsRtlNotifyInitializeSync()
1639 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC
*NotifySync
)
1643 ExFreePoolWithTag(*NotifySync
, 'FSNS');