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.

1174 lines
30 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. Close.c
  5. Abstract:
  6. This module implements the File Close routine for Fat called by the
  7. dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. Gary Kimura [GaryKi] 28-Dec-1989
  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_CLOSE)
  19. //
  20. // The local debug trace level
  21. //
  22. #define Dbg (DEBUG_TRACE_CLOSE)
  23. ULONG FatMaxDelayedCloseCount;
  24. #define FatAcquireCloseMutex() { \
  25. ASSERT(KeAreApcsDisabled()); \
  26. ExAcquireFastMutexUnsafe( &FatCloseQueueMutex ); \
  27. }
  28. #define FatReleaseCloseMutex() { \
  29. ASSERT(KeAreApcsDisabled()); \
  30. ExReleaseFastMutexUnsafe( &FatCloseQueueMutex ); \
  31. }
  32. //
  33. // Local procedure prototypes
  34. //
  35. VOID
  36. FatQueueClose (
  37. IN PCLOSE_CONTEXT CloseContext,
  38. IN BOOLEAN DelayClose
  39. );
  40. PCLOSE_CONTEXT
  41. FatRemoveClose (
  42. PVCB Vcb OPTIONAL,
  43. PVCB LastVcbHint OPTIONAL
  44. );
  45. VOID
  46. FatCloseWorker (
  47. IN PDEVICE_OBJECT DeviceObject,
  48. IN PVOID Context
  49. );
  50. #ifdef ALLOC_PRAGMA
  51. #pragma alloc_text(PAGE, FatFsdClose)
  52. #pragma alloc_text(PAGE, FatFspClose)
  53. #pragma alloc_text(PAGE, FatCommonClose)
  54. #pragma alloc_text(PAGE, FatCloseWorker)
  55. #endif
  56. NTSTATUS
  57. FatFsdClose (
  58. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  59. IN PIRP Irp
  60. )
  61. /*++
  62. Routine Description:
  63. This routine implements the FSD part of Close.
  64. Arguments:
  65. VolumeDeviceObject - Supplies the volume device object where the
  66. file exists
  67. Irp - Supplies the Irp being processed
  68. Return Value:
  69. NTSTATUS - The FSD status for the IRP
  70. --*/
  71. {
  72. NTSTATUS Status = STATUS_SUCCESS;
  73. PIO_STACK_LOCATION IrpSp;
  74. PFILE_OBJECT FileObject;
  75. PVCB Vcb;
  76. PFCB Fcb;
  77. PCCB Ccb;
  78. TYPE_OF_OPEN TypeOfOpen;
  79. BOOLEAN TopLevel;
  80. //
  81. // If we were called with our file system device object instead of a
  82. // volume device object, just complete this request with STATUS_SUCCESS
  83. //
  84. if (FatDeviceIsFatFsdo( VolumeDeviceObject)) {
  85. Irp->IoStatus.Status = STATUS_SUCCESS;
  86. Irp->IoStatus.Information = FILE_OPENED;
  87. IoCompleteRequest( Irp, IO_DISK_INCREMENT );
  88. return STATUS_SUCCESS;
  89. }
  90. DebugTrace(+1, Dbg, "FatFsdClose\n", 0);
  91. //
  92. // Call the common Close routine
  93. //
  94. FsRtlEnterFileSystem();
  95. TopLevel = FatIsIrpTopLevel( Irp );
  96. //
  97. // Get a pointer to the current stack location and the file object
  98. //
  99. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  100. FileObject = IrpSp->FileObject;
  101. //
  102. // Decode the file object and set the read-only bit in the Ccb.
  103. //
  104. TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
  105. if (Ccb && IsFileObjectReadOnly(FileObject)) {
  106. SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY );
  107. }
  108. try {
  109. PCLOSE_CONTEXT CloseContext;
  110. //
  111. // If we are top level, WAIT can be TRUE, otherwise make it FALSE
  112. // to avoid deadlocks, unless this is a top
  113. // level request not originating from the system process.
  114. //
  115. BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess);
  116. //
  117. // Call the common Close routine if we are not delaying this close.
  118. //
  119. if ((((TypeOfOpen == UserFileOpen) ||
  120. (TypeOfOpen == UserDirectoryOpen)) &&
  121. FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) &&
  122. !FatData.ShutdownStarted) ||
  123. (FatCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, NULL) == STATUS_PENDING)) {
  124. //
  125. // Metadata streams have had close contexts preallocated.
  126. //
  127. if (TypeOfOpen == VirtualVolumeFile ||
  128. TypeOfOpen == DirectoryFile ||
  129. TypeOfOpen == EaFile) {
  130. CloseContext = FatAllocateCloseContext();
  131. ASSERT( CloseContext != NULL );
  132. CloseContext->Free = TRUE;
  133. } else {
  134. //
  135. // Free up any query template strings before using the close context fields,
  136. // which overlap (union)
  137. //
  138. FatDeallocateCcbStrings( Ccb);
  139. CloseContext = &Ccb->CloseContext;
  140. CloseContext->Free = FALSE;
  141. SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT );
  142. }
  143. //
  144. // If the status is pending, then let's get the information we
  145. // need into the close context we already have bagged, complete
  146. // the request, and post it. It is important we allocate nothing
  147. // in the close path.
  148. //
  149. CloseContext->Vcb = Vcb;
  150. CloseContext->Fcb = Fcb;
  151. CloseContext->TypeOfOpen = TypeOfOpen;
  152. //
  153. // Send it off, either to an ExWorkerThread or to the async
  154. // close list.
  155. //
  156. FatQueueClose( CloseContext,
  157. (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE)));
  158. } else {
  159. //
  160. // The close proceeded synchronously, so for the metadata objects we
  161. // can now drop the close context we preallocated.
  162. //
  163. if (TypeOfOpen == VirtualVolumeFile ||
  164. TypeOfOpen == DirectoryFile ||
  165. TypeOfOpen == EaFile) {
  166. CloseContext = FatAllocateCloseContext();
  167. ASSERT( CloseContext != NULL );
  168. ExFreePool( CloseContext );
  169. }
  170. }
  171. FatCompleteRequest( FatNull, Irp, Status );
  172. } except(FatExceptionFilter( NULL, GetExceptionInformation() )) {
  173. //
  174. // We had some trouble trying to perform the requested
  175. // operation, so we'll abort the I/O request with the
  176. // error status that we get back from the execption code.
  177. //
  178. Status = FatProcessException( NULL, Irp, GetExceptionCode() );
  179. }
  180. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  181. FsRtlExitFileSystem();
  182. //
  183. // And return to our caller
  184. //
  185. DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status);
  186. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  187. return Status;
  188. }
  189. VOID
  190. FatCloseWorker (
  191. IN PDEVICE_OBJECT DeviceObject,
  192. IN PVOID Context
  193. )
  194. /*++
  195. Routine Description:
  196. This routine is a shim between the IO worker package and FatFspClose.
  197. Arguments:
  198. DeviceObject - Registration device object, unused
  199. Context - Context value, unused
  200. Return Value:
  201. None.
  202. --*/
  203. {
  204. FsRtlEnterFileSystem();
  205. FatFspClose (Context);
  206. FsRtlExitFileSystem();
  207. }
  208. VOID
  209. FatFspClose (
  210. IN PVCB Vcb OPTIONAL
  211. )
  212. /*++
  213. Routine Description:
  214. This routine implements the FSP part of Close.
  215. Arguments:
  216. Vcb - If present, tells us to only close file objects opened on the
  217. specified volume.
  218. Return Value:
  219. None.
  220. --*/
  221. {
  222. PCLOSE_CONTEXT CloseContext;
  223. PVCB CurrentVcb = NULL;
  224. PVCB LastVcb = NULL;
  225. BOOLEAN FreeContext;
  226. ULONG LoopsWithVcbHeld;
  227. DebugTrace(+1, Dbg, "FatFspClose\n", 0);
  228. //
  229. // Set the top level IRP for the true FSP operation.
  230. //
  231. if (!ARGUMENT_PRESENT( Vcb )) {
  232. IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
  233. }
  234. while (CloseContext = FatRemoveClose(Vcb, LastVcb)) {
  235. //
  236. // If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of
  237. // creates by doing several closes with one acquisition of the Vcb.
  238. //
  239. // Note that we cannot be holding the Vcb on entry to FatCommonClose
  240. // if this is last close as we will try to acquire FatData, and
  241. // worse the volume (and therefore the Vcb) may go away.
  242. //
  243. if (!ARGUMENT_PRESENT(Vcb)) {
  244. if (!FatData.ShutdownStarted) {
  245. if (CloseContext->Vcb != CurrentVcb) {
  246. LoopsWithVcbHeld = 0;
  247. //
  248. // Release a previously held Vcb, if any.
  249. //
  250. if (CurrentVcb != NULL) {
  251. ExReleaseResourceLite( &CurrentVcb->Resource);
  252. }
  253. //
  254. // Get the new Vcb.
  255. //
  256. CurrentVcb = CloseContext->Vcb;
  257. (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
  258. } else {
  259. //
  260. // Share the resource occasionally if we seem to be finding a lot
  261. // of closes for a single volume.
  262. //
  263. if (++LoopsWithVcbHeld >= 20) {
  264. if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) +
  265. ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) {
  266. ExReleaseResourceLite( &CurrentVcb->Resource);
  267. (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE );
  268. }
  269. LoopsWithVcbHeld = 0;
  270. }
  271. }
  272. //
  273. // Now check the Open count. We may be about to delete this volume!
  274. //
  275. // The test below must be <= 1 because there could still be outstanding
  276. // stream references on this VCB that are not counted in the OpenFileCount.
  277. // For example if there are no open files OpenFileCount could be zero and we would
  278. // not release the resource here. The call to FatCommonClose() below may cause
  279. // the VCB to be torn down and we will try to release memory we don't
  280. // own later.
  281. //
  282. if (CurrentVcb->OpenFileCount <= 1) {
  283. ExReleaseResourceLite( &CurrentVcb->Resource);
  284. CurrentVcb = NULL;
  285. }
  286. //
  287. // If shutdown has started while processing our list, drop the
  288. // current Vcb resource.
  289. //
  290. } else if (CurrentVcb != NULL) {
  291. ExReleaseResourceLite( &CurrentVcb->Resource);
  292. CurrentVcb = NULL;
  293. }
  294. }
  295. LastVcb = CurrentVcb;
  296. //
  297. // Call the common Close routine. Protected in a try {} except {}
  298. //
  299. try {
  300. //
  301. // The close context either is in the CCB, automatically freed,
  302. // or was from pool for a metadata fileobject, CCB is NULL, and
  303. // we'll need to free it.
  304. //
  305. FreeContext = CloseContext->Free;
  306. (VOID)FatCommonClose( CloseContext->Vcb,
  307. CloseContext->Fcb,
  308. (FreeContext ? NULL :
  309. CONTAINING_RECORD( CloseContext, CCB, CloseContext)),
  310. CloseContext->TypeOfOpen,
  311. TRUE,
  312. NULL );
  313. } except(FatExceptionFilter( NULL, GetExceptionInformation() )) {
  314. //
  315. // Ignore anything we expect.
  316. //
  317. NOTHING;
  318. }
  319. //
  320. // Drop the context if it came from pool.
  321. //
  322. if (FreeContext) {
  323. ExFreePool( CloseContext );
  324. }
  325. }
  326. //
  327. // Release a previously held Vcb, if any.
  328. //
  329. if (CurrentVcb != NULL) {
  330. ExReleaseResourceLite( &CurrentVcb->Resource);
  331. }
  332. //
  333. // Clean up the top level IRP hint if we owned it.
  334. //
  335. if (!ARGUMENT_PRESENT( Vcb )) {
  336. IoSetTopLevelIrp( NULL );
  337. }
  338. //
  339. // And return to our caller
  340. //
  341. DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0);
  342. return;
  343. }
  344. VOID
  345. FatQueueClose (
  346. IN PCLOSE_CONTEXT CloseContext,
  347. IN BOOLEAN DelayClose
  348. )
  349. /*++
  350. Routine Description:
  351. Enqueue a deferred close to one of the two delayed close queues.
  352. Arguments:
  353. CloseContext - a close context to enqueue for the delayed close thread.
  354. DelayClose - whether this should go on the delayed close queue (unreferenced
  355. objects).
  356. Return Value:
  357. None.
  358. --*/
  359. {
  360. BOOLEAN StartWorker = FALSE;
  361. FatAcquireCloseMutex();
  362. if (DelayClose) {
  363. InsertTailList( &FatData.DelayedCloseList,
  364. &CloseContext->GlobalLinks );
  365. InsertTailList( &CloseContext->Vcb->DelayedCloseList,
  366. &CloseContext->VcbLinks );
  367. FatData.DelayedCloseCount += 1;
  368. if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) &&
  369. !FatData.AsyncCloseActive) {
  370. FatData.AsyncCloseActive = TRUE;
  371. StartWorker = TRUE;
  372. }
  373. } else {
  374. InsertTailList( &FatData.AsyncCloseList,
  375. &CloseContext->GlobalLinks );
  376. InsertTailList( &CloseContext->Vcb->AsyncCloseList,
  377. &CloseContext->VcbLinks );
  378. FatData.AsyncCloseCount += 1;
  379. if (!FatData.AsyncCloseActive) {
  380. FatData.AsyncCloseActive = TRUE;
  381. StartWorker = TRUE;
  382. }
  383. }
  384. FatReleaseCloseMutex();
  385. if (StartWorker) {
  386. IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL );
  387. }
  388. }
  389. PCLOSE_CONTEXT
  390. FatRemoveClose (
  391. PVCB Vcb OPTIONAL,
  392. PVCB LastVcbHint OPTIONAL
  393. )
  394. /*++
  395. Routine Description:
  396. Dequeue a deferred close from one of the two delayed close queues.
  397. Arguments:
  398. Vcb - if specified, only returns close for this volume.
  399. LastVcbHint - if specified and other starvation avoidance is required by
  400. the system condition, will attempt to return closes for this volume.
  401. Return Value:
  402. A close to perform.
  403. --*/
  404. {
  405. PLIST_ENTRY Entry;
  406. PCLOSE_CONTEXT CloseContext;
  407. BOOLEAN WorkerThread;
  408. FatAcquireCloseMutex();
  409. //
  410. // Remember if this is the worker thread, so we can pull down the active
  411. // flag should we run everything out.
  412. //
  413. WorkerThread = (Vcb == NULL);
  414. //
  415. // If the queues are above the limits by a significant amount, we have
  416. // to try hard to pull them down. To do this, we will aggresively try
  417. // to find closes for the last volume the caller looked at. This will
  418. // make sure we fully utilize the acquisition of the volume, which can
  419. // be a hugely expensive resource to get (create/close/cleanup use it
  420. // exclusively).
  421. //
  422. // Only do this in the delayed close thread. We will know this is the
  423. // case by seeing a NULL mandatory Vcb.
  424. //
  425. if (Vcb == NULL && LastVcbHint != NULL) {
  426. //
  427. // Flip over to aggressive at twice the legal limit, and flip it
  428. // off at the legal limit.
  429. //
  430. if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) {
  431. FatData.HighAsync = TRUE;
  432. } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) {
  433. FatData.HighAsync = FALSE;
  434. }
  435. if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) {
  436. FatData.HighDelayed = TRUE;
  437. } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) {
  438. FatData.HighDelayed = FALSE;
  439. }
  440. if (FatData.HighAsync || FatData.HighDelayed) {
  441. Vcb = LastVcbHint;
  442. }
  443. }
  444. //
  445. // Do the case when we don't care about which Vcb the close is on.
  446. // This is the case when we are in an ExWorkerThread and aren't
  447. // under pressure.
  448. //
  449. if (Vcb == NULL) {
  450. AnyClose:
  451. //
  452. // First check the list of async closes.
  453. //
  454. if (!IsListEmpty( &FatData.AsyncCloseList )) {
  455. Entry = RemoveHeadList( &FatData.AsyncCloseList );
  456. FatData.AsyncCloseCount -= 1;
  457. CloseContext = CONTAINING_RECORD( Entry,
  458. CLOSE_CONTEXT,
  459. GlobalLinks );
  460. RemoveEntryList( &CloseContext->VcbLinks );
  461. //
  462. // Do any delayed closes over half the limit, unless shutdown has
  463. // started (then kill them all).
  464. //
  465. } else if (!IsListEmpty( &FatData.DelayedCloseList ) &&
  466. (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 ||
  467. FatData.ShutdownStarted)) {
  468. Entry = RemoveHeadList( &FatData.DelayedCloseList );
  469. FatData.DelayedCloseCount -= 1;
  470. CloseContext = CONTAINING_RECORD( Entry,
  471. CLOSE_CONTEXT,
  472. GlobalLinks );
  473. RemoveEntryList( &CloseContext->VcbLinks );
  474. //
  475. // There are no more closes to perform; show that we are done.
  476. //
  477. } else {
  478. CloseContext = NULL;
  479. if (WorkerThread) {
  480. FatData.AsyncCloseActive = FALSE;
  481. }
  482. }
  483. //
  484. // We're running down a specific volume.
  485. //
  486. } else {
  487. //
  488. // First check the list of async closes.
  489. //
  490. if (!IsListEmpty( &Vcb->AsyncCloseList )) {
  491. Entry = RemoveHeadList( &Vcb->AsyncCloseList );
  492. FatData.AsyncCloseCount -= 1;
  493. CloseContext = CONTAINING_RECORD( Entry,
  494. CLOSE_CONTEXT,
  495. VcbLinks );
  496. RemoveEntryList( &CloseContext->GlobalLinks );
  497. //
  498. // Do any delayed closes.
  499. //
  500. } else if (!IsListEmpty( &Vcb->DelayedCloseList )) {
  501. Entry = RemoveHeadList( &Vcb->DelayedCloseList );
  502. FatData.DelayedCloseCount -= 1;
  503. CloseContext = CONTAINING_RECORD( Entry,
  504. CLOSE_CONTEXT,
  505. VcbLinks );
  506. RemoveEntryList( &CloseContext->GlobalLinks );
  507. //
  508. // If we were trying to run down the queues but didn't find anything for this
  509. // volume, flip over to accept anything and try again.
  510. //
  511. } else if (LastVcbHint) {
  512. goto AnyClose;
  513. //
  514. // There are no more closes to perform; show that we are done.
  515. //
  516. } else {
  517. CloseContext = NULL;
  518. }
  519. }
  520. FatReleaseCloseMutex();
  521. return CloseContext;
  522. }
  523. NTSTATUS
  524. FatCommonClose (
  525. IN PVCB Vcb,
  526. IN PFCB Fcb,
  527. IN PCCB Ccb,
  528. IN TYPE_OF_OPEN TypeOfOpen,
  529. IN BOOLEAN Wait,
  530. IN PVOLUME_DEVICE_OBJECT *VolDo OPTIONAL
  531. )
  532. /*++
  533. Routine Description:
  534. This is the common routine for closing a file/directory called by both
  535. the fsd and fsp threads.
  536. Close is invoked whenever the last reference to a file object is deleted.
  537. Cleanup is invoked when the last handle to a file object is closed, and
  538. is called before close.
  539. The function of close is to completely tear down and remove the fcb/dcb/ccb
  540. structures associated with the file object.
  541. Arguments:
  542. Fcb - Supplies the file to process.
  543. Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE
  544. then we must try to acquire the Vcb anyway.
  545. VolDo - This is really gross. If we are really in the Fsp, and a volume
  546. goes away. We need some way to NULL out the VolDo variable in
  547. FspDispatch().
  548. Return Value:
  549. NTSTATUS - The return status for the operation
  550. --*/
  551. {
  552. NTSTATUS Status;
  553. PDCB ParentDcb;
  554. BOOLEAN RecursiveClose;
  555. IRP_CONTEXT IrpContext;
  556. DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);
  557. //
  558. // Special case the unopened file object
  559. //
  560. if (TypeOfOpen == UnopenedFileObject) {
  561. DebugTrace(0, Dbg, "Close unopened file object\n", 0);
  562. Status = STATUS_SUCCESS;
  563. DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
  564. return Status;
  565. }
  566. //
  567. // Set up our stack IrpContext.
  568. //
  569. RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
  570. if (Wait) {
  571. SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
  572. }
  573. //
  574. // Acquire exclusive access to the Vcb and enqueue the irp if we didn't
  575. // get access.
  576. //
  577. if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {
  578. return STATUS_PENDING;
  579. }
  580. //
  581. // The following test makes sure that we don't blow away an Fcb if we
  582. // are trying to do a Supersede/Overwrite open above us. This test
  583. // does not apply for the EA file.
  584. //
  585. if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) &&
  586. Vcb->EaFcb != Fcb) {
  587. ExReleaseResourceLite( &Vcb->Resource );
  588. return STATUS_PENDING;
  589. }
  590. //
  591. // Setting the following flag prevents recursive closes of directory file
  592. // objects, which are handled in a special case loop.
  593. //
  594. if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {
  595. RecursiveClose = TRUE;
  596. } else {
  597. SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
  598. RecursiveClose = FALSE;
  599. }
  600. //
  601. // Synchronize here with other closes regarding volume deletion. Note
  602. // that the Vcb->OpenFileCount can be safely incremented here without
  603. // FatData synchronization for the following reasons:
  604. //
  605. // This counter only becomes relevant when (holding a spinlock):
  606. //
  607. // A: The Vcb->OpenFileCount is zero, and
  608. // B: The Vpb->Refcount is the residual (2/3 for close/verify)
  609. //
  610. // For A to be true, there can be no more pending closes at this point
  611. // in the close code. For B to be true, in close, there cannot be
  612. // a create in process, and thus no verify in process.
  613. //
  614. // Also we only increment the count if this is a top level close.
  615. //
  616. if ( !RecursiveClose ) {
  617. Vcb->OpenFileCount += 1;
  618. }
  619. try {
  620. //
  621. // Case on the type of open that we are trying to close.
  622. //
  623. switch (TypeOfOpen) {
  624. case VirtualVolumeFile:
  625. DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);
  626. try_return( Status = STATUS_SUCCESS );
  627. break;
  628. case UserVolumeOpen:
  629. DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);
  630. Vcb->DirectAccessOpenCount -= 1;
  631. Vcb->OpenFileCount -= 1;
  632. if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
  633. FatDeleteCcb( &IrpContext, Ccb );
  634. try_return( Status = STATUS_SUCCESS );
  635. break;
  636. case EaFile:
  637. DebugTrace(0, Dbg, "Close EaFile\n", 0);
  638. try_return( Status = STATUS_SUCCESS );
  639. break;
  640. case DirectoryFile:
  641. DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);
  642. InterlockedDecrement( &Fcb->Specific.Dcb.DirectoryFileOpenCount );
  643. //
  644. // If this is a recursive close, just return here.
  645. //
  646. if ( RecursiveClose ) {
  647. try_return( Status = STATUS_SUCCESS );
  648. } else {
  649. break;
  650. }
  651. case UserDirectoryOpen:
  652. case UserFileOpen:
  653. DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);
  654. //
  655. // Uninitialize the cache map if we no longer need to use it
  656. //
  657. if ((NodeType(Fcb) == FAT_NTC_DCB) &&
  658. IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) &&
  659. (Fcb->OpenCount == 1) &&
  660. (Fcb->Specific.Dcb.DirectoryFile != NULL)) {
  661. PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;
  662. DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);
  663. CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
  664. //
  665. // Dereference the directory file. This may cause a close
  666. // Irp to be processed, so we need to do this before we destory
  667. // the Fcb.
  668. //
  669. Fcb->Specific.Dcb.DirectoryFile = NULL;
  670. ObDereferenceObject( DirectoryFileObject );
  671. }
  672. Fcb->OpenCount -= 1;
  673. Vcb->OpenFileCount -= 1;
  674. if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
  675. FatDeleteCcb( &IrpContext, Ccb );
  676. break;
  677. default:
  678. FatBugCheck( TypeOfOpen, 0, 0 );
  679. }
  680. //
  681. // At this point we've cleaned up any on-disk structure that needs
  682. // to be done, and we can now update the in-memory structures.
  683. // Now if this is an unreferenced FCB or if it is
  684. // an unreferenced DCB (not the root) then we can remove
  685. // the fcb and set our ParentDcb to non null.
  686. //
  687. if (((NodeType(Fcb) == FAT_NTC_FCB) &&
  688. (Fcb->OpenCount == 0))
  689. ||
  690. ((NodeType(Fcb) == FAT_NTC_DCB) &&
  691. (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) &&
  692. (Fcb->OpenCount == 0) &&
  693. (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {
  694. ParentDcb = Fcb->ParentDcb;
  695. SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
  696. FatDeleteFcb( &IrpContext, Fcb );
  697. //
  698. // Uninitialize our parent's cache map if we no longer need
  699. // to use it.
  700. //
  701. while ((NodeType(ParentDcb) == FAT_NTC_DCB) &&
  702. IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) &&
  703. (ParentDcb->OpenCount == 0) &&
  704. (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
  705. PFILE_OBJECT DirectoryFileObject;
  706. DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;
  707. DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
  708. CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
  709. ParentDcb->Specific.Dcb.DirectoryFile = NULL;
  710. ObDereferenceObject( DirectoryFileObject );
  711. //
  712. // Now, if the ObDereferenceObject() caused the final close
  713. // to come in, then blow away the Fcb and continue up,
  714. // otherwise wait for Mm to to dereference its file objects
  715. // and stop here..
  716. //
  717. if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {
  718. PDCB CurrentDcb;
  719. CurrentDcb = ParentDcb;
  720. ParentDcb = CurrentDcb->ParentDcb;
  721. SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
  722. FatDeleteFcb( &IrpContext, CurrentDcb );
  723. } else {
  724. break;
  725. }
  726. }
  727. }
  728. Status = STATUS_SUCCESS;
  729. try_exit: NOTHING;
  730. } finally {
  731. DebugUnwind( FatCommonClose );
  732. if ( !RecursiveClose ) {
  733. ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS );
  734. }
  735. //
  736. // Check if we should delete the volume. Unfortunately, to correctly
  737. // synchronize with verify, we can only unsafely check our own
  738. // transition. This results in a little bit of extra overhead in the
  739. // 1 -> 0 OpenFileCount transition.
  740. //
  741. // 2 is the residual Vpb->RefCount on a volume to be freed.
  742. //
  743. //
  744. // Here is the deal with releasing the Vcb. We must be holding the
  745. // Vcb when decrementing the Vcb->OpenFileCount. If we don't this
  746. // could cause the decrement to mal-function on an MP system. But we
  747. // want to be holding the Global resource exclusive when decrement
  748. // the count so that nobody else will try to dismount the volume.
  749. // However, because of locking rules, the Global resource must be
  750. // acquired first, which is why we do what we do below.
  751. //
  752. if ( !RecursiveClose ) {
  753. if ( Vcb->OpenFileCount == 1 ) {
  754. PVPB Vpb = Vcb->Vpb;
  755. SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
  756. FatReleaseVcb( &IrpContext, Vcb );
  757. (VOID)FatAcquireExclusiveGlobal( &IrpContext );
  758. (VOID)FatAcquireExclusiveVcb( &IrpContext, Vcb );
  759. Vcb->OpenFileCount -= 1;
  760. FatReleaseVcb( &IrpContext, Vcb );
  761. //
  762. // We can now "safely" check OpenFileCount and VcbCondition.
  763. // If they are OK, we will proceed to checking the
  764. // Vpb Ref Count in FatCheckForDismount.
  765. //
  766. if ( (Vcb->OpenFileCount == 0) &&
  767. ((Vcb->VcbCondition == VcbNotMounted) ||
  768. (Vcb->VcbCondition == VcbBad) ||
  769. FlagOn( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN )) &&
  770. FatCheckForDismount( &IrpContext, Vcb, FALSE ) ) {
  771. //
  772. // If this is not the Vpb "attached" to the device, free it.
  773. //
  774. if ((Vpb->RealDevice->Vpb != Vpb) &&
  775. !FlagOn( Vpb->Flags, VPB_PERSISTENT)) {
  776. ExFreePool( Vpb );
  777. }
  778. if (ARGUMENT_PRESENT(VolDo)) {
  779. *VolDo = NULL;
  780. }
  781. }
  782. FatReleaseGlobal( &IrpContext );
  783. } else {
  784. Vcb->OpenFileCount -= 1;
  785. FatReleaseVcb( &IrpContext, Vcb );
  786. }
  787. } else {
  788. FatReleaseVcb( &IrpContext, Vcb );
  789. }
  790. DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
  791. }
  792. return Status;
  793. }