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.

2703 lines
87 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Flush.c
  5. Abstract:
  6. This module implements the flush buffers routine for Ntfs called by the
  7. dispatch driver.
  8. Author:
  9. Tom Miller [TomM] 18-Jan-1992
  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_FLUSH)
  17. //
  18. // The local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_FLUSH)
  21. //
  22. // Macro to attempt to flush a stream from an Scb.
  23. //
  24. #define FlushScb(IRPC,SCB,IOS) { \
  25. (IOS)->Status = NtfsFlushUserStream((IRPC),(SCB),NULL,0); \
  26. NtfsNormalizeAndCleanupTransaction( IRPC, \
  27. &(IOS)->Status, \
  28. TRUE, \
  29. STATUS_UNEXPECTED_IO_ERROR ); \
  30. if (FlagOn((SCB)->ScbState, SCB_STATE_FILE_SIZE_LOADED)) { \
  31. NtfsWriteFileSizes( (IRPC), \
  32. (SCB), \
  33. &(SCB)->Header.ValidDataLength.QuadPart, \
  34. TRUE, \
  35. TRUE, \
  36. TRUE ); \
  37. } \
  38. }
  39. //
  40. // Local procedure prototypes
  41. //
  42. NTSTATUS
  43. NtfsFlushCompletionRoutine(
  44. IN PDEVICE_OBJECT DeviceObject,
  45. IN PIRP Irp,
  46. IN PVOID Contxt
  47. );
  48. NTSTATUS
  49. NtfsFlushFcbFileRecords (
  50. IN PIRP_CONTEXT IrpContext,
  51. IN PFCB Fcb
  52. );
  53. LONG
  54. NtfsFlushVolumeExceptionFilter (
  55. IN PIRP_CONTEXT IrpContext,
  56. IN PEXCEPTION_POINTERS ExceptionPointer,
  57. IN NTSTATUS ExceptionCode
  58. );
  59. #ifdef ALLOC_PRAGMA
  60. #pragma alloc_text(PAGE, NtfsCommonFlushBuffers)
  61. #pragma alloc_text(PAGE, NtfsFlushAndPurgeFcb)
  62. #pragma alloc_text(PAGE, NtfsFlushAndPurgeScb)
  63. #pragma alloc_text(PAGE, NtfsFlushFcbFileRecords)
  64. #pragma alloc_text(PAGE, NtfsFlushLsnStreams)
  65. #pragma alloc_text(PAGE, NtfsFlushVolume)
  66. #pragma alloc_text(PAGE, NtfsFsdFlushBuffers)
  67. #pragma alloc_text(PAGE, NtfsFlushUserStream)
  68. #endif
  69. NTSTATUS
  70. NtfsFsdFlushBuffers (
  71. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  72. IN PIRP Irp
  73. )
  74. /*++
  75. Routine Description:
  76. This routine implements the FSD part of flush buffers.
  77. Arguments:
  78. VolumeDeviceObject - Supplies the volume device object where the
  79. file exists
  80. Irp - Supplies the Irp being processed
  81. Return Value:
  82. NTSTATUS - The FSD status for the IRP
  83. --*/
  84. {
  85. TOP_LEVEL_CONTEXT TopLevelContext;
  86. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  87. NTSTATUS Status = STATUS_SUCCESS;
  88. PIRP_CONTEXT IrpContext = NULL;
  89. ASSERT_IRP( Irp );
  90. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  91. PAGED_CODE();
  92. DebugTrace( +1, Dbg, ("NtfsFsdFlushBuffers\n") );
  93. //
  94. // Call the common flush buffer routine
  95. //
  96. FsRtlEnterFileSystem();
  97. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  98. do {
  99. try {
  100. //
  101. // We are either initiating this request or retrying it.
  102. //
  103. if (IrpContext == NULL) {
  104. //
  105. // Allocate and initialize the Irp.
  106. //
  107. NtfsInitializeIrpContext( Irp, CanFsdWait( Irp ), &IrpContext );
  108. //
  109. // Initialize the thread top level structure, if needed.
  110. //
  111. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  112. } else if (Status == STATUS_LOG_FILE_FULL) {
  113. NtfsCheckpointForLogFileFull( IrpContext );
  114. }
  115. Status = NtfsCommonFlushBuffers( IrpContext, Irp );
  116. break;
  117. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  118. //
  119. // We had some trouble trying to perform the requested
  120. // operation, so we'll abort the I/O request with
  121. // the error status that we get back from the
  122. // execption code
  123. //
  124. Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
  125. }
  126. } while (Status == STATUS_CANT_WAIT ||
  127. Status == STATUS_LOG_FILE_FULL);
  128. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  129. FsRtlExitFileSystem();
  130. //
  131. // And return to our caller
  132. //
  133. DebugTrace( -1, Dbg, ("NtfsFsdFlushBuffers -> %08lx\n", Status) );
  134. return Status;
  135. }
  136. NTSTATUS
  137. NtfsCommonFlushBuffers (
  138. IN PIRP_CONTEXT IrpContext,
  139. IN PIRP Irp
  140. )
  141. /*++
  142. Routine Description:
  143. This is the common routine for flush buffers called by both the fsd and fsp
  144. threads.
  145. Arguments:
  146. Irp - Supplies the Irp to process
  147. Return Value:
  148. NTSTATUS - The return status for the operation
  149. --*/
  150. {
  151. NTSTATUS Status;
  152. PIO_STACK_LOCATION IrpSp;
  153. PFILE_OBJECT FileObject;
  154. TYPE_OF_OPEN TypeOfOpen;
  155. PVCB Vcb;
  156. PFCB Fcb;
  157. PSCB Scb;
  158. PCCB Ccb;
  159. PLCB Lcb = NULL;
  160. PSCB ParentScb = NULL;
  161. BOOLEAN VcbAcquired = FALSE;
  162. BOOLEAN ScbAcquired = FALSE;
  163. BOOLEAN ParentScbAcquired = FALSE;
  164. ASSERT_IRP_CONTEXT( IrpContext );
  165. ASSERT_IRP( Irp );
  166. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  167. PAGED_CODE();
  168. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  169. DebugTrace( +1, Dbg, ("NtfsCommonFlushBuffers\n") );
  170. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  171. DebugTrace( 0, Dbg, ("->FileObject = %08lx\n", IrpSp->FileObject) );
  172. //
  173. // Extract and decode the file object
  174. //
  175. FileObject = IrpSp->FileObject;
  176. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  177. //
  178. // abort immediately for non files
  179. //
  180. if (UnopenedFileObject == TypeOfOpen) {
  181. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  182. return STATUS_INVALID_PARAMETER;
  183. }
  184. //
  185. // Nuthin-doing if the volume is mounted read only.
  186. //
  187. if (NtfsIsVolumeReadOnly( Vcb )) {
  188. Status = STATUS_MEDIA_WRITE_PROTECTED;
  189. NtfsCompleteRequest( IrpContext, Irp, Status );
  190. DebugTrace( -1, Dbg, ("NtfsCommonFlushBuffers -> %08lx\n", Status) );
  191. return Status;
  192. }
  193. Status = STATUS_SUCCESS;
  194. try {
  195. //
  196. // Case on the type of open that we are trying to flush
  197. //
  198. switch (TypeOfOpen) {
  199. case UserFileOpen:
  200. DebugTrace( 0, Dbg, ("Flush User File Open\n") );
  201. //
  202. // Acquire the Vcb so we can update the duplicate information as well.
  203. //
  204. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  205. VcbAcquired = TRUE;
  206. //
  207. // While we have the Vcb, let's make sure it's still mounted.
  208. //
  209. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  210. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  211. }
  212. //
  213. // Make sure the data gets out to disk.
  214. //
  215. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  216. //
  217. // Acquire exclusive access to the Scb and enqueue the irp
  218. // if we didn't get access
  219. //
  220. NtfsAcquireExclusiveScb( IrpContext, Scb );
  221. ScbAcquired = TRUE;
  222. //
  223. // Flush the stream and verify there were no errors.
  224. //
  225. FlushScb( IrpContext, Scb, &Irp->IoStatus );
  226. //
  227. // Now commit what we've done so far.
  228. //
  229. NtfsCheckpointCurrentTransaction( IrpContext );
  230. //
  231. // Update the time stamps and file sizes in the Fcb based on
  232. // the state of the File Object.
  233. //
  234. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  235. //
  236. // If we are to update standard information then do so now.
  237. //
  238. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  239. NtfsUpdateStandardInformation( IrpContext, Fcb );
  240. }
  241. //
  242. // If this is the system hive there is more work to do. We want to flush
  243. // all of the file records for this file as well as for the parent index
  244. // stream. We also want to flush the parent index stream. Acquire the
  245. // parent stream exclusively now so that the update duplicate call won't
  246. // acquire it shared first.
  247. //
  248. if (FlagOn( Ccb->Flags, CCB_FLAG_SYSTEM_HIVE )) {
  249. //
  250. // Start by acquiring all of the necessary files to avoid deadlocks.
  251. //
  252. if (Ccb->Lcb != NULL) {
  253. ParentScb = Ccb->Lcb->Scb;
  254. if (ParentScb != NULL) {
  255. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  256. ParentScbAcquired = TRUE;
  257. }
  258. }
  259. }
  260. //
  261. // Update the duplicate information if there are updates to apply.
  262. //
  263. if (FlagOn( Fcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS )) {
  264. Lcb = Ccb->Lcb;
  265. NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &Lcb, &ParentScb, TRUE );
  266. NtfsUpdateDuplicateInfo( IrpContext, Fcb, Lcb, ParentScb );
  267. NtfsUpdateLcbDuplicateInfo( Fcb, Lcb );
  268. if (ParentScbAcquired) {
  269. NtfsReleaseScb( IrpContext, ParentScb );
  270. ParentScbAcquired = FALSE;
  271. }
  272. }
  273. //
  274. // Now flush the file records for this stream.
  275. //
  276. if (FlagOn( Ccb->Flags, CCB_FLAG_SYSTEM_HIVE )) {
  277. //
  278. // Flush the file records for this file.
  279. //
  280. Status = NtfsFlushFcbFileRecords( IrpContext, Scb->Fcb );
  281. //
  282. // Now flush the parent index stream.
  283. //
  284. if (NT_SUCCESS(Status) && (ParentScb != NULL)) {
  285. CcFlushCache( &ParentScb->NonpagedScb->SegmentObject, NULL, 0, &Irp->IoStatus );
  286. Status = Irp->IoStatus.Status;
  287. //
  288. // Finish by flushing the file records for the parent out
  289. // to disk.
  290. //
  291. if (NT_SUCCESS( Status )) {
  292. Status = NtfsFlushFcbFileRecords( IrpContext, ParentScb->Fcb );
  293. }
  294. }
  295. }
  296. //
  297. // If our status is still success then flush the log file and
  298. // report any changes.
  299. //
  300. if (NT_SUCCESS( Status )) {
  301. ULONG FilterMatch;
  302. LfsFlushToLsn( Vcb->LogHandle, LiMax );
  303. //
  304. // We only want to do this DirNotify if we updated duplicate
  305. // info and set the ParentScb.
  306. //
  307. if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID ) &&
  308. (Vcb->NotifyCount != 0) &&
  309. FlagOn( Fcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS )) {
  310. FilterMatch = NtfsBuildDirNotifyFilter( IrpContext, Fcb->InfoFlags );
  311. if (FilterMatch != 0) {
  312. NtfsReportDirNotify( IrpContext,
  313. Fcb->Vcb,
  314. &Ccb->FullFileName,
  315. Ccb->LastFileNameOffset,
  316. NULL,
  317. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  318. (Ccb->Lcb != NULL) &&
  319. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  320. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  321. NULL),
  322. FilterMatch,
  323. FILE_ACTION_MODIFIED,
  324. ParentScb->Fcb );
  325. }
  326. }
  327. ClearFlag( Fcb->InfoFlags,
  328. FCB_INFO_NOTIFY_FLAGS | FCB_INFO_DUPLICATE_FLAGS );
  329. }
  330. break;
  331. case UserViewIndexOpen:
  332. case UserDirectoryOpen:
  333. //
  334. // If the user had opened the root directory then we'll
  335. // oblige by flushing the volume.
  336. //
  337. if (NodeType(Scb) != NTFS_NTC_SCB_ROOT_INDEX) {
  338. DebugTrace( 0, Dbg, ("Flush a directory does nothing\n") );
  339. break;
  340. }
  341. case UserVolumeOpen:
  342. DebugTrace( 0, Dbg, ("Flush User Volume Open\n") );
  343. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  344. VcbAcquired = TRUE;
  345. //
  346. // While we have the Vcb, let's make sure it's still mounted.
  347. //
  348. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  349. try_return( Status = STATUS_VOLUME_DISMOUNTED );
  350. }
  351. NtfsFlushVolume( IrpContext,
  352. Vcb,
  353. TRUE,
  354. FALSE,
  355. TRUE,
  356. FALSE );
  357. //
  358. // Make sure all of the data written in the flush gets to disk.
  359. //
  360. LfsFlushToLsn( Vcb->LogHandle, LiMax );
  361. break;
  362. case StreamFileOpen:
  363. //
  364. // Nothing to do here.
  365. //
  366. break;
  367. default:
  368. //
  369. // Nothing to do if we have our driver object.
  370. //
  371. break;
  372. }
  373. //
  374. // Abort transaction on error by raising.
  375. //
  376. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  377. try_exit: NOTHING;
  378. } finally {
  379. DebugUnwind( NtfsCommonFlushBuffers );
  380. //
  381. // Release any resources which were acquired.
  382. //
  383. if (ScbAcquired) {
  384. NtfsReleaseScb( IrpContext, Scb );
  385. }
  386. if (ParentScbAcquired) {
  387. NtfsReleaseScb( IrpContext, ParentScb );
  388. }
  389. if (VcbAcquired) {
  390. NtfsReleaseVcb( IrpContext, Vcb );
  391. }
  392. //
  393. // If this is a normal termination then pass the request on
  394. // to the target device object.
  395. //
  396. if (!AbnormalTermination()) {
  397. NTSTATUS DriverStatus;
  398. PIO_STACK_LOCATION NextIrpSp;
  399. //
  400. // Free the IrpContext now before calling the lower driver. Do this
  401. // now in case this fails so that we won't complete the Irp in our
  402. // exception routine after passing it to the lower driver.
  403. //
  404. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  405. ASSERT( Vcb != NULL );
  406. //
  407. // Get the next stack location, and copy over the stack location
  408. //
  409. NextIrpSp = IoGetNextIrpStackLocation( Irp );
  410. *NextIrpSp = *IrpSp;
  411. //
  412. // Set up the completion routine
  413. //
  414. IoSetCompletionRoutine( Irp,
  415. NtfsFlushCompletionRoutine,
  416. NULL,
  417. TRUE,
  418. TRUE,
  419. TRUE );
  420. //
  421. // Send the request.
  422. //
  423. DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp);
  424. Status = (DriverStatus == STATUS_INVALID_DEVICE_REQUEST) ?
  425. Status : DriverStatus;
  426. }
  427. DebugTrace( -1, Dbg, ("NtfsCommonFlushBuffers -> %08lx\n", Status) );
  428. }
  429. return Status;
  430. }
  431. NTSTATUS
  432. NtfsFlushVolume (
  433. IN PIRP_CONTEXT IrpContext,
  434. IN PVCB Vcb,
  435. IN BOOLEAN FlushCache,
  436. IN BOOLEAN PurgeFromCache,
  437. IN BOOLEAN ReleaseAllFiles,
  438. IN BOOLEAN MarkFilesForDismount
  439. )
  440. /*++
  441. Routine Description:
  442. This routine non-recursively flushes a volume. This routine will always do
  443. as much of the operation as possible. It will continue until getting a logfile
  444. full. If any of the streams can't be flushed because of corruption then we
  445. will try to flush the others. We will mark the volume dirty in this case.
  446. We will pass the error code back to the caller because they often need to
  447. proceed as best as possible (i.e. shutdown).
  448. Arguments:
  449. Vcb - Supplies the volume to flush
  450. FlushCache - Supplies TRUE if the caller wants to flush the data in the
  451. cache to disk.
  452. PurgeFromCache - Supplies TRUE if the caller wants the data purged from
  453. the Cache (such as for autocheck!)
  454. ReleaseAllFiles - Indicates that our caller would like to release all Fcb's
  455. after TeardownStructures. This will prevent a deadlock when acquiring
  456. paging io resource after a main resource which is held from a previous
  457. teardown.
  458. Return Value:
  459. STATUS_SUCCESS or else the first error status.
  460. --*/
  461. {
  462. NTSTATUS Status = STATUS_SUCCESS;
  463. PFCB Fcb;
  464. PFCB NextFcb;
  465. PSCB Scb;
  466. PSCB NextScb;
  467. IO_STATUS_BLOCK IoStatus;
  468. ULONG Pass;
  469. BOOLEAN UserDataFile;
  470. BOOLEAN RemovedFcb = FALSE;
  471. BOOLEAN DecrementScbCleanup = FALSE;
  472. BOOLEAN DecrementNextFcbClose = FALSE;
  473. BOOLEAN DecrementNextScbCleanup = FALSE;
  474. BOOLEAN AcquiredFcb = FALSE;
  475. BOOLEAN PagingIoAcquired = FALSE;
  476. BOOLEAN ReleaseFiles = FALSE;
  477. LOGICAL MediaRemoved = FALSE;
  478. LONG ReleaseVcbCount = 0;
  479. PAGED_CODE();
  480. DebugTrace( +1, Dbg, ("NtfsFlushVolume, Vcb = %08lx\n", Vcb) );
  481. //
  482. // This operation must be able to wait.
  483. //
  484. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  485. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  486. }
  487. //
  488. // Make sure there is nothing on the delayed close queue.
  489. //
  490. NtfsFspClose( Vcb );
  491. //
  492. // Acquire the Vcb exclusive. The Raise condition cannot happen.
  493. //
  494. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  495. ReleaseVcbCount += 1;
  496. try {
  497. //
  498. // We won't do any flushes, but we still have to
  499. // do the dismount/teardown processing.
  500. //
  501. if ((IrpContext->MajorFunction == IRP_MJ_PNP) &&
  502. (IrpContext->MinorFunction == IRP_MN_SURPRISE_REMOVAL)) {
  503. MediaRemoved = TRUE;
  504. }
  505. //
  506. // Don't bother flushing read only volumes
  507. //
  508. if (NtfsIsVolumeReadOnly( Vcb )) {
  509. FlushCache = FALSE;
  510. }
  511. //
  512. // Set the PURGE_IN_PROGRESS flag if this is a purge operation.
  513. //
  514. if (PurgeFromCache) {
  515. SetFlag( Vcb->VcbState, VCB_STATE_VOL_PURGE_IN_PROGRESS);
  516. }
  517. //
  518. // Start by flushing the log file to assure Write-Ahead-Logging.
  519. //
  520. if (!MediaRemoved) {
  521. LfsFlushToLsn( Vcb->LogHandle, LiMax );
  522. }
  523. //
  524. // There will be two passes through the Fcb's for the volume. On the
  525. // first pass we just want to flush/purge the user data streams. On
  526. // the second pass we want to flush the other streams. We hold off on
  527. // several of the system files until after these two passes since they
  528. // may be modified during the flush phases.
  529. //
  530. Pass = 0;
  531. do {
  532. PVOID RestartKey;
  533. //
  534. // Loop through all of the Fcb's in the Fcb table.
  535. //
  536. RestartKey = NULL;
  537. NtfsAcquireFcbTable( IrpContext, Vcb );
  538. NextFcb = Fcb = NtfsGetNextFcbTableEntry( Vcb, &RestartKey );
  539. NtfsReleaseFcbTable( IrpContext, Vcb );
  540. if (NextFcb != NULL) {
  541. InterlockedIncrement( &NextFcb->CloseCount );
  542. DecrementNextFcbClose = TRUE;
  543. }
  544. while (Fcb != NULL) {
  545. //
  546. // Acquire Paging I/O first, since we may be deleting or truncating.
  547. // Testing for the PagingIoResource is not really safe without
  548. // holding the main resource, so we correct for that below.
  549. //
  550. if (Fcb->PagingIoResource != NULL) {
  551. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  552. PagingIoAcquired = TRUE;
  553. }
  554. //
  555. // Let's acquire this Scb exclusively.
  556. //
  557. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  558. AcquiredFcb = TRUE;
  559. //
  560. // We depend on the state of the RemovedFcb flag to tell us that
  561. // we can trust the 'Acquired' booleans above.
  562. //
  563. ASSERT( !RemovedFcb );
  564. //
  565. // If we now do not see a paging I/O resource we are golden,
  566. // othewise we can absolutely release and acquire the resources
  567. // safely in the right order, since a resource in the Fcb is
  568. // not going to go away.
  569. //
  570. if (!PagingIoAcquired && (Fcb->PagingIoResource != NULL)) {
  571. NtfsReleaseFcb( IrpContext, Fcb );
  572. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  573. PagingIoAcquired = TRUE;
  574. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  575. }
  576. //
  577. // If this is not one of the special system files then perform
  578. // the flush and purge as requested. Go ahead and test file numbers
  579. // instead of walking through the Scbs in the Vcb just in case they
  580. // have been deleted.
  581. //
  582. if (NtfsSegmentNumber( &Fcb->FileReference ) != MASTER_FILE_TABLE_NUMBER &&
  583. NtfsSegmentNumber( &Fcb->FileReference ) != LOG_FILE_NUMBER &&
  584. NtfsSegmentNumber( &Fcb->FileReference ) != VOLUME_DASD_NUMBER &&
  585. NtfsSegmentNumber( &Fcb->FileReference ) != BIT_MAP_FILE_NUMBER &&
  586. NtfsSegmentNumber( &Fcb->FileReference ) != BOOT_FILE_NUMBER &&
  587. NtfsSegmentNumber( &Fcb->FileReference ) != BAD_CLUSTER_FILE_NUMBER &&
  588. !FlagOn( Fcb->FcbState, FCB_STATE_USN_JOURNAL )) {
  589. //
  590. // We will walk through all of the Scb's for this Fcb. In
  591. // the first pass we will only deal with user data streams.
  592. // In the second pass we will do the others.
  593. //
  594. Scb = NULL;
  595. while (TRUE) {
  596. Scb = NtfsGetNextChildScb( Fcb, Scb );
  597. if (Scb == NULL) { break; }
  598. //
  599. // Reference the Scb to keep it from going away.
  600. //
  601. InterlockedIncrement( &Scb->CleanupCount );
  602. DecrementScbCleanup = TRUE;
  603. //
  604. // Check whether this is a user data file.
  605. //
  606. UserDataFile = FALSE;
  607. if ((NodeType( Scb ) == NTFS_NTC_SCB_DATA) &&
  608. (Scb->AttributeTypeCode == $DATA)) {
  609. UserDataFile = TRUE;
  610. }
  611. //
  612. // Process this Scb in the correct loop.
  613. //
  614. if ((Pass == 0) == (UserDataFile)) {
  615. //
  616. // Initialize the state of the Io to SUCCESS.
  617. //
  618. IoStatus.Status = STATUS_SUCCESS;
  619. //
  620. // Don't put this Scb on the delayed close queue.
  621. //
  622. ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
  623. //
  624. // Flush this stream if it is not already deleted.
  625. // Also don't flush resident streams for system attributes.
  626. //
  627. if (FlushCache &&
  628. !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) &&
  629. (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT) ||
  630. (Scb->AttributeTypeCode == $DATA))) {
  631. //
  632. // Enclose the flushes with try-except, so that we can
  633. // react to log file full, and in any case keep on truckin.
  634. //
  635. try {
  636. FlushScb( IrpContext, Scb, &IoStatus );
  637. NtfsCheckpointCurrentTransaction( IrpContext );
  638. //
  639. // We will handle all errors except LOG_FILE_FULL and fatal
  640. // bugcheck errors here. In the corruption case we will
  641. // want to mark the volume dirty and continue.
  642. //
  643. } except( NtfsFlushVolumeExceptionFilter( IrpContext,
  644. GetExceptionInformation(),
  645. (IoStatus.Status = GetExceptionCode()) )) {
  646. //
  647. // To make sure that we can access all of our streams correctly,
  648. // we first restore all of the higher sizes before aborting the
  649. // transaction. Then we restore all of the lower sizes after
  650. // the abort, so that all Scbs are finally restored.
  651. //
  652. NtfsRestoreScbSnapshots( IrpContext, TRUE );
  653. NtfsAbortTransaction( IrpContext, IrpContext->Vcb, NULL );
  654. NtfsRestoreScbSnapshots( IrpContext, FALSE );
  655. //
  656. // Clear the top-level exception status so we won't raise
  657. // later.
  658. //
  659. NtfsMinimumExceptionProcessing( IrpContext );
  660. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  661. //
  662. // Remember the first error.
  663. //
  664. if (Status == STATUS_SUCCESS) {
  665. Status = IoStatus.Status;
  666. }
  667. //
  668. // If the current status is either DISK_CORRUPT or FILE_CORRUPT then
  669. // mark the volume dirty. We clear the IoStatus to allow
  670. // a corrupt file to be purged. Otherwise it will never
  671. // leave memory.
  672. //
  673. if ((IoStatus.Status == STATUS_DISK_CORRUPT_ERROR) ||
  674. (IoStatus.Status == STATUS_FILE_CORRUPT_ERROR)) {
  675. NtfsMarkVolumeDirty( IrpContext, Vcb );
  676. IoStatus.Status = STATUS_SUCCESS;
  677. }
  678. }
  679. }
  680. //
  681. // Proceed with the purge if there are no failures. We will
  682. // purge if the flush revealed a corrupt file though.
  683. //
  684. if (PurgeFromCache
  685. && IoStatus.Status == STATUS_SUCCESS) {
  686. BOOLEAN DataSectionExists;
  687. BOOLEAN ImageSectionExists;
  688. DataSectionExists = (BOOLEAN)(Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL);
  689. ImageSectionExists = (BOOLEAN)(Scb->NonpagedScb->SegmentObject.ImageSectionObject != NULL);
  690. //
  691. // Since purging the data section can cause the image
  692. // section to go away, we will flush the image section first.
  693. //
  694. if (ImageSectionExists) {
  695. (VOID)MmFlushImageSection( &Scb->NonpagedScb->SegmentObject, MmFlushForWrite );
  696. }
  697. if (DataSectionExists &&
  698. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  699. NULL,
  700. 0,
  701. FALSE ) &&
  702. (Status == STATUS_SUCCESS)) {
  703. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  704. }
  705. }
  706. if (MarkFilesForDismount) {
  707. //
  708. // Set the dismounted flag for this stream so we
  709. // know we have to fail reads & writes to it.
  710. //
  711. SetFlag( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED );
  712. // Also mark the Scb as not allowing fast io --
  713. // this ensures that the file system will get a
  714. // chance to see all reads & writes to this stream.
  715. //
  716. NtfsAcquireFsrtlHeader( Scb );
  717. Scb->Header.IsFastIoPossible = FastIoIsNotPossible;
  718. NtfsReleaseFsrtlHeader( Scb );
  719. }
  720. }
  721. //
  722. // Move to the next Scb.
  723. //
  724. InterlockedDecrement( &Scb->CleanupCount );
  725. DecrementScbCleanup = FALSE;
  726. }
  727. }
  728. //
  729. // If the current Fcb has a USN journal entry and we are forcing a dismount
  730. // then generate the close record.
  731. //
  732. if (MarkFilesForDismount &&
  733. (IoStatus.Status == STATUS_SUCCESS) &&
  734. (NextFcb->FcbUsnRecord != NULL) &&
  735. (NextFcb->FcbUsnRecord->UsnRecord.Reason != 0) &&
  736. (!NtfsIsVolumeReadOnly( Vcb ))) {
  737. //
  738. // Try to post the change but don't fail on an error like DISK_FULL.
  739. //
  740. try {
  741. //
  742. // Now try to actually post the change.
  743. //
  744. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_CLOSE );
  745. //
  746. // Now write the journal, checkpoint the transaction, and free the UsnJournal to
  747. // reduce contention. We force the write now, because the Fcb may get deleted
  748. // before we normally would write the changes when the transaction commits.
  749. //
  750. NtfsWriteUsnJournalChanges( IrpContext );
  751. NtfsCheckpointCurrentTransaction( IrpContext );
  752. } except( NtfsFlushVolumeExceptionFilter( IrpContext,
  753. GetExceptionInformation(),
  754. (IoStatus.Status = GetExceptionCode()) )) {
  755. NtfsMinimumExceptionProcessing( IrpContext );
  756. IoStatus.Status = STATUS_SUCCESS;
  757. if (IrpContext->TransactionId != 0) {
  758. //
  759. // We couldn't write the commit record, we clean up as
  760. // best we can.
  761. //
  762. NtfsCleanupFailedTransaction( IrpContext );
  763. }
  764. }
  765. }
  766. //
  767. // Remove our reference to the current Fcb.
  768. //
  769. InterlockedDecrement( &NextFcb->CloseCount );
  770. DecrementNextFcbClose = FALSE;
  771. //
  772. // Get the next Fcb and reference it so it won't go away.
  773. //
  774. NtfsAcquireFcbTable( IrpContext, Vcb );
  775. NextFcb = NtfsGetNextFcbTableEntry( Vcb, &RestartKey );
  776. NtfsReleaseFcbTable( IrpContext, Vcb );
  777. if (NextFcb != NULL) {
  778. InterlockedIncrement( &NextFcb->CloseCount );
  779. DecrementNextFcbClose = TRUE;
  780. }
  781. //
  782. // Flushing the volume can cause new file objects to be allocated.
  783. // If we are in the second pass and the Fcb is for a user file
  784. // or directory then try to perform a teardown on this.
  785. //
  786. if ((Pass == 1) &&
  787. !FlagOn(Fcb->FcbState, FCB_STATE_SYSTEM_FILE)) {
  788. ASSERT( IrpContext->TransactionId == 0 );
  789. //
  790. // We can actually get failures in this routine if we need to log standard info.
  791. //
  792. try {
  793. NtfsTeardownStructures( IrpContext,
  794. Fcb,
  795. NULL,
  796. FALSE,
  797. 0,
  798. &RemovedFcb );
  799. //
  800. // TeardownStructures can create a transaction. Commit
  801. // it if present.
  802. //
  803. if (IrpContext->TransactionId != 0) {
  804. NtfsCheckpointCurrentTransaction( IrpContext );
  805. }
  806. } except( NtfsFlushVolumeExceptionFilter( IrpContext,
  807. GetExceptionInformation(),
  808. GetExceptionCode() )) {
  809. NtfsMinimumExceptionProcessing( IrpContext );
  810. if (IrpContext->TransactionId != 0) {
  811. //
  812. // We couldn't write the commit record, we clean up as
  813. // best we can.
  814. //
  815. NtfsCleanupFailedTransaction( IrpContext );
  816. }
  817. }
  818. }
  819. //
  820. // If the Fcb is still around then free any of the the other
  821. // resources we have acquired.
  822. //
  823. if (!RemovedFcb) {
  824. //
  825. // Free the snapshots for the current Fcb. This will keep us
  826. // from having a snapshot for all open attributes in the
  827. // system.
  828. //
  829. NtfsFreeSnapshotsForFcb( IrpContext, Fcb );
  830. if (PagingIoAcquired) {
  831. ASSERT( IrpContext->TransactionId == 0 );
  832. NtfsReleasePagingIo( IrpContext, Fcb );
  833. }
  834. if (AcquiredFcb) {
  835. NtfsReleaseFcb( IrpContext, Fcb );
  836. }
  837. }
  838. //
  839. // If our caller wants to insure that all files are released
  840. // between flushes then walk through the exclusive Fcb list
  841. // and free everything.
  842. //
  843. if (ReleaseAllFiles) {
  844. while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
  845. NtfsReleaseFcb( IrpContext,
  846. (PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
  847. FCB,
  848. ExclusiveFcbLinks ));
  849. }
  850. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  851. IRP_CONTEXT_FLAG_RELEASE_MFT );
  852. }
  853. PagingIoAcquired = FALSE;
  854. AcquiredFcb = FALSE;
  855. //
  856. // Always set this back to FALSE to indicate that we can trust the
  857. // 'Acquired' flags above.
  858. //
  859. RemovedFcb = FALSE;
  860. //
  861. // Now move to the next Fcb.
  862. //
  863. Fcb = NextFcb;
  864. }
  865. } while (++Pass < 2);
  866. //
  867. // The root directory is the only fcb with a mixture of user
  868. // streams that should be torn down now and system streams that
  869. // can't be torn down now.
  870. // When we tried to teardown the whole Fcb, we might have run
  871. // into the index root attribute and stopped our teardown, in
  872. // which case we may leave a close count on the Vcb which will
  873. // keep autochk from being able to lock the volume. We need to
  874. // make sure the root directory indeed exists, and this isn't
  875. // the call to flush the volume during mount when we haven't yet
  876. // opened the root directory.
  877. //
  878. if (Vcb->RootIndexScb != NULL) {
  879. Fcb = Vcb->RootIndexScb->Fcb;
  880. //
  881. // Get the first Scb for the root directory Fcb.
  882. //
  883. Scb = NtfsGetNextChildScb( Fcb, NULL );
  884. while (Scb != NULL) {
  885. NextScb = NtfsGetNextChildScb( Fcb, Scb );
  886. if (NextScb != NULL) {
  887. InterlockedIncrement( &NextScb->CleanupCount );
  888. DecrementNextScbCleanup = TRUE;
  889. }
  890. //
  891. // We can actually get failures in this routine if we need to log standard info.
  892. //
  893. try {
  894. if (NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  895. //
  896. // Notice that we don't bother passing RemovedFcb, since
  897. // the root directory Fcb isn't going to go away.
  898. //
  899. NtfsTeardownStructures( IrpContext,
  900. Scb,
  901. NULL,
  902. FALSE,
  903. 0,
  904. NULL );
  905. }
  906. //
  907. // TeardownStructures can create a transaction. Commit
  908. // it if present.
  909. //
  910. if (IrpContext->TransactionId != 0) {
  911. NtfsCheckpointCurrentTransaction( IrpContext );
  912. }
  913. } except( NtfsFlushVolumeExceptionFilter( IrpContext,
  914. GetExceptionInformation(),
  915. GetExceptionCode() )) {
  916. NtfsMinimumExceptionProcessing( IrpContext );
  917. if (IrpContext->TransactionId != 0) {
  918. //
  919. // We couldn't write the commit record, we clean up as
  920. // best we can.
  921. //
  922. NtfsCleanupFailedTransaction( IrpContext );
  923. }
  924. }
  925. //
  926. // Decrement the cleanup count of the next Scb if we incremented it.
  927. //
  928. if (DecrementNextScbCleanup) {
  929. InterlockedDecrement( &NextScb->CleanupCount );
  930. DecrementNextScbCleanup = FALSE;
  931. }
  932. //
  933. // Move to the next Scb.
  934. //
  935. Scb = NextScb;
  936. }
  937. }
  938. //
  939. // Make sure that all of the delayed or async closes for this Vcb are gone.
  940. //
  941. if (PurgeFromCache) {
  942. NtfsFspClose( Vcb );
  943. }
  944. //
  945. // If we are to mark the files for dismount then do the Volume Dasd file now.
  946. //
  947. if (MarkFilesForDismount) {
  948. NtfsAcquireExclusiveFcb( IrpContext, Vcb->VolumeDasdScb->Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  949. SetFlag( Vcb->VolumeDasdScb->ScbState, SCB_STATE_VOLUME_DISMOUNTED );
  950. NtfsReleaseFcb( IrpContext, Vcb->VolumeDasdScb->Fcb );
  951. }
  952. //
  953. // Now we want to flush/purge the streams for volume bitmap and then the Usn
  954. // journal and Scb.
  955. //
  956. {
  957. PFCB SystemFcbs[3];
  958. PSCB ThisScb;
  959. //
  960. // Store the volume bitmap, usn journal and Mft into the array.
  961. //
  962. RtlZeroMemory( SystemFcbs, sizeof( SystemFcbs ));
  963. if (Vcb->BitmapScb != NULL) {
  964. SystemFcbs[0] = Vcb->BitmapScb->Fcb;
  965. }
  966. if (Vcb->UsnJournal != NULL) {
  967. SystemFcbs[1] = Vcb->UsnJournal->Fcb;
  968. }
  969. if (Vcb->MftScb != NULL) {
  970. SystemFcbs[2] = Vcb->MftScb->Fcb;
  971. }
  972. Pass = 0;
  973. do {
  974. Fcb = SystemFcbs[Pass];
  975. if (Fcb != NULL) {
  976. //
  977. // Purge the Mft cache if we are at the Mft.
  978. //
  979. if (Pass == 2) {
  980. //
  981. // If we are operating on the MFT, make sure we don't have any
  982. // cached maps lying around...
  983. //
  984. NtfsPurgeFileRecordCache( IrpContext );
  985. //
  986. // If we are purging the MFT then acquire all files to
  987. // avoid a purge deadlock. If someone create an MFT mapping
  988. // between the flush and purge then the purge can spin
  989. // indefinitely in CC.
  990. //
  991. if (PurgeFromCache && !ReleaseFiles) {
  992. NtfsAcquireAllFiles( IrpContext, Vcb, TRUE, FALSE, FALSE );
  993. ReleaseFiles = TRUE;
  994. //
  995. // NtfsAcquireAllFiles acquired the Vcb one more time.
  996. //
  997. ReleaseVcbCount += 1;
  998. }
  999. //
  1000. // For the other Fcb's we still need to synchronize the flush and
  1001. // purge so acquire and drop the Fcb.
  1002. //
  1003. } else {
  1004. NextFcb = Fcb;
  1005. InterlockedIncrement( &NextFcb->CloseCount );
  1006. DecrementNextFcbClose = TRUE;
  1007. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  1008. AcquiredFcb = TRUE;
  1009. }
  1010. //
  1011. // Go through each Scb for each of these Fcb's.
  1012. //
  1013. ThisScb = NtfsGetNextChildScb( Fcb, NULL );
  1014. while (ThisScb != NULL) {
  1015. Scb = NtfsGetNextChildScb( Fcb, ThisScb );
  1016. //
  1017. // Initialize the state of the Io to SUCCESS.
  1018. //
  1019. IoStatus.Status = STATUS_SUCCESS;
  1020. //
  1021. // Reference the next Scb to keep it from going away if
  1022. // we purge the current one.
  1023. //
  1024. if (Scb != NULL) {
  1025. InterlockedIncrement( &Scb->CleanupCount );
  1026. DecrementScbCleanup = TRUE;
  1027. }
  1028. if (FlushCache) {
  1029. //
  1030. // Flush the stream. No need to update file sizes because these
  1031. // are all logged streams.
  1032. //
  1033. CcFlushCache( &ThisScb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
  1034. if (!NT_SUCCESS( IoStatus.Status )) {
  1035. Status = IoStatus.Status;
  1036. }
  1037. //
  1038. // Use a try-except to commit the current transaction.
  1039. //
  1040. try {
  1041. NtfsCleanupTransaction( IrpContext, IoStatus.Status, TRUE );
  1042. NtfsCheckpointCurrentTransaction( IrpContext );
  1043. //
  1044. // We will handle all errors except LOG_FILE_FULL and fatal
  1045. // bugcheck errors here. In the corruption case we will
  1046. // want to mark the volume dirty and continue.
  1047. //
  1048. } except( NtfsFlushVolumeExceptionFilter( IrpContext,
  1049. GetExceptionInformation(),
  1050. (IoStatus.Status = GetExceptionCode()) )) {
  1051. //
  1052. // To make sure that we can access all of our streams correctly,
  1053. // we first restore all of the higher sizes before aborting the
  1054. // transaction. Then we restore all of the lower sizes after
  1055. // the abort, so that all Scbs are finally restored.
  1056. //
  1057. NtfsRestoreScbSnapshots( IrpContext, TRUE );
  1058. NtfsAbortTransaction( IrpContext, IrpContext->Vcb, NULL );
  1059. NtfsRestoreScbSnapshots( IrpContext, FALSE );
  1060. //
  1061. // Clear the top-level exception status so we won't raise
  1062. // later.
  1063. //
  1064. NtfsMinimumExceptionProcessing( IrpContext );
  1065. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  1066. //
  1067. // Remember the first error.
  1068. //
  1069. if (Status == STATUS_SUCCESS) {
  1070. Status = IoStatus.Status;
  1071. }
  1072. //
  1073. // If the current status is either DISK_CORRUPT or FILE_CORRUPT then
  1074. // mark the volume dirty. We clear the IoStatus to allow
  1075. // a corrupt file to be purged. Otherwise it will never
  1076. // leave memory.
  1077. //
  1078. if ((IoStatus.Status == STATUS_DISK_CORRUPT_ERROR) ||
  1079. (IoStatus.Status == STATUS_FILE_CORRUPT_ERROR)) {
  1080. NtfsMarkVolumeDirty( IrpContext, Vcb );
  1081. IoStatus.Status = STATUS_SUCCESS;
  1082. }
  1083. }
  1084. }
  1085. //
  1086. // Purge this stream if there have been no errors.
  1087. //
  1088. if (PurgeFromCache
  1089. && IoStatus.Status == STATUS_SUCCESS) {
  1090. if (!CcPurgeCacheSection( &ThisScb->NonpagedScb->SegmentObject,
  1091. NULL,
  1092. 0,
  1093. FALSE ) &&
  1094. (Status == STATUS_SUCCESS)) {
  1095. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  1096. }
  1097. }
  1098. //
  1099. // Remove any reference we have to the next Scb and move
  1100. // forward to the next Scb.
  1101. //
  1102. if (DecrementScbCleanup) {
  1103. InterlockedDecrement( &Scb->CleanupCount );
  1104. DecrementScbCleanup = FALSE;
  1105. }
  1106. ThisScb = Scb;
  1107. }
  1108. //
  1109. // Purge the Mft cache if we are at the Mft. Do this before and
  1110. // after dealing with the Mft.
  1111. //
  1112. if (Pass == 2) {
  1113. //
  1114. // If we are operating on the MFT, make sure we don't have any
  1115. // cached maps lying around...
  1116. //
  1117. NtfsPurgeFileRecordCache( IrpContext );
  1118. //
  1119. // If we are purging the MFT then acquire all files to
  1120. // avoid a purge deadlock. If someone create an MFT mapping
  1121. // between the flush and purge then the purge can spin
  1122. // indefinitely in CC.
  1123. //
  1124. if (PurgeFromCache && !ReleaseFiles) {
  1125. NtfsAcquireAllFiles( IrpContext, Vcb, TRUE, FALSE, FALSE );
  1126. ReleaseFiles = TRUE;
  1127. //
  1128. // NtfsAcquireAllFiles acquired the Vcb one more time.
  1129. //
  1130. ReleaseVcbCount += 1;
  1131. }
  1132. //
  1133. // Release the volume bitmap and Usn journal.
  1134. //
  1135. } else {
  1136. InterlockedDecrement( &NextFcb->CloseCount );
  1137. DecrementNextFcbClose = FALSE;
  1138. NtfsReleaseFcb( IrpContext, Fcb );
  1139. AcquiredFcb = FALSE;
  1140. }
  1141. }
  1142. Pass += 1;
  1143. } while (Pass != 3);
  1144. //
  1145. // Also flag as dismounted the usnjournal and volume bitmap.
  1146. //
  1147. if (MarkFilesForDismount) {
  1148. if (Vcb->BitmapScb != NULL) {
  1149. NtfsAcquireExclusiveFcb( IrpContext, Vcb->BitmapScb->Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  1150. SetFlag( Vcb->BitmapScb->ScbState, SCB_STATE_VOLUME_DISMOUNTED );
  1151. NtfsReleaseFcb( IrpContext, Vcb->BitmapScb->Fcb );
  1152. }
  1153. if (Vcb->UsnJournal != NULL) {
  1154. NtfsAcquireExclusiveFcb( IrpContext, Vcb->UsnJournal->Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  1155. SetFlag( Vcb->UsnJournal->ScbState, SCB_STATE_VOLUME_DISMOUNTED );
  1156. NtfsReleaseFcb( IrpContext, Vcb->UsnJournal->Fcb );
  1157. }
  1158. }
  1159. }
  1160. } finally {
  1161. //
  1162. // If this is a purge then clear the purge flag.
  1163. //
  1164. if (PurgeFromCache) {
  1165. ClearFlag( Vcb->VcbState, VCB_STATE_VOL_PURGE_IN_PROGRESS );
  1166. }
  1167. //
  1168. // Restore any counts we may have incremented to reference
  1169. // in-memory structures.
  1170. //
  1171. if (DecrementScbCleanup) {
  1172. InterlockedDecrement( &Scb->CleanupCount );
  1173. }
  1174. if (DecrementNextFcbClose) {
  1175. InterlockedDecrement( &NextFcb->CloseCount );
  1176. }
  1177. if (DecrementNextScbCleanup) {
  1178. InterlockedDecrement( &NextScb->CleanupCount );
  1179. }
  1180. //
  1181. // We would've released our resources if we had
  1182. // successfully removed the fcb.
  1183. //
  1184. if (!RemovedFcb) {
  1185. if (PagingIoAcquired) {
  1186. NtfsReleasePagingIo( IrpContext, Fcb );
  1187. }
  1188. if (AcquiredFcb) {
  1189. NtfsReleaseFcb( IrpContext, Fcb );
  1190. }
  1191. }
  1192. if (ReleaseFiles) {
  1193. //
  1194. // NtfsReleaseAllFiles is going to release the Vcb. We'd
  1195. // better have it acquired at least once.
  1196. //
  1197. ASSERT( ReleaseVcbCount >= 1 );
  1198. NtfsReleaseAllFiles( IrpContext, Vcb, FALSE );
  1199. ReleaseVcbCount -= 1;
  1200. }
  1201. //
  1202. // Release the Vcb now. We'd better have the Vcb acquired at least once.
  1203. //
  1204. ASSERTMSG( "Ignore this assert, 96773 is truly fixed",
  1205. (ReleaseVcbCount >= 1) );
  1206. if (ReleaseVcbCount >= 1) {
  1207. NtfsReleaseVcb( IrpContext, Vcb );
  1208. }
  1209. }
  1210. DebugTrace( -1, Dbg, ("NtfsFlushVolume -> %08lx\n", Status) );
  1211. return Status;
  1212. }
  1213. NTSTATUS
  1214. NtfsFlushLsnStreams (
  1215. IN PIRP_CONTEXT IrpContext,
  1216. IN PVCB Vcb,
  1217. IN BOOLEAN ForceRemove,
  1218. IN BOOLEAN Partial
  1219. )
  1220. /*++
  1221. Routine Description:
  1222. This routine non-recursively flushes all of the Lsn streams in the open
  1223. attribute table and removes them from the table. We assume the vcb
  1224. has been pre-acquired shared and the open attribute table is preacquired
  1225. Arguments:
  1226. Vcb - Supplies the volume to flush
  1227. ForceRemove - If true remove the open attribute even if the flush fails
  1228. OpenAttributeTableAcquired - Set to final state of table on exit - it should be true
  1229. to start off with and we may drop and reacquire it in between
  1230. Partial - if true only flush a few streams
  1231. Return Value:
  1232. STATUS_SUCCESS or else the most recent error status
  1233. --*/
  1234. {
  1235. NTSTATUS Status = STATUS_SUCCESS;
  1236. IO_STATUS_BLOCK IoStatus;
  1237. ULONG Pass = 1;
  1238. ULONG AttrIndex;
  1239. ULONG AttributesToFlush;
  1240. POPEN_ATTRIBUTE_ENTRY AttributeEntry;
  1241. PSCB Scb;
  1242. PFCB Fcb;
  1243. BOOLEAN AcquiredPaging = FALSE;
  1244. BOOLEAN RemovedFcb;
  1245. BOOLEAN FcbTableAcquired = FALSE;
  1246. BOOLEAN ScbValid;
  1247. #if DBG || defined( NTFS_FREE_ASSERT )
  1248. BOOLEAN EmptyList = IsListEmpty( &IrpContext->ExclusiveFcbList );
  1249. #endif
  1250. PAGED_CODE();
  1251. DebugTrace( +1, Dbg, ("NtfsFlushLsnStreams, Vcb = %08lx\n", Vcb) );
  1252. ASSERT( NtfsIsSharedVcb( Vcb ) );
  1253. ASSERT( ExIsResourceAcquiredExclusive( &Vcb->OpenAttributeTable.Resource ) );
  1254. try {
  1255. //
  1256. // This operation must be able to wait.
  1257. //
  1258. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  1259. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  1260. }
  1261. //
  1262. // Start by flushing the log file to push as much of it out as possible in 1 chunk
  1263. //
  1264. LfsFlushToLsn( Vcb->LogHandle, LiMax );
  1265. //
  1266. // preacquire the fcbtable mutex to prevent any fcb's from being deleted
  1267. //
  1268. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1269. NtfsAcquireFcbTable( IrpContext, Vcb );
  1270. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1271. if (Partial) {
  1272. AttributesToFlush = Vcb->OpenAttributeTable.Table->NumberAllocated / 2;
  1273. Pass = 2;
  1274. } else {
  1275. Pass = 1;
  1276. }
  1277. //
  1278. // Scan the table in 2 passes - the first for user files the second for metadata
  1279. // during each pass files and remove them from the table. We are guaranteed
  1280. // that all transactions are done at this point so the bitmap will not
  1281. // be reopened during an abort for example
  1282. //
  1283. // Partial flushes start in pass 2 so they flush the mft first
  1284. //
  1285. for (; Pass <= 2; Pass++) {
  1286. //
  1287. // Loop through to flush all of the streams in the open attribute table.
  1288. //
  1289. AttributeEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  1290. while ((AttributeEntry != NULL) && (!Partial || AttributesToFlush > 0)) {
  1291. AttrIndex = GetIndexFromRestartEntry( &Vcb->OpenAttributeTable, AttributeEntry );
  1292. Scb = AttributeEntry->OatData->Overlay.Scb;
  1293. if (Scb != NULL) {
  1294. ScbValid = TRUE;
  1295. Fcb = Scb->Fcb;
  1296. //
  1297. // Skip system files during pass 1
  1298. //
  1299. if ((Pass == 1) && FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  1300. AttributeEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable, AttributeEntry );
  1301. continue;
  1302. }
  1303. //
  1304. // The mft is neither going to be torn down nor is it sync'ed on the main resource
  1305. // during a flush
  1306. //
  1307. if (Scb != Vcb->MftScb) {
  1308. //
  1309. // Reference the fcb to keep something around - we have the fcbtable mutex
  1310. // which is preventing any teardowns
  1311. //
  1312. Fcb->ReferenceCount += 1;
  1313. //
  1314. // If there is an scb to acquire - we must drop the open attr table and fcbtable mutex
  1315. // first since they are end resources
  1316. //
  1317. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1318. AttributeEntry = NULL;
  1319. NtfsReleaseFcbTable( IrpContext, Vcb );
  1320. //
  1321. // Acquire main exclusive for flushing synchronization (this is metadata) and
  1322. // so that we can change the oat info in the non paged scb (see AcquireSharedScbForTransaction
  1323. // for what we need to lock out). Note we need to acquire the file even
  1324. // if its deleted.
  1325. //
  1326. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_HOLD_BITMAP );
  1327. //
  1328. // We've acquired the fcb so release our ref count
  1329. //
  1330. NtfsAcquireFcbTable( IrpContext, Vcb );
  1331. Fcb->ReferenceCount -= 1;
  1332. NtfsReleaseFcbTable( IrpContext, Vcb );
  1333. //
  1334. // Is the scb still relevant? The open attribute table will only be
  1335. // decreasing in size due to the drain pending state so check if the
  1336. // scb is still in it now that we're synchronized
  1337. //
  1338. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1339. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, AttrIndex );
  1340. if ((AttributeEntry->AllocatedOrNextFree != RESTART_ENTRY_ALLOCATED) ||
  1341. (AttributeEntry->OatData->Overlay.Scb != Scb)) {
  1342. //
  1343. // If we're not partially draining the table then a new entry should
  1344. // not appear in it
  1345. //
  1346. ASSERT( Partial || (AttributeEntry->AllocatedOrNextFree != RESTART_ENTRY_ALLOCATED) ||
  1347. (AttributeEntry->OatData->Overlay.Scb == NULL) );
  1348. ScbValid = FALSE;
  1349. }
  1350. } else {
  1351. NtfsReleaseFcbTable( IrpContext, Vcb );
  1352. }
  1353. IoStatus.Status = STATUS_SUCCESS;
  1354. //
  1355. // Skip flushing the Mft mirror and any deleted streams. If the header
  1356. // is uninitialized for this stream then it means that the
  1357. // attribute doesn't exist (INDEX_ALLOCATION where the create failed)
  1358. // or the attribute is now resident. Streams with paging resources
  1359. // (except for the volume bitmap) are skipped to prevent a possible
  1360. // deadlock (normally only seen in the hot fix path -- that's the
  1361. // only time an ordinary user stream ends up in the open attribute
  1362. // table) when this routine acquires the main resource without
  1363. // holding the paging resource. The easiest way to avoid this is
  1364. // to skip such files, since it's user data, not logged metadata,
  1365. // that's going to be flushed anyway, and flushing user data
  1366. // doesn't help a checkpoint at all.
  1367. //
  1368. if (ScbValid) {
  1369. if ((Scb != Vcb->Mft2Scb) &&
  1370. !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) &&
  1371. FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ) &&
  1372. ((Scb == Vcb->BitmapScb) || (Scb->Header.PagingIoResource == NULL))) {
  1373. //
  1374. // The current attribute entry scb should match what we originally found
  1375. //
  1376. ASSERT( AttributeEntry->OatData->Overlay.Scb == Scb );
  1377. //
  1378. // Drop the table during the flush
  1379. //
  1380. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1381. //
  1382. // Now flush the stream. We don't worry about file sizes because
  1383. // any logged stream should have the file size already in the log.
  1384. //
  1385. CcFlushCache( &Scb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
  1386. if (!NT_SUCCESS( IoStatus.Status )) {
  1387. Status = IoStatus.Status;
  1388. }
  1389. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1390. }
  1391. //
  1392. // Remove the open attribute entry for this scb from the table and clean it up
  1393. // If the flush succeeded or we're forcing removal
  1394. //
  1395. if (!Partial && (NT_SUCCESS( IoStatus.Status ) || ForceRemove)) {
  1396. ASSERT( Scb->NonpagedScb->OpenAttributeTableIndex == AttrIndex );
  1397. //
  1398. // We now need the mft exclusive (everyone else already has it) to
  1399. // zero out the oat info in the nonpaged scb
  1400. //
  1401. if (Scb == Vcb->MftScb) {
  1402. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1403. NtfsAcquireExclusiveScb( IrpContext, Scb );
  1404. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1405. }
  1406. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, AttrIndex );
  1407. if (AttributeEntry->AllocatedOrNextFree == RESTART_ENTRY_ALLOCATED) {
  1408. NtfsFreeAttributeEntry( Vcb, AttributeEntry );
  1409. }
  1410. if (Scb == Vcb->MftScb) {
  1411. NtfsReleaseScb( IrpContext, Scb );
  1412. }
  1413. }
  1414. }
  1415. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1416. if (Scb != Vcb->MftScb) {
  1417. RemovedFcb = FALSE;
  1418. NtfsTeardownStructures( IrpContext,
  1419. Fcb,
  1420. NULL,
  1421. FALSE,
  1422. 0,
  1423. &RemovedFcb );
  1424. if (!RemovedFcb) {
  1425. NtfsReleaseFcb( IrpContext, Fcb );
  1426. }
  1427. }
  1428. #if DBG || defined( NTFS_FREE_ASSERT )
  1429. //
  1430. // We shouldn't own anything more at this point than what we started with
  1431. //
  1432. ASSERT( !EmptyList || IsListEmpty( &IrpContext->ExclusiveFcbList ) );
  1433. #endif
  1434. NtfsAcquireFcbTable( IrpContext, Vcb );
  1435. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1436. } else if (!Partial) {
  1437. //
  1438. // There was no scb so just clean this entry up. Note we still own the table
  1439. // unlike above where we had to drop it
  1440. //
  1441. NtfsFreeAttributeEntry( Vcb, AttributeEntry );
  1442. }
  1443. if (Partial) {
  1444. //
  1445. // Find our place in the table since we're not removing any entries
  1446. // starting with our old index
  1447. //
  1448. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, AttrIndex );
  1449. if (AttributeEntry) {
  1450. AttributeEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable, AttributeEntry );
  1451. }
  1452. AttributesToFlush -= 1;
  1453. } else {
  1454. AttributeEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  1455. }
  1456. }
  1457. }
  1458. } finally {
  1459. //
  1460. // We should still have 1 and only 1 owner count on the table
  1461. //
  1462. ASSERT( ExIsResourceAcquiredSharedLite( &Vcb->OpenAttributeTable.Resource ) == 1 );
  1463. }
  1464. //
  1465. // At this point there should be no entries left in the table unless its a partial flush
  1466. //
  1467. ASSERT( !NT_SUCCESS( Status ) || (Vcb->OpenAttributeTable.Table->NumberAllocated == 0) || Partial);
  1468. NtfsReleaseFcbTable( IrpContext, Vcb );
  1469. DebugTrace( -1, Dbg, ("NtfsFlushLsnStreams2 -> %08lx\n", Status) );
  1470. return Status;
  1471. UNREFERENCED_PARAMETER( IrpContext );
  1472. }
  1473. VOID
  1474. NtfsFlushAndPurgeFcb (
  1475. IN PIRP_CONTEXT IrpContext,
  1476. IN PFCB Fcb
  1477. )
  1478. /*++
  1479. Routine Description:
  1480. This routine will flush and purge all of the open streams for an
  1481. Fcb. It is indended to prepare this Fcb such that a teardown will
  1482. remove this Fcb for the tree. The caller has guaranteed that the
  1483. Fcb can't go away.
  1484. Arguments:
  1485. Fcb - Supplies the Fcb to flush
  1486. Return Value:
  1487. None. The caller calls teardown structures and checks the result.
  1488. --*/
  1489. {
  1490. IO_STATUS_BLOCK IoStatus;
  1491. BOOLEAN DecrementNextScbCleanup = FALSE;
  1492. PSCB Scb;
  1493. PSCB NextScb;
  1494. PAGED_CODE();
  1495. //
  1496. // Use a try-finally to facilitate cleanup.
  1497. //
  1498. try {
  1499. //
  1500. // Get the first Scb for the Fcb.
  1501. //
  1502. Scb = NtfsGetNextChildScb( Fcb, NULL );
  1503. while (Scb != NULL) {
  1504. BOOLEAN DataSectionExists;
  1505. BOOLEAN ImageSectionExists;
  1506. NextScb = NtfsGetNextChildScb( Fcb, Scb );
  1507. //
  1508. // Save the attribute list for last so we don't purge it
  1509. // and then bring it back for another attribute.
  1510. //
  1511. if ((Scb->AttributeTypeCode == $ATTRIBUTE_LIST) &&
  1512. (NextScb != NULL)) {
  1513. RemoveEntryList( &Scb->FcbLinks );
  1514. InsertTailList( &Fcb->ScbQueue, &Scb->FcbLinks );
  1515. Scb = NextScb;
  1516. continue;
  1517. }
  1518. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  1519. FlushScb( IrpContext, Scb, &IoStatus );
  1520. }
  1521. //
  1522. // The call to purge below may generate a close call.
  1523. // We increment the cleanup count of the next Scb to prevent
  1524. // it from going away in a TearDownStructures as part of that
  1525. // close.
  1526. //
  1527. DataSectionExists = (BOOLEAN)(Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL);
  1528. ImageSectionExists = (BOOLEAN)(Scb->NonpagedScb->SegmentObject.ImageSectionObject != NULL);
  1529. if (NextScb != NULL) {
  1530. InterlockedIncrement( &NextScb->CleanupCount );
  1531. DecrementNextScbCleanup = TRUE;
  1532. }
  1533. if (ImageSectionExists) {
  1534. (VOID)MmFlushImageSection( &Scb->NonpagedScb->SegmentObject, MmFlushForWrite );
  1535. }
  1536. if (DataSectionExists) {
  1537. CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  1538. NULL,
  1539. 0,
  1540. FALSE );
  1541. }
  1542. //
  1543. // Decrement the cleanup count of the next Scb if we incremented
  1544. // it.
  1545. //
  1546. if (DecrementNextScbCleanup) {
  1547. InterlockedDecrement( &NextScb->CleanupCount );
  1548. DecrementNextScbCleanup = FALSE;
  1549. }
  1550. //
  1551. // Move to the next Scb.
  1552. //
  1553. Scb = NextScb;
  1554. }
  1555. } finally {
  1556. //
  1557. // Restore any counts we may have incremented to reference
  1558. // in-memory structures.
  1559. //
  1560. if (DecrementNextScbCleanup) {
  1561. InterlockedDecrement( &NextScb->CleanupCount );
  1562. }
  1563. }
  1564. return;
  1565. }
  1566. VOID
  1567. NtfsFlushAndPurgeScb (
  1568. IN PIRP_CONTEXT IrpContext,
  1569. IN PSCB Scb,
  1570. IN PSCB ParentScb OPTIONAL
  1571. )
  1572. /*++
  1573. Routine Description:
  1574. This routine is called to flush and purge a stream. It is used
  1575. when there are now only non-cached handles on a file and there is
  1576. a data section. Flushing and purging the data section will mean that
  1577. the user non-cached io won't have to block for the cache coherency calls.
  1578. We want to remove all of the Fcb's from the exclusive list so that the
  1579. lower level flush will be its own transaction. We don't want to drop
  1580. any of the resources however so we acquire the Scb's above explicitly
  1581. and then empty the exclusive list. In all cases we will reacquire the
  1582. Scb's before raising out of this routine.
  1583. Because this routines causes the write out of all data to disk its critical
  1584. that it also updates the filesizes on disk. If NtfsWriteFileSizes raises logfile full
  1585. the caller (create, cleanup etc.) must recall this routine or update the filesizes
  1586. itself. To help with doing this we set the irpcontext state that we attemted a
  1587. flushandpurge. Also note because we purged the section on a retry it may no longer
  1588. be there so a test on (SectionObjectPointer->DataSection != NULL) will miss retrying
  1589. Arguments:
  1590. Scb - Scb for the stream to flush and purge. The reference count on this
  1591. stream will prevent it from going away.
  1592. ParentScb - If specified then this is the parent for the stream being flushed.
  1593. Return Value:
  1594. None.
  1595. --*/
  1596. {
  1597. IO_STATUS_BLOCK Iosb;
  1598. BOOLEAN PurgeResult;
  1599. PAGED_CODE();
  1600. //
  1601. // Only actually flush and purge if there is a data section
  1602. //
  1603. if (Scb->NonpagedScb->SegmentObject.DataSectionObject) {
  1604. //
  1605. // Commit the current transaction.
  1606. //
  1607. NtfsCheckpointCurrentTransaction( IrpContext );
  1608. //
  1609. // Acquire the Scb explicitly. We don't bother to do the same for the
  1610. // parent SCB here; we'll just acquire it on our way out.
  1611. //
  1612. NtfsAcquireResourceExclusive( IrpContext, Scb, TRUE );
  1613. //
  1614. // Walk through and release all of the Fcb's in the Fcb list.
  1615. //
  1616. while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
  1617. NtfsReleaseFcb( IrpContext,
  1618. (PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
  1619. FCB,
  1620. ExclusiveFcbLinks ));
  1621. }
  1622. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  1623. IRP_CONTEXT_FLAG_RELEASE_MFT );
  1624. //
  1625. // Use a try-finally to reacquire the Scbs.
  1626. //
  1627. try {
  1628. //
  1629. // Perform the flush, raise on error.
  1630. //
  1631. #ifdef COMPRESS_ON_WIRE
  1632. if (Scb->Header.FileObjectC != NULL) {
  1633. PCOMPRESSION_SYNC CompressionSync = NULL;
  1634. //
  1635. // Use a try-finally to clean up the compression sync.
  1636. //
  1637. try {
  1638. Iosb.Status = NtfsSynchronizeUncompressedIo( Scb,
  1639. NULL,
  1640. 0,
  1641. TRUE,
  1642. &CompressionSync );
  1643. } finally {
  1644. NtfsReleaseCompressionSync( CompressionSync );
  1645. }
  1646. NtfsNormalizeAndCleanupTransaction( IrpContext, &Iosb.Status, TRUE, STATUS_UNEXPECTED_IO_ERROR );
  1647. }
  1648. #endif
  1649. //
  1650. // After doing the work of the flush we must update the ondisk sizes either
  1651. // here or in close if we fail logfile full
  1652. //
  1653. NtfsPurgeFileRecordCache( IrpContext );
  1654. SetFlag( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE );
  1655. CcFlushCache( &Scb->NonpagedScb->SegmentObject, NULL, 0, &Iosb );
  1656. #ifdef SYSCACHE_DEBUG
  1657. if (ScbIsBeingLogged( Scb )) {
  1658. ASSERT( Scb->Fcb->PagingIoResource != NULL );
  1659. ASSERT( NtfsIsExclusiveScbPagingIo( Scb ) );
  1660. FsRtlLogSyscacheEvent( Scb, SCE_CC_FLUSH, 0, 0, 0, Iosb.Status );
  1661. }
  1662. #endif
  1663. NtfsNormalizeAndCleanupTransaction( IrpContext, &Iosb.Status, TRUE, STATUS_UNEXPECTED_IO_ERROR );
  1664. //
  1665. // If no error, then purge the section
  1666. //
  1667. PurgeResult = CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject, NULL, 0, FALSE );
  1668. #ifdef SYSCACHE_DEBUG
  1669. if (ScbIsBeingLogged( Scb )) {
  1670. ASSERT( (Scb->Fcb->PagingIoResource != NULL) && NtfsIsExclusiveScbPagingIo( Scb ) );
  1671. FsRtlLogSyscacheEvent( Scb, SCE_CC_FLUSH_AND_PURGE, 0, 0, (DWORD_PTR)(Scb->NonpagedScb->SegmentObject.SharedCacheMap), PurgeResult );
  1672. }
  1673. #endif
  1674. } finally {
  1675. //
  1676. // Reacquire the Scb and it's parent..
  1677. //
  1678. NtfsAcquireExclusiveScb( IrpContext, Scb );
  1679. NtfsReleaseResource( IrpContext, Scb );
  1680. if (ARGUMENT_PRESENT( ParentScb )) {
  1681. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  1682. }
  1683. }
  1684. } // endif DataSection existed
  1685. //
  1686. // Write the file sizes to the attribute. Commit the transaction since the
  1687. // file sizes must get to disk.
  1688. //
  1689. ASSERT( FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ) );
  1690. NtfsWriteFileSizes( IrpContext, Scb, &Scb->Header.ValidDataLength.QuadPart, TRUE, TRUE, FALSE );
  1691. NtfsCheckpointCurrentTransaction( IrpContext );
  1692. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_FILESIZE_ON_CLOSE );
  1693. return;
  1694. }
  1695. //
  1696. // Local support routine
  1697. //
  1698. NTSTATUS
  1699. NtfsFlushCompletionRoutine(
  1700. IN PDEVICE_OBJECT DeviceObject,
  1701. IN PIRP Irp,
  1702. IN PVOID Contxt
  1703. )
  1704. {
  1705. UNREFERENCED_PARAMETER( DeviceObject );
  1706. UNREFERENCED_PARAMETER( Contxt );
  1707. //
  1708. // Add the hack-o-ramma to fix formats.
  1709. //
  1710. if ( Irp->PendingReturned ) {
  1711. IoMarkIrpPending( Irp );
  1712. }
  1713. //
  1714. // If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it
  1715. // to STATUS_SUCCESS.
  1716. //
  1717. if (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST) {
  1718. Irp->IoStatus.Status = STATUS_SUCCESS;
  1719. }
  1720. return STATUS_SUCCESS;
  1721. }
  1722. //
  1723. // Local support routine
  1724. //
  1725. NTSTATUS
  1726. NtfsFlushFcbFileRecords (
  1727. IN PIRP_CONTEXT IrpContext,
  1728. IN PFCB Fcb
  1729. )
  1730. /*++
  1731. Routine Description:
  1732. This routine is called to flush the file records for a given file. It is
  1733. intended to flush the critical file records for the system hives.
  1734. Arguments:
  1735. Fcb - This is the Fcb to flush.
  1736. Return Value:
  1737. NTSTATUS - The status returned from the flush operation.
  1738. --*/
  1739. {
  1740. IO_STATUS_BLOCK IoStatus;
  1741. BOOLEAN MoreToGo;
  1742. LONGLONG LastFileOffset = MAXLONGLONG;
  1743. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1744. PAGED_CODE();
  1745. NtfsInitializeAttributeContext( &AttrContext );
  1746. IoStatus.Status = STATUS_SUCCESS;
  1747. //
  1748. // Use a try-finally to cleanup the context.
  1749. //
  1750. try {
  1751. //
  1752. // Find the first. It should be there.
  1753. //
  1754. MoreToGo = NtfsLookupAttribute( IrpContext,
  1755. Fcb,
  1756. &Fcb->FileReference,
  1757. &AttrContext );
  1758. if (!MoreToGo) {
  1759. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1760. }
  1761. while (MoreToGo) {
  1762. if (AttrContext.FoundAttribute.MftFileOffset != LastFileOffset) {
  1763. LastFileOffset = AttrContext.FoundAttribute.MftFileOffset;
  1764. CcFlushCache( &Fcb->Vcb->MftScb->NonpagedScb->SegmentObject,
  1765. (PLARGE_INTEGER) &LastFileOffset,
  1766. Fcb->Vcb->BytesPerFileRecordSegment,
  1767. &IoStatus );
  1768. if (!NT_SUCCESS( IoStatus.Status )) {
  1769. IoStatus.Status = FsRtlNormalizeNtstatus( IoStatus.Status,
  1770. STATUS_UNEXPECTED_IO_ERROR );
  1771. break;
  1772. }
  1773. }
  1774. MoreToGo = NtfsLookupNextAttribute( IrpContext,
  1775. Fcb,
  1776. &AttrContext );
  1777. }
  1778. } finally {
  1779. DebugUnwind( NtfsFlushFcbFileRecords );
  1780. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1781. }
  1782. return IoStatus.Status;
  1783. }
  1784. NTSTATUS
  1785. NtfsFlushUserStream (
  1786. IN PIRP_CONTEXT IrpContext,
  1787. IN PSCB Scb,
  1788. IN PLONGLONG FileOffset OPTIONAL,
  1789. IN ULONG Length
  1790. )
  1791. /*++
  1792. Routine Description:
  1793. This routine flushes a user stream as a top-level action. To do so
  1794. it checkpoints the current transaction first and frees all of the
  1795. caller's snapshots. After doing the flush, it snapshots the input
  1796. Scb again, just in case the caller plans to do any more work on that
  1797. stream. If the caller needs to modify any other streams (presumably
  1798. metadata), it must know to snapshot them itself after calling this
  1799. routine.
  1800. Arguments:
  1801. Scb - Stream to flush
  1802. FileOffset - FileOffset at which the flush is to start, or NULL for
  1803. entire stream.
  1804. Length - Number of bytes to flush. Ignored if FileOffset not specified.
  1805. Return Value:
  1806. Status of the flush
  1807. --*/
  1808. {
  1809. IO_STATUS_BLOCK IoStatus;
  1810. BOOLEAN ScbAcquired = FALSE;
  1811. PAGED_CODE();
  1812. //
  1813. // Checkpoint the current transaction and free all of its snapshots,
  1814. // in order to treat the flush as a top-level action with his own
  1815. // snapshots, etc.
  1816. //
  1817. NtfsCheckpointCurrentTransaction( IrpContext );
  1818. NtfsFreeSnapshotsForFcb( IrpContext, NULL );
  1819. //
  1820. // Set the wait flag in the IrpContext so we don't hit a case where the
  1821. // reacquire below fails because we can't wait. If our caller was asynchronous
  1822. // and we get this far we will continue synchronously.
  1823. //
  1824. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1825. //
  1826. // We must free the Scb now before calling through MM to prevent
  1827. // collided page deadlocks.
  1828. //
  1829. //
  1830. // We are about to flush the stream. The Scb may be acquired exclusive
  1831. // and, thus, is linked onto the IrpContext or onto one higher
  1832. // up in the IoCallDriver stack. We are about to make a
  1833. // call back into Ntfs which may acquire the Scb exclusive, but
  1834. // NOT put it onto the nested IrpContext exclusive queue which prevents
  1835. // the nested completion from freeing the Scb.
  1836. //
  1837. // This is only a problem for Scb's without a paging resource.
  1838. //
  1839. // We acquire the Scb via ExAcquireResourceExclusiveLite, sidestepping
  1840. // Ntfs bookkeeping, and release it via NtfsReleaseScb.
  1841. //
  1842. ScbAcquired = NtfsIsExclusiveScb( Scb );
  1843. if (ScbAcquired) {
  1844. if (Scb->Header.PagingIoResource == NULL) {
  1845. NtfsAcquireResourceExclusive( IrpContext, Scb, TRUE );
  1846. }
  1847. NtfsReleaseScb( IrpContext, Scb );
  1848. }
  1849. #ifdef COMPRESS_ON_WIRE
  1850. if (Scb->Header.FileObjectC != NULL) {
  1851. PCOMPRESSION_SYNC CompressionSync = NULL;
  1852. //
  1853. // Use a try-finally to clean up the compression sync.
  1854. //
  1855. try {
  1856. NtfsSynchronizeUncompressedIo( Scb,
  1857. NULL,
  1858. 0,
  1859. TRUE,
  1860. &CompressionSync );
  1861. } finally {
  1862. NtfsReleaseCompressionSync( CompressionSync );
  1863. }
  1864. }
  1865. #endif
  1866. //
  1867. // Clear the file record cache before doing the flush. Otherwise FlushVolume may hold this
  1868. // file and be purging the Mft at the same time this thread has a Vacb in the Mft and is
  1869. // trying to reacquire the file in the recursive IO thread.
  1870. //
  1871. NtfsPurgeFileRecordCache( IrpContext );
  1872. //
  1873. // Now do the flush he wanted as a top-level action
  1874. //
  1875. CcFlushCache( &Scb->NonpagedScb->SegmentObject, (PLARGE_INTEGER)FileOffset, Length, &IoStatus );
  1876. //
  1877. // Now reacquire for the caller.
  1878. //
  1879. if (ScbAcquired) {
  1880. NtfsAcquireExclusiveScb( IrpContext, Scb );
  1881. if (Scb->Header.PagingIoResource == NULL) {
  1882. NtfsReleaseResource( IrpContext, Scb );
  1883. }
  1884. }
  1885. return IoStatus.Status;
  1886. }
  1887. //
  1888. // Local support routine
  1889. //
  1890. LONG
  1891. NtfsFlushVolumeExceptionFilter (
  1892. IN PIRP_CONTEXT IrpContext,
  1893. IN PEXCEPTION_POINTERS ExceptionPointer,
  1894. IN NTSTATUS ExceptionCode
  1895. )
  1896. {
  1897. //
  1898. // Swallow any errors except LOG_FILE_FULL, CANT_WAIT and anything else not expected.
  1899. //
  1900. if ((ExceptionCode == STATUS_LOG_FILE_FULL) ||
  1901. (ExceptionCode == STATUS_CANT_WAIT) ||
  1902. !FsRtlIsNtstatusExpected( ExceptionCode )) {
  1903. return EXCEPTION_CONTINUE_SEARCH;
  1904. } else {
  1905. return EXCEPTION_EXECUTE_HANDLER;
  1906. }
  1907. UNREFERENCED_PARAMETER( IrpContext );
  1908. UNREFERENCED_PARAMETER( ExceptionPointer );
  1909. }