Source code of Windows XP (NT5)
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.

721 lines
20 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Pnp.c
  5. Abstract:
  6. This module implements the Pnp routines for Ntfs called by the
  7. dispatch driver.
  8. Author:
  9. Gary Kimura [GaryKi] 29-Aug-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (NTFS_BUG_CHECK_PNP)
  17. //
  18. // The local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_PNP)
  21. //
  22. // Local procedure prototypes
  23. //
  24. NTSTATUS
  25. NtfsCommonPnp (
  26. IN PIRP_CONTEXT IrpContext,
  27. IN PIRP *Irp
  28. );
  29. NTSTATUS
  30. NtfsPnpCompletionRoutine (
  31. IN PDEVICE_OBJECT DeviceObject,
  32. IN PIRP Irp,
  33. IN PNTFS_COMPLETION_CONTEXT CompletionContext
  34. );
  35. VOID
  36. NtfsPerformSurpriseRemoval(
  37. IN PIRP_CONTEXT IrpContext,
  38. IN PVCB Vcb
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text(PAGE, NtfsCommonPnp)
  42. #pragma alloc_text(PAGE, NtfsFsdPnp)
  43. #pragma alloc_text(PAGE, NtfsPerformSurpriseRemoval)
  44. #endif
  45. NTSTATUS
  46. NtfsFsdPnp (
  47. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  48. IN PIRP Irp
  49. )
  50. /*++
  51. Routine Description:
  52. This routine implements the FSD entry point for plug and play (Pnp).
  53. Arguments:
  54. VolumeDeviceObject - Supplies the volume device object where the
  55. file exists
  56. Irp - Supplies the Irp being processed
  57. Return Value:
  58. NTSTATUS - The FSD status for the IRP
  59. --*/
  60. {
  61. NTSTATUS Status = STATUS_SUCCESS;
  62. TOP_LEVEL_CONTEXT TopLevelContext;
  63. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  64. PIRP_CONTEXT IrpContext = NULL;
  65. ASSERT_IRP( Irp );
  66. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  67. #ifdef NTFSPNPDBG
  68. if (NtfsDebugTraceLevel != 0) SetFlag( NtfsDebugTraceLevel, DEBUG_TRACE_PNP );
  69. #endif
  70. DebugTrace( +1, Dbg, ("NtfsFsdPnp\n") );
  71. //
  72. // Call the common Pnp routine
  73. //
  74. FsRtlEnterFileSystem();
  75. switch( IoGetCurrentIrpStackLocation( Irp )->MinorFunction ) {
  76. case IRP_MN_QUERY_REMOVE_DEVICE:
  77. case IRP_MN_REMOVE_DEVICE:
  78. case IRP_MN_CANCEL_REMOVE_DEVICE:
  79. case IRP_MN_SURPRISE_REMOVAL:
  80. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  81. break;
  82. default:
  83. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
  84. break;
  85. }
  86. do {
  87. try {
  88. //
  89. // We are either initiating this request or retrying it.
  90. //
  91. if (IrpContext == NULL) {
  92. //
  93. // Allocate and initialize the Irp.
  94. //
  95. NtfsInitializeIrpContext( Irp, TRUE, &IrpContext );
  96. //
  97. // Initialize the thread top level structure, if needed.
  98. //
  99. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  100. } else if (Status == STATUS_LOG_FILE_FULL) {
  101. NtfsCheckpointForLogFileFull( IrpContext );
  102. }
  103. Status = NtfsCommonPnp( IrpContext, &Irp );
  104. break;
  105. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  106. //
  107. // We had some trouble trying to perform the requested
  108. // operation, so we'll abort the I/O request with
  109. // the error status that we get back from the
  110. // execption code
  111. //
  112. Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
  113. }
  114. } while (Status == STATUS_CANT_WAIT ||
  115. Status == STATUS_LOG_FILE_FULL);
  116. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  117. FsRtlExitFileSystem();
  118. //
  119. // And return to our caller
  120. //
  121. DebugTrace( -1, Dbg, ("NtfsFsdPnp -> %08lx\n", Status) );
  122. return Status;
  123. }
  124. NTSTATUS
  125. NtfsCommonPnp (
  126. IN PIRP_CONTEXT IrpContext,
  127. IN PIRP *Irp
  128. )
  129. /*++
  130. Routine Description:
  131. This is the common routine for PnP called by the fsd thread.
  132. Arguments:
  133. Irp - Supplies the Irp to process. WARNING! THIS IRP HAS NO
  134. FILE OBJECT IN OUR IRP STACK LOCATION!!!
  135. Return Value:
  136. NTSTATUS - The return status for the operation
  137. --*/
  138. {
  139. NTSTATUS Status;
  140. NTSTATUS FlushStatus;
  141. PIO_STACK_LOCATION IrpSp;
  142. NTFS_COMPLETION_CONTEXT CompletionContext;
  143. PVOLUME_DEVICE_OBJECT OurDeviceObject;
  144. PVCB Vcb;
  145. BOOLEAN VcbAcquired = FALSE;
  146. BOOLEAN CheckpointAcquired = FALSE;
  147. BOOLEAN DecrementCloseCount = FALSE;
  148. #ifdef SYSCACHE_DEBUG
  149. ULONG SystemHandleCount = 0;
  150. #endif
  151. ASSERT_IRP_CONTEXT( IrpContext );
  152. ASSERT_IRP( *Irp );
  153. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  154. //
  155. // Get the current Irp stack location.
  156. //
  157. IrpSp = IoGetCurrentIrpStackLocation( *Irp );
  158. //
  159. // Find our Vcb. This is tricky since we have no file object in the Irp.
  160. //
  161. OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
  162. //
  163. // Make sure this device object really is big enough to be a volume device
  164. // object. If it isn't, we need to get out before we try to reference some
  165. // field that takes us past the end of an ordinary device object. Then we
  166. // check if it is actually one of ours, just to be perfectly paranoid.
  167. //
  168. if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
  169. NodeType(&OurDeviceObject->Vcb) != NTFS_NTC_VCB) {
  170. NtfsCompleteRequest( IrpContext, *Irp, STATUS_INVALID_PARAMETER );
  171. return STATUS_INVALID_PARAMETER;
  172. }
  173. Vcb = &OurDeviceObject->Vcb;
  174. KeInitializeEvent( &CompletionContext.Event, NotificationEvent, FALSE );
  175. //
  176. // Anyone who is flushing the volume or setting Vcb bits needs to get the
  177. // vcb exclusively.
  178. //
  179. switch ( IrpSp->MinorFunction ) {
  180. case IRP_MN_QUERY_REMOVE_DEVICE:
  181. case IRP_MN_SURPRISE_REMOVAL:
  182. //
  183. // Lock volume / dismount synchs with checkpoint - we need to do this first before
  184. // acquiring the vcb to preserve locking order since we're going to do a lock in
  185. // the query remove case and a dismount in the surprise removal
  186. //
  187. NtfsAcquireCheckpointSynchronization( IrpContext, Vcb );
  188. CheckpointAcquired = TRUE;
  189. // fall through
  190. case IRP_MN_REMOVE_DEVICE:
  191. case IRP_MN_CANCEL_REMOVE_DEVICE:
  192. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  193. VcbAcquired = TRUE;
  194. break;
  195. }
  196. try {
  197. switch ( IrpSp->MinorFunction ) {
  198. case IRP_MN_QUERY_REMOVE_DEVICE:
  199. DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE\n") );
  200. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  201. Status = STATUS_VOLUME_DISMOUNTED;
  202. break;
  203. }
  204. //
  205. // If we already know we don't want to dismount this volume, don't bother
  206. // flushing now. If there's a nonzero cleanup count, flushing won't get
  207. // the close count down to zero, so we might as well get out now.
  208. //
  209. #ifdef SYSCACHE_DEBUG
  210. if (Vcb->SyscacheScb != NULL) {
  211. SystemHandleCount = Vcb->SyscacheScb->CleanupCount;
  212. }
  213. if ((Vcb->CleanupCount > SystemHandleCount) ||
  214. #else
  215. if ((Vcb->CleanupCount > 0) ||
  216. #endif
  217. FlagOn(Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT)) {
  218. DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> cleanup count still %x \n", Vcb->CleanupCount) );
  219. //
  220. // We don't want the device to get removed or stopped if this volume has files
  221. // open. We'll fail this query, and we won't bother calling the driver(s) below us.
  222. //
  223. Status = STATUS_UNSUCCESSFUL;
  224. } else {
  225. //
  226. // We might dismount this volume soon, so let's try to flush and purge
  227. // everything we can right now.
  228. //
  229. FlushStatus = NtfsFlushVolume( IrpContext,
  230. Vcb,
  231. TRUE,
  232. TRUE,
  233. TRUE,
  234. FALSE );
  235. //
  236. // We need to make sure the cache manager is done with any lazy writes
  237. // that might be keeping the close count up. Since Cc might need to
  238. // close some streams, we need to release the vcb. We'd hate to have
  239. // the Vcb go away, so we'll bias the close count temporarily.
  240. //
  241. Vcb->CloseCount += 1;
  242. DecrementCloseCount = TRUE;
  243. NtfsReleaseVcb( IrpContext, Vcb );
  244. CcWaitForCurrentLazyWriterActivity();
  245. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  246. Vcb->CloseCount -= 1;
  247. DecrementCloseCount = FALSE;
  248. //
  249. // Since we dropped the Vcb, we need to redo any tests we've done.
  250. //
  251. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  252. Status = STATUS_VOLUME_DISMOUNTED;
  253. break;
  254. }
  255. #ifdef SYSCACHE_DEBUG
  256. if (Vcb->SyscacheScb != NULL) {
  257. SystemHandleCount = Vcb->SyscacheScb->CleanupCount;
  258. }
  259. if ((Vcb->CleanupCount > SystemHandleCount) ||
  260. #else
  261. if ((Vcb->CleanupCount > 0) ||
  262. #endif
  263. FlagOn(Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT)) {
  264. Status = STATUS_UNSUCCESSFUL;
  265. break;
  266. }
  267. if ((Vcb->CloseCount - (Vcb->SystemFileCloseCount + Vcb->QueuedCloseCount)) > 0) {
  268. DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> %x user files still open \n", (Vcb->CloseCount - Vcb->SystemFileCloseCount)) );
  269. //
  270. // We don't want the device to get removed or stopped if this volume has files
  271. // open. We'll fail this query, and we won't bother calling the driver(s) below us.
  272. //
  273. Status = STATUS_UNSUCCESSFUL;
  274. } else {
  275. //
  276. // We've already done all we can to clear up any open files, so there's
  277. // no point in retrying if this lock volume fails. We'll just tell
  278. // NtfsLockVolumeInternal we're already retrying.
  279. //
  280. ULONG Retrying = 1;
  281. DebugTrace( 0, Dbg, ("IRP_MN_QUERY_REMOVE_DEVICE --> No user files, Locking volume \n") );
  282. Status = NtfsLockVolumeInternal( IrpContext,
  283. Vcb,
  284. ((PFILE_OBJECT) 1),
  285. &Retrying );
  286. //
  287. // Remember not to send any irps to the target device now.
  288. //
  289. if (NT_SUCCESS( Status )) {
  290. ASSERT_EXCLUSIVE_RESOURCE( &Vcb->Resource );
  291. SetFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
  292. }
  293. }
  294. }
  295. break;
  296. case IRP_MN_REMOVE_DEVICE:
  297. DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE\n") );
  298. //
  299. // If remove_device is preceded by query_remove, we treat this just
  300. // like a cancel_remove and unlock the volume and pass the irp to
  301. // the driver(s) below the filesystem.
  302. //
  303. if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
  304. DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE --> Volume locked \n") );
  305. Status = NtfsUnlockVolumeInternal( IrpContext, Vcb );
  306. } else {
  307. //
  308. // The only other possibility is for remove_device to be prededed
  309. // by surprise_remove, in which case we treat this as a failed verify.
  310. //
  311. // **** TODO **** ADD CODE TO TREAT THIS LIKE A FAILED VERIFY
  312. DebugTrace( 0, Dbg, ("IRP_MN_REMOVE_DEVICE --> Volume _not_ locked \n") );
  313. Status = STATUS_SUCCESS;
  314. }
  315. break;
  316. case IRP_MN_SURPRISE_REMOVAL:
  317. DebugTrace( 0, Dbg, ("IRP_MN_SURPRISE_REMOVAL\n") );
  318. //
  319. // For surprise removal, we call the driver(s) below us first, then do
  320. // our processing. Let us also remember that we can't send any more
  321. // IRPs to the target device.
  322. //
  323. SetFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
  324. Status = STATUS_SUCCESS;
  325. break;
  326. case IRP_MN_CANCEL_REMOVE_DEVICE:
  327. Status = STATUS_SUCCESS;
  328. break;
  329. default:
  330. DebugTrace( 0, Dbg, ("Some other PnP IRP_MN_ %x\n", IrpSp->MinorFunction) );
  331. Status = STATUS_SUCCESS;
  332. break;
  333. }
  334. //
  335. // We only pass this irp down if we didn't have some reason to fail it ourselves.
  336. // We want to keep the IrpContext around for our own cleanup.
  337. //
  338. if (!NT_SUCCESS( Status )) {
  339. NtfsCompleteRequest( NULL, *Irp, Status );
  340. try_return( NOTHING );
  341. }
  342. //
  343. // Get the next stack location, and copy over the stack location
  344. //
  345. IoCopyCurrentIrpStackLocationToNext( *Irp );
  346. //
  347. // Set up the completion routine
  348. //
  349. CompletionContext.IrpContext = IrpContext;
  350. IoSetCompletionRoutine( *Irp,
  351. NtfsPnpCompletionRoutine,
  352. &CompletionContext,
  353. TRUE,
  354. TRUE,
  355. TRUE );
  356. //
  357. // Send the request to the driver(s) below us. - We don't own it anymore
  358. // so null it out
  359. //
  360. Status = IoCallDriver( Vcb->TargetDeviceObject, *Irp );
  361. *Irp = NULL;
  362. //
  363. // Wait for the driver to definitely complete
  364. //
  365. if (Status == STATUS_PENDING) {
  366. KeWaitForSingleObject( &CompletionContext.Event,
  367. Executive,
  368. KernelMode,
  369. FALSE,
  370. NULL );
  371. KeClearEvent( &CompletionContext.Event );
  372. }
  373. //
  374. // Post processing - these are items that need to be done after the lower
  375. // storage stack has processed the request.
  376. //
  377. switch (IrpContext->MinorFunction) {
  378. case IRP_MN_SURPRISE_REMOVAL:
  379. //
  380. // Start the tear-down process irrespective of the status
  381. // the driver below us sent back. There's no turning back here.
  382. //
  383. if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  384. NtfsPerformSurpriseRemoval( IrpContext, Vcb );
  385. }
  386. break;
  387. case IRP_MN_CANCEL_REMOVE_DEVICE:
  388. //
  389. // Since we cancelled and have told the driver we can now safely unlock
  390. // the volume and send ioctls to the drive (unlock media)
  391. //
  392. ClearFlag( Vcb->VcbState, VCB_STATE_TARGET_DEVICE_STOPPED );
  393. if (FlagOn( Vcb->VcbState, VCB_STATE_EXPLICIT_LOCK )) {
  394. DebugTrace( 0, Dbg, ("IRP_MN_CANCEL_REMOVE_DEVICE --> Volume locked \n") );
  395. NtfsUnlockVolumeInternal( IrpContext, Vcb );
  396. }
  397. break;
  398. }
  399. try_exit: NOTHING;
  400. } finally {
  401. if (DecrementCloseCount) {
  402. if (!VcbAcquired) {
  403. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  404. VcbAcquired = TRUE;
  405. }
  406. Vcb->CloseCount -= 1;
  407. }
  408. if (VcbAcquired) {
  409. //
  410. // All 4 paths query / remove / surprise remove / cancel remove
  411. // come through here. For the 3 except query we want the vcb to go away
  412. // if possible. In the query remove path - dismount won't be complete
  413. // even if the close count is 0 (since the dismount is incomplete)
  414. // so this will only release
  415. //
  416. NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IrpContext->MajorFunction, NULL );
  417. }
  418. if (CheckpointAcquired) {
  419. NtfsReleaseCheckpointSynchronization( IrpContext, Vcb );
  420. }
  421. }
  422. //
  423. // Cleanup our IrpContext; The underlying driver completed the Irp.
  424. //
  425. DebugTrace( -1, Dbg, ("NtfsCommonPnp -> %08lx\n", Status ) );
  426. NtfsCompleteRequest( IrpContext, NULL, Status );
  427. return Status;
  428. }
  429. //
  430. // Local support routine
  431. //
  432. NTSTATUS
  433. NtfsPnpCompletionRoutine (
  434. IN PDEVICE_OBJECT DeviceObject,
  435. IN PIRP Irp,
  436. IN PNTFS_COMPLETION_CONTEXT CompletionContext
  437. )
  438. {
  439. PIO_STACK_LOCATION IrpSp;
  440. PIRP_CONTEXT IrpContext;
  441. PVOLUME_DEVICE_OBJECT OurDeviceObject;
  442. PVCB Vcb;
  443. BOOLEAN VcbAcquired = FALSE;
  444. ASSERT_IRP( Irp );
  445. IrpContext = CompletionContext->IrpContext;
  446. ASSERT_IRP_CONTEXT( IrpContext );
  447. //
  448. // Get the current Irp stack location.
  449. //
  450. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  451. //
  452. // Find our Vcb. This is tricky since we have no file object in the Irp.
  453. //
  454. OurDeviceObject = (PVOLUME_DEVICE_OBJECT) DeviceObject;
  455. //
  456. // Make sure this device object really is big enough to be a volume device
  457. // object. If it isn't, we need to get out before we try to reference some
  458. // field that takes us past the end of an ordinary device object. Then we
  459. // check if it is actually one of ours, just to be perfectly paranoid.
  460. //
  461. if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
  462. NodeType(&OurDeviceObject->Vcb) != NTFS_NTC_VCB) {
  463. return STATUS_INVALID_PARAMETER;
  464. }
  465. Vcb = &OurDeviceObject->Vcb;
  466. KeSetEvent( &CompletionContext->Event, 0, FALSE );
  467. //
  468. // Propagate the Irp pending state.
  469. //
  470. if (Irp->PendingReturned) {
  471. IoMarkIrpPending( Irp );
  472. }
  473. return STATUS_SUCCESS;
  474. }
  475. //
  476. // Local utility routine
  477. //
  478. VOID
  479. NtfsPerformSurpriseRemoval (
  480. IN PIRP_CONTEXT IrpContext,
  481. IN PVCB Vcb
  482. )
  483. /*++
  484. Performs further processing on SURPRISE_REMOVAL notifications.
  485. --*/
  486. {
  487. ASSERT(ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ));
  488. //
  489. // Flush and purge and mark all files as dismounted.
  490. // Since there may be outstanding handles, we could still see any
  491. // operation (read, write, set info, etc.) happen for files on the
  492. // volume after surprise_remove. Since all the files will be marked
  493. // for dismount, we will fail these operations gracefully. All
  494. // operations besides cleanup & close on the volume will fail from
  495. // this time on.
  496. //
  497. if (!FlagOn( Vcb->VcbState, VCB_STATE_DISALLOW_DISMOUNT )) {
  498. (VOID)NtfsFlushVolume( IrpContext,
  499. Vcb,
  500. FALSE,
  501. TRUE,
  502. TRUE,
  503. TRUE );
  504. NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE, NULL );
  505. }
  506. return;
  507. }