a9bd1a24afcf94947618c1af6f43b82c76437ba2
[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
590 /*
591 * If NotifyChange == NULL then an
592 * exception was already raised.
593 */
594 ASSERT(NotifyChange != NULL);
595
596 RtlZeroMemory(NotifyChange, sizeof(NOTIFY_CHANGE));
597
598 /* Set basic information */
599 NotifyChange->NotifySync = NotifySync;
600 NotifyChange->FsContext = FsContext;
601 NotifyChange->StreamID = Stack->FileObject->FsContext;
602 NotifyChange->TraverseCallback = TraverseCallback;
603 NotifyChange->SubjectContext = SubjectContext;
604 NotifyChange->FullDirectoryName = FullDirectoryName;
605 NotifyChange->FilterCallback = FilterCallback;
606 InitializeListHead(&(NotifyChange->NotifyIrps));
607
608 /* Keep trace of WatchTree */
609 if (WatchTree)
610 {
611 NotifyChange->Flags |= WATCH_TREE;
612 }
613
614 /* If string is empty, faulty to ANSI */
615 if (FullDirectoryName->Length == 0)
616 {
617 NotifyChange->CharacterSize = sizeof(CHAR);
618 }
619 else
620 {
621 /* If it can't contain WCHAR, it's ANSI */
622 if (FullDirectoryName->Length < sizeof(WCHAR))
623 {
624 NotifyChange->CharacterSize = sizeof(CHAR);
625 }
626 /* First char is \, so in unicode, right part is 0
627 * whereas in ANSI it contains next char
628 */
629 else if (((CHAR*)FullDirectoryName->Buffer)[1] == 0)
630 {
631 NotifyChange->CharacterSize = sizeof(WCHAR);
632 }
633 else
634 {
635 NotifyChange->CharacterSize = sizeof(CHAR);
636 }
637
638 /* Now, check is user is willing to watch root */
639 if (FullDirectoryName->Length == NotifyChange->CharacterSize)
640 {
641 NotifyChange->Flags |= WATCH_ROOT;
642 }
643 }
644
645 NotifyChange->CompletionFilter = CompletionFilter;
646
647 /* In case we have not to ignore buffer , keep its length */
648 if (!IgnoreBuffer)
649 {
650 NotifyChange->BufferLength = Stack->Parameters.NotifyDirectory.Length;
651 }
652
653 NotifyChange->OwningProcess = NotifyIrp->Tail.Overlay.Thread->ThreadsProcess;
654
655 /* Insert the notification into the notification list */
656 InsertTailList(NotifyList, &(NotifyChange->NotifyList));
657
658 NotifyChange->ReferenceCount = 1;
659
660 HandleIRP:
661 /* Associate the notification to the IRP */
662 NotifyIrp->IoStatus.Information = (ULONG_PTR)NotifyChange;
663 /* The IRP is pending */
664 IoMarkIrpPending(NotifyIrp);
665 /* Insert the IRP in the IRP list */
666 InsertTailList(&(NotifyChange->NotifyIrps), &(NotifyIrp->Tail.Overlay.ListEntry));
667 /* Increment reference count */
668 InterlockedIncrement((PLONG)&(NotifyChange->ReferenceCount));
669 /* Set cancel routine to FsRtl one */
670 FsRtlNotifySetCancelRoutine(NotifyIrp, NULL);
671 }
672 _SEH2_FINALLY
673 {
674 /* Release fast mutex */
675 FsRtlNotifyReleaseFastMutex(RealNotifySync);
676
677 /* If the subject security context was captured and there's no notify */
678 if (SubjectContext && (!NotifyChange || FullDirectoryName))
679 {
680 SeReleaseSubjectContext(SubjectContext);
681 ExFreePool(SubjectContext);
682 }
683 }
684 _SEH2_END;
685 }
686
687 /*++
688 * @name FsRtlNotifyFilterReportChange
689 * @unimplemented
690 *
691 * FILLME
692 *
693 * @param NotifySync
694 * FILLME
695 *
696 * @param NotifyList
697 * FILLME
698 *
699 * @param FullTargetName
700 * FILLME
701 *
702 * @param TargetNameOffset
703 * FILLME
704 *
705 * @param StreamName
706 * FILLME
707 *
708 * @param NormalizedParentName
709 * FILLME
710 *
711 * @param FilterMatch
712 * FILLME
713 *
714 * @param Action
715 * FILLME
716 *
717 * @param TargetContext
718 * FILLME
719 *
720 * @param FilterContext
721 * FILLME
722 *
723 * @return None
724 *
725 * @remarks None
726 *
727 *--*/
728 VOID
729 NTAPI
730 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
731 IN PLIST_ENTRY NotifyList,
732 IN PSTRING FullTargetName,
733 IN USHORT TargetNameOffset,
734 IN PSTRING StreamName OPTIONAL,
735 IN PSTRING NormalizedParentName OPTIONAL,
736 IN ULONG FilterMatch,
737 IN ULONG Action,
738 IN PVOID TargetContext,
739 IN PVOID FilterContext)
740 {
741 KeBugCheck(FILE_SYSTEM);
742 }
743
744 /*++
745 * @name FsRtlNotifyFullChangeDirectory
746 * @implemented
747 *
748 * Lets FSD know if changes occures in the specified directory.
749 *
750 * @param NotifySync
751 * Synchronization object pointer
752 *
753 * @param NotifyList
754 * Notify list pointer (to head)
755 *
756 * @param FsContext
757 * Used to identify the notify structure
758 *
759 * @param FullDirectoryName
760 * String (A or W) containing the full directory name
761 *
762 * @param WatchTree
763 * True to notify changes in subdirectories too
764 *
765 * @param IgnoreBuffer
766 * True to reenumerate directory. It's ignored it NotifyIrp is null
767 *
768 * @param CompletionFilter
769 * Used to define types of changes to notify
770 *
771 * @param NotifyIrp
772 * IRP pointer to complete notify operation. It can be null
773 *
774 * @param TraverseCallback
775 * Pointer to a callback function. It's called each time a change is
776 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
777 * is null
778 *
779 * @param SubjectContext
780 * Pointer to pass to SubjectContext member of TraverseCallback.
781 * It's freed after use. It's ignored it NotifyIrp is null
782 *
783 * @return None
784 *
785 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
786 *
787 *--*/
788 VOID
789 NTAPI
790 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
791 IN PLIST_ENTRY NotifyList,
792 IN PVOID FsContext,
793 IN PSTRING FullDirectoryName,
794 IN BOOLEAN WatchTree,
795 IN BOOLEAN IgnoreBuffer,
796 IN ULONG CompletionFilter,
797 IN PIRP NotifyIrp,
798 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
799 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
800 {
801 FsRtlNotifyFilterChangeDirectory(NotifySync,
802 NotifyList,
803 FsContext,
804 FullDirectoryName,
805 WatchTree,
806 IgnoreBuffer,
807 CompletionFilter,
808 NotifyIrp,
809 TraverseCallback,
810 SubjectContext,
811 NULL);
812 }
813
814 /*++
815 * @name FsRtlNotifyFullReportChange
816 * @implemented
817 *
818 * Complets the pending notify IRPs.
819 *
820 * @param NotifySync
821 * Synchronization object pointer
822 *
823 * @param NotifyList
824 * Notify list pointer (to head)
825 *
826 * @param FullTargetName
827 * String (A or W) containing the full directory name that changed
828 *
829 * @param TargetNameOffset
830 * Offset, in FullTargetName, of the final component that is in the changed directory
831 *
832 * @param StreamName
833 * String (A or W) containing a stream name
834 *
835 * @param NormalizedParentName
836 * String (A or W) containing the full directory name that changed with long names
837 *
838 * @param FilterMatch
839 * Flags that will be compared to the completion filter
840 *
841 * @param Action
842 * Action code to store in user's buffer
843 *
844 * @param TargetContext
845 * Pointer to a callback function. It's called each time a change is
846 * done in a subdirectory of the main directory.
847 *
848 * @return None
849 *
850 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
851 *
852 *--*/
853 VOID
854 NTAPI
855 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
856 IN PLIST_ENTRY NotifyList,
857 IN PSTRING FullTargetName,
858 IN USHORT TargetNameOffset,
859 IN PSTRING StreamName OPTIONAL,
860 IN PSTRING NormalizedParentName OPTIONAL,
861 IN ULONG FilterMatch,
862 IN ULONG Action,
863 IN PVOID TargetContext)
864 {
865 FsRtlNotifyFilterReportChange(NotifySync,
866 NotifyList,
867 FullTargetName,
868 TargetNameOffset,
869 StreamName,
870 NormalizedParentName,
871 FilterMatch,
872 Action,
873 TargetContext,
874 NULL);
875 }
876
877 /*++
878 * @name FsRtlNotifyInitializeSync
879 * @implemented
880 *
881 * Allocates the internal structure associated with notifications.
882 *
883 * @param NotifySync
884 * Opaque pointer. It will receive the address of the allocated internal structure.
885 *
886 * @return None
887 *
888 * @remarks This function raise an exception in case of a failure.
889 *
890 *--*/
891 VOID
892 NTAPI
893 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
894 {
895 PREAL_NOTIFY_SYNC RealNotifySync;
896
897 *NotifySync = NULL;
898
899 RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
900 sizeof(REAL_NOTIFY_SYNC), 'FSNS');
901 ExInitializeFastMutex(&(RealNotifySync->FastMutex));
902 RealNotifySync->OwningThread = 0;
903 RealNotifySync->OwnerCount = 0;
904
905 *NotifySync = RealNotifySync;
906 }
907
908 /*++
909 * @name FsRtlNotifyReportChange
910 * @implemented
911 *
912 * Complets the pending notify IRPs.
913 *
914 * @param NotifySync
915 * Synchronization object pointer
916 *
917 * @param NotifyList
918 * Notify list pointer (to head)
919 *
920 * @param FullTargetName
921 * String (A or W) containing the full directory name that changed
922 *
923 * @param FileNamePartLength
924 * Length of the final component that is in the changed directory
925 *
926 * @param FilterMatch
927 * Flags that will be compared to the completion filter
928 *
929 * @return None
930 *
931 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
932 *
933 *--*/
934 VOID
935 NTAPI
936 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
937 IN PLIST_ENTRY NotifyList,
938 IN PSTRING FullTargetName,
939 IN PUSHORT FileNamePartLength,
940 IN ULONG FilterMatch)
941 {
942 FsRtlNotifyFilterReportChange(NotifySync,
943 NotifyList,
944 FullTargetName,
945 FullTargetName->Length - *FileNamePartLength,
946 NULL,
947 NULL,
948 FilterMatch,
949 0,
950 NULL,
951 NULL);
952 }
953
954 /*++
955 * @name FsRtlNotifyUninitializeSync
956 * @implemented
957 *
958 * Uninitialize a NOTIFY_SYNC object
959 *
960 * @param NotifySync
961 * Address of a pointer to a PNOTIFY_SYNC object previously
962 * initialized by FsRtlNotifyInitializeSync()
963 *
964 * @return None
965 *
966 * @remarks None
967 *
968 *--*/
969 VOID
970 NTAPI
971 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
972 {
973 if (*NotifySync)
974 {
975 ExFreePoolWithTag(*NotifySync, 'FSNS');
976 *NotifySync = NULL;
977 }
978 }
979