1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/notify.c
5 * PURPOSE: Change Notifications and Sync for File System Drivers
6 * PROGRAMMERS: Pierre Schweitzer
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* INLINED FUNCTIONS *********************************************************/
16
17 /*
18 * @implemented
19 */
20 FORCEINLINE
21 VOID
FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)22 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
23 {
24 ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread();
25
26 /* Only acquire fast mutex if it's not already acquired by the current thread */
27 if (RealNotifySync->OwningThread != CurrentThread)
28 {
29 ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex));
30 RealNotifySync->OwningThread = CurrentThread;
31 }
32 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
33 RealNotifySync->OwnerCount++;
34 }
35
36 /*
37 * @implemented
38 */
39 FORCEINLINE
40 VOID
FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)41 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
42 {
43 RealNotifySync->OwnerCount--;
44 /* Release the fast mutex only if no other instance needs it */
45 if (!RealNotifySync->OwnerCount)
46 {
47 ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex));
48 RealNotifySync->OwningThread = (ULONG_PTR)0;
49 }
50 }
51
52 #define FsRtlNotifyGetLastPartOffset(FullLen, TargLen, Type, Chr) \
53 for (FullPosition = 0; FullPosition < FullLen; ++FullPosition) \
54 if (((Type)NotifyChange->FullDirectoryName->Buffer)[FullPosition] == Chr) \
55 ++FullNumberOfParts; \
56 for (LastPartOffset = 0; LastPartOffset < TargLen; ++LastPartOffset) { \
57 if ( ((Type)TargetDirectory.Buffer)[LastPartOffset] == Chr) { \
58 ++TargetNumberOfParts; \
59 if (TargetNumberOfParts == FullNumberOfParts) \
60 break; \
61 } \
62 }
63
64 /* PRIVATE FUNCTIONS *********************************************************/
65
66 VOID
67 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
68 IN NTSTATUS Status);
69
70 BOOLEAN
71 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
72 IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
73
74 /*
75 * @implemented
76 */
77 VOID
78 NTAPI
FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)79 FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
80 IN PIRP Irp)
81 {
82 PVOID Buffer;
83 PIRP NotifyIrp;
84 ULONG BufferLength;
85 PIO_STACK_LOCATION Stack;
86 PNOTIFY_CHANGE NotifyChange;
87 PREAL_NOTIFY_SYNC RealNotifySync;
88 BOOLEAN PoolQuotaCharged;
89 PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext = NULL;
90
91 /* Get the NOTIFY_CHANGE struct and reset it */
92 NotifyChange = (PNOTIFY_CHANGE)Irp->IoStatus.Information;
93 Irp->IoStatus.Information = 0;
94 /* Reset the cancel routine */
95 IoSetCancelRoutine(Irp, NULL);
96 /* And release lock */
97 IoReleaseCancelSpinLock(Irp->CancelIrql);
98 /* Get REAL_NOTIFY_SYNC struct */
99 RealNotifySync = NotifyChange->NotifySync;
100
101 FsRtlNotifyAcquireFastMutex(RealNotifySync);
102
103 _SEH2_TRY
104 {
105 /* Remove the IRP from the notifications list and mark it pending */
106 RemoveEntryList(&(Irp->Tail.Overlay.ListEntry));
107 IoMarkIrpPending(Irp);
108
109 /* Now, the tricky part - let's find a buffer big enough to hold the return data */
110 if (NotifyChange->Buffer && NotifyChange->AllocatedBuffer == NULL &&
111 ((Irp->MdlAddress && MmGetSystemAddressForMdl(Irp->MdlAddress) == NotifyChange->Buffer) ||
112 NotifyChange->Buffer == Irp->AssociatedIrp.SystemBuffer))
113 {
114 /* Assume we didn't find any */
115 Buffer = NULL;
116 BufferLength = 0;
117
118 /* If we don't have IRPs, check if current buffer is big enough */
119 if (IsListEmpty(&NotifyChange->NotifyIrps))
120 {
121 if (NotifyChange->BufferLength >= NotifyChange->DataLength)
122 {
123 BufferLength = NotifyChange->BufferLength;
124 }
125 }
126 else
127 {
128 /* Otherwise, try to look at next IRP available */
129 NotifyIrp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry);
130 Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
131
132 /* If its buffer is big enough, get it */
133 if (Stack->Parameters.NotifyDirectory.Length >= NotifyChange->BufferLength)
134 {
135 /* Is it MDL? */
136 if (NotifyIrp->AssociatedIrp.SystemBuffer == NULL)
137 {
138 if (NotifyIrp->MdlAddress != NULL)
139 {
140 Buffer = MmGetSystemAddressForMdl(NotifyIrp->MdlAddress);
141 }
142 }
143 else
144 {
145 Buffer = NotifyIrp->AssociatedIrp.MasterIrp;
146 }
147
148 /* Backup our accepted buffer length */
149 BufferLength = Stack->Parameters.NotifyDirectory.Length;
150 if (BufferLength > NotifyChange->BufferLength)
151 {
152 BufferLength = NotifyChange->BufferLength;
153 }
154 }
155 }
156
157 /* At that point, we *may* have a buffer */
158
159 /* If it has null length, then note that we won't use it */
160 if (BufferLength == 0)
161 {
162 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
163 }
164 else
165 {
166 /* If we have a buffer length, but no buffer then allocate one */
167 if (Buffer == NULL)
168 {
169 /* Assume we haven't charged quotas */
170 PoolQuotaCharged = FALSE;
171
172 _SEH2_TRY
173 {
174 /* Charge quotas */
175 PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, BufferLength);
176 PoolQuotaCharged = TRUE;
177
178 /* Allocate buffer */
179 Buffer = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, BufferLength, TAG_FS_NOTIFICATIONS);
180 NotifyChange->AllocatedBuffer = Buffer;
181
182 /* Copy data in that buffer */
183 RtlCopyMemory(Buffer, NotifyChange->Buffer, NotifyChange->DataLength);
184 NotifyChange->ThisBufferLength = BufferLength;
185 NotifyChange->Buffer = Buffer;
186 }
187 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
188 {
189 /* Something went wrong, have we charged quotas? */
190 if (PoolQuotaCharged)
191 {
192 /* We did, return quotas */
193 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, BufferLength);
194 }
195
196 /* And notify immediately */
197 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
198 }
199 _SEH2_END;
200 }
201 }
202
203 /* If we have to notify immediately, ensure that any buffer is 0-ed out */
204 if (NotifyChange->Flags & NOTIFY_IMMEDIATELY)
205 {
206 NotifyChange->Buffer = 0;
207 NotifyChange->AllocatedBuffer = 0;
208 NotifyChange->LastEntry = 0;
209 NotifyChange->DataLength = 0;
210 NotifyChange->ThisBufferLength = 0;
211 }
212 }
213
214 /* It's now time to complete - data are ready */
215
216 /* Set appropriate status and complete */
217 Irp->IoStatus.Status = STATUS_CANCELLED;
218 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
219
220 /* If that notification isn't referenced any longer, drop it */
221 if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
222 {
223 /* If it had an allocated buffer, delete */
224 if (NotifyChange->AllocatedBuffer)
225 {
226 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
227 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, TAG_FS_NOTIFICATIONS);
228 }
229
230 /* In case of full name, remember subject context for later deletion */
231 if (NotifyChange->FullDirectoryName)
232 {
233 SubjectContext = NotifyChange->SubjectContext;
234 }
235
236 /* We mustn't have ANY change left anymore */
237 ASSERT(NotifyChange->NotifyList.Flink == NULL);
238 ExFreePoolWithTag(NotifyChange, 0);
239 }
240 }
241 _SEH2_FINALLY
242 {
243 FsRtlNotifyReleaseFastMutex(RealNotifySync);
244
245 /* If the subject security context was captured, release and free it */
246 if (SubjectContext)
247 {
248 SeReleaseSubjectContext(SubjectContext);
249 ExFreePool(SubjectContext);
250 }
251 }
252 _SEH2_END;
253 }
254
255 /*
256 * @implemented
257 */
258 VOID
FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,IN PVOID FsContext)259 FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,
260 IN PVOID FsContext)
261 {
262 PLIST_ENTRY NextEntry;
263 PNOTIFY_CHANGE NotifyChange;
264
265 if (!IsListEmpty(NotifyList))
266 {
267 /* Browse the notifications list to find the matching entry */
268 for (NextEntry = NotifyList->Flink;
269 NextEntry != NotifyList;
270 NextEntry = NextEntry->Flink)
271 {
272 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
273 /* If the current record matches with the given context, it's the good one */
274 if (NotifyChange->FsContext == FsContext && !IsListEmpty(&(NotifyChange->NotifyIrps)))
275 {
276 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_DELETE_PENDING);
277 }
278 }
279 }
280 }
281
282 /*
283 *@implemented
284 */
285 PNOTIFY_CHANGE
FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,IN PVOID FsContext)286 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
287 IN PVOID FsContext)
288 {
289 PLIST_ENTRY NextEntry;
290 PNOTIFY_CHANGE NotifyChange;
291
292 if (!IsListEmpty(NotifyList))
293 {
294 /* Browse the notifications list to find the matching entry */
295 for (NextEntry = NotifyList->Flink;
296 NextEntry != NotifyList;
297 NextEntry = NextEntry->Flink)
298 {
299 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
300 /* If the current record matches with the given context, it's the good one */
301 if (NotifyChange->FsContext == FsContext)
302 {
303 return NotifyChange;
304 }
305 }
306 }
307 return NULL;
308 }
309
310 /*
311 * @implemented
312 */
313 VOID
FsRtlNotifyCompleteIrp(IN PIRP Irp,IN PNOTIFY_CHANGE NotifyChange,IN ULONG DataLength,IN NTSTATUS Status,IN BOOLEAN SkipCompletion)314 FsRtlNotifyCompleteIrp(IN PIRP Irp,
315 IN PNOTIFY_CHANGE NotifyChange,
316 IN ULONG DataLength,
317 IN NTSTATUS Status,
318 IN BOOLEAN SkipCompletion)
319 {
320 PVOID Buffer;
321 PIO_STACK_LOCATION Stack;
322
323 PAGED_CODE();
324
325 /* Check if we need to complete */
326 if (!FsRtlNotifySetCancelRoutine(Irp, NotifyChange) && SkipCompletion)
327 {
328 return;
329 }
330
331 /* No succes => no data to return just complete */
332 if (Status != STATUS_SUCCESS)
333 {
334 goto Completion;
335 }
336
337 /* Ensure there's something to return */
338 Stack = IoGetCurrentIrpStackLocation(Irp);
339 if (!DataLength || Stack->Parameters.NotifyDirectory.Length < DataLength)
340 {
341 Status = STATUS_NOTIFY_ENUM_DIR;
342 goto Completion;
343 }
344
345 /* Ensture there's a buffer where to find data */
346 if (!NotifyChange->AllocatedBuffer)
347 {
348 Irp->IoStatus.Information = DataLength;
349 NotifyChange->Buffer = NULL;
350 goto Completion;
351 }
352
353 /* Now, browse all the way to return data
354 * and find the one that will work. We will
355 * return data whatever happens
356 */
357 if (Irp->AssociatedIrp.SystemBuffer)
358 {
359 Buffer = Irp->AssociatedIrp.SystemBuffer;
360 goto CopyAndComplete;
361 }
362
363 if (Irp->MdlAddress)
364 {
365 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
366 goto CopyAndComplete;
367 }
368
369 if (!(Stack->Control & SL_PENDING_RETURNED))
370 {
371 Buffer = Irp->UserBuffer;
372 goto CopyAndComplete;
373 }
374
375 Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_SYNCHRONOUS_PAGING_IO);
376 Irp->AssociatedIrp.SystemBuffer = NotifyChange->AllocatedBuffer;
377 /* Nothing to copy */
378 goto ReleaseAndComplete;
379
380 CopyAndComplete:
381 _SEH2_TRY
382 {
383 RtlCopyMemory(Buffer, NotifyChange->AllocatedBuffer, DataLength);
384 }
385 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
386 {
387 /* Do nothing */
388 }
389 _SEH2_END;
390
391 ReleaseAndComplete:
392 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
393
394 /* Release buffer UNLESS it's used */
395 if (NotifyChange->AllocatedBuffer != Irp->AssociatedIrp.SystemBuffer &&
396 NotifyChange->AllocatedBuffer)
397 {
398 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 0);
399 }
400
401 /* Prepare for return */
402 NotifyChange->AllocatedBuffer = 0;
403 NotifyChange->ThisBufferLength = 0;
404 Irp->IoStatus.Information = DataLength;
405 NotifyChange->Buffer = NULL;
406
407 /* Finally complete */
408 Completion:
409 IoMarkIrpPending(Irp);
410 Irp->IoStatus.Status = Status;
411 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
412 }
413
414 /*
415 * @implemented
416 */
417 VOID
FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,IN NTSTATUS Status)418 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
419 IN NTSTATUS Status)
420 {
421 PIRP Irp;
422 ULONG DataLength;
423 PLIST_ENTRY NextEntry;
424
425 DataLength = NotifyChange->DataLength;
426
427 NotifyChange->Flags &= (NOTIFY_IMMEDIATELY | WATCH_TREE);
428 NotifyChange->DataLength = 0;
429 NotifyChange->LastEntry = 0;
430
431 while (!IsListEmpty(&(NotifyChange->NotifyIrps)))
432 {
433 /* We take the first entry */
434 NextEntry = RemoveHeadList(&(NotifyChange->NotifyIrps));
435 Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
436 /* We complete it */
437 FsRtlNotifyCompleteIrp(Irp, NotifyChange, DataLength, Status, TRUE);
438 /* If we're notifying success, just notify first one */
439 if (Status == STATUS_SUCCESS)
440 break;
441 }
442 }
443
444 /*
445 * @implemented
446 */
447 BOOLEAN
FsRtlNotifySetCancelRoutine(IN PIRP Irp,IN PNOTIFY_CHANGE NotifyChange OPTIONAL)448 FsRtlNotifySetCancelRoutine(IN PIRP Irp,
449 IN PNOTIFY_CHANGE NotifyChange OPTIONAL)
450 {
451 PDRIVER_CANCEL CancelRoutine;
452
453 /* Acquire cancel lock */
454 IoAcquireCancelSpinLock(&Irp->CancelIrql);
455
456 /* If NotifyChange was given */
457 if (NotifyChange)
458 {
459 /* First get cancel routine */
460 CancelRoutine = IoSetCancelRoutine(Irp, NULL);
461 Irp->IoStatus.Information = 0;
462 /* Release cancel lock */
463 IoReleaseCancelSpinLock(Irp->CancelIrql);
464 /* If there was a cancel routine */
465 if (CancelRoutine)
466 {
467 /* Decrease reference count */
468 InterlockedDecrement((PLONG)&NotifyChange->ReferenceCount);
469 /* Notify that we removed cancel routine */
470 return TRUE;
471 }
472 }
473 else
474 {
475 /* If IRP is cancel, call FsRtl cancel routine */
476 if (Irp->Cancel)
477 {
478 FsRtlCancelNotify(NULL, Irp);
479 }
480 else
481 {
482 /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
483 IoSetCancelRoutine(Irp, FsRtlCancelNotify);
484 /* Release lock */
485 IoReleaseCancelSpinLock(Irp->CancelIrql);
486 }
487 }
488
489 /* Return that we didn't removed cancel routine */
490 return FALSE;
491 }
492
493 /*
494 * @implemented
495 */
496 BOOLEAN
FsRtlNotifyUpdateBuffer(OUT PFILE_NOTIFY_INFORMATION OutputBuffer,IN ULONG Action,IN PSTRING ParentName,IN PSTRING TargetName,IN PSTRING StreamName,IN BOOLEAN IsUnicode,IN ULONG DataLength)497 FsRtlNotifyUpdateBuffer(OUT PFILE_NOTIFY_INFORMATION OutputBuffer,
498 IN ULONG Action,
499 IN PSTRING ParentName,
500 IN PSTRING TargetName,
501 IN PSTRING StreamName,
502 IN BOOLEAN IsUnicode,
503 IN ULONG DataLength)
504 {
505 /* Unless there's an issue with buffers, there's no reason to fail */
506 BOOLEAN Succeed = TRUE;
507 ULONG AlreadyWritten = 0, ResultSize;
508
509 PAGED_CODE();
510
511 /* Update user buffer with the change that occured
512 * First copy parent name if any
513 * Then copy target name, there's always one
514 * And finally, copy stream name if any
515 * If these names aren't unicode, then convert first
516 */
517 _SEH2_TRY
518 {
519 OutputBuffer->NextEntryOffset = 0;
520 OutputBuffer->Action = Action;
521 OutputBuffer->FileNameLength = DataLength - FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName);
522 if (IsUnicode)
523 {
524 if (ParentName->Length)
525 {
526 RtlCopyMemory(OutputBuffer->FileName, ParentName->Buffer, ParentName->Length);
527 OutputBuffer->FileName[ParentName->Length / sizeof(WCHAR)] = L'\\';
528 AlreadyWritten = ParentName->Length + sizeof(WCHAR);
529 }
530 RtlCopyMemory((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten),
531 TargetName->Buffer, TargetName->Length);
532 if (StreamName)
533 {
534 AlreadyWritten += TargetName->Length;
535 OutputBuffer->FileName[AlreadyWritten / sizeof(WCHAR)] = L':';
536 RtlCopyMemory((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten + sizeof(WCHAR)),
537 StreamName->Buffer, StreamName->Length);
538 }
539 }
540 else
541 {
542 if (!ParentName->Length)
543 {
544 ASSERT(StreamName);
545 RtlCopyMemory(OutputBuffer->FileName, StreamName->Buffer, StreamName->Length);
546 }
547 else
548 {
549 RtlOemToUnicodeN(OutputBuffer->FileName, OutputBuffer->FileNameLength,
550 &ResultSize, ParentName->Buffer,
551 ParentName->Length);
552 OutputBuffer->FileName[ResultSize / sizeof(WCHAR)] = L'\\';
553 AlreadyWritten = ResultSize + sizeof(WCHAR);
554
555 RtlOemToUnicodeN((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten),
556 OutputBuffer->FileNameLength, &ResultSize,
557 TargetName->Buffer, TargetName->Length);
558
559 if (StreamName)
560 {
561 AlreadyWritten += ResultSize;
562 OutputBuffer->FileName[AlreadyWritten / sizeof(WCHAR)] = L':';
563 RtlOemToUnicodeN((PVOID)((ULONG_PTR)OutputBuffer->FileName + AlreadyWritten + sizeof(WCHAR)),
564 OutputBuffer->FileNameLength, &ResultSize,
565 StreamName->Buffer, StreamName->Length);
566 }
567 }
568 }
569 }
570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
571 {
572 Succeed = FALSE;
573 }
574 _SEH2_END;
575
576 return Succeed;
577 }
578
579 /* PUBLIC FUNCTIONS **********************************************************/
580
581 /*++
582 * @name FsRtlNotifyChangeDirectory
583 * @implemented
584 *
585 * Lets FSD know if changes occures in the specified directory.
586 * Directory will be reenumerated.
587 *
588 * @param NotifySync
589 * Synchronization object pointer
590 *
591 * @param FsContext
592 * Used to identify the notify structure
593 *
594 * @param FullDirectoryName
595 * String (A or W) containing the full directory name
596 *
597 * @param NotifyList
598 * Notify list pointer (to head)
599 *
600 * @param WatchTree
601 * True to notify changes in subdirectories too
602 *
603 * @param CompletionFilter
604 * Used to define types of changes to notify
605 *
606 * @param NotifyIrp
607 * IRP pointer to complete notify operation. It can be null
608 *
609 * @return None
610 *
611 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
612 *
613 *--*/
614 VOID
615 NTAPI
FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,IN PVOID FsContext,IN PSTRING FullDirectoryName,IN PLIST_ENTRY NotifyList,IN BOOLEAN WatchTree,IN ULONG CompletionFilter,IN PIRP NotifyIrp)616 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
617 IN PVOID FsContext,
618 IN PSTRING FullDirectoryName,
619 IN PLIST_ENTRY NotifyList,
620 IN BOOLEAN WatchTree,
621 IN ULONG CompletionFilter,
622 IN PIRP NotifyIrp)
623 {
624 FsRtlNotifyFilterChangeDirectory(NotifySync,
625 NotifyList,
626 FsContext,
627 FullDirectoryName,
628 WatchTree,
629 TRUE,
630 CompletionFilter,
631 NotifyIrp,
632 NULL,
633 NULL,
634 NULL);
635 }
636
637 /*++
638 * @name FsRtlNotifyCleanup
639 * @implemented
640 *
641 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
642 *
643 * @param NotifySync
644 * Synchronization object pointer
645 *
646 * @param NotifyList
647 * Notify list pointer (to head)
648 *
649 * @param FsContext
650 * Used to identify the notify structure
651 *
652 * @return None
653 *
654 * @remarks None
655 *
656 *--*/
657 VOID
658 NTAPI
FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PVOID FsContext)659 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
660 IN PLIST_ENTRY NotifyList,
661 IN PVOID FsContext)
662 {
663 PNOTIFY_CHANGE NotifyChange;
664 PREAL_NOTIFY_SYNC RealNotifySync;
665 PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext = NULL;
666
667 /* Get real structure hidden behind the opaque pointer */
668 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
669
670 /* Acquire the fast mutex */
671 FsRtlNotifyAcquireFastMutex(RealNotifySync);
672
673 _SEH2_TRY
674 {
675 /* Find if there's a matching notification with the FsContext */
676 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
677 if (NotifyChange)
678 {
679 /* Mark it as to know that cleanup is in process */
680 NotifyChange->Flags |= CLEANUP_IN_PROCESS;
681
682 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
683 if (!IsListEmpty(&NotifyChange->NotifyIrps))
684 {
685 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
686 }
687
688 /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
689 if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
690 {
691 /* Remove it from the notifications list */
692 RemoveEntryList(&NotifyChange->NotifyList);
693
694 /* In case there was an allocated buffer, free it */
695 if (NotifyChange->AllocatedBuffer)
696 {
697 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
698 ExFreePool(NotifyChange->AllocatedBuffer);
699 }
700
701 /* In case there the string was set, get the captured subject security context */
702 if (NotifyChange->FullDirectoryName)
703 {
704 SubjectContext = NotifyChange->SubjectContext;
705 }
706
707 /* Finally, free the notification, as it's not needed anymore */
708 ExFreePoolWithTag(NotifyChange, 'NrSF');
709 }
710 }
711 }
712 _SEH2_FINALLY
713 {
714 /* Release fast mutex */
715 FsRtlNotifyReleaseFastMutex(RealNotifySync);
716
717 /* If the subject security context was captured, release and free it */
718 if (SubjectContext)
719 {
720 SeReleaseSubjectContext(SubjectContext);
721 ExFreePool(SubjectContext);
722 }
723 }
724 _SEH2_END;
725 }
726
727 /*++
728 * @name FsRtlNotifyFilterChangeDirectory
729 * @implemented
730 *
731 * FILLME
732 *
733 * @param NotifySync
734 * FILLME
735 *
736 * @param NotifyList
737 * FILLME
738 *
739 * @param FsContext
740 * FILLME
741 *
742 * @param FullDirectoryName
743 * FILLME
744 *
745 * @param WatchTree
746 * FILLME
747 *
748 * @param IgnoreBuffer
749 * FILLME
750 *
751 * @param CompletionFilter
752 * FILLME
753 *
754 * @param NotifyIrp
755 * FILLME
756 *
757 * @param TraverseCallback
758 * FILLME
759 *
760 * @param SubjectContext
761 * FILLME
762 *
763 * @param FilterCallback
764 * FILLME
765 *
766 * @return None
767 *
768 * @remarks None
769 *
770 *--*/
771 VOID
772 NTAPI
FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PVOID FsContext,IN PSTRING FullDirectoryName,IN BOOLEAN WatchTree,IN BOOLEAN IgnoreBuffer,IN ULONG CompletionFilter,IN PIRP NotifyIrp,IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)773 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
774 IN PLIST_ENTRY NotifyList,
775 IN PVOID FsContext,
776 IN PSTRING FullDirectoryName,
777 IN BOOLEAN WatchTree,
778 IN BOOLEAN IgnoreBuffer,
779 IN ULONG CompletionFilter,
780 IN PIRP NotifyIrp,
781 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
782 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
783 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
784 {
785 ULONG SavedLength;
786 PIO_STACK_LOCATION Stack;
787 PNOTIFY_CHANGE NotifyChange = NULL;
788 PREAL_NOTIFY_SYNC RealNotifySync;
789
790 PAGED_CODE();
791
792 DPRINT("FsRtlNotifyFilterChangeDirectory(): %p, %p, %p, %wZ, %u, %u, %u, %p, %p, %p, %p\n",
793 NotifySync, NotifyList, FsContext, FullDirectoryName, WatchTree, IgnoreBuffer, CompletionFilter, NotifyIrp,
794 TraverseCallback, SubjectContext, FilterCallback);
795
796 /* Get real structure hidden behind the opaque pointer */
797 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
798
799 /* Acquire the fast mutex */
800 FsRtlNotifyAcquireFastMutex(RealNotifySync);
801
802 _SEH2_TRY
803 {
804 /* If we have no IRP, FSD is performing a cleanup */
805 if (!NotifyIrp)
806 {
807 /* So, we delete */
808 FsRtlCheckNotifyForDelete(NotifyList, FsContext);
809 _SEH2_LEAVE;
810 }
811
812 NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
813 NotifyIrp->IoStatus.Information = (ULONG_PTR)NULL;
814
815 Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
816 /* If FileObject's been cleaned up, just return */
817 if (Stack->FileObject->Flags & FO_CLEANUP_COMPLETE)
818 {
819 IoMarkIrpPending(NotifyIrp);
820 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
821 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
822 _SEH2_LEAVE;
823 }
824
825 /* Try to find a matching notification has been already registered */
826 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
827 if (NotifyChange)
828 {
829 /* If it's been found, and is cleaned up, immediatly complete */
830 if (NotifyChange->Flags & CLEANUP_IN_PROCESS)
831 {
832 IoMarkIrpPending(NotifyIrp);
833 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
834 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
835 }
836 /* Or if it's about to be deleted, complete */
837 else if (NotifyChange->Flags & DELETE_IN_PROCESS)
838 {
839 IoMarkIrpPending(NotifyIrp);
840 NotifyIrp->IoStatus.Status = STATUS_DELETE_PENDING;
841 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
842 }
843 /* Complete now if asked to (and not asked to notify later on) */
844 if ((NotifyChange->Flags & NOTIFY_IMMEDIATELY) && !(NotifyChange->Flags & NOTIFY_LATER))
845 {
846 NotifyChange->Flags &= ~NOTIFY_IMMEDIATELY;
847 IoMarkIrpPending(NotifyIrp);
848 NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR;
849 IoCompleteRequest(NotifyIrp, IO_DISK_INCREMENT);
850 }
851 /* If no data yet, or asked to notify later on, handle */
852 else if (NotifyChange->DataLength == 0 || (NotifyChange->Flags & NOTIFY_LATER))
853 {
854 goto HandleIRP;
855 }
856 /* Else, just complete with we have */
857 else
858 {
859 SavedLength = NotifyChange->DataLength;
860 NotifyChange->DataLength = 0;
861 FsRtlNotifyCompleteIrp(NotifyIrp, NotifyChange, SavedLength, STATUS_SUCCESS, FALSE);
862 }
863
864 _SEH2_LEAVE;
865 }
866
867 /* Allocate new notification */
868 NotifyChange = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
869 sizeof(NOTIFY_CHANGE), 'NrSF');
870 RtlZeroMemory(NotifyChange, sizeof(NOTIFY_CHANGE));
871
872 /* Set basic information */
873 NotifyChange->NotifySync = NotifySync;
874 NotifyChange->FsContext = FsContext;
875 NotifyChange->StreamID = Stack->FileObject->FsContext;
876 NotifyChange->TraverseCallback = TraverseCallback;
877 NotifyChange->SubjectContext = SubjectContext;
878 NotifyChange->FullDirectoryName = FullDirectoryName;
879 NotifyChange->FilterCallback = FilterCallback;
880 InitializeListHead(&(NotifyChange->NotifyIrps));
881
882 /* Keep trace of WatchTree */
883 if (WatchTree)
884 {
885 NotifyChange->Flags |= WATCH_TREE;
886 }
887
888 /* If string is empty, faulty to ANSI */
889 if (FullDirectoryName->Length == 0)
890 {
891 NotifyChange->CharacterSize = sizeof(CHAR);
892 }
893 else
894 {
895 /* If it can't contain WCHAR, it's ANSI */
896 if (FullDirectoryName->Length < sizeof(WCHAR) || ((CHAR*)FullDirectoryName->Buffer)[1] != 0)
897 {
898 NotifyChange->CharacterSize = sizeof(CHAR);
899 }
900 else
901 {
902 NotifyChange->CharacterSize = sizeof(WCHAR);
903 }
904
905 /* Now, check is user is willing to watch root */
906 if (FullDirectoryName->Length == NotifyChange->CharacterSize)
907 {
908 NotifyChange->Flags |= WATCH_ROOT;
909 }
910 }
911
912 NotifyChange->CompletionFilter = CompletionFilter;
913
914 /* In case we have not to ignore buffer , keep its length */
915 if (!IgnoreBuffer)
916 {
917 NotifyChange->BufferLength = Stack->Parameters.NotifyDirectory.Length;
918 }
919
920 NotifyChange->OwningProcess = NotifyIrp->Tail.Overlay.Thread->ThreadsProcess;
921
922 /* Insert the notification into the notification list */
923 InsertTailList(NotifyList, &(NotifyChange->NotifyList));
924
925 NotifyChange->ReferenceCount = 1;
926
927 HandleIRP:
928 /* Associate the notification to the IRP */
929 NotifyIrp->IoStatus.Information = (ULONG_PTR)NotifyChange;
930 /* The IRP is pending */
931 IoMarkIrpPending(NotifyIrp);
932 /* Insert the IRP in the IRP list */
933 InsertTailList(&(NotifyChange->NotifyIrps), &(NotifyIrp->Tail.Overlay.ListEntry));
934 /* Increment reference count */
935 InterlockedIncrement((PLONG)&(NotifyChange->ReferenceCount));
936 /* Set cancel routine to FsRtl one */
937 FsRtlNotifySetCancelRoutine(NotifyIrp, NULL);
938 }
939 _SEH2_FINALLY
940 {
941 /* Release fast mutex */
942 FsRtlNotifyReleaseFastMutex(RealNotifySync);
943
944 /* If the subject security context was captured and there's no notify */
945 if (SubjectContext && (!NotifyChange || NotifyChange->FullDirectoryName))
946 {
947 SeReleaseSubjectContext(SubjectContext);
948 ExFreePool(SubjectContext);
949 }
950 }
951 _SEH2_END;
952 }
953
954 /*++
955 * @name FsRtlNotifyFilterReportChange
956 * @implemented
957 *
958 * FILLME
959 *
960 * @param NotifySync
961 * FILLME
962 *
963 * @param NotifyList
964 * FILLME
965 *
966 * @param FullTargetName
967 * FILLME
968 *
969 * @param TargetNameOffset
970 * FILLME
971 *
972 * @param StreamName
973 * FILLME
974 *
975 * @param NormalizedParentName
976 * FILLME
977 *
978 * @param FilterMatch
979 * FILLME
980 *
981 * @param Action
982 * FILLME
983 *
984 * @param TargetContext
985 * FILLME
986 *
987 * @param FilterContext
988 * FILLME
989 *
990 * @return None
991 *
992 * @remarks None
993 *
994 *--*/
995 VOID
996 NTAPI
FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PSTRING FullTargetName,IN USHORT TargetNameOffset,IN PSTRING StreamName OPTIONAL,IN PSTRING NormalizedParentName OPTIONAL,IN ULONG FilterMatch,IN ULONG Action,IN PVOID TargetContext,IN PVOID FilterContext)997 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
998 IN PLIST_ENTRY NotifyList,
999 IN PSTRING FullTargetName,
1000 IN USHORT TargetNameOffset,
1001 IN PSTRING StreamName OPTIONAL,
1002 IN PSTRING NormalizedParentName OPTIONAL,
1003 IN ULONG FilterMatch,
1004 IN ULONG Action,
1005 IN PVOID TargetContext,
1006 IN PVOID FilterContext)
1007 {
1008 PIRP Irp;
1009 PVOID OutputBuffer;
1010 USHORT FullPosition;
1011 PLIST_ENTRY NextEntry;
1012 PIO_STACK_LOCATION Stack;
1013 PNOTIFY_CHANGE NotifyChange;
1014 PREAL_NOTIFY_SYNC RealNotifySync;
1015 PFILE_NOTIFY_INFORMATION FileNotifyInfo;
1016 BOOLEAN IsStream, IsParent, PoolQuotaCharged;
1017 STRING TargetDirectory, TargetName, ParentName, IntNormalizedParentName;
1018 ULONG NumberOfBytes, TargetNumberOfParts, FullNumberOfParts, LastPartOffset, ParentNameOffset, ParentNameLength;
1019 ULONG DataLength, AlignedDataLength;
1020
1021 TargetDirectory.Length = 0;
1022 TargetDirectory.MaximumLength = 0;
1023 TargetDirectory.Buffer = NULL;
1024 TargetName.Length = 0;
1025 TargetName.MaximumLength = 0;
1026 TargetName.Buffer = NULL;
1027 ParentName.Length = 0;
1028 ParentName.MaximumLength = 0;
1029 ParentName.Buffer = NULL;
1030 IsStream = FALSE;
1031
1032 PAGED_CODE();
1033
1034 DPRINT("FsRtlNotifyFilterReportChange(%p, %p, %p, %u, %p, %p, %p, %x, %x, %p, %p)\n",
1035 NotifySync, NotifyList, FullTargetName, TargetNameOffset, StreamName, NormalizedParentName,
1036 FilterMatch, Action, TargetContext, FilterContext);
1037
1038 /* We need offset in name */
1039 if (!TargetNameOffset && FullTargetName)
1040 {
1041 return;
1042 }
1043
1044 /* Get real structure hidden behind the opaque pointer */
1045 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
1046 /* Acquire lock - will be released in finally block */
1047 FsRtlNotifyAcquireFastMutex(RealNotifySync);
1048 _SEH2_TRY
1049 {
1050 /* Browse all the registered notifications we have */
1051 for (NextEntry = NotifyList->Flink; NextEntry != NotifyList;
1052 NextEntry = NextEntry->Flink)
1053 {
1054 /* Try to find an entry matching our change */
1055 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
1056 if (FullTargetName != NULL)
1057 {
1058 ASSERT(NotifyChange->FullDirectoryName != NULL);
1059 if (!NotifyChange->FullDirectoryName->Length)
1060 {
1061 continue;
1062 }
1063
1064 if (!(FilterMatch & NotifyChange->CompletionFilter))
1065 {
1066 continue;
1067 }
1068
1069 /* If no normalized name provided, construct it from full target name */
1070 if (NormalizedParentName == NULL)
1071 {
1072 IntNormalizedParentName.Buffer = FullTargetName->Buffer;
1073 if (TargetNameOffset != NotifyChange->CharacterSize)
1074 {
1075 IntNormalizedParentName.MaximumLength =
1076 IntNormalizedParentName.Length = TargetNameOffset - NotifyChange->CharacterSize;
1077 }
1078 else
1079 {
1080 IntNormalizedParentName.MaximumLength =
1081 IntNormalizedParentName.Length = TargetNameOffset;
1082 }
1083 NormalizedParentName = &IntNormalizedParentName;
1084 }
1085
1086 /* heh? Watched directory bigger than changed file? */
1087 if (NormalizedParentName->Length < NotifyChange->FullDirectoryName->Length)
1088 {
1089 continue;
1090 }
1091
1092 /* Same len => parent */
1093 if (NormalizedParentName->Length == NotifyChange->FullDirectoryName->Length)
1094 {
1095 IsParent = TRUE;
1096 }
1097 /* If not, then, we have to be watching the tree, otherwise we don't have to report such changes */
1098 else if (!(NotifyChange->Flags & WATCH_TREE))
1099 {
1100 continue;
1101 }
1102 /* And finally, we've to check we're properly \-terminated */
1103 else
1104 {
1105 if (!(NotifyChange->Flags & WATCH_ROOT))
1106 {
1107 if (NotifyChange->CharacterSize == sizeof(CHAR))
1108 {
1109 if (((PSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length] != '\\')
1110 {
1111 continue;
1112 }
1113 }
1114 else
1115 {
1116 if (((PWSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length / sizeof (WCHAR)] != L'\\')
1117 {
1118 continue;
1119 }
1120 }
1121 }
1122
1123 IsParent = FALSE;
1124 }
1125
1126 /* If len matches, then check that both name are equal */
1127 if (!RtlEqualMemory(NormalizedParentName->Buffer, NotifyChange->FullDirectoryName->Buffer,
1128 NotifyChange->FullDirectoryName->Length))
1129 {
1130 continue;
1131 }
1132
1133 /* Call traverse callback (only if we have to traverse ;-)) */
1134 if (!IsParent
1135 && NotifyChange->TraverseCallback != NULL
1136 && !NotifyChange->TraverseCallback(NotifyChange->FsContext,
1137 TargetContext,
1138 NotifyChange->SubjectContext))
1139 {
1140 continue;
1141 }
1142
1143 /* And then, filter callback if provided */
1144 if (NotifyChange->FilterCallback != NULL
1145 && FilterContext != NULL
1146 && !NotifyChange->FilterCallback(NotifyChange->FsContext, FilterContext))
1147 {
1148 continue;
1149 }
1150 }
1151 /* We have a stream! */
1152 else
1153 {
1154 ASSERT(NotifyChange->FullDirectoryName == NULL);
1155 if (TargetContext != NotifyChange->SubjectContext)
1156 {
1157 continue;
1158 }
1159
1160 ParentName.Buffer = NULL;
1161 ParentName.Length = 0;
1162 IsStream = TRUE;
1163 IsParent = FALSE;
1164 }
1165
1166 /* If we don't have to notify immediately, prepare for output */
1167 if (!(NotifyChange->Flags & NOTIFY_IMMEDIATELY))
1168 {
1169 /* If we have something to output... */
1170 if (NotifyChange->BufferLength)
1171 {
1172 /* Get size of the output */
1173 NumberOfBytes = 0;
1174 Irp = NULL;
1175 if (!NotifyChange->ThisBufferLength)
1176 {
1177 if (IsListEmpty(&NotifyChange->NotifyIrps))
1178 {
1179 NumberOfBytes = NotifyChange->BufferLength;
1180 }
1181 else
1182 {
1183 Irp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry);
1184 Stack = IoGetCurrentIrpStackLocation(Irp);
1185 NumberOfBytes = Stack->Parameters.NotifyDirectory.Length;
1186 }
1187 }
1188 else
1189 {
1190 NumberOfBytes = NotifyChange->ThisBufferLength;
1191 }
1192
1193 /* If we're matching parent, we don't care about parent (redundant) */
1194 if (IsParent)
1195 {
1196 ParentName.Length = 0;
1197 }
1198 else
1199 {
1200 /* If we don't deal with streams, some more work is required */
1201 if (!IsStream)
1202 {
1203 if (NotifyChange->Flags & WATCH_ROOT ||
1204 (NormalizedParentName->Buffer != FullTargetName->Buffer))
1205 {
1206 /* Construct TargetDirectory if we don't have it yet */
1207 if (TargetDirectory.Buffer == NULL)
1208 {
1209 TargetDirectory.Buffer = FullTargetName->Buffer;
1210 TargetDirectory.Length = TargetNameOffset;
1211 if (TargetNameOffset != NotifyChange->CharacterSize)
1212 {
1213 TargetDirectory.Length = TargetNameOffset - NotifyChange->CharacterSize;
1214 }
1215 TargetDirectory.MaximumLength = TargetDirectory.Length;
1216 }
1217 /* Now, we start looking for matching parts (unless we watch root) */
1218 TargetNumberOfParts = 0;
1219 if (!(NotifyChange->Flags & WATCH_ROOT))
1220 {
1221 FullNumberOfParts = 1;
1222 if (NotifyChange->CharacterSize == sizeof(CHAR))
1223 {
1224 FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length,
1225 TargetDirectory.Length, PSTR, '\\');
1226 }
1227 else
1228 {
1229 FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length / sizeof(WCHAR),
1230 TargetDirectory.Length / sizeof(WCHAR), PWSTR, L'\\');
1231 LastPartOffset *= NotifyChange->CharacterSize;
1232 }
1233 }
1234
1235 /* Then, we can construct proper parent name */
1236 ParentNameOffset = NotifyChange->CharacterSize + LastPartOffset;
1237 ParentName.Buffer = &TargetDirectory.Buffer[ParentNameOffset];
1238 ParentNameLength = TargetDirectory.Length;
1239 }
1240 else
1241 {
1242 /* Construct parent name even for streams */
1243 ParentName.Buffer = &NormalizedParentName->Buffer[NotifyChange->FullDirectoryName->Length] + NotifyChange->CharacterSize;
1244 ParentNameLength = NormalizedParentName->Length - NotifyChange->FullDirectoryName->Length;
1245 ParentNameOffset = NotifyChange->CharacterSize;
1246 }
1247 ParentNameLength -= ParentNameOffset;
1248 ParentName.Length = ParentNameLength;
1249 ParentName.MaximumLength = ParentNameLength;
1250 }
1251 }
1252
1253 /* Start to count amount of data to write, we've first the structure itself */
1254 DataLength = FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName);
1255
1256 /* If stream, we'll just append stream name */
1257 if (IsStream)
1258 {
1259 ASSERT(StreamName != NULL);
1260 DataLength += StreamName->Length;
1261 }
1262 else
1263 {
1264 /* If not parent, we've to append parent name */
1265 if (!IsParent)
1266 {
1267 if (NotifyChange->CharacterSize == sizeof(CHAR))
1268 {
1269 DataLength += RtlOemStringToCountedUnicodeSize(&ParentName);
1270 }
1271 else
1272 {
1273 DataLength += ParentName.Length;
1274 }
1275 DataLength += sizeof(WCHAR);
1276 }
1277
1278 /* Look for target name & construct it, if required */
1279 if (TargetName.Buffer == NULL)
1280 {
1281 TargetName.Buffer = &FullTargetName->Buffer[TargetNameOffset];
1282 TargetName.Length =
1283 TargetName.MaximumLength = FullTargetName->Length - TargetNameOffset;
1284 }
1285
1286 /* Then, we will append it as well */
1287 if (NotifyChange->CharacterSize == sizeof(CHAR))
1288 {
1289 DataLength += RtlOemStringToCountedUnicodeSize(&TargetName);
1290 }
1291 else
1292 {
1293 DataLength += TargetName.Length;
1294 }
1295
1296 /* If we also had a stream name, then we can append it as well */
1297 if (StreamName != NULL)
1298 {
1299 if (NotifyChange->CharacterSize == sizeof(WCHAR))
1300 {
1301 DataLength += StreamName->Length + sizeof(WCHAR);
1302 }
1303 else
1304 {
1305 DataLength = DataLength + RtlOemStringToCountedUnicodeSize(&TargetName) + sizeof(CHAR);
1306 }
1307 }
1308 }
1309
1310 /* Get the position where we can put our data (aligned!) */
1311 AlignedDataLength = ROUND_UP(NotifyChange->DataLength, sizeof(ULONG));
1312 /* If it's higher than buffer length, then, bail out without outputing */
1313 if (DataLength > NumberOfBytes || AlignedDataLength + DataLength > NumberOfBytes)
1314 {
1315 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1316 }
1317 else
1318 {
1319 OutputBuffer = NULL;
1320 FileNotifyInfo = NULL;
1321 /* If we already had a buffer, update last entry position */
1322 if (NotifyChange->Buffer != NULL)
1323 {
1324 FileNotifyInfo = (PVOID)((ULONG_PTR)NotifyChange->Buffer + NotifyChange->LastEntry);
1325 FileNotifyInfo->NextEntryOffset = AlignedDataLength - NotifyChange->LastEntry;
1326 NotifyChange->LastEntry = AlignedDataLength;
1327 /* And get our output buffer */
1328 OutputBuffer = (PVOID)((ULONG_PTR)NotifyChange->Buffer + AlignedDataLength);
1329 }
1330 /* If we hadn't buffer, try to find one */
1331 else if (Irp != NULL)
1332 {
1333 if (Irp->AssociatedIrp.SystemBuffer != NULL)
1334 {
1335 OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
1336 }
1337 else if (Irp->MdlAddress != NULL)
1338 {
1339 OutputBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
1340 }
1341
1342 NotifyChange->Buffer = OutputBuffer;
1343 NotifyChange->ThisBufferLength = NumberOfBytes;
1344 }
1345
1346 /* If we couldn't find one, then allocate one */
1347 if (NotifyChange->Buffer == NULL)
1348 {
1349 /* Assign the length buffer */
1350 NotifyChange->ThisBufferLength = NumberOfBytes;
1351
1352 /* Assume we have not charged quotas */
1353 PoolQuotaCharged = FALSE;
1354 _SEH2_TRY
1355 {
1356 /* And charge quotas */
1357 PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, NotifyChange->ThisBufferLength);
1358 PoolQuotaCharged = TRUE;
1359 OutputBuffer = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
1360 NumberOfBytes, TAG_FS_NOTIFICATIONS);
1361 NotifyChange->Buffer = OutputBuffer;
1362 NotifyChange->AllocatedBuffer = OutputBuffer;
1363 }
1364 /* If something went wrong during allocation, notify immediately instead of outputing */
1365 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1366 {
1367 if (PoolQuotaCharged)
1368 {
1369 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
1370 }
1371 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1372 }
1373 _SEH2_END;
1374 }
1375
1376 /* Finally, if we have a buffer, fill it in! */
1377 if (OutputBuffer != NULL)
1378 {
1379 if (FsRtlNotifyUpdateBuffer((FILE_NOTIFY_INFORMATION *)OutputBuffer,
1380 Action, &ParentName, &TargetName,
1381 StreamName, NotifyChange->CharacterSize == sizeof(WCHAR),
1382 DataLength))
1383 {
1384 NotifyChange->DataLength = DataLength + AlignedDataLength;
1385 }
1386 /* If it failed, notify immediately */
1387 else
1388 {
1389 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
1390 }
1391 }
1392 }
1393
1394 /* If we have to notify right now (something went wrong?) */
1395 if (NotifyChange->Flags & NOTIFY_IMMEDIATELY)
1396 {
1397 /* Ensure that all our buffers are NULL */
1398 if (NotifyChange->Buffer != NULL)
1399 {
1400 if (NotifyChange->AllocatedBuffer != NULL)
1401 {
1402 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
1403 ExFreePoolWithTag(NotifyChange->AllocatedBuffer, TAG_FS_NOTIFICATIONS);
1404 }
1405
1406 NotifyChange->Buffer = NULL;
1407 NotifyChange->AllocatedBuffer = NULL;
1408 NotifyChange->LastEntry = 0;
1409 NotifyChange->DataLength = 0;
1410 NotifyChange->ThisBufferLength = 0;
1411 }
1412 }
1413 }
1414 }
1415
1416 /* If asking for old name in case of a rename, notify later on,
1417 * so that we can wait for new name.
1418 * http://msdn.microsoft.com/en-us/library/dn392331.aspx
1419 */
1420 if (Action == FILE_ACTION_RENAMED_OLD_NAME)
1421 {
1422 NotifyChange->Flags |= NOTIFY_LATER;
1423 }
1424 else
1425 {
1426 NotifyChange->Flags &= ~NOTIFY_LATER;
1427 if (!IsListEmpty(&NotifyChange->NotifyIrps))
1428 {
1429 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_SUCCESS);
1430 }
1431 }
1432 }
1433 }
1434 _SEH2_FINALLY
1435 {
1436 FsRtlNotifyReleaseFastMutex(RealNotifySync);
1437 }
1438 _SEH2_END;
1439 }
1440
1441 /*++
1442 * @name FsRtlNotifyFullChangeDirectory
1443 * @implemented
1444 *
1445 * Lets FSD know if changes occures in the specified directory.
1446 *
1447 * @param NotifySync
1448 * Synchronization object pointer
1449 *
1450 * @param NotifyList
1451 * Notify list pointer (to head)
1452 *
1453 * @param FsContext
1454 * Used to identify the notify structure
1455 *
1456 * @param FullDirectoryName
1457 * String (A or W) containing the full directory name
1458 *
1459 * @param WatchTree
1460 * True to notify changes in subdirectories too
1461 *
1462 * @param IgnoreBuffer
1463 * True to reenumerate directory. It's ignored it NotifyIrp is null
1464 *
1465 * @param CompletionFilter
1466 * Used to define types of changes to notify
1467 *
1468 * @param NotifyIrp
1469 * IRP pointer to complete notify operation. It can be null
1470 *
1471 * @param TraverseCallback
1472 * Pointer to a callback function. It's called each time a change is
1473 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
1474 * is null
1475 *
1476 * @param SubjectContext
1477 * Pointer to pass to SubjectContext member of TraverseCallback.
1478 * It's freed after use. It's ignored it NotifyIrp is null
1479 *
1480 * @return None
1481 *
1482 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
1483 *
1484 *--*/
1485 VOID
1486 NTAPI
FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PVOID FsContext,IN PSTRING FullDirectoryName,IN BOOLEAN WatchTree,IN BOOLEAN IgnoreBuffer,IN ULONG CompletionFilter,IN PIRP NotifyIrp,IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)1487 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
1488 IN PLIST_ENTRY NotifyList,
1489 IN PVOID FsContext,
1490 IN PSTRING FullDirectoryName,
1491 IN BOOLEAN WatchTree,
1492 IN BOOLEAN IgnoreBuffer,
1493 IN ULONG CompletionFilter,
1494 IN PIRP NotifyIrp,
1495 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
1496 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
1497 {
1498 FsRtlNotifyFilterChangeDirectory(NotifySync,
1499 NotifyList,
1500 FsContext,
1501 FullDirectoryName,
1502 WatchTree,
1503 IgnoreBuffer,
1504 CompletionFilter,
1505 NotifyIrp,
1506 TraverseCallback,
1507 SubjectContext,
1508 NULL);
1509 }
1510
1511 /*++
1512 * @name FsRtlNotifyFullReportChange
1513 * @implemented
1514 *
1515 * Complets the pending notify IRPs.
1516 *
1517 * @param NotifySync
1518 * Synchronization object pointer
1519 *
1520 * @param NotifyList
1521 * Notify list pointer (to head)
1522 *
1523 * @param FullTargetName
1524 * String (A or W) containing the full directory name that changed
1525 *
1526 * @param TargetNameOffset
1527 * Offset, in FullTargetName, of the final component that is in the changed directory
1528 *
1529 * @param StreamName
1530 * String (A or W) containing a stream name
1531 *
1532 * @param NormalizedParentName
1533 * String (A or W) containing the full directory name that changed with long names
1534 *
1535 * @param FilterMatch
1536 * Flags that will be compared to the completion filter
1537 *
1538 * @param Action
1539 * Action code to store in user's buffer
1540 *
1541 * @param TargetContext
1542 * Pointer to a callback function. It's called each time a change is
1543 * done in a subdirectory of the main directory.
1544 *
1545 * @return None
1546 *
1547 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1548 *
1549 *--*/
1550 VOID
1551 NTAPI
FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PSTRING FullTargetName,IN USHORT TargetNameOffset,IN PSTRING StreamName OPTIONAL,IN PSTRING NormalizedParentName OPTIONAL,IN ULONG FilterMatch,IN ULONG Action,IN PVOID TargetContext)1552 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
1553 IN PLIST_ENTRY NotifyList,
1554 IN PSTRING FullTargetName,
1555 IN USHORT TargetNameOffset,
1556 IN PSTRING StreamName OPTIONAL,
1557 IN PSTRING NormalizedParentName OPTIONAL,
1558 IN ULONG FilterMatch,
1559 IN ULONG Action,
1560 IN PVOID TargetContext)
1561 {
1562 FsRtlNotifyFilterReportChange(NotifySync,
1563 NotifyList,
1564 FullTargetName,
1565 TargetNameOffset,
1566 StreamName,
1567 NormalizedParentName,
1568 FilterMatch,
1569 Action,
1570 TargetContext,
1571 NULL);
1572 }
1573
1574 /*++
1575 * @name FsRtlNotifyInitializeSync
1576 * @implemented
1577 *
1578 * Allocates the internal structure associated with notifications.
1579 *
1580 * @param NotifySync
1581 * Opaque pointer. It will receive the address of the allocated internal structure.
1582 *
1583 * @return None
1584 *
1585 * @remarks This function raise an exception in case of a failure.
1586 *
1587 *--*/
1588 VOID
1589 NTAPI
FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC * NotifySync)1590 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
1591 {
1592 PREAL_NOTIFY_SYNC RealNotifySync;
1593
1594 *NotifySync = NULL;
1595
1596 RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
1597 sizeof(REAL_NOTIFY_SYNC), 'SNSF');
1598 ExInitializeFastMutex(&(RealNotifySync->FastMutex));
1599 RealNotifySync->OwningThread = 0;
1600 RealNotifySync->OwnerCount = 0;
1601
1602 *NotifySync = RealNotifySync;
1603 }
1604
1605 /*++
1606 * @name FsRtlNotifyReportChange
1607 * @implemented
1608 *
1609 * Complets the pending notify IRPs.
1610 *
1611 * @param NotifySync
1612 * Synchronization object pointer
1613 *
1614 * @param NotifyList
1615 * Notify list pointer (to head)
1616 *
1617 * @param FullTargetName
1618 * String (A or W) containing the full directory name that changed
1619 *
1620 * @param FileNamePartLength
1621 * Length of the final component that is in the changed directory
1622 *
1623 * @param FilterMatch
1624 * Flags that will be compared to the completion filter
1625 *
1626 * @return None
1627 *
1628 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
1629 *
1630 *--*/
1631 VOID
1632 NTAPI
FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,IN PLIST_ENTRY NotifyList,IN PSTRING FullTargetName,IN PUSHORT FileNamePartLength,IN ULONG FilterMatch)1633 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
1634 IN PLIST_ENTRY NotifyList,
1635 IN PSTRING FullTargetName,
1636 IN PUSHORT FileNamePartLength,
1637 IN ULONG FilterMatch)
1638 {
1639 FsRtlNotifyFilterReportChange(NotifySync,
1640 NotifyList,
1641 FullTargetName,
1642 FullTargetName->Length - *FileNamePartLength,
1643 NULL,
1644 NULL,
1645 FilterMatch,
1646 0,
1647 NULL,
1648 NULL);
1649 }
1650
1651 /*++
1652 * @name FsRtlNotifyUninitializeSync
1653 * @implemented
1654 *
1655 * Uninitialize a NOTIFY_SYNC object
1656 *
1657 * @param NotifySync
1658 * Address of a pointer to a PNOTIFY_SYNC object previously
1659 * initialized by FsRtlNotifyInitializeSync()
1660 *
1661 * @return None
1662 *
1663 * @remarks None
1664 *
1665 *--*/
1666 VOID
1667 NTAPI
FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC * NotifySync)1668 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
1669 {
1670 if (*NotifySync)
1671 {
1672 ExFreePoolWithTag(*NotifySync, 'SNSF');
1673 *NotifySync = NULL;
1674 }
1675 }
1676
1677