Cleanup isn't necessary after calling the driver in NtQueryDirectoryFile.
[reactos.git] / reactos / ntoskrnl / fs / notify.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/fs/notify.c
6 * PURPOSE: No purpose listed.
7 *
8 * PROGRAMMERS: Gunnar Dalsnes
9 */
10
11 #include <ntoskrnl.h>
12
13 //#define NDEBUG
14 #include <internal/debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, FsRtlpInitNotifyImplementation)
18 #endif
19
20
21 PAGED_LOOKASIDE_LIST NotifyEntryLookaside;
22
23 typedef struct _NOTIFY_ENTRY
24 {
25 LIST_ENTRY ListEntry;
26 PSTRING FullDirectoryName;
27 BOOLEAN WatchTree;
28 BOOLEAN PendingChanges;
29 ULONG CompletionFilter;
30 LIST_ENTRY IrpQueue;
31 PVOID Fcb;
32 PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback;
33 PSECURITY_SUBJECT_CONTEXT SubjectContext;
34 PVOID FsContext;
35 BOOLEAN Unicode;
36 BOOLEAN BufferExhausted;
37 PVOID Buffer; /* Buffer == NULL equals IgnoreBuffer == TRUE */
38 ULONG BufferSize;
39 ULONG NextEntryOffset;
40 PFILE_NOTIFY_INFORMATION PrevEntry;
41 } NOTIFY_ENTRY, *PNOTIFY_ENTRY;
42
43
44 /**********************************************************************
45 * NAME PRIVATE
46 * FsRtlpInitNotifyImplementation
47 *
48 */
49 VOID
50 STDCALL INIT_FUNCTION
51 FsRtlpInitNotifyImplementation(VOID)
52 {
53 ExInitializePagedLookasideList( &NotifyEntryLookaside,
54 NULL,
55 NULL,
56 0,
57 sizeof(NOTIFY_ENTRY),
58 FSRTL_NOTIFY_TAG,
59 0
60 );
61
62
63 }
64
65
66
67 static
68 __inline
69 BOOLEAN
70 FsRtlpIsUnicodePath(
71 PSTRING Path
72 )
73 {
74 ASSERT(Path->Length);
75
76 if (Path->Length == 1) return FALSE;
77
78 if (*(WCHAR*)Path->Buffer == '\\') return TRUE;
79
80 return FALSE;
81 }
82
83
84 /**********************************************************************
85 * NAME PRIVATE
86 * FsRtlpNotifyCancelRoutine
87 *
88 */
89 static
90 VOID
91 STDCALL
92 FsRtlpNotifyCancelRoutine(
93 IN PDEVICE_OBJECT DeviceObject,
94 IN PIRP Irp
95 )
96 {
97 PFAST_MUTEX Lock;
98
99 //don't need this since we have our own sync. protecting irp cancellation
100 IoReleaseCancelSpinLock(Irp->CancelIrql);
101
102 Lock = (PFAST_MUTEX)Irp->Tail.Overlay.DriverContext[3];
103
104 ExAcquireFastMutex(Lock );
105
106 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
107
108 ExReleaseFastMutex(Lock);
109
110 Irp->IoStatus.Status = STATUS_CANCELLED;
111 Irp->IoStatus.Information = 0;
112
113 IoCompleteRequest(Irp, IO_NO_INCREMENT);
114
115 }
116
117
118
119 static
120 PNOTIFY_ENTRY
121 FASTCALL
122 FsRtlpFindNotifyEntry(
123 PLIST_ENTRY NotifyList,
124 PVOID FsContext
125 )
126 {
127 PNOTIFY_ENTRY NotifyEntry;
128
129 LIST_FOR_EACH(NotifyEntry, NotifyList, NOTIFY_ENTRY, ListEntry)
130 {
131 if (NotifyEntry->FsContext == FsContext)
132 {
133 return NotifyEntry;
134 }
135 }
136
137 return NULL;
138 }
139
140 /**********************************************************************
141 * NAME EXPORTED
142 * FsRtlNotifyChangeDirectory@28
143 *
144 * DESCRIPTION
145 *
146 * ARGUMENTS
147 *
148 * RETURN VALUE
149 *
150 * @implemented
151 */
152 VOID
153 STDCALL
154 FsRtlNotifyChangeDirectory (
155 IN PNOTIFY_SYNC NotifySync,
156 IN PVOID FsContext,
157 IN PSTRING FullDirectoryName,
158 IN PLIST_ENTRY NotifyList,
159 IN BOOLEAN WatchTree,
160 IN ULONG CompletionFilter,
161 IN PIRP NotifyIrp
162 )
163 {
164 FsRtlNotifyFullChangeDirectory (
165 NotifySync,
166 NotifyList,
167 FsContext,
168 FullDirectoryName,
169 WatchTree,
170 TRUE, /* IgnoreBuffer */
171 CompletionFilter,
172 NotifyIrp,
173 NULL,
174 NULL
175 );
176 }
177
178
179
180 /**********************************************************************
181 * NAME EXPORTED
182 * FsRtlNotifyCleanup@12
183 *
184 * DESCRIPTION
185 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
186 *
187 * ARGUMENTS
188 *
189 * RETURN VALUE
190 *
191 * @unimplemented
192 */
193 VOID
194 STDCALL
195 FsRtlNotifyCleanup (
196 IN PNOTIFY_SYNC NotifySync,
197 IN PLIST_ENTRY NotifyList,
198 IN PVOID FsContext
199 )
200 {
201 PNOTIFY_ENTRY NotifyEntry;
202 LIST_ENTRY CompletedListHead;
203 PLIST_ENTRY TmpEntry;
204 PIRP Irp;
205
206 InitializeListHead(&CompletedListHead);
207
208 ExAcquireFastMutex((PFAST_MUTEX)NotifySync);
209
210 NotifyEntry = FsRtlpFindNotifyEntry(NotifyList, FsContext);
211
212 if (NotifyEntry)
213 {
214 /* free buffered changes */
215 if (NotifyEntry->Buffer)
216 {
217 ExFreePool(NotifyEntry->Buffer);
218 }
219
220 /* cancel(?) pending irps */
221 while (!IsListEmpty(&NotifyEntry->IrpQueue))
222 {
223 TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue);
224 Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry);
225
226 /* irp cancelation bolilerplate */
227 if (!IoSetCancelRoutine(Irp, NULL))
228 {
229 //The cancel routine will be called. When we release the lock it will complete the irp.
230 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
231 continue;
232 }
233
234 Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; /* FIXME: correct status? */
235 Irp->IoStatus.Information = 0;
236
237 /* avoid holding lock while completing irp */
238 InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry);
239 }
240
241 /* Unlink and free the NotifyStruct */
242 RemoveEntryList(&NotifyEntry->ListEntry);
243 ExFreeToPagedLookasideList(&NotifyEntryLookaside, NotifyEntry);
244 }
245
246 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
247
248 /* complete defered irps */
249 while (!IsListEmpty(&CompletedListHead))
250 {
251 TmpEntry = RemoveHeadList(&CompletedListHead);
252 Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry);
253 IoCompleteRequest(Irp, IO_NO_INCREMENT);
254 }
255
256 }
257
258
259 /*
260 * @unimplemented
261 */
262 VOID
263 STDCALL
264 FsRtlNotifyFilterChangeDirectory (
265 IN PNOTIFY_SYNC NotifySync,
266 IN PLIST_ENTRY NotifyList,
267 IN PVOID FsContext,
268 IN PSTRING FullDirectoryName,
269 IN BOOLEAN WatchTree,
270 IN BOOLEAN IgnoreBuffer,
271 IN ULONG CompletionFilter,
272 IN PIRP NotifyIrp,
273 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
274 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
275 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL
276 )
277 {
278 UNIMPLEMENTED;
279 }
280
281 /*
282 * @unimplemented
283 */
284 VOID
285 STDCALL
286 FsRtlNotifyFilterReportChange (
287 IN PNOTIFY_SYNC NotifySync,
288 IN PLIST_ENTRY NotifyList,
289 IN PSTRING FullTargetName,
290 IN USHORT TargetNameOffset,
291 IN PSTRING StreamName OPTIONAL,
292 IN PSTRING NormalizedParentName OPTIONAL,
293 IN ULONG FilterMatch,
294 IN ULONG Action,
295 IN PVOID TargetContext,
296 IN PVOID FilterContext
297 )
298 {
299 UNIMPLEMENTED;
300 }
301
302
303
304 static
305 VOID
306 FASTCALL
307 FsRtlpWatchedDirectoryWasDeleted(
308 IN PNOTIFY_SYNC NotifySync,
309 IN PLIST_ENTRY NotifyList,
310 IN PVOID Fcb
311 )
312 {
313 LIST_ENTRY CompletedListHead;
314 PLIST_ENTRY TmpEntry;
315 PNOTIFY_ENTRY NotifyEntry, tmp;
316 PIRP Irp;
317
318 InitializeListHead(&CompletedListHead);
319
320 ExAcquireFastMutex((PFAST_MUTEX)NotifySync);
321
322 LIST_FOR_EACH_SAFE(NotifyEntry, tmp, NotifyList, NOTIFY_ENTRY, ListEntry )
323 {
324 if (NotifyEntry->Fcb == Fcb)
325 {
326 RemoveEntryList(&NotifyEntry->ListEntry);
327
328 /* free buffered changes */
329 if (NotifyEntry->Buffer)
330 {
331 ExFreePool(NotifyEntry->Buffer);
332 }
333
334 /* cleanup pending irps */
335 while (!IsListEmpty(&NotifyEntry->IrpQueue))
336 {
337 TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue);
338 Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry);
339
340 /* irp cancelation bolilerplate */
341 if (!IoSetCancelRoutine(Irp, NULL))
342 {
343 //The cancel routine will be called. When we release the lock it will complete the irp.
344 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
345 continue;
346 }
347
348 Irp->IoStatus.Status = STATUS_DELETE_PENDING;
349 Irp->IoStatus.Information = 0;
350
351 /* avoid holding lock while completing irp */
352 InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry);
353 }
354 }
355 }
356
357 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
358
359 /* complete defered irps */
360 while (!IsListEmpty(&CompletedListHead))
361 {
362 TmpEntry = RemoveHeadList(&CompletedListHead);
363 Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry);
364 IoCompleteRequest(Irp, IO_NO_INCREMENT);
365 }
366
367 }
368
369
370
371
372
373
374 /**********************************************************************
375 * NAME EXPORTED
376 * FsRtlNotifyFullChangeDirectory@40
377 *
378 * DESCRIPTION
379 *
380 * ARGUMENTS
381 *
382 * RETURN VALUE
383 *
384 * @unimplemented
385 */
386 VOID
387 STDCALL
388 FsRtlNotifyFullChangeDirectory (
389 IN PNOTIFY_SYNC NotifySync,
390 IN PLIST_ENTRY NotifyList,
391 IN PVOID FsContext,
392 IN PSTRING FullDirectoryName,
393 IN BOOLEAN WatchTree,
394 IN BOOLEAN IgnoreBuffer,
395 IN ULONG CompletionFilter,
396 IN PIRP Irp,
397 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
398 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
399 )
400 {
401 PIO_STACK_LOCATION IrpStack;
402 PNOTIFY_ENTRY NotifyEntry;
403 ULONG IrpBuffLen;
404
405 if (!Irp)
406 {
407 /* all other params are ignored if NotifyIrp == NULL */
408 FsRtlpWatchedDirectoryWasDeleted(NotifySync, NotifyList, FsContext);
409 return;
410 }
411
412 DPRINT("FullDirectoryName: %wZ\n", FullDirectoryName);
413
414 ExAcquireFastMutex((PFAST_MUTEX)NotifySync);
415
416 IrpStack = IoGetCurrentIrpStackLocation(Irp);
417 if (IrpStack->FileObject->Flags & FO_CLEANUP_COMPLETE)
418 {
419 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
420
421 Irp->IoStatus.Information = 0;
422 Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
423 IoCompleteRequest(Irp, IO_NO_INCREMENT);
424 return;
425 }
426
427 IrpBuffLen = IrpStack->Parameters.NotifyDirectory.Length;
428
429 NotifyEntry = FsRtlpFindNotifyEntry(NotifyList, FsContext);
430
431 if (!NotifyEntry)
432 {
433 /* No NotifyStruct for this FileObject existed */
434
435 /* The first request for this FileObject set the standards.
436 * For subsequent requests, these params will be ignored.
437 * Ref: Windows NT File System Internals page 516
438 */
439
440 NotifyEntry = ExAllocateFromPagedLookasideList(&NotifyEntryLookaside);
441
442 RtlZeroMemory(NotifyEntry, sizeof(NOTIFY_ENTRY));
443
444 NotifyEntry->FsContext = FsContext;
445 NotifyEntry->FullDirectoryName = FullDirectoryName;
446 NotifyEntry->WatchTree = WatchTree;
447 NotifyEntry->CompletionFilter = CompletionFilter;
448 NotifyEntry->TraverseCallback = TraverseCallback;
449 NotifyEntry->SubjectContext = SubjectContext;
450 NotifyEntry->Fcb = IrpStack->FileObject->FsContext;
451 NotifyEntry->Unicode = FsRtlpIsUnicodePath(FullDirectoryName);
452
453 /* Init. buffer */
454 if (IrpBuffLen && !IgnoreBuffer)
455 {
456 _SEH_TRY
457 {
458 NotifyEntry->Buffer = ExAllocatePoolWithQuotaTag(
459 PagedPool,
460 IrpBuffLen,
461 FSRTL_NOTIFY_TAG
462 );
463
464 NotifyEntry->BufferSize = IrpBuffLen;
465 }
466 _SEH_HANDLE
467 {
468 /* ExAllocatePoolWithQuotaTag raised exception but we dont care.
469 The impl. doesnt require a buffer, so well continue as usual.
470 */
471 }
472 _SEH_END;
473 }
474
475 InitializeListHead(&NotifyEntry->IrpQueue);
476
477 InsertTailList(NotifyList, &NotifyEntry->ListEntry);
478 }
479
480
481
482 if (!NotifyEntry->PendingChanges)
483 {
484 /* No changes are pending. Queue the irp */
485
486 /* Irp cancelation boilerplate */
487
488 /* save NotifySych for use in the cancel routine */
489 Irp->Tail.Overlay.DriverContext[3] = NotifySync;
490
491 IoSetCancelRoutine(Irp, FsRtlpNotifyCancelRoutine);
492 if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
493 {
494 //irp was canceled
495 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
496
497 Irp->IoStatus.Status = STATUS_CANCELLED;
498 Irp->IoStatus.Information = 0;
499
500 IoCompleteRequest(Irp, IO_NO_INCREMENT);
501 return;
502 }
503
504 IoMarkIrpPending(Irp);
505
506 //FIXME: any point in setting irp status/information before queueing?
507 Irp->IoStatus.Status = STATUS_PENDING;
508
509 InsertTailList(&NotifyEntry->IrpQueue, &Irp->Tail.Overlay.ListEntry);
510
511 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
512 return;
513 }
514
515
516 /* Pending changes exist */
517
518 if (NotifyEntry->Buffer == NULL ||
519 NotifyEntry->BufferExhausted ||
520 IrpBuffLen < NotifyEntry->NextEntryOffset)
521 {
522 /*
523 Can't return detailed changes to user cause:
524 -No buffer exist, OR
525 -Buffer were overflowed, OR
526 -Current irp buff was not large enough
527 */
528
529 Irp->IoStatus.Information = 0;
530 Irp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR;
531
532 }
533 else
534 {
535 PVOID Adr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
536
537 if (Adr)
538 {
539 memcpy(Adr, NotifyEntry->Buffer, NotifyEntry->NextEntryOffset);
540 Irp->IoStatus.Information = NotifyEntry->NextEntryOffset;
541 }
542 else
543 {
544 Irp->IoStatus.Information = 0;
545 }
546
547 Irp->IoStatus.Status = STATUS_SUCCESS;
548 }
549
550 /* reset buffer */
551 NotifyEntry->PrevEntry = NULL;
552 NotifyEntry->NextEntryOffset = 0;
553 NotifyEntry->BufferExhausted = FALSE;
554
555 NotifyEntry->PendingChanges = FALSE;
556
557 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
558
559 IoCompleteRequest(Irp, IO_NO_INCREMENT);
560
561 /* caller must return STATUS_PENDING */
562 }
563
564
565
566 static
567 PIRP
568 FASTCALL
569 FsRtlpGetNextIrp(PNOTIFY_ENTRY NotifyEntry)
570 {
571 PIRP Irp;
572 PLIST_ENTRY TmpEntry;
573
574 /* Loop to get a non-canceled irp */
575 while (!IsListEmpty(&NotifyEntry->IrpQueue))
576 {
577 /* If we have queued irp(s) we can't possibly have pending changes too */
578 ASSERT(!NotifyEntry->PendingChanges);
579
580 TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue);
581 Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry);
582
583 /* irp cancelation bolilerplate */
584 if (!IoSetCancelRoutine(Irp, NULL))
585 {
586 //The cancel routine will be called. When we release the lock it will complete the irp.
587 InitializeListHead(&Irp->Tail.Overlay.ListEntry);
588 continue;
589 }
590
591 /* Finally we got a non-canceled irp */
592 return Irp;
593 }
594
595 return NULL;
596 }
597
598
599 static
600 __inline
601 VOID
602 FsRtlpCopyName(
603 PFILE_NOTIFY_INFORMATION CurrentEntry,
604 BOOLEAN Unicode,
605 PSTRING RelativeName,
606 PSTRING StreamName
607 )
608 {
609 /* Buffer size is allready probed, so just copy the data */
610
611 if (Unicode)
612 {
613 memcpy(CurrentEntry->FileName, RelativeName->Buffer, RelativeName->Length);
614 if (StreamName)
615 {
616 CurrentEntry->FileName[RelativeName->Length/sizeof(WCHAR)] = ':';
617 memcpy(&CurrentEntry->FileName[(RelativeName->Length/sizeof(WCHAR))+1],
618 StreamName->Buffer,
619 StreamName->Length);
620 }
621 }
622 else
623 {
624 //FIXME: convert to unicode etc.
625 DPRINT1("FIXME: ansi strings in notify impl. not supported yet\n");
626 }
627 }
628
629
630 /**********************************************************************
631 * NAME EXPORTED
632 * FsRtlNotifyFullReportChange@36
633 *
634 * DESCRIPTION
635 *
636 * ARGUMENTS
637 *
638 * RETURN VALUE
639 *
640 * @unimplemented
641 */
642 VOID
643 STDCALL
644 FsRtlNotifyFullReportChange (
645 IN PNOTIFY_SYNC NotifySync,
646 IN PLIST_ENTRY NotifyList,
647 IN PSTRING FullTargetName, /* can include short names! */
648 IN USHORT TargetNameOffset, /* in bytes */
649 IN PSTRING StreamName OPTIONAL,
650 IN PSTRING NormalizedParentName OPTIONAL, /* same as FullTargetName, but with long names */
651 IN ULONG FilterMatch,
652 IN ULONG Action,
653 IN PVOID TargetContext
654 )
655 {
656 USHORT FullDirLen;
657 STRING RelativeName;
658 PNOTIFY_ENTRY NotifyEntry, tmp;
659 PLIST_ENTRY EnumEntry;
660 PIRP Irp;
661 LIST_ENTRY CompletedListHead;
662 USHORT NameLenU;
663 ULONG RecordLen;
664 PFILE_NOTIFY_INFORMATION CurrentEntry;
665
666 InitializeListHead(&CompletedListHead);
667
668 DPRINT("FullTargetName: %wZ\n", FullTargetName);
669
670 /*
671 I think FullTargetName can include/be a short file name! What the heck do i do with this?
672 Dont think this apply to FsRtlNotifyFullChangeDirectory's FullDirectoryName.
673 */
674
675
676
677
678 ExAcquireFastMutex((PFAST_MUTEX)NotifySync);
679
680 LIST_FOR_EACH_SAFE(NotifyEntry, tmp, NotifyList, NOTIFY_ENTRY, ListEntry )
681 {
682 ASSERT(NotifyEntry->Unicode == FsRtlpIsUnicodePath(FullTargetName));
683
684 /* rule out some easy cases */
685 /* FIXME: short vs. long names??? lower case/upper case/mixed case? */
686 if (!(FilterMatch & NotifyEntry->CompletionFilter)) continue;
687
688 FullDirLen = TargetNameOffset - (NotifyEntry->Unicode ? sizeof(WCHAR) : sizeof(char));
689 if (FullDirLen == 0)
690 {
691 /* special case for root dir */
692 FullDirLen = (NotifyEntry->Unicode ? sizeof(WCHAR) : sizeof(char));
693 }
694
695 if (FullDirLen < NotifyEntry->FullDirectoryName->Length) continue;
696
697 if (!NotifyEntry->WatchTree && FullDirLen != NotifyEntry->FullDirectoryName->Length) continue;
698
699 DPRINT("NotifyEntry->FullDirectoryName: %wZ\n", NotifyEntry->FullDirectoryName);
700
701 /* FIXME: short vs. long names??? lower case/upper case/mixed case? */
702 if (memcmp(NotifyEntry->FullDirectoryName->Buffer,
703 FullTargetName->Buffer,
704 NotifyEntry->FullDirectoryName->Length) != 0) continue;
705
706
707 if (NotifyEntry->WatchTree &&
708 NotifyEntry->TraverseCallback &&
709 FullDirLen != NotifyEntry->FullDirectoryName->Length)
710 {
711 /* change happend in a subdir. ask caller if we are allowed in here */
712 NTSTATUS Status = NotifyEntry->TraverseCallback(NotifyEntry->FsContext,
713 TargetContext,
714 NotifyEntry->SubjectContext);
715
716 if (!NT_SUCCESS(Status)) continue;
717
718 /*
719 FIXME: notify-dir impl. should release and free the SubjectContext
720 */
721 }
722
723 DPRINT("Found match\n");
724
725 /* Found a valid change */
726
727 RelativeName.Buffer = FullTargetName->Buffer + TargetNameOffset;
728 RelativeName.MaximumLength =
729 RelativeName.Length =
730 FullTargetName->Length - TargetNameOffset;
731
732 DPRINT("RelativeName: %wZ\n",&RelativeName);
733
734 /* calculate unicode bytes of relative-name + stream-name */
735 if (NotifyEntry->Unicode)
736 {
737 NameLenU = RelativeName.Length + (StreamName ? (StreamName->Length + sizeof(WCHAR)) : 0);
738 }
739 else
740 {
741 NameLenU = RelativeName.Length * sizeof(WCHAR) +
742 (StreamName ? ((StreamName->Length * sizeof(WCHAR)) + sizeof(WCHAR)) : 0);
743 }
744
745 RecordLen = FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName) + NameLenU;
746
747 if ((Irp = FsRtlpGetNextIrp(NotifyEntry)))
748 {
749 PIO_STACK_LOCATION IrpStack;
750 ULONG IrpBuffLen;
751
752 IrpStack = IoGetCurrentIrpStackLocation(Irp);
753 IrpBuffLen = IrpStack->Parameters.NotifyDirectory.Length;
754
755 DPRINT("Got pending irp\n");
756
757 ASSERT(!NotifyEntry->PendingChanges);
758
759 if (NotifyEntry->Buffer == NULL || /* aka. IgnoreBuffer */
760 RecordLen > IrpBuffLen)
761 {
762 /* ignore buffer / buffer not large enough */
763 Irp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR;
764 Irp->IoStatus.Information = 0;
765 }
766 else
767 {
768 CurrentEntry = (PFILE_NOTIFY_INFORMATION)
769 MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
770
771 if (CurrentEntry)
772 {
773 CurrentEntry->Action = Action;
774 CurrentEntry->FileNameLength = NameLenU;
775 CurrentEntry->NextEntryOffset = 0;
776
777 FsRtlpCopyName(
778 CurrentEntry,
779 NotifyEntry->Unicode,
780 &RelativeName,
781 StreamName
782 );
783
784 Irp->IoStatus.Information = RecordLen;
785 }
786 else
787 {
788 Irp->IoStatus.Information = 0;
789 }
790
791
792 Irp->IoStatus.Status = STATUS_SUCCESS;
793 }
794
795 /* avoid holding lock while completing irp */
796 InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry);
797 }
798 else
799 {
800 DPRINT("No irp\n");
801
802 NotifyEntry->PendingChanges = TRUE;
803
804 if (NotifyEntry->Buffer == NULL || NotifyEntry->BufferExhausted) continue;
805
806 if (RecordLen > NotifyEntry->BufferSize - NotifyEntry->NextEntryOffset)
807 {
808 /* overflow. drop these changes and stop buffering any other changes too */
809 NotifyEntry->BufferExhausted = TRUE;
810 continue;
811 }
812
813 /* The buffer has enough room for the changes.
814 * Copy data to buffer.
815 */
816
817 CurrentEntry = (PFILE_NOTIFY_INFORMATION)NotifyEntry->Buffer;
818
819 CurrentEntry->Action = Action;
820 CurrentEntry->FileNameLength = NameLenU;
821 CurrentEntry->NextEntryOffset = 0;
822
823 FsRtlpCopyName(CurrentEntry,
824 NotifyEntry->Unicode,
825 &RelativeName,
826 StreamName
827 );
828
829 if (NotifyEntry->PrevEntry)
830 {
831 NotifyEntry->PrevEntry->NextEntryOffset = (char*)CurrentEntry - (char*)NotifyEntry->PrevEntry;
832 }
833 NotifyEntry->PrevEntry = CurrentEntry;
834 NotifyEntry->NextEntryOffset += RecordLen;
835
836
837 // {
838 // UNICODE_STRING TmpStr;
839 // TmpStr.Buffer = BufferedChange->RelativeName;
840 // TmpStr.MaximumLength = TmpStr.Length = BufferedChange->NameLen;
841 // DPRINT("BufferedChange->RelativeName: %wZ\n", &TmpStr);
842 // }
843
844
845 }
846 }
847
848 ExReleaseFastMutex((PFAST_MUTEX)NotifySync);
849
850 /* complete defered irps */
851 while (!IsListEmpty(&CompletedListHead))
852 {
853 EnumEntry = RemoveHeadList(&CompletedListHead);
854 Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
855
856 IoCompleteRequest(Irp, IO_NO_INCREMENT);
857 }
858
859 }
860
861
862 /**********************************************************************
863 * NAME EXPORTED
864 * FsRtlNotifyInitializeSync@4
865 *
866 * DESCRIPTION
867 *
868 * ARGUMENTS
869 *
870 * RETURN VALUE
871 *
872 * @implemented
873 */
874 VOID
875 STDCALL
876 FsRtlNotifyInitializeSync (
877 IN PNOTIFY_SYNC *NotifySync
878 )
879 {
880 *NotifySync = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), FSRTL_NOTIFY_TAG );
881 ExInitializeFastMutex((PFAST_MUTEX)*NotifySync);
882 }
883
884
885 /**********************************************************************
886 * NAME EXPORTED
887 * FsRtlNotifyReportChange@20
888 *
889 * DESCRIPTION
890 *
891 * ARGUMENTS
892 *
893 * RETURN VALUE
894 *
895 * @implemented
896 */
897 VOID
898 STDCALL
899 FsRtlNotifyReportChange (
900 IN PNOTIFY_SYNC NotifySync,
901 IN PLIST_ENTRY NotifyList,
902 IN PSTRING FullTargetName,
903 IN PUSHORT FileNamePartLength,
904 IN ULONG FilterMatch
905 )
906 {
907 FsRtlNotifyFullReportChange (
908 NotifySync,
909 NotifyList,
910 FullTargetName,
911 (FullTargetName->Length - *FileNamePartLength),
912 NULL,
913 NULL,
914 FilterMatch,
915 0,
916 NULL
917 );
918 }
919
920
921 /**********************************************************************
922 * NAME EXPORTED
923 * FsRtlNotifyUninitializeSync@4
924 *
925 * DESCRIPTION
926 * Uninitialize a NOTIFY_SYNC object.
927 *
928 * ARGUMENTS
929 * NotifySync is the address of a pointer
930 * to a PNOTIFY_SYNC object previously initialized by
931 * FsRtlNotifyInitializeSync().
932 *
933 * RETURN VALUE
934 * None.
935 *
936 * @implemented
937 */
938 VOID
939 STDCALL
940 FsRtlNotifyUninitializeSync (
941 IN PNOTIFY_SYNC *NotifySync
942 )
943 {
944 ExFreePool (*NotifySync);
945 }
946
947 /**********************************************************************
948 * NAME EXPORTED
949 * FsRtlNotifyVolumeEvent@8
950 *
951 * DESCRIPTION
952 * NOTE: Only present in NT 5+.
953 *
954 * ARGUMENTS
955 *
956 * RETURN VALUE
957 *
958 * @unimplemented
959 */
960 NTSTATUS
961 STDCALL
962 FsRtlNotifyVolumeEvent (
963 IN PFILE_OBJECT FileObject,
964 IN ULONG EventCode
965 )
966 {
967 return STATUS_NOT_IMPLEMENTED;
968 }
969
970 /*
971 *
972 * @unimplemented
973 */
974 NTSTATUS
975 STDCALL
976 FsRtlRegisterFileSystemFilterCallbacks(IN PDRIVER_OBJECT FilterDriverObject,
977 IN PFS_FILTER_CALLBACKS Callbacks)
978 {
979 UNIMPLEMENTED;
980 return STATUS_NOT_IMPLEMENTED;
981 }
982 /* EOF */