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