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