Sync to trunk r65566.
[reactos.git] / ntoskrnl / fsrtl / notify.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/notify.c
5 * PURPOSE: Change Notifications and Sync for File System Drivers
6 * PROGRAMMERS: Pierre Schweitzer
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* 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 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, 'NrSF');
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 IofCompleteRequest(Irp, EVENT_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, 'NrSF');
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, EVENT_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 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, 'FSrN');
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, EVENT_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, EVENT_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, EVENT_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, EVENT_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), 'FSrN');
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, 'NrSF');
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, 'NrSF');
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), 'FSNS');
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, 'FSNS');
1644 *NotifySync = NULL;
1645 }
1646 }
1647