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.

1529 lines
43 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Close.c
  5. Abstract:
  6. This module implements the File Close routine for Ntfs called by the
  7. dispatch driver.
  8. Author:
  9. Your Name [Email] dd-Mon-Year
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_CLOSE)
  17. ULONG NtfsAsyncPassCount = 0;
  18. //
  19. // Local procedure prototypes
  20. //
  21. NTSTATUS
  22. NtfsCommonClose (
  23. IN PIRP_CONTEXT IrpContext,
  24. IN PSCB Scb,
  25. IN PFCB Fcb,
  26. IN PVCB Vcb,
  27. IN PCCB *Ccb,
  28. IN TYPE_OF_OPEN TypeOfOpen,
  29. IN BOOLEAN ReadOnly,
  30. IN BOOLEAN CalledFromFsp
  31. );
  32. VOID
  33. NtfsQueueClose (
  34. IN PIRP_CONTEXT IrpContext,
  35. IN BOOLEAN DelayClose
  36. );
  37. PIRP_CONTEXT
  38. NtfsRemoveClose (
  39. IN PVCB Vcb OPTIONAL,
  40. IN BOOLEAN ThrottleCreate
  41. );
  42. #ifdef ALLOC_PRAGMA
  43. #pragma alloc_text(PAGE, NtfsCommonClose)
  44. #pragma alloc_text(PAGE, NtfsFsdClose)
  45. #pragma alloc_text(PAGE, NtfsFspClose)
  46. #endif
  47. NTSTATUS
  48. NtfsFsdClose (
  49. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  50. IN PIRP Irp
  51. )
  52. /*++
  53. Routine Description:
  54. This routine implements the FSD part of Close.
  55. Arguments:
  56. VolumeDeviceObject - Supplies the volume device object where the
  57. file exists
  58. Irp - Supplies the Irp being processed
  59. Return Value:
  60. NTSTATUS - The FSD status for the IRP
  61. --*/
  62. {
  63. TOP_LEVEL_CONTEXT TopLevelContext;
  64. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  65. NTSTATUS Status = STATUS_SUCCESS;
  66. PIRP_CONTEXT IrpContext = NULL;
  67. PFILE_OBJECT FileObject;
  68. TYPE_OF_OPEN TypeOfOpen;
  69. BOOLEAN IsSystemFile;
  70. BOOLEAN IsReadOnly;
  71. PVCB Vcb;
  72. PFCB Fcb;
  73. PSCB Scb;
  74. PCCB Ccb;
  75. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  76. PAGED_CODE();
  77. ASSERT_IRP( Irp );
  78. //
  79. // If we were called with our file system device object instead of a
  80. // volume device object, just complete this request with STATUS_SUCCESS
  81. //
  82. if (VolumeDeviceObject->DeviceObject.Size == (USHORT)sizeof(DEVICE_OBJECT)) {
  83. Irp->IoStatus.Status = STATUS_SUCCESS;
  84. Irp->IoStatus.Information = FILE_OPENED;
  85. IoCompleteRequest( Irp, IO_DISK_INCREMENT );
  86. return STATUS_SUCCESS;
  87. }
  88. DebugTrace( +1, Dbg, ("NtfsFsdClose\n") );
  89. //
  90. // Extract and decode the file object, we are willing to handle the unmounted
  91. // file object.
  92. //
  93. FileObject = IrpSp->FileObject;
  94. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
  95. //
  96. // Special case the unopened file object
  97. //
  98. if (TypeOfOpen == UnopenedFileObject) {
  99. DebugTrace( 0, Dbg, ("Close unopened file object\n") );
  100. Status = STATUS_SUCCESS;
  101. NtfsCompleteRequest( NULL, Irp, Status );
  102. DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) );
  103. return Status;
  104. }
  105. //
  106. // If this is the log file object for the Vcb then clear the field in the Vcb and
  107. // return. We don't need to synchronize here since there is only one file object
  108. // and it is closed only once.
  109. //
  110. if (FileObject == Vcb->LogFileObject) {
  111. //
  112. // Clear the internal file name constant
  113. //
  114. NtfsClearInternalFilename( Vcb->LogFileObject );
  115. Vcb->LogFileObject = NULL;
  116. Status = STATUS_SUCCESS;
  117. NtfsCompleteRequest( NULL, Irp, Status );
  118. DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) );
  119. return Status;
  120. }
  121. //
  122. // Call the common Close routine
  123. //
  124. FsRtlEnterFileSystem();
  125. //
  126. // Remember if this Ccb has gone through close.
  127. //
  128. if (Ccb != NULL) {
  129. //
  130. // We are not synchronized with the file resources at this point.
  131. // It is possible that NtfsUpdateFileDupInfo or the rename path may want to
  132. // update the name in the CCB. Our intention here is to mark this CCB_FLAG_CLOSE
  133. // so that these other operations know to skip this CCB. We need to deal with the
  134. // race condition where these other operations don't see the CLOSE flag but
  135. // then access the CCB name (which points back to the file object) after we
  136. // return the file object to the object manager (but put the CCB on the delayed
  137. // close queue).
  138. //
  139. // We will use the Fcb mutex to close the hole where DupInfo and rename need to look
  140. // at a CCB that might be in the close path.
  141. //
  142. NtfsLockFcb( NULL, Fcb );
  143. SetFlag( Ccb->Flags, CCB_FLAG_CLOSE );
  144. NtfsUnlockFcb( NULL, Fcb );
  145. ASSERT( FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ));
  146. }
  147. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  148. IsSystemFile = FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) || (TypeOfOpen == StreamFileOpen);
  149. IsReadOnly = (BOOLEAN)IsFileObjectReadOnly( FileObject );
  150. do {
  151. try {
  152. //
  153. // Jam Wait to FALSE when we create the IrpContext, to avoid
  154. // deadlocks when coming in from cleanup.
  155. //
  156. if (IrpContext == NULL) {
  157. //
  158. // Allocate and initialize the Irp.
  159. //
  160. NtfsInitializeIrpContext( Irp, FALSE, &IrpContext );
  161. //
  162. // Set the level structure on the stack.
  163. //
  164. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  165. //
  166. // If this is a top level request and we are not in the
  167. // system process, then we can wait. If it is a top level
  168. // request and we are in the system process then we would
  169. // rather not block this thread at all. If the number of pending
  170. // async closes is not too large we will post this immediately.
  171. //
  172. if (NtfsIsTopLevelRequest( IrpContext )) {
  173. if (PsGetCurrentProcess() != NtfsData.OurProcess) {
  174. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  175. //
  176. // This close is within the system process. It could be
  177. // the segment derefernce thread. We want to be careful
  178. // about processing the close in this thread. If we
  179. // process the close too slowly we can eventually
  180. // cause a large backlog of file objects within
  181. // MM. We will consider posting under the following conditions.
  182. //
  183. // - There are more that four times as many file objects as handles (AND)
  184. // - The number of excess file objects (CloseCount - CleanupCount) is
  185. // over our async post threshold for this size system.
  186. //
  187. } else {
  188. NtfsAsyncPassCount += 1;
  189. if (FlagOn( NtfsAsyncPassCount, 3 ) &&
  190. (Vcb->CleanupCount * 4 < Vcb->CloseCount) &&
  191. (Vcb->CloseCount - Vcb->CleanupCount > NtfsAsyncPostThreshold + NtfsMaxDelayedCloseCount)) {
  192. Status = STATUS_PENDING;
  193. break;
  194. }
  195. }
  196. //
  197. // This is a recursive Ntfs call. Post this unless we already
  198. // own this file. Otherwise we could deadlock walking
  199. // up the tree. Also if there was any error in the top level post it to
  200. // preserve stack
  201. //
  202. } else if (!NtfsIsExclusiveScb( Scb ) ||
  203. (IrpContext->TopLevelIrpContext->ExceptionStatus != STATUS_SUCCESS )) {
  204. Status = STATUS_PENDING;
  205. break;
  206. }
  207. } else if (Status == STATUS_LOG_FILE_FULL) {
  208. NtfsCheckpointForLogFileFull( IrpContext );
  209. }
  210. //
  211. // If this Scb should go on the delayed close queue then
  212. // status is STATUS_PENDING;
  213. //
  214. if (FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE ) &&
  215. (Scb->Fcb->DelayedCloseCount == 0)) {
  216. Status = STATUS_PENDING;
  217. } else {
  218. Status = NtfsCommonClose( IrpContext,
  219. Scb,
  220. Fcb,
  221. Vcb,
  222. &Ccb,
  223. TypeOfOpen,
  224. IsReadOnly,
  225. FALSE );
  226. }
  227. break;
  228. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  229. //
  230. // We had some trouble trying to perform the requested
  231. // operation, so we'll abort the I/O request with
  232. // the error status that we get back from the
  233. // exception code.
  234. //
  235. if (IrpContext == NULL) {
  236. //
  237. // We could've hit insufficient resources in trying to allocate
  238. // the IrpContext. Make sure we don't leave a reference
  239. // hanging around in this case. ProcessException will complete
  240. // the IRP for us.
  241. //
  242. PLCB Lcb;
  243. ASSERT( GetExceptionCode() == STATUS_INSUFFICIENT_RESOURCES );
  244. if (Ccb != NULL) {
  245. Lcb = Ccb->Lcb;
  246. NtfsUnlinkCcbFromLcb( NULL, Ccb );
  247. NtfsDeleteCcb( Fcb, &Ccb );
  248. } else {
  249. Lcb = NULL;
  250. }
  251. NtfsDecrementCloseCounts( NULL,
  252. Scb,
  253. Lcb,
  254. IsSystemFile,
  255. IsReadOnly,
  256. TRUE );
  257. }
  258. Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
  259. }
  260. ASSERT( NT_SUCCESS( Status ) || (IrpContext == NULL) || IsListEmpty(&IrpContext->ExclusiveFcbList) );
  261. } while (Status == STATUS_CANT_WAIT ||
  262. Status == STATUS_LOG_FILE_FULL);
  263. //
  264. // Io believes that it needs to free the FileObject->FileName.Buffer ONLY
  265. // if FileObject->FileName.Length != 0. Ntfs hides the attribute name
  266. // between FileObject->FileName.Length and FileObject->Filename.MaximumLength
  267. // and for a attribute-name-open relative to a file opened by Id, the Length
  268. // field will be zero. This, alas, causes Io to leak names. So...
  269. //
  270. // If we have a buffer allocated, make sure that the length is not zero when
  271. // Io gets to see it.
  272. //
  273. if (FileObject->FileName.Buffer != NULL) {
  274. FileObject->FileName.Length = 1;
  275. }
  276. //
  277. // Trigger an assert on any unexpected cases.
  278. //
  279. ASSERT( (Status == STATUS_SUCCESS) || (Status == STATUS_PENDING) ||
  280. (Status == STATUS_INSUFFICIENT_RESOURCES) );
  281. //
  282. // Post the request to the close queue on PENDING.
  283. //
  284. if (Status == STATUS_PENDING) {
  285. BOOLEAN DelayCloseQueue = FALSE;
  286. //
  287. // If the status is can't wait, then let's get the information we
  288. // need into the IrpContext, complete the request,
  289. // and post the IrpContext.
  290. //
  291. //
  292. // Restore the thread context pointer if associated with this IrpContext.
  293. //
  294. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
  295. NtfsRestoreTopLevelIrp();
  296. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
  297. }
  298. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
  299. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  300. Status = STATUS_SUCCESS;
  301. IrpContext->OriginatingIrp = (PIRP) Scb;
  302. IrpContext->Union.SubjectContext = (PSECURITY_SUBJECT_CONTEXT) Ccb;
  303. IrpContext->TransactionId = (TRANSACTION_ID) TypeOfOpen;
  304. //
  305. // At this point the file is effectively readonly - by changing it
  306. // here we remove a race with implict locking through volume opens and
  307. // the async close queue. Note: we have NO synchroniation here other
  308. // than the interlocked operation. The vcb will not go away until
  309. // this close is done
  310. //
  311. if (Ccb != NULL) {
  312. if (!IsFileObjectReadOnly( FileObject )) {
  313. FileObject->WriteAccess = 0;
  314. FileObject->DeleteAccess = 0;
  315. InterlockedIncrement( &Vcb->ReadOnlyCloseCount );
  316. }
  317. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
  318. } else {
  319. //
  320. // System files should never be read-only. There will be
  321. // a ccb for all user fileobjects. Internal fileobjects are
  322. // also always marked as system
  323. //
  324. ASSERT( !IsFileObjectReadOnly( FileObject ));
  325. }
  326. //
  327. // Decide which close queue this will go on.
  328. //
  329. if (FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE )) {
  330. NtfsAcquireFsrtlHeader( Scb );
  331. ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
  332. NtfsReleaseFsrtlHeader( Scb );
  333. if (Scb->Fcb->DelayedCloseCount == 0) {
  334. DelayCloseQueue = TRUE;
  335. }
  336. }
  337. NtfsQueueClose( IrpContext, DelayCloseQueue );
  338. //
  339. // Succeed in all other cases.
  340. //
  341. } else {
  342. if (Status == STATUS_SUCCESS) {
  343. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  344. }
  345. //
  346. // INSUFFICIENT_RESOURCES is the only other status that
  347. // we can hit at this point. We would've completed the IRP in
  348. // the except clause above in this case, so don't try doing it again.
  349. //
  350. ASSERT( Status == STATUS_SUCCESS || Status == STATUS_INSUFFICIENT_RESOURCES );
  351. }
  352. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  353. FsRtlExitFileSystem();
  354. //
  355. // And return to our caller
  356. //
  357. DebugTrace( -1, Dbg, ("NtfsFsdClose -> %08lx\n", Status) );
  358. return Status;
  359. }
  360. VOID
  361. NtfsFspClose (
  362. IN PVCB ThisVcb OPTIONAL
  363. )
  364. /*++
  365. Routine Description:
  366. This routine implements the FSP part of Close.
  367. Arguments:
  368. ThisVcb - If specified then we want to remove all closes for a given Vcb.
  369. Otherwise this routine will close all of the async closes and as many
  370. of the delayed closes as possible.
  371. Return Value:
  372. None.
  373. --*/
  374. {
  375. PIRP_CONTEXT IrpContext;
  376. TOP_LEVEL_CONTEXT TopLevelContext;
  377. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  378. TYPE_OF_OPEN TypeOfOpen;
  379. PSCB Scb;
  380. PCCB Ccb;
  381. BOOLEAN ReadOnly;
  382. NTSTATUS Status = STATUS_SUCCESS;
  383. PVCB CurrentVcb = NULL;
  384. BOOLEAN ThrottleCreate = FALSE;
  385. ULONG ClosedCount = 0;
  386. DebugTrace( +1, Dbg, ("NtfsFspClose\n") );
  387. PAGED_CODE();
  388. FsRtlEnterFileSystem();
  389. //
  390. // Occasionally we are called from some other routine to try to
  391. // reduce the backlog of closes. This is indicated by a pointer
  392. // value of 1.
  393. //
  394. if (ThisVcb == (PVCB) 1) {
  395. ThisVcb = NULL;
  396. ThrottleCreate = TRUE;
  397. }
  398. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, FALSE );
  399. ASSERT( ThreadTopLevelContext == &TopLevelContext );
  400. //
  401. // Extract and decode the file object, we are willing to handle the unmounted
  402. // file object. Note we normally get here via an IrpContext which really
  403. // just points to a file object. We should never see an Irp, unless it can
  404. // happen for verify or some other reason.
  405. //
  406. while (IrpContext = NtfsRemoveClose( ThisVcb, ThrottleCreate )) {
  407. ASSERT_IRP_CONTEXT( IrpContext );
  408. //
  409. // Recover the information about the file object being closed from
  410. // the data stored in the IrpContext. The following fields are
  411. // used for this.
  412. //
  413. // OriginatingIrp - Contains the Scb
  414. // SubjectContext - Contains the Ccb
  415. // TransactionId - Contains the TypeOfOpen
  416. // Flags - Has bit for read-only file.
  417. //
  418. Scb = (PSCB) IrpContext->OriginatingIrp;
  419. IrpContext->OriginatingIrp = NULL;
  420. Ccb = (PCCB) IrpContext->Union.SubjectContext;
  421. IrpContext->Union.SubjectContext = NULL;
  422. TypeOfOpen = (TYPE_OF_OPEN) IrpContext->TransactionId;
  423. IrpContext->TransactionId = 0;
  424. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO )) {
  425. ReadOnly = TRUE;
  426. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
  427. } else {
  428. ReadOnly = FALSE;
  429. }
  430. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST );
  431. SetFlag( IrpContext->State,
  432. IRP_CONTEXT_STATE_IN_FSP | IRP_CONTEXT_STATE_WAIT );
  433. //
  434. // Loop for retryable errors.
  435. //
  436. Status = STATUS_SUCCESS;
  437. do {
  438. //
  439. // Set the TopLevel structure.
  440. //
  441. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  442. //
  443. // Call the common Close routine.
  444. //
  445. try {
  446. //
  447. // Do logfile full checkpointing
  448. //
  449. if (Status == STATUS_LOG_FILE_FULL) {
  450. NtfsCheckpointForLogFileFull( IrpContext );
  451. }
  452. CurrentVcb = IrpContext->Vcb;
  453. Status = NtfsCommonClose( IrpContext,
  454. Scb,
  455. Scb->Fcb,
  456. IrpContext->Vcb,
  457. &Ccb,
  458. TypeOfOpen,
  459. ReadOnly,
  460. TRUE );
  461. ASSERT(Status == STATUS_SUCCESS);
  462. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  463. Status = NtfsProcessException( IrpContext, NULL, GetExceptionCode() );
  464. }
  465. ASSERT( NT_SUCCESS(Status) || IsListEmpty(&IrpContext->ExclusiveFcbList) );
  466. //
  467. // If we got a log file full, and our caller may have something
  468. // acquired, then clean up and raise again.
  469. //
  470. if (((Status == STATUS_LOG_FILE_FULL) ||
  471. (Status == STATUS_CANT_WAIT)) &&
  472. ARGUMENT_PRESENT( ThisVcb )) {
  473. //
  474. // If the status is can't wait, then let's get the information we
  475. // need into the IrpContext, complete the request,
  476. // and post the IrpContext.
  477. //
  478. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE );
  479. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  480. //
  481. // Restore the information on the file object being closed.
  482. //
  483. IrpContext->OriginatingIrp = (PIRP)Scb;
  484. IrpContext->Union.SubjectContext = (PVOID)Ccb;
  485. IrpContext->TransactionId = TypeOfOpen;
  486. if (ReadOnly) {
  487. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_READ_ONLY_FO );
  488. }
  489. //
  490. // Now queue the close as an async close and get out.
  491. //
  492. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL )) {
  493. NtfsRestoreTopLevelIrp();
  494. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL );
  495. }
  496. NtfsQueueClose( IrpContext, FALSE );
  497. FsRtlExitFileSystem();
  498. ExRaiseStatus( Status );
  499. }
  500. } while ((Status == STATUS_LOG_FILE_FULL) || (Status == STATUS_CANT_WAIT));
  501. //
  502. // No more for us to do. Clean up the IrpContext in any case.
  503. //
  504. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  505. //
  506. // If we were just throttling creates and we made our last pass
  507. // then exit.
  508. //
  509. if (ThrottleCreate) {
  510. break;
  511. }
  512. }
  513. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  514. FsRtlExitFileSystem();
  515. //
  516. // And return to our caller
  517. //
  518. DebugTrace( -1, Dbg, ("NtfsFspClose -> NULL\n") );
  519. return;
  520. }
  521. BOOLEAN
  522. NtfsAddScbToFspClose (
  523. IN PIRP_CONTEXT IrpContext,
  524. IN PSCB Scb,
  525. IN BOOLEAN DelayClose
  526. )
  527. /*++
  528. Routine Description:
  529. This routine is called to add an entry for the current Scb onto one
  530. of the Fsp close queues. This is used when we want to guarantee that
  531. a teardown will be called on an Scb or Fcb when the current operation
  532. can't begin the operation.
  533. Arguments:
  534. Scb - Scb to add to the queue.
  535. DelayClose - Indicates which queue this should go into.
  536. Return Value:
  537. BOOLEAN - Indicates whether or not the SCB was added to the delayed
  538. close queue
  539. --*/
  540. {
  541. PIRP_CONTEXT NewIrpContext = NULL;
  542. BOOLEAN Result = TRUE;
  543. PAGED_CODE();
  544. //
  545. // Use a try-except to catch any allocation failures. The only valid
  546. // error here is an allocation failure for the new irp context.
  547. //
  548. try {
  549. NtfsInitializeIrpContext( NULL, TRUE, &NewIrpContext );
  550. //
  551. // Set the necessary fields to post this to the workqueue.
  552. //
  553. NewIrpContext->Vcb = Scb->Vcb;
  554. NewIrpContext->MajorFunction = IRP_MJ_CLOSE;
  555. NewIrpContext->OriginatingIrp = (PIRP) Scb;
  556. NewIrpContext->TransactionId = (TRANSACTION_ID) StreamFileOpen;
  557. //
  558. // Now increment the close counts for this Scb.
  559. //
  560. NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
  561. //
  562. // Move the Scb to the end of the Fcb queue. We don't want to
  563. // keep other Scb's from being deleted because this one is on
  564. // the delayed close queue.
  565. //
  566. if (Scb->FcbLinks.Flink != &Scb->Fcb->ScbQueue) {
  567. NtfsLockFcb( IrpContext, Scb->Fcb );
  568. RemoveEntryList( &Scb->FcbLinks );
  569. InsertTailList( &Scb->Fcb->ScbQueue, &Scb->FcbLinks );
  570. ASSERT( Scb->FcbLinks.Flink == &Scb->Fcb->ScbQueue );
  571. NtfsUnlockFcb( IrpContext, Scb->Fcb );
  572. }
  573. //
  574. // Now add this to the correct queue.
  575. //
  576. NtfsQueueClose( NewIrpContext, DelayClose );
  577. } except( FsRtlIsNtstatusExpected( GetExceptionCode() ) ?
  578. EXCEPTION_EXECUTE_HANDLER :
  579. EXCEPTION_CONTINUE_SEARCH ) {
  580. NtfsMinimumExceptionProcessing( IrpContext );
  581. Result = FALSE;
  582. }
  583. return Result;
  584. UNREFERENCED_PARAMETER( IrpContext );
  585. }
  586. //
  587. // Internal support routine
  588. //
  589. NTSTATUS
  590. NtfsCommonClose (
  591. IN PIRP_CONTEXT IrpContext,
  592. IN PSCB Scb,
  593. IN PFCB Fcb,
  594. IN PVCB Vcb,
  595. IN PCCB *Ccb,
  596. IN TYPE_OF_OPEN TypeOfOpen,
  597. IN BOOLEAN ReadOnly,
  598. IN BOOLEAN CalledFromFsp
  599. )
  600. /*++
  601. Routine Description:
  602. This is the common routine for Close called by both the fsd and fsp
  603. threads. Key for this routine is how to acquire the Vcb and whether to
  604. leave the Vcb acquired on exit.
  605. Arguments:
  606. Scb - Scb for this stream.
  607. Fcb - Fcb for this stream.
  608. Vcb - Vcb for this volume.
  609. Ccb - User's Ccb for user files.
  610. TypeOfOpen - Indicates the type of open for this stream.
  611. ReadOnly - Indicates if the file object was for read-only access.
  612. CalledFromFsp - Indicates whether this function was called from NtfsFspClose.
  613. Return Value:
  614. NTSTATUS - The return status for the operation
  615. --*/
  616. {
  617. BOOLEAN ExclusiveVcb = FALSE;
  618. BOOLEAN AcquiredFcb = FALSE;
  619. BOOLEAN SystemFile;
  620. BOOLEAN RemovedFcb = FALSE;
  621. ULONG AcquireFlags = ACQUIRE_NO_DELETE_CHECK | ACQUIRE_HOLD_BITMAP;
  622. BOOLEAN NeedVcbExclusive = FALSE;
  623. BOOLEAN WriteFileSize;
  624. NTSTATUS Status = STATUS_SUCCESS;
  625. PLCB Lcb;
  626. ASSERT_IRP_CONTEXT( IrpContext );
  627. PAGED_CODE();
  628. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  629. //
  630. // Get the current Irp stack location
  631. //
  632. DebugTrace( +1, Dbg, ("NtfsCommonClose\n") );
  633. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  634. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  635. SetFlag( AcquireFlags, ACQUIRE_DONT_WAIT );
  636. }
  637. //
  638. // Loop here to acquire both the Vcb and Fcb. We want to acquire
  639. // the Vcb exclusively if the file has multiple links.
  640. //
  641. while (TRUE) {
  642. WriteFileSize = FALSE;
  643. //
  644. // Perform an unsafe test and optimistically acquire Vcb.
  645. //
  646. if (NeedVcbExclusive ||
  647. (Fcb->LcbQueue.Flink != Fcb->LcbQueue.Blink) ||
  648. FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) {
  649. if (!NtfsAcquireExclusiveVcb( IrpContext, Vcb, FALSE )) {
  650. return STATUS_PENDING;
  651. }
  652. ExclusiveVcb = TRUE;
  653. } else {
  654. if (!NtfsAcquireSharedVcb( IrpContext, Vcb, FALSE )) {
  655. return STATUS_PENDING;
  656. }
  657. }
  658. //
  659. // Now try to acquire the Fcb. If we are unable to acquire it then
  660. // release the Vcb and return. This can only be from the Fsd path
  661. // since otherwise Wait will be TRUE.
  662. //
  663. if (!NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, AcquireFlags )) {
  664. //
  665. // Always release the Vcb. This can only be from the Fsd thread.
  666. //
  667. NtfsReleaseVcb( IrpContext, Vcb );
  668. return STATUS_PENDING;
  669. }
  670. AcquiredFcb = TRUE;
  671. //
  672. // Recheck scbstate now that we own the fcb exclusive to see if we need
  673. // to write the filesize at this point
  674. //
  675. if ((!FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) &&
  676. (!FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) &&
  677. (FlagOn( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE )) &&
  678. (Fcb->LinkCount > 0) &&
  679. (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ))) {
  680. WriteFileSize = TRUE;
  681. NtfsReleaseFcb( IrpContext, Fcb );
  682. AcquiredFcb = FALSE;
  683. //
  684. // NtfsAcquireWithPaging only gets the paging if the irpcontext
  685. // flag is set. Also it assumes no delete check which we explictly
  686. // want here anyway.
  687. //
  688. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING );
  689. if (!NtfsAcquireFcbWithPaging( IrpContext, Fcb, AcquireFlags )) {
  690. NtfsReleaseVcb( IrpContext, Vcb );
  691. return STATUS_PENDING;
  692. }
  693. AcquiredFcb = TRUE;
  694. //
  695. // Recapture whether we need to write file size since dropping
  696. //
  697. if ((!FlagOn( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE )) ||
  698. (Fcb->LinkCount == 0) ||
  699. (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ))) {
  700. WriteFileSize = FALSE;
  701. }
  702. }
  703. if (ExclusiveVcb) {
  704. break;
  705. }
  706. //
  707. // Otherwise we need to confirm that our unsafe test above was correct.
  708. //
  709. if ((Fcb->LcbQueue.Flink != Fcb->LcbQueue.Blink) ||
  710. FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT )) {
  711. NeedVcbExclusive = TRUE;
  712. NtfsReleaseFcb( IrpContext, Fcb );
  713. NtfsReleaseVcb( IrpContext, Vcb );
  714. AcquiredFcb = FALSE;
  715. } else {
  716. break;
  717. }
  718. }
  719. //
  720. // Set the wait flag in the IrpContext so we can acquire any other files
  721. // we encounter.
  722. //
  723. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  724. try {
  725. //
  726. // See if we possibly have to do any Usn processing
  727. //
  728. if (Fcb->FcbUsnRecord != NULL) {
  729. //
  730. // If the file has no more user handles, but there is a pending Usn
  731. // update (this should normally only happen if a stream was mapped
  732. // by the user), then scan the streams to see if there are any
  733. // remaining datasections, and if not then post the close.
  734. //
  735. if ((Fcb->CleanupCount == 0) &&
  736. (Fcb->FcbUsnRecord->UsnRecord.Reason != 0)) {
  737. if (!FlagOn( Vcb->VcbState, VCB_STATE_LOCKED ) &&
  738. !FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED ) &&
  739. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_FAILED_CLOSE )) {
  740. PSCB TempScb;
  741. //
  742. // Leave if there are any streams with user-mapped files.
  743. //
  744. TempScb = (PSCB)CONTAINING_RECORD( Fcb->ScbQueue.Flink,
  745. SCB,
  746. FcbLinks );
  747. while (&TempScb->FcbLinks != &Fcb->ScbQueue) {
  748. if ((TempScb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  749. !MmCanFileBeTruncated( &TempScb->NonpagedScb->SegmentObject, &Li0)) {
  750. goto NoPost;
  751. }
  752. TempScb = (PSCB)CONTAINING_RECORD( TempScb->FcbLinks.Flink,
  753. SCB,
  754. FcbLinks );
  755. }
  756. //
  757. // If we are not supposed to wait, then we should force this request to
  758. // be posted. All recursive closes will go here since they are async
  759. //
  760. if (FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT )) {
  761. Status = STATUS_PENDING;
  762. leave;
  763. }
  764. //
  765. // We cannot generate logfile fulls in a regular thread with a recursive close
  766. // safely without deadlocking
  767. //
  768. ASSERT( NtfsIsTopLevelRequest( IrpContext ) ||
  769. FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP ) );
  770. //
  771. // Protect the call to the Usn routines with a try-except. If we hit
  772. // any non-fatal error then set the IrpContext flag which indicates
  773. // not to bother with the Usn and force a retry.
  774. //
  775. try {
  776. //
  777. // Now try to actually post the change.
  778. //
  779. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_CLOSE );
  780. //
  781. // Now write the journal, checkpoint the transaction, and free the UsnJournal to
  782. // reduce contention. We force the write now, because the Fcb may get deleted
  783. // before we normally would write the changes when the transaction commits.
  784. //
  785. NtfsWriteUsnJournalChanges( IrpContext );
  786. NtfsCheckpointCurrentTransaction( IrpContext );
  787. } except( (!FsRtlIsNtstatusExpected( Status = GetExceptionCode() ) ||
  788. (Status == STATUS_LOG_FILE_FULL) ||
  789. (Status == STATUS_CANT_WAIT)) ?
  790. EXCEPTION_CONTINUE_SEARCH :
  791. EXCEPTION_EXECUTE_HANDLER ) {
  792. //
  793. // We got some sort of error processing the Usn journal. We can't
  794. // handle it in the close path. Let's retry this request but don't
  795. // try to do the Usn operation.
  796. //
  797. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_FAILED_CLOSE );
  798. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  799. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  800. }
  801. //
  802. // Free any remaining resources before decrementing close counts below,
  803. // except for our Fcb. This reduces contention via the Usn Journal and
  804. // prevents deadlocks since the Usn Journal is acquired last.
  805. //
  806. ASSERT(Fcb->ExclusiveFcbLinks.Flink != NULL);
  807. while (!IsListEmpty(&IrpContext->ExclusiveFcbList)) {
  808. if (&Fcb->ExclusiveFcbLinks == IrpContext->ExclusiveFcbList.Flink) {
  809. RemoveEntryList( &Fcb->ExclusiveFcbLinks );
  810. Fcb->ExclusiveFcbLinks.Flink = NULL;
  811. } else {
  812. NtfsReleaseFcb( IrpContext,
  813. (PFCB)CONTAINING_RECORD(IrpContext->ExclusiveFcbList.Flink,
  814. FCB,
  815. ExclusiveFcbLinks ));
  816. }
  817. }
  818. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  819. IRP_CONTEXT_FLAG_RELEASE_MFT );
  820. //
  821. // Now reinsert our Fcb if we removed it from the list. Check the Flink
  822. // field to know if this is the case. Otherwise a higher level IrpContext
  823. // will own this.
  824. //
  825. if (Fcb->ExclusiveFcbLinks.Flink == NULL) {
  826. InsertTailList( &IrpContext->ExclusiveFcbList, &Fcb->ExclusiveFcbLinks );
  827. }
  828. //
  829. // Escape here if we are not posting the close due to a user-mapped file.
  830. //
  831. NoPost: NOTHING;
  832. }
  833. }
  834. }
  835. //
  836. // Now rewrite the filesizes if we have to
  837. //
  838. if (WriteFileSize) {
  839. ASSERT( IrpContext->CleanupStructure != NULL );
  840. //
  841. // If the call to write the file size or the commit produces a logfile full
  842. // we must retry in the fsp thread to prevent deadlocking from
  843. // a recursive caller's already owning the vcb and an attempt to
  844. // checkpoint
  845. //
  846. try {
  847. NtfsWriteFileSizes( IrpContext, Scb, &Scb->Header.ValidDataLength.QuadPart, TRUE, TRUE, FALSE );
  848. NtfsCheckpointCurrentTransaction( IrpContext );
  849. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE );
  850. } except( (Status = GetExceptionCode()), (Status != STATUS_LOG_FILE_FULL || FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP )) ?
  851. EXCEPTION_CONTINUE_SEARCH :
  852. EXCEPTION_EXECUTE_HANDLER ) {
  853. NtfsMinimumExceptionProcessing( IrpContext );
  854. Status = STATUS_PENDING;
  855. }
  856. if (Status == STATUS_PENDING) {
  857. leave;
  858. }
  859. } // endif writing filesize
  860. //
  861. // We take the same action for all open files. We
  862. // delete the Ccb if present, and we decrement the close
  863. // file counts.
  864. //
  865. if ((*Ccb) != NULL) {
  866. Lcb = (*Ccb)->Lcb;
  867. NtfsUnlinkCcbFromLcb( IrpContext, (*Ccb) );
  868. NtfsDeleteCcb( Fcb, Ccb );
  869. } else {
  870. Lcb = NULL;
  871. }
  872. SystemFile = FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) || (TypeOfOpen == StreamFileOpen);
  873. RemovedFcb = NtfsDecrementCloseCounts( IrpContext,
  874. Scb,
  875. Lcb,
  876. SystemFile,
  877. ReadOnly,
  878. FALSE );
  879. //
  880. // Now that we're holding the Vcb, and we're past the point where we might
  881. // raise log file full, we can safely adjust this field.
  882. //
  883. if (CalledFromFsp) {
  884. InterlockedDecrement( &Vcb->QueuedCloseCount );
  885. }
  886. //
  887. // If we had to write a log record for close, it can only be for duplicate
  888. // information. We will commit that transaction here and remove
  889. // the entry from the transaction table. We do it here so we won't
  890. // fail inside the 'except' of a 'try-except'.
  891. //
  892. if (IrpContext->TransactionId != 0) {
  893. try {
  894. NtfsCommitCurrentTransaction( IrpContext );
  895. } except( EXCEPTION_EXECUTE_HANDLER ) {
  896. NtfsMinimumExceptionProcessing( IrpContext );
  897. if (IrpContext->TransactionId != 0) {
  898. NtfsCleanupFailedTransaction( IrpContext );
  899. }
  900. }
  901. }
  902. } finally {
  903. DebugUnwind( NtfsCommonClose );
  904. //
  905. // Manage fcb explictly because we recursively come into this path
  906. // and its cleaner to release the fcb at the same level in which you acquire it
  907. //
  908. if (AcquiredFcb && !RemovedFcb) {
  909. NtfsReleaseFcb( IrpContext, Fcb );
  910. }
  911. if (ExclusiveVcb) {
  912. NtfsReleaseVcbCheckDelete( IrpContext, Vcb, IRP_MJ_CLOSE, NULL );
  913. } else {
  914. NtfsReleaseVcb( IrpContext, Vcb );
  915. }
  916. DebugTrace( -1, Dbg, ("NtfsCommonClose -> returning\n") );
  917. }
  918. return Status;
  919. }
  920. //
  921. // Internal support routine, spinlock wrapper.
  922. //
  923. VOID
  924. NtfsQueueClose (
  925. IN PIRP_CONTEXT IrpContext,
  926. IN BOOLEAN DelayClose
  927. )
  928. {
  929. KIRQL SavedIrql;
  930. BOOLEAN StartWorker = FALSE;
  931. InterlockedIncrement( &(IrpContext->Vcb->QueuedCloseCount) );
  932. if (DelayClose) {
  933. //
  934. // Increment the delayed close count for the Fcb for this
  935. // file.
  936. //
  937. InterlockedIncrement( &((PSCB) IrpContext->OriginatingIrp)->Fcb->DelayedCloseCount );
  938. SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
  939. InsertTailList( &NtfsData.DelayedCloseList,
  940. &IrpContext->WorkQueueItem.List );
  941. NtfsData.DelayedCloseCount += 1;
  942. if (NtfsData.DelayedCloseCount > NtfsMaxDelayedCloseCount) {
  943. NtfsData.ReduceDelayedClose = TRUE;
  944. if (!NtfsData.AsyncCloseActive) {
  945. NtfsData.AsyncCloseActive = TRUE;
  946. StartWorker = TRUE;
  947. }
  948. }
  949. } else {
  950. SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
  951. InsertTailList( &NtfsData.AsyncCloseList,
  952. &IrpContext->WorkQueueItem.List );
  953. NtfsData.AsyncCloseCount += 1;
  954. if (!NtfsData.AsyncCloseActive) {
  955. NtfsData.AsyncCloseActive = TRUE;
  956. StartWorker = TRUE;
  957. }
  958. }
  959. KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, SavedIrql );
  960. if (StartWorker) {
  961. ExQueueWorkItem( &NtfsData.NtfsCloseItem, CriticalWorkQueue );
  962. }
  963. }
  964. //
  965. // Internal support routine, spinlock wrapper.
  966. //
  967. PIRP_CONTEXT
  968. NtfsRemoveClose (
  969. IN PVCB Vcb OPTIONAL,
  970. IN BOOLEAN ThrottleCreate
  971. )
  972. {
  973. PLIST_ENTRY Entry;
  974. KIRQL SavedIrql;
  975. PIRP_CONTEXT IrpContext = NULL;
  976. BOOLEAN FromDelayedClose = FALSE;
  977. SavedIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
  978. //
  979. // First check the list of async closes.
  980. //
  981. if (!IsListEmpty( &NtfsData.AsyncCloseList )) {
  982. Entry = NtfsData.AsyncCloseList.Flink;
  983. while (Entry != &NtfsData.AsyncCloseList) {
  984. //
  985. // Extract the IrpContext.
  986. //
  987. IrpContext = CONTAINING_RECORD( Entry,
  988. IRP_CONTEXT,
  989. WorkQueueItem.List );
  990. //
  991. // If no Vcb was specified or this Vcb is for our volume
  992. // then perform the close.
  993. //
  994. if (!ARGUMENT_PRESENT( Vcb ) ||
  995. IrpContext->Vcb == Vcb) {
  996. RemoveEntryList( Entry );
  997. NtfsData.AsyncCloseCount -= 1;
  998. break;
  999. } else {
  1000. IrpContext = NULL;
  1001. Entry = Entry->Flink;
  1002. }
  1003. }
  1004. }
  1005. //
  1006. // If we didn't find anything look through the delayed close
  1007. // queue.
  1008. //
  1009. if (IrpContext == NULL) {
  1010. //
  1011. // Now check our delayed close list.
  1012. //
  1013. if (ARGUMENT_PRESENT( Vcb )) {
  1014. Entry = NtfsData.DelayedCloseList.Flink;
  1015. IrpContext = NULL;
  1016. //
  1017. // If we were given a Vcb, only do the closes for this volume.
  1018. //
  1019. while (Entry != &NtfsData.DelayedCloseList) {
  1020. //
  1021. // Extract the IrpContext.
  1022. //
  1023. IrpContext = CONTAINING_RECORD( Entry,
  1024. IRP_CONTEXT,
  1025. WorkQueueItem.List );
  1026. //
  1027. // Is this close on our volume?
  1028. //
  1029. if (IrpContext->Vcb == Vcb) {
  1030. RemoveEntryList( Entry );
  1031. NtfsData.DelayedCloseCount -= 1;
  1032. FromDelayedClose = TRUE;
  1033. break;
  1034. } else {
  1035. IrpContext = NULL;
  1036. Entry = Entry->Flink;
  1037. }
  1038. }
  1039. //
  1040. // Check if need to reduce the delayed close count.
  1041. //
  1042. } else if (NtfsData.ReduceDelayedClose) {
  1043. if (NtfsData.DelayedCloseCount > NtfsMinDelayedCloseCount) {
  1044. //
  1045. // Do any closes over the limit.
  1046. //
  1047. Entry = RemoveHeadList( &NtfsData.DelayedCloseList );
  1048. NtfsData.DelayedCloseCount -= 1;
  1049. //
  1050. // Extract the IrpContext.
  1051. //
  1052. IrpContext = CONTAINING_RECORD( Entry,
  1053. IRP_CONTEXT,
  1054. WorkQueueItem.List );
  1055. FromDelayedClose = TRUE;
  1056. } else {
  1057. NtfsData.ReduceDelayedClose = FALSE;
  1058. }
  1059. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1060. } else {
  1061. ASSERT( NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount );
  1062. #endif
  1063. }
  1064. }
  1065. //
  1066. // If this is the delayed close case then decrement the delayed close count
  1067. // on this Fcb.
  1068. //
  1069. if (FromDelayedClose) {
  1070. KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, SavedIrql );
  1071. InterlockedDecrement( &((PSCB) IrpContext->OriginatingIrp)->Fcb->DelayedCloseCount );
  1072. //
  1073. // If we are returning NULL, show that we are done.
  1074. //
  1075. } else {
  1076. if (!ARGUMENT_PRESENT( Vcb ) &&
  1077. (IrpContext == NULL) &&
  1078. !ThrottleCreate) {
  1079. NtfsData.AsyncCloseActive = FALSE;
  1080. }
  1081. KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, SavedIrql );
  1082. }
  1083. ASSERT( (Vcb == NULL) || NtfsIsExclusiveVcb( Vcb ) || (IrpContext == NULL) );
  1084. return IrpContext;
  1085. }