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.

5576 lines
185 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. RestrSup.c
  5. Abstract:
  6. This module implements the Ntfs routine to perform Restart on an
  7. Ntfs volume, i.e., to restore a consistent state to the volume that
  8. existed before the last failure.
  9. Author:
  10. Tom Miller [TomM] 24-Jul-1991
  11. Revision History:
  12. --*/
  13. #include "NtfsProc.h"
  14. //
  15. // **** This is a way to disable a restart to get a volume going "as-is".
  16. //
  17. BOOLEAN NtfsDisableRestart = FALSE;
  18. //
  19. // The local debug trace level
  20. //
  21. #define Dbg (DEBUG_TRACE_LOGSUP)
  22. //
  23. // Define a tag for general pool allocations from this module
  24. //
  25. #undef MODULE_POOL_TAG
  26. #define MODULE_POOL_TAG ('RFtN')
  27. //
  28. // Size for initial in memory dirty page table
  29. //
  30. #define INITIAL_NUMBER_DIRTY_PAGES 32
  31. //
  32. // The following macro returns the length of the log record header of
  33. // of log record.
  34. //
  35. //
  36. // ULONG
  37. // NtfsLogRecordHeaderLength (
  38. // IN PIRP_CONTEXT IrpContext,
  39. // IN PNTFS_LOG_RECORD_HEADER LogRecord
  40. // );
  41. //
  42. #define NtfsLogRecordHeaderLength( IC, LR ) \
  43. (sizeof( NTFS_LOG_RECORD_HEADER ) \
  44. + (((PNTFS_LOG_RECORD_HEADER) (LR))->LcnsToFollow > 1 \
  45. ? (((PNTFS_LOG_RECORD_HEADER) (LR))->LcnsToFollow - 1) \
  46. * sizeof( LCN ) \
  47. : 0 ))
  48. //
  49. //
  50. // Local procedure prototypes
  51. //
  52. VOID
  53. InitializeRestartState (
  54. IN PIRP_CONTEXT IrpContext,
  55. IN PVCB Vcb,
  56. OUT PRESTART_POINTERS DirtyPageTable,
  57. OUT PATTRIBUTE_NAME_ENTRY *AttributeNames,
  58. OUT PLSN CheckpointLsn,
  59. OUT PBOOLEAN UnrecognizedRestart
  60. );
  61. VOID
  62. ReleaseRestartState (
  63. IN PVCB Vcb,
  64. IN PRESTART_POINTERS DirtyPageTable,
  65. IN PATTRIBUTE_NAME_ENTRY AttributeNames,
  66. IN BOOLEAN ReleaseVcbTables
  67. );
  68. VOID
  69. AnalysisPass (
  70. IN PIRP_CONTEXT IrpContext,
  71. IN PVCB Vcb,
  72. IN LSN CheckpointLsn,
  73. IN OUT PRESTART_POINTERS DirtyPageTable,
  74. OUT PLSN RedoLsn
  75. );
  76. VOID
  77. RedoPass (
  78. IN PIRP_CONTEXT IrpContext,
  79. IN PVCB Vcb,
  80. IN LSN RedoLsn,
  81. IN OUT PRESTART_POINTERS DirtyPageTable
  82. );
  83. VOID
  84. UndoPass (
  85. IN PIRP_CONTEXT IrpContext,
  86. IN PVCB Vcb
  87. );
  88. VOID
  89. DoAction (
  90. IN PIRP_CONTEXT IrpContext,
  91. IN PVCB Vcb,
  92. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  93. IN NTFS_LOG_OPERATION Operation,
  94. IN PVOID Data,
  95. IN ULONG Length,
  96. IN ULONG LogRecordLength,
  97. IN PLSN RedoLsn OPTIONAL,
  98. IN PSCB Scb OPTIONAL,
  99. OUT PBCB *Bcb,
  100. OUT PLSN *PageLsn
  101. );
  102. VOID
  103. PinMftRecordForRestart (
  104. IN PIRP_CONTEXT IrpContext,
  105. IN PVCB Vcb,
  106. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  107. OUT PBCB *Bcb,
  108. OUT PFILE_RECORD_SEGMENT_HEADER *FileRecord
  109. );
  110. VOID
  111. OpenAttributeForRestart (
  112. IN PIRP_CONTEXT IrpContext,
  113. IN PVCB Vcb,
  114. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  115. IN OUT PSCB *Scb
  116. );
  117. VOID
  118. PinAttributeForRestart (
  119. IN PIRP_CONTEXT IrpContext,
  120. IN PVCB Vcb,
  121. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  122. IN ULONG Length OPTIONAL,
  123. OUT PBCB *Bcb,
  124. OUT PVOID *Buffer,
  125. IN OUT PSCB *Scb
  126. );
  127. BOOLEAN
  128. FindDirtyPage (
  129. IN PRESTART_POINTERS DirtyPageTable,
  130. IN ULONG TargetAttribute,
  131. IN VCN Vcn,
  132. OUT PDIRTY_PAGE_ENTRY *DirtyPageEntry
  133. );
  134. VOID
  135. PageUpdateAnalysis (
  136. IN PVCB Vcb,
  137. IN LSN Lsn,
  138. IN OUT PRESTART_POINTERS DirtyPageTable,
  139. IN PNTFS_LOG_RECORD_HEADER LogRecord
  140. );
  141. VOID
  142. OpenAttributesForRestart (
  143. IN PIRP_CONTEXT IrpContext,
  144. IN PVCB Vcb,
  145. IN PRESTART_POINTERS DirtyPageTable
  146. );
  147. #ifdef ALLOC_PRAGMA
  148. #pragma alloc_text(PAGE, AnalysisPass)
  149. #pragma alloc_text(PAGE, DoAction)
  150. #pragma alloc_text(PAGE, FindDirtyPage)
  151. #pragma alloc_text(PAGE, InitializeRestartState)
  152. #pragma alloc_text(PAGE, NtfsAbortTransaction)
  153. #pragma alloc_text(PAGE, NtfsCloseAttributesFromRestart)
  154. #pragma alloc_text(PAGE, NtfsRestartVolume)
  155. #pragma alloc_text(PAGE, OpenAttributeForRestart)
  156. #pragma alloc_text(PAGE, OpenAttributesForRestart)
  157. #pragma alloc_text(PAGE, PageUpdateAnalysis)
  158. #pragma alloc_text(PAGE, PinAttributeForRestart)
  159. #pragma alloc_text(PAGE, PinMftRecordForRestart)
  160. #pragma alloc_text(PAGE, RedoPass)
  161. #pragma alloc_text(PAGE, ReleaseRestartState)
  162. #pragma alloc_text(PAGE, UndoPass)
  163. #endif
  164. BOOLEAN
  165. NtfsRestartVolume (
  166. IN PIRP_CONTEXT IrpContext,
  167. IN PVCB Vcb,
  168. OUT PBOOLEAN UnrecognizedRestart
  169. )
  170. /*++
  171. Routine Description:
  172. This routine is called by the mount process after the log file has been
  173. started, to restart the volume. Restarting the volume means restoring
  174. it to a consistent state as of the last request which was successfully
  175. completed and written to the log for this volume.
  176. The Restart process is a standard recovery from the Log File in three
  177. passes: Analysis Pass, Redo Pass and Undo pass. Each one of these passes
  178. is implemented in a separate routine in this module.
  179. Arguments:
  180. Vcb - Vcb for the volume which is to be restarted.
  181. UnrecognizedRestart - Indicates that this version of Ntfs doesn't recognize the
  182. restart area. Chkdsk should run to repair the disk.
  183. Return Value:
  184. FALSE - if no updates were applied during restart
  185. TRUE - if updates were applied
  186. --*/
  187. {
  188. RESTART_POINTERS DirtyPageTable;
  189. LSN CheckpointLsn;
  190. LSN RedoLsn;
  191. PATTRIBUTE_NAME_ENTRY AttributeNames = NULL;
  192. BOOLEAN UpdatesApplied = FALSE;
  193. BOOLEAN ReleaseVcbTables = FALSE;
  194. PAGED_CODE();
  195. DebugTrace( +1, Dbg, ("NtfsRestartVolume:\n") );
  196. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  197. #ifdef SYSCACHE
  198. DebugTrace( 0, Dbg, ("Syscache test build\n") );
  199. #endif
  200. RtlZeroMemory( &DirtyPageTable, sizeof(RESTART_POINTERS) );
  201. //
  202. // Use try-finally to insure cleanup on the way out.
  203. //
  204. try {
  205. //
  206. // First we initialize the Open Attribute Table, Transaction Table,
  207. // and Dirty Page Table from our last Checkpoint (as found from our
  208. // Restart Area) in the log.
  209. //
  210. InitializeRestartState( IrpContext,
  211. Vcb,
  212. &DirtyPageTable,
  213. &AttributeNames,
  214. &CheckpointLsn,
  215. UnrecognizedRestart );
  216. ReleaseVcbTables = TRUE;
  217. //
  218. // If the CheckpointLsn is zero, then this is a freshly formattted
  219. // disk and we have no work to do.
  220. //
  221. if (CheckpointLsn.QuadPart == 0) {
  222. LfsResetUndoTotal( Vcb->LogHandle, 2, QuadAlign(sizeof(RESTART_AREA)) + (2 * PAGE_SIZE) );
  223. try_return(NOTHING);
  224. }
  225. #ifdef BENL_DBG
  226. {
  227. PRESTART_LOG RedoLog;
  228. RedoLog = (PRESTART_LOG) NtfsAllocatePoolNoRaise( NonPagedPool, sizeof( RESTART_LOG ) );
  229. if (RedoLog) {
  230. RedoLog->Lsn = CheckpointLsn;
  231. InsertTailList( &(Vcb->RestartRedoHead), &(RedoLog->Links) );
  232. } else {
  233. KdPrint(( "NTFS: Out of memory during restart1\n" ));
  234. }
  235. }
  236. #endif
  237. //
  238. // Start the analysis pass from the Checkpoint Lsn. This pass potentially
  239. // updates all of the tables, and returns the RedoLsn, which is the Lsn
  240. // at which the Redo Pass is to begin.
  241. //
  242. if (!NtfsDisableRestart &&
  243. !FlagOn( Vcb->VcbState, VCB_STATE_BAD_RESTART )) {
  244. AnalysisPass( IrpContext, Vcb, CheckpointLsn, &DirtyPageTable, &RedoLsn );
  245. }
  246. //
  247. // Only proceed if the the Dirty Page Table or Transaction table are
  248. // not empty.
  249. //
  250. // REM: Once we implement the new USN journal restart optimization, this
  251. // won't be a simple !empty test.
  252. //
  253. if (!IsRestartTableEmpty(&DirtyPageTable)
  254. ||
  255. !IsRestartTableEmpty(&Vcb->TransactionTable)) {
  256. //
  257. // If the user wants to mount this readonly, we can't go on.
  258. //
  259. if (NtfsIsVolumeReadOnly( Vcb )) {
  260. LfsResetUndoTotal( Vcb->LogHandle, 2, QuadAlign(sizeof(RESTART_AREA)) + (2 * PAGE_SIZE) );
  261. NtfsRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED, NULL, NULL );
  262. }
  263. UpdatesApplied = TRUE;
  264. //
  265. // Before starting the Redo Pass, we have to reopen all of the
  266. // attributes with dirty pages, and preinitialize their Mcbs with the
  267. // mapping information from the Dirty Page Table.
  268. //
  269. OpenAttributesForRestart( IrpContext, Vcb, &DirtyPageTable );
  270. //
  271. // Perform the Redo Pass, to restore all of the dirty pages to the same
  272. // contents that they had immediately before the crash.
  273. //
  274. RedoPass( IrpContext, Vcb, RedoLsn, &DirtyPageTable );
  275. //
  276. // Finally, perform the Undo Pass to undo any updates which may exist
  277. // for transactions which did not complete.
  278. //
  279. UndoPass( IrpContext, Vcb );
  280. } else {
  281. //
  282. // We know that there's no restart work left to do.
  283. // Hence, if the user has requested a readonly mount, go ahead.
  284. //
  285. if (NtfsIsVolumeReadOnly( Vcb )) {
  286. //
  287. // REM: Make sure the USN journal is clean too.
  288. //
  289. //
  290. // Make sure that the pagingfile isn't on this volume?
  291. }
  292. }
  293. //
  294. // Now that we know that there is no one to abort, we can initialize our
  295. // Undo requirements, to our standard starting point to include the size
  296. // of our Restart Area (for a clean checkpoint) + a page, which is the
  297. // worst case loss when flushing the volume causes Lfs to flush to Lsn.
  298. //
  299. LfsResetUndoTotal( Vcb->LogHandle, 2, QuadAlign(sizeof(RESTART_AREA)) + (2 * PAGE_SIZE) );
  300. //
  301. // If we got an exception, we can at least clean up on the way out.
  302. //
  303. try_exit: NOTHING;
  304. } finally {
  305. DebugUnwind( NtfsRestartVolume );
  306. //
  307. // Free up any resources tied down with the Restart State.
  308. //
  309. ReleaseRestartState( Vcb,
  310. &DirtyPageTable,
  311. AttributeNames,
  312. ReleaseVcbTables );
  313. }
  314. DebugTrace( -1, Dbg, ("NtfsRestartVolume -> %02lx\n", UpdatesApplied) );
  315. return UpdatesApplied;
  316. }
  317. VOID
  318. NtfsAbortTransaction (
  319. IN PIRP_CONTEXT IrpContext,
  320. IN PVCB Vcb,
  321. IN PTRANSACTION_ENTRY Transaction OPTIONAL
  322. )
  323. /*++
  324. Routine Description:
  325. This routine aborts a transaction by undoing all of its actions.
  326. The Undo actions are all performed in the common routine DoAction,
  327. which is also used by the Redo Pass.
  328. Arguments:
  329. Vcb - Vcb for the Volume. NOTE - This argument is not guaranteed to
  330. be valid if Transaction is NULL and there is no Transaction ID
  331. in the IrpContext.
  332. Transaction - Pointer to the transaction entry of the transaction to be
  333. aborted, or NULL to abort current transaction (if there is
  334. one).
  335. Return Value:
  336. None.
  337. --*/
  338. {
  339. LFS_LOG_CONTEXT LogContext;
  340. PNTFS_LOG_RECORD_HEADER LogRecord;
  341. ULONG LogRecordLength;
  342. PVOID Data;
  343. LONG Length;
  344. LSN LogRecordLsn;
  345. LSN UndoRecordLsn;
  346. LFS_RECORD_TYPE RecordType;
  347. TRANSACTION_ID TransactionId;
  348. LSN UndoNextLsn;
  349. LSN PreviousLsn;
  350. TRANSACTION_ID SavedTransaction = IrpContext->TransactionId;
  351. DebugTrace( +1, Dbg, ("NtfsAbortTransaction:\n") );
  352. //
  353. // If a transaction was specified, then we have to set our transaction Id
  354. // into the IrpContext (it was saved above), since NtfsWriteLog requires
  355. // it.
  356. //
  357. if (ARGUMENT_PRESENT(Transaction)) {
  358. IrpContext->TransactionId = GetIndexFromRestartEntry( &Vcb->TransactionTable,
  359. Transaction );
  360. UndoNextLsn = Transaction->UndoNextLsn;
  361. //
  362. // Set the flag in the IrpContext so we will always write the commit
  363. // record.
  364. //
  365. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WROTE_LOG );
  366. //
  367. // Otherwise, we are aborting the current transaction, and we must get the
  368. // pointer to its transaction entry.
  369. //
  370. } else {
  371. if (IrpContext->TransactionId == 0) {
  372. DebugTrace( -1, Dbg, ("NtfsAbortTransaction->VOID (no transaction)\n") );
  373. return;
  374. }
  375. //
  376. // Synchronize access to the transaction table in case the table
  377. // is growing.
  378. //
  379. NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable,
  380. TRUE );
  381. Transaction = GetRestartEntryFromIndex( &Vcb->TransactionTable,
  382. IrpContext->TransactionId );
  383. UndoNextLsn = Transaction->UndoNextLsn;
  384. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  385. }
  386. ASSERT( !NtfsIsVolumeReadOnly( Vcb ) );
  387. //
  388. // If we are aborting the current transaction (by default or explicit
  389. // request), then restore 0 on return because he will be gone.
  390. //
  391. if (IrpContext->TransactionId == SavedTransaction) {
  392. SavedTransaction = 0;
  393. }
  394. DebugTrace( 0, Dbg, ("Transaction = %08lx\n", Transaction) );
  395. //
  396. // We only have to do anything if the transaction has something in its
  397. // UndoNextLsn field.
  398. //
  399. if (UndoNextLsn.QuadPart != 0) {
  400. PBCB PageBcb = NULL;
  401. //
  402. // Read the first record to be undone by this transaction.
  403. //
  404. LfsReadLogRecord( Vcb->LogHandle,
  405. UndoNextLsn,
  406. LfsContextUndoNext,
  407. &LogContext,
  408. &RecordType,
  409. &TransactionId,
  410. &UndoNextLsn,
  411. &PreviousLsn,
  412. &LogRecordLength,
  413. (PVOID *)&LogRecord );
  414. //
  415. // Now loop to read all of our log records forwards, until we hit
  416. // the end of the file, cleaning up at the end.
  417. //
  418. try {
  419. do {
  420. PLSN PageLsn;
  421. //
  422. // Check that the log record is valid.
  423. //
  424. if (!NtfsCheckLogRecord( LogRecord,
  425. LogRecordLength,
  426. TransactionId,
  427. Vcb->OatEntrySize )) {
  428. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  429. }
  430. DebugTrace( 0, Dbg, ("Undo of Log Record at: %08lx\n", LogRecord) );
  431. DebugTrace( 0, Dbg, ("Log Record Lsn = %016I64x\n", LogRecordLsn) );
  432. //
  433. // Log the Undo operation as a CLR, i.e., it has no undo,
  434. // and the UndoNext points to the UndoNext of the current
  435. // log record.
  436. //
  437. // Don't do this if the undo is a noop. This is not only
  438. // efficient, but in the case of a clean shutdown, there
  439. // will be no Scb to pick up from the table below.
  440. //
  441. if (LogRecord->UndoOperation != Noop) {
  442. ULONG i;
  443. PSCB Scb;
  444. VCN Vcn;
  445. LONGLONG Size;
  446. //
  447. // Acquire and release the restart table. We must synchronize
  448. // even though our entry can't be removed because the table
  449. // could be growing (or shrinking) and the table pointer
  450. // could be changing.
  451. //
  452. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable,
  453. TRUE );
  454. //
  455. // We are getting the attribute index from a log record on disk. We
  456. // may have to go through the on-disk Oat.
  457. //
  458. if (Vcb->RestartVersion == 0) {
  459. ULONG InMemoryIndex;
  460. //
  461. // Go through the on-disk Oat.
  462. //
  463. InMemoryIndex = ((POPEN_ATTRIBUTE_ENTRY_V0) GetRestartEntryFromIndex( Vcb->OnDiskOat,
  464. LogRecord->TargetAttribute ))->OatIndex;
  465. ASSERT( InMemoryIndex != 0 );
  466. Scb = ((POPEN_ATTRIBUTE_ENTRY) GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  467. InMemoryIndex ))->OatData->Overlay.Scb;
  468. } else {
  469. ASSERT( Vcb->RestartVersion == 1 );
  470. ASSERT( LogRecord->TargetAttribute != 0 );
  471. Scb = ((POPEN_ATTRIBUTE_ENTRY)GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  472. LogRecord->TargetAttribute))->OatData->Overlay.Scb;
  473. }
  474. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  475. //
  476. // If we have Lcn's to process and restart is in progress,
  477. // then we need to check if this is part of a partial page.
  478. //
  479. if (FlagOn( Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  480. (LogRecord->LcnsToFollow != 0)) {
  481. LCN TargetLcn;
  482. LONGLONG SectorCount, SectorsInRun;
  483. BOOLEAN MappingInMcb;
  484. //
  485. // If the mapping isn't already in the table or the
  486. // mapping corresponds to a hole in the mapping, we
  487. // need to make sure there is no partial page already
  488. // in memory.
  489. //
  490. if (!(MappingInMcb = NtfsLookupNtfsMcbEntry( &Scb->Mcb,
  491. LogRecord->TargetVcn,
  492. &TargetLcn,
  493. &SectorCount,
  494. NULL,
  495. &SectorsInRun,
  496. NULL,
  497. NULL )) ||
  498. (TargetLcn == UNUSED_LCN) ||
  499. ((ULONG)SectorCount) < LogRecord->LcnsToFollow) {
  500. VCN StartingPageVcn;
  501. ULONG ClusterOffset;
  502. BOOLEAN FlushAndPurge;
  503. FlushAndPurge = FALSE;
  504. //
  505. // Remember the Vcn at the start of the containing
  506. // page.
  507. //
  508. ClusterOffset = ((ULONG)LogRecord->TargetVcn) & (Vcb->ClustersPerPage - 1);
  509. StartingPageVcn = BlockAlignTruncate( LogRecord->TargetVcn, (LONG)Vcb->ClustersPerPage );
  510. //
  511. // If this mapping was not in the Mcb, then if the
  512. // Mcb is empty or the last entry is not in this page
  513. // then there is nothing to do.
  514. //
  515. if (!MappingInMcb) {
  516. LCN LastLcn;
  517. VCN LastVcn;
  518. if ((ClusterOffset != 0) &&
  519. NtfsLookupLastNtfsMcbEntry( &Scb->Mcb,
  520. &LastVcn,
  521. &LastLcn ) &&
  522. (LastVcn >= StartingPageVcn)) {
  523. FlushAndPurge = TRUE;
  524. }
  525. //
  526. // If the mapping showed a hole, then the entire
  527. // page needs to be a hole. We know that this mapping
  528. // can't be the last mapping on the page. We just
  529. // need to starting point and the number of clusters
  530. // required for the run.
  531. //
  532. } else if (TargetLcn == UNUSED_LCN) {
  533. if (((ClusterOffset + (ULONG) SectorCount) < Vcb->ClustersPerPage) ||
  534. ((ClusterOffset + (ULONG) SectorCount) > (ULONG) SectorsInRun)) {
  535. FlushAndPurge = TRUE;
  536. }
  537. //
  538. // In the rare case where we are extending an existing mapping
  539. // let's flush and purge.
  540. //
  541. } else {
  542. FlushAndPurge = TRUE;
  543. }
  544. if (FlushAndPurge) {
  545. LONGLONG StartingOffset;
  546. IO_STATUS_BLOCK Iosb;
  547. StartingOffset = LlBytesFromClusters( Vcb, StartingPageVcn );
  548. StartingOffset += BytesFromLogBlocks( LogRecord->ClusterBlockOffset );
  549. CcFlushCache( &Scb->NonpagedScb->SegmentObject,
  550. (PLARGE_INTEGER)&StartingOffset,
  551. PAGE_SIZE,
  552. &Iosb );
  553. NtfsNormalizeAndCleanupTransaction( IrpContext,
  554. &Iosb.Status,
  555. TRUE,
  556. STATUS_UNEXPECTED_IO_ERROR );
  557. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  558. (PLARGE_INTEGER)&StartingOffset,
  559. PAGE_SIZE,
  560. FALSE )) {
  561. KdPrint(("NtfsUndoPass: Unable to purge page\n"));
  562. NtfsRaiseStatus( IrpContext, STATUS_INTERNAL_ERROR, NULL, NULL );
  563. }
  564. }
  565. }
  566. }
  567. //
  568. // Loop to add the allocated Vcns. Note that the page
  569. // may not have been dirty, which means we may not have
  570. // added the run information in the Redo Pass, so we
  571. // add it here.
  572. //
  573. for (i = 0, Vcn = LogRecord->TargetVcn, Size = LlBytesFromClusters( Vcb, Vcn + 1 );
  574. i < (ULONG)LogRecord->LcnsToFollow;
  575. i++, Vcn = Vcn + 1, Size = Size + Vcb->BytesPerCluster ) {
  576. //
  577. // Add this run to the Mcb if the Vcn has not been deleted,
  578. // and it is not for the fixed part of the Mft.
  579. //
  580. if ((LogRecord->LcnsForPage[i] != 0)
  581. &&
  582. (NtfsSegmentNumber( &Scb->Fcb->FileReference ) > MASTER_FILE_TABLE2_NUMBER ||
  583. (Size >= ((VOLUME_DASD_NUMBER + 1) * Vcb->BytesPerFileRecordSegment)) ||
  584. (Scb->AttributeTypeCode != $DATA))) {
  585. //
  586. // We test here if we are performing restart. In that case
  587. // we need to test if the Lcn's are already in the Mcb.
  588. // If not, then we want to flush and purge the page in
  589. // case we have zeroed any half pages.
  590. //
  591. while (!NtfsAddNtfsMcbEntry( &Scb->Mcb,
  592. Vcn,
  593. LogRecord->LcnsForPage[i],
  594. (LONGLONG)1,
  595. FALSE )) {
  596. NtfsRemoveNtfsMcbEntry( &Scb->Mcb,
  597. Vcn,
  598. 1 );
  599. }
  600. }
  601. if (Size > Scb->Header.AllocationSize.QuadPart) {
  602. Scb->Header.AllocationSize.QuadPart =
  603. Scb->Header.FileSize.QuadPart =
  604. Scb->Header.ValidDataLength.QuadPart = Size;
  605. //
  606. // Update the Cache Manager if we have a file object.
  607. //
  608. if (Scb->FileObject != NULL) {
  609. ASSERT( !FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL ) );
  610. CcSetFileSizes( Scb->FileObject,
  611. (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  612. }
  613. }
  614. }
  615. //
  616. // Point to the Redo Data and get its length.
  617. //
  618. Data = (PVOID)((PCHAR)LogRecord + LogRecord->UndoOffset);
  619. Length = LogRecord->UndoLength;
  620. //
  621. // Once we have logged the Undo operation, it is time to apply
  622. // the undo action.
  623. //
  624. DoAction( IrpContext,
  625. Vcb,
  626. LogRecord,
  627. LogRecord->UndoOperation,
  628. Data,
  629. Length,
  630. LogRecordLength,
  631. NULL,
  632. Scb,
  633. &PageBcb,
  634. &PageLsn );
  635. UndoRecordLsn =
  636. NtfsWriteLog( IrpContext,
  637. Scb,
  638. PageBcb,
  639. LogRecord->UndoOperation,
  640. Data,
  641. Length,
  642. CompensationLogRecord,
  643. (PVOID)&UndoNextLsn,
  644. LogRecord->RedoLength,
  645. LlBytesFromClusters( Vcb, LogRecord->TargetVcn ) + BytesFromLogBlocks( LogRecord->ClusterBlockOffset ),
  646. LogRecord->RecordOffset,
  647. LogRecord->AttributeOffset,
  648. BytesFromClusters( Vcb, LogRecord->LcnsToFollow ));
  649. if (PageLsn != NULL) {
  650. *PageLsn = UndoRecordLsn;
  651. }
  652. NtfsUnpinBcb( IrpContext, &PageBcb );
  653. }
  654. //
  655. // Keep reading and looping back until we have read the last record
  656. // for this transaction.
  657. //
  658. } while (LfsReadNextLogRecord( Vcb->LogHandle,
  659. LogContext,
  660. &RecordType,
  661. &TransactionId,
  662. &UndoNextLsn,
  663. &PreviousLsn,
  664. &LogRecordLsn,
  665. &LogRecordLength,
  666. (PVOID *)&LogRecord ));
  667. //
  668. // Now "commit" this guy, just to clean up the transaction table and
  669. // make sure we do not try to abort him again. Also don't wake any
  670. // waiters.
  671. //
  672. if (IrpContext->CheckNewLength != NULL) {
  673. NtfsProcessNewLengthQueue( IrpContext, TRUE );
  674. }
  675. NtfsCommitCurrentTransaction( IrpContext );
  676. } finally {
  677. NtfsUnpinBcb( IrpContext, &PageBcb );
  678. //
  679. // Finally we can kill the log handle.
  680. //
  681. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  682. //
  683. // If we raised out of this routine, we want to be sure to remove
  684. // this entry from the transaction table. Otherwise it will
  685. // be written to disk with the transaction table.
  686. //
  687. if (AbnormalTermination() &&
  688. (IrpContext->TransactionId != 0)) {
  689. NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable,
  690. TRUE );
  691. NtfsFreeRestartTableIndex( &Vcb->TransactionTable,
  692. IrpContext->TransactionId );
  693. //
  694. // signal any waiters if there are no transactions left
  695. //
  696. if (Vcb->TransactionTable.Table->NumberAllocated == 0) {
  697. KeSetEvent( &Vcb->TransactionsDoneEvent, 0, FALSE );
  698. }
  699. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  700. IrpContext->TransactionId = 0;
  701. }
  702. }
  703. //
  704. // This is a wierd case where we are aborting a guy who has not written anything.
  705. // Either his empty transaction entry was captured during a checkpoint and we are
  706. // in restart, or he failed to write his first log record. The important thing
  707. // is to at least go ahead and free his transaction entry.
  708. //
  709. } else {
  710. //
  711. // We can now free the transaction table index, because we are
  712. // done with it now.
  713. //
  714. NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable,
  715. TRUE );
  716. NtfsFreeRestartTableIndex( &Vcb->TransactionTable,
  717. IrpContext->TransactionId );
  718. //
  719. // signal any waiters if there are no transactions left
  720. //
  721. if (Vcb->TransactionTable.Table->NumberAllocated == 0) {
  722. KeSetEvent( &Vcb->TransactionsDoneEvent, 0, FALSE );
  723. }
  724. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  725. }
  726. IrpContext->TransactionId = SavedTransaction;
  727. DebugTrace( -1, Dbg, ("NtfsAbortTransaction->VOID\n") );
  728. }
  729. //
  730. // Internal support routine
  731. //
  732. VOID
  733. InitializeRestartState (
  734. IN PIRP_CONTEXT IrpContext,
  735. IN PVCB Vcb,
  736. OUT PRESTART_POINTERS DirtyPageTable,
  737. OUT PATTRIBUTE_NAME_ENTRY *AttributeNames,
  738. OUT PLSN CheckpointLsn,
  739. OUT PBOOLEAN UnrecognizedRestart
  740. )
  741. /*++
  742. Routine Description:
  743. This routine initializes the volume state for restart, as a first step
  744. in performing restart on the volume. Essentially it reads the last
  745. Ntfs Restart Area on the volume, and then loads all of the Restart
  746. Tables. The Open Attribute Table and Transaction Table are allocated,
  747. read in, and linked to the Vcb in the normal way. (The names for the
  748. Open Attribute Table are separately read into pool, in order to fix
  749. up the Unicode Name Strings in the Attribute Entries, for the duration
  750. of Restart, after which they must switch over to use the same name as
  751. in the Scb as they do in the running system.) In addition, the Dirty
  752. Pages Table is read and returned directly, since it is only during
  753. Restart anyway.
  754. The Checkpoint Lsn is also returned. This is the Lsn at which the
  755. Analysis Pass should start.
  756. Arguments:
  757. Vcb - Vcb for volume which is being restarted.
  758. DirtyPageTable - Returns the Dirty Page Table read from the log.
  759. AttributeNames - Returns pointer to AttributeNames buffer, which should
  760. be deleted at the end of Restart, if not NULL
  761. CheckpointLsn - Returns the Checkpoint Lsn to be passed to the
  762. Analysis Pass.
  763. UnrecognizedRestart - Indicates that this version of Ntfs doesn't recognize the
  764. restart area. Chkdsk should run to repair the disk.
  765. Return Value:
  766. None.
  767. --*/
  768. {
  769. PRESTART_AREA RestartArea;
  770. RESTART_AREA RestartAreaBuffer[2];
  771. LFS_LOG_CONTEXT LogContext;
  772. LSN RestartAreaLsn;
  773. PNTFS_LOG_RECORD_HEADER LogRecord;
  774. ULONG LogHeaderLength;
  775. PATTRIBUTE_NAME_ENTRY Name;
  776. LFS_RECORD_TYPE RecordType;
  777. TRANSACTION_ID TransactionId;
  778. LSN UndoNextLsn;
  779. LSN PreviousLsn;
  780. ULONG RestartAreaLength = 2 * sizeof(RESTART_AREA);
  781. BOOLEAN CleanupLogContext = FALSE;
  782. BOOLEAN ReleaseTransactionTable = FALSE;
  783. BOOLEAN ReleaseAttributeTable = FALSE;
  784. BOOLEAN ReleaseDirtyPageTable = FALSE;
  785. PRESTART_POINTERS NewTable = NULL;
  786. LOG_FILE_INFORMATION LogFileInformation;
  787. ULONG InfoLength = sizeof(LogFileInformation);
  788. NTSTATUS Status;
  789. PAGED_CODE();
  790. DebugTrace( +1, Dbg, ("InitializeRestartState:\n") );
  791. DebugTrace( 0, Dbg, ("DirtyPageTable = %08lx\n", DirtyPageTable) );
  792. *AttributeNames = NULL;
  793. *CheckpointLsn = Li0;
  794. //
  795. // Use the correct version for the dirty pages.
  796. //
  797. NtfsInitializeRestartTable( sizeof( DIRTY_PAGE_ENTRY ) + ((Vcb->ClustersPerPage - 1) * sizeof( LCN )),
  798. INITIAL_NUMBER_DIRTY_PAGES,
  799. DirtyPageTable );
  800. //
  801. // Read our Restart Area. Use a larger buffer than this version understands
  802. // in case a later version of Ntfs wants to tack some information to
  803. // the end of the restart area.
  804. //
  805. RestartArea = &RestartAreaBuffer[0];
  806. if (!NtfsDisableRestart &&
  807. !FlagOn( Vcb->VcbState, VCB_STATE_BAD_RESTART )) {
  808. Status = LfsReadRestartArea( Vcb->LogHandle,
  809. &RestartAreaLength,
  810. RestartArea,
  811. &RestartAreaLsn );
  812. if (STATUS_BUFFER_TOO_SMALL == Status) {
  813. RestartArea = NtfsAllocatePool( PagedPool , RestartAreaLength );
  814. Status = LfsReadRestartArea( Vcb->LogHandle,
  815. &RestartAreaLength,
  816. RestartArea,
  817. &RestartAreaLsn );
  818. }
  819. ASSERT( NT_SUCCESS( Status ) );
  820. DebugTrace( 0, Dbg, ("RestartArea read at %08lx\n", &RestartArea) );
  821. }
  822. //
  823. // Record the current lsn at this point
  824. //
  825. LfsReadLogFileInformation( Vcb->LogHandle,
  826. &LogFileInformation,
  827. &InfoLength );
  828. ASSERT( InfoLength != 0 );
  829. Vcb->CurrentLsnAtMount = LogFileInformation.LastLsn;
  830. //
  831. // If we get back zero for Restart Area Length, then zero it and proceed.
  832. // Generally this will only happen on a virgin disk.
  833. //
  834. if ((RestartAreaLength == 0) ||
  835. NtfsDisableRestart ||
  836. FlagOn( Vcb->VcbState, VCB_STATE_BAD_RESTART )) {
  837. RtlZeroMemory( RestartArea, sizeof(RESTART_AREA) );
  838. RestartAreaLength = sizeof(RESTART_AREA);
  839. //
  840. // If the restart version is unrecognized then use the default.
  841. //
  842. } else if ((RestartArea->MajorVersion != 1) &&
  843. ((RestartArea->MajorVersion != 0) || (RestartArea->MinorVersion != 0))) {
  844. *UnrecognizedRestart = TRUE;
  845. RtlZeroMemory( RestartArea, sizeof(RESTART_AREA) );
  846. RestartAreaLength = sizeof(RESTART_AREA);
  847. RestartAreaLsn.QuadPart = 0;
  848. } else {
  849. //
  850. // Use the Restart version number from the disk. Update the Vcb version if needed.
  851. //
  852. if (RestartArea->MajorVersion != Vcb->RestartVersion) {
  853. NtfsUpdateOatVersion( Vcb, RestartArea->MajorVersion );
  854. }
  855. //
  856. // If the RestartArea does not include an LowestOpenUsn, then just set it to 0.
  857. // Also default the Usn file reference and cache bias to zero.
  858. //
  859. if (RestartAreaLength == SIZEOF_OLD_RESTART_AREA) {
  860. RestartArea->LowestOpenUsn = 0;
  861. RestartAreaLength = sizeof(RESTART_AREA);
  862. *((PLONGLONG) &RestartArea->UsnJournalReference) = 0;
  863. RestartArea->UsnCacheBias = 0;
  864. }
  865. }
  866. Vcb->LowestOpenUsn = RestartArea->LowestOpenUsn;
  867. Vcb->UsnJournalReference = RestartArea->UsnJournalReference;
  868. Vcb->UsnCacheBias = 0;
  869. //
  870. // Return the Start Of Checkpoint Lsn. Typically we can use the value we stored
  871. // in our restart area. The exception is where we have never written a fuzzy
  872. // checkpoint since mounting the volume. In that case the CheckpointLsn will
  873. // be zero but we may have log records on the disk. Use our restart area
  874. // Lsn in that case.
  875. //
  876. *CheckpointLsn = RestartArea->StartOfCheckpoint;
  877. if (RestartArea->StartOfCheckpoint.QuadPart == 0) {
  878. *CheckpointLsn = RestartAreaLsn;
  879. }
  880. try {
  881. //
  882. // Allocate and Read in the Transaction Table.
  883. //
  884. if (RestartArea->TransactionTableLength != 0) {
  885. //
  886. // Workaround for compiler bug.
  887. //
  888. PreviousLsn = RestartArea->TransactionTableLsn;
  889. LfsReadLogRecord( Vcb->LogHandle,
  890. PreviousLsn,
  891. LfsContextPrevious,
  892. &LogContext,
  893. &RecordType,
  894. &TransactionId,
  895. &UndoNextLsn,
  896. &PreviousLsn,
  897. &RestartAreaLength,
  898. (PVOID *) &LogRecord );
  899. CleanupLogContext = TRUE;
  900. //
  901. // Check that the log record is valid.
  902. //
  903. if (!NtfsCheckLogRecord( LogRecord,
  904. RestartAreaLength,
  905. TransactionId,
  906. Vcb->OatEntrySize )) {
  907. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  908. }
  909. //
  910. // Now check that this is a valid restart table.
  911. //
  912. if (!NtfsCheckRestartTable( Add2Ptr( LogRecord, LogRecord->RedoOffset ),
  913. RestartAreaLength - LogRecord->RedoOffset)) {
  914. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  915. }
  916. //
  917. // Subtract the length of the log page header and increment the
  918. // pointer for
  919. //
  920. LogHeaderLength = NtfsLogRecordHeaderLength( IrpContext, LogRecord );
  921. RestartAreaLength -= LogHeaderLength;
  922. ASSERT( RestartAreaLength >= RestartArea->TransactionTableLength );
  923. //
  924. // TEMPCODE RESTART_DEBUG There is already a buffer.
  925. //
  926. NtfsFreePool( Vcb->TransactionTable.Table );
  927. Vcb->TransactionTable.Table =
  928. NtfsAllocatePool( NonPagedPool, RestartAreaLength );
  929. RtlCopyMemory( Vcb->TransactionTable.Table,
  930. Add2Ptr( LogRecord, LogHeaderLength ),
  931. RestartAreaLength );
  932. //
  933. // Kill the log handle.
  934. //
  935. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  936. CleanupLogContext = FALSE;
  937. }
  938. //
  939. // TEMPCODE RESTART_DEBUG There is already a structure.
  940. //
  941. NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable, TRUE );
  942. ReleaseTransactionTable = TRUE;
  943. //
  944. // The next record back should be the Dirty Pages Table.
  945. //
  946. if (RestartArea->DirtyPageTableLength != 0) {
  947. //
  948. // Workaround for compiler bug.
  949. //
  950. PreviousLsn = RestartArea->DirtyPageTableLsn;
  951. LfsReadLogRecord( Vcb->LogHandle,
  952. PreviousLsn,
  953. LfsContextPrevious,
  954. &LogContext,
  955. &RecordType,
  956. &TransactionId,
  957. &UndoNextLsn,
  958. &PreviousLsn,
  959. &RestartAreaLength,
  960. (PVOID *) &LogRecord );
  961. CleanupLogContext = TRUE;
  962. //
  963. // Check that the log record is valid.
  964. //
  965. if (!NtfsCheckLogRecord( LogRecord,
  966. RestartAreaLength,
  967. TransactionId,
  968. Vcb->OatEntrySize )) {
  969. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  970. }
  971. //
  972. // Now check that this is a valid restart table.
  973. //
  974. if (!NtfsCheckRestartTable( Add2Ptr( LogRecord, LogRecord->RedoOffset ),
  975. RestartAreaLength - LogRecord->RedoOffset)) {
  976. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  977. }
  978. //
  979. // Subtract the length of the log page header and increment the
  980. // pointer for
  981. //
  982. LogHeaderLength = NtfsLogRecordHeaderLength( IrpContext, LogRecord );
  983. RestartAreaLength -= LogHeaderLength;
  984. ASSERT( RestartAreaLength >= RestartArea->DirtyPageTableLength );
  985. //
  986. // If the version number in the restart table is version 0 then
  987. // we need to pull the entries out of the on-disk table and put them
  988. // into in-memory version.
  989. //
  990. if (RestartArea->MajorVersion == 0) {
  991. RESTART_POINTERS OldTable;
  992. PDIRTY_PAGE_ENTRY_V0 OldEntry;
  993. PDIRTY_PAGE_ENTRY NewEntry;
  994. OldTable.Table = Add2Ptr( LogRecord, LogHeaderLength );
  995. //
  996. // Check that our assumption about clusters per page matches the data on disk
  997. // if not in sync reallocate the table using the number of on disk lcns
  998. //
  999. if (OldTable.Table->EntrySize - sizeof( DIRTY_PAGE_ENTRY_V0 ) > DirtyPageTable->Table->EntrySize - sizeof( DIRTY_PAGE_ENTRY )) {
  1000. DebugTrace(+1, Dbg, ("NTFS: resizing table in initrestartstate\n"));
  1001. NtfsFreeRestartTable( DirtyPageTable );
  1002. NtfsInitializeRestartTable( sizeof( DIRTY_PAGE_ENTRY ) + OldTable.Table->EntrySize - sizeof( DIRTY_PAGE_ENTRY_V0 ),
  1003. INITIAL_NUMBER_DIRTY_PAGES,
  1004. DirtyPageTable );
  1005. }
  1006. OldEntry = NtfsGetFirstRestartTable( &OldTable );
  1007. while (OldEntry != NULL) {
  1008. ULONG PageIndex;
  1009. PageIndex = NtfsAllocateRestartTableIndex( DirtyPageTable, TRUE );
  1010. NewEntry = GetRestartEntryFromIndex( DirtyPageTable, PageIndex );
  1011. RtlCopyMemory( NewEntry, OldEntry, FIELD_OFFSET( DIRTY_PAGE_ENTRY_V0, Reserved ));
  1012. NewEntry->Vcn = OldEntry->Vcn;
  1013. NewEntry->OldestLsn = OldEntry->OldestLsn;
  1014. if (NewEntry->LcnsToFollow != 0) {
  1015. RtlCopyMemory( &NewEntry->LcnsForPage[0],
  1016. &OldEntry->LcnsForPage[0],
  1017. sizeof( LCN ) * NewEntry->LcnsToFollow );
  1018. }
  1019. OldEntry = NtfsGetNextRestartTable( &OldTable, OldEntry );
  1020. }
  1021. } else {
  1022. //
  1023. // Simply copy the old data over.
  1024. //
  1025. NtfsFreePool( DirtyPageTable->Table );
  1026. DirtyPageTable->Table = NULL;
  1027. DirtyPageTable->Table =
  1028. NtfsAllocatePool( NonPagedPool, RestartAreaLength );
  1029. RtlCopyMemory( DirtyPageTable->Table,
  1030. Add2Ptr( LogRecord, LogHeaderLength ),
  1031. RestartAreaLength );
  1032. }
  1033. //
  1034. // Kill the log handle.
  1035. //
  1036. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  1037. CleanupLogContext = FALSE;
  1038. //
  1039. // If the cluster size is larger than the page size we may have
  1040. // multiple entries for the same Vcn. Go through the table
  1041. // and remove the duplicates, remembering the oldest Lsn values.
  1042. //
  1043. if (Vcb->BytesPerCluster > PAGE_SIZE) {
  1044. PDIRTY_PAGE_ENTRY CurrentEntry;
  1045. PDIRTY_PAGE_ENTRY NextEntry;
  1046. CurrentEntry = NtfsGetFirstRestartTable( DirtyPageTable );
  1047. while (CurrentEntry != NULL) {
  1048. NextEntry = CurrentEntry;
  1049. while ((NextEntry = NtfsGetNextRestartTable( DirtyPageTable, NextEntry )) != NULL) {
  1050. if ((NextEntry->TargetAttribute == CurrentEntry->TargetAttribute) &&
  1051. (NextEntry->Vcn == CurrentEntry->Vcn)) {
  1052. if (NextEntry->OldestLsn.QuadPart < CurrentEntry->OldestLsn.QuadPart) {
  1053. CurrentEntry->OldestLsn.QuadPart = NextEntry->OldestLsn.QuadPart;
  1054. }
  1055. NtfsFreeRestartTableIndex( DirtyPageTable,
  1056. GetIndexFromRestartEntry( DirtyPageTable,
  1057. NextEntry ));
  1058. }
  1059. }
  1060. CurrentEntry = NtfsGetNextRestartTable( DirtyPageTable, CurrentEntry );
  1061. }
  1062. }
  1063. }
  1064. NtfsAcquireExclusiveRestartTable( DirtyPageTable, TRUE );
  1065. ReleaseDirtyPageTable = TRUE;
  1066. //
  1067. // The next record back should be the Attribute Names.
  1068. //
  1069. if (RestartArea->AttributeNamesLength != 0) {
  1070. //
  1071. // Workaround for compiler bug.
  1072. //
  1073. PreviousLsn = RestartArea->AttributeNamesLsn;
  1074. LfsReadLogRecord( Vcb->LogHandle,
  1075. PreviousLsn,
  1076. LfsContextPrevious,
  1077. &LogContext,
  1078. &RecordType,
  1079. &TransactionId,
  1080. &UndoNextLsn,
  1081. &PreviousLsn,
  1082. &RestartAreaLength,
  1083. (PVOID *) &LogRecord );
  1084. CleanupLogContext = TRUE;
  1085. //
  1086. // Check that the log record is valid.
  1087. //
  1088. if (!NtfsCheckLogRecord( LogRecord,
  1089. RestartAreaLength,
  1090. TransactionId,
  1091. Vcb->OatEntrySize )) {
  1092. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1093. }
  1094. //
  1095. // Subtract the length of the log page header and increment the
  1096. // pointer for
  1097. //
  1098. LogHeaderLength = NtfsLogRecordHeaderLength( IrpContext, LogRecord );
  1099. RestartAreaLength -= LogHeaderLength;
  1100. ASSERT( RestartAreaLength >= RestartArea->AttributeNamesLength );
  1101. *AttributeNames =
  1102. NtfsAllocatePool( NonPagedPool, RestartAreaLength );
  1103. RtlCopyMemory( *AttributeNames,
  1104. Add2Ptr( LogRecord, LogHeaderLength ),
  1105. RestartAreaLength );
  1106. //
  1107. // Kill the log handle.
  1108. //
  1109. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  1110. CleanupLogContext = FALSE;
  1111. }
  1112. //
  1113. // The next record back should be the Attribute Table.
  1114. //
  1115. if (RestartArea->OpenAttributeTableLength != 0) {
  1116. POPEN_ATTRIBUTE_ENTRY OpenEntry;
  1117. //
  1118. // Workaround for compiler bug.
  1119. //
  1120. PreviousLsn = RestartArea->OpenAttributeTableLsn;
  1121. LfsReadLogRecord( Vcb->LogHandle,
  1122. PreviousLsn,
  1123. LfsContextPrevious,
  1124. &LogContext,
  1125. &RecordType,
  1126. &TransactionId,
  1127. &UndoNextLsn,
  1128. &PreviousLsn,
  1129. &RestartAreaLength,
  1130. (PVOID *) &LogRecord );
  1131. CleanupLogContext = TRUE;
  1132. //
  1133. // Check that the log record is valid.
  1134. //
  1135. if (!NtfsCheckLogRecord( LogRecord,
  1136. RestartAreaLength,
  1137. TransactionId,
  1138. Vcb->OatEntrySize )) {
  1139. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1140. }
  1141. //
  1142. // Now check that this is a valid restart table.
  1143. //
  1144. if (!NtfsCheckRestartTable( Add2Ptr( LogRecord, LogRecord->RedoOffset ),
  1145. RestartAreaLength - LogRecord->RedoOffset)) {
  1146. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1147. }
  1148. //
  1149. // Subtract the length of the log page header and increment the
  1150. // pointer for
  1151. //
  1152. LogHeaderLength = NtfsLogRecordHeaderLength( IrpContext, LogRecord );
  1153. RestartAreaLength -= LogHeaderLength;
  1154. ASSERT( RestartAreaLength >= RestartArea->OpenAttributeTableLength );
  1155. //
  1156. // If the restart version is version 0 then we need to create
  1157. // a corresponding in-memory structure and refer back to it.
  1158. //
  1159. if (RestartArea->MajorVersion == 0) {
  1160. POPEN_ATTRIBUTE_ENTRY_V0 OldEntry;
  1161. NewTable = NtfsAllocatePool( NonPagedPool, RestartAreaLength );
  1162. NtfsFreePool( Vcb->OnDiskOat->Table );
  1163. Vcb->OnDiskOat->Table = (PRESTART_TABLE) NewTable;
  1164. NewTable = NULL;
  1165. RtlCopyMemory( Vcb->OnDiskOat->Table,
  1166. Add2Ptr( LogRecord, LogHeaderLength ),
  1167. RestartAreaLength );
  1168. //
  1169. // Now for each entry in this table create one in our in-memory version.
  1170. //
  1171. OldEntry = NtfsGetFirstRestartTable( Vcb->OnDiskOat );
  1172. while (OldEntry != NULL) {
  1173. //
  1174. // Allocate the attribute data structure.
  1175. //
  1176. NewTable = NtfsAllocatePool( PagedPool, sizeof( OPEN_ATTRIBUTE_DATA ) );
  1177. RtlZeroMemory( NewTable, sizeof( OPEN_ATTRIBUTE_DATA ));
  1178. //
  1179. // Now get an new index for the data.
  1180. //
  1181. OldEntry->OatIndex = NtfsAllocateRestartTableIndex( &Vcb->OpenAttributeTable, TRUE );
  1182. //
  1183. // Initialize the new entry with data from the on-disk entry.
  1184. //
  1185. OpenEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, OldEntry->OatIndex );
  1186. InsertTailList( &Vcb->OpenAttributeData, &((POPEN_ATTRIBUTE_DATA) NewTable)->Links );
  1187. OpenEntry->OatData = (POPEN_ATTRIBUTE_DATA) NewTable;
  1188. NewTable = NULL;
  1189. OpenEntry->OatData->OnDiskAttributeIndex = GetIndexFromRestartEntry( Vcb->OnDiskOat,
  1190. OldEntry );
  1191. OpenEntry->BytesPerIndexBuffer = OldEntry->BytesPerIndexBuffer;
  1192. OpenEntry->AttributeTypeCode = OldEntry->AttributeTypeCode;
  1193. OpenEntry->FileReference = OldEntry->FileReference;
  1194. OpenEntry->LsnOfOpenRecord.QuadPart = OldEntry->LsnOfOpenRecord.QuadPart;
  1195. OldEntry = NtfsGetNextRestartTable( Vcb->OnDiskOat, OldEntry );
  1196. }
  1197. //
  1198. // If the restart version is version 1 then simply copy it over.
  1199. // We also need to allocate the auxiliary data structure.
  1200. //
  1201. } else {
  1202. //
  1203. // TEMPCODE RESTART_DEBUG There is already a buffer.
  1204. //
  1205. NewTable = NtfsAllocatePool( NonPagedPool, RestartAreaLength );
  1206. NtfsFreePool( Vcb->OpenAttributeTable.Table );
  1207. Vcb->OpenAttributeTable.Table = (PRESTART_TABLE) NewTable;
  1208. NewTable = NULL;
  1209. RtlCopyMemory( Vcb->OpenAttributeTable.Table,
  1210. Add2Ptr( LogRecord, LogHeaderLength ),
  1211. RestartAreaLength );
  1212. //
  1213. // First loop to clear all of the Scb pointers in case we
  1214. // have a premature abort and want to clean up.
  1215. //
  1216. OpenEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  1217. //
  1218. // Loop to end of table.
  1219. //
  1220. while (OpenEntry != NULL) {
  1221. //
  1222. // Allocate the attribute data structure.
  1223. //
  1224. NewTable = NtfsAllocatePool( PagedPool, sizeof( OPEN_ATTRIBUTE_DATA ) );
  1225. RtlZeroMemory( NewTable, sizeof( OPEN_ATTRIBUTE_DATA ));
  1226. InsertTailList( &Vcb->OpenAttributeData, &((POPEN_ATTRIBUTE_DATA) NewTable)->Links );
  1227. OpenEntry->OatData = (POPEN_ATTRIBUTE_DATA) NewTable;
  1228. NewTable = NULL;
  1229. //
  1230. // The on-disk index is the same as the in-memory index.
  1231. //
  1232. OpenEntry->OatData->OnDiskAttributeIndex = GetIndexFromRestartEntry( &Vcb->OpenAttributeTable,
  1233. OpenEntry );
  1234. //
  1235. // Point to next entry in table, or NULL.
  1236. //
  1237. OpenEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable,
  1238. OpenEntry );
  1239. }
  1240. }
  1241. //
  1242. // Kill the log handle.
  1243. //
  1244. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  1245. CleanupLogContext = FALSE;
  1246. }
  1247. //
  1248. // Here is a case where there was no attribute table on disk. Make sure we have the
  1249. // correct on-disk version in the Vcb if this is version 0.
  1250. //
  1251. ASSERT( (RestartArea->OpenAttributeTableLength != 0) ||
  1252. (Vcb->RestartVersion != 0) ||
  1253. (Vcb->OnDiskOat != NULL) );
  1254. //
  1255. // TEMPCODE RESTART_DEBUG There is already a structure.
  1256. //
  1257. NtfsAcquireExclusiveRestartTable( &Vcb->OpenAttributeTable, TRUE );
  1258. ReleaseAttributeTable = TRUE;
  1259. //
  1260. // The only other thing we have to do before returning is patch up the
  1261. // Unicode String's in the Attribute Table to point to their respective
  1262. // attribute names.
  1263. //
  1264. if (RestartArea->AttributeNamesLength != 0) {
  1265. Name = *AttributeNames;
  1266. while (Name->Index != 0) {
  1267. POPEN_ATTRIBUTE_ENTRY Entry;
  1268. if (!IsRestartIndexWithinTable( Vcb->OnDiskOat, Name->Index )) {
  1269. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1270. }
  1271. Entry = GetRestartEntryFromIndex( Vcb->OnDiskOat, Name->Index );
  1272. //
  1273. // Check if we have a level of indirection.
  1274. //
  1275. if (Vcb->RestartVersion == 0) {
  1276. if (!IsRestartIndexWithinTable( &Vcb->OpenAttributeTable, ((POPEN_ATTRIBUTE_ENTRY_V0) Entry)->OatIndex )) {
  1277. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1278. }
  1279. Entry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  1280. ((POPEN_ATTRIBUTE_ENTRY_V0) Entry)->OatIndex );
  1281. }
  1282. Entry->OatData->AttributeName.MaximumLength =
  1283. Entry->OatData->AttributeName.Length = Name->NameLength;
  1284. Entry->OatData->AttributeName.Buffer = (PWSTR)&Name->Name[0];
  1285. Name = (PATTRIBUTE_NAME_ENTRY)((PCHAR)Name +
  1286. sizeof(ATTRIBUTE_NAME_ENTRY) +
  1287. (ULONG)Name->NameLength );
  1288. }
  1289. }
  1290. } finally {
  1291. //
  1292. // Release any transaction tables we acquired if we raised during
  1293. // this routine.
  1294. //
  1295. if (AbnormalTermination()) {
  1296. if (ReleaseTransactionTable) {
  1297. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  1298. }
  1299. if (ReleaseAttributeTable) {
  1300. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1301. }
  1302. if (ReleaseDirtyPageTable) {
  1303. NtfsReleaseRestartTable( DirtyPageTable );
  1304. }
  1305. }
  1306. if (CleanupLogContext) {
  1307. //
  1308. // Kill the log handle.
  1309. //
  1310. LfsTerminateLogQuery( Vcb->LogHandle, LogContext );
  1311. }
  1312. //
  1313. // Did we fail to create the new table.
  1314. //
  1315. if (NewTable != NULL) {
  1316. NtfsFreePool( NewTable );
  1317. }
  1318. //
  1319. // If we allocated a restart area rather than using the stack
  1320. // free it here
  1321. //
  1322. if (RestartArea != &RestartAreaBuffer[0]) {
  1323. NtfsFreePool( RestartArea );
  1324. }
  1325. }
  1326. DebugTrace( 0, Dbg, ("AttributeNames > %08lx\n", *AttributeNames) );
  1327. DebugTrace( 0, Dbg, ("CheckpointLsn > %016I64x\n", *CheckpointLsn) );
  1328. DebugTrace( -1, Dbg, ("NtfsInitializeRestartState -> VOID\n") );
  1329. }
  1330. VOID
  1331. ReleaseRestartState (
  1332. IN PVCB Vcb,
  1333. IN PRESTART_POINTERS DirtyPageTable,
  1334. IN PATTRIBUTE_NAME_ENTRY AttributeNames,
  1335. IN BOOLEAN ReleaseVcbTables
  1336. )
  1337. /*++
  1338. Routine Description:
  1339. This routine releases all of the restart state.
  1340. Arguments:
  1341. Vcb - Vcb for the volume being restarted.
  1342. DirtyPageTable - pointer to the dirty page table, if one was allocated.
  1343. AttributeNames - pointer to the attribute names buffer, if one was allocated.
  1344. ReleaseVcbTables - TRUE if we are to release the restart tables in the Vcb,
  1345. FALSE otherwise.
  1346. Return Value:
  1347. None.
  1348. --*/
  1349. {
  1350. PAGED_CODE();
  1351. //
  1352. // If the caller successfully had a successful restart, then we must release
  1353. // the transaction and open attribute tables.
  1354. //
  1355. if (ReleaseVcbTables) {
  1356. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  1357. NtfsReleaseRestartTable( &Vcb->OpenAttributeTable );
  1358. }
  1359. //
  1360. // Free the dirty page table, if there is one.
  1361. //
  1362. if (DirtyPageTable != NULL) {
  1363. NtfsFreeRestartTable( DirtyPageTable );
  1364. }
  1365. //
  1366. // Free the temporary attribute names buffer, if there is one.
  1367. //
  1368. if (AttributeNames != NULL) {
  1369. NtfsFreePool( AttributeNames );
  1370. }
  1371. }
  1372. //
  1373. // Internal support routine
  1374. //
  1375. VOID
  1376. AnalysisPass (
  1377. IN PIRP_CONTEXT IrpContext,
  1378. IN PVCB Vcb,
  1379. IN LSN CheckpointLsn,
  1380. IN OUT PRESTART_POINTERS DirtyPageTable,
  1381. OUT PLSN RedoLsn
  1382. )
  1383. /*++
  1384. Routine Description:
  1385. This routine performs the analysis phase of Restart. Starting at
  1386. the CheckpointLsn, it reads all records written by Ntfs, and takes
  1387. the following actions:
  1388. For all log records which create or update attributes, a check is
  1389. made to see if the affected page(s) are already in the Dirty Pages
  1390. Table. For any page that is not, it is added, and the OldestLsn
  1391. field is set to the Lsn of the log record.
  1392. The transaction table is updated on transaction state changes,
  1393. and also to maintain the PreviousLsn and UndoNextLsn fields.
  1394. If any attributes are truncated or deleted (including delete of
  1395. an entire file), then any corrsponding pages in the Dirty Page
  1396. Table are deleted.
  1397. When attributes or entire files are deleted, the respective entries
  1398. are deleted from the Open Attribute Table.
  1399. For Hot Fix records, the Dirty Pages Table is scanned for the HotFixed
  1400. Vcn, and if one is found, the Lcn field in the table is updated to
  1401. the new location.
  1402. When the end of the log file is encountered, the Dirty Page Table is
  1403. scanned for the Oldest of the OldestLsn fields. This value is returned
  1404. as the RedoLsn, i.e., the point at which the Redo Pass must occur.
  1405. Arguments:
  1406. Vcb - Volume which is being restarted.
  1407. CheckpointLsn - Lsn at which the Analysis Pass is to begin.
  1408. DirtyPageTable - Pointer to a pointer to the Dirty Page Table, as
  1409. found from the last Restart Area.
  1410. RedoLsn - Returns point at which the Redo Pass should begin.
  1411. Return Value:
  1412. None.
  1413. --*/
  1414. {
  1415. LFS_LOG_CONTEXT LogContext;
  1416. PNTFS_LOG_RECORD_HEADER LogRecord;
  1417. ULONG LogRecordLength;
  1418. LSN LogRecordLsn = CheckpointLsn;
  1419. PRESTART_POINTERS TransactionTable = &Vcb->TransactionTable;
  1420. PRESTART_POINTERS OpenAttributeTable = &Vcb->OpenAttributeTable;
  1421. LFS_LOG_HANDLE LogHandle = Vcb->LogHandle;
  1422. LFS_RECORD_TYPE RecordType;
  1423. TRANSACTION_ID TransactionId;
  1424. PTRANSACTION_ENTRY Transaction;
  1425. LSN UndoNextLsn;
  1426. LSN PreviousLsn;
  1427. POPEN_ATTRIBUTE_DATA OatData = NULL;
  1428. PAGED_CODE();
  1429. DebugTrace( +1, Dbg, ("AnalysisPass:\n") );
  1430. DebugTrace( 0, Dbg, ("CheckpointLsn = %016I64x\n", CheckpointLsn) );
  1431. *RedoLsn = Li0; //**** LfsZeroLsn;
  1432. //
  1433. // Read the first Lsn.
  1434. //
  1435. LfsReadLogRecord( LogHandle,
  1436. CheckpointLsn,
  1437. LfsContextForward,
  1438. &LogContext,
  1439. &RecordType,
  1440. &TransactionId,
  1441. &UndoNextLsn,
  1442. &PreviousLsn,
  1443. &LogRecordLength,
  1444. (PVOID *)&LogRecord );
  1445. //
  1446. // Use a try-finally to cleanup the query context.
  1447. //
  1448. try {
  1449. //
  1450. // Since the checkpoint remembers the previous Lsn, not the one he wants to
  1451. // start at, we must always skip the first record.
  1452. //
  1453. // Loop to read all subsequent records to the end of the log file.
  1454. //
  1455. while ( LfsReadNextLogRecord( LogHandle,
  1456. LogContext,
  1457. &RecordType,
  1458. &TransactionId,
  1459. &UndoNextLsn,
  1460. &PreviousLsn,
  1461. &LogRecordLsn,
  1462. &LogRecordLength,
  1463. (PVOID *)&LogRecord )) {
  1464. //
  1465. // Check that the log record is valid.
  1466. //
  1467. if (!NtfsCheckLogRecord( LogRecord,
  1468. LogRecordLength,
  1469. TransactionId,
  1470. Vcb->OatEntrySize )) {
  1471. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1472. }
  1473. //
  1474. // The first Lsn after the previous Lsn remembered in the checkpoint is
  1475. // the first candidate for the RedoLsn.
  1476. //
  1477. if (RedoLsn->QuadPart == 0) {
  1478. *RedoLsn = LogRecordLsn;
  1479. }
  1480. if (RecordType != LfsClientRecord) {
  1481. continue;
  1482. }
  1483. DebugTrace( 0, Dbg, ("Analysis of LogRecord at: %08lx\n", LogRecord) );
  1484. DebugTrace( 0, Dbg, ("Log Record Lsn = %016I64x\n", LogRecordLsn) );
  1485. DebugTrace( 0, Dbg, ("LogRecord->RedoOperation = %08lx\n", LogRecord->RedoOperation) );
  1486. DebugTrace( 0, Dbg, ("TransactionId = %08lx\n", TransactionId) );
  1487. //
  1488. // Now update the Transaction Table for this transaction. If there is no
  1489. // entry present or it is unallocated we allocate the entry.
  1490. //
  1491. Transaction = (PTRANSACTION_ENTRY)GetRestartEntryFromIndex( &Vcb->TransactionTable,
  1492. TransactionId );
  1493. if (!IsRestartIndexWithinTable( &Vcb->TransactionTable, TransactionId ) ||
  1494. !IsRestartTableEntryAllocated( Transaction )) {
  1495. Transaction = (PTRANSACTION_ENTRY) NtfsAllocateRestartTableFromIndex( &Vcb->TransactionTable,
  1496. TransactionId );
  1497. Transaction->TransactionState = TransactionActive;
  1498. Transaction->FirstLsn = LogRecordLsn;
  1499. }
  1500. Transaction->PreviousLsn =
  1501. Transaction->UndoNextLsn = LogRecordLsn;
  1502. //
  1503. // If this is a compensation log record (CLR), then change the UndoNextLsn to
  1504. // be the UndoNextLsn of this record.
  1505. //
  1506. if (LogRecord->UndoOperation == CompensationLogRecord) {
  1507. Transaction->UndoNextLsn = UndoNextLsn;
  1508. }
  1509. //
  1510. // Dispatch to handle log record depending on type.
  1511. //
  1512. switch (LogRecord->RedoOperation) {
  1513. //
  1514. // The following cases are performing various types of updates
  1515. // and need to make the appropriate updates to the Transaction
  1516. // and Dirty Page Tables.
  1517. //
  1518. case InitializeFileRecordSegment:
  1519. case DeallocateFileRecordSegment:
  1520. case WriteEndOfFileRecordSegment:
  1521. case CreateAttribute:
  1522. case DeleteAttribute:
  1523. case UpdateResidentValue:
  1524. case UpdateNonresidentValue:
  1525. case UpdateMappingPairs:
  1526. case SetNewAttributeSizes:
  1527. case AddIndexEntryRoot:
  1528. case DeleteIndexEntryRoot:
  1529. case AddIndexEntryAllocation:
  1530. case DeleteIndexEntryAllocation:
  1531. case WriteEndOfIndexBuffer:
  1532. case SetIndexEntryVcnRoot:
  1533. case SetIndexEntryVcnAllocation:
  1534. case UpdateFileNameRoot:
  1535. case UpdateFileNameAllocation:
  1536. case SetBitsInNonresidentBitMap:
  1537. case ClearBitsInNonresidentBitMap:
  1538. case UpdateRecordDataRoot:
  1539. case UpdateRecordDataAllocation:
  1540. PageUpdateAnalysis( Vcb,
  1541. LogRecordLsn,
  1542. DirtyPageTable,
  1543. LogRecord );
  1544. break;
  1545. //
  1546. // This case is deleting clusters from a nonresident attribute,
  1547. // thus it deletes a range of pages from the Dirty Page Table.
  1548. // This log record is written each time a nonresident attribute
  1549. // is truncated, whether explicitly or as part of deletion.
  1550. //
  1551. // Processing one of these records is pretty compute-intensive
  1552. // (three nested loops, where a couple of them can be large),
  1553. // but this is the code that prevents us from dropping, for example,
  1554. // index updates into the middle of user files, if the index stream
  1555. // is truncated and the sectors are reallocated to a user file
  1556. // and we crash after the user data has been written.
  1557. //
  1558. // I.e., note the following sequence:
  1559. //
  1560. // <checkpoint>
  1561. // <Index update>
  1562. // <Index page deleted>
  1563. // <Same cluster(s) reallocated to user file>
  1564. // <User data written>
  1565. //
  1566. // CRASH!
  1567. //
  1568. // Since the user data was not logged (else there would be no problem),
  1569. // It could get overwritten while applying the index update after a
  1570. // crash - Pisses off the user as well as the security dudes!
  1571. //
  1572. case DeleteDirtyClusters:
  1573. {
  1574. PDIRTY_PAGE_ENTRY DirtyPage;
  1575. PLCN_RANGE LcnRange;
  1576. ULONG i, j;
  1577. LCN FirstLcn, LastLcn;
  1578. ULONG RangeCount = LogRecord->RedoLength / sizeof(LCN_RANGE);
  1579. //
  1580. // Point to the Lcn range array.
  1581. //
  1582. LcnRange = Add2Ptr(LogRecord, LogRecord->RedoOffset);
  1583. //
  1584. // Loop through all of the Lcn ranges in this log record.
  1585. //
  1586. for (i = 0; i < RangeCount; i++) {
  1587. FirstLcn = LcnRange[i].StartLcn;
  1588. LastLcn = FirstLcn + (LcnRange[i].Count - 1);
  1589. DebugTrace( 0, Dbg, ("Deleting from FirstLcn = %016I64x\n", FirstLcn));
  1590. DebugTrace( 0, Dbg, ("Deleting to LastLcn = %016I64x\n", LastLcn ));
  1591. //
  1592. // Point to first Dirty Page Entry.
  1593. //
  1594. DirtyPage = NtfsGetFirstRestartTable( DirtyPageTable );
  1595. //
  1596. // Loop to end of table.
  1597. //
  1598. while (DirtyPage != NULL) {
  1599. //
  1600. // Loop through all of the Lcns for this dirty page.
  1601. //
  1602. for (j = 0; j < (ULONG)DirtyPage->LcnsToFollow; j++) {
  1603. if ((DirtyPage->LcnsForPage[j] >= FirstLcn) &&
  1604. (DirtyPage->LcnsForPage[j] <= LastLcn)) {
  1605. DirtyPage->LcnsForPage[j] = 0;
  1606. }
  1607. }
  1608. //
  1609. // Point to next entry in table, or NULL.
  1610. //
  1611. DirtyPage = NtfsGetNextRestartTable( DirtyPageTable,
  1612. DirtyPage );
  1613. }
  1614. }
  1615. }
  1616. break;
  1617. //
  1618. // When a record is encountered for a nonresident attribute that
  1619. // was opened, we have to add an entry to the Open Attribute Table.
  1620. //
  1621. case OpenNonresidentAttribute:
  1622. {
  1623. POPEN_ATTRIBUTE_ENTRY AttributeEntry;
  1624. ULONG NameSize;
  1625. //
  1626. // If the table is not currently big enough, then we must
  1627. // expand it.
  1628. //
  1629. if (!IsRestartIndexWithinTable( Vcb->OnDiskOat,
  1630. (ULONG)LogRecord->TargetAttribute )) {
  1631. ULONG NeededEntries;
  1632. //
  1633. // Compute how big the table needs to be. Add 10 extra entries
  1634. // for some cushion.
  1635. //
  1636. NeededEntries = (LogRecord->TargetAttribute / Vcb->OnDiskOat->Table->EntrySize);
  1637. NeededEntries = (NeededEntries + 10 - Vcb->OnDiskOat->Table->NumberEntries);
  1638. NtfsExtendRestartTable( Vcb->OnDiskOat,
  1639. NeededEntries,
  1640. MAXULONG );
  1641. }
  1642. ASSERT( IsRestartIndexWithinTable( Vcb->OnDiskOat,
  1643. (ULONG)LogRecord->TargetAttribute ));
  1644. //
  1645. // Calculate size of Attribute Name Entry, if there is one.
  1646. //
  1647. NameSize = LogRecord->UndoLength;
  1648. //
  1649. // Point to the entry being opened.
  1650. //
  1651. OatData = NtfsAllocatePool( PagedPool, sizeof( OPEN_ATTRIBUTE_DATA ) );
  1652. RtlZeroMemory( OatData, sizeof( OPEN_ATTRIBUTE_DATA ));
  1653. OatData->OnDiskAttributeIndex = LogRecord->TargetAttribute;
  1654. //
  1655. // We extended the table above so allocating an oat index won't raise
  1656. // any exceptions
  1657. //
  1658. NtfsAcquireExclusiveRestartTable( Vcb->OnDiskOat, TRUE );
  1659. AttributeEntry = GetRestartEntryFromIndex( Vcb->OnDiskOat, LogRecord->TargetAttribute );
  1660. if (IsRestartTableEntryAllocated( AttributeEntry )) {
  1661. //
  1662. // For restart version 0 we need to look up the corresponding in memory
  1663. // entry to use for the free
  1664. //
  1665. if (Vcb->RestartVersion == 0) {
  1666. POPEN_ATTRIBUTE_ENTRY_V0 OldEntry = (POPEN_ATTRIBUTE_ENTRY_V0) AttributeEntry;
  1667. if (IsRestartIndexWithinTable( &Vcb->OpenAttributeTable, OldEntry->OatIndex )) {
  1668. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, OldEntry->OatIndex );
  1669. } else {
  1670. //
  1671. // The on disk entry is invalid so just let NtfsAllocateRestarTableFromIndex
  1672. // overwrite it.
  1673. //
  1674. AttributeEntry = NULL;
  1675. }
  1676. }
  1677. if (AttributeEntry) {
  1678. NtfsFreeAttributeEntry( Vcb, AttributeEntry );
  1679. }
  1680. }
  1681. AttributeEntry = NtfsAllocateRestartTableFromIndex( Vcb->OnDiskOat, LogRecord->TargetAttribute );
  1682. NtfsReleaseRestartTable( Vcb->OnDiskOat );
  1683. //
  1684. // The attribute entry better either not be allocated or it must
  1685. // be for the same file.
  1686. //
  1687. // **** May eliminate this test.
  1688. //
  1689. // ASSERT( !IsRestartTableEntryAllocated(AttributeEntry) ||
  1690. // xxEql(AttributeEntry->FileReference,
  1691. // ((POPEN_ATTRIBUTE_ENTRY)Add2Ptr(LogRecord,
  1692. // LogRecord->RedoOffset))->FileReference));
  1693. //
  1694. // Initialize this entry from the log record.
  1695. //
  1696. ASSERT( LogRecord->RedoLength == Vcb->OnDiskOat->Table->EntrySize );
  1697. RtlCopyMemory( AttributeEntry,
  1698. (PCHAR)LogRecord + LogRecord->RedoOffset,
  1699. LogRecord->RedoLength );
  1700. ASSERT( IsRestartTableEntryAllocated(AttributeEntry) );
  1701. //
  1702. // Get a new entry for the in-memory copy if needed.
  1703. //
  1704. if (Vcb->RestartVersion == 0) {
  1705. POPEN_ATTRIBUTE_ENTRY_V0 OldEntry = (POPEN_ATTRIBUTE_ENTRY_V0) AttributeEntry;
  1706. ULONG NewIndex;
  1707. NewIndex = NtfsAllocateRestartTableIndex( &Vcb->OpenAttributeTable, TRUE );
  1708. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, NewIndex );
  1709. AttributeEntry->BytesPerIndexBuffer = OldEntry->BytesPerIndexBuffer;
  1710. AttributeEntry->AttributeTypeCode = OldEntry->AttributeTypeCode;
  1711. AttributeEntry->FileReference = OldEntry->FileReference;
  1712. AttributeEntry->LsnOfOpenRecord.QuadPart = OldEntry->LsnOfOpenRecord.QuadPart;
  1713. OldEntry->OatIndex = NewIndex;
  1714. }
  1715. //
  1716. // Finish initializing the AttributeData.
  1717. //
  1718. AttributeEntry->OatData = OatData;
  1719. InsertTailList( &Vcb->OpenAttributeData, &OatData->Links );
  1720. OatData = NULL;
  1721. //
  1722. // If there is a name at the end, then allocate space to
  1723. // copy it into, and do the copy. We also set the buffer
  1724. // pointer in the string descriptor, although note that the
  1725. // lengths must be correct.
  1726. //
  1727. if (NameSize != 0) {
  1728. AttributeEntry->OatData->Overlay.AttributeName =
  1729. NtfsAllocatePool( NonPagedPool, NameSize );
  1730. RtlCopyMemory( AttributeEntry->OatData->Overlay.AttributeName,
  1731. Add2Ptr(LogRecord, LogRecord->UndoOffset),
  1732. NameSize );
  1733. AttributeEntry->OatData->AttributeName.Buffer = AttributeEntry->OatData->Overlay.AttributeName;
  1734. AttributeEntry->OatData->AttributeNamePresent = TRUE;
  1735. //
  1736. // Otherwise, show there is no name.
  1737. //
  1738. } else {
  1739. AttributeEntry->OatData->Overlay.AttributeName = NULL;
  1740. AttributeEntry->OatData->AttributeName.Buffer = NULL;
  1741. AttributeEntry->OatData->AttributeNamePresent = FALSE;
  1742. }
  1743. AttributeEntry->OatData->AttributeName.MaximumLength =
  1744. AttributeEntry->OatData->AttributeName.Length = (USHORT) NameSize;
  1745. }
  1746. break;
  1747. //
  1748. // For HotFix records, we need to update the Lcn in the Dirty Page
  1749. // Table.
  1750. //
  1751. case HotFix:
  1752. {
  1753. PDIRTY_PAGE_ENTRY DirtyPage;
  1754. //
  1755. // First see if the Vcn is currently in the Dirty Page
  1756. // Table. If not, there is nothing to do.
  1757. //
  1758. if (FindDirtyPage( DirtyPageTable,
  1759. LogRecord->TargetAttribute,
  1760. LogRecord->TargetVcn,
  1761. &DirtyPage )) {
  1762. //
  1763. // Index to the Lcn in question in the Dirty Page Entry
  1764. // and rewrite it with the Hot Fixed Lcn from the log
  1765. // record. Note that it is ok to just use the LowPart
  1766. // of the Vcns to calculate the array offset, because
  1767. // any multiple of 2**32 is guaranteed to be on a page
  1768. // boundary!
  1769. //
  1770. if (DirtyPage->LcnsForPage[((ULONG)LogRecord->TargetVcn) - ((ULONG)DirtyPage->Vcn)] != 0) {
  1771. DirtyPage->LcnsForPage[((ULONG)LogRecord->TargetVcn) - ((ULONG)DirtyPage->Vcn)] = LogRecord->LcnsForPage[0];
  1772. }
  1773. }
  1774. }
  1775. break;
  1776. //
  1777. // For end top level action, we will just update the transaction
  1778. // table to skip the top level action on undo.
  1779. //
  1780. case EndTopLevelAction:
  1781. {
  1782. //
  1783. // Now update the Transaction Table for this transaction.
  1784. //
  1785. Transaction->PreviousLsn = LogRecordLsn;
  1786. Transaction->UndoNextLsn = UndoNextLsn;
  1787. }
  1788. break;
  1789. //
  1790. // For Prepare Transaction, we just change the state of our entry.
  1791. //
  1792. case PrepareTransaction:
  1793. ASSERT( !IsRestartTableEntryAllocated( Transaction ));
  1794. Transaction->TransactionState = TransactionPrepared;
  1795. break;
  1796. //
  1797. // For Commit Transaction, we just change the state of our entry.
  1798. //
  1799. case CommitTransaction:
  1800. ASSERT( !IsRestartTableEntryAllocated( Transaction ));
  1801. Transaction->TransactionState = TransactionCommitted;
  1802. break;
  1803. //
  1804. // For forget, we can delete our transaction entry, since the transaction
  1805. // will not have to be aborted.
  1806. //
  1807. case ForgetTransaction:
  1808. {
  1809. NtfsFreeRestartTableIndex( &Vcb->TransactionTable,
  1810. TransactionId );
  1811. }
  1812. break;
  1813. //
  1814. // The following cases require no action in the Analysis Pass.
  1815. //
  1816. case Noop:
  1817. case OpenAttributeTableDump:
  1818. case AttributeNamesDump:
  1819. case DirtyPageTableDump:
  1820. case TransactionTableDump:
  1821. break;
  1822. //
  1823. // All codes will be explicitly handled. If we see a code we
  1824. // do not expect, then we are in trouble.
  1825. //
  1826. default:
  1827. DebugTrace( 0, Dbg, ("Unexpected Log Record Type: %04lx\n", LogRecord->RedoOperation) );
  1828. DebugTrace( 0, Dbg, ("Record address: %08lx\n", LogRecord) );
  1829. DebugTrace( 0, Dbg, ("Record length: %08lx\n", LogRecordLength) );
  1830. ASSERTMSG( "Unknown Action!\n", FALSE );
  1831. break;
  1832. }
  1833. }
  1834. } finally {
  1835. //
  1836. // Finally we can kill the log handle.
  1837. //
  1838. LfsTerminateLogQuery( LogHandle, LogContext );
  1839. if (OatData != NULL) { NtfsFreePool( OatData ); }
  1840. }
  1841. //
  1842. // Now we just have to scan the Dirty Page Table and Transaction Table
  1843. // for the lowest Lsn, and return it as the Redo Lsn.
  1844. //
  1845. {
  1846. PDIRTY_PAGE_ENTRY DirtyPage;
  1847. //
  1848. // Point to first Dirty Page Entry.
  1849. //
  1850. DirtyPage = NtfsGetFirstRestartTable( DirtyPageTable );
  1851. //
  1852. // Loop to end of table.
  1853. //
  1854. while (DirtyPage != NULL) {
  1855. //
  1856. // Update the Redo Lsn if this page has an older one.
  1857. //
  1858. if ((DirtyPage->OldestLsn.QuadPart != 0) &&
  1859. (DirtyPage->OldestLsn.QuadPart < RedoLsn->QuadPart)) {
  1860. *RedoLsn = DirtyPage->OldestLsn;
  1861. }
  1862. //
  1863. // Point to next entry in table, or NULL.
  1864. //
  1865. DirtyPage = NtfsGetNextRestartTable( DirtyPageTable,
  1866. DirtyPage );
  1867. }
  1868. }
  1869. //
  1870. // Point to first Transaction Entry.
  1871. //
  1872. Transaction = NtfsGetFirstRestartTable( &Vcb->TransactionTable );
  1873. //
  1874. // Loop to end of table.
  1875. //
  1876. while (Transaction != NULL) {
  1877. //
  1878. // Update the Redo Lsn if this transaction has an older one.
  1879. //
  1880. if ((Transaction->FirstLsn.QuadPart != 0) &&
  1881. (Transaction->FirstLsn.QuadPart < RedoLsn->QuadPart)) {
  1882. *RedoLsn = Transaction->FirstLsn;
  1883. }
  1884. //
  1885. // Point to next entry in table, or NULL.
  1886. //
  1887. Transaction = NtfsGetNextRestartTable( &Vcb->TransactionTable,
  1888. Transaction );
  1889. }
  1890. DebugTrace( 0, Dbg, ("RedoLsn > %016I64x\n", *RedoLsn) );
  1891. DebugTrace( 0, Dbg, ("AnalysisPass -> VOID\n") );
  1892. return;
  1893. }
  1894. //
  1895. // Internal support routine
  1896. //
  1897. VOID
  1898. RedoPass (
  1899. IN PIRP_CONTEXT IrpContext,
  1900. IN PVCB Vcb,
  1901. IN LSN RedoLsn,
  1902. IN OUT PRESTART_POINTERS DirtyPageTable
  1903. )
  1904. /*++
  1905. Routine Description:
  1906. This routine performs the Redo Pass of Restart. Beginning at the
  1907. Redo Lsn established during the Analysis Pass, the redo operations
  1908. of all log records are applied, until the end of file is encountered.
  1909. Updates are only applied to clusters in the dirty page table. If a
  1910. cluster was deleted, then its entry will have been deleted during the
  1911. Analysis Pass.
  1912. The Redo actions are all performed in the common routine DoAction,
  1913. which is also used by the Undo Pass.
  1914. Arguments:
  1915. Vcb - Volume which is being restarted.
  1916. RedoLsn - Lsn at which the Redo Pass is to begin.
  1917. DirtyPageTable - Pointer to the Dirty Page Table, as reconstructed
  1918. from the Analysis Pass.
  1919. Return Value:
  1920. None.
  1921. --*/
  1922. {
  1923. LFS_LOG_CONTEXT LogContext;
  1924. PNTFS_LOG_RECORD_HEADER LogRecord;
  1925. ULONG LogRecordLength;
  1926. PVOID Data;
  1927. ULONG Length;
  1928. LFS_RECORD_TYPE RecordType;
  1929. TRANSACTION_ID TransactionId;
  1930. LSN UndoNextLsn;
  1931. LSN PreviousLsn;
  1932. ULONG i, SavedLength;
  1933. LSN LogRecordLsn = RedoLsn;
  1934. LFS_LOG_HANDLE LogHandle = Vcb->LogHandle;
  1935. PBCB PageBcb = NULL;
  1936. BOOLEAN GeneratedUsnBias = FALSE;
  1937. PAGED_CODE();
  1938. DebugTrace( +1, Dbg, ("RedoPass:\n") );
  1939. DebugTrace( 0, Dbg, ("RedoLsn = %016I64x\n", RedoLsn) );
  1940. DebugTrace( 0, Dbg, ("DirtyPageTable = %08lx\n", DirtyPageTable) );
  1941. //
  1942. // If the dirty page table is empty, then we can skip the entire Redo Pass.
  1943. //
  1944. if (IsRestartTableEmpty( DirtyPageTable )) {
  1945. return;
  1946. }
  1947. //
  1948. // Read the record at the Redo Lsn, before falling into common code
  1949. // to handle each record.
  1950. //
  1951. LfsReadLogRecord( LogHandle,
  1952. RedoLsn,
  1953. LfsContextForward,
  1954. &LogContext,
  1955. &RecordType,
  1956. &TransactionId,
  1957. &UndoNextLsn,
  1958. &PreviousLsn,
  1959. &LogRecordLength,
  1960. (PVOID *)&LogRecord );
  1961. //
  1962. // Now loop to read all of our log records forwards, until we hit
  1963. // the end of the file, cleaning up at the end.
  1964. //
  1965. try {
  1966. do {
  1967. PDIRTY_PAGE_ENTRY DirtyPage;
  1968. PLSN PageLsn;
  1969. BOOLEAN FoundPage;
  1970. if (RecordType != LfsClientRecord) {
  1971. continue;
  1972. }
  1973. //
  1974. // Check that the log record is valid.
  1975. //
  1976. if (!NtfsCheckLogRecord( LogRecord,
  1977. LogRecordLength,
  1978. TransactionId,
  1979. Vcb->OatEntrySize )) {
  1980. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1981. }
  1982. DebugTrace( 0, Dbg, ("Redo of LogRecord at: %08lx\n", LogRecord) );
  1983. DebugTrace( 0, Dbg, ("Log Record Lsn = %016I64x\n", LogRecordLsn) );
  1984. //
  1985. // Ignore log records that do not update pages.
  1986. //
  1987. if (LogRecord->LcnsToFollow == 0) {
  1988. DebugTrace( 0, Dbg, ("Skipping log record (no update)\n") );
  1989. continue;
  1990. }
  1991. //
  1992. // Consult Dirty Page Table to see if we have to apply this update.
  1993. // If the page is not there, or if the Lsn of this Log Record is
  1994. // older than the Lsn in the Dirty Page Table, then we do not have
  1995. // to apply the update.
  1996. //
  1997. FoundPage = FindDirtyPage( DirtyPageTable,
  1998. LogRecord->TargetAttribute,
  1999. LogRecord->TargetVcn,
  2000. &DirtyPage );
  2001. if (!FoundPage ||
  2002. (LogRecordLsn.QuadPart < DirtyPage->OldestLsn.QuadPart)) {
  2003. DebugDoit(
  2004. DebugTrace( 0, Dbg, ("Skipping log record operation %08lx\n",
  2005. LogRecord->RedoOperation ));
  2006. if (!FoundPage) {
  2007. DebugTrace( 0, Dbg, ("Page not in dirty page table\n") );
  2008. } else {
  2009. DebugTrace( 0, Dbg, ("Page Lsn more current: %016I64x\n",
  2010. DirtyPage->OldestLsn) );
  2011. }
  2012. );
  2013. continue;
  2014. //
  2015. // We also skip the update if the entry was never put in the Mcb for
  2016. // the file.
  2017. } else {
  2018. POPEN_ATTRIBUTE_ENTRY ThisEntry;
  2019. PSCB TargetScb;
  2020. LCN TargetLcn;
  2021. //
  2022. // Check that the entry is within the table and is allocated.
  2023. //
  2024. if (!IsRestartIndexWithinTable( Vcb->OnDiskOat,
  2025. LogRecord->TargetAttribute )) {
  2026. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  2027. }
  2028. ThisEntry = GetRestartEntryFromIndex( Vcb->OnDiskOat, LogRecord->TargetAttribute );
  2029. if (!IsRestartTableEntryAllocated( ThisEntry )) {
  2030. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  2031. }
  2032. //
  2033. // Check if we need to go to a different restart table.
  2034. //
  2035. if (Vcb->RestartVersion == 0) {
  2036. ThisEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  2037. ((POPEN_ATTRIBUTE_ENTRY_V0) ThisEntry)->OatIndex );
  2038. }
  2039. TargetScb = ThisEntry->OatData->Overlay.Scb;
  2040. //
  2041. // If there is no Scb it means that we don't have an entry in Open
  2042. // Attribute Table for this attribute.
  2043. //
  2044. if (TargetScb == NULL) {
  2045. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  2046. }
  2047. if (!NtfsLookupNtfsMcbEntry( &TargetScb->Mcb,
  2048. LogRecord->TargetVcn,
  2049. &TargetLcn,
  2050. NULL,
  2051. NULL,
  2052. NULL,
  2053. NULL,
  2054. NULL ) ||
  2055. (TargetLcn == UNUSED_LCN)) {
  2056. DebugTrace( 0, Dbg, ("Clusters removed from page entry\n") );
  2057. continue;
  2058. }
  2059. //
  2060. // Check if we need to generate the usncachebias.
  2061. // Since we read log records fwd the usn offsets are also going to be
  2062. // monotonic - the 1st one we see will be the farthest back
  2063. //
  2064. if (FlagOn( TargetScb->ScbPersist, SCB_PERSIST_USN_JOURNAL ) &&
  2065. !GeneratedUsnBias) {
  2066. LONGLONG ClusterOffset;
  2067. LONGLONG FileOffset;
  2068. if (LogRecord->RedoLength > 0) {
  2069. ClusterOffset = BytesFromLogBlocks( LogRecord->ClusterBlockOffset );
  2070. FileOffset = LlBytesFromClusters( Vcb, LogRecord->TargetVcn ) + ClusterOffset;
  2071. ASSERT( FileOffset >= Vcb->UsnCacheBias );
  2072. Vcb->UsnCacheBias = FileOffset & ~(USN_JOURNAL_CACHE_BIAS - 1);
  2073. if (Vcb->UsnCacheBias != 0) {
  2074. Vcb->UsnCacheBias -= USN_JOURNAL_CACHE_BIAS;
  2075. }
  2076. #ifdef BENL_DBG
  2077. if (Vcb->UsnCacheBias != 0) {
  2078. KdPrint(( "Ntfs: vcb:0x%x restart cache bias: 0x%x\n", Vcb, Vcb->UsnCacheBias ));
  2079. }
  2080. #endif
  2081. }
  2082. GeneratedUsnBias = TRUE;
  2083. }
  2084. }
  2085. //
  2086. // Point to the Redo Data and get its length.
  2087. //
  2088. Data = (PVOID)((PCHAR)LogRecord + LogRecord->RedoOffset);
  2089. Length = LogRecord->RedoLength;
  2090. //
  2091. // Shorten length by any Lcns which were deleted.
  2092. //
  2093. SavedLength = Length;
  2094. for (i = (ULONG)LogRecord->LcnsToFollow; i != 0; i--) {
  2095. ULONG AllocatedLength;
  2096. ULONG VcnOffset;
  2097. VcnOffset = BytesFromLogBlocks( LogRecord->ClusterBlockOffset ) + LogRecord->RecordOffset + LogRecord->AttributeOffset;
  2098. //
  2099. // If the Vcn in question is allocated, we can just get out.
  2100. //
  2101. if (DirtyPage->LcnsForPage[((ULONG)LogRecord->TargetVcn) - ((ULONG)DirtyPage->Vcn) + i - 1] != 0) {
  2102. break;
  2103. }
  2104. //
  2105. // The only log records that update pages but have a length of zero
  2106. // are deleting things from Usa-protected structures. If we hit such
  2107. // a log record and any Vcn has been deleted within the Usa structure,
  2108. // let us assume that the entire Usa structure has been deleted. Change
  2109. // the SavedLength to be nonzero to cause us to skip this log record
  2110. // at the end of this for loop!
  2111. //
  2112. if (SavedLength == 0) {
  2113. SavedLength = 1;
  2114. }
  2115. //
  2116. // Calculate the allocated space left relative to the log record Vcn,
  2117. // after removing this unallocated Vcn.
  2118. //
  2119. AllocatedLength = BytesFromClusters( Vcb, i - 1 );
  2120. //
  2121. // If the update described in this log record goes beyond the allocated
  2122. // space, then we will have to reduce the length.
  2123. //
  2124. if ((VcnOffset + Length) > AllocatedLength) {
  2125. //
  2126. // If the specified update starts at or beyond the allocated length, then
  2127. // we must set length to zero.
  2128. //
  2129. if (VcnOffset >= AllocatedLength) {
  2130. Length = 0;
  2131. //
  2132. // Otherwise set the length to end exactly at the end of the previous
  2133. // cluster.
  2134. //
  2135. } else {
  2136. Length = AllocatedLength - VcnOffset;
  2137. }
  2138. }
  2139. }
  2140. //
  2141. // If the resulting Length from above is now zero, we can skip this log record.
  2142. //
  2143. if ((Length == 0) && (SavedLength != 0)) {
  2144. continue;
  2145. }
  2146. #ifdef BENL_DBG
  2147. {
  2148. PRESTART_LOG RedoLog;
  2149. RedoLog = (PRESTART_LOG) NtfsAllocatePoolNoRaise( NonPagedPool, sizeof( RESTART_LOG ) );
  2150. if (RedoLog) {
  2151. RedoLog->Lsn = LogRecordLsn;
  2152. InsertTailList( &(Vcb->RestartRedoHead), &(RedoLog->Links) );
  2153. } else {
  2154. KdPrint(( "NTFS: out of memory during restart redo\n" ));
  2155. }
  2156. }
  2157. #endif
  2158. //
  2159. // Apply the Redo operation in a common routine.
  2160. //
  2161. DoAction( IrpContext,
  2162. Vcb,
  2163. LogRecord,
  2164. LogRecord->RedoOperation,
  2165. Data,
  2166. Length,
  2167. LogRecordLength,
  2168. &LogRecordLsn,
  2169. NULL,
  2170. &PageBcb,
  2171. &PageLsn );
  2172. if (PageLsn != NULL) {
  2173. *PageLsn = LogRecordLsn;
  2174. }
  2175. if (PageBcb != NULL) {
  2176. CcSetDirtyPinnedData( PageBcb, &LogRecordLsn );
  2177. NtfsUnpinBcb( IrpContext, &PageBcb );
  2178. }
  2179. //
  2180. // Keep reading and looping back until end of file.
  2181. //
  2182. } while (LfsReadNextLogRecord( LogHandle,
  2183. LogContext,
  2184. &RecordType,
  2185. &TransactionId,
  2186. &UndoNextLsn,
  2187. &PreviousLsn,
  2188. &LogRecordLsn,
  2189. &LogRecordLength,
  2190. (PVOID *)&LogRecord ));
  2191. } finally {
  2192. NtfsUnpinBcb( IrpContext, &PageBcb );
  2193. //
  2194. // Finally we can kill the log handle.
  2195. //
  2196. LfsTerminateLogQuery( LogHandle, LogContext );
  2197. }
  2198. DebugTrace( -1, Dbg, ("RedoPass -> VOID\n") );
  2199. }
  2200. //
  2201. // Internal support routine
  2202. //
  2203. VOID
  2204. UndoPass (
  2205. IN PIRP_CONTEXT IrpContext,
  2206. IN PVCB Vcb
  2207. )
  2208. /*++
  2209. Routine Description:
  2210. This routine performs the Undo Pass of Restart. It does this by scanning
  2211. the Transaction Table produced by the Analysis Pass. For every transaction
  2212. in this table which is in the active state, all of its Undo log records, as
  2213. linked together by the UndoNextLsn, are applied to undo the logged operation.
  2214. Note that all pages at this point should be uptodate with the contents they
  2215. had at about the time of the crash. The dirty page table is not consulted
  2216. during the Undo Pass, all relevant Undo operations are unconditionally
  2217. performed.
  2218. The Undo actions are all performed in the common routine DoAction,
  2219. which is also used by the Redo Pass.
  2220. Arguments:
  2221. Vcb - Volume which is being restarted.
  2222. Return Value:
  2223. None.
  2224. --*/
  2225. {
  2226. PTRANSACTION_ENTRY Transaction;
  2227. POPEN_ATTRIBUTE_ENTRY OpenEntry;
  2228. PRESTART_POINTERS TransactionTable = &Vcb->TransactionTable;
  2229. PAGED_CODE();
  2230. DebugTrace( +1, Dbg, ("UndoPass:\n") );
  2231. //
  2232. // Point to first Transaction Entry.
  2233. //
  2234. Transaction = NtfsGetFirstRestartTable( TransactionTable );
  2235. //
  2236. // Loop to end of table.
  2237. //
  2238. while (Transaction != NULL) {
  2239. if ((Transaction->TransactionState == TransactionActive)
  2240. &&
  2241. (Transaction->UndoNextLsn.QuadPart != 0)) {
  2242. //
  2243. // Abort transaction if it is active and has undo work to do.
  2244. //
  2245. NtfsAbortTransaction( IrpContext, Vcb, Transaction );
  2246. #ifdef BENL_DBG
  2247. {
  2248. PRESTART_LOG UndoLog;
  2249. UndoLog = (PRESTART_LOG) NtfsAllocatePoolNoRaise( NonPagedPool, sizeof( RESTART_LOG ) );
  2250. if (UndoLog) {
  2251. UndoLog->Lsn = Transaction->FirstLsn;
  2252. InsertTailList( &(Vcb->RestartUndoHead), &(UndoLog->Links) );
  2253. } else {
  2254. KdPrint(( "NTFS: out of memory during restart undo\n" ));
  2255. }
  2256. }
  2257. #endif
  2258. //
  2259. // Remove this entry from the transaction table.
  2260. //
  2261. } else {
  2262. TRANSACTION_ID TransactionId = GetIndexFromRestartEntry( &Vcb->TransactionTable,
  2263. Transaction );
  2264. NtfsAcquireExclusiveRestartTable( &Vcb->TransactionTable,
  2265. TRUE );
  2266. NtfsFreeRestartTableIndex( &Vcb->TransactionTable,
  2267. TransactionId );
  2268. NtfsReleaseRestartTable( &Vcb->TransactionTable );
  2269. }
  2270. //
  2271. // Point to next entry in table, or NULL.
  2272. //
  2273. Transaction = NtfsGetNextRestartTable( TransactionTable, Transaction );
  2274. }
  2275. //
  2276. // Now we will flush and purge all the streams to verify that the purges
  2277. // will work.
  2278. //
  2279. OpenEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  2280. //
  2281. // Loop to end of table.
  2282. //
  2283. while (OpenEntry != NULL) {
  2284. IO_STATUS_BLOCK IoStatus;
  2285. PSCB Scb;
  2286. Scb = OpenEntry->OatData->Overlay.Scb;
  2287. //
  2288. // We clean up the Scb only if it exists and this is index in the
  2289. // OpenAttributeTable that this Scb actually refers to.
  2290. // If this Scb has several entries in the table, this check will insure
  2291. // that it only gets cleaned up once.
  2292. //
  2293. if ((Scb != NULL) &&
  2294. (Scb->NonpagedScb->OpenAttributeTableIndex == GetIndexFromRestartEntry( &Vcb->OpenAttributeTable, OpenEntry))) {
  2295. //
  2296. // Now flush the file. It is important to call the
  2297. // same routine the Lazy Writer calls, so that write.c
  2298. // will not decide to update file size for the attribute,
  2299. // since we really are working here with the wrong size.
  2300. //
  2301. // We also now purge all pages, in case we go to update
  2302. // half of a page that was clean and read in as zeros in
  2303. // the Redo Pass.
  2304. //
  2305. NtfsPurgeFileRecordCache( IrpContext );
  2306. NtfsAcquireScbForLazyWrite( (PVOID)Scb, TRUE );
  2307. CcFlushCache( &Scb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
  2308. NtfsReleaseScbFromLazyWrite( (PVOID)Scb );
  2309. NtfsNormalizeAndCleanupTransaction( IrpContext,
  2310. &IoStatus.Status,
  2311. TRUE,
  2312. STATUS_UNEXPECTED_IO_ERROR );
  2313. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject, NULL, 0, FALSE )) {
  2314. KdPrint(("NtfsUndoPass: Unable to purge volume\n"));
  2315. NtfsRaiseStatus( IrpContext, STATUS_INTERNAL_ERROR, NULL, NULL );
  2316. }
  2317. }
  2318. //
  2319. // Point to next entry in table, or NULL.
  2320. //
  2321. OpenEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable,
  2322. OpenEntry );
  2323. }
  2324. DebugTrace( -1, Dbg, ("UndoPass -> VOID\n") );
  2325. }
  2326. //
  2327. // Internal support routine
  2328. //
  2329. //
  2330. // First define some "local" macros for Lsn in page manipulation.
  2331. //
  2332. //
  2333. // Macro to check the Lsn and break (out of the switch statement in DoAction)
  2334. // if the respective redo record need not be applied. Note that if the structure's
  2335. // clusters were deleted, then it will read as all zero's so we also check a field
  2336. // which must be nonzero.
  2337. //
  2338. #define CheckLsn(PAGE) { \
  2339. if (*(PULONG)((PMULTI_SECTOR_HEADER)(PAGE))->Signature == \
  2340. *(PULONG)BaadSignature) { \
  2341. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2342. NtfsUnpinBcb( IrpContext, Bcb ); \
  2343. break; \
  2344. } \
  2345. \
  2346. if (ARGUMENT_PRESENT(RedoLsn) && \
  2347. ((*(PULONG)((PMULTI_SECTOR_HEADER)(PAGE))->Signature == \
  2348. *(PULONG)HoleSignature) || \
  2349. (RedoLsn->QuadPart <= ((PFILE_RECORD_SEGMENT_HEADER)(PAGE))->Lsn.QuadPart))) { \
  2350. /**** xxLeq(*RedoLsn,((PFILE_RECORD_SEGMENT_HEADER)(PAGE))->Lsn) ****/ \
  2351. DebugTrace( 0, Dbg, ("Skipping Page with Lsn: %016I64x\n", \
  2352. ((PFILE_RECORD_SEGMENT_HEADER)(PAGE))->Lsn) ); \
  2353. \
  2354. NtfsUnpinBcb( IrpContext, Bcb ); \
  2355. break; \
  2356. } \
  2357. }
  2358. //
  2359. // Macros for checking File Records and Index Buffers before and after the action
  2360. // routines. The after checks are only for debug. The before check is not
  2361. // always possible.
  2362. //
  2363. #define CheckFileRecordBefore { \
  2364. if (!NtfsCheckFileRecord( Vcb, FileRecord, NULL, &CorruptHint )) { \
  2365. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2366. NtfsUnpinBcb( IrpContext, Bcb ); \
  2367. break; \
  2368. } \
  2369. }
  2370. #define CheckFileRecordAfter { \
  2371. DbgDoit(NtfsCheckFileRecord( Vcb, FileRecord, NULL, &CorruptHint )); \
  2372. }
  2373. #define CheckIndexBufferBefore { \
  2374. if (!NtfsCheckIndexBuffer( Scb, IndexBuffer )) { \
  2375. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2376. NtfsUnpinBcb( IrpContext, Bcb ); \
  2377. break; \
  2378. } \
  2379. }
  2380. #define CheckIndexBufferAfter { \
  2381. DbgDoit(NtfsCheckIndexBuffer( Scb, IndexBuffer )); \
  2382. }
  2383. //
  2384. // Checks if the record offset + length will fit into a file record.
  2385. //
  2386. #define CheckWriteFileRecord { \
  2387. if (LogRecord->RecordOffset + Length > Vcb->BytesPerFileRecordSegment) { \
  2388. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2389. NtfsUnpinBcb( IrpContext, Bcb ); \
  2390. break; \
  2391. } \
  2392. }
  2393. //
  2394. // Checks if the record offset in the log record points to an attribute.
  2395. //
  2396. #define CheckIfAttribute( ENDOK ) { \
  2397. _Length = FileRecord->FirstAttributeOffset; \
  2398. _AttrHeader = Add2Ptr( FileRecord, _Length ); \
  2399. while (_Length < LogRecord->RecordOffset) { \
  2400. if ((_AttrHeader->TypeCode == $END) || \
  2401. (_AttrHeader->RecordLength == 0)) { \
  2402. break; \
  2403. } \
  2404. _Length += _AttrHeader->RecordLength; \
  2405. _AttrHeader = NtfsGetNextRecord( _AttrHeader ); \
  2406. } \
  2407. if ((_Length != LogRecord->RecordOffset) || \
  2408. (!(ENDOK) && (_AttrHeader->TypeCode == $END))) { \
  2409. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2410. NtfsUnpinBcb( IrpContext, Bcb ); \
  2411. break; \
  2412. } \
  2413. }
  2414. //
  2415. // Checks if the attribute described by 'Data' fits within the log record
  2416. // and will fit in the file record.
  2417. //
  2418. #define CheckInsertAttribute { \
  2419. _AttrHeader = (PATTRIBUTE_RECORD_HEADER) Data; \
  2420. if ((Length < (ULONG) SIZEOF_RESIDENT_ATTRIBUTE_HEADER) || \
  2421. (_AttrHeader->RecordLength & 7) || \
  2422. ((ULONG_PTR) Add2Ptr( Data, _AttrHeader->RecordLength ) \
  2423. > (ULONG_PTR) Add2Ptr( LogRecord, LogRecordLength )) || \
  2424. (Length > FileRecord->BytesAvailable - FileRecord->FirstFreeByte)) { \
  2425. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2426. NtfsUnpinBcb( IrpContext, Bcb ); \
  2427. break; \
  2428. } \
  2429. }
  2430. //
  2431. // This checks
  2432. // - the attribute fits if we are growing the attribute
  2433. //
  2434. #define CheckResidentFits { \
  2435. _AttrHeader = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord, LogRecord->RecordOffset ); \
  2436. _Length = LogRecord->AttributeOffset + Length; \
  2437. if ((LogRecord->RedoLength == LogRecord->UndoLength) ? \
  2438. (LogRecord->AttributeOffset + Length > _AttrHeader->RecordLength) : \
  2439. ((_Length > _AttrHeader->RecordLength) && \
  2440. ((_Length - _AttrHeader->RecordLength) > \
  2441. (FileRecord->BytesAvailable - FileRecord->FirstFreeByte)))) { \
  2442. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2443. NtfsUnpinBcb( IrpContext, Bcb ); \
  2444. break; \
  2445. } \
  2446. }
  2447. //
  2448. // This routine checks that the data in this log record will fit into the
  2449. // allocation described in the log record.
  2450. //
  2451. #define CheckNonResidentFits { \
  2452. if (BytesFromClusters( Vcb, LogRecord->LcnsToFollow ) \
  2453. < (BytesFromLogBlocks( LogRecord->ClusterBlockOffset ) + LogRecord->RecordOffset + Length)) { \
  2454. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2455. NtfsUnpinBcb( IrpContext, Bcb ); \
  2456. break; \
  2457. } \
  2458. }
  2459. //
  2460. // This routine checks
  2461. // - the attribute is non-resident.
  2462. // - the data is beyond the mapping pairs offset.
  2463. // - the new data begins within the current size of the attribute.
  2464. // - the new data will fit in the file record.
  2465. //
  2466. #define CheckMappingFits { \
  2467. _AttrHeader = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord, LogRecord->RecordOffset );\
  2468. _Length = LogRecord->AttributeOffset + Length; \
  2469. if ((_AttrHeader->TypeCode == $END) || \
  2470. NtfsIsAttributeResident( _AttrHeader ) || \
  2471. (LogRecord->AttributeOffset < _AttrHeader->Form.Nonresident.MappingPairsOffset) || \
  2472. (LogRecord->AttributeOffset > _AttrHeader->RecordLength) || \
  2473. ((_Length > _AttrHeader->RecordLength) && \
  2474. ((_Length - _AttrHeader->RecordLength) > \
  2475. (FileRecord->BytesAvailable - FileRecord->FirstFreeByte)))) { \
  2476. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2477. NtfsUnpinBcb( IrpContext, Bcb ); \
  2478. break; \
  2479. } \
  2480. }
  2481. //
  2482. // This routine simply checks that the attribute is non-resident.
  2483. //
  2484. #define CheckIfNonResident { \
  2485. if (NtfsIsAttributeResident( (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord, \
  2486. LogRecord->RecordOffset ))) { \
  2487. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2488. NtfsUnpinBcb( IrpContext, Bcb ); \
  2489. break; \
  2490. } \
  2491. }
  2492. //
  2493. // This routine checks if the record offset points to an index_root attribute.
  2494. //
  2495. #define CheckIfIndexRoot { \
  2496. _Length = FileRecord->FirstAttributeOffset; \
  2497. _AttrHeader = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset ); \
  2498. while (_Length < LogRecord->RecordOffset) { \
  2499. if ((_AttrHeader->TypeCode == $END) || \
  2500. (_AttrHeader->RecordLength == 0)) { \
  2501. break; \
  2502. } \
  2503. _Length += _AttrHeader->RecordLength; \
  2504. _AttrHeader = NtfsGetNextRecord( _AttrHeader ); \
  2505. } \
  2506. if ((_Length != LogRecord->RecordOffset) || \
  2507. (_AttrHeader->TypeCode != $INDEX_ROOT)) { \
  2508. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2509. NtfsUnpinBcb( IrpContext, Bcb ); \
  2510. break; \
  2511. } \
  2512. }
  2513. //
  2514. // This routine checks if the attribute offset points to a valid index entry.
  2515. //
  2516. #define CheckIfRootIndexEntry { \
  2517. _Length = PtrOffset( Attribute, IndexHeader ) + \
  2518. IndexHeader->FirstIndexEntry; \
  2519. _CurrentEntry = Add2Ptr( IndexHeader, IndexHeader->FirstIndexEntry ); \
  2520. while (_Length < LogRecord->AttributeOffset) { \
  2521. if ((_Length >= Attribute->RecordLength) || \
  2522. (_CurrentEntry->Length == 0)) { \
  2523. break; \
  2524. } \
  2525. _Length += _CurrentEntry->Length; \
  2526. _CurrentEntry = Add2Ptr( _CurrentEntry, _CurrentEntry->Length ); \
  2527. } \
  2528. if (_Length != LogRecord->AttributeOffset) { \
  2529. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2530. NtfsUnpinBcb( IrpContext, Bcb ); \
  2531. break; \
  2532. } \
  2533. }
  2534. //
  2535. // This routine checks if the attribute offset points to a valid index entry.
  2536. //
  2537. #define CheckIfAllocationIndexEntry { \
  2538. ULONG _AdjustedOffset; \
  2539. _Length = IndexHeader->FirstIndexEntry; \
  2540. _AdjustedOffset = FIELD_OFFSET( INDEX_ALLOCATION_BUFFER, IndexHeader ) \
  2541. + IndexHeader->FirstIndexEntry; \
  2542. _CurrentEntry = Add2Ptr( IndexHeader, IndexHeader->FirstIndexEntry ); \
  2543. while (_AdjustedOffset < LogRecord->AttributeOffset) { \
  2544. if ((_Length >= IndexHeader->FirstFreeByte) || \
  2545. (_CurrentEntry->Length == 0)) { \
  2546. break; \
  2547. } \
  2548. _AdjustedOffset += _CurrentEntry->Length; \
  2549. _Length += _CurrentEntry->Length; \
  2550. _CurrentEntry = Add2Ptr( _CurrentEntry, _CurrentEntry->Length ); \
  2551. } \
  2552. if (_AdjustedOffset != LogRecord->AttributeOffset) { \
  2553. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2554. NtfsUnpinBcb( IrpContext, Bcb ); \
  2555. break; \
  2556. } \
  2557. }
  2558. //
  2559. // This routine checks if we can safely add this index entry.
  2560. // - The index entry must be within the log record
  2561. // - There must be enough space in the attribute to insert this.
  2562. //
  2563. #define CheckIfRootEntryFits { \
  2564. if (((ULONG_PTR) Add2Ptr( Data, IndexEntry->Length ) > (ULONG_PTR) Add2Ptr( LogRecord, LogRecordLength )) || \
  2565. (IndexEntry->Length > FileRecord->BytesAvailable - FileRecord->FirstFreeByte)) { \
  2566. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2567. NtfsUnpinBcb( IrpContext, Bcb ); \
  2568. break; \
  2569. } \
  2570. }
  2571. //
  2572. // This routine checks that we can safely add this index entry.
  2573. // - The entry must be contained in a log record.
  2574. // - The entry must fit in the index buffer.
  2575. //
  2576. #define CheckIfAllocationEntryFits { \
  2577. if (((ULONG_PTR) Add2Ptr( Data, IndexEntry->Length ) > \
  2578. (ULONG_PTR) Add2Ptr( LogRecord, LogRecordLength )) || \
  2579. (IndexEntry->Length > IndexHeader->BytesAvailable - IndexHeader->FirstFreeByte)) { \
  2580. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2581. NtfsUnpinBcb( IrpContext, Bcb ); \
  2582. break; \
  2583. } \
  2584. }
  2585. //
  2586. // This routine will check that the data will fit in the tail of an index buffer.
  2587. //
  2588. #define CheckWriteIndexBuffer { \
  2589. if (LogRecord->AttributeOffset + Length > \
  2590. (FIELD_OFFSET( INDEX_ALLOCATION_BUFFER, IndexHeader ) + \
  2591. IndexHeader->BytesAvailable)) { \
  2592. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2593. NtfsUnpinBcb( IrpContext, Bcb ); \
  2594. break; \
  2595. } \
  2596. }
  2597. //
  2598. // This routine verifies that the bitmap bits are contained in the Lcns described.
  2599. //
  2600. #define CheckBitmapRange { \
  2601. if ((BytesFromLogBlocks( LogRecord->ClusterBlockOffset ) + \
  2602. ((BitMapRange->BitMapOffset + BitMapRange->NumberOfBits + 7) / 8)) > \
  2603. BytesFromClusters( Vcb, LogRecord->LcnsToFollow )) { \
  2604. NtfsMarkVolumeDirty( IrpContext, Vcb ); \
  2605. NtfsUnpinBcb( IrpContext, Bcb ); \
  2606. break; \
  2607. } \
  2608. }
  2609. VOID
  2610. DoAction (
  2611. IN PIRP_CONTEXT IrpContext,
  2612. IN PVCB Vcb,
  2613. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  2614. IN NTFS_LOG_OPERATION Operation,
  2615. IN PVOID Data,
  2616. IN ULONG Length,
  2617. IN ULONG LogRecordLength,
  2618. IN PLSN RedoLsn OPTIONAL,
  2619. IN PSCB Scb OPTIONAL,
  2620. OUT PBCB *Bcb,
  2621. OUT PLSN *PageLsn
  2622. )
  2623. /*++
  2624. Routine Description:
  2625. This routine is a common routine for the Redo and Undo Passes, for performing
  2626. the respective redo and undo operations. All Redo- and Undo-specific
  2627. processing is performed in RedoPass or UndoPass; in this routine all actions
  2628. are treated identically, regardless of whether the action is undo or redo.
  2629. Note that most actions are possible for both redo and undo, although some
  2630. are only used for one or the other.
  2631. Basically this routine is just a big switch statement dispatching on operation
  2632. code. The parameter descriptions provide some insight on how some of the
  2633. parameters must be initialized differently for redo or undo.
  2634. Arguments:
  2635. Vcb - Vcb for the volume being restarted.
  2636. LogRecord - Pointer to the log record from which Redo or Undo is being executed.
  2637. Only the common fields are accessed.
  2638. Operation - The Redo or Undo operation to be performed.
  2639. Data - Pointer to the Redo or Undo buffer, depending on the caller.
  2640. Length - Length of the Redo or Undo buffer.
  2641. LogRecordLength - Length of the entire log record.
  2642. RedoLsn - For Redo this must be the Lsn of the Log Record for which the
  2643. redo is being applied. Must be NULL for transaction abort/undo.
  2644. Scb - If specified this is the Scb for the stream to which this log record
  2645. applies. We have already looked this up (with proper synchronization) in
  2646. the abort path.
  2647. Bcb - Returns the Bcb of the page to which the action was performed, or NULL.
  2648. PageLsn - Returns a pointer to where a new Lsn may be stored, or NULL.
  2649. Return Value:
  2650. None.
  2651. --*/
  2652. {
  2653. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  2654. PATTRIBUTE_RECORD_HEADER Attribute;
  2655. PINDEX_HEADER IndexHeader;
  2656. PINDEX_ALLOCATION_BUFFER IndexBuffer;
  2657. PINDEX_ENTRY IndexEntry;
  2658. //
  2659. // The following are used in the Check macros
  2660. //
  2661. PATTRIBUTE_RECORD_HEADER _AttrHeader;
  2662. PINDEX_ENTRY _CurrentEntry;
  2663. ULONG _Length;
  2664. ULONG CorruptHint;
  2665. PAGED_CODE();
  2666. DebugTrace( +1, Dbg, ("DoAction:\n") );
  2667. DebugTrace( 0, Dbg, ("Operation = %08lx\n", Operation) );
  2668. DebugTrace( 0, Dbg, ("Data = %08lx\n", Data) );
  2669. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  2670. //
  2671. // Initially clear outputs.
  2672. //
  2673. *Bcb = NULL;
  2674. *PageLsn = NULL;
  2675. //
  2676. // Dispatch to handle log record depending on type.
  2677. //
  2678. switch (Operation) {
  2679. //
  2680. // To initialize a file record segment, we simply do a prepare write and copy the
  2681. // file record in.
  2682. //
  2683. case InitializeFileRecordSegment:
  2684. //
  2685. // Check the log record and that the data is a valid file record.
  2686. //
  2687. CheckWriteFileRecord;
  2688. //
  2689. // Pin the desired Mft record.
  2690. //
  2691. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2692. *PageLsn = &FileRecord->Lsn;
  2693. RtlCopyMemory( FileRecord, Data, Length );
  2694. break;
  2695. //
  2696. // To deallocate a file record segment, we do a prepare write (no need to read it
  2697. // to deallocate it), and clear FILE_RECORD_SEGMENT_IN_USE.
  2698. //
  2699. case DeallocateFileRecordSegment:
  2700. //
  2701. // Pin the desired Mft record.
  2702. //
  2703. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2704. *PageLsn = &FileRecord->Lsn;
  2705. ASSERT( FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) ||
  2706. FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE ) );
  2707. ClearFlag(FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE);
  2708. FileRecord->SequenceNumber += 1;
  2709. break;
  2710. //
  2711. // To write the end of a file record segment, we calculate a pointer to the
  2712. // destination position (OldAttribute), and then call the routine to take
  2713. // care of it.
  2714. //
  2715. case WriteEndOfFileRecordSegment:
  2716. //
  2717. // Pin the desired Mft record.
  2718. //
  2719. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2720. CheckLsn( FileRecord );
  2721. CheckFileRecordBefore;
  2722. CheckIfAttribute( TRUE );
  2723. CheckWriteFileRecord;
  2724. *PageLsn = &FileRecord->Lsn;
  2725. Attribute = Add2Ptr( FileRecord, LogRecord->RecordOffset );
  2726. NtfsRestartWriteEndOfFileRecord( FileRecord,
  2727. Attribute,
  2728. (PATTRIBUTE_RECORD_HEADER)Data,
  2729. Length );
  2730. CheckFileRecordAfter;
  2731. break;
  2732. //
  2733. // For Create Attribute, we read in the designated Mft record, and
  2734. // insert the attribute record from the log record.
  2735. //
  2736. case CreateAttribute:
  2737. //
  2738. // Pin the desired Mft record.
  2739. //
  2740. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2741. CheckLsn( FileRecord );
  2742. CheckFileRecordBefore;
  2743. CheckIfAttribute( TRUE );
  2744. CheckInsertAttribute;
  2745. *PageLsn = &FileRecord->Lsn;
  2746. NtfsRestartInsertAttribute( IrpContext,
  2747. FileRecord,
  2748. LogRecord->RecordOffset,
  2749. (PATTRIBUTE_RECORD_HEADER)Data,
  2750. NULL,
  2751. NULL,
  2752. 0 );
  2753. CheckFileRecordAfter;
  2754. break;
  2755. //
  2756. // To Delete an attribute, we read the designated Mft record and make
  2757. // a call to remove the attribute record.
  2758. //
  2759. case DeleteAttribute:
  2760. //
  2761. // Pin the desired Mft record.
  2762. //
  2763. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2764. CheckLsn( FileRecord );
  2765. CheckFileRecordBefore;
  2766. CheckIfAttribute( FALSE );
  2767. *PageLsn = &FileRecord->Lsn;
  2768. NtfsRestartRemoveAttribute( IrpContext,
  2769. FileRecord,
  2770. LogRecord->RecordOffset );
  2771. CheckFileRecordAfter;
  2772. break;
  2773. //
  2774. // To update a resident attribute, we read the designated Mft record and
  2775. // call the routine to change its value.
  2776. //
  2777. case UpdateResidentValue:
  2778. //
  2779. // Pin the desired Mft record.
  2780. //
  2781. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2782. CheckLsn( FileRecord );
  2783. CheckFileRecordBefore;
  2784. CheckIfAttribute( FALSE );
  2785. CheckResidentFits;
  2786. *PageLsn = &FileRecord->Lsn;
  2787. NtfsRestartChangeValue( IrpContext,
  2788. FileRecord,
  2789. LogRecord->RecordOffset,
  2790. LogRecord->AttributeOffset,
  2791. Data,
  2792. Length,
  2793. (BOOLEAN)((LogRecord->RedoLength !=
  2794. LogRecord->UndoLength) ?
  2795. TRUE : FALSE) );
  2796. CheckFileRecordAfter;
  2797. break;
  2798. //
  2799. // To update a nonresident value, we simply pin the attribute and copy
  2800. // the data in. Log record will limit us to a page at a time.
  2801. //
  2802. case UpdateNonresidentValue:
  2803. {
  2804. PVOID Buffer;
  2805. //
  2806. // Pin the desired index buffer, and check the Lsn.
  2807. //
  2808. ASSERT( Length <= PAGE_SIZE );
  2809. PinAttributeForRestart( IrpContext,
  2810. Vcb,
  2811. LogRecord,
  2812. Length,
  2813. Bcb,
  2814. &Buffer,
  2815. &Scb );
  2816. CheckNonResidentFits;
  2817. RtlCopyMemory( (PCHAR)Buffer + LogRecord->RecordOffset, Data, Length );
  2818. break;
  2819. }
  2820. //
  2821. // To update the mapping pairs in a nonresident attribute, we read the
  2822. // designated Mft record and call the routine to change them.
  2823. //
  2824. case UpdateMappingPairs:
  2825. //
  2826. // Pin the desired Mft record.
  2827. //
  2828. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2829. CheckLsn( FileRecord );
  2830. CheckFileRecordBefore;
  2831. CheckIfAttribute( FALSE );
  2832. CheckMappingFits;
  2833. *PageLsn = &FileRecord->Lsn;
  2834. NtfsRestartChangeMapping( IrpContext,
  2835. Vcb,
  2836. FileRecord,
  2837. LogRecord->RecordOffset,
  2838. LogRecord->AttributeOffset,
  2839. Data,
  2840. Length );
  2841. CheckFileRecordAfter;
  2842. break;
  2843. //
  2844. // To set new attribute sizes, we read the designated Mft record, point
  2845. // to the attribute, and copy in the new sizes.
  2846. //
  2847. case SetNewAttributeSizes:
  2848. {
  2849. PNEW_ATTRIBUTE_SIZES Sizes;
  2850. //
  2851. // Pin the desired Mft record.
  2852. //
  2853. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2854. CheckLsn( FileRecord );
  2855. CheckFileRecordBefore;
  2856. CheckIfAttribute( FALSE );
  2857. CheckIfNonResident;
  2858. *PageLsn = &FileRecord->Lsn;
  2859. Sizes = (PNEW_ATTRIBUTE_SIZES)Data;
  2860. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord +
  2861. LogRecord->RecordOffset);
  2862. NtfsVerifySizesLongLong( Sizes );
  2863. Attribute->Form.Nonresident.AllocatedLength = Sizes->AllocationSize;
  2864. Attribute->Form.Nonresident.FileSize = Sizes->FileSize;
  2865. Attribute->Form.Nonresident.ValidDataLength = Sizes->ValidDataLength;
  2866. if (Length >= SIZEOF_FULL_ATTRIBUTE_SIZES) {
  2867. Attribute->Form.Nonresident.TotalAllocated = Sizes->TotalAllocated;
  2868. }
  2869. CheckFileRecordAfter;
  2870. break;
  2871. }
  2872. //
  2873. // To insert a new index entry in the root, we read the designated Mft
  2874. // record, point to the attribute and the insertion point, and call the
  2875. // same routine used in normal operation.
  2876. //
  2877. case AddIndexEntryRoot:
  2878. //
  2879. // Pin the desired Mft record.
  2880. //
  2881. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2882. CheckLsn( FileRecord );
  2883. CheckFileRecordBefore;
  2884. CheckIfIndexRoot;
  2885. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord +
  2886. LogRecord->RecordOffset);
  2887. IndexEntry = (PINDEX_ENTRY)Data;
  2888. IndexHeader = &((PINDEX_ROOT) NtfsAttributeValue( Attribute ))->IndexHeader;
  2889. CheckIfRootIndexEntry;
  2890. CheckIfRootEntryFits;
  2891. *PageLsn = &FileRecord->Lsn;
  2892. NtfsRestartInsertSimpleRoot( IrpContext,
  2893. IndexEntry,
  2894. FileRecord,
  2895. Attribute,
  2896. Add2Ptr( Attribute, LogRecord->AttributeOffset ));
  2897. CheckFileRecordAfter;
  2898. break;
  2899. //
  2900. // To insert a new index entry in the root, we read the designated Mft
  2901. // record, point to the attribute and the insertion point, and call the
  2902. // same routine used in normal operation.
  2903. //
  2904. case DeleteIndexEntryRoot:
  2905. //
  2906. // Pin the desired Mft record.
  2907. //
  2908. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2909. CheckLsn( FileRecord );
  2910. CheckFileRecordBefore;
  2911. CheckIfIndexRoot;
  2912. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord +
  2913. LogRecord->RecordOffset);
  2914. IndexHeader = &((PINDEX_ROOT) NtfsAttributeValue( Attribute ))->IndexHeader;
  2915. CheckIfRootIndexEntry;
  2916. *PageLsn = &FileRecord->Lsn;
  2917. IndexEntry = (PINDEX_ENTRY) Add2Ptr( Attribute,
  2918. LogRecord->AttributeOffset);
  2919. NtfsRestartDeleteSimpleRoot( IrpContext,
  2920. IndexEntry,
  2921. FileRecord,
  2922. Attribute );
  2923. CheckFileRecordAfter;
  2924. break;
  2925. //
  2926. // To insert a new index entry in the allocation, we read the designated index
  2927. // buffer, point to the insertion point, and call the same routine used in
  2928. // normal operation.
  2929. //
  2930. case AddIndexEntryAllocation:
  2931. //
  2932. // Pin the desired index buffer, and check the Lsn.
  2933. //
  2934. ASSERT( Length <= PAGE_SIZE );
  2935. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  2936. CheckLsn( IndexBuffer );
  2937. CheckIndexBufferBefore;
  2938. IndexEntry = (PINDEX_ENTRY)Data;
  2939. IndexHeader = &IndexBuffer->IndexHeader;
  2940. CheckIfAllocationIndexEntry;
  2941. CheckIfAllocationEntryFits;
  2942. *PageLsn = &IndexBuffer->Lsn;
  2943. NtfsRestartInsertSimpleAllocation( IndexEntry,
  2944. IndexBuffer,
  2945. Add2Ptr( IndexBuffer, LogRecord->AttributeOffset ));
  2946. CheckIndexBufferAfter;
  2947. break;
  2948. //
  2949. // To delete an index entry in the allocation, we read the designated index
  2950. // buffer, point to the deletion point, and call the same routine used in
  2951. // normal operation.
  2952. //
  2953. case DeleteIndexEntryAllocation:
  2954. //
  2955. // Pin the desired index buffer, and check the Lsn.
  2956. //
  2957. ASSERT( Length <= PAGE_SIZE );
  2958. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  2959. CheckLsn( IndexBuffer );
  2960. CheckIndexBufferBefore;
  2961. IndexHeader = &IndexBuffer->IndexHeader;
  2962. CheckIfAllocationIndexEntry;
  2963. IndexEntry = (PINDEX_ENTRY)((PCHAR)IndexBuffer + LogRecord->AttributeOffset);
  2964. ASSERT( (0 == Length) || (Length == IndexEntry->Length) );
  2965. ASSERT( (0 == Length) || (0 == RtlCompareMemory( IndexEntry, Data, Length)) );
  2966. *PageLsn = &IndexBuffer->Lsn;
  2967. NtfsRestartDeleteSimpleAllocation( IndexEntry, IndexBuffer );
  2968. CheckIndexBufferAfter;
  2969. break;
  2970. case WriteEndOfIndexBuffer:
  2971. //
  2972. // Pin the desired index buffer, and check the Lsn.
  2973. //
  2974. ASSERT( Length <= PAGE_SIZE );
  2975. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  2976. CheckLsn( IndexBuffer );
  2977. CheckIndexBufferBefore;
  2978. IndexHeader = &IndexBuffer->IndexHeader;
  2979. CheckIfAllocationIndexEntry;
  2980. CheckWriteIndexBuffer;
  2981. *PageLsn = &IndexBuffer->Lsn;
  2982. IndexEntry = (PINDEX_ENTRY)((PCHAR)IndexBuffer + LogRecord->AttributeOffset);
  2983. NtfsRestartWriteEndOfIndex( IndexHeader,
  2984. IndexEntry,
  2985. (PINDEX_ENTRY)Data,
  2986. Length );
  2987. CheckIndexBufferAfter;
  2988. break;
  2989. //
  2990. // To set a new index entry Vcn in the root, we read the designated Mft
  2991. // record, point to the attribute and the index entry, and call the
  2992. // same routine used in normal operation.
  2993. //
  2994. case SetIndexEntryVcnRoot:
  2995. //
  2996. // Pin the desired Mft record.
  2997. //
  2998. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  2999. CheckLsn( FileRecord );
  3000. CheckFileRecordBefore;
  3001. CheckIfIndexRoot;
  3002. Attribute = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord, LogRecord->RecordOffset );
  3003. IndexHeader = &((PINDEX_ROOT) NtfsAttributeValue( Attribute ))->IndexHeader;
  3004. CheckIfRootIndexEntry;
  3005. *PageLsn = &FileRecord->Lsn;
  3006. IndexEntry = (PINDEX_ENTRY)((PCHAR)Attribute +
  3007. LogRecord->AttributeOffset);
  3008. NtfsRestartSetIndexBlock( IndexEntry,
  3009. *((PLONGLONG) Data) );
  3010. CheckFileRecordAfter;
  3011. break;
  3012. //
  3013. // To set a new index entry Vcn in the allocation, we read the designated index
  3014. // buffer, point to the index entry, and call the same routine used in
  3015. // normal operation.
  3016. //
  3017. case SetIndexEntryVcnAllocation:
  3018. //
  3019. // Pin the desired index buffer, and check the Lsn.
  3020. //
  3021. ASSERT( Length <= PAGE_SIZE );
  3022. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  3023. CheckLsn( IndexBuffer );
  3024. CheckIndexBufferBefore;
  3025. IndexHeader = &IndexBuffer->IndexHeader;
  3026. CheckIfAllocationIndexEntry;
  3027. *PageLsn = &IndexBuffer->Lsn;
  3028. IndexEntry = (PINDEX_ENTRY) Add2Ptr( IndexBuffer, LogRecord->AttributeOffset );
  3029. NtfsRestartSetIndexBlock( IndexEntry,
  3030. *((PLONGLONG) Data) );
  3031. CheckIndexBufferAfter;
  3032. break;
  3033. //
  3034. // To update a file name in the root, we read the designated Mft
  3035. // record, point to the attribute and the index entry, and call the
  3036. // same routine used in normal operation.
  3037. //
  3038. case UpdateFileNameRoot:
  3039. //
  3040. // Pin the desired Mft record.
  3041. //
  3042. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  3043. CheckLsn( FileRecord );
  3044. CheckFileRecordBefore;
  3045. CheckIfIndexRoot;
  3046. Attribute = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord, LogRecord->RecordOffset );
  3047. IndexHeader = &((PINDEX_ROOT) NtfsAttributeValue( Attribute ))->IndexHeader;
  3048. CheckIfRootIndexEntry;
  3049. IndexEntry = (PINDEX_ENTRY) Add2Ptr( Attribute, LogRecord->AttributeOffset );
  3050. NtfsRestartUpdateFileName( IndexEntry,
  3051. (PDUPLICATED_INFORMATION) Data );
  3052. CheckFileRecordAfter;
  3053. break;
  3054. //
  3055. // To update a file name in the allocation, we read the designated index
  3056. // buffer, point to the index entry, and call the same routine used in
  3057. // normal operation.
  3058. //
  3059. case UpdateFileNameAllocation:
  3060. //
  3061. // Pin the desired index buffer, and check the Lsn.
  3062. //
  3063. ASSERT( Length <= PAGE_SIZE );
  3064. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  3065. CheckLsn( IndexBuffer );
  3066. CheckIndexBufferBefore;
  3067. IndexHeader = &IndexBuffer->IndexHeader;
  3068. CheckIfAllocationIndexEntry;
  3069. IndexEntry = (PINDEX_ENTRY) Add2Ptr( IndexBuffer, LogRecord->AttributeOffset );
  3070. NtfsRestartUpdateFileName( IndexEntry,
  3071. (PDUPLICATED_INFORMATION) Data );
  3072. CheckIndexBufferAfter;
  3073. break;
  3074. //
  3075. // To set a range of bits in the volume bitmap, we just read in the a hunk
  3076. // of the bitmap as described by the log record, and then call the restart
  3077. // routine to do it.
  3078. //
  3079. case SetBitsInNonresidentBitMap:
  3080. {
  3081. PBITMAP_RANGE BitMapRange;
  3082. PVOID BitMapBuffer;
  3083. ULONG BitMapSize;
  3084. RTL_BITMAP Bitmap;
  3085. //
  3086. // Open the attribute first to get the Scb.
  3087. //
  3088. OpenAttributeForRestart( IrpContext, Vcb, LogRecord, &Scb );
  3089. //
  3090. // Pin the desired bitmap buffer.
  3091. //
  3092. ASSERT( Length <= PAGE_SIZE );
  3093. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, &BitMapBuffer, &Scb );
  3094. BitMapRange = (PBITMAP_RANGE)Data;
  3095. CheckBitmapRange;
  3096. //
  3097. // Initialize our bitmap description, and call the restart
  3098. // routine with the bitmap Scb exclusive (assuming it cannot
  3099. // raise).
  3100. //
  3101. BitMapSize = BytesFromClusters( Vcb, LogRecord->LcnsToFollow ) * 8;
  3102. RtlInitializeBitMap( &Bitmap, BitMapBuffer, BitMapSize );
  3103. NtfsRestartSetBitsInBitMap( IrpContext,
  3104. &Bitmap,
  3105. BitMapRange->BitMapOffset,
  3106. BitMapRange->NumberOfBits );
  3107. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  3108. (Scb == Vcb->BitmapScb)) {
  3109. ULONGLONG ThisLcn;
  3110. LONGLONG FoundLcn;
  3111. LONGLONG FoundClusters;
  3112. BOOLEAN FoundMatch = FALSE;
  3113. PDEALLOCATED_CLUSTERS Clusters;
  3114. ThisLcn = (ULONGLONG) ((BytesFromClusters( Vcb, LogRecord->TargetVcn ) + BytesFromLogBlocks( LogRecord->ClusterBlockOffset )) * 8);
  3115. ThisLcn += BitMapRange->BitMapOffset;
  3116. //
  3117. // Best odds are that these are in the active deallocated clusters.
  3118. //
  3119. Clusters = (PDEALLOCATED_CLUSTERS)Vcb->DeallocatedClusterListHead.Flink;
  3120. do {
  3121. if (FsRtlLookupLargeMcbEntry( &Clusters->Mcb,
  3122. ThisLcn,
  3123. &FoundLcn,
  3124. &FoundClusters,
  3125. NULL,
  3126. NULL,
  3127. NULL ) &&
  3128. (FoundLcn != UNUSED_LCN)) {
  3129. ASSERT( FoundClusters >= BitMapRange->NumberOfBits );
  3130. FsRtlRemoveLargeMcbEntry( &Clusters->Mcb,
  3131. ThisLcn,
  3132. BitMapRange->NumberOfBits );
  3133. //
  3134. // Assume again that we will always be able to remove
  3135. // the entries. Even if we don't it just means that it won't be
  3136. // available to allocate this cluster. The counts should be in-sync
  3137. // since they are changed together.
  3138. //
  3139. Clusters->ClusterCount -= BitMapRange->NumberOfBits;
  3140. Vcb->DeallocatedClusters -= BitMapRange->NumberOfBits;
  3141. FoundMatch = TRUE;
  3142. break;
  3143. }
  3144. Clusters = (PDEALLOCATED_CLUSTERS)Clusters->Link.Flink;
  3145. } while ( &Clusters->Link != &Vcb->DeallocatedClusterListHead );
  3146. }
  3147. #ifdef NTFS_CHECK_BITMAP
  3148. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  3149. (Scb == Vcb->BitmapScb) &&
  3150. (Vcb->BitmapCopy != NULL)) {
  3151. ULONG BitmapOffset;
  3152. ULONG BitmapPage;
  3153. ULONG StartBit;
  3154. BitmapOffset = (BytesFromClusters( Vcb, LogRecord->TargetVcn ) + BytesFromLogBlocks( LogRecord->ClusterBlockOffset )) * 8;
  3155. BitmapPage = (BitmapOffset + BitMapRange->BitMapOffset) / (PAGE_SIZE * 8);
  3156. StartBit = (BitmapOffset + BitMapRange->BitMapOffset) & ((PAGE_SIZE * 8) - 1);
  3157. RtlSetBits( Vcb->BitmapCopy + BitmapPage, StartBit, BitMapRange->NumberOfBits );
  3158. }
  3159. #endif
  3160. break;
  3161. }
  3162. //
  3163. // To clear a range of bits in the volume bitmap, we just read in the a hunk
  3164. // of the bitmap as described by the log record, and then call the restart
  3165. // routine to do it.
  3166. //
  3167. case ClearBitsInNonresidentBitMap:
  3168. {
  3169. PBITMAP_RANGE BitMapRange;
  3170. PVOID BitMapBuffer;
  3171. ULONG BitMapSize;
  3172. RTL_BITMAP Bitmap;
  3173. //
  3174. // Open the attribute first to get the Scb.
  3175. //
  3176. OpenAttributeForRestart( IrpContext, Vcb, LogRecord, &Scb );
  3177. //
  3178. // Pin the desired bitmap buffer.
  3179. //
  3180. ASSERT( Length <= PAGE_SIZE );
  3181. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, &BitMapBuffer, &Scb );
  3182. BitMapRange = (PBITMAP_RANGE)Data;
  3183. CheckBitmapRange;
  3184. BitMapSize = BytesFromClusters( Vcb, LogRecord->LcnsToFollow ) * 8;
  3185. //
  3186. // Initialize our bitmap description, and call the restart
  3187. // routine with the bitmap Scb exclusive (assuming it cannot
  3188. // raise).
  3189. //
  3190. RtlInitializeBitMap( &Bitmap, BitMapBuffer, BitMapSize );
  3191. NtfsRestartClearBitsInBitMap( IrpContext,
  3192. &Bitmap,
  3193. BitMapRange->BitMapOffset,
  3194. BitMapRange->NumberOfBits );
  3195. //
  3196. // Look and see if we can return these to the free cluster Mcb.
  3197. //
  3198. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  3199. (Scb == Vcb->BitmapScb)) {
  3200. ULONGLONG ThisLcn;
  3201. ThisLcn = (ULONGLONG) ((BytesFromClusters( Vcb, LogRecord->TargetVcn ) + BytesFromLogBlocks( LogRecord->ClusterBlockOffset )) * 8);
  3202. ThisLcn += BitMapRange->BitMapOffset;
  3203. //
  3204. // Use a try-finally to protect against failures.
  3205. //
  3206. try {
  3207. NtfsAddCachedRun( IrpContext,
  3208. IrpContext->Vcb,
  3209. ThisLcn,
  3210. BitMapRange->NumberOfBits,
  3211. RunStateFree );
  3212. } except( (GetExceptionCode() == STATUS_INSUFFICIENT_RESOURCES) ?
  3213. EXCEPTION_EXECUTE_HANDLER :
  3214. EXCEPTION_CONTINUE_SEARCH ) {
  3215. NtfsMinimumExceptionProcessing( IrpContext );
  3216. }
  3217. }
  3218. #ifdef NTFS_CHECK_BITMAP
  3219. if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  3220. (Scb == Vcb->BitmapScb) &&
  3221. (Vcb->BitmapCopy != NULL)) {
  3222. ULONG BitmapOffset;
  3223. ULONG BitmapPage;
  3224. ULONG StartBit;
  3225. BitmapOffset = (BytesFromClusters( Vcb, LogRecord->TargetVcn ) + BytesFromLogBlocks( LogRecord->ClusterBlockOffset )) * 8;
  3226. BitmapPage = (BitmapOffset + BitMapRange->BitMapOffset) / (PAGE_SIZE * 8);
  3227. StartBit = (BitmapOffset + BitMapRange->BitMapOffset) & ((PAGE_SIZE * 8) - 1);
  3228. RtlClearBits( Vcb->BitmapCopy + BitmapPage, StartBit, BitMapRange->NumberOfBits );
  3229. }
  3230. #endif
  3231. break;
  3232. }
  3233. //
  3234. // To update a file name in the root, we read the designated Mft
  3235. // record, point to the attribute and the index entry, and call the
  3236. // same routine used in normal operation.
  3237. //
  3238. case UpdateRecordDataRoot:
  3239. //
  3240. // Pin the desired Mft record.
  3241. //
  3242. PinMftRecordForRestart( IrpContext, Vcb, LogRecord, Bcb, &FileRecord );
  3243. CheckLsn( FileRecord );
  3244. CheckFileRecordBefore;
  3245. CheckIfIndexRoot;
  3246. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord +
  3247. LogRecord->RecordOffset);
  3248. IndexHeader = &((PINDEX_ROOT) NtfsAttributeValue( Attribute ))->IndexHeader;
  3249. CheckIfRootIndexEntry;
  3250. IndexEntry = (PINDEX_ENTRY)((PCHAR)Attribute +
  3251. LogRecord->AttributeOffset);
  3252. NtOfsRestartUpdateDataInIndex( IndexEntry, Data, Length );
  3253. CheckFileRecordAfter;
  3254. break;
  3255. //
  3256. // To update a file name in the allocation, we read the designated index
  3257. // buffer, point to the index entry, and call the same routine used in
  3258. // normal operation.
  3259. //
  3260. case UpdateRecordDataAllocation:
  3261. //
  3262. // Pin the desired index buffer, and check the Lsn.
  3263. //
  3264. ASSERT( Length <= PAGE_SIZE );
  3265. PinAttributeForRestart( IrpContext, Vcb, LogRecord, 0, Bcb, (PVOID *)&IndexBuffer, &Scb );
  3266. CheckLsn( IndexBuffer );
  3267. CheckIndexBufferBefore;
  3268. IndexHeader = &IndexBuffer->IndexHeader;
  3269. CheckIfAllocationIndexEntry;
  3270. IndexEntry = (PINDEX_ENTRY)((PCHAR)IndexBuffer +
  3271. LogRecord->AttributeOffset);
  3272. NtOfsRestartUpdateDataInIndex( IndexEntry, Data, Length );
  3273. CheckIndexBufferAfter;
  3274. break;
  3275. //
  3276. // The following cases require no action during the Redo or Undo Pass.
  3277. //
  3278. case Noop:
  3279. case DeleteDirtyClusters:
  3280. case HotFix:
  3281. case EndTopLevelAction:
  3282. case PrepareTransaction:
  3283. case CommitTransaction:
  3284. case ForgetTransaction:
  3285. case CompensationLogRecord:
  3286. case OpenNonresidentAttribute:
  3287. case OpenAttributeTableDump:
  3288. case AttributeNamesDump:
  3289. case DirtyPageTableDump:
  3290. case TransactionTableDump:
  3291. break;
  3292. //
  3293. // All codes will be explicitly handled. If we see a code we
  3294. // do not expect, then we are in trouble.
  3295. //
  3296. default:
  3297. DebugTrace( 0, Dbg, ("Record address: %08lx\n", LogRecord) );
  3298. DebugTrace( 0, Dbg, ("Redo operation is: %04lx\n", LogRecord->RedoOperation) );
  3299. DebugTrace( 0, Dbg, ("Undo operation is: %04lx\n", LogRecord->RedoOperation) );
  3300. ASSERTMSG( "Unknown Action!\n", FALSE );
  3301. break;
  3302. }
  3303. DebugDoit(
  3304. if (*Bcb != NULL) {
  3305. DebugTrace( 0, Dbg, ("**** Update applied\n") );
  3306. }
  3307. );
  3308. DebugTrace( 0, Dbg, ("Bcb > %08lx\n", *Bcb) );
  3309. DebugTrace( 0, Dbg, ("PageLsn > %08lx\n", *PageLsn) );
  3310. DebugTrace( -1, Dbg, ("DoAction -> VOID\n") );
  3311. }
  3312. //
  3313. // Internal support routine
  3314. //
  3315. VOID
  3316. PinMftRecordForRestart (
  3317. IN PIRP_CONTEXT IrpContext,
  3318. IN PVCB Vcb,
  3319. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  3320. OUT PBCB *Bcb,
  3321. OUT PFILE_RECORD_SEGMENT_HEADER *FileRecord
  3322. )
  3323. /*++
  3324. Routine Description:
  3325. This routine pins a record in the Mft for restart, as described
  3326. by the current log record.
  3327. Arguments:
  3328. Vcb - Supplies the Vcb pointer for the volume
  3329. LogRecord - Supplies the pointer to the current log record.
  3330. Bcb - Returns a pointer to the Bcb for the pinned record.
  3331. FileRecord - Returns a pointer to the desired file record.
  3332. Return Value:
  3333. None
  3334. --*/
  3335. {
  3336. LONGLONG SegmentReference;
  3337. PAGED_CODE();
  3338. //
  3339. // Calculate the file number part of the segment reference. Do this
  3340. // by obtaining the file offset of the file record and then convert to
  3341. // a file number.
  3342. //
  3343. SegmentReference = LlBytesFromClusters( Vcb, LogRecord->TargetVcn );
  3344. SegmentReference += BytesFromLogBlocks( LogRecord->ClusterBlockOffset );
  3345. SegmentReference = LlFileRecordsFromBytes( Vcb, SegmentReference );
  3346. //
  3347. // Pin the Mft record.
  3348. //
  3349. NtfsPinMftRecord( IrpContext,
  3350. Vcb,
  3351. (PMFT_SEGMENT_REFERENCE)&SegmentReference,
  3352. TRUE,
  3353. Bcb,
  3354. FileRecord,
  3355. NULL );
  3356. ASSERT( (*FileRecord)->MultiSectorHeader.Signature != BaadSignature );
  3357. }
  3358. //
  3359. // Internal support routine
  3360. //
  3361. VOID
  3362. OpenAttributeForRestart (
  3363. IN PIRP_CONTEXT IrpContext,
  3364. IN PVCB Vcb,
  3365. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  3366. IN OUT PSCB *Scb
  3367. )
  3368. /*++
  3369. Routine Description:
  3370. This routine opens the desired attribute for restart, as described
  3371. by the current log record.
  3372. Arguments:
  3373. Vcb - Supplies the Vcb pointer for the volume
  3374. LogRecord - Supplies the pointer to the current log record.
  3375. Scb - On input points to an optional Scb. On return it points to
  3376. the Scb for the log record. It is either the input Scb if specified
  3377. or the Scb for the attribute entry.
  3378. Return Value:
  3379. None
  3380. --*/
  3381. {
  3382. POPEN_ATTRIBUTE_ENTRY AttributeEntry;
  3383. PAGED_CODE();
  3384. //
  3385. // Get a pointer to the attribute entry for the described attribute.
  3386. //
  3387. if (*Scb == NULL) {
  3388. AttributeEntry = GetRestartEntryFromIndex( Vcb->OnDiskOat, LogRecord->TargetAttribute );
  3389. //
  3390. // Check if want to go to the other table.
  3391. //
  3392. if (Vcb->RestartVersion == 0) {
  3393. AttributeEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  3394. ((POPEN_ATTRIBUTE_ENTRY_V0) AttributeEntry)->OatIndex );
  3395. }
  3396. *Scb = AttributeEntry->OatData->Overlay.Scb;
  3397. }
  3398. if ((*Scb)->FileObject == NULL) {
  3399. NtfsCreateInternalAttributeStream( IrpContext, *Scb, TRUE, NULL );
  3400. if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS )) {
  3401. CcSetAdditionalCacheAttributes( (*Scb)->FileObject, TRUE, TRUE );
  3402. }
  3403. }
  3404. return;
  3405. }
  3406. //
  3407. // Internal support routine
  3408. //
  3409. VOID
  3410. PinAttributeForRestart (
  3411. IN PIRP_CONTEXT IrpContext,
  3412. IN PVCB Vcb,
  3413. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  3414. IN ULONG Length OPTIONAL,
  3415. OUT PBCB *Bcb,
  3416. OUT PVOID *Buffer,
  3417. IN OUT PSCB *Scb
  3418. )
  3419. /*++
  3420. Routine Description:
  3421. This routine pins the desired buffer for restart, as described
  3422. by the current log record.
  3423. Arguments:
  3424. Vcb - Supplies the Vcb pointer for the volume
  3425. LogRecord - Supplies the pointer to the current log record.
  3426. Length - If specified we will use this to determine the length
  3427. to pin. This will handle the non-resident streams which may
  3428. change size (ACL, attribute lists). The log record may have
  3429. more clusters than are currently in the stream.
  3430. Bcb - Returns a pointer to the Bcb for the pinned record.
  3431. Buffer - Returns a pointer to the desired buffer.
  3432. Scb - Returns a pointer to the Scb for the attribute
  3433. Return Value:
  3434. None
  3435. --*/
  3436. {
  3437. LONGLONG FileOffset;
  3438. ULONG ClusterOffset;
  3439. ULONG PinLength;
  3440. PAGED_CODE();
  3441. //
  3442. // First open the described atttribute.
  3443. //
  3444. OpenAttributeForRestart( IrpContext, Vcb, LogRecord, Scb );
  3445. //
  3446. // Calculate the desired file offset and pin the buffer.
  3447. //
  3448. ClusterOffset = BytesFromLogBlocks( LogRecord->ClusterBlockOffset );
  3449. FileOffset = LlBytesFromClusters( Vcb, LogRecord->TargetVcn ) + ClusterOffset;
  3450. ASSERT((!FlagOn( (*Scb)->ScbPersist, SCB_PERSIST_USN_JOURNAL )) || (FileOffset >= Vcb->UsnCacheBias));
  3451. //
  3452. // We only want to pin the requested clusters or to the end of
  3453. // a page, whichever is smaller.
  3454. //
  3455. if (Vcb->BytesPerCluster > PAGE_SIZE) {
  3456. PinLength = PAGE_SIZE - (((ULONG) FileOffset) & (PAGE_SIZE - 1));
  3457. } else if (Length != 0) {
  3458. PinLength = Length;
  3459. } else {
  3460. PinLength = BytesFromClusters( Vcb, LogRecord->LcnsToFollow ) - ClusterOffset;
  3461. }
  3462. //
  3463. // We don't want to pin more than a page
  3464. //
  3465. NtfsPinStream( IrpContext,
  3466. *Scb,
  3467. FileOffset,
  3468. PinLength,
  3469. Bcb,
  3470. Buffer );
  3471. #if DBG
  3472. //
  3473. // Check index signature integrity
  3474. //
  3475. {
  3476. PVOID AlignedBuffer;
  3477. PINDEX_ALLOCATION_BUFFER AllocBuffer;
  3478. AlignedBuffer = (PVOID) BlockAlignTruncate( (ULONG_PTR)(*Buffer), 0x1000 );
  3479. AllocBuffer = (PINDEX_ALLOCATION_BUFFER) AlignedBuffer;
  3480. if ((LogRecord->RedoOperation != UpdateNonresidentValue) &&
  3481. (LogRecord->UndoOperation != UpdateNonresidentValue) &&
  3482. ((*Scb)->AttributeTypeCode == $INDEX_ALLOCATION) &&
  3483. ((*Scb)->AttributeName.Length == 8) &&
  3484. (wcsncmp( (*Scb)->AttributeName.Buffer, L"$I30", 4 ) == 0)) {
  3485. if (*(PULONG)AllocBuffer->MultiSectorHeader.Signature != *(PULONG)IndexSignature) {
  3486. KdPrint(( "Ntfs: index signature is: %d %c%c%c%c for LCN: 0x%I64x\n",
  3487. *(PULONG)AllocBuffer->MultiSectorHeader.Signature,
  3488. AllocBuffer->MultiSectorHeader.Signature[0],
  3489. AllocBuffer->MultiSectorHeader.Signature[1],
  3490. AllocBuffer->MultiSectorHeader.Signature[2],
  3491. AllocBuffer->MultiSectorHeader.Signature[3],
  3492. LogRecord->LcnsForPage[0] ));
  3493. if (*(PULONG)AllocBuffer->MultiSectorHeader.Signature != 0 &&
  3494. *(PULONG)AllocBuffer->MultiSectorHeader.Signature != *(PULONG)BaadSignature &&
  3495. *(PULONG)AllocBuffer->MultiSectorHeader.Signature != *(PULONG)HoleSignature) {
  3496. DbgBreakPoint();
  3497. }
  3498. } //endif signature fork
  3499. } //endif index scb fork
  3500. }
  3501. #endif
  3502. }
  3503. //
  3504. // Internal support routine
  3505. //
  3506. BOOLEAN
  3507. FindDirtyPage (
  3508. IN PRESTART_POINTERS DirtyPageTable,
  3509. IN ULONG TargetAttribute,
  3510. IN VCN Vcn,
  3511. OUT PDIRTY_PAGE_ENTRY *DirtyPageEntry
  3512. )
  3513. /*++
  3514. Routine Description:
  3515. This routine searches for a Vcn to see if it is already in the Dirty Page
  3516. Table, returning the Dirty Page Entry if it is.
  3517. Arguments:
  3518. DirtyPageTable - pointer to the Dirty Page Table to search.
  3519. TargetAttribute - Attribute for which the dirty Vcn is to be searched.
  3520. Vcn - Vcn to search for.
  3521. DirtyPageEntry - returns a pointer to the Dirty Page Entry if returning TRUE.
  3522. Return Value:
  3523. TRUE if the page was found and is being returned, else FALSE.
  3524. --*/
  3525. {
  3526. PDIRTY_PAGE_ENTRY DirtyPage;
  3527. PAGED_CODE();
  3528. DebugTrace( +1, Dbg, ("FindDirtyPage:\n") );
  3529. DebugTrace( 0, Dbg, ("TargetAttribute = %08lx\n", TargetAttribute) );
  3530. DebugTrace( 0, Dbg, ("Vcn = %016I64x\n", Vcn) );
  3531. //
  3532. // If table has not yet been initialized, return.
  3533. //
  3534. if (DirtyPageTable->Table == NULL) {
  3535. return FALSE;
  3536. }
  3537. //
  3538. // Loop through all of the dirty pages to look for a match.
  3539. //
  3540. DirtyPage = NtfsGetFirstRestartTable( DirtyPageTable );
  3541. //
  3542. // Loop to end of table.
  3543. //
  3544. while (DirtyPage != NULL) {
  3545. if ((DirtyPage->TargetAttribute == TargetAttribute)
  3546. &&
  3547. (Vcn >= DirtyPage->Vcn)) {
  3548. //
  3549. // Compute the Last Vcn outside of the comparison or the xxAdd and
  3550. // xxFromUlong will be called three times.
  3551. //
  3552. LONGLONG BeyondLastVcn;
  3553. BeyondLastVcn = DirtyPage->Vcn + DirtyPage->LcnsToFollow;
  3554. if (Vcn < BeyondLastVcn) {
  3555. *DirtyPageEntry = DirtyPage;
  3556. DebugTrace( 0, Dbg, ("DirtyPageEntry %08lx\n", *DirtyPageEntry) );
  3557. DebugTrace( -1, Dbg, ("FindDirtypage -> TRUE\n") );
  3558. return TRUE;
  3559. }
  3560. }
  3561. //
  3562. // Point to next entry in table, or NULL.
  3563. //
  3564. DirtyPage = NtfsGetNextRestartTable( DirtyPageTable,
  3565. DirtyPage );
  3566. }
  3567. *DirtyPageEntry = NULL;
  3568. DebugTrace( -1, Dbg, ("FindDirtypage -> FALSE\n") );
  3569. return FALSE;
  3570. }
  3571. //
  3572. // Internal support routine
  3573. //
  3574. VOID
  3575. PageUpdateAnalysis (
  3576. IN PVCB Vcb,
  3577. IN LSN Lsn,
  3578. IN OUT PRESTART_POINTERS DirtyPageTable,
  3579. IN PNTFS_LOG_RECORD_HEADER LogRecord
  3580. )
  3581. /*++
  3582. Routine Description:
  3583. This routine updates the Dirty Pages Table during the analysis phase
  3584. for all log records which update a page.
  3585. Arguments:
  3586. Vcb - Pointer to the Vcb for the volume.
  3587. Lsn - The Lsn of the log record.
  3588. DirtyPageTable - A pointer to the Dirty Page Table pointer, to be
  3589. updated and potentially expanded.
  3590. LogRecord - Pointer to the Log Record being analyzed.
  3591. Return Value:
  3592. None.
  3593. --*/
  3594. {
  3595. PDIRTY_PAGE_ENTRY DirtyPage;
  3596. ULONG i;
  3597. RESTART_POINTERS NewDirtyPageTable;
  3598. ULONG ClustersPerPage;
  3599. ULONG PageIndex;
  3600. PAGED_CODE();
  3601. DebugTrace( +1, Dbg, ("PageUpdateAnalysis:\n") );
  3602. //
  3603. // Calculate the number of clusters per page in the system which wrote
  3604. // the checkpoint, possibly creating the table.
  3605. //
  3606. if (DirtyPageTable->Table != NULL) {
  3607. ClustersPerPage = ((DirtyPageTable->Table->EntrySize -
  3608. sizeof(DIRTY_PAGE_ENTRY)) / sizeof(LCN)) + 1;
  3609. } else {
  3610. ClustersPerPage = Vcb->ClustersPerPage;
  3611. NtfsInitializeRestartTable( sizeof(DIRTY_PAGE_ENTRY) +
  3612. (ClustersPerPage - 1) * sizeof(LCN),
  3613. INITIAL_NUMBER_DIRTY_PAGES,
  3614. DirtyPageTable );
  3615. }
  3616. //
  3617. // If the on disk number of lcns doesn't match our curent page size
  3618. // we need to reallocate the entire table to accomodate this
  3619. //
  3620. if((ULONG)LogRecord->LcnsToFollow > ClustersPerPage) {
  3621. PDIRTY_PAGE_ENTRY OldDirtyPage;
  3622. DebugTrace( +1, Dbg, ("Ntfs: resizing table in pageupdateanalysis\n") );
  3623. //
  3624. // Adjust clusters per page up to the number of clusters in this record
  3625. //
  3626. ClustersPerPage = (ULONG)LogRecord->LcnsToFollow;
  3627. ASSERT( DirtyPageTable->Table->NumberEntries >= INITIAL_NUMBER_DIRTY_PAGES );
  3628. NtfsInitializeRestartTable( sizeof(DIRTY_PAGE_ENTRY) +
  3629. (ClustersPerPage - 1) * sizeof(LCN),
  3630. DirtyPageTable->Table->NumberEntries,
  3631. &NewDirtyPageTable );
  3632. OldDirtyPage = (PDIRTY_PAGE_ENTRY) NtfsGetFirstRestartTable( DirtyPageTable );
  3633. //
  3634. // Loop to copy table entries
  3635. //
  3636. while (OldDirtyPage) {
  3637. //
  3638. // Allocate a new dirty page entry.
  3639. //
  3640. PageIndex = NtfsAllocateRestartTableIndex( &NewDirtyPageTable, TRUE );
  3641. //
  3642. // Get a pointer to the entry we just allocated.
  3643. //
  3644. DirtyPage = GetRestartEntryFromIndex( &NewDirtyPageTable, PageIndex );
  3645. DirtyPage->TargetAttribute = OldDirtyPage->TargetAttribute;
  3646. DirtyPage->LengthOfTransfer = BytesFromClusters( Vcb, ClustersPerPage );
  3647. DirtyPage->LcnsToFollow = ClustersPerPage;
  3648. DirtyPage->Vcn = BlockAlignTruncate( OldDirtyPage->Vcn, (LONG)ClustersPerPage );
  3649. DirtyPage->OldestLsn = OldDirtyPage->OldestLsn;
  3650. for (i = 0; i < OldDirtyPage->LcnsToFollow; i++) {
  3651. DirtyPage->LcnsForPage[i] = OldDirtyPage->LcnsForPage[i];
  3652. }
  3653. OldDirtyPage = (PDIRTY_PAGE_ENTRY) NtfsGetNextRestartTable( DirtyPageTable, OldDirtyPage );
  3654. }
  3655. //
  3656. // OldTable is really on the stack so swap the new restart table into it
  3657. // and free up the old one and the rest of the new restart pointers
  3658. //
  3659. NtfsFreePool( DirtyPageTable->Table );
  3660. DirtyPageTable->Table = NewDirtyPageTable.Table;
  3661. NewDirtyPageTable.Table = NULL;
  3662. NtfsFreeRestartTable( &NewDirtyPageTable );
  3663. } // endif table needed to be resized
  3664. //
  3665. // Update the dirty page entry or create a new one
  3666. //
  3667. if (!FindDirtyPage( DirtyPageTable,
  3668. LogRecord->TargetAttribute,
  3669. LogRecord->TargetVcn,
  3670. &DirtyPage )) {
  3671. //
  3672. // Allocate a dirty page entry.
  3673. //
  3674. PageIndex = NtfsAllocateRestartTableIndex( DirtyPageTable, TRUE );
  3675. //
  3676. // Get a pointer to the entry we just allocated.
  3677. //
  3678. DirtyPage = GetRestartEntryFromIndex( DirtyPageTable, PageIndex );
  3679. //
  3680. // Initialize the dirty page entry.
  3681. //
  3682. DirtyPage->TargetAttribute = LogRecord->TargetAttribute;
  3683. DirtyPage->LengthOfTransfer = BytesFromClusters( Vcb, ClustersPerPage );
  3684. DirtyPage->LcnsToFollow = ClustersPerPage;
  3685. DirtyPage->Vcn = BlockAlignTruncate( LogRecord->TargetVcn, (LONG)ClustersPerPage );
  3686. DirtyPage->OldestLsn = Lsn;
  3687. }
  3688. //
  3689. // Copy the Lcns from the log record into the Dirty Page Entry.
  3690. //
  3691. // *** for different page size support, must somehow make whole routine a loop,
  3692. // in case Lcns do not fit below.
  3693. //
  3694. for (i = 0; i < (ULONG)LogRecord->LcnsToFollow; i++) {
  3695. DirtyPage->LcnsForPage[((ULONG)LogRecord->TargetVcn) - ((ULONG)DirtyPage->Vcn) + i] =
  3696. LogRecord->LcnsForPage[i];
  3697. }
  3698. DebugTrace( -1, Dbg, ("PageUpdateAnalysis -> VOID\n") );
  3699. }
  3700. //
  3701. // Internal support routine
  3702. //
  3703. VOID
  3704. OpenAttributesForRestart (
  3705. IN PIRP_CONTEXT IrpContext,
  3706. IN PVCB Vcb,
  3707. IN PRESTART_POINTERS DirtyPageTable
  3708. )
  3709. /*++
  3710. Routine Description:
  3711. This routine is called immediately after the Analysis Pass to open all of
  3712. the attributes in the Open Attribute Table, and preload their Mcbs with
  3713. any run information required to apply updates in the Dirty Page Table.
  3714. With this trick we are effectively doing physical I/O directly to Lbns on
  3715. the disk without relying on any of the file structure to be correct.
  3716. Arguments:
  3717. Vcb - Vcb for the volume, for which the Open Attribute Table has been
  3718. initialized.
  3719. DirtyPageTable - Dirty Page table reconstructed from the Analysis Pass.
  3720. Return Value:
  3721. None.
  3722. --*/
  3723. {
  3724. POPEN_ATTRIBUTE_ENTRY OpenEntry;
  3725. POPEN_ATTRIBUTE_ENTRY OldOpenEntry;
  3726. PDIRTY_PAGE_ENTRY DirtyPage;
  3727. ULONG i;
  3728. PSCB TempScb;
  3729. PAGED_CODE();
  3730. DebugTrace( +1, Dbg, ("OpenAttributesForRestart:\n") );
  3731. //
  3732. // First we scan the Open Attribute Table to open all of the attributes.
  3733. //
  3734. OpenEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  3735. //
  3736. // Loop to end of table.
  3737. //
  3738. while (OpenEntry != NULL) {
  3739. //
  3740. // Create the Scb from the data in the Open Attribute Entry.
  3741. //
  3742. TempScb = NtfsCreatePrerestartScb( IrpContext,
  3743. Vcb,
  3744. &OpenEntry->FileReference,
  3745. OpenEntry->AttributeTypeCode,
  3746. &OpenEntry->OatData->AttributeName,
  3747. OpenEntry->BytesPerIndexBuffer );
  3748. //
  3749. // If we dynamically allocated a name for this guy, then delete
  3750. // it here.
  3751. //
  3752. if (OpenEntry->OatData->Overlay.AttributeName != NULL) {
  3753. ASSERT( OpenEntry->OatData->AttributeNamePresent );
  3754. NtfsFreePool( OpenEntry->OatData->Overlay.AttributeName );
  3755. OpenEntry->OatData->AttributeNamePresent = FALSE;
  3756. }
  3757. OpenEntry->OatData->AttributeName = TempScb->AttributeName;
  3758. //
  3759. // Now we can lay in the Scb. We must say the header is initialized
  3760. // to keep anyone from going to disk yet.
  3761. //
  3762. SetFlag( TempScb->ScbState, SCB_STATE_HEADER_INITIALIZED );
  3763. //
  3764. // Now store the index in the newly created Scb if its newer.
  3765. // precalc oldopenentry buts its only good if the scb's attributeindex is nonzero
  3766. //
  3767. OldOpenEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable, TempScb->NonpagedScb->OpenAttributeTableIndex );
  3768. if ((TempScb->NonpagedScb->OpenAttributeTableIndex == 0) ||
  3769. (OldOpenEntry->LsnOfOpenRecord.QuadPart < OpenEntry->LsnOfOpenRecord.QuadPart)) {
  3770. TempScb->NonpagedScb->OpenAttributeTableIndex = GetIndexFromRestartEntry( &Vcb->OpenAttributeTable, OpenEntry );
  3771. TempScb->NonpagedScb->OnDiskOatIndex = OpenEntry->OatData->OnDiskAttributeIndex;
  3772. }
  3773. OpenEntry->OatData->Overlay.Scb = TempScb;
  3774. //
  3775. // Point to next entry in table, or NULL.
  3776. //
  3777. OpenEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable,
  3778. OpenEntry );
  3779. }
  3780. //
  3781. // Now loop through the dirty page table to extract all of the Vcn/Lcn
  3782. // Mapping that we have, and insert it into the appropriate Scb.
  3783. //
  3784. DirtyPage = NtfsGetFirstRestartTable( DirtyPageTable );
  3785. //
  3786. // Loop to end of table.
  3787. //
  3788. while (DirtyPage != NULL) {
  3789. PSCB Scb;
  3790. //
  3791. // Safety check
  3792. //
  3793. if (!IsRestartIndexWithinTable( Vcb->OnDiskOat, DirtyPage->TargetAttribute )) {
  3794. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  3795. }
  3796. OpenEntry = GetRestartEntryFromIndex( Vcb->OnDiskOat,
  3797. DirtyPage->TargetAttribute );
  3798. if (IsRestartTableEntryAllocated( OpenEntry )) {
  3799. //
  3800. // Get the entry from the other table if necessary.
  3801. //
  3802. if (Vcb->RestartVersion == 0) {
  3803. //
  3804. // Safety check
  3805. //
  3806. if (!IsRestartIndexWithinTable( &Vcb->OpenAttributeTable, ((POPEN_ATTRIBUTE_ENTRY_V0) OpenEntry)->OatIndex )) {
  3807. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  3808. }
  3809. OpenEntry = GetRestartEntryFromIndex( &Vcb->OpenAttributeTable,
  3810. ((POPEN_ATTRIBUTE_ENTRY_V0) OpenEntry)->OatIndex );
  3811. }
  3812. Scb = OpenEntry->OatData->Overlay.Scb;
  3813. //
  3814. // Loop to add the allocated Vcns.
  3815. //
  3816. for (i = 0; i < DirtyPage->LcnsToFollow; i++) {
  3817. VCN Vcn;
  3818. LONGLONG Size;
  3819. Vcn = DirtyPage->Vcn + i;
  3820. Size = LlBytesFromClusters( Vcb, Vcn + 1);
  3821. //
  3822. // Add this run to the Mcb if the Vcn has not been deleted,
  3823. // and it is not for the fixed part of the Mft.
  3824. //
  3825. if ((DirtyPage->LcnsForPage[i] != 0)
  3826. &&
  3827. (NtfsSegmentNumber( &OpenEntry->FileReference ) > MASTER_FILE_TABLE2_NUMBER ||
  3828. (Size >= ((VOLUME_DASD_NUMBER + 1) * Vcb->BytesPerFileRecordSegment)) ||
  3829. (OpenEntry->AttributeTypeCode != $DATA))) {
  3830. if (!NtfsAddNtfsMcbEntry( &Scb->Mcb,
  3831. Vcn,
  3832. DirtyPage->LcnsForPage[i],
  3833. (LONGLONG)1,
  3834. FALSE )) {
  3835. //
  3836. // Replace with new entry if collision comes from
  3837. // the newest attribute
  3838. //
  3839. if (DirtyPage->TargetAttribute == Scb->NonpagedScb->OnDiskOatIndex) {
  3840. #if DBG
  3841. BOOLEAN Result;
  3842. #endif
  3843. NtfsRemoveNtfsMcbEntry( &Scb->Mcb,
  3844. Vcn,
  3845. 1 );
  3846. #if DBG
  3847. Result =
  3848. #endif
  3849. NtfsAddNtfsMcbEntry( &Scb->Mcb,
  3850. Vcn,
  3851. DirtyPage->LcnsForPage[i],
  3852. (LONGLONG)1,
  3853. FALSE );
  3854. #if DBG
  3855. ASSERT( Result );
  3856. #endif
  3857. }
  3858. }
  3859. if (Size > Scb->Header.AllocationSize.QuadPart) {
  3860. Scb->Header.AllocationSize.QuadPart =
  3861. Scb->Header.FileSize.QuadPart =
  3862. Scb->Header.ValidDataLength.QuadPart = Size;
  3863. }
  3864. }
  3865. }
  3866. }
  3867. //
  3868. // Point to next entry in table, or NULL.
  3869. //
  3870. DirtyPage = NtfsGetNextRestartTable( DirtyPageTable,
  3871. DirtyPage );
  3872. }
  3873. //
  3874. // Now we know how big all of the files have to be, and recorded that in the
  3875. // Scb. We have not created streams for any of these Scbs yet, except for
  3876. // the Mft, Mft2 and LogFile. The size should be correct for Mft2 and LogFile,
  3877. // but we have to inform the Cache Manager here of the final size of the Mft.
  3878. //
  3879. TempScb = Vcb->MftScb;
  3880. ASSERT( !FlagOn( TempScb->ScbPersist, SCB_PERSIST_USN_JOURNAL ) );
  3881. CcSetFileSizes( TempScb->FileObject,
  3882. (PCC_FILE_SIZES)&TempScb->Header.AllocationSize );
  3883. DebugTrace( -1, Dbg, ("OpenAttributesForRestart -> VOID\n") );
  3884. }
  3885. NTSTATUS
  3886. NtfsCloseAttributesFromRestart (
  3887. IN PIRP_CONTEXT IrpContext,
  3888. IN PVCB Vcb
  3889. )
  3890. /*++
  3891. Routine Description:
  3892. This routine is called at the end of a Restart to close any attributes
  3893. that had to be opened for Restart purposes. Actually what this does is
  3894. delete all of the internal streams so that the attributes will eventually
  3895. go away. This routine cannot raise because it is called in the finally of
  3896. MountVolume. Raising in the main line path will leave the global resource
  3897. acquired.
  3898. Arguments:
  3899. Vcb - Vcb for the volume, for which the Open Attribute Table has been
  3900. initialized.
  3901. Return Value:
  3902. NTSTATUS - STATUS_SUCCESS if all of the I/O completed successfully. Otherwise
  3903. the error in the IrpContext or the first I/O error.
  3904. --*/
  3905. {
  3906. NTSTATUS Status = STATUS_SUCCESS;
  3907. POPEN_ATTRIBUTE_ENTRY OpenEntry;
  3908. PAGED_CODE();
  3909. DebugTrace( +1, Dbg, ("CloseAttributesForRestart:\n") );
  3910. //
  3911. // Set this flag again now, so we do not try to flush out the holes!
  3912. //
  3913. SetFlag(Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS);
  3914. //
  3915. // Remove duplicate Scbs - in rare case no dirty pages the scb's were never
  3916. // opened at all
  3917. //
  3918. OpenEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  3919. while (OpenEntry != NULL) {
  3920. if ((OpenEntry->OatData->Overlay.Scb != NULL) &&
  3921. (!(OpenEntry->OatData->AttributeNamePresent)) &&
  3922. (OpenEntry->OatData->Overlay.Scb->NonpagedScb->OpenAttributeTableIndex !=
  3923. GetIndexFromRestartEntry( &Vcb->OpenAttributeTable, OpenEntry ))) {
  3924. OpenEntry->OatData->Overlay.Scb = NULL;
  3925. }
  3926. //
  3927. // Point to next entry in table, or NULL.
  3928. //
  3929. OpenEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable,
  3930. OpenEntry );
  3931. }
  3932. //
  3933. // Scan the Open Attribute Table to close all of the open attributes.
  3934. //
  3935. OpenEntry = NtfsGetFirstRestartTable( &Vcb->OpenAttributeTable );
  3936. //
  3937. // Loop to end of table.
  3938. //
  3939. while (OpenEntry != NULL) {
  3940. IO_STATUS_BLOCK IoStatus;
  3941. PSCB Scb;
  3942. if (OpenEntry->OatData->AttributeNamePresent) {
  3943. NtfsFreeScbAttributeName( OpenEntry->OatData->AttributeName.Buffer );
  3944. OpenEntry->OatData->Overlay.AttributeName = NULL;
  3945. OpenEntry->OatData->AttributeName.Buffer = NULL;
  3946. }
  3947. Scb = OpenEntry->OatData->Overlay.Scb;
  3948. //
  3949. // We clean up the Scb only if it exists and this is index in the
  3950. // OpenAttributeTable that this Scb actually refers to.
  3951. // If this Scb has several entries in the table, we nulled out older
  3952. // duplicates in the loop above
  3953. //
  3954. if (Scb != NULL) {
  3955. FILE_REFERENCE FileReference;
  3956. //
  3957. // Only shut it down if it is not the Mft or its mirror.
  3958. //
  3959. FileReference = Scb->Fcb->FileReference;
  3960. if (NtfsSegmentNumber( &FileReference ) > LOG_FILE_NUMBER ||
  3961. (Scb->AttributeTypeCode != $DATA)) {
  3962. //
  3963. // Now flush the file. It is important to call the
  3964. // same routine the Lazy Writer calls, so that write.c
  3965. // will not decide to update file size for the attribute,
  3966. // since we really are working here with the wrong size.
  3967. //
  3968. NtfsAcquireScbForLazyWrite( (PVOID)Scb, TRUE );
  3969. CcFlushCache( &Scb->NonpagedScb->SegmentObject, NULL, 0, &IoStatus );
  3970. NtfsReleaseScbFromLazyWrite( (PVOID)Scb );
  3971. if (NT_SUCCESS( Status )) {
  3972. if (!NT_SUCCESS( IrpContext->ExceptionStatus )) {
  3973. Status = IrpContext->ExceptionStatus;
  3974. } else if (!NT_SUCCESS( IoStatus.Status )) {
  3975. Status = FsRtlNormalizeNtstatus( IoStatus.Status,
  3976. STATUS_UNEXPECTED_IO_ERROR );
  3977. }
  3978. }
  3979. //
  3980. // If there is an Scb and it is not for a system file, then delete
  3981. // the stream file so it can eventually go away.
  3982. //
  3983. NtfsUninitializeNtfsMcb( &Scb->Mcb );
  3984. NtfsInitializeNtfsMcb( &Scb->Mcb,
  3985. &Scb->Header,
  3986. &Scb->McbStructs,
  3987. FlagOn( Scb->Fcb->FcbState,
  3988. FCB_STATE_PAGING_FILE ) ? NonPagedPool :
  3989. PagedPool );
  3990. //
  3991. // Now that we are restarted, we must clear the header state
  3992. // so that we will go look up the sizes and load the Scb
  3993. // from disk.
  3994. //
  3995. ClearFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED |
  3996. SCB_STATE_FILE_SIZE_LOADED );
  3997. //
  3998. // Show the indexed portions are "uninitialized".
  3999. //
  4000. if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
  4001. Scb->ScbType.Index.BytesPerIndexBuffer = 0;
  4002. }
  4003. //
  4004. // If this Fcb is past the log file then remove it from the
  4005. // Fcb table.
  4006. //
  4007. if ((NtfsSegmentNumber( &FileReference ) > LOG_FILE_NUMBER) &&
  4008. FlagOn( Scb->Fcb->FcbState, FCB_STATE_IN_FCB_TABLE )) {
  4009. NtfsDeleteFcbTableEntry( Scb->Fcb->Vcb, FileReference );
  4010. ClearFlag( Scb->Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
  4011. }
  4012. //
  4013. // If the Scb is a root index scb then change the type to INDEX_SCB.
  4014. // Otherwise the teardown routines will skip it.
  4015. //
  4016. if (SafeNodeType( Scb ) == NTFS_NTC_SCB_ROOT_INDEX) {
  4017. SafeNodeType( Scb ) = NTFS_NTC_SCB_INDEX;
  4018. }
  4019. if (Scb->FileObject != NULL) {
  4020. NtfsDeleteInternalAttributeStream( Scb, TRUE, FALSE );
  4021. } else {
  4022. //
  4023. // Make sure the Scb is acquired exclusively.
  4024. //
  4025. NtfsAcquireExclusiveFcb( IrpContext, Scb->Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  4026. NtfsTeardownStructures( IrpContext,
  4027. Scb,
  4028. NULL,
  4029. FALSE,
  4030. 0,
  4031. NULL );
  4032. }
  4033. }
  4034. } else {
  4035. //
  4036. // We want to check whether there is also an entry in the other table.
  4037. //
  4038. if (Vcb->RestartVersion == 0) {
  4039. NtfsFreeRestartTableIndex( Vcb->OnDiskOat, OpenEntry->OatData->OnDiskAttributeIndex );
  4040. }
  4041. NtfsFreeOpenAttributeData( OpenEntry->OatData );
  4042. NtfsFreeRestartTableIndex( &Vcb->OpenAttributeTable,
  4043. GetIndexFromRestartEntry( &Vcb->OpenAttributeTable,
  4044. OpenEntry ));
  4045. }
  4046. //
  4047. // Point to next entry in table, or NULL.
  4048. //
  4049. OpenEntry = NtfsGetNextRestartTable( &Vcb->OpenAttributeTable,
  4050. OpenEntry );
  4051. }
  4052. //
  4053. // Resume normal operation.
  4054. //
  4055. ClearFlag(Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS);
  4056. DebugTrace( -1, Dbg, ("CloseAttributesForRestart -> %08lx\n", Status) );
  4057. return Status;
  4058. }