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 FAT called by 12 the dispatch driver. 13 14 15 --*/ 16 17 #include "fatprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (FAT_BUG_CHECK_PNP) 24 25 #define Dbg (DEBUG_TRACE_PNP) 26 27 _Requires_lock_held_(_Global_critical_region_) 28 _Requires_lock_held_(FatData.Resource) 29 NTSTATUS 30 FatPnpQueryRemove ( 31 PIRP_CONTEXT IrpContext, 32 PIRP Irp, 33 PVCB Vcb 34 ); 35 36 _Requires_lock_held_(_Global_critical_region_) 37 NTSTATUS 38 FatPnpRemove ( 39 PIRP_CONTEXT IrpContext, 40 PIRP Irp, 41 PVCB Vcb 42 ); 43 44 _Requires_lock_held_(_Global_critical_region_) 45 NTSTATUS 46 FatPnpSurpriseRemove ( 47 PIRP_CONTEXT IrpContext, 48 PIRP Irp, 49 PVCB Vcb 50 ); 51 52 _Requires_lock_held_(_Global_critical_region_) 53 NTSTATUS 54 FatPnpCancelRemove ( 55 PIRP_CONTEXT IrpContext, 56 PIRP Irp, 57 PVCB Vcb 58 ); 59 60 IO_COMPLETION_ROUTINE FatPnpCompletionRoutine; 61 62 NTSTATUS 63 NTAPI 64 FatPnpCompletionRoutine ( 65 _In_ PDEVICE_OBJECT DeviceObject, 66 _In_ PIRP Irp, 67 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 68 ); 69 70 #ifdef ALLOC_PRAGMA 71 #pragma alloc_text(PAGE, FatCommonPnp) 72 #pragma alloc_text(PAGE, FatFsdPnp) 73 #pragma alloc_text(PAGE, FatPnpCancelRemove) 74 #pragma alloc_text(PAGE, FatPnpQueryRemove) 75 #pragma alloc_text(PAGE, FatPnpRemove) 76 #pragma alloc_text(PAGE, FatPnpSurpriseRemove) 77 #endif 78 79 80 _Function_class_(IRP_MJ_PNP) 81 _Function_class_(DRIVER_DISPATCH) 82 NTSTATUS 83 NTAPI 84 FatFsdPnp ( 85 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, 86 _Inout_ PIRP Irp 87 ) 88 89 /*++ 90 91 Routine Description: 92 93 This routine implements the FSD part of PnP operations 94 95 Arguments: 96 97 VolumeDeviceObject - Supplies the volume device object where the 98 file exists 99 100 Irp - Supplies the Irp being processed 101 102 Return Value: 103 104 NTSTATUS - The FSD status for the IRP 105 106 --*/ 107 108 { 109 NTSTATUS Status; 110 PIRP_CONTEXT IrpContext = NULL; 111 112 BOOLEAN TopLevel; 113 BOOLEAN Wait; 114 115 PAGED_CODE(); 116 117 DebugTrace(+1, Dbg, "FatFsdPnp\n", 0); 118 119 FsRtlEnterFileSystem(); 120 121 TopLevel = FatIsIrpTopLevel( Irp ); 122 123 _SEH2_TRY { 124 125 // 126 // We expect there to never be a fileobject, in which case we will always 127 // wait. Since at the moment we don't have any concept of pending Pnp 128 // operations, this is a bit nitpicky. 129 // 130 131 if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) { 132 133 Wait = TRUE; 134 135 } else { 136 137 Wait = CanFsdWait( Irp ); 138 } 139 140 IrpContext = FatCreateIrpContext( Irp, Wait ); 141 142 Status = FatCommonPnp( IrpContext, Irp ); 143 144 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { 145 146 // 147 // We had some trouble trying to perform the requested 148 // operation, so we'll abort the I/O request with 149 // the error status that we get back from the 150 // execption code 151 // 152 153 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); 154 } _SEH2_END; 155 156 if (TopLevel) { IoSetTopLevelIrp( NULL ); } 157 158 FsRtlExitFileSystem(); 159 160 // 161 // And return to our caller 162 // 163 164 DebugTrace(-1, Dbg, "FatFsdPnp -> %08lx\n", Status); 165 166 UNREFERENCED_PARAMETER( VolumeDeviceObject ); 167 168 return Status; 169 } 170 171 172 _Requires_lock_held_(_Global_critical_region_) 173 NTSTATUS 174 FatCommonPnp ( 175 IN PIRP_CONTEXT IrpContext, 176 IN PIRP Irp 177 ) 178 179 /*++ 180 181 Routine Description: 182 183 This is the common routine for doing PnP operations called 184 by both the fsd and fsp threads 185 186 Arguments: 187 188 Irp - Supplies the Irp to process 189 190 Return Value: 191 192 NTSTATUS - The return status for the operation 193 194 --*/ 195 196 { 197 NTSTATUS Status; 198 199 PIO_STACK_LOCATION IrpSp; 200 201 PVOLUME_DEVICE_OBJECT OurDeviceObject; 202 PVCB Vcb; 203 204 PAGED_CODE(); 205 206 // 207 // Force everything to wait. 208 // 209 210 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); 211 212 // 213 // Get the current Irp stack location. 214 // 215 216 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 217 218 // 219 // Find our Vcb. This is tricky since we have no file object in the Irp. 220 // 221 222 OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject; 223 224 // 225 // Take the global lock to synchronise against volume teardown. 226 // 227 228 #ifdef _MSC_VER 229 #pragma prefast( push ) 230 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) 231 #pragma prefast( disable: 28193, "this will always wait" ) 232 #endif 233 234 FatAcquireExclusiveGlobal( IrpContext ); 235 236 #ifdef _MSC_VER 237 #pragma prefast( pop ) 238 #endif 239 240 // 241 // Make sure this device object really is big enough to be a volume device 242 // object. If it isn't, we need to get out before we try to reference some 243 // field that takes us past the end of an ordinary device object. 244 // 245 246 #ifdef _MSC_VER 247 #pragma prefast( suppress: 28175, "touching Size is ok for a filesystem" ) 248 #endif 249 if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || 250 NodeType( &OurDeviceObject->Vcb ) != FAT_NTC_VCB) { 251 252 // 253 // We were called with something we don't understand. 254 // 255 256 FatReleaseGlobal( IrpContext ); 257 258 Status = STATUS_INVALID_PARAMETER; 259 FatCompleteRequest( IrpContext, Irp, Status ); 260 return Status; 261 } 262 263 Vcb = &OurDeviceObject->Vcb; 264 265 // 266 // Case on the minor code. 267 // 268 269 switch ( IrpSp->MinorFunction ) { 270 271 case IRP_MN_QUERY_REMOVE_DEVICE: 272 273 Status = FatPnpQueryRemove( IrpContext, Irp, Vcb ); 274 break; 275 276 case IRP_MN_SURPRISE_REMOVAL: 277 278 Status = FatPnpSurpriseRemove( IrpContext, Irp, Vcb ); 279 break; 280 281 case IRP_MN_REMOVE_DEVICE: 282 283 Status = FatPnpRemove( IrpContext, Irp, Vcb ); 284 break; 285 286 case IRP_MN_CANCEL_REMOVE_DEVICE: 287 288 Status = FatPnpCancelRemove( IrpContext, Irp, Vcb ); 289 break; 290 291 default: 292 293 FatReleaseGlobal( IrpContext ); 294 295 // 296 // Just pass the IRP on. As we do not need to be in the 297 // way on return, ellide ourselves out of the stack. 298 // 299 300 IoSkipCurrentIrpStackLocation( Irp ); 301 302 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 303 304 // 305 // Cleanup our Irp Context. The driver has completed the Irp. 306 // 307 308 FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); 309 310 break; 311 } 312 313 return Status; 314 } 315 316 317 VOID 318 FatPnpAdjustVpbRefCount( 319 IN PVCB Vcb, 320 IN ULONG Delta 321 ) 322 { 323 KIRQL OldIrql; 324 325 IoAcquireVpbSpinLock( &OldIrql); 326 Vcb->Vpb->ReferenceCount += Delta; 327 IoReleaseVpbSpinLock( OldIrql); 328 } 329 330 _Requires_lock_held_(_Global_critical_region_) 331 _Requires_lock_held_(FatData.Resource) 332 NTSTATUS 333 FatPnpQueryRemove ( 334 PIRP_CONTEXT IrpContext, 335 PIRP Irp, 336 PVCB Vcb 337 ) 338 339 /*++ 340 341 Routine Description: 342 343 This routine handles the PnP query remove operation. The filesystem 344 is responsible for answering whether there are any reasons it sees 345 that the volume can not go away (and the device removed). Initiation 346 of the dismount begins when we answer yes to this question. 347 348 Query will be followed by a Cancel or Remove. 349 350 Arguments: 351 352 Irp - Supplies the Irp to process 353 354 Vcb - Supplies the volume being queried. 355 356 Return Value: 357 358 NTSTATUS - The return status for the operation 359 360 --*/ 361 362 { 363 NTSTATUS Status = STATUS_SUCCESS; 364 KEVENT Event; 365 BOOLEAN VcbDeleted = FALSE; 366 BOOLEAN GlobalHeld = TRUE; 367 368 PAGED_CODE(); 369 370 // 371 // Having said yes to a QUERY, any communication with the 372 // underlying storage stack is undefined (and may block) 373 // until the bounding CANCEL or REMOVE is sent. 374 // 375 376 FatAcquireExclusiveVcb( IrpContext, Vcb ); 377 378 FatReleaseGlobal( IrpContext); 379 GlobalHeld = FALSE; 380 381 _SEH2_TRY { 382 383 Status = FatLockVolumeInternal( IrpContext, Vcb, NULL ); 384 385 // 386 // Drop an additional reference on the Vpb so that the volume cannot be 387 // torn down when we drop all the locks below. 388 // 389 390 FatPnpAdjustVpbRefCount( Vcb, 1); 391 392 // 393 // Drop and reacquire the resources in the right order. 394 // 395 396 FatReleaseVcb( IrpContext, Vcb ); 397 398 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); 399 400 #ifdef _MSC_VER 401 #pragma prefast( push ) 402 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) 403 #pragma prefast( disable: 28193, "this will always wait" ) 404 #endif 405 406 FatAcquireExclusiveGlobal( IrpContext ); 407 GlobalHeld = TRUE; 408 409 #ifdef _MSC_VER 410 #pragma prefast( pop ) 411 #endif 412 413 FatAcquireExclusiveVcb( IrpContext, Vcb ); 414 415 // 416 // Drop the reference we added above. 417 // 418 419 FatPnpAdjustVpbRefCount( Vcb, (ULONG)-1); 420 421 if (NT_SUCCESS( Status )) { 422 423 // 424 // With the volume held locked, note that we must finalize as much 425 // as possible right now. 426 // 427 428 FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush ); 429 430 // 431 // We need to pass this down before starting the dismount, which 432 // could disconnect us immediately from the stack. 433 // 434 435 // 436 // Get the next stack location, and copy over the stack location 437 // 438 439 IoCopyCurrentIrpStackLocationToNext( Irp ); 440 441 // 442 // Set up the completion routine 443 // 444 445 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 446 IoSetCompletionRoutine( Irp, 447 FatPnpCompletionRoutine, 448 &Event, 449 TRUE, 450 TRUE, 451 TRUE ); 452 453 // 454 // Send the request and wait. 455 // 456 457 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 458 459 if (Status == STATUS_PENDING) { 460 461 KeWaitForSingleObject( &Event, 462 Executive, 463 KernelMode, 464 FALSE, 465 NULL ); 466 467 Status = Irp->IoStatus.Status; 468 } 469 470 // 471 // Now if no one below us failed already, initiate the dismount 472 // on this volume, make it go away. PnP needs to see our internal 473 // streams close and drop their references to the target device. 474 // 475 // Since we were able to lock the volume, we are guaranteed to 476 // move this volume into dismount state and disconnect it from 477 // the underlying storage stack. The force on our part is actually 478 // unnecesary, though complete. 479 // 480 // What is not strictly guaranteed, though, is that the closes 481 // for the metadata streams take effect synchronously underneath 482 // of this call. This would leave references on the target device 483 // even though we are disconnected! 484 // 485 486 if (NT_SUCCESS( Status )) { 487 488 VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); 489 490 NT_ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad ); 491 492 } 493 } 494 495 } _SEH2_FINALLY { 496 497 // 498 // Release the Vcb if it could still remain. 499 // 500 501 if (!VcbDeleted) { 502 503 FatReleaseVcb( IrpContext, Vcb ); 504 } 505 506 if (GlobalHeld) { 507 508 FatReleaseGlobal( IrpContext ); 509 } 510 } _SEH2_END; 511 512 // 513 // Cleanup our IrpContext and complete the IRP if neccesary. 514 // 515 516 FatCompleteRequest( IrpContext, Irp, Status ); 517 518 return Status; 519 } 520 521 522 523 _Requires_lock_held_(_Global_critical_region_) 524 NTSTATUS 525 FatPnpRemove ( 526 PIRP_CONTEXT IrpContext, 527 PIRP Irp, 528 PVCB Vcb 529 ) 530 531 /*++ 532 533 Routine Description: 534 535 This routine handles the PnP remove operation. This is our notification 536 that the underlying storage device for the volume we have is gone, and 537 an excellent indication that the volume will never reappear. The filesystem 538 is responsible for initiation or completion of the dismount. 539 540 Arguments: 541 542 Irp - Supplies the Irp to process 543 544 Vcb - Supplies the volume being removed. 545 546 Return Value: 547 548 NTSTATUS - The return status for the operation 549 550 --*/ 551 552 { 553 NTSTATUS Status; 554 KEVENT Event; 555 BOOLEAN VcbDeleted = FALSE; 556 557 PAGED_CODE(); 558 559 // 560 // REMOVE - a storage device is now gone. We either got 561 // QUERY'd and said yes OR got a SURPRISE OR a storage 562 // stack failed to spin back up from a sleep/stop state 563 // (the only case in which this will be the first warning). 564 // 565 // Note that it is entirely unlikely that we will be around 566 // for a REMOVE in the first two cases, as we try to intiate 567 // dismount. 568 // 569 570 // 571 // Acquire the global resource so that we can try to vaporize 572 // the volume, and the vcb resource itself. 573 // 574 575 FatAcquireExclusiveVcb( IrpContext, Vcb ); 576 577 // 578 // The device will be going away. Remove our lock (benign 579 // if we never had it). 580 // 581 582 (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); 583 584 // 585 // We need to pass this down before starting the dismount, which 586 // could disconnect us immediately from the stack. 587 // 588 589 // 590 // Get the next stack location, and copy over the stack location 591 // 592 593 IoCopyCurrentIrpStackLocationToNext( Irp ); 594 595 // 596 // Set up the completion routine 597 // 598 599 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 600 IoSetCompletionRoutine( Irp, 601 FatPnpCompletionRoutine, 602 &Event, 603 TRUE, 604 TRUE, 605 TRUE ); 606 607 // 608 // Send the request and wait. 609 // 610 611 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 612 613 if (Status == STATUS_PENDING) { 614 615 KeWaitForSingleObject( &Event, 616 Executive, 617 KernelMode, 618 FALSE, 619 NULL ); 620 621 Status = Irp->IoStatus.Status; 622 } 623 624 _SEH2_TRY { 625 626 // 627 // Knock as many files down for this volume as we can. 628 // 629 630 FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); 631 632 // 633 // Now make our dismount happen. This may not vaporize the 634 // Vcb, of course, since there could be any number of handles 635 // outstanding if we were not preceeded by a QUERY. 636 // 637 // PnP will take care of disconnecting this stack if we 638 // couldn't get off of it immediately. 639 // 640 641 VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); 642 643 } _SEH2_FINALLY { 644 645 // 646 // Release the Vcb if it could still remain. 647 // 648 649 if (!VcbDeleted) { 650 651 FatReleaseVcb( IrpContext, Vcb ); 652 } 653 654 FatReleaseGlobal( IrpContext ); 655 } _SEH2_END; 656 657 // 658 // Cleanup our IrpContext and complete the IRP. 659 // 660 661 FatCompleteRequest( IrpContext, Irp, Status ); 662 663 return Status; 664 } 665 666 667 _Requires_lock_held_(_Global_critical_region_) 668 NTSTATUS 669 FatPnpSurpriseRemove ( 670 PIRP_CONTEXT IrpContext, 671 PIRP Irp, 672 PVCB Vcb 673 ) 674 675 /*++ 676 677 Routine Description: 678 679 This routine handles the PnP surprise remove operation. This is another 680 type of notification that the underlying storage device for the volume we 681 have is gone, and is excellent indication that the volume will never reappear. 682 The filesystem is responsible for initiation or completion the dismount. 683 684 For the most part, only "real" drivers care about the distinction of a 685 surprise remove, which is a result of our noticing that a user (usually) 686 physically reached into the machine and pulled something out. 687 688 Surprise will be followed by a Remove when all references have been shut down. 689 690 Arguments: 691 692 Irp - Supplies the Irp to process 693 694 Vcb - Supplies the volume being removed. 695 696 Return Value: 697 698 NTSTATUS - The return status for the operation 699 700 --*/ 701 702 { 703 NTSTATUS Status; 704 KEVENT Event; 705 BOOLEAN VcbDeleted = FALSE; 706 707 PAGED_CODE(); 708 709 // 710 // SURPRISE - a device was physically yanked away without 711 // any warning. This means external forces. 712 // 713 714 FatAcquireExclusiveVcb( IrpContext, Vcb ); 715 716 // 717 // We need to pass this down before starting the dismount, which 718 // could disconnect us immediately from the stack. 719 // 720 721 // 722 // Get the next stack location, and copy over the stack location 723 // 724 725 IoCopyCurrentIrpStackLocationToNext( Irp ); 726 727 // 728 // Set up the completion routine 729 // 730 731 KeInitializeEvent( &Event, NotificationEvent, FALSE ); 732 IoSetCompletionRoutine( Irp, 733 FatPnpCompletionRoutine, 734 &Event, 735 TRUE, 736 TRUE, 737 TRUE ); 738 739 // 740 // Send the request and wait. 741 // 742 743 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 744 745 if (Status == STATUS_PENDING) { 746 747 KeWaitForSingleObject( &Event, 748 Executive, 749 KernelMode, 750 FALSE, 751 NULL ); 752 753 Status = Irp->IoStatus.Status; 754 } 755 756 _SEH2_TRY { 757 758 // 759 // Knock as many files down for this volume as we can. 760 // 761 762 FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); 763 764 // 765 // Now make our dismount happen. This may not vaporize the 766 // Vcb, of course, since there could be any number of handles 767 // outstanding since this is an out of band notification. 768 // 769 770 VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); 771 772 } _SEH2_FINALLY { 773 774 // 775 // Release the Vcb if it could still remain. 776 // 777 778 if (!VcbDeleted) { 779 780 FatReleaseVcb( IrpContext, Vcb ); 781 } 782 783 FatReleaseGlobal( IrpContext ); 784 } _SEH2_END; 785 786 // 787 // Cleanup our IrpContext and complete the IRP. 788 // 789 790 FatCompleteRequest( IrpContext, Irp, Status ); 791 792 return Status; 793 } 794 795 796 _Requires_lock_held_(_Global_critical_region_) 797 NTSTATUS 798 FatPnpCancelRemove ( 799 PIRP_CONTEXT IrpContext, 800 PIRP Irp, 801 PVCB Vcb 802 ) 803 804 /*++ 805 806 Routine Description: 807 808 This routine handles the PnP cancel remove operation. This is our 809 notification that a previously proposed remove (query) was eventually 810 vetoed by a component. The filesystem is responsible for cleaning up 811 and getting ready for more IO. 812 813 Arguments: 814 815 Irp - Supplies the Irp to process 816 817 Vcb - Supplies the volume being removed. 818 819 Return Value: 820 821 NTSTATUS - The return status for the operation 822 823 --*/ 824 825 { 826 NTSTATUS Status = STATUS_SUCCESS; 827 828 PAGED_CODE(); 829 830 // 831 // CANCEL - a previous QUERY has been rescinded as a result 832 // of someone vetoing. Since PnP cannot figure out who may 833 // have gotten the QUERY (think about it: stacked drivers), 834 // we must expect to deal with getting a CANCEL without having 835 // seen the QUERY. 836 // 837 // For FAT, this is quite easy. In fact, we can't get a 838 // CANCEL if the underlying drivers succeeded the QUERY since 839 // we disconnect the Vpb on our dismount initiation. This is 840 // actually pretty important because if PnP could get to us 841 // after the disconnect we'd be thoroughly unsynchronized 842 // with respect to the Vcb getting torn apart - merely referencing 843 // the volume device object is insufficient to keep us intact. 844 // 845 846 FatAcquireExclusiveVcb( IrpContext, Vcb ); 847 FatReleaseGlobal( IrpContext); 848 849 // 850 // Unlock the volume. This is benign if we never had seen 851 // a QUERY. 852 // 853 854 (VOID)FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); 855 856 _SEH2_TRY { 857 858 // 859 // Send the request. The underlying driver will complete the 860 // IRP. Since we don't need to be in the way, simply ellide 861 // ourselves out of the IRP stack. 862 // 863 864 IoSkipCurrentIrpStackLocation( Irp ); 865 866 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); 867 } 868 _SEH2_FINALLY { 869 870 FatReleaseVcb( IrpContext, Vcb ); 871 } _SEH2_END; 872 873 FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); 874 875 return Status; 876 } 877 878 879 // 880 // Local support routine 881 // 882 883 NTSTATUS 884 NTAPI 885 FatPnpCompletionRoutine ( 886 _In_ PDEVICE_OBJECT DeviceObject, 887 _In_ PIRP Irp, 888 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt 889 ) 890 { 891 PKEVENT Event = (PKEVENT) Contxt; 892 893 KeSetEvent( Event, 0, FALSE ); 894 895 return STATUS_MORE_PROCESSING_REQUIRED; 896 897 UNREFERENCED_PARAMETER( DeviceObject ); 898 UNREFERENCED_PARAMETER( Contxt ); 899 UNREFERENCED_PARAMETER( Irp ); 900 } 901 902 903