1 /*++ 2 3 Copyright (c) 1997-2000 Microsoft Corporation 4 5 Module Name: 6 7 Pnp.c 8 9 Abstract: 10 11 This module implements the Plug and Play routines for CDFS called by 12 the dispatch driver. 13 14 15 --*/ 16 17 #include "cdprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (CDFS_BUG_CHECK_PNP) 24 25 _Requires_lock_held_(_Global_critical_region_) 26 _Releases_nonreentrant_lock_(CdData.DataResource) 27 NTSTATUS 28 CdPnpQueryRemove ( 29 _Inout_ PIRP_CONTEXT IrpContext, 30 _Inout_ PIRP Irp, 31 _Inout_ PVCB Vcb 32 ); 33 34 _Requires_lock_held_(_Global_critical_region_) 35 _Releases_nonreentrant_lock_(CdData.DataResource) 36 NTSTATUS 37 CdPnpRemove ( 38 _Inout_ PIRP_CONTEXT IrpContext, 39 _Inout_ PIRP Irp, 40 _Inout_ PVCB Vcb 41 ); 42 43 _Requires_lock_held_(_Global_critical_region_) 44 _Releases_nonreentrant_lock_(CdData.DataResource) 45 NTSTATUS 46 CdPnpSurpriseRemove ( 47 _Inout_ PIRP_CONTEXT IrpContext, 48 _Inout_ PIRP Irp, 49 _Inout_ PVCB Vcb 50 ); 51 52 _Requires_lock_held_(_Global_critical_region_) 53 _Releases_nonreentrant_lock_(CdData.DataResource) 54 NTSTATUS 55 CdPnpCancelRemove ( 56 _Inout_ PIRP_CONTEXT IrpContext, 57 _Inout_ PIRP Irp, 58 _Inout_ PVCB Vcb 59 ); 60 61 // Tell prefast this is a completion routine. 62 IO_COMPLETION_ROUTINE CdPnpCompletionRoutine; 63 64 NTSTATUS 65 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 66 CdPnpCompletionRoutine ( 67 _In_ PDEVICE_OBJECT DeviceObject, 68 _In_ PIRP Irp, 69 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 70 ); 71 72 #ifdef ALLOC_PRAGMA 73 #pragma alloc_text(PAGE, CdCommonPnp) 74 #pragma alloc_text(PAGE, CdPnpCancelRemove) 75 #pragma alloc_text(PAGE, CdPnpQueryRemove) 76 #pragma alloc_text(PAGE, CdPnpRemove) 77 #pragma alloc_text(PAGE, CdPnpSurpriseRemove) 78 #endif 79 80 _Requires_lock_held_(_Global_critical_region_) 81 NTSTATUS 82 CdCommonPnp ( 83 _Inout_ PIRP_CONTEXT IrpContext, 84 _Inout_ PIRP Irp 85 ) 86 87 /*++ 88 89 Routine Description: 90 91 This is the common routine for doing PnP operations called 92 by both the fsd and fsp threads 93 94 Arguments: 95 96 Irp - Supplies the Irp to process 97 98 Return Value: 99 100 NTSTATUS - The return status for the operation 101 102 --*/ 103 104 { 105 NTSTATUS Status = STATUS_SUCCESS; 106 BOOLEAN PassThrough = FALSE; 107 108 PIO_STACK_LOCATION IrpSp; 109 110 PVOLUME_DEVICE_OBJECT OurDeviceObject; 111 PVCB Vcb; 112 113 PAGED_CODE(); 114 115 // Global lock object is acquired based on internal book-keeping 116 _Analysis_suppress_lock_checking_(CdData.DataResource); 117 118 // 119 // Get the current Irp stack location. 120 // 121 122 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 123 124 // 125 // Find our Vcb. This is tricky since we have no file object in the Irp. 126 // 127 128 OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject; 129 130 // 131 // IO holds a handle reference on our VDO and holds the device lock, which 132 // syncs us against mounts/verifies. However we hold no reference on the 133 // volume, which may already have been torn down (and the Vpb freed), for 134 // example by a force dismount. Check for this condition. We must hold this 135 // lock until the pnp worker functions take additional locks/refs on the Vcb. 136 // 137 138 CdAcquireCdData( IrpContext); 139 140 // 141 // Make sure this device object really is big enough to be a volume device 142 // object. If it isn't, we need to get out before we try to reference some 143 // field that takes us past the end of an ordinary device object. 144 // 145 146 #ifdef _MSC_VER 147 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the size member is allowed") 148 #endif 149 if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || 150 NodeType( &OurDeviceObject->Vcb ) != CDFS_NTC_VCB) { 151 152 // 153 // We were called with something we don't understand. 154 // 155 156 Status = STATUS_INVALID_PARAMETER; 157 CdReleaseCdData( IrpContext); 158 CdCompleteRequest( IrpContext, Irp, Status ); 159 return Status; 160 } 161 162 // 163 // Force all PnP operations to be synchronous. 164 // 165 166 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); 167 168 Vcb = &OurDeviceObject->Vcb; 169 170 // 171 // Check that the Vcb hasn't already been deleted. If so, just pass the 172 // request through to the driver below, we don't need to do anything. 173 // 174 175 if (NULL == Vcb->Vpb) { 176 177 PassThrough = TRUE; 178 } 179 else { 180 181 // 182 // Case on the minor code. 183 // 184 185 switch ( IrpSp->MinorFunction ) { 186 187 case IRP_MN_QUERY_REMOVE_DEVICE: 188 189 Status = CdPnpQueryRemove( IrpContext, Irp, Vcb ); 190 break; 191 192 case IRP_MN_SURPRISE_REMOVAL: 193 194 Status = CdPnpSurpriseRemove( IrpContext, Irp, Vcb ); 195 break; 196 197 case IRP_MN_REMOVE_DEVICE: 198 199 Status = CdPnpRemove( IrpContext, Irp, Vcb ); 200 break; 201 202 case IRP_MN_CANCEL_REMOVE_DEVICE: 203 204 Status = CdPnpCancelRemove( IrpContext, Irp, Vcb ); 205 break; 206 207 default: 208 209 PassThrough = TRUE; 210 break; 211 } 212 } 213 214 if (PassThrough) { 215 216 CdReleaseCdData( IrpContext); 217 218 // 219 // Just pass the IRP on. As we do not need to be in the 220 // way on return, ellide ourselves out of the stack. 221 // 222 223 IoSkipCurrentIrpStackLocation( Irp ); 224 225 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 226 227 // 228 // Cleanup our Irp Context. The driver has completed the Irp. 229 // 230 231 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); 232 } 233 234 return Status; 235 } 236 237 _Requires_lock_held_(_Global_critical_region_) 238 _Releases_nonreentrant_lock_(CdData.DataResource) 239 NTSTATUS 240 CdPnpQueryRemove ( 241 _Inout_ PIRP_CONTEXT IrpContext, 242 _Inout_ PIRP Irp, 243 _Inout_ PVCB Vcb 244 ) 245 246 /*++ 247 248 Routine Description: 249 250 This routine handles the PnP query remove operation. The filesystem 251 is responsible for answering whether there are any reasons it sees 252 that the volume can not go away (and the device removed). Initiation 253 of the dismount begins when we answer yes to this question. 254 255 Query will be followed by a Cancel or Remove. 256 257 Arguments: 258 259 Irp - Supplies the Irp to process 260 261 Vcb - Supplies the volume being queried. 262 263 Return Value: 264 265 NTSTATUS - The return status for the operation 266 267 --*/ 268 269 { 270 NTSTATUS Status; 271 KEVENT Event; 272 BOOLEAN VcbPresent = TRUE; 273 274 PAGED_CODE(); 275 276 ASSERT_EXCLUSIVE_CDDATA; 277 278 // 279 // Having said yes to a QUERY, any communication with the 280 // underlying storage stack is undefined (and may block) 281 // until the bounding CANCEL or REMOVE is sent. 282 // 283 // Acquire the global resource so that we can try to vaporize the volume, 284 // and the vcb resource itself. 285 // 286 287 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 288 289 // 290 // Drop a reference on the Vcb to keep it around after we drop the locks. 291 // 292 293 CdLockVcb( IrpContext, Vcb); 294 Vcb->VcbReference += 1; 295 CdUnlockVcb( IrpContext, Vcb); 296 297 CdReleaseCdData( IrpContext); 298 299 Status = CdLockVolumeInternal( IrpContext, Vcb, NULL ); 300 301 // 302 // Reacquire the global lock, which means dropping the Vcb resource. 303 // 304 305 CdReleaseVcb( IrpContext, Vcb ); 306 307 CdAcquireCdData( IrpContext ); 308 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 309 310 // 311 // Remove our extra reference. 312 // 313 314 CdLockVcb( IrpContext, Vcb); 315 Vcb->VcbReference -= 1; 316 CdUnlockVcb( IrpContext, Vcb); 317 318 if (NT_SUCCESS( Status )) { 319 320 // 321 // We need to pass this down before starting the dismount, which 322 // could disconnect us immediately from the stack. 323 // 324 325 // 326 // Get the next stack location, and copy over the stack location 327 // 328 329 IoCopyCurrentIrpStackLocationToNext( Irp ); 330 331 // 332 // Set up the completion routine 333 // 334 335 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 336 IoSetCompletionRoutine( Irp, 337 CdPnpCompletionRoutine, 338 &Event, 339 TRUE, 340 TRUE, 341 TRUE ); 342 343 // 344 // Send the request and wait. 345 // 346 347 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 348 349 if (Status == STATUS_PENDING) { 350 351 (VOID)KeWaitForSingleObject( &Event, 352 Executive, 353 KernelMode, 354 FALSE, 355 NULL ); 356 357 Status = Irp->IoStatus.Status; 358 } 359 360 // 361 // Now if no one below us failed already, initiate the dismount 362 // on this volume, make it go away. PnP needs to see our internal 363 // streams close and drop their references to the target device. 364 // 365 // Since we were able to lock the volume, we are guaranteed to 366 // move this volume into dismount state and disconnect it from 367 // the underlying storage stack. The force on our part is actually 368 // unnecesary, though complete. 369 // 370 // What is not strictly guaranteed, though, is that the closes 371 // for the metadata streams take effect synchronously underneath 372 // of this call. This would leave references on the target device 373 // even though we are disconnected! 374 // 375 376 if (NT_SUCCESS( Status )) { 377 378 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); 379 380 NT_ASSERT( !VcbPresent || Vcb->VcbCondition == VcbDismountInProgress ); 381 } 382 383 // 384 // Note: Normally everything will complete and the internal streams will 385 // vaporise. However there is some code in the system which drops additional 386 // references on fileobjects, including our internal stream file objects, 387 // for (WMI) tracing purposes. If that happens to run concurrently with our 388 // teardown, our internal streams will not vaporise until those references 389 // are removed. So it's possible that the volume still remains at this 390 // point. The pnp query remove will fail due to our references on the device. 391 // To be cleaner we will return an error here. We could pend the pnp 392 // IRP until the volume goes away, but since we don't know when that will 393 // be, and this is a very rare case, we'll just fail the query. 394 // 395 // The reason this is the case is that handles/fileobjects place a reference 396 // on the device objects they overly. In the filesystem case, these references 397 // are on our target devices. PnP correcly thinks that if references remain 398 // on the device objects in the stack that someone has a handle, and that this 399 // counts as a reason to not succeed the query - even though every interrogated 400 // driver thinks that it is OK. 401 // 402 403 if (NT_SUCCESS( Status) && VcbPresent && (Vcb->VcbReference != 0)) { 404 405 Status = STATUS_DEVICE_BUSY; 406 } 407 } 408 409 // 410 // Release the Vcb if it could still remain. 411 // 412 413 if (VcbPresent) { 414 415 CdReleaseVcb( IrpContext, Vcb ); 416 } 417 else { 418 _Analysis_assume_lock_not_held_(Vcb->VcbResource); 419 } 420 421 CdReleaseCdData( IrpContext ); 422 423 // 424 // Cleanup our IrpContext and complete the IRP if neccesary. 425 // 426 427 CdCompleteRequest( IrpContext, Irp, Status ); 428 429 return Status; 430 } 431 432 _Requires_lock_held_(_Global_critical_region_) 433 _Releases_nonreentrant_lock_(CdData.DataResource) 434 NTSTATUS 435 CdPnpRemove ( 436 _Inout_ PIRP_CONTEXT IrpContext, 437 _Inout_ PIRP Irp, 438 _Inout_ PVCB Vcb 439 ) 440 441 /*++ 442 443 Routine Description: 444 445 This routine handles the PnP remove operation. This is our notification 446 that the underlying storage device for the volume we have is gone, and 447 an excellent indication that the volume will never reappear. The filesystem 448 is responsible for initiation or completion the dismount. 449 450 Arguments: 451 452 Irp - Supplies the Irp to process 453 454 Vcb - Supplies the volume being removed. 455 456 Return Value: 457 458 NTSTATUS - The return status for the operation 459 460 --*/ 461 462 { 463 NTSTATUS Status; 464 KEVENT Event; 465 BOOLEAN VcbPresent = TRUE; 466 467 PAGED_CODE(); 468 469 ASSERT_EXCLUSIVE_CDDATA; 470 471 // 472 // REMOVE - a storage device is now gone. We either got 473 // QUERY'd and said yes OR got a SURPRISE OR a storage 474 // stack failed to spin back up from a sleep/stop state 475 // (the only case in which this will be the first warning). 476 // 477 // Note that it is entirely unlikely that we will be around 478 // for a REMOVE in the first two cases, as we try to intiate 479 // dismount. 480 // 481 482 // 483 // Acquire the global resource so that we can try to vaporize 484 // the volume, and the vcb resource itself. 485 // 486 487 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 488 489 // 490 // The device will be going away. Remove our lock and find 491 // out if we ever had one in the first place. 492 // 493 494 Status = CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); 495 496 // 497 // If the volume had not been locked, we must invalidate the 498 // volume to ensure it goes away properly. The remove will 499 // succeed. 500 // 501 502 if (!NT_SUCCESS( Status )) { 503 504 CdLockVcb( IrpContext, Vcb ); 505 506 if (Vcb->VcbCondition != VcbDismountInProgress) { 507 508 CdUpdateVcbCondition( Vcb, VcbInvalid); 509 } 510 511 CdUnlockVcb( IrpContext, Vcb ); 512 513 Status = STATUS_SUCCESS; 514 } 515 516 // 517 // We need to pass this down before starting the dismount, which 518 // could disconnect us immediately from the stack. 519 // 520 521 // 522 // Get the next stack location, and copy over the stack location 523 // 524 525 IoCopyCurrentIrpStackLocationToNext( Irp ); 526 527 // 528 // Set up the completion routine 529 // 530 531 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 532 IoSetCompletionRoutine( Irp, 533 CdPnpCompletionRoutine, 534 &Event, 535 TRUE, 536 TRUE, 537 TRUE ); 538 539 // 540 // Send the request and wait. 541 // 542 543 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 544 545 if (Status == STATUS_PENDING) { 546 547 (VOID)KeWaitForSingleObject( &Event, 548 Executive, 549 KernelMode, 550 FALSE, 551 NULL ); 552 553 Status = Irp->IoStatus.Status; 554 } 555 556 // 557 // Now make our dismount happen. This may not vaporize the 558 // Vcb, of course, since there could be any number of handles 559 // outstanding if we were not preceeded by a QUERY. 560 // 561 // PnP will take care of disconnecting this stack if we 562 // couldn't get off of it immediately. 563 // 564 565 566 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); 567 568 // 569 // Release the Vcb if it could still remain. 570 // 571 572 if (VcbPresent) { 573 574 CdReleaseVcb( IrpContext, Vcb ); 575 } 576 else { 577 _Analysis_assume_lock_not_held_(Vcb->VcbResource); 578 } 579 580 CdReleaseCdData( IrpContext ); 581 582 // 583 // Cleanup our IrpContext and complete the IRP. 584 // 585 586 CdCompleteRequest( IrpContext, Irp, Status ); 587 588 return Status; 589 } 590 591 _Requires_lock_held_(_Global_critical_region_) 592 _Releases_nonreentrant_lock_(CdData.DataResource) 593 NTSTATUS 594 CdPnpSurpriseRemove ( 595 _Inout_ PIRP_CONTEXT IrpContext, 596 _Inout_ PIRP Irp, 597 _Inout_ PVCB Vcb 598 ) 599 600 /*++ 601 602 Routine Description: 603 604 This routine handles the PnP surprise remove operation. This is another 605 type of notification that the underlying storage device for the volume we 606 have is gone, and is excellent indication that the volume will never reappear. 607 The filesystem is responsible for initiation or completion the dismount. 608 609 For the most part, only "real" drivers care about the distinction of a 610 surprise remove, which is a result of our noticing that a user (usually) 611 physically reached into the machine and pulled something out. 612 613 Surprise will be followed by a Remove when all references have been shut down. 614 615 Arguments: 616 617 Irp - Supplies the Irp to process 618 619 Vcb - Supplies the volume being removed. 620 621 Return Value: 622 623 NTSTATUS - The return status for the operation 624 625 --*/ 626 627 { 628 NTSTATUS Status; 629 KEVENT Event; 630 BOOLEAN VcbPresent = TRUE; 631 632 PAGED_CODE(); 633 634 ASSERT_EXCLUSIVE_CDDATA; 635 636 // 637 // SURPRISE - a device was physically yanked away without 638 // any warning. This means external forces. 639 // 640 641 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 642 643 // 644 // Invalidate the volume right now. 645 // 646 // The intent here is to make every subsequent operation 647 // on the volume fail and grease the rails toward dismount. 648 // By definition there is no going back from a SURPRISE. 649 // 650 651 CdLockVcb( IrpContext, Vcb ); 652 653 if (Vcb->VcbCondition != VcbDismountInProgress) { 654 655 CdUpdateVcbCondition( Vcb, VcbInvalid); 656 } 657 658 CdUnlockVcb( IrpContext, Vcb ); 659 660 // 661 // We need to pass this down before starting the dismount, which 662 // could disconnect us immediately from the stack. 663 // 664 665 // 666 // Get the next stack location, and copy over the stack location 667 // 668 669 IoCopyCurrentIrpStackLocationToNext( Irp ); 670 671 // 672 // Set up the completion routine 673 // 674 675 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 676 IoSetCompletionRoutine( Irp, 677 CdPnpCompletionRoutine, 678 &Event, 679 TRUE, 680 TRUE, 681 TRUE ); 682 683 // 684 // Send the request and wait. 685 // 686 687 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 688 689 if (Status == STATUS_PENDING) { 690 691 (VOID)KeWaitForSingleObject( &Event, 692 Executive, 693 KernelMode, 694 FALSE, 695 NULL ); 696 697 Status = Irp->IoStatus.Status; 698 } 699 700 // 701 // Now make our dismount happen. This may not vaporize the 702 // Vcb, of course, since there could be any number of handles 703 // outstanding since this is an out of band notification. 704 // 705 706 707 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); 708 709 // 710 // Release the Vcb if it could still remain. 711 // 712 713 if (VcbPresent) { 714 715 CdReleaseVcb( IrpContext, Vcb ); 716 } 717 else { 718 _Analysis_assume_lock_not_held_(Vcb->VcbResource); 719 } 720 721 CdReleaseCdData( IrpContext ); 722 723 // 724 // Cleanup our IrpContext and complete the IRP. 725 // 726 727 CdCompleteRequest( IrpContext, Irp, Status ); 728 729 return Status; 730 } 731 732 _Requires_lock_held_(_Global_critical_region_) 733 _Releases_nonreentrant_lock_(CdData.DataResource) 734 NTSTATUS 735 CdPnpCancelRemove ( 736 _Inout_ PIRP_CONTEXT IrpContext, 737 _Inout_ PIRP Irp, 738 _Inout_ PVCB Vcb 739 ) 740 741 /*++ 742 743 Routine Description: 744 745 This routine handles the PnP cancel remove operation. This is our 746 notification that a previously proposed remove (query) was eventually 747 vetoed by a component. The filesystem is responsible for cleaning up 748 and getting ready for more IO. 749 750 Arguments: 751 752 Irp - Supplies the Irp to process 753 754 Vcb - Supplies the volume being removed. 755 756 Return Value: 757 758 NTSTATUS - The return status for the operation 759 760 --*/ 761 762 { 763 NTSTATUS Status; 764 765 PAGED_CODE(); 766 767 ASSERT_EXCLUSIVE_CDDATA; 768 769 // 770 // CANCEL - a previous QUERY has been rescinded as a result 771 // of someone vetoing. Since PnP cannot figure out who may 772 // have gotten the QUERY (think about it: stacked drivers), 773 // we must expect to deal with getting a CANCEL without having 774 // seen the QUERY. 775 // 776 // For CDFS, this is quite easy. In fact, we can't get a 777 // CANCEL if the underlying drivers succeeded the QUERY since 778 // we disconnect the Vpb on our dismount initiation. This is 779 // actually pretty important because if PnP could get to us 780 // after the disconnect we'd be thoroughly unsynchronized 781 // with respect to the Vcb getting torn apart - merely referencing 782 // the volume device object is insufficient to keep us intact. 783 // 784 785 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); 786 CdReleaseCdData( IrpContext); 787 788 // 789 // Unlock the volume. This is benign if we never had seen 790 // a QUERY. 791 // 792 793 (VOID) CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); 794 795 CdReleaseVcb( IrpContext, Vcb ); 796 797 // 798 // Send the request. The underlying driver will complete the 799 // IRP. Since we don't need to be in the way, simply ellide 800 // ourselves out of the IRP stack. 801 // 802 803 IoSkipCurrentIrpStackLocation( Irp ); 804 805 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 806 807 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); 808 809 return Status; 810 } 811 812 813 // 814 // Local support routine 815 // 816 817 NTSTATUS 818 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 819 CdPnpCompletionRoutine ( 820 _In_ PDEVICE_OBJECT DeviceObject, 821 _In_ PIRP Irp, 822 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 823 ) 824 { 825 PKEVENT Event = (PKEVENT) Contxt; 826 _Analysis_assume_(Contxt != NULL); 827 828 KeSetEvent( Event, 0, FALSE ); 829 830 return STATUS_MORE_PROCESSING_REQUIRED; 831 832 UNREFERENCED_PARAMETER( DeviceObject ); 833 UNREFERENCED_PARAMETER( Irp ); 834 UNREFERENCED_PARAMETER( Contxt ); 835 } 836 837 838