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