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.

877 lines
26 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. FspDisp.c
  5. Abstract:
  6. This module implements the main dispatch procedure/thread for the Ntfs
  7. Fsp
  8. Author:
  9. Gary Kimura [GaryKi] 21-May-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. #define BugCheckFileId (NTFS_BUG_CHECK_FSPDISP)
  14. #pragma alloc_text(PAGE, NtfsSpecialDispatch)
  15. #pragma alloc_text(PAGE, NtfsPostSpecial)
  16. //
  17. // Define our local debug trace level
  18. //
  19. #define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
  20. extern PETHREAD NtfsDesignatedTimeoutThread;
  21. VOID
  22. NtfsFspDispatch (
  23. IN PVOID Context
  24. )
  25. /*++
  26. Routine Description:
  27. This is the main FSP thread routine that is executed to receive
  28. and dispatch IRP requests. Each FSP thread begins its execution here.
  29. There is one thread created at system initialization time and subsequent
  30. threads created as needed.
  31. Arguments:
  32. Context - Supplies the thread id.
  33. Return Value:
  34. None - This routine never exits
  35. --*/
  36. {
  37. TOP_LEVEL_CONTEXT TopLevelContext;
  38. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  39. PIRP Irp;
  40. PIRP_CONTEXT IrpContext;
  41. PIO_STACK_LOCATION IrpSp;
  42. ULONG LogFileFullCount = 0;
  43. PVOLUME_DEVICE_OBJECT VolDo;
  44. BOOLEAN Retry;
  45. NTSTATUS Status = STATUS_SUCCESS;
  46. PCREATE_CONTEXT CreateContext;
  47. IrpContext = (PIRP_CONTEXT)Context;
  48. //
  49. // Reset the shared fields
  50. //
  51. InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
  52. InitializeListHead( &IrpContext->ExclusiveFcbList );
  53. Irp = IrpContext->OriginatingIrp;
  54. if (Irp != NULL) {
  55. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  56. }
  57. //
  58. // Now because we are the Fsp we will force the IrpContext to
  59. // indicate true on Wait.
  60. //
  61. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  62. //
  63. // If this request has an associated volume device object, remember it.
  64. //
  65. if ((Irp != NULL) &&
  66. (IrpSp->FileObject != NULL)) {
  67. VolDo = CONTAINING_RECORD( IrpSp->DeviceObject,
  68. VOLUME_DEVICE_OBJECT,
  69. DeviceObject );
  70. ObReferenceObject( IrpSp->DeviceObject );
  71. } else {
  72. VolDo = NULL;
  73. }
  74. //
  75. // Now case on the function code. For each major function code,
  76. // either call the appropriate FSP routine or case on the minor
  77. // function and then call the FSP routine. The FSP routine that
  78. // we call is responsible for completing the IRP, and not us.
  79. // That way the routine can complete the IRP and then continue
  80. // post processing as required. For example, a read can be
  81. // satisfied right away and then read can be done.
  82. //
  83. // We'll do all of the work within an exception handler that
  84. // will be invoked if ever some underlying operation gets into
  85. // trouble (e.g., if NtfsReadSectorsSync has trouble).
  86. //
  87. while (TRUE) {
  88. FsRtlEnterFileSystem();
  89. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  90. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
  91. ASSERT( ThreadTopLevelContext == &TopLevelContext );
  92. NtfsPostRequests += 1;
  93. do {
  94. //
  95. // If this is the initial try with this Irp Context, update the
  96. // top level Irp fields.
  97. //
  98. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  99. Retry = FALSE;
  100. try {
  101. //
  102. // Always clear the exception code in the IrpContext so we respond
  103. // correctly to errors encountered in the Fsp.
  104. //
  105. IrpContext->ExceptionStatus = 0;
  106. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP );
  107. //
  108. // See if we were posted due to a log file full condition, and
  109. // if so, then do a clean volume checkpoint if we are the
  110. // first ones to get there. If we see a different Lsn and do
  111. // not do the checkpoint, the worst that can happen is that we
  112. // will get posted again if the log file is still full.
  113. //
  114. if (IrpContext->LastRestartArea.QuadPart != 0) {
  115. NtfsCheckpointForLogFileFull( IrpContext );
  116. if (++LogFileFullCount >= 2) {
  117. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
  118. }
  119. }
  120. //
  121. // If we have an Irp then proceed with our normal processing.
  122. //
  123. if (Irp != NULL) {
  124. switch ( IrpContext->MajorFunction ) {
  125. //
  126. // For Create Operation,
  127. //
  128. case IRP_MJ_CREATE:
  129. //
  130. // Clear the efs flag so we complete the irp
  131. // Any poster will have set a completion routine to catch that
  132. //
  133. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_EFS_CREATE );
  134. CreateContext = IrpContext->Union.CreateContext;
  135. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_DASD_OPEN )) {
  136. Status = NtfsCommonVolumeOpen( IrpContext, Irp );
  137. } else {
  138. RtlZeroMemory( CreateContext, sizeof( CREATE_CONTEXT ) );
  139. Status = NtfsCommonCreate( IrpContext, Irp, CreateContext );
  140. }
  141. break;
  142. //
  143. // For close operations
  144. //
  145. case IRP_MJ_CLOSE:
  146. //
  147. // We should never post closes to this workqueue.
  148. //
  149. NtfsBugCheck( 0, 0, 0 );
  150. break;
  151. //
  152. // For read operations
  153. //
  154. case IRP_MJ_READ:
  155. (VOID) NtfsCommonRead( IrpContext, Irp, TRUE );
  156. break;
  157. //
  158. // For write operations,
  159. //
  160. case IRP_MJ_WRITE:
  161. (VOID) NtfsCommonWrite( IrpContext, Irp );
  162. break;
  163. //
  164. // For Query Information operations,
  165. //
  166. case IRP_MJ_QUERY_INFORMATION:
  167. (VOID) NtfsCommonQueryInformation( IrpContext, Irp );
  168. break;
  169. //
  170. // For Set Information operations,
  171. //
  172. case IRP_MJ_SET_INFORMATION:
  173. (VOID) NtfsCommonSetInformation( IrpContext, Irp );
  174. break;
  175. //
  176. // For Query EA operations,
  177. //
  178. case IRP_MJ_QUERY_EA:
  179. (VOID) NtfsCommonQueryEa( IrpContext, Irp );
  180. break;
  181. //
  182. // For Set EA operations,
  183. //
  184. case IRP_MJ_SET_EA:
  185. (VOID) NtfsCommonSetEa( IrpContext, Irp );
  186. break;
  187. //
  188. // For Flush buffers operations,
  189. //
  190. case IRP_MJ_FLUSH_BUFFERS:
  191. (VOID) NtfsCommonFlushBuffers( IrpContext, Irp );
  192. break;
  193. //
  194. // For Query Volume Information operations,
  195. //
  196. case IRP_MJ_QUERY_VOLUME_INFORMATION:
  197. (VOID) NtfsCommonQueryVolumeInfo( IrpContext, Irp );
  198. break;
  199. //
  200. // For Set Volume Information operations,
  201. //
  202. case IRP_MJ_SET_VOLUME_INFORMATION:
  203. (VOID) NtfsCommonSetVolumeInfo( IrpContext, Irp );
  204. break;
  205. //
  206. // For File Cleanup operations,
  207. //
  208. case IRP_MJ_CLEANUP:
  209. (VOID) NtfsCommonCleanup( IrpContext, Irp );
  210. break;
  211. //
  212. // For Directory Control operations,
  213. //
  214. case IRP_MJ_DIRECTORY_CONTROL:
  215. (VOID) NtfsCommonDirectoryControl( IrpContext, Irp );
  216. break;
  217. //
  218. // For File System Control operations,
  219. //
  220. case IRP_MJ_FILE_SYSTEM_CONTROL:
  221. (VOID) NtfsCommonFileSystemControl( IrpContext, Irp );
  222. break;
  223. //
  224. // For Lock Control operations,
  225. //
  226. case IRP_MJ_LOCK_CONTROL:
  227. (VOID) NtfsCommonLockControl( IrpContext, Irp );
  228. break;
  229. //
  230. // For Device Control operations,
  231. //
  232. case IRP_MJ_DEVICE_CONTROL:
  233. (VOID) NtfsCommonDeviceControl( IrpContext, Irp );
  234. break;
  235. //
  236. // For Query Security Information operations,
  237. //
  238. case IRP_MJ_QUERY_SECURITY:
  239. (VOID) NtfsCommonQuerySecurityInfo( IrpContext, Irp );
  240. break;
  241. //
  242. // For Set Security Information operations,
  243. //
  244. case IRP_MJ_SET_SECURITY:
  245. (VOID) NtfsCommonSetSecurityInfo( IrpContext, Irp );
  246. break;
  247. //
  248. // For Query Quota operations,
  249. //
  250. case IRP_MJ_QUERY_QUOTA:
  251. (VOID) NtfsCommonQueryQuota( IrpContext, Irp );
  252. break;
  253. //
  254. // For Set Quota operations,
  255. //
  256. case IRP_MJ_SET_QUOTA:
  257. (VOID) NtfsCommonSetQuota( IrpContext, Irp );
  258. break;
  259. //
  260. // For any other major operations, return an invalid
  261. // request.
  262. //
  263. default:
  264. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
  265. break;
  266. }
  267. //
  268. // Otherwise complete the request to clean up this Irp Context.
  269. //
  270. } else {
  271. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  272. IrpContext = NULL;
  273. }
  274. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  275. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  276. //
  277. // We had some trouble trying to perform the requested
  278. // operation, so we'll abort the I/O request with
  279. // the error status that we get back from the
  280. // execption code
  281. //
  282. if (Irp != NULL) {
  283. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  284. Status = GetExceptionCode();
  285. if ((Status == STATUS_FILE_DELETED) &&
  286. ((IrpContext->MajorFunction == IRP_MJ_READ) ||
  287. (IrpContext->MajorFunction == IRP_MJ_WRITE) ||
  288. ((IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) &&
  289. (IrpSp->Parameters.SetFile.FileInformationClass == FileEndOfFileInformation)))) {
  290. IrpContext->ExceptionStatus = Status = STATUS_SUCCESS;
  291. }
  292. }
  293. //
  294. // If we failed to upgrade the volume's version during mount, we may
  295. // not have put the right exception code into the irp context yet.
  296. //
  297. if ((IrpContext != NULL) &&
  298. (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_VOL_UPGR_FAILED )) &&
  299. (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  300. (IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME)) {
  301. IrpContext->ExceptionStatus = Status;
  302. }
  303. //
  304. // This is the return status code that we want the Irp Completion routine to receive.
  305. //
  306. Status = NtfsProcessException( IrpContext, Irp, Status );
  307. if ((Status == STATUS_CANT_WAIT) || (Status == STATUS_LOG_FILE_FULL)) {
  308. Retry = TRUE;
  309. }
  310. }
  311. } while (Retry);
  312. FsRtlExitFileSystem();
  313. //
  314. // If there are any entries on this volume's overflow queue, service
  315. // them.
  316. //
  317. if (VolDo != NULL) {
  318. KIRQL SavedIrql;
  319. PLIST_ENTRY Entry = NULL;
  320. //
  321. // We have a volume device object so see if there is any work
  322. // left to do in its overflow queue.
  323. //
  324. KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql );
  325. while (VolDo->OverflowQueueCount > 0) {
  326. //
  327. // There is overflow work to do in this volume so we'll
  328. // decrement the Overflow count, dequeue the IRP, and release
  329. // the Event
  330. //
  331. Entry = VolDo->OverflowQueue.Flink;
  332. IrpContext = CONTAINING_RECORD( Entry,
  333. IRP_CONTEXT,
  334. WorkQueueItem.List );
  335. Irp = IrpContext->OriginatingIrp;
  336. //
  337. // If the cancel routine thinks it owns the irp ignore it
  338. //
  339. if (NtfsSetCancelRoutine( Irp, NULL, 0, FALSE )) {
  340. VolDo->OverflowQueueCount -= 1;
  341. RemoveEntryList( (PLIST_ENTRY)Entry );
  342. //
  343. // Reset the shared fields
  344. //
  345. InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
  346. InitializeListHead( &IrpContext->ExclusiveFcbList );
  347. break;
  348. } else {
  349. //
  350. // Release the spinlock to let the cancel routine gain it and finish
  351. // its action
  352. //
  353. KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql );
  354. KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql );
  355. Entry = NULL;
  356. }
  357. } // endwhile
  358. //
  359. // There wasn't an entry, so before dropping the spinlock decrement the posted
  360. // count to be in synch with NtfsAddToWorkQueue and deref the device
  361. //
  362. if (Entry == NULL) {
  363. VolDo->PostedRequestCount -= 1;
  364. KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql );
  365. ObDereferenceObject( &VolDo->DeviceObject );
  366. break;
  367. } else {
  368. KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql );
  369. }
  370. if (VolDo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
  371. KeSetEvent( &VolDo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
  372. }
  373. //
  374. // set wait to TRUE, and loop.
  375. //
  376. LogFileFullCount = 0;
  377. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  378. continue;
  379. } else {
  380. //
  381. // No VolDo so just leave
  382. //
  383. break;
  384. }
  385. }
  386. return;
  387. }
  388. VOID
  389. NtfsPostSpecial (
  390. IN PIRP_CONTEXT IrpContext,
  391. IN PVCB Vcb,
  392. IN POST_SPECIAL_CALLOUT PostSpecialCallout,
  393. IN PVOID Context
  394. )
  395. /*++
  396. Routine Description:
  397. This routine posts a special request to a worker thread. The function
  398. to be called is passed in. The Vcb is referenced to ensure it is not
  399. deleted while the posted request is excuting.
  400. Arguments:
  401. Vcb - Volume control block for volume to post to.
  402. PostSpecialCallout - Function to be called from the worker thread.
  403. Context - Context point to pass to the function.
  404. Return Value:
  405. None
  406. --*/
  407. {
  408. PIRP_CONTEXT NewIrpContext = NULL;
  409. UNREFERENCED_PARAMETER( IrpContext );
  410. PAGED_CODE();
  411. //
  412. // Create an IrpContext for use to post the request.
  413. //
  414. NtfsInitializeIrpContext( NULL, TRUE, &NewIrpContext );
  415. NewIrpContext->Vcb = Vcb;
  416. NewIrpContext->Union.PostSpecialCallout = PostSpecialCallout;
  417. NewIrpContext->OriginatingIrp = Context;
  418. //
  419. // Updating the CloseCount and SystemFileCloseCount allows the volume
  420. // to be locked or dismounted, but the Vcb will not be deleted. This
  421. // routine will only be called with non-zero close counts so it is ok
  422. // to increment theses counts.
  423. //
  424. ASSERT( Vcb->CloseCount > 0 );
  425. InterlockedIncrement( &Vcb->CloseCount );
  426. InterlockedIncrement( &Vcb->SystemFileCloseCount );
  427. RtlZeroMemory( &NewIrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ) );
  428. ExInitializeWorkItem( &NewIrpContext->WorkQueueItem,
  429. NtfsSpecialDispatch,
  430. NewIrpContext );
  431. //
  432. // Determine if the scavenger is already running.
  433. //
  434. ExAcquireFastMutexUnsafe( &NtfsScavengerLock );
  435. if (NtfsScavengerRunning) {
  436. //
  437. // Add this item to the scavanger work list.
  438. //
  439. NewIrpContext->WorkQueueItem.List.Flink = NULL;
  440. if (NtfsScavengerWorkList == NULL) {
  441. NtfsScavengerWorkList = NewIrpContext;
  442. } else {
  443. PIRP_CONTEXT WorkIrpContext;
  444. WorkIrpContext = NtfsScavengerWorkList;
  445. while (WorkIrpContext->WorkQueueItem.List.Flink != NULL) {
  446. WorkIrpContext = (PIRP_CONTEXT)
  447. WorkIrpContext->WorkQueueItem.List.Flink;
  448. }
  449. WorkIrpContext->WorkQueueItem.List.Flink = (PLIST_ENTRY)
  450. NewIrpContext;
  451. }
  452. } else {
  453. //
  454. // Start a worker thread to do scavenger work.
  455. //
  456. ExQueueWorkItem( &NewIrpContext->WorkQueueItem, DelayedWorkQueue );
  457. NtfsScavengerRunning = TRUE;
  458. }
  459. ExReleaseFastMutexUnsafe( &NtfsScavengerLock);
  460. }
  461. VOID
  462. NtfsSpecialDispatch (
  463. PVOID Context
  464. )
  465. /*++
  466. Routine Description:
  467. This routine is called when a special operation needs to be posted.
  468. It is called indirectly by NtfsPostSpecial. It is assumes that the
  469. Vcb is protected from going away by incrementing the volemue close
  470. counts for a file. If this routine fails nothing is done except
  471. to clean up the Vcb. This routine also handles issues log file full
  472. and can't wait.
  473. The function to be called is stored in the PostSpecialCallout field
  474. of the Irp Context, and the context is stored int he OriginatingIrp.
  475. Both fields are zeroed before the the callout function is called.
  476. Arguments:
  477. Context - Supplies a pointer to an IrpContext.
  478. Return Value:
  479. --*/
  480. {
  481. PVCB Vcb;
  482. PIRP_CONTEXT IrpContext = Context;
  483. TOP_LEVEL_CONTEXT TopLevelContext;
  484. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  485. POST_SPECIAL_CALLOUT PostSpecialCallout;
  486. PVOID SpecialContext;
  487. ULONG LogFileFullCount;
  488. BOOLEAN Retry;
  489. PAGED_CODE();
  490. FsRtlEnterFileSystem();
  491. do {
  492. Vcb = IrpContext->Vcb;
  493. LogFileFullCount = 0;
  494. //
  495. // Capture the funciton pointer and context before using the IrpContext.
  496. //
  497. PostSpecialCallout = IrpContext->Union.PostSpecialCallout;
  498. SpecialContext = IrpContext->OriginatingIrp;
  499. IrpContext->Union.PostSpecialCallout = NULL;
  500. IrpContext->OriginatingIrp = NULL;
  501. //
  502. // Reset the shared fields
  503. //
  504. InitializeListHead( &IrpContext->RecentlyDeallocatedQueue );
  505. InitializeListHead( &IrpContext->ExclusiveFcbList );
  506. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
  507. ASSERT( ThreadTopLevelContext == &TopLevelContext );
  508. ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  509. ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_FROM_POOL ));
  510. //
  511. // Initialize the thread top level structure, if needed.
  512. //
  513. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  514. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  515. //
  516. // Don't let this IrpContext be deleted.
  517. //
  518. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT );
  519. do {
  520. Retry = FALSE;
  521. try {
  522. //
  523. // See if we failed due to a log file full condition, and
  524. // if so, then do a clean volume checkpoint if we are the
  525. // first ones to get there. If we see a different Lsn and do
  526. // not do the checkpoint, the worst that can happen is that we
  527. // will fail again if the log file is still full.
  528. //
  529. if (IrpContext->LastRestartArea.QuadPart != 0) {
  530. NtfsCheckpointForLogFileFull( IrpContext );
  531. if (++LogFileFullCount >= 2) {
  532. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
  533. }
  534. }
  535. //
  536. // Call the requested function.
  537. //
  538. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  539. PostSpecialCallout( IrpContext, SpecialContext );
  540. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  541. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  542. NTSTATUS ExceptionCode;
  543. ExceptionCode = GetExceptionCode();
  544. ExceptionCode = NtfsProcessException( IrpContext, NULL, ExceptionCode );
  545. if ((ExceptionCode == STATUS_CANT_WAIT) ||
  546. (ExceptionCode == STATUS_LOG_FILE_FULL)) {
  547. Retry = TRUE;
  548. }
  549. }
  550. } while (Retry);
  551. //
  552. // Ok to let this IrpContext be deleted.
  553. //
  554. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT );
  555. //
  556. // At this point regardless of the status the volume needs to
  557. // be cleaned up and the IrpContext freed.
  558. // Dereference the Vcb and check to see if it needs to be deleted.
  559. // since this call might raise wrap it with a try/execpt.
  560. //
  561. try {
  562. //
  563. // Acquire the volume exclusive so the counts can be
  564. // updated.
  565. //
  566. ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
  567. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  568. InterlockedDecrement( &Vcb->SystemFileCloseCount );
  569. InterlockedDecrement( &Vcb->CloseCount );
  570. NtfsReleaseVcb( IrpContext, Vcb );
  571. } except( EXCEPTION_EXECUTE_HANDLER ) {
  572. ASSERT( FsRtlIsNtstatusExpected( GetExceptionCode() ) );
  573. }
  574. //
  575. // Free the irp context.
  576. //
  577. NtfsCleanupIrpContext( IrpContext, TRUE );
  578. //
  579. // See if there is more work on the scavenger list.
  580. //
  581. ExAcquireFastMutexUnsafe( &NtfsScavengerLock );
  582. ASSERT( NtfsScavengerRunning );
  583. IrpContext = NtfsScavengerWorkList;
  584. if (IrpContext != NULL) {
  585. //
  586. // Remove the entry from the list.
  587. //
  588. NtfsScavengerWorkList = (PIRP_CONTEXT) IrpContext->WorkQueueItem.List.Flink;
  589. } else {
  590. NtfsScavengerRunning = FALSE;
  591. }
  592. ExReleaseFastMutexUnsafe( &NtfsScavengerLock );
  593. } while ( IrpContext != NULL );
  594. FsRtlExitFileSystem();
  595. }