Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

878 lines
21 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. Pnp.c
  5. Abstract:
  6. This module implements the Plug and Play routines for FAT called by
  7. the dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Dan Lovinger [DanLo] 23-Jul-1997
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "FatProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (FAT_BUG_CHECK_PNP)
  19. #define Dbg (DEBUG_TRACE_PNP)
  20. NTSTATUS
  21. FatPnpQueryRemove (
  22. PIRP_CONTEXT IrpContext,
  23. PIRP Irp,
  24. PVCB Vcb
  25. );
  26. NTSTATUS
  27. FatPnpRemove (
  28. PIRP_CONTEXT IrpContext,
  29. PIRP Irp,
  30. PVCB Vcb
  31. );
  32. NTSTATUS
  33. FatPnpSurpriseRemove (
  34. PIRP_CONTEXT IrpContext,
  35. PIRP Irp,
  36. PVCB Vcb
  37. );
  38. NTSTATUS
  39. FatPnpCancelRemove (
  40. PIRP_CONTEXT IrpContext,
  41. PIRP Irp,
  42. PVCB Vcb
  43. );
  44. NTSTATUS
  45. FatPnpCompletionRoutine (
  46. IN PDEVICE_OBJECT DeviceObject,
  47. IN PIRP Irp,
  48. IN PVOID Contxt
  49. );
  50. #ifdef ALLOC_PRAGMA
  51. #pragma alloc_text(PAGE, FatCommonPnp)
  52. #pragma alloc_text(PAGE, FatFsdPnp)
  53. #pragma alloc_text(PAGE, FatPnpCancelRemove)
  54. #pragma alloc_text(PAGE, FatPnpQueryRemove)
  55. #pragma alloc_text(PAGE, FatPnpRemove)
  56. #pragma alloc_text(PAGE, FatPnpSurpriseRemove)
  57. #endif
  58. NTSTATUS
  59. FatFsdPnp (
  60. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  61. IN PIRP Irp
  62. )
  63. /*++
  64. Routine Description:
  65. This routine implements the FSD part of PnP operations
  66. Arguments:
  67. VolumeDeviceObject - Supplies the volume device object where the
  68. file exists
  69. Irp - Supplies the Irp being processed
  70. Return Value:
  71. NTSTATUS - The FSD status for the IRP
  72. --*/
  73. {
  74. NTSTATUS Status;
  75. PIRP_CONTEXT IrpContext = NULL;
  76. BOOLEAN TopLevel;
  77. BOOLEAN Wait;
  78. DebugTrace(+1, Dbg, "FatFsdPnp\n", 0);
  79. FsRtlEnterFileSystem();
  80. TopLevel = FatIsIrpTopLevel( Irp );
  81. try {
  82. //
  83. // We expect there to never be a fileobject, in which case we will always
  84. // wait. Since at the moment we don't have any concept of pending Pnp
  85. // operations, this is a bit nitpicky.
  86. //
  87. if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) {
  88. Wait = TRUE;
  89. } else {
  90. Wait = CanFsdWait( Irp );
  91. }
  92. IrpContext = FatCreateIrpContext( Irp, Wait );
  93. Status = FatCommonPnp( IrpContext, Irp );
  94. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  95. //
  96. // We had some trouble trying to perform the requested
  97. // operation, so we'll abort the I/O request with
  98. // the error status that we get back from the
  99. // execption code
  100. //
  101. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  102. }
  103. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  104. FsRtlExitFileSystem();
  105. //
  106. // And return to our caller
  107. //
  108. DebugTrace(-1, Dbg, "FatFsdPnp -> %08lx\n", Status);
  109. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  110. return Status;
  111. }
  112. NTSTATUS
  113. FatCommonPnp (
  114. IN PIRP_CONTEXT IrpContext,
  115. IN PIRP Irp
  116. )
  117. /*++
  118. Routine Description:
  119. This is the common routine for doing PnP operations called
  120. by both the fsd and fsp threads
  121. Arguments:
  122. Irp - Supplies the Irp to process
  123. Return Value:
  124. NTSTATUS - The return status for the operation
  125. --*/
  126. {
  127. NTSTATUS Status;
  128. PIO_STACK_LOCATION IrpSp;
  129. PVOLUME_DEVICE_OBJECT OurDeviceObject;
  130. PVCB Vcb;
  131. //
  132. // Force everything to wait.
  133. //
  134. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
  135. //
  136. // Get the current Irp stack location.
  137. //
  138. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  139. //
  140. // Find our Vcb. This is tricky since we have no file object in the Irp.
  141. //
  142. OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
  143. //
  144. // Take the global lock to synchronise against volume teardown.
  145. //
  146. FatAcquireExclusiveGlobal( IrpContext );
  147. //
  148. // Make sure this device object really is big enough to be a volume device
  149. // object. If it isn't, we need to get out before we try to reference some
  150. // field that takes us past the end of an ordinary device object.
  151. //
  152. if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
  153. NodeType( &OurDeviceObject->Vcb ) != FAT_NTC_VCB) {
  154. //
  155. // We were called with something we don't understand.
  156. //
  157. FatReleaseGlobal( IrpContext );
  158. Status = STATUS_INVALID_PARAMETER;
  159. FatCompleteRequest( IrpContext, Irp, Status );
  160. return Status;
  161. }
  162. Vcb = &OurDeviceObject->Vcb;
  163. //
  164. // Case on the minor code.
  165. //
  166. switch ( IrpSp->MinorFunction ) {
  167. case IRP_MN_QUERY_REMOVE_DEVICE:
  168. Status = FatPnpQueryRemove( IrpContext, Irp, Vcb );
  169. break;
  170. case IRP_MN_SURPRISE_REMOVAL:
  171. Status = FatPnpSurpriseRemove( IrpContext, Irp, Vcb );
  172. break;
  173. case IRP_MN_REMOVE_DEVICE:
  174. Status = FatPnpRemove( IrpContext, Irp, Vcb );
  175. break;
  176. case IRP_MN_CANCEL_REMOVE_DEVICE:
  177. Status = FatPnpCancelRemove( IrpContext, Irp, Vcb );
  178. break;
  179. default:
  180. FatReleaseGlobal( IrpContext );
  181. //
  182. // Just pass the IRP on. As we do not need to be in the
  183. // way on return, ellide ourselves out of the stack.
  184. //
  185. IoSkipCurrentIrpStackLocation( Irp );
  186. Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  187. //
  188. // Cleanup our Irp Context. The driver has completed the Irp.
  189. //
  190. FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  191. break;
  192. }
  193. return Status;
  194. }
  195. VOID
  196. FatPnpAdjustVpbRefCount(
  197. IN PVCB Vcb,
  198. IN ULONG Delta
  199. )
  200. {
  201. KIRQL OldIrql;
  202. IoAcquireVpbSpinLock( &OldIrql);
  203. Vcb->Vpb->ReferenceCount += Delta;
  204. IoReleaseVpbSpinLock( OldIrql);
  205. }
  206. NTSTATUS
  207. FatPnpQueryRemove (
  208. PIRP_CONTEXT IrpContext,
  209. PIRP Irp,
  210. PVCB Vcb
  211. )
  212. /*++
  213. Routine Description:
  214. This routine handles the PnP query remove operation. The filesystem
  215. is responsible for answering whether there are any reasons it sees
  216. that the volume can not go away (and the device removed). Initiation
  217. of the dismount begins when we answer yes to this question.
  218. Query will be followed by a Cancel or Remove.
  219. Arguments:
  220. Irp - Supplies the Irp to process
  221. Vcb - Supplies the volume being queried.
  222. Return Value:
  223. NTSTATUS - The return status for the operation
  224. --*/
  225. {
  226. NTSTATUS Status;
  227. KEVENT Event;
  228. BOOLEAN VcbDeleted = FALSE;
  229. BOOLEAN GlobalHeld = TRUE;
  230. //
  231. // Having said yes to a QUERY, any communication with the
  232. // underlying storage stack is undefined (and may block)
  233. // until the bounding CANCEL or REMOVE is sent.
  234. //
  235. FatAcquireExclusiveVcb( IrpContext, Vcb );
  236. FatReleaseGlobal( IrpContext);
  237. GlobalHeld = FALSE;
  238. try {
  239. Status = FatLockVolumeInternal( IrpContext, Vcb, NULL );
  240. //
  241. // Drop an additional reference on the Vpb so that the volume cannot be
  242. // torn down when we drop all the locks below.
  243. //
  244. FatPnpAdjustVpbRefCount( Vcb, 1);
  245. //
  246. // Drop and reacquire the resources in the right order.
  247. //
  248. FatReleaseVcb( IrpContext, Vcb );
  249. FatAcquireExclusiveGlobal( IrpContext );
  250. GlobalHeld = TRUE;
  251. FatAcquireExclusiveVcb( IrpContext, Vcb );
  252. //
  253. // Drop the reference we added above.
  254. //
  255. FatPnpAdjustVpbRefCount( Vcb, -1);
  256. if (NT_SUCCESS( Status )) {
  257. //
  258. // With the volume held locked, note that we must finalize as much
  259. // as possible right now.
  260. //
  261. FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush );
  262. //
  263. // We need to pass this down before starting the dismount, which
  264. // could disconnect us immediately from the stack.
  265. //
  266. //
  267. // Get the next stack location, and copy over the stack location
  268. //
  269. IoCopyCurrentIrpStackLocationToNext( Irp );
  270. //
  271. // Set up the completion routine
  272. //
  273. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  274. IoSetCompletionRoutine( Irp,
  275. FatPnpCompletionRoutine,
  276. &Event,
  277. TRUE,
  278. TRUE,
  279. TRUE );
  280. //
  281. // Send the request and wait.
  282. //
  283. Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  284. if (Status == STATUS_PENDING) {
  285. KeWaitForSingleObject( &Event,
  286. Executive,
  287. KernelMode,
  288. FALSE,
  289. NULL );
  290. Status = Irp->IoStatus.Status;
  291. }
  292. //
  293. // Now if no one below us failed already, initiate the dismount
  294. // on this volume, make it go away. PnP needs to see our internal
  295. // streams close and drop their references to the target device.
  296. //
  297. // Since we were able to lock the volume, we are guaranteed to
  298. // move this volume into dismount state and disconnect it from
  299. // the underlying storage stack. The force on our part is actually
  300. // unnecesary, though complete.
  301. //
  302. // What is not strictly guaranteed, though, is that the closes
  303. // for the metadata streams take effect synchronously underneath
  304. // of this call. This would leave references on the target device
  305. // even though we are disconnected!
  306. //
  307. if (NT_SUCCESS( Status )) {
  308. VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
  309. ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad );
  310. }
  311. }
  312. //
  313. // Release the Vcb if it could still remain.
  314. //
  315. // Note: if everything else succeeded and the Vcb is persistent because the
  316. // internal streams did not vaporize, we really need to pend this IRP off on
  317. // the side until the dismount is completed. I can't think of a reasonable
  318. // case (in FAT) where this would actually happen, though it might still need
  319. // to be implemented.
  320. //
  321. // The reason this is the case is that handles/fileobjects place a reference
  322. // on the device objects they overly. In the filesystem case, these references
  323. // are on our target devices. PnP correcly thinks that if references remain
  324. // on the device objects in the stack that someone has a handle, and that this
  325. // counts as a reason to not succeed the query - even though every interrogated
  326. // driver thinks that it is OK.
  327. //
  328. ASSERT( !(NT_SUCCESS( Status ) && !VcbDeleted ));
  329. } finally {
  330. if (!VcbDeleted) {
  331. FatReleaseVcb( IrpContext, Vcb );
  332. }
  333. if (GlobalHeld) {
  334. FatReleaseGlobal( IrpContext );
  335. }
  336. }
  337. //
  338. // Cleanup our IrpContext and complete the IRP if neccesary.
  339. //
  340. FatCompleteRequest( IrpContext, Irp, Status );
  341. return Status;
  342. }
  343. NTSTATUS
  344. FatPnpRemove (
  345. PIRP_CONTEXT IrpContext,
  346. PIRP Irp,
  347. PVCB Vcb
  348. )
  349. /*++
  350. Routine Description:
  351. This routine handles the PnP remove operation. This is our notification
  352. that the underlying storage device for the volume we have is gone, and
  353. an excellent indication that the volume will never reappear. The filesystem
  354. is responsible for initiation or completion of the dismount.
  355. Arguments:
  356. Irp - Supplies the Irp to process
  357. Vcb - Supplies the volume being removed.
  358. Return Value:
  359. NTSTATUS - The return status for the operation
  360. --*/
  361. {
  362. NTSTATUS Status;
  363. KEVENT Event;
  364. BOOLEAN VcbDeleted;
  365. //
  366. // REMOVE - a storage device is now gone. We either got
  367. // QUERY'd and said yes OR got a SURPRISE OR a storage
  368. // stack failed to spin back up from a sleep/stop state
  369. // (the only case in which this will be the first warning).
  370. //
  371. // Note that it is entirely unlikely that we will be around
  372. // for a REMOVE in the first two cases, as we try to intiate
  373. // dismount.
  374. //
  375. //
  376. // Acquire the global resource so that we can try to vaporize
  377. // the volume, and the vcb resource itself.
  378. //
  379. FatAcquireExclusiveVcb( IrpContext, Vcb );
  380. //
  381. // The device will be going away. Remove our lock (benign
  382. // if we never had it).
  383. //
  384. (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL );
  385. //
  386. // We need to pass this down before starting the dismount, which
  387. // could disconnect us immediately from the stack.
  388. //
  389. //
  390. // Get the next stack location, and copy over the stack location
  391. //
  392. IoCopyCurrentIrpStackLocationToNext( Irp );
  393. //
  394. // Set up the completion routine
  395. //
  396. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  397. IoSetCompletionRoutine( Irp,
  398. FatPnpCompletionRoutine,
  399. &Event,
  400. TRUE,
  401. TRUE,
  402. TRUE );
  403. //
  404. // Send the request and wait.
  405. //
  406. Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  407. if (Status == STATUS_PENDING) {
  408. KeWaitForSingleObject( &Event,
  409. Executive,
  410. KernelMode,
  411. FALSE,
  412. NULL );
  413. Status = Irp->IoStatus.Status;
  414. }
  415. try {
  416. //
  417. // Knock as many files down for this volume as we can.
  418. //
  419. FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );
  420. //
  421. // Now make our dismount happen. This may not vaporize the
  422. // Vcb, of course, since there could be any number of handles
  423. // outstanding if we were not preceeded by a QUERY.
  424. //
  425. // PnP will take care of disconnecting this stack if we
  426. // couldn't get off of it immediately.
  427. //
  428. VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
  429. } finally {
  430. //
  431. // Release the Vcb if it could still remain.
  432. //
  433. if (!VcbDeleted) {
  434. FatReleaseVcb( IrpContext, Vcb );
  435. }
  436. FatReleaseGlobal( IrpContext );
  437. }
  438. //
  439. // Cleanup our IrpContext and complete the IRP.
  440. //
  441. FatCompleteRequest( IrpContext, Irp, Status );
  442. return Status;
  443. }
  444. NTSTATUS
  445. FatPnpSurpriseRemove (
  446. PIRP_CONTEXT IrpContext,
  447. PIRP Irp,
  448. PVCB Vcb
  449. )
  450. /*++
  451. Routine Description:
  452. This routine handles the PnP surprise remove operation. This is another
  453. type of notification that the underlying storage device for the volume we
  454. have is gone, and is excellent indication that the volume will never reappear.
  455. The filesystem is responsible for initiation or completion the dismount.
  456. For the most part, only "real" drivers care about the distinction of a
  457. surprise remove, which is a result of our noticing that a user (usually)
  458. physically reached into the machine and pulled something out.
  459. Surprise will be followed by a Remove when all references have been shut down.
  460. Arguments:
  461. Irp - Supplies the Irp to process
  462. Vcb - Supplies the volume being removed.
  463. Return Value:
  464. NTSTATUS - The return status for the operation
  465. --*/
  466. {
  467. NTSTATUS Status;
  468. KEVENT Event;
  469. BOOLEAN VcbDeleted;
  470. //
  471. // SURPRISE - a device was physically yanked away without
  472. // any warning. This means external forces.
  473. //
  474. FatAcquireExclusiveVcb( IrpContext, Vcb );
  475. //
  476. // We need to pass this down before starting the dismount, which
  477. // could disconnect us immediately from the stack.
  478. //
  479. //
  480. // Get the next stack location, and copy over the stack location
  481. //
  482. IoCopyCurrentIrpStackLocationToNext( Irp );
  483. //
  484. // Set up the completion routine
  485. //
  486. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  487. IoSetCompletionRoutine( Irp,
  488. FatPnpCompletionRoutine,
  489. &Event,
  490. TRUE,
  491. TRUE,
  492. TRUE );
  493. //
  494. // Send the request and wait.
  495. //
  496. Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  497. if (Status == STATUS_PENDING) {
  498. KeWaitForSingleObject( &Event,
  499. Executive,
  500. KernelMode,
  501. FALSE,
  502. NULL );
  503. Status = Irp->IoStatus.Status;
  504. }
  505. try {
  506. //
  507. // Knock as many files down for this volume as we can.
  508. //
  509. FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );
  510. //
  511. // Now make our dismount happen. This may not vaporize the
  512. // Vcb, of course, since there could be any number of handles
  513. // outstanding since this is an out of band notification.
  514. //
  515. VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
  516. } finally {
  517. //
  518. // Release the Vcb if it could still remain.
  519. //
  520. if (!VcbDeleted) {
  521. FatReleaseVcb( IrpContext, Vcb );
  522. }
  523. FatReleaseGlobal( IrpContext );
  524. }
  525. //
  526. // Cleanup our IrpContext and complete the IRP.
  527. //
  528. FatCompleteRequest( IrpContext, Irp, Status );
  529. return Status;
  530. }
  531. NTSTATUS
  532. FatPnpCancelRemove (
  533. PIRP_CONTEXT IrpContext,
  534. PIRP Irp,
  535. PVCB Vcb
  536. )
  537. /*++
  538. Routine Description:
  539. This routine handles the PnP cancel remove operation. This is our
  540. notification that a previously proposed remove (query) was eventually
  541. vetoed by a component. The filesystem is responsible for cleaning up
  542. and getting ready for more IO.
  543. Arguments:
  544. Irp - Supplies the Irp to process
  545. Vcb - Supplies the volume being removed.
  546. Return Value:
  547. NTSTATUS - The return status for the operation
  548. --*/
  549. {
  550. NTSTATUS Status;
  551. //
  552. // CANCEL - a previous QUERY has been rescinded as a result
  553. // of someone vetoing. Since PnP cannot figure out who may
  554. // have gotten the QUERY (think about it: stacked drivers),
  555. // we must expect to deal with getting a CANCEL without having
  556. // seen the QUERY.
  557. //
  558. // For FAT, this is quite easy. In fact, we can't get a
  559. // CANCEL if the underlying drivers succeeded the QUERY since
  560. // we disconnect the Vpb on our dismount initiation. This is
  561. // actually pretty important because if PnP could get to us
  562. // after the disconnect we'd be thoroughly unsynchronized
  563. // with respect to the Vcb getting torn apart - merely referencing
  564. // the volume device object is insufficient to keep us intact.
  565. //
  566. FatAcquireExclusiveVcb( IrpContext, Vcb );
  567. FatReleaseGlobal( IrpContext);
  568. //
  569. // Unlock the volume. This is benign if we never had seen
  570. // a QUERY.
  571. //
  572. Status = FatUnlockVolumeInternal( IrpContext, Vcb, NULL );
  573. try {
  574. //
  575. // We must re-enable allocation support if we got through
  576. // the first stages of a QUERY_REMOVE; i.e., we decided we
  577. // could place a lock on the volume.
  578. //
  579. if (NT_SUCCESS( Status )) {
  580. FatSetupAllocationSupport( IrpContext, Vcb );
  581. }
  582. //
  583. // Send the request. The underlying driver will complete the
  584. // IRP. Since we don't need to be in the way, simply ellide
  585. // ourselves out of the IRP stack.
  586. //
  587. IoSkipCurrentIrpStackLocation( Irp );
  588. Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  589. }
  590. finally {
  591. FatReleaseVcb( IrpContext, Vcb );
  592. }
  593. FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  594. return Status;
  595. }
  596. //
  597. // Local support routine
  598. //
  599. NTSTATUS
  600. FatPnpCompletionRoutine (
  601. IN PDEVICE_OBJECT DeviceObject,
  602. IN PIRP Irp,
  603. IN PVOID Contxt
  604. )
  605. {
  606. PKEVENT Event = (PKEVENT) Contxt;
  607. KeSetEvent( Event, 0, FALSE );
  608. return STATUS_MORE_PROCESSING_REQUIRED;
  609. UNREFERENCED_PARAMETER( DeviceObject );
  610. UNREFERENCED_PARAMETER( Contxt );
  611. }