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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1668 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync) 1669 { 1670 if (*NotifySync) 1671 { 1672 ExFreePoolWithTag(*NotifySync, 'SNSF'); 1673 *NotifySync = NULL; 1674 } 1675 } 1676 1677