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