Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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