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