[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / notify.c
1 /*
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
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
19 IN NTSTATUS Status);
20
21 BOOLEAN
22 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
23 IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
24
25 VOID
26 NTAPI
27 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
28 IN PIRP Irp)
29 {
30 IoReleaseCancelSpinLock(Irp->CancelIrql);
31 UNIMPLEMENTED;
32 }
33
34 /*
35 * @implemented
36 */
37 VOID
38 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,
39 IN PVOID FsContext)
40 {
41 PLIST_ENTRY NextEntry;
42 PNOTIFY_CHANGE NotifyChange;
43
44 if (!IsListEmpty(NotifyList))
45 {
46 /* Browse the notifications list to find the matching entry */
47 for (NextEntry = NotifyList->Flink;
48 NextEntry != NotifyList;
49 NextEntry = NextEntry->Flink)
50 {
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)))
54 {
55 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_DELETE_PENDING);
56 }
57 }
58 }
59 }
60
61 PNOTIFY_CHANGE
62 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
63 IN PVOID FsContext)
64 {
65 PLIST_ENTRY NextEntry;
66 PNOTIFY_CHANGE NotifyChange;
67
68 if (!IsListEmpty(NotifyList))
69 {
70 /* Browse the notifications list to find the matching entry */
71 for (NextEntry = NotifyList->Flink;
72 NextEntry != NotifyList;
73 NextEntry = NextEntry->Flink)
74 {
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)
78 {
79 return NotifyChange;
80 }
81 }
82 }
83 return NULL;
84 }
85
86 VOID
87 FORCEINLINE
88 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
89 {
90 ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread();
91
92 /* Only acquire fast mutex if it's not already acquired by the current thread */
93 if (RealNotifySync->OwningThread != CurrentThread)
94 {
95 ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex));
96 RealNotifySync->OwningThread = CurrentThread;
97 }
98 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
99 RealNotifySync->OwnerCount++;
100 }
101
102 /*
103 * @implemented
104 */
105 VOID
106 FsRtlNotifyCompleteIrp(IN PIRP Irp,
107 IN PNOTIFY_CHANGE NotifyChange,
108 IN ULONG DataLength,
109 IN NTSTATUS Status,
110 IN BOOLEAN SkipCompletion)
111 {
112 PVOID Buffer;
113 PIO_STACK_LOCATION Stack;
114
115 PAGED_CODE();
116
117 /* Check if we need to complete */
118 if (!FsRtlNotifySetCancelRoutine(Irp, NotifyChange) && SkipCompletion)
119 {
120 return;
121 }
122
123 /* No succes => no data to return just complete */
124 if (Status != STATUS_SUCCESS)
125 {
126 goto Completion;
127 }
128
129 /* Ensure there's something to return */
130 Stack = IoGetCurrentIrpStackLocation(Irp);
131 if (!DataLength || Stack->Parameters.NotifyDirectory.Length < DataLength)
132 {
133 Status = STATUS_NOTIFY_ENUM_DIR;
134 goto Completion;
135 }
136
137 /* Ensture there's a buffer where to find data */
138 if (!NotifyChange->AllocatedBuffer)
139 {
140 Irp->IoStatus.Information = DataLength;
141 NotifyChange->Buffer = NULL;
142 goto Completion;
143 }
144
145 /* Now, browse all the way to return data
146 * and find the one that will work. We will
147 * return data whatever happens
148 */
149 if (Irp->AssociatedIrp.SystemBuffer)
150 {
151 Buffer = Irp->AssociatedIrp.SystemBuffer;
152 goto CopyAndComplete;
153 }
154
155 if (Irp->MdlAddress)
156 {
157 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
158 goto CopyAndComplete;
159 }
160
161 if (!(Stack->Control & SL_PENDING_RETURNED))
162 {
163 Buffer = Irp->UserBuffer;
164 goto CopyAndComplete;
165 }
166
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;
171
172 CopyAndComplete:
173 _SEH2_TRY
174 {
175 RtlCopyMemory(Buffer, NotifyChange->AllocatedBuffer, DataLength);
176 }
177 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
178 {
179 /* Do nothing */
180 }
181 _SEH2_END;
182
183 ReleaseAndComplete:
184 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
185
186 /* Release buffer UNLESS it's used */
187 if (NotifyChange->AllocatedBuffer != Irp->AssociatedIrp.SystemBuffer &&
188 NotifyChange->AllocatedBuffer)
189 {
190 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 0);
191 }
192
193 /* Prepare for return */
194 NotifyChange->AllocatedBuffer = 0;
195 NotifyChange->ThisBufferLength = 0;
196 Irp->IoStatus.Information = DataLength;
197 NotifyChange->Buffer = NULL;
198
199 /* Finally complete */
200 Completion:
201 IoMarkIrpPending(Irp);
202 Irp->IoStatus.Status = Status;
203 IofCompleteRequest(Irp, EVENT_INCREMENT);
204 }
205
206 /*
207 * @implemented
208 */
209 VOID
210 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
211 IN NTSTATUS Status)
212 {
213 PIRP Irp;
214 ULONG DataLength;
215 PLIST_ENTRY NextEntry;
216
217 DataLength = NotifyChange->DataLength;
218
219 NotifyChange->Flags &= (INVALIDATE_BUFFERS | WATCH_TREE);
220 NotifyChange->DataLength = 0;
221 NotifyChange->LastEntry = 0;
222
223 while (!IsListEmpty(&(NotifyChange->NotifyIrps)))
224 {
225 /* We take the first entry */
226 NextEntry = RemoveHeadList(&(NotifyChange->NotifyIrps));
227 Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
228 /* We complete it */
229 FsRtlNotifyCompleteIrp(Irp, NotifyChange, DataLength, Status, TRUE);
230 /* If we're notifying success, just notify first one */
231 if (Status == STATUS_SUCCESS)
232 break;
233 }
234 }
235
236 VOID
237 FORCEINLINE
238 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
239 {
240 RealNotifySync->OwnerCount--;
241 /* Release the fast mutex only if no other instance needs it */
242 if (!RealNotifySync->OwnerCount)
243 {
244 ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex));
245 RealNotifySync->OwningThread = (ULONG_PTR)0;
246 }
247 }
248
249 /*
250 * @implemented
251 */
252 BOOLEAN
253 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
254 IN PNOTIFY_CHANGE NotifyChange OPTIONAL)
255 {
256 PDRIVER_CANCEL CancelRoutine;
257
258 /* Acquire cancel lock */
259 IoAcquireCancelSpinLock(&Irp->CancelIrql);
260
261 /* If NotifyChange was given */
262 if (NotifyChange)
263 {
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 */
270 if (CancelRoutine)
271 {
272 /* Decrease reference count */
273 InterlockedDecrement((PLONG)&NotifyChange->ReferenceCount);
274 /* Notify that we removed cancel routine */
275 return TRUE;
276 }
277 }
278 else
279 {
280 /* If IRP is cancel, call FsRtl cancel routine */
281 if (Irp->Cancel)
282 {
283 FsRtlCancelNotify(NULL, Irp);
284 }
285 else
286 {
287 /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
288 IoSetCancelRoutine(Irp, FsRtlCancelNotify);
289 /* Release lock */
290 IoReleaseCancelSpinLock(Irp->CancelIrql);
291 }
292 }
293
294 /* Return that we didn't removed cancel routine */
295 return FALSE;
296 }
297
298 /* PUBLIC FUNCTIONS **********************************************************/
299
300 /*++
301 * @name FsRtlNotifyChangeDirectory
302 * @implemented
303 *
304 * Lets FSD know if changes occures in the specified directory.
305 * Directory will be reenumerated.
306 *
307 * @param NotifySync
308 * Synchronization object pointer
309 *
310 * @param FsContext
311 * Used to identify the notify structure
312 *
313 * @param FullDirectoryName
314 * String (A or W) containing the full directory name
315 *
316 * @param NotifyList
317 * Notify list pointer (to head)
318 *
319 * @param WatchTree
320 * True to notify changes in subdirectories too
321 *
322 * @param CompletionFilter
323 * Used to define types of changes to notify
324 *
325 * @param NotifyIrp
326 * IRP pointer to complete notify operation. It can be null
327 *
328 * @return None
329 *
330 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
331 *
332 *--*/
333 VOID
334 NTAPI
335 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
336 IN PVOID FsContext,
337 IN PSTRING FullDirectoryName,
338 IN PLIST_ENTRY NotifyList,
339 IN BOOLEAN WatchTree,
340 IN ULONG CompletionFilter,
341 IN PIRP NotifyIrp)
342 {
343 FsRtlNotifyFilterChangeDirectory(NotifySync,
344 NotifyList,
345 FsContext,
346 FullDirectoryName,
347 WatchTree,
348 TRUE,
349 CompletionFilter,
350 NotifyIrp,
351 NULL,
352 NULL,
353 NULL);
354 }
355
356 /*++
357 * @name FsRtlNotifyCleanup
358 * @implemented
359 *
360 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
361 *
362 * @param NotifySync
363 * Synchronization object pointer
364 *
365 * @param NotifyList
366 * Notify list pointer (to head)
367 *
368 * @param FsContext
369 * Used to identify the notify structure
370 *
371 * @return None
372 *
373 * @remarks None
374 *
375 *--*/
376 VOID
377 NTAPI
378 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
379 IN PLIST_ENTRY NotifyList,
380 IN PVOID FsContext)
381 {
382 PNOTIFY_CHANGE NotifyChange;
383 PREAL_NOTIFY_SYNC RealNotifySync;
384 PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
385
386 /* Get real structure hidden behind the opaque pointer */
387 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
388
389 /* Acquire the fast mutex */
390 FsRtlNotifyAcquireFastMutex(RealNotifySync);
391
392 _SEH2_TRY
393 {
394 /* Find if there's a matching notification with the FsContext */
395 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
396 if (NotifyChange)
397 {
398 /* Mark it as to know that cleanup is in process */
399 NotifyChange->Flags |= CLEANUP_IN_PROCESS;
400
401 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
402 if (!IsListEmpty(&NotifyChange->NotifyIrps))
403 {
404 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
405 }
406
407 /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
408 if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
409 {
410 /* Remove it from the notifications list */
411 RemoveEntryList(&NotifyChange->NotifyList);
412
413 /* In case there was an allocated buffer, free it */
414 if (NotifyChange->AllocatedBuffer)
415 {
416 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
417 ExFreePool(NotifyChange->AllocatedBuffer);
418 }
419
420 /* In case there the string was set, get the captured subject security context */
421 if (NotifyChange->FullDirectoryName)
422 {
423 SubjectContext = NotifyChange->SubjectContext;
424 }
425
426 /* Finally, free the notification, as it's not needed anymore */
427 ExFreePool(NotifyChange);
428 }
429 }
430 }
431 _SEH2_FINALLY
432 {
433 /* Release fast mutex */
434 FsRtlNotifyReleaseFastMutex(RealNotifySync);
435
436 /* If the subject security context was captured, release and free it */
437 if (SubjectContext)
438 {
439 SeReleaseSubjectContext(SubjectContext);
440 ExFreePool(SubjectContext);
441 }
442 }
443 _SEH2_END;
444 }
445
446 /*++
447 * @name FsRtlNotifyFilterChangeDirectory
448 * @unimplemented
449 *
450 * FILLME
451 *
452 * @param NotifySync
453 * FILLME
454 *
455 * @param NotifyList
456 * FILLME
457 *
458 * @param FsContext
459 * FILLME
460 *
461 * @param FullDirectoryName
462 * FILLME
463 *
464 * @param WatchTree
465 * FILLME
466 *
467 * @param IgnoreBuffer
468 * FILLME
469 *
470 * @param CompletionFilter
471 * FILLME
472 *
473 * @param NotifyIrp
474 * FILLME
475 *
476 * @param TraverseCallback
477 * FILLME
478 *
479 * @param SubjectContext
480 * FILLME
481 *
482 * @param FilterCallback
483 * FILLME
484 *
485 * @return None
486 *
487 * @remarks None
488 *
489 *--*/
490 VOID
491 NTAPI
492 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
493 IN PLIST_ENTRY NotifyList,
494 IN PVOID FsContext,
495 IN PSTRING FullDirectoryName,
496 IN BOOLEAN WatchTree,
497 IN BOOLEAN IgnoreBuffer,
498 IN ULONG CompletionFilter,
499 IN PIRP NotifyIrp,
500 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
501 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
502 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
503 {
504 KeBugCheck(FILE_SYSTEM);
505 }
506
507 /*++
508 * @name FsRtlNotifyFilterReportChange
509 * @unimplemented
510 *
511 * FILLME
512 *
513 * @param NotifySync
514 * FILLME
515 *
516 * @param NotifyList
517 * FILLME
518 *
519 * @param FullTargetName
520 * FILLME
521 *
522 * @param TargetNameOffset
523 * FILLME
524 *
525 * @param StreamName
526 * FILLME
527 *
528 * @param NormalizedParentName
529 * FILLME
530 *
531 * @param FilterMatch
532 * FILLME
533 *
534 * @param Action
535 * FILLME
536 *
537 * @param TargetContext
538 * FILLME
539 *
540 * @param FilterContext
541 * FILLME
542 *
543 * @return None
544 *
545 * @remarks None
546 *
547 *--*/
548 VOID
549 NTAPI
550 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
551 IN PLIST_ENTRY NotifyList,
552 IN PSTRING FullTargetName,
553 IN USHORT TargetNameOffset,
554 IN PSTRING StreamName OPTIONAL,
555 IN PSTRING NormalizedParentName OPTIONAL,
556 IN ULONG FilterMatch,
557 IN ULONG Action,
558 IN PVOID TargetContext,
559 IN PVOID FilterContext)
560 {
561 KeBugCheck(FILE_SYSTEM);
562 }
563
564 /*++
565 * @name FsRtlNotifyFullChangeDirectory
566 * @implemented
567 *
568 * Lets FSD know if changes occures in the specified directory.
569 *
570 * @param NotifySync
571 * Synchronization object pointer
572 *
573 * @param NotifyList
574 * Notify list pointer (to head)
575 *
576 * @param FsContext
577 * Used to identify the notify structure
578 *
579 * @param FullDirectoryName
580 * String (A or W) containing the full directory name
581 *
582 * @param WatchTree
583 * True to notify changes in subdirectories too
584 *
585 * @param IgnoreBuffer
586 * True to reenumerate directory. It's ignored it NotifyIrp is null
587 *
588 * @param CompletionFilter
589 * Used to define types of changes to notify
590 *
591 * @param NotifyIrp
592 * IRP pointer to complete notify operation. It can be null
593 *
594 * @param TraverseCallback
595 * Pointer to a callback function. It's called each time a change is
596 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
597 * is null
598 *
599 * @param SubjectContext
600 * Pointer to pass to SubjectContext member of TraverseCallback.
601 * It's freed after use. It's ignored it NotifyIrp is null
602 *
603 * @return None
604 *
605 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
606 *
607 *--*/
608 VOID
609 NTAPI
610 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
611 IN PLIST_ENTRY NotifyList,
612 IN PVOID FsContext,
613 IN PSTRING FullDirectoryName,
614 IN BOOLEAN WatchTree,
615 IN BOOLEAN IgnoreBuffer,
616 IN ULONG CompletionFilter,
617 IN PIRP NotifyIrp,
618 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
619 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
620 {
621 FsRtlNotifyFilterChangeDirectory(NotifySync,
622 NotifyList,
623 FsContext,
624 FullDirectoryName,
625 WatchTree,
626 IgnoreBuffer,
627 CompletionFilter,
628 NotifyIrp,
629 TraverseCallback,
630 SubjectContext,
631 NULL);
632 }
633
634 /*++
635 * @name FsRtlNotifyFullReportChange
636 * @implemented
637 *
638 * Complets the pending notify IRPs.
639 *
640 * @param NotifySync
641 * Synchronization object pointer
642 *
643 * @param NotifyList
644 * Notify list pointer (to head)
645 *
646 * @param FullTargetName
647 * String (A or W) containing the full directory name that changed
648 *
649 * @param TargetNameOffset
650 * Offset, in FullTargetName, of the final component that is in the changed directory
651 *
652 * @param StreamName
653 * String (A or W) containing a stream name
654 *
655 * @param NormalizedParentName
656 * String (A or W) containing the full directory name that changed with long names
657 *
658 * @param FilterMatch
659 * Flags that will be compared to the completion filter
660 *
661 * @param Action
662 * Action code to store in user's buffer
663 *
664 * @param TargetContext
665 * Pointer to a callback function. It's called each time a change is
666 * done in a subdirectory of the main directory.
667 *
668 * @return None
669 *
670 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
671 *
672 *--*/
673 VOID
674 NTAPI
675 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
676 IN PLIST_ENTRY NotifyList,
677 IN PSTRING FullTargetName,
678 IN USHORT TargetNameOffset,
679 IN PSTRING StreamName OPTIONAL,
680 IN PSTRING NormalizedParentName OPTIONAL,
681 IN ULONG FilterMatch,
682 IN ULONG Action,
683 IN PVOID TargetContext)
684 {
685 FsRtlNotifyFilterReportChange(NotifySync,
686 NotifyList,
687 FullTargetName,
688 TargetNameOffset,
689 StreamName,
690 NormalizedParentName,
691 FilterMatch,
692 Action,
693 TargetContext,
694 NULL);
695 }
696
697 /*++
698 * @name FsRtlNotifyInitializeSync
699 * @implemented
700 *
701 * Allocates the internal structure associated with notifications.
702 *
703 * @param NotifySync
704 * Opaque pointer. It will receive the address of the allocated internal structure.
705 *
706 * @return None
707 *
708 * @remarks This function raise an exception in case of a failure.
709 *
710 *--*/
711 VOID
712 NTAPI
713 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
714 {
715 PREAL_NOTIFY_SYNC RealNotifySync;
716
717 *NotifySync = NULL;
718
719 RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
720 sizeof(REAL_NOTIFY_SYNC), 'FSNS');
721 ExInitializeFastMutex(&(RealNotifySync->FastMutex));
722 RealNotifySync->OwningThread = 0;
723 RealNotifySync->OwnerCount = 0;
724
725 *NotifySync = RealNotifySync;
726 }
727
728 /*++
729 * @name FsRtlNotifyReportChange
730 * @implemented
731 *
732 * Complets the pending notify IRPs.
733 *
734 * @param NotifySync
735 * Synchronization object pointer
736 *
737 * @param NotifyList
738 * Notify list pointer (to head)
739 *
740 * @param FullTargetName
741 * String (A or W) containing the full directory name that changed
742 *
743 * @param FileNamePartLength
744 * Length of the final component that is in the changed directory
745 *
746 * @param FilterMatch
747 * Flags that will be compared to the completion filter
748 *
749 * @return None
750 *
751 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
752 *
753 *--*/
754 VOID
755 NTAPI
756 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
757 IN PLIST_ENTRY NotifyList,
758 IN PSTRING FullTargetName,
759 IN PUSHORT FileNamePartLength,
760 IN ULONG FilterMatch)
761 {
762 FsRtlNotifyFilterReportChange(NotifySync,
763 NotifyList,
764 FullTargetName,
765 FullTargetName->Length - *FileNamePartLength,
766 NULL,
767 NULL,
768 FilterMatch,
769 0,
770 NULL,
771 NULL);
772 }
773
774 /*++
775 * @name FsRtlNotifyUninitializeSync
776 * @implemented
777 *
778 * Uninitialize a NOTIFY_SYNC object
779 *
780 * @param NotifySync
781 * Address of a pointer to a PNOTIFY_SYNC object previously
782 * initialized by FsRtlNotifyInitializeSync()
783 *
784 * @return None
785 *
786 * @remarks None
787 *
788 *--*/
789 VOID
790 NTAPI
791 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
792 {
793 if (*NotifySync)
794 {
795 ExFreePoolWithTag(*NotifySync, 'FSNS');
796 *NotifySync = NULL;
797 }
798 }
799