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.

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