[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / notify.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/notify.c
5 * PURPOSE: Change Notifications and Sync for File System Drivers
6 * PROGRAMMERS: Pierre Schweitzer
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
19 IN NTSTATUS Status);
20
21 BOOLEAN
22 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
23 IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
24
25 VOID
26 NTAPI
27 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
28 IN PIRP Irp)
29 {
30 IoReleaseCancelSpinLock(Irp->CancelIrql);
31 UNIMPLEMENTED;
32 }
33
34 /*
35 * @implemented
36 */
37 VOID
38 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,
39 IN PVOID FsContext)
40 {
41 PLIST_ENTRY NextEntry;
42 PNOTIFY_CHANGE NotifyChange;
43
44 if (!IsListEmpty(NotifyList))
45 {
46 /* Browse the notifications list to find the matching entry */
47 for (NextEntry = NotifyList->Flink;
48 NextEntry != NotifyList;
49 NextEntry = NextEntry->Flink)
50 {
51 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
52 /* If the current record matches with the given context, it's the good one */
53 if (NotifyChange->FsContext == FsContext && !IsListEmpty(&(NotifyChange->NotifyIrps)))
54 {
55 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_DELETE_PENDING);
56 }
57 }
58 }
59 }
60
61 PNOTIFY_CHANGE
62 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
63 IN PVOID FsContext)
64 {
65 PLIST_ENTRY NextEntry;
66 PNOTIFY_CHANGE NotifyChange;
67
68 if (!IsListEmpty(NotifyList))
69 {
70 /* Browse the notifications list to find the matching entry */
71 for (NextEntry = NotifyList->Flink;
72 NextEntry != NotifyList;
73 NextEntry = NextEntry->Flink)
74 {
75 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
76 /* If the current record matches with the given context, it's the good one */
77 if (NotifyChange->FsContext == FsContext)
78 {
79 return NotifyChange;
80 }
81 }
82 }
83 return NULL;
84 }
85
86 VOID
87 FORCEINLINE
88 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
89 {
90 ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread();
91
92 /* Only acquire fast mutex if it's not already acquired by the current thread */
93 if (RealNotifySync->OwningThread != CurrentThread)
94 {
95 ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex));
96 RealNotifySync->OwningThread = CurrentThread;
97 }
98 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
99 RealNotifySync->OwnerCount++;
100 }
101
102 /*
103 * @implemented
104 */
105 VOID
106 FsRtlNotifyCompleteIrp(IN PIRP Irp,
107 IN PNOTIFY_CHANGE NotifyChange,
108 IN ULONG DataLength,
109 IN NTSTATUS Status,
110 IN BOOLEAN SkipCompletion)
111 {
112 PVOID Buffer;
113 PIO_STACK_LOCATION Stack;
114
115 PAGED_CODE();
116
117 /* Check if we need to complete */
118 if (!FsRtlNotifySetCancelRoutine(Irp, NotifyChange) && SkipCompletion)
119 {
120 return;
121 }
122
123 /* No succes => no data to return just complete */
124 if (Status != STATUS_SUCCESS)
125 {
126 goto Completion;
127 }
128
129 /* Ensure there's something to return */
130 Stack = IoGetCurrentIrpStackLocation(Irp);
131 if (!DataLength || Stack->Parameters.NotifyDirectory.Length < DataLength)
132 {
133 Status = STATUS_NOTIFY_ENUM_DIR;
134 goto Completion;
135 }
136
137 /* Ensture there's a buffer where to find data */
138 if (!NotifyChange->AllocatedBuffer)
139 {
140 Irp->IoStatus.Information = DataLength;
141 NotifyChange->Buffer = NULL;
142 goto Completion;
143 }
144
145 /* Now, browse all the way to return data
146 * and find the one that will work. We will
147 * return data whatever happens
148 */
149 if (Irp->AssociatedIrp.SystemBuffer)
150 {
151 Buffer = Irp->AssociatedIrp.SystemBuffer;
152 goto CopyAndComplete;
153 }
154
155 if (Irp->MdlAddress)
156 {
157 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
158 goto CopyAndComplete;
159 }
160
161 if (!(Stack->Control & SL_PENDING_RETURNED))
162 {
163 Buffer = Irp->UserBuffer;
164 goto CopyAndComplete;
165 }
166
167 Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_SYNCHRONOUS_PAGING_IO);
168 Irp->AssociatedIrp.SystemBuffer = NotifyChange->AllocatedBuffer;
169 /* Nothing to copy */
170 goto ReleaseAndComplete;
171
172 CopyAndComplete:
173 _SEH2_TRY
174 {
175 RtlCopyMemory(Buffer, NotifyChange->AllocatedBuffer, DataLength);
176 }
177 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
178 {
179 /* Do nothing */
180 }
181 _SEH2_END;
182
183 ReleaseAndComplete:
184 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
185
186 /* Release buffer UNLESS it's used */
187 if (NotifyChange->AllocatedBuffer != Irp->AssociatedIrp.SystemBuffer &&
188 NotifyChange->AllocatedBuffer)
189 {
190 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 0);
191 }
192
193 /* Prepare for return */
194 NotifyChange->AllocatedBuffer = 0;
195 NotifyChange->ThisBufferLength = 0;
196 Irp->IoStatus.Information = DataLength;
197 NotifyChange->Buffer = NULL;
198
199 /* Finally complete */
200 Completion:
201 IoMarkIrpPending(Irp);
202 Irp->IoStatus.Status = Status;
203 IoCompleteRequest(Irp, EVENT_INCREMENT);
204 }
205
206 /*
207 * @implemented
208 */
209 VOID
210 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
211 IN NTSTATUS Status)
212 {
213 PIRP Irp;
214 ULONG DataLength;
215 PLIST_ENTRY NextEntry;
216
217 DataLength = NotifyChange->DataLength;
218
219 NotifyChange->Flags &= (INVALIDATE_BUFFERS | WATCH_TREE);
220 NotifyChange->DataLength = 0;
221 NotifyChange->LastEntry = 0;
222
223 while (!IsListEmpty(&(NotifyChange->NotifyIrps)))
224 {
225 /* We take the first entry */
226 NextEntry = RemoveHeadList(&(NotifyChange->NotifyIrps));
227 Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
228 /* We complete it */
229 FsRtlNotifyCompleteIrp(Irp, NotifyChange, DataLength, Status, TRUE);
230 /* If we're notifying success, just notify first one */
231 if (Status == STATUS_SUCCESS)
232 break;
233 }
234 }
235
236 VOID
237 FORCEINLINE
238 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
239 {
240 RealNotifySync->OwnerCount--;
241 /* Release the fast mutex only if no other instance needs it */
242 if (!RealNotifySync->OwnerCount)
243 {
244 ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex));
245 RealNotifySync->OwningThread = (ULONG_PTR)0;
246 }
247 }
248
249 /*
250 * @implemented
251 */
252 BOOLEAN
253 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
254 IN PNOTIFY_CHANGE NotifyChange OPTIONAL)
255 {
256 PDRIVER_CANCEL CancelRoutine;
257
258 /* Acquire cancel lock */
259 IoAcquireCancelSpinLock(&Irp->CancelIrql);
260
261 /* If NotifyChange was given */
262 if (NotifyChange)
263 {
264 /* First get cancel routine */
265 CancelRoutine = IoSetCancelRoutine(Irp, NULL);
266 Irp->IoStatus.Information = 0;
267 /* Release cancel lock */
268 IoReleaseCancelSpinLock(Irp->CancelIrql);
269 /* If there was a cancel routine */
270 if (CancelRoutine)
271 {
272 /* Decrease reference count */
273 InterlockedDecrement((PLONG)&NotifyChange->ReferenceCount);
274 /* Notify that we removed cancel routine */
275 return TRUE;
276 }
277 }
278 else
279 {
280 /* If IRP is cancel, call FsRtl cancel routine */
281 if (Irp->Cancel)
282 {
283 FsRtlCancelNotify(NULL, Irp);
284 }
285 else
286 {
287 /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
288 IoSetCancelRoutine(Irp, FsRtlCancelNotify);
289 /* Release lock */
290 IoReleaseCancelSpinLock(Irp->CancelIrql);
291 }
292 }
293
294 /* Return that we didn't removed cancel routine */
295 return FALSE;
296 }
297
298 /* PUBLIC FUNCTIONS **********************************************************/
299
300 /*++
301 * @name FsRtlNotifyChangeDirectory
302 * @implemented
303 *
304 * Lets FSD know if changes occures in the specified directory.
305 * Directory will be reenumerated.
306 *
307 * @param NotifySync
308 * Synchronization object pointer
309 *
310 * @param FsContext
311 * Used to identify the notify structure
312 *
313 * @param FullDirectoryName
314 * String (A or W) containing the full directory name
315 *
316 * @param NotifyList
317 * Notify list pointer (to head)
318 *
319 * @param WatchTree
320 * True to notify changes in subdirectories too
321 *
322 * @param CompletionFilter
323 * Used to define types of changes to notify
324 *
325 * @param NotifyIrp
326 * IRP pointer to complete notify operation. It can be null
327 *
328 * @return None
329 *
330 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
331 *
332 *--*/
333 VOID
334 NTAPI
335 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
336 IN PVOID FsContext,
337 IN PSTRING FullDirectoryName,
338 IN PLIST_ENTRY NotifyList,
339 IN BOOLEAN WatchTree,
340 IN ULONG CompletionFilter,
341 IN PIRP NotifyIrp)
342 {
343 FsRtlNotifyFilterChangeDirectory(NotifySync,
344 NotifyList,
345 FsContext,
346 FullDirectoryName,
347 WatchTree,
348 TRUE,
349 CompletionFilter,
350 NotifyIrp,
351 NULL,
352 NULL,
353 NULL);
354 }
355
356 /*++
357 * @name FsRtlNotifyCleanup
358 * @implemented
359 *
360 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
361 *
362 * @param NotifySync
363 * Synchronization object pointer
364 *
365 * @param NotifyList
366 * Notify list pointer (to head)
367 *
368 * @param FsContext
369 * Used to identify the notify structure
370 *
371 * @return None
372 *
373 * @remarks None
374 *
375 *--*/
376 VOID
377 NTAPI
378 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
379 IN PLIST_ENTRY NotifyList,
380 IN PVOID FsContext)
381 {
382 PNOTIFY_CHANGE NotifyChange;
383 PREAL_NOTIFY_SYNC RealNotifySync;
384 PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
385
386 /* Get real structure hidden behind the opaque pointer */
387 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
388
389 /* Acquire the fast mutex */
390 FsRtlNotifyAcquireFastMutex(RealNotifySync);
391
392 _SEH2_TRY
393 {
394 /* Find if there's a matching notification with the FsContext */
395 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
396 if (NotifyChange)
397 {
398 /* Mark it as to know that cleanup is in process */
399 NotifyChange->Flags |= CLEANUP_IN_PROCESS;
400
401 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
402 if (!IsListEmpty(&NotifyChange->NotifyIrps))
403 {
404 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
405 }
406
407 /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
408 if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
409 {
410 /* Remove it from the notifications list */
411 RemoveEntryList(&NotifyChange->NotifyList);
412
413 /* In case there was an allocated buffer, free it */
414 if (NotifyChange->AllocatedBuffer)
415 {
416 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
417 ExFreePool(NotifyChange->AllocatedBuffer);
418 }
419
420 /* In case there the string was set, get the captured subject security context */
421 if (NotifyChange->FullDirectoryName)
422 {
423 SubjectContext = NotifyChange->SubjectContext;
424 }
425
426 /* Finally, free the notification, as it's not needed anymore */
427 ExFreePool(NotifyChange);
428 }
429 }
430 }
431 _SEH2_FINALLY
432 {
433 /* Release fast mutex */
434 FsRtlNotifyReleaseFastMutex(RealNotifySync);
435
436 /* If the subject security context was captured, release and free it */
437 if (SubjectContext)
438 {
439 SeReleaseSubjectContext(SubjectContext);
440 ExFreePool(SubjectContext);
441 }
442 }
443 _SEH2_END;
444 }
445
446 /*++
447 * @name FsRtlNotifyFilterChangeDirectory
448 * @unimplemented
449 *
450 * FILLME
451 *
452 * @param NotifySync
453 * FILLME
454 *
455 * @param NotifyList
456 * FILLME
457 *
458 * @param FsContext
459 * FILLME
460 *
461 * @param FullDirectoryName
462 * FILLME
463 *
464 * @param WatchTree
465 * FILLME
466 *
467 * @param IgnoreBuffer
468 * FILLME
469 *
470 * @param CompletionFilter
471 * FILLME
472 *
473 * @param NotifyIrp
474 * FILLME
475 *
476 * @param TraverseCallback
477 * FILLME
478 *
479 * @param SubjectContext
480 * FILLME
481 *
482 * @param FilterCallback
483 * FILLME
484 *
485 * @return None
486 *
487 * @remarks None
488 *
489 *--*/
490 VOID
491 NTAPI
492 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
493 IN PLIST_ENTRY NotifyList,
494 IN PVOID FsContext,
495 IN PSTRING FullDirectoryName,
496 IN BOOLEAN WatchTree,
497 IN BOOLEAN IgnoreBuffer,
498 IN ULONG CompletionFilter,
499 IN PIRP NotifyIrp,
500 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
501 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
502 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
503 {
504 ULONG SavedLength;
505 PIO_STACK_LOCATION Stack;
506 PNOTIFY_CHANGE NotifyChange;
507 PREAL_NOTIFY_SYNC RealNotifySync;
508
509 PAGED_CODE();
510
511 DPRINT("FsRtlNotifyFilterChangeDirectory(): %p, %p, %p, %wZ, %d, %d, %u, %p, %p, %p, %p\n",
512 NotifySync, NotifyList, FsContext, FullDirectoryName, WatchTree, IgnoreBuffer, CompletionFilter, NotifyIrp,
513 TraverseCallback, SubjectContext, FilterCallback);
514
515 /* Get real structure hidden behind the opaque pointer */
516 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
517
518 /* Acquire the fast mutex */
519 FsRtlNotifyAcquireFastMutex(RealNotifySync);
520
521 _SEH2_TRY
522 {
523 /* If we have no IRP, FSD is performing a cleanup */
524 if (!NotifyIrp)
525 {
526 /* So, we delete */
527 FsRtlCheckNotifyForDelete(NotifyList, FsContext);
528 _SEH2_LEAVE;
529 }
530
531 NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
532 NotifyIrp->IoStatus.Information = (ULONG_PTR)NULL;
533
534 Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
535 /* If FileObject's been cleaned up, just return */
536 if (Stack->FileObject->Flags & FO_CLEANUP_COMPLETE)
537 {
538 IoMarkIrpPending(NotifyIrp);
539 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
540 IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
541 _SEH2_LEAVE;
542 }
543
544 /* Try to find a matching notification has been already registered */
545 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
546 if (NotifyChange)
547 {
548 /* If it's been found, and is cleaned up, immediatly complete */
549 if (NotifyChange->Flags & CLEANUP_IN_PROCESS)
550 {
551 IoMarkIrpPending(NotifyIrp);
552 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
553 IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
554 }
555 /* Or if it's about to be deleted, complete */
556 else if (NotifyChange->Flags & DELETE_IN_PROCESS)
557 {
558 IoMarkIrpPending(NotifyIrp);
559 NotifyIrp->IoStatus.Status = STATUS_DELETE_PENDING;
560 IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
561 }
562 /* Complete if there is directory enumeration and no buffer available any more */
563 if ((NotifyChange->Flags & INVALIDATE_BUFFERS) && (NotifyChange->Flags & ENUMERATE_DIR))
564 {
565 NotifyChange->Flags &= ~INVALIDATE_BUFFERS;
566 IoMarkIrpPending(NotifyIrp);
567 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR;
568 IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
569 }
570 /* If no data yet, or directory enumeration, handle */
571 else if (NotifyChange->DataLength == 0 || (NotifyChange->Flags & ENUMERATE_DIR))
572 {
573 goto HandleIRP;
574 }
575 /* Else, just complete with we have */
576 else
577 {
578 SavedLength = NotifyChange->DataLength;
579 NotifyChange->DataLength = 0;
580 FsRtlNotifyCompleteIrp(NotifyIrp, NotifyChange, SavedLength, STATUS_SUCCESS, FALSE);
581 }
582
583 _SEH2_LEAVE;
584 }
585
586 /* Allocate new notification */
587 NotifyChange = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
588 sizeof(NOTIFY_CHANGE), 'FSrN');
589 RtlZeroMemory(NotifyChange, sizeof(NOTIFY_CHANGE));
590
591 /* Set basic information */
592 NotifyChange->NotifySync = NotifySync;
593 NotifyChange->FsContext = FsContext;
594 NotifyChange->StreamID = Stack->FileObject->FsContext;
595 NotifyChange->TraverseCallback = TraverseCallback;
596 NotifyChange->SubjectContext = SubjectContext;
597 NotifyChange->FullDirectoryName = FullDirectoryName;
598 NotifyChange->FilterCallback = FilterCallback;
599 InitializeListHead(&(NotifyChange->NotifyIrps));
600
601 /* Keep trace of WatchTree */
602 if (WatchTree)
603 {
604 NotifyChange->Flags |= WATCH_TREE;
605 }
606
607 /* If string is empty, faulty to ANSI */
608 if (FullDirectoryName->Length == 0)
609 {
610 NotifyChange->CharacterSize = sizeof(CHAR);
611 }
612 else
613 {
614 /* If it can't contain WCHAR, it's ANSI */
615 if (FullDirectoryName->Length < sizeof(WCHAR))
616 {
617 NotifyChange->CharacterSize = sizeof(CHAR);
618 }
619 /* First char is \, so in unicode, right part is 0
620 * whereas in ANSI it contains next char
621 */
622 else if (((CHAR*)FullDirectoryName->Buffer)[1] == 0)
623 {
624 NotifyChange->CharacterSize = sizeof(WCHAR);
625 }
626 else
627 {
628 NotifyChange->CharacterSize = sizeof(CHAR);
629 }
630
631 /* Now, check is user is willing to watch root */
632 if (FullDirectoryName->Length == NotifyChange->CharacterSize)
633 {
634 NotifyChange->Flags |= WATCH_ROOT;
635 }
636 }
637
638 NotifyChange->CompletionFilter = CompletionFilter;
639
640 /* In case we have not to ignore buffer , keep its length */
641 if (!IgnoreBuffer)
642 {
643 NotifyChange->BufferLength = Stack->Parameters.NotifyDirectory.Length;
644 }
645
646 NotifyChange->OwningProcess = NotifyIrp->Tail.Overlay.Thread->ThreadsProcess;
647
648 /* Insert the notification into the notification list */
649 InsertTailList(NotifyList, &(NotifyChange->NotifyList));
650
651 NotifyChange->ReferenceCount = 1;
652
653 HandleIRP:
654 /* Associate the notification to the IRP */
655 NotifyIrp->IoStatus.Information = (ULONG_PTR)NotifyChange;
656 /* The IRP is pending */
657 IoMarkIrpPending(NotifyIrp);
658 /* Insert the IRP in the IRP list */
659 InsertTailList(&(NotifyChange->NotifyIrps), &(NotifyIrp->Tail.Overlay.ListEntry));
660 /* Increment reference count */
661 InterlockedIncrement((PLONG)&(NotifyChange->ReferenceCount));
662 /* Set cancel routine to FsRtl one */
663 FsRtlNotifySetCancelRoutine(NotifyIrp, NULL);
664 }
665 _SEH2_FINALLY
666 {
667 /* Release fast mutex */
668 FsRtlNotifyReleaseFastMutex(RealNotifySync);
669
670 /* If the subject security context was captured and there's no notify */
671 if (SubjectContext && (!NotifyChange || FullDirectoryName))
672 {
673 SeReleaseSubjectContext(SubjectContext);
674 ExFreePool(SubjectContext);
675 }
676 }
677 _SEH2_END;
678 }
679
680 /*++
681 * @name FsRtlNotifyFilterReportChange
682 * @unimplemented
683 *
684 * FILLME
685 *
686 * @param NotifySync
687 * FILLME
688 *
689 * @param NotifyList
690 * FILLME
691 *
692 * @param FullTargetName
693 * FILLME
694 *
695 * @param TargetNameOffset
696 * FILLME
697 *
698 * @param StreamName
699 * FILLME
700 *
701 * @param NormalizedParentName
702 * FILLME
703 *
704 * @param FilterMatch
705 * FILLME
706 *
707 * @param Action
708 * FILLME
709 *
710 * @param TargetContext
711 * FILLME
712 *
713 * @param FilterContext
714 * FILLME
715 *
716 * @return None
717 *
718 * @remarks None
719 *
720 *--*/
721 VOID
722 NTAPI
723 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
724 IN PLIST_ENTRY NotifyList,
725 IN PSTRING FullTargetName,
726 IN USHORT TargetNameOffset,
727 IN PSTRING StreamName OPTIONAL,
728 IN PSTRING NormalizedParentName OPTIONAL,
729 IN ULONG FilterMatch,
730 IN ULONG Action,
731 IN PVOID TargetContext,
732 IN PVOID FilterContext)
733 {
734 KeBugCheck(FILE_SYSTEM);
735 }
736
737 /*++
738 * @name FsRtlNotifyFullChangeDirectory
739 * @implemented
740 *
741 * Lets FSD know if changes occures in the specified directory.
742 *
743 * @param NotifySync
744 * Synchronization object pointer
745 *
746 * @param NotifyList
747 * Notify list pointer (to head)
748 *
749 * @param FsContext
750 * Used to identify the notify structure
751 *
752 * @param FullDirectoryName
753 * String (A or W) containing the full directory name
754 *
755 * @param WatchTree
756 * True to notify changes in subdirectories too
757 *
758 * @param IgnoreBuffer
759 * True to reenumerate directory. It's ignored it NotifyIrp is null
760 *
761 * @param CompletionFilter
762 * Used to define types of changes to notify
763 *
764 * @param NotifyIrp
765 * IRP pointer to complete notify operation. It can be null
766 *
767 * @param TraverseCallback
768 * Pointer to a callback function. It's called each time a change is
769 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
770 * is null
771 *
772 * @param SubjectContext
773 * Pointer to pass to SubjectContext member of TraverseCallback.
774 * It's freed after use. It's ignored it NotifyIrp is null
775 *
776 * @return None
777 *
778 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
779 *
780 *--*/
781 VOID
782 NTAPI
783 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
784 IN PLIST_ENTRY NotifyList,
785 IN PVOID FsContext,
786 IN PSTRING FullDirectoryName,
787 IN BOOLEAN WatchTree,
788 IN BOOLEAN IgnoreBuffer,
789 IN ULONG CompletionFilter,
790 IN PIRP NotifyIrp,
791 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
792 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
793 {
794 FsRtlNotifyFilterChangeDirectory(NotifySync,
795 NotifyList,
796 FsContext,
797 FullDirectoryName,
798 WatchTree,
799 IgnoreBuffer,
800 CompletionFilter,
801 NotifyIrp,
802 TraverseCallback,
803 SubjectContext,
804 NULL);
805 }
806
807 /*++
808 * @name FsRtlNotifyFullReportChange
809 * @implemented
810 *
811 * Complets the pending notify IRPs.
812 *
813 * @param NotifySync
814 * Synchronization object pointer
815 *
816 * @param NotifyList
817 * Notify list pointer (to head)
818 *
819 * @param FullTargetName
820 * String (A or W) containing the full directory name that changed
821 *
822 * @param TargetNameOffset
823 * Offset, in FullTargetName, of the final component that is in the changed directory
824 *
825 * @param StreamName
826 * String (A or W) containing a stream name
827 *
828 * @param NormalizedParentName
829 * String (A or W) containing the full directory name that changed with long names
830 *
831 * @param FilterMatch
832 * Flags that will be compared to the completion filter
833 *
834 * @param Action
835 * Action code to store in user's buffer
836 *
837 * @param TargetContext
838 * Pointer to a callback function. It's called each time a change is
839 * done in a subdirectory of the main directory.
840 *
841 * @return None
842 *
843 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
844 *
845 *--*/
846 VOID
847 NTAPI
848 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
849 IN PLIST_ENTRY NotifyList,
850 IN PSTRING FullTargetName,
851 IN USHORT TargetNameOffset,
852 IN PSTRING StreamName OPTIONAL,
853 IN PSTRING NormalizedParentName OPTIONAL,
854 IN ULONG FilterMatch,
855 IN ULONG Action,
856 IN PVOID TargetContext)
857 {
858 FsRtlNotifyFilterReportChange(NotifySync,
859 NotifyList,
860 FullTargetName,
861 TargetNameOffset,
862 StreamName,
863 NormalizedParentName,
864 FilterMatch,
865 Action,
866 TargetContext,
867 NULL);
868 }
869
870 /*++
871 * @name FsRtlNotifyInitializeSync
872 * @implemented
873 *
874 * Allocates the internal structure associated with notifications.
875 *
876 * @param NotifySync
877 * Opaque pointer. It will receive the address of the allocated internal structure.
878 *
879 * @return None
880 *
881 * @remarks This function raise an exception in case of a failure.
882 *
883 *--*/
884 VOID
885 NTAPI
886 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
887 {
888 PREAL_NOTIFY_SYNC RealNotifySync;
889
890 *NotifySync = NULL;
891
892 RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
893 sizeof(REAL_NOTIFY_SYNC), 'FSNS');
894 ExInitializeFastMutex(&(RealNotifySync->FastMutex));
895 RealNotifySync->OwningThread = 0;
896 RealNotifySync->OwnerCount = 0;
897
898 *NotifySync = RealNotifySync;
899 }
900
901 /*++
902 * @name FsRtlNotifyReportChange
903 * @implemented
904 *
905 * Complets the pending notify IRPs.
906 *
907 * @param NotifySync
908 * Synchronization object pointer
909 *
910 * @param NotifyList
911 * Notify list pointer (to head)
912 *
913 * @param FullTargetName
914 * String (A or W) containing the full directory name that changed
915 *
916 * @param FileNamePartLength
917 * Length of the final component that is in the changed directory
918 *
919 * @param FilterMatch
920 * Flags that will be compared to the completion filter
921 *
922 * @return None
923 *
924 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
925 *
926 *--*/
927 VOID
928 NTAPI
929 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
930 IN PLIST_ENTRY NotifyList,
931 IN PSTRING FullTargetName,
932 IN PUSHORT FileNamePartLength,
933 IN ULONG FilterMatch)
934 {
935 FsRtlNotifyFilterReportChange(NotifySync,
936 NotifyList,
937 FullTargetName,
938 FullTargetName->Length - *FileNamePartLength,
939 NULL,
940 NULL,
941 FilterMatch,
942 0,
943 NULL,
944 NULL);
945 }
946
947 /*++
948 * @name FsRtlNotifyUninitializeSync
949 * @implemented
950 *
951 * Uninitialize a NOTIFY_SYNC object
952 *
953 * @param NotifySync
954 * Address of a pointer to a PNOTIFY_SYNC object previously
955 * initialized by FsRtlNotifyInitializeSync()
956 *
957 * @return None
958 *
959 * @remarks None
960 *
961 *--*/
962 VOID
963 NTAPI
964 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
965 {
966 if (*NotifySync)
967 {
968 ExFreePoolWithTag(*NotifySync, 'FSNS');
969 *NotifySync = NULL;
970 }
971 }
972