xref: /reactos/ntoskrnl/fsrtl/notify.c (revision bd712186)
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 /* INLINED FUNCTIONS *********************************************************/
16 
17 /*
18  * @implemented
19  */
20 FORCEINLINE
21 VOID
22 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
23 {
24     ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread();
25 
26     /* Only acquire fast mutex if it's not already acquired by the current thread */
27     if (RealNotifySync->OwningThread != CurrentThread)
28     {
29         ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex));
30         RealNotifySync->OwningThread = CurrentThread;
31     }
32     /* Whatever the case, keep trace of the attempt to acquire fast mutex */
33     RealNotifySync->OwnerCount++;
34 }
35 
36 /*
37  * @implemented
38  */
39 FORCEINLINE
40 VOID
41 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
42 {
43     RealNotifySync->OwnerCount--;
44     /* Release the fast mutex only if no other instance needs it */
45     if (!RealNotifySync->OwnerCount)
46     {
47         ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex));
48         RealNotifySync->OwningThread = (ULONG_PTR)0;
49     }
50 }
51 
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)                             \
60             break;                                                                \
61         }                                                                         \
62     }
63 
64 /* PRIVATE FUNCTIONS *********************************************************/
65 
66 VOID
67 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
68                            IN NTSTATUS Status);
69 
70 BOOLEAN
71 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
72                             IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
73 
74 /*
75  * @implemented
76  */
77 VOID
78 NTAPI
79 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
80                   IN PIRP Irp)
81 {
82     PVOID Buffer;
83     PIRP NotifyIrp;
84     ULONG BufferLength;
85     PIO_STACK_LOCATION Stack;
86     PNOTIFY_CHANGE NotifyChange;
87     PREAL_NOTIFY_SYNC RealNotifySync;
88     PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext = NULL;
89 
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;
99 
100     FsRtlNotifyAcquireFastMutex(RealNotifySync);
101 
102    _SEH2_TRY
103    {
104        /* Remove the IRP from the notifications list and mark it pending */
105        RemoveEntryList(&(Irp->Tail.Overlay.ListEntry));
106        IoMarkIrpPending(Irp);
107 
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))
112        {
113            /* Assume we didn't find any */
114            Buffer = NULL;
115            BufferLength = 0;
116 
117            /* If we don't have IRPs, check if current buffer is big enough */
118            if (IsListEmpty(&NotifyChange->NotifyIrps))
119            {
120                if (NotifyChange->BufferLength >= NotifyChange->DataLength)
121                {
122                    BufferLength = NotifyChange->BufferLength;
123                }
124            }
125            else
126            {
127                /* Otherwise, try to look at next IRP available */
128                NotifyIrp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry);
129                Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
130 
131                /* If its buffer is big enough, get it */
132                if (Stack->Parameters.NotifyDirectory.Length >= NotifyChange->BufferLength)
133                {
134                    /* Is it MDL? */
135                    if (NotifyIrp->AssociatedIrp.SystemBuffer == NULL)
136                    {
137                        if (NotifyIrp->MdlAddress != NULL)
138                        {
139                            Buffer = MmGetSystemAddressForMdl(NotifyIrp->MdlAddress);
140                        }
141                    }
142                    else
143                    {
144                        Buffer = NotifyIrp->AssociatedIrp.MasterIrp;
145                    }
146 
147                    /* Backup our accepted buffer length */
148                    BufferLength = Stack->Parameters.NotifyDirectory.Length;
149                    if (BufferLength > NotifyChange->BufferLength)
150                    {
151                        BufferLength = NotifyChange->BufferLength;
152                    }
153                }
154            }
155 
156            /* At that point, we *may* have a buffer */
157 
158            /* If it has null length, then note that we won't use it */
159            if (BufferLength == 0)
160            {
161                NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
162            }
163            else
164            {
165                /* If we have a buffer length, but no buffer then allocate one */
166                if (Buffer == NULL)
167                {
168                    PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, BufferLength);
169                    Buffer = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, BufferLength, TAG_FS_NOTIFICATIONS);
170                    NotifyChange->AllocatedBuffer = Buffer;
171                }
172 
173                /* Copy data in that buffer */
174                RtlCopyMemory(Buffer, NotifyChange->Buffer, NotifyChange->DataLength);
175                NotifyChange->ThisBufferLength = BufferLength;
176                NotifyChange->Buffer = Buffer;
177            }
178 
179            /* If we have to notify immediately, ensure that any buffer is 0-ed out */
180            if (NotifyChange->Flags & NOTIFY_IMMEDIATELY)
181            {
182                NotifyChange->Buffer = 0;
183                NotifyChange->AllocatedBuffer = 0;
184                NotifyChange->LastEntry = 0;
185                NotifyChange->DataLength = 0;
186                NotifyChange->ThisBufferLength = 0;
187            }
188        }
189 
190        /* It's now time to complete - data are ready */
191 
192        /* Set appropriate status and complete */
193        Irp->IoStatus.Status = STATUS_CANCELLED;
194        IoCompleteRequest(Irp, IO_DISK_INCREMENT);
195 
196        /* If that notification isn't referenced any longer, drop it */
197        if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
198        {
199            /* If it had an allocated buffer, delete */
200            if (NotifyChange->AllocatedBuffer)
201            {
202                PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
203                ExFreePoolWithTag(NotifyChange->AllocatedBuffer, TAG_FS_NOTIFICATIONS);
204            }
205 
206            /* In case of full name, remember subject context for later deletion */
207            if (NotifyChange->FullDirectoryName)
208            {
209                SubjectContext = NotifyChange->SubjectContext;
210            }
211 
212            /* We mustn't have ANY change left anymore */
213            ASSERT(NotifyChange->NotifyList.Flink == NULL);
214            ExFreePoolWithTag(NotifyChange, 0);
215        }
216     }
217     _SEH2_FINALLY
218     {
219         FsRtlNotifyReleaseFastMutex(RealNotifySync);
220 
221         /* If the subject security context was captured, release and free it */
222         if (SubjectContext)
223         {
224             SeReleaseSubjectContext(SubjectContext);
225             ExFreePool(SubjectContext);
226         }
227     }
228     _SEH2_END;
229 }
230 
231 /*
232  * @implemented
233  */
234 VOID
235 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,
236                           IN PVOID FsContext)
237 {
238     PLIST_ENTRY NextEntry;
239     PNOTIFY_CHANGE NotifyChange;
240 
241     if (!IsListEmpty(NotifyList))
242     {
243         /* Browse the notifications list to find the matching entry */
244         for (NextEntry = NotifyList->Flink;
245              NextEntry != NotifyList;
246              NextEntry = NextEntry->Flink)
247         {
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)))
251             {
252                 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_DELETE_PENDING);
253             }
254         }
255     }
256 }
257 
258 /*
259  *@implemented
260  */
261 PNOTIFY_CHANGE
262 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
263                     IN PVOID FsContext)
264 {
265     PLIST_ENTRY NextEntry;
266     PNOTIFY_CHANGE NotifyChange;
267 
268     if (!IsListEmpty(NotifyList))
269     {
270         /* Browse the notifications list to find the matching entry */
271         for (NextEntry = NotifyList->Flink;
272              NextEntry != NotifyList;
273              NextEntry = NextEntry->Flink)
274         {
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)
278             {
279                 return NotifyChange;
280             }
281         }
282     }
283     return NULL;
284 }
285 
286 /*
287  * @implemented
288  */
289 VOID
290 FsRtlNotifyCompleteIrp(IN PIRP Irp,
291                        IN PNOTIFY_CHANGE NotifyChange,
292                        IN ULONG DataLength,
293                        IN NTSTATUS Status,
294                        IN BOOLEAN SkipCompletion)
295 {
296     PVOID Buffer;
297     PIO_STACK_LOCATION Stack;
298 
299     PAGED_CODE();
300 
301     /* Check if we need to complete */
302     if (!FsRtlNotifySetCancelRoutine(Irp, NotifyChange) && SkipCompletion)
303     {
304         return;
305     }
306 
307     /* No succes => no data to return just complete */
308     if (Status != STATUS_SUCCESS)
309     {
310         goto Completion;
311     }
312 
313     /* Ensure there's something to return */
314     Stack = IoGetCurrentIrpStackLocation(Irp);
315     if (!DataLength || Stack->Parameters.NotifyDirectory.Length < DataLength)
316     {
317         Status = STATUS_NOTIFY_ENUM_DIR;
318         goto Completion;
319     }
320 
321     /* Ensture there's a buffer where to find data */
322     if (!NotifyChange->AllocatedBuffer)
323     {
324         Irp->IoStatus.Information = DataLength;
325         NotifyChange->Buffer = NULL;
326         goto Completion;
327     }
328 
329     /* Now, browse all the way to return data
330      * and find the one that will work. We will
331      * return data whatever happens
332      */
333     if (Irp->AssociatedIrp.SystemBuffer)
334     {
335         Buffer = Irp->AssociatedIrp.SystemBuffer;
336         goto CopyAndComplete;
337     }
338 
339     if (Irp->MdlAddress)
340     {
341         Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
342         goto CopyAndComplete;
343     }
344 
345     if (!(Stack->Control & SL_PENDING_RETURNED))
346     {
347         Buffer = Irp->UserBuffer;
348         goto CopyAndComplete;
349     }
350 
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;
355 
356 CopyAndComplete:
357     _SEH2_TRY
358     {
359         RtlCopyMemory(Buffer, NotifyChange->AllocatedBuffer, DataLength);
360     }
361     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
362     {
363         /* Do nothing */
364     }
365     _SEH2_END;
366 
367 ReleaseAndComplete:
368     PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
369 
370     /* Release buffer UNLESS it's used */
371     if (NotifyChange->AllocatedBuffer != Irp->AssociatedIrp.SystemBuffer &&
372         NotifyChange->AllocatedBuffer)
373     {
374         ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 0);
375     }
376 
377     /* Prepare for return */
378     NotifyChange->AllocatedBuffer = 0;
379     NotifyChange->ThisBufferLength = 0;
380     Irp->IoStatus.Information = DataLength;
381     NotifyChange->Buffer = NULL;
382 
383     /* Finally complete */
384 Completion:
385     IoMarkIrpPending(Irp);
386     Irp->IoStatus.Status = Status;
387     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
388 }
389 
390 /*
391  * @implemented
392  */
393 VOID
394 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
395                            IN NTSTATUS Status)
396 {
397     PIRP Irp;
398     ULONG DataLength;
399     PLIST_ENTRY NextEntry;
400 
401     DataLength = NotifyChange->DataLength;
402 
403     NotifyChange->Flags &= (NOTIFY_IMMEDIATELY | WATCH_TREE);
404     NotifyChange->DataLength = 0;
405     NotifyChange->LastEntry = 0;
406 
407     while (!IsListEmpty(&(NotifyChange->NotifyIrps)))
408     {
409         /* We take the first entry */
410         NextEntry = RemoveHeadList(&(NotifyChange->NotifyIrps));
411         Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
412         /* We complete it */
413         FsRtlNotifyCompleteIrp(Irp, NotifyChange, DataLength, Status, TRUE);
414         /* If we're notifying success, just notify first one */
415         if (Status == STATUS_SUCCESS)
416             break;
417     }
418 }
419 
420 /*
421  * @implemented
422  */
423 BOOLEAN
424 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
425                             IN PNOTIFY_CHANGE NotifyChange OPTIONAL)
426 {
427     PDRIVER_CANCEL CancelRoutine;
428 
429     /* Acquire cancel lock */
430     IoAcquireCancelSpinLock(&Irp->CancelIrql);
431 
432     /* If NotifyChange was given */
433     if (NotifyChange)
434     {
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 */
441         if (CancelRoutine)
442         {
443             /* Decrease reference count */
444             InterlockedDecrement((PLONG)&NotifyChange->ReferenceCount);
445             /* Notify that we removed cancel routine */
446             return TRUE;
447         }
448     }
449     else
450     {
451         /* If IRP is cancel, call FsRtl cancel routine */
452         if (Irp->Cancel)
453         {
454              FsRtlCancelNotify(NULL, Irp);
455         }
456         else
457         {
458             /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
459             IoSetCancelRoutine(Irp, FsRtlCancelNotify);
460             /* Release lock */
461             IoReleaseCancelSpinLock(Irp->CancelIrql);
462         }
463     }
464 
465     /* Return that we didn't removed cancel routine */
466     return FALSE;
467 }
468 
469 /*
470  * @implemented
471  */
472 BOOLEAN
473 FsRtlNotifyUpdateBuffer(OUT PFILE_NOTIFY_INFORMATION OutputBuffer,
474                         IN ULONG Action,
475                         IN PSTRING ParentName,
476                         IN PSTRING TargetName,
477                         IN PSTRING StreamName,
478                         IN BOOLEAN IsUnicode,
479                         IN ULONG DataLength)
480 {
481     /* Unless there's an issue with buffers, there's no reason to fail */
482     BOOLEAN Succeed = TRUE;
483     ULONG AlreadyWritten = 0, ResultSize;
484 
485     PAGED_CODE();
486 
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
492      */
493     _SEH2_TRY
494     {
495         OutputBuffer->NextEntryOffset = 0;
496         OutputBuffer->Action = Action;
497         OutputBuffer->FileNameLength = DataLength - FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName);
498         if (IsUnicode)
499         {
500             if (ParentName->Length)
501             {
502                 RtlCopyMemory(OutputBuffer->FileName, ParentName->Buffer, ParentName->Length);
503                 OutputBuffer->FileName[ParentName->Length / sizeof(WCHAR)] = L'\\';
504                 AlreadyWritten = ParentName->Length + sizeof(WCHAR);
505             }
506             RtlCopyMemory((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten),
507                           TargetName->Buffer, TargetName->Length);
508             if (StreamName)
509             {
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);
514             }
515         }
516         else
517         {
518             if (!ParentName->Length)
519             {
520                 ASSERT(StreamName);
521                 RtlCopyMemory(OutputBuffer->FileName, StreamName->Buffer, StreamName->Length);
522             }
523             else
524             {
525                 RtlOemToUnicodeN(OutputBuffer->FileName, OutputBuffer->FileNameLength,
526                                  &ResultSize, ParentName->Buffer,
527                                  ParentName->Length);
528                 OutputBuffer->FileName[ResultSize / sizeof(WCHAR)] = L'\\';
529                 AlreadyWritten = ResultSize + sizeof(WCHAR);
530 
531                 RtlOemToUnicodeN((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten),
532                                  OutputBuffer->FileNameLength, &ResultSize,
533                                  TargetName->Buffer, TargetName->Length);
534 
535                 if (StreamName)
536                 {
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);
542                 }
543             }
544         }
545     }
546     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
547     {
548         Succeed = FALSE;
549     }
550     _SEH2_END;
551 
552     return Succeed;
553 }
554 
555 /* PUBLIC FUNCTIONS **********************************************************/
556 
557 /*++
558  * @name FsRtlNotifyChangeDirectory
559  * @implemented
560  *
561  * Lets FSD know if changes occures in the specified directory.
562  * Directory will be reenumerated.
563  *
564  * @param NotifySync
565  *        Synchronization object pointer
566  *
567  * @param FsContext
568  *        Used to identify the notify structure
569  *
570  * @param FullDirectoryName
571  *        String (A or W) containing the full directory name
572  *
573  * @param NotifyList
574  *        Notify list pointer (to head)
575  *
576  * @param WatchTree
577  *        True to notify changes in subdirectories too
578  *
579  * @param CompletionFilter
580  *        Used to define types of changes to notify
581  *
582  * @param NotifyIrp
583  *        IRP pointer to complete notify operation. It can be null
584  *
585  * @return None
586  *
587  * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
588  *
589  *--*/
590 VOID
591 NTAPI
592 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
593                            IN PVOID FsContext,
594                            IN PSTRING FullDirectoryName,
595                            IN PLIST_ENTRY NotifyList,
596                            IN BOOLEAN WatchTree,
597                            IN ULONG CompletionFilter,
598                            IN PIRP NotifyIrp)
599 {
600     FsRtlNotifyFilterChangeDirectory(NotifySync,
601                                      NotifyList,
602                                      FsContext,
603                                      FullDirectoryName,
604                                      WatchTree,
605                                      TRUE,
606                                      CompletionFilter,
607                                      NotifyIrp,
608                                      NULL,
609                                      NULL,
610                                      NULL);
611 }
612 
613 /*++
614  * @name FsRtlNotifyCleanup
615  * @implemented
616  *
617  * Called by FSD when all handles to FileObject (identified by FsContext) are closed
618  *
619  * @param NotifySync
620  *        Synchronization object pointer
621  *
622  * @param NotifyList
623  *        Notify list pointer (to head)
624  *
625  * @param FsContext
626  *        Used to identify the notify structure
627  *
628  * @return None
629  *
630  * @remarks None
631  *
632  *--*/
633 VOID
634 NTAPI
635 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
636                    IN PLIST_ENTRY NotifyList,
637                    IN PVOID FsContext)
638 {
639     PNOTIFY_CHANGE NotifyChange;
640     PREAL_NOTIFY_SYNC RealNotifySync;
641     PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext = NULL;
642 
643     /* Get real structure hidden behind the opaque pointer */
644     RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
645 
646     /* Acquire the fast mutex */
647     FsRtlNotifyAcquireFastMutex(RealNotifySync);
648 
649     _SEH2_TRY
650     {
651         /* Find if there's a matching notification with the FsContext */
652         NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
653         if (NotifyChange)
654         {
655             /* Mark it as to know that cleanup is in process */
656             NotifyChange->Flags |= CLEANUP_IN_PROCESS;
657 
658             /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
659             if (!IsListEmpty(&NotifyChange->NotifyIrps))
660             {
661                 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
662             }
663 
664             /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
665             if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
666             {
667                 /* Remove it from the notifications list */
668                 RemoveEntryList(&NotifyChange->NotifyList);
669 
670                 /* In case there was an allocated buffer, free it */
671                 if (NotifyChange->AllocatedBuffer)
672                 {
673                     PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
674                     ExFreePool(NotifyChange->AllocatedBuffer);
675                 }
676 
677                 /* In case there the string was set, get the captured subject security context */
678                 if (NotifyChange->FullDirectoryName)
679                 {
680                     SubjectContext = NotifyChange->SubjectContext;
681                 }
682 
683                 /* Finally, free the notification, as it's not needed anymore */
684                 ExFreePoolWithTag(NotifyChange, 'NrSF');
685             }
686         }
687     }
688     _SEH2_FINALLY
689     {
690         /* Release fast mutex */
691         FsRtlNotifyReleaseFastMutex(RealNotifySync);
692 
693         /* If the subject security context was captured, release and free it */
694         if (SubjectContext)
695         {
696             SeReleaseSubjectContext(SubjectContext);
697             ExFreePool(SubjectContext);
698         }
699     }
700     _SEH2_END;
701 }
702 
703 /*++
704  * @name FsRtlNotifyFilterChangeDirectory
705  * @implemented
706  *
707  * FILLME
708  *
709  * @param NotifySync
710  *        FILLME
711  *
712  * @param NotifyList
713  *        FILLME
714  *
715  * @param FsContext
716  *        FILLME
717  *
718  * @param FullDirectoryName
719  *        FILLME
720  *
721  * @param WatchTree
722  *        FILLME
723  *
724  * @param IgnoreBuffer
725  *        FILLME
726  *
727  * @param CompletionFilter
728  *        FILLME
729  *
730  * @param NotifyIrp
731  *        FILLME
732  *
733  * @param TraverseCallback
734  *        FILLME
735  *
736  * @param SubjectContext
737  *        FILLME
738  *
739  * @param FilterCallback
740  *        FILLME
741  *
742  * @return None
743  *
744  * @remarks None
745  *
746  *--*/
747 VOID
748 NTAPI
749 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
750                                  IN PLIST_ENTRY NotifyList,
751                                  IN PVOID FsContext,
752                                  IN PSTRING FullDirectoryName,
753                                  IN BOOLEAN WatchTree,
754                                  IN BOOLEAN IgnoreBuffer,
755                                  IN ULONG CompletionFilter,
756                                  IN PIRP NotifyIrp,
757                                  IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
758                                  IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
759                                  IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
760 {
761     ULONG SavedLength;
762     PIO_STACK_LOCATION Stack;
763     PNOTIFY_CHANGE NotifyChange = NULL;
764     PREAL_NOTIFY_SYNC RealNotifySync;
765 
766     PAGED_CODE();
767 
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);
771 
772     /* Get real structure hidden behind the opaque pointer */
773     RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
774 
775     /* Acquire the fast mutex */
776     FsRtlNotifyAcquireFastMutex(RealNotifySync);
777 
778     _SEH2_TRY
779     {
780         /* If we have no IRP, FSD is performing a cleanup */
781         if (!NotifyIrp)
782         {
783             /* So, we delete */
784             FsRtlCheckNotifyForDelete(NotifyList, FsContext);
785             _SEH2_LEAVE;
786         }
787 
788         NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
789         NotifyIrp->IoStatus.Information = (ULONG_PTR)NULL;
790 
791         Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
792         /* If FileObject's been cleaned up, just return */
793         if (Stack->FileObject->Flags & FO_CLEANUP_COMPLETE)
794         {
795             IoMarkIrpPending(NotifyIrp);
796             NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
797             IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
798             _SEH2_LEAVE;
799         }
800 
801         /* Try to find a matching notification has been already registered */
802         NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
803         if (NotifyChange)
804         {
805             /* If it's been found, and is cleaned up, immediatly complete */
806             if (NotifyChange->Flags & CLEANUP_IN_PROCESS)
807             {
808                 IoMarkIrpPending(NotifyIrp);
809                 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
810                 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
811             }
812             /* Or if it's about to be deleted, complete */
813             else if (NotifyChange->Flags & DELETE_IN_PROCESS)
814             {
815                 IoMarkIrpPending(NotifyIrp);
816                 NotifyIrp->IoStatus.Status = STATUS_DELETE_PENDING;
817                 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
818             }
819             /* Complete now if asked to (and not asked to notify later on) */
820             if ((NotifyChange->Flags & NOTIFY_IMMEDIATELY) && !(NotifyChange->Flags & NOTIFY_LATER))
821             {
822                 NotifyChange->Flags &= ~NOTIFY_IMMEDIATELY;
823                 IoMarkIrpPending(NotifyIrp);
824                 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR;
825                 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
826             }
827             /* If no data yet, or asked to notify later on, handle */
828             else if (NotifyChange->DataLength == 0 || (NotifyChange->Flags & NOTIFY_LATER))
829             {
830                 goto HandleIRP;
831             }
832             /* Else, just complete with we have */
833             else
834             {
835                 SavedLength = NotifyChange->DataLength;
836                 NotifyChange->DataLength = 0;
837                 FsRtlNotifyCompleteIrp(NotifyIrp, NotifyChange, SavedLength, STATUS_SUCCESS, FALSE);
838             }
839 
840             _SEH2_LEAVE;
841         }
842 
843         /* Allocate new notification */
844         NotifyChange = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
845                                              sizeof(NOTIFY_CHANGE), 'NrSF');
846         RtlZeroMemory(NotifyChange, sizeof(NOTIFY_CHANGE));
847 
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));
857 
858         /* Keep trace of WatchTree */
859         if (WatchTree)
860         {
861             NotifyChange->Flags |= WATCH_TREE;
862         }
863 
864         /* If string is empty, faulty to ANSI */
865         if (FullDirectoryName->Length == 0)
866         {
867             NotifyChange->CharacterSize = sizeof(CHAR);
868         }
869         else
870         {
871             /* If it can't contain WCHAR, it's ANSI */
872             if (FullDirectoryName->Length < sizeof(WCHAR) || ((CHAR*)FullDirectoryName->Buffer)[1] != 0)
873             {
874                 NotifyChange->CharacterSize = sizeof(CHAR);
875             }
876             else
877             {
878                 NotifyChange->CharacterSize = sizeof(WCHAR);
879             }
880 
881             /* Now, check is user is willing to watch root */
882             if (FullDirectoryName->Length == NotifyChange->CharacterSize)
883             {
884                 NotifyChange->Flags |= WATCH_ROOT;
885             }
886         }
887 
888         NotifyChange->CompletionFilter = CompletionFilter;
889 
890         /* In case we have not to ignore buffer , keep its length */
891         if (!IgnoreBuffer)
892         {
893             NotifyChange->BufferLength = Stack->Parameters.NotifyDirectory.Length;
894         }
895 
896         NotifyChange->OwningProcess = NotifyIrp->Tail.Overlay.Thread->ThreadsProcess;
897 
898         /* Insert the notification into the notification list */
899         InsertTailList(NotifyList, &(NotifyChange->NotifyList));
900 
901         NotifyChange->ReferenceCount = 1;
902 
903 HandleIRP:
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);
914     }
915     _SEH2_FINALLY
916     {
917         /* Release fast mutex */
918         FsRtlNotifyReleaseFastMutex(RealNotifySync);
919 
920         /* If the subject security context was captured and there's no notify */
921         if (SubjectContext && (!NotifyChange || NotifyChange->FullDirectoryName))
922         {
923             SeReleaseSubjectContext(SubjectContext);
924             ExFreePool(SubjectContext);
925         }
926     }
927     _SEH2_END;
928 }
929 
930 /*++
931  * @name FsRtlNotifyFilterReportChange
932  * @implemented
933  *
934  * FILLME
935  *
936  * @param NotifySync
937  *        FILLME
938  *
939  * @param NotifyList
940  *        FILLME
941  *
942  * @param FullTargetName
943  *        FILLME
944  *
945  * @param TargetNameOffset
946  *        FILLME
947  *
948  * @param StreamName
949  *        FILLME
950  *
951  * @param NormalizedParentName
952  *        FILLME
953  *
954  * @param FilterMatch
955  *        FILLME
956  *
957  * @param Action
958  *        FILLME
959  *
960  * @param TargetContext
961  *        FILLME
962  *
963  * @param FilterContext
964  *        FILLME
965  *
966  * @return None
967  *
968  * @remarks None
969  *
970  *--*/
971 VOID
972 NTAPI
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,
980                               IN ULONG Action,
981                               IN PVOID TargetContext,
982                               IN PVOID FilterContext)
983 {
984     PIRP Irp;
985     PVOID OutputBuffer;
986     USHORT FullPosition;
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;
996 
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;
1006     IsStream = FALSE;
1007 
1008     PAGED_CODE();
1009 
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);
1013 
1014     /* We need offset in name */
1015     if (!TargetNameOffset && FullTargetName)
1016     {
1017         return;
1018     }
1019 
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);
1024     _SEH2_TRY
1025     {
1026         /* Browse all the registered notifications we have */
1027         for (NextEntry = NotifyList->Flink; NextEntry != NotifyList;
1028              NextEntry = NextEntry->Flink)
1029         {
1030             /* Try to find an entry matching our change */
1031             NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
1032             if (FullTargetName != NULL)
1033             {
1034                 ASSERT(NotifyChange->FullDirectoryName != NULL);
1035                 if (!NotifyChange->FullDirectoryName->Length)
1036                 {
1037                     continue;
1038                 }
1039 
1040                 if (!(FilterMatch & NotifyChange->CompletionFilter))
1041                 {
1042                     continue;
1043                 }
1044 
1045                 /* If no normalized name provided, construct it from full target name */
1046                 if (NormalizedParentName == NULL)
1047                 {
1048                     IntNormalizedParentName.Buffer = FullTargetName->Buffer;
1049                     if (TargetNameOffset != NotifyChange->CharacterSize)
1050                     {
1051                         IntNormalizedParentName.MaximumLength =
1052                         IntNormalizedParentName.Length = TargetNameOffset - NotifyChange->CharacterSize;
1053                     }
1054                     else
1055                     {
1056                         IntNormalizedParentName.MaximumLength =
1057                         IntNormalizedParentName.Length = TargetNameOffset;
1058                     }
1059                     NormalizedParentName = &IntNormalizedParentName;
1060                 }
1061 
1062                 /* heh? Watched directory bigger than changed file? */
1063                 if (NormalizedParentName->Length < NotifyChange->FullDirectoryName->Length)
1064                 {
1065                     continue;
1066                 }
1067 
1068                 /* Same len => parent */
1069                 if (NormalizedParentName->Length == NotifyChange->FullDirectoryName->Length)
1070                 {
1071                     IsParent = TRUE;
1072                 }
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))
1075                 {
1076                     continue;
1077                 }
1078                 /* And finally, we've to check we're properly \-terminated */
1079                 else
1080                 {
1081                     if (!(NotifyChange->Flags & WATCH_ROOT))
1082                     {
1083                         if (NotifyChange->CharacterSize == sizeof(CHAR))
1084                         {
1085                             if (((PSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length] != '\\')
1086                             {
1087                                 continue;
1088                             }
1089                         }
1090                         else
1091                         {
1092                             if (((PWSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length / sizeof (WCHAR)] != L'\\')
1093                             {
1094                                 continue;
1095                             }
1096                         }
1097                     }
1098 
1099                     IsParent = FALSE;
1100                 }
1101 
1102                 /* If len matches, then check that both name are equal */
1103                 if (!RtlEqualMemory(NormalizedParentName->Buffer, NotifyChange->FullDirectoryName->Buffer,
1104                                     NotifyChange->FullDirectoryName->Length))
1105                 {
1106                     continue;
1107                 }
1108 
1109                 /* Call traverse callback (only if we have to traverse ;-)) */
1110                 if (!IsParent
1111                     && NotifyChange->TraverseCallback != NULL
1112                     && !NotifyChange->TraverseCallback(NotifyChange->FsContext,
1113                                                        TargetContext,
1114                                                        NotifyChange->SubjectContext))
1115                 {
1116                     continue;
1117                 }
1118 
1119                 /* And then, filter callback if provided */
1120                 if (NotifyChange->FilterCallback != NULL
1121                     && FilterContext != NULL
1122                     && !NotifyChange->FilterCallback(NotifyChange->FsContext, FilterContext))
1123                 {
1124                     continue;
1125                 }
1126             }
1127             /* We have a stream! */
1128             else
1129             {
1130                 ASSERT(NotifyChange->FullDirectoryName == NULL);
1131                 if (TargetContext != NotifyChange->SubjectContext)
1132                 {
1133                     continue;
1134                 }
1135 
1136                 ParentName.Buffer = NULL;
1137                 ParentName.Length = 0;
1138                 IsStream = TRUE;
1139                 IsParent = FALSE;
1140             }
1141 
1142             /* If we don't have to notify immediately, prepare for output */
1143             if (!(NotifyChange->Flags & NOTIFY_IMMEDIATELY))
1144             {
1145                 /* If we have something to output... */
1146                 if (NotifyChange->BufferLength)
1147                 {
1148                     /* Get size of the output */
1149                     NumberOfBytes = 0;
1150                     Irp = NULL;
1151                     if (!NotifyChange->ThisBufferLength)
1152                     {
1153                         if (IsListEmpty(&NotifyChange->NotifyIrps))
1154                         {
1155                             NumberOfBytes = NotifyChange->BufferLength;
1156                         }
1157                         else
1158                         {
1159                             Irp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry);
1160                             Stack = IoGetCurrentIrpStackLocation(Irp);
1161                             NumberOfBytes = Stack->Parameters.NotifyDirectory.Length;
1162                         }
1163                     }
1164                     else
1165                     {
1166                         NumberOfBytes = NotifyChange->ThisBufferLength;
1167                     }
1168 
1169                     /* If we're matching parent, we don't care about parent (redundant) */
1170                     if (IsParent)
1171                     {
1172                         ParentName.Length = 0;
1173                     }
1174                     else
1175                     {
1176                         /* If we don't deal with streams, some more work is required */
1177                         if (!IsStream)
1178                         {
1179                             if (NotifyChange->Flags & WATCH_ROOT ||
1180                                 (NormalizedParentName->Buffer != FullTargetName->Buffer))
1181                             {
1182                                 /* Construct TargetDirectory if we don't have it yet */
1183                                 if (TargetDirectory.Buffer == NULL)
1184                                 {
1185                                     TargetDirectory.Buffer = FullTargetName->Buffer;
1186                                     TargetDirectory.Length = TargetNameOffset;
1187                                     if (TargetNameOffset != NotifyChange->CharacterSize)
1188                                     {
1189                                         TargetDirectory.Length = TargetNameOffset - NotifyChange->CharacterSize;
1190                                     }
1191                                     TargetDirectory.MaximumLength = TargetDirectory.Length;
1192                                 }
1193                                 /* Now, we start looking for matching parts (unless we watch root) */
1194                                 TargetNumberOfParts = 0;
1195                                 if (!(NotifyChange->Flags & WATCH_ROOT))
1196                                 {
1197                                     FullNumberOfParts = 1;
1198                                     if (NotifyChange->CharacterSize == sizeof(CHAR))
1199                                     {
1200                                         FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length,
1201                                                                      TargetDirectory.Length, PSTR, '\\');
1202                                     }
1203                                     else
1204                                     {
1205                                         FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length / sizeof(WCHAR),
1206                                                                      TargetDirectory.Length / sizeof(WCHAR), PWSTR, L'\\');
1207                                         LastPartOffset *= NotifyChange->CharacterSize;
1208                                     }
1209                                 }
1210 
1211                                 /* Then, we can construct proper parent name */
1212                                 ParentNameOffset = NotifyChange->CharacterSize + LastPartOffset;
1213                                 ParentName.Buffer = &TargetDirectory.Buffer[ParentNameOffset];
1214                                 ParentNameLength = TargetDirectory.Length;
1215                             }
1216                             else
1217                             {
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;
1222                             }
1223                             ParentNameLength -= ParentNameOffset;
1224                             ParentName.Length = ParentNameLength;
1225                             ParentName.MaximumLength = ParentNameLength;
1226                         }
1227                     }
1228 
1229                     /* Start to count amount of data to write, we've first the structure itself */
1230                     DataLength = FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName);
1231 
1232                     /* If stream, we'll just append stream name */
1233                     if (IsStream)
1234                     {
1235                         ASSERT(StreamName != NULL);
1236                         DataLength += StreamName->Length;
1237                     }
1238                     else
1239                     {
1240                         /* If not parent, we've to append parent name */
1241                         if (!IsParent)
1242                         {
1243                             if (NotifyChange->CharacterSize == sizeof(CHAR))
1244                             {
1245                                 DataLength += RtlOemStringToCountedUnicodeSize(&ParentName);
1246                             }
1247                             else
1248                             {
1249                                 DataLength += ParentName.Length;
1250                             }
1251                             DataLength += sizeof(WCHAR);
1252                         }
1253 
1254                         /* Look for target name & construct it, if required */
1255                         if (TargetName.Buffer == NULL)
1256                         {
1257                             TargetName.Buffer = &FullTargetName->Buffer[TargetNameOffset];
1258                             TargetName.Length =
1259                             TargetName.MaximumLength = FullTargetName->Length - TargetNameOffset;
1260                         }
1261 
1262                         /* Then, we will append it as well */
1263                         if (NotifyChange->CharacterSize == sizeof(CHAR))
1264                         {
1265                             DataLength += RtlOemStringToCountedUnicodeSize(&TargetName);
1266                         }
1267                         else
1268                         {
1269                             DataLength += TargetName.Length;
1270                         }
1271 
1272                         /* If we also had a stream name, then we can append it as well */
1273                         if (StreamName != NULL)
1274                         {
1275                             if (NotifyChange->CharacterSize == sizeof(WCHAR))
1276                             {
1277                                 DataLength += StreamName->Length + sizeof(WCHAR);
1278                             }
1279                             else
1280                             {
1281                                 DataLength = DataLength + RtlOemStringToCountedUnicodeSize(&TargetName) + sizeof(CHAR);
1282                             }
1283                         }
1284                     }
1285 
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)
1290                     {
1291                         NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1292                     }
1293                     else
1294                     {
1295                         OutputBuffer = NULL;
1296                         FileNotifyInfo = NULL;
1297                         /* If we already had a buffer, update last entry position */
1298                         if (NotifyChange->Buffer != NULL)
1299                         {
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);
1305                         }
1306                         /* If we hadn't buffer, try to find one */
1307                         else if (Irp != NULL)
1308                         {
1309                             if (Irp->AssociatedIrp.SystemBuffer != NULL)
1310                             {
1311                                 OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
1312                             }
1313                             else if (Irp->MdlAddress != NULL)
1314                             {
1315                                 OutputBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
1316                             }
1317 
1318                             NotifyChange->Buffer = OutputBuffer;
1319                             NotifyChange->ThisBufferLength = NumberOfBytes;
1320                         }
1321 
1322                         /* If we couldn't find one, then allocate one */
1323                         if (NotifyChange->Buffer == NULL)
1324                         {
1325                             PoolQuotaCharged = FALSE;
1326                             _SEH2_TRY
1327                             {
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;
1334                             }
1335                             /* If something went wrong during allocation, notify immediately instead of outputing */
1336                             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1337                             {
1338                                 if (PoolQuotaCharged)
1339                                 {
1340                                     PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NumberOfBytes);
1341                                 }
1342                                 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1343                             }
1344                             _SEH2_END;
1345                         }
1346 
1347                         /* Finally, if we have a buffer, fill it in! */
1348                         if (OutputBuffer != NULL)
1349                         {
1350                             if (FsRtlNotifyUpdateBuffer((FILE_NOTIFY_INFORMATION *)OutputBuffer,
1351                                                          Action, &ParentName, &TargetName,
1352                                                          StreamName, NotifyChange->CharacterSize == sizeof(WCHAR),
1353                                                          DataLength))
1354                             {
1355                                 NotifyChange->DataLength = DataLength + AlignedDataLength;
1356                             }
1357                             /* If it failed, notify immediately */
1358                             else
1359                             {
1360                                 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1361                             }
1362                         }
1363                     }
1364 
1365                     /* If we have to notify right now (something went wrong?) */
1366                     if (NotifyChange->Flags & NOTIFY_IMMEDIATELY)
1367                     {
1368                         /* Ensure that all our buffers are NULL */
1369                         if (NotifyChange->Buffer != NULL)
1370                         {
1371                             if (NotifyChange->AllocatedBuffer != NULL)
1372                             {
1373                                 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
1374                                 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, TAG_FS_NOTIFICATIONS);
1375                             }
1376 
1377                             NotifyChange->Buffer = NULL;
1378                             NotifyChange->AllocatedBuffer = NULL;
1379                             NotifyChange->LastEntry = 0;
1380                             NotifyChange->DataLength = 0;
1381                             NotifyChange->ThisBufferLength = 0;
1382                         }
1383                     }
1384                 }
1385             }
1386 
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
1390              */
1391             if (Action == FILE_ACTION_RENAMED_OLD_NAME)
1392             {
1393                 NotifyChange->Flags |= NOTIFY_LATER;
1394             }
1395             else
1396             {
1397                 NotifyChange->Flags &= ~NOTIFY_LATER;
1398                 if (!IsListEmpty(&NotifyChange->NotifyIrps))
1399                 {
1400                     FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_SUCCESS);
1401                 }
1402             }
1403         }
1404     }
1405     _SEH2_FINALLY
1406     {
1407         FsRtlNotifyReleaseFastMutex(RealNotifySync);
1408     }
1409     _SEH2_END;
1410 }
1411 
1412 /*++
1413  * @name FsRtlNotifyFullChangeDirectory
1414  * @implemented
1415  *
1416  * Lets FSD know if changes occures in the specified directory.
1417  *
1418  * @param NotifySync
1419  *        Synchronization object pointer
1420  *
1421  * @param NotifyList
1422  *        Notify list pointer (to head)
1423  *
1424  * @param FsContext
1425  *        Used to identify the notify structure
1426  *
1427  * @param FullDirectoryName
1428  *        String (A or W) containing the full directory name
1429  *
1430  * @param WatchTree
1431  *        True to notify changes in subdirectories too
1432  *
1433  * @param IgnoreBuffer
1434  *        True to reenumerate directory. It's ignored it NotifyIrp is null
1435  *
1436  * @param CompletionFilter
1437  *        Used to define types of changes to notify
1438  *
1439  * @param NotifyIrp
1440  *        IRP pointer to complete notify operation. It can be null
1441  *
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
1445  *        is null
1446  *
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
1450  *
1451  * @return None
1452  *
1453  * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
1454  *
1455  *--*/
1456 VOID
1457 NTAPI
1458 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
1459                                IN PLIST_ENTRY NotifyList,
1460                                IN PVOID FsContext,
1461                                IN PSTRING FullDirectoryName,
1462                                IN BOOLEAN WatchTree,
1463                                IN BOOLEAN IgnoreBuffer,
1464                                IN ULONG CompletionFilter,
1465                                IN PIRP NotifyIrp,
1466                                IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
1467                                IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
1468 {
1469     FsRtlNotifyFilterChangeDirectory(NotifySync,
1470                                      NotifyList,
1471                                      FsContext,
1472                                      FullDirectoryName,
1473                                      WatchTree,
1474                                      IgnoreBuffer,
1475                                      CompletionFilter,
1476                                      NotifyIrp,
1477                                      TraverseCallback,
1478                                      SubjectContext,
1479                                      NULL);
1480 }
1481 
1482 /*++
1483  * @name FsRtlNotifyFullReportChange
1484  * @implemented
1485  *
1486  * Complets the pending notify IRPs.
1487  *
1488  * @param NotifySync
1489  *        Synchronization object pointer
1490  *
1491  * @param NotifyList
1492  *        Notify list pointer (to head)
1493  *
1494  * @param FullTargetName
1495  *        String (A or W) containing the full directory name that changed
1496  *
1497  * @param TargetNameOffset
1498  *        Offset, in FullTargetName, of the final component that is in the changed directory
1499  *
1500  * @param StreamName
1501  *        String (A or W) containing a stream name
1502  *
1503  * @param NormalizedParentName
1504  *        String (A or W) containing the full directory name that changed with long names
1505  *
1506  * @param FilterMatch
1507  *        Flags that will be compared to the completion filter
1508  *
1509  * @param Action
1510  *        Action code to store in user's buffer
1511  *
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.
1515  *
1516  * @return None
1517  *
1518  * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1519  *
1520  *--*/
1521 VOID
1522 NTAPI
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,
1530                             IN ULONG Action,
1531                             IN PVOID TargetContext)
1532 {
1533     FsRtlNotifyFilterReportChange(NotifySync,
1534                                   NotifyList,
1535                                   FullTargetName,
1536                                   TargetNameOffset,
1537                                   StreamName,
1538                                   NormalizedParentName,
1539                                   FilterMatch,
1540                                   Action,
1541                                   TargetContext,
1542                                   NULL);
1543 }
1544 
1545 /*++
1546  * @name FsRtlNotifyInitializeSync
1547  * @implemented
1548  *
1549  * Allocates the internal structure associated with notifications.
1550  *
1551  * @param NotifySync
1552  *        Opaque pointer. It will receive the address of the allocated internal structure.
1553  *
1554  * @return None
1555  *
1556  * @remarks This function raise an exception in case of a failure.
1557  *
1558  *--*/
1559 VOID
1560 NTAPI
1561 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
1562 {
1563     PREAL_NOTIFY_SYNC RealNotifySync;
1564 
1565     *NotifySync = NULL;
1566 
1567     RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
1568                                            sizeof(REAL_NOTIFY_SYNC), 'SNSF');
1569     ExInitializeFastMutex(&(RealNotifySync->FastMutex));
1570     RealNotifySync->OwningThread = 0;
1571     RealNotifySync->OwnerCount = 0;
1572 
1573     *NotifySync = RealNotifySync;
1574 }
1575 
1576 /*++
1577  * @name FsRtlNotifyReportChange
1578  * @implemented
1579  *
1580  * Complets the pending notify IRPs.
1581  *
1582  * @param NotifySync
1583  *        Synchronization object pointer
1584  *
1585  * @param NotifyList
1586  *        Notify list pointer (to head)
1587  *
1588  * @param FullTargetName
1589  *        String (A or W) containing the full directory name that changed
1590  *
1591  * @param FileNamePartLength
1592  *        Length of the final component that is in the changed directory
1593  *
1594  * @param FilterMatch
1595  *        Flags that will be compared to the completion filter
1596  *
1597  * @return None
1598  *
1599  * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1600  *
1601  *--*/
1602 VOID
1603 NTAPI
1604 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
1605                         IN PLIST_ENTRY NotifyList,
1606                         IN PSTRING FullTargetName,
1607                         IN PUSHORT FileNamePartLength,
1608                         IN ULONG FilterMatch)
1609 {
1610       FsRtlNotifyFilterReportChange(NotifySync,
1611                                     NotifyList,
1612                                     FullTargetName,
1613                                     FullTargetName->Length - *FileNamePartLength,
1614                                     NULL,
1615                                     NULL,
1616                                     FilterMatch,
1617                                     0,
1618                                     NULL,
1619                                     NULL);
1620 }
1621 
1622 /*++
1623  * @name FsRtlNotifyUninitializeSync
1624  * @implemented
1625  *
1626  * Uninitialize a NOTIFY_SYNC object
1627  *
1628  * @param NotifySync
1629  *        Address of a pointer to a PNOTIFY_SYNC object previously
1630  *        initialized by FsRtlNotifyInitializeSync()
1631  *
1632  * @return None
1633  *
1634  * @remarks None
1635  *
1636  *--*/
1637 VOID
1638 NTAPI
1639 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
1640 {
1641     if (*NotifySync)
1642     {
1643         ExFreePoolWithTag(*NotifySync, 'SNSF');
1644         *NotifySync = NULL;
1645     }
1646 }
1647 
1648