Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2476 lines
71 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. CacheSup.c
  5. Abstract:
  6. This module provides an interface with the cache manager.
  7. Author:
  8. Brian Andrew [BrianAn] 20-June-1991
  9. Revision History:
  10. --*/
  11. #include "lfsprocs.h"
  12. #include <ntdddisk.h>
  13. #include <NtIoLogc.h>
  14. #include <elfmsg.h>
  15. //
  16. // The debug trace level
  17. //
  18. #define Dbg (DEBUG_TRACE_CACHE_SUP)
  19. //
  20. // Following is used to generate a sequence number when the cache manager
  21. // gives us a page of zeroes. Otherwise all of the sequence numbers will
  22. // be 1.
  23. //
  24. USHORT LfsUsaSeqNumber;
  25. LARGE_INTEGER LiMinus1 = {(ULONG)-1,-1};
  26. BOOLEAN
  27. LfsIsRestartPageHeaderValid (
  28. IN LONGLONG FileOffset,
  29. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  30. OUT PBOOLEAN LogPacked
  31. );
  32. BOOLEAN
  33. LfsIsRestartAreaValid (
  34. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  35. IN BOOLEAN LogPacked
  36. );
  37. BOOLEAN
  38. LfsIsClientAreaValid (
  39. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  40. IN BOOLEAN LogPacked,
  41. IN BOOLEAN UsaError
  42. );
  43. VOID
  44. LfsFindFirstIo (
  45. IN PLFCB Lfcb,
  46. IN LSN TargetLsn,
  47. IN BOOLEAN RestartLsn,
  48. IN PLBCB FirstLbcb,
  49. OUT PLBCB *NextLbcb,
  50. OUT PLONGLONG FileOffset,
  51. OUT PBOOLEAN ContainsLastEntry,
  52. OUT PBOOLEAN LfsRestart,
  53. OUT PBOOLEAN UseTailCopy,
  54. OUT PULONG IoBlocks
  55. );
  56. #ifdef ALLOC_PRAGMA
  57. #pragma alloc_text(PAGE, LfsCopyReadLogRecord)
  58. #pragma alloc_text(PAGE, LfsFindFirstIo)
  59. #pragma alloc_text(PAGE, LfsIsClientAreaValid)
  60. #pragma alloc_text(PAGE, LfsIsRestartAreaValid)
  61. #pragma alloc_text(PAGE, LfsIsRestartPageHeaderValid)
  62. #pragma alloc_text(PAGE, LfsPinOrMapData)
  63. #pragma alloc_text(PAGE, LfsPinOrMapLogRecordHeader)
  64. #pragma alloc_text(PAGE, LfsReadRestart)
  65. #endif
  66. NTSTATUS
  67. LfsPinOrMapData (
  68. IN PLFCB Lfcb,
  69. IN LONGLONG FileOffset,
  70. IN ULONG Length,
  71. IN BOOLEAN PinData,
  72. IN BOOLEAN AllowErrors,
  73. IN BOOLEAN IgnoreUsaErrors,
  74. OUT PBOOLEAN UsaError,
  75. OUT PVOID *Buffer,
  76. OUT PBCB *Bcb
  77. )
  78. /*++
  79. Routine Description:
  80. This routine will pin or map a portion of the log file.
  81. Arguments:
  82. Lfcb - This is the file control block for the log file.
  83. FileOffset - This is the offset of the log page to pin.
  84. Length - This is the length of the data to access.
  85. PinData - Boolean indicating if we are to pin or map this data.
  86. AllowErrors - This boolean indicates whether we should raise on an
  87. I/O error or return on an I/O error.
  88. IgnoreUsaErrors - Boolean indicating whether we will raise on Usa
  89. errors.
  90. UsaError - Address to store whether the Usa had an error.
  91. Buffer - This is the address to store the address of the data.
  92. Bcb - This is the Bcb for this operation.
  93. Return Value:
  94. NTSTATUS - The result of the I/O.
  95. --*/
  96. {
  97. volatile NTSTATUS Status;
  98. ULONG Signature;
  99. BOOLEAN Result = FALSE;
  100. Status = STATUS_SUCCESS;
  101. PAGED_CODE();
  102. DebugTrace( +1, Dbg, "LfsPinReadLogPage: Entered\n", 0 );
  103. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  104. DebugTrace( 0, Dbg, "FileOffset (Low) -> %08lx\n", FileOffset.HighPart );
  105. DebugTrace( 0, Dbg, "FileOffset (High) -> %08lx\n", FileOffset.LowPart );
  106. DebugTrace( 0, Dbg, "Length -> %08lx\n", Length );
  107. DebugTrace( 0, Dbg, "PinData -> %04x\n", PinData );
  108. DebugTrace( 0, Dbg, "AllowErrors -> %08x\n", AllowErrors );
  109. DebugTrace( 0, Dbg, "IgnoreUsaErrors -> %04x\n", IgnoreUsaErrors );
  110. if (FileOffset + Length > Lfcb->FileSize) {
  111. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  112. }
  113. //
  114. // Use a try-finally to facilitate cleanup.
  115. //
  116. try {
  117. //
  118. // Use a try-except to catch cache manager errors.
  119. //
  120. try {
  121. //
  122. // We call the cache to perform the work.
  123. //
  124. if (PinData) {
  125. Result = CcPinRead( Lfcb->FileObject,
  126. (PLARGE_INTEGER)&FileOffset,
  127. Length,
  128. TRUE,
  129. Bcb,
  130. Buffer );
  131. } else {
  132. Result = CcMapData( Lfcb->FileObject,
  133. (PLARGE_INTEGER)&FileOffset,
  134. Length,
  135. TRUE,
  136. Bcb,
  137. Buffer );
  138. }
  139. //
  140. // Capture the signature now while we are within the
  141. // exception filter.
  142. //
  143. Signature = *((PULONG) *Buffer);
  144. } except( LfsExceptionFilter( GetExceptionInformation() )) {
  145. Status = GetExceptionCode();
  146. if (Result) {
  147. CcUnpinData( *Bcb );
  148. *Bcb = NULL;
  149. }
  150. }
  151. *UsaError = FALSE;
  152. //
  153. // If an error occurred, we raise the status.
  154. //
  155. if (!NT_SUCCESS( Status )) {
  156. if (!AllowErrors) {
  157. DebugTrace( 0, Dbg, "Read on log page failed -> %08lx\n", Status );
  158. ExRaiseStatus( Status );
  159. }
  160. //
  161. // Check that the update sequence array for this
  162. // page is valid.
  163. //
  164. } else if (Signature == LFS_SIGNATURE_BAD_USA_ULONG) {
  165. //
  166. // If we don't allow errors, raise an error status.
  167. //
  168. if (!IgnoreUsaErrors) {
  169. DebugTrace( 0, Dbg, "Usa error on log page\n", 0 );
  170. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  171. }
  172. *UsaError = TRUE;
  173. }
  174. } finally {
  175. DebugUnwind( LfsPinOrMapData );
  176. DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
  177. DebugTrace( 0, Dbg, "Bcb -> %08lx\n", *Bcb );
  178. DebugTrace( -1, Dbg, "LfsPinOrMapData: Exit -> %08lx\n", Status );
  179. }
  180. return Status;
  181. }
  182. VOID
  183. LfsPinOrMapLogRecordHeader (
  184. IN PLFCB Lfcb,
  185. IN LSN Lsn,
  186. IN BOOLEAN PinData,
  187. IN BOOLEAN IgnoreUsaErrors,
  188. OUT PBOOLEAN UsaError,
  189. OUT PLFS_RECORD_HEADER *RecordHeader,
  190. OUT PBCB *Bcb
  191. )
  192. /*++
  193. Routine Description:
  194. This routine will pin or map a log record for read access.
  195. Arguments:
  196. Lfcb - This is the file control block for the log file.
  197. Lsn - This is the Lsn whose header should be pinned.
  198. PinData - Boolean indicating if we are to pin or map this data.
  199. IgnoreUsaErrors - Boolean indicating whether we will raise on Usa
  200. errors.
  201. UsaError - Address to store whether the Usa had an error.
  202. RecordHeader - This is the address to store the address of the pinned data.
  203. Bcb - This is the Bcb for this pin operation.
  204. Return Value:
  205. None.
  206. --*/
  207. {
  208. PLFS_RECORD_PAGE_HEADER LogPageHeader;
  209. LONGLONG LogPage;
  210. ULONG PageOffset;
  211. PAGED_CODE();
  212. DebugTrace( +1, Dbg, "LfsPinOrMapLogRecordHeader: Entered\n", 0 );
  213. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  214. DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.HighPart );
  215. DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.LowPart );
  216. DebugTrace( 0, Dbg, "PinData -> %04x\n", PinData );
  217. DebugTrace( 0, Dbg, "IgnoreUsaErrors -> %04x\n", IgnoreUsaErrors );
  218. //
  219. // Compute the log page and the offset of the log record header
  220. // in the log page.
  221. //
  222. LfsTruncateLsnToLogPage( Lfcb, Lsn, &LogPage );
  223. PageOffset = LfsLsnToPageOffset( Lfcb, Lsn );
  224. //
  225. // Call the cache manager to pin the page.
  226. //
  227. LfsPinOrMapData( Lfcb,
  228. LogPage,
  229. (ULONG)Lfcb->LogPageSize,
  230. PinData,
  231. FALSE,
  232. IgnoreUsaErrors,
  233. UsaError,
  234. (PVOID *) &LogPageHeader,
  235. Bcb );
  236. //
  237. // The actual offset we need is at PageOffset from the start of the page.
  238. //
  239. *RecordHeader = Add2Ptr( LogPageHeader, PageOffset, PLFS_RECORD_HEADER );
  240. DebugTrace( 0, Dbg, "Record Header -> %08lx\n", *RecordHeader );
  241. DebugTrace( 0, Dbg, "Bcb -> %08lx\n", *Bcb );
  242. DebugTrace( -1, Dbg, "LfsPinOrMapLogRecordHeader: Exit\n", 0 );
  243. return;
  244. }
  245. VOID
  246. LfsCopyReadLogRecord (
  247. IN PLFCB Lfcb,
  248. IN PLFS_RECORD_HEADER RecordHeader,
  249. OUT PVOID Buffer
  250. )
  251. /*++
  252. Routine Description:
  253. This routines copies a log record from the file to a buffer. The log
  254. record may span several log pages and may even wrap in the file.
  255. Arguments:
  256. Lfcb - A pointer to the control block for the log file.
  257. RecordHeader - Pointer to the log record header for this log record.
  258. Buffer - Pointer to the buffer to store the log record.
  259. Return Value:
  260. None.
  261. --*/
  262. {
  263. PBCB Bcb = NULL;
  264. BOOLEAN UsaError;
  265. PLFS_RECORD_PAGE_HEADER PageHeader;
  266. LONGLONG LogPageFileOffset;
  267. ULONG LogPageOffset;
  268. ULONG RemainingTransferBytes;
  269. PAGED_CODE();
  270. DebugTrace( +1, Dbg, "LfsCopyReadLogRecord: Entered\n", 0 );
  271. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  272. DebugTrace( 0, Dbg, "RecordHeader -> %08lx\n", RecordHeader );
  273. DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
  274. //
  275. // We find the file offset of the log page containing the start of
  276. // this log record, the offset within the page to start the transfer from,
  277. // the number of bytes to transfer on this page and the starting
  278. // position in the buffer to begin the transfer to.
  279. //
  280. LfsTruncateLsnToLogPage( Lfcb, RecordHeader->ThisLsn, &LogPageFileOffset );
  281. LogPageOffset = LfsLsnToPageOffset( Lfcb, RecordHeader->ThisLsn ) + Lfcb->RecordHeaderLength;
  282. RemainingTransferBytes = RecordHeader->ClientDataLength;
  283. //
  284. // Use a try-finally to facilitate cleanup.
  285. //
  286. try {
  287. //
  288. // While there are more bytes to transfer, we continue to attempt to
  289. // perform the read.
  290. //
  291. while (TRUE) {
  292. ULONG RemainingPageBytes;
  293. BOOLEAN Wrapped;
  294. RemainingPageBytes = (ULONG)Lfcb->LogPageSize - LogPageOffset;
  295. //
  296. // We compute the number of bytes to read from this log page and
  297. // call the cache package to perform the transfer.
  298. //
  299. if (RemainingTransferBytes <= RemainingPageBytes) {
  300. RemainingPageBytes = RemainingTransferBytes;
  301. }
  302. RemainingTransferBytes -= RemainingPageBytes;
  303. //
  304. // Unpin any previous buffer.
  305. //
  306. if (Bcb != NULL) {
  307. CcUnpinData( Bcb );
  308. Bcb = NULL;
  309. }
  310. LfsPinOrMapData( Lfcb,
  311. LogPageFileOffset,
  312. (ULONG)Lfcb->LogPageSize,
  313. FALSE,
  314. FALSE,
  315. TRUE,
  316. &UsaError,
  317. (PVOID *) &PageHeader,
  318. &Bcb );
  319. //
  320. // The last Lsn on this page better be greater or equal to the Lsn we
  321. // are copying.
  322. //
  323. if ( PageHeader->Copy.LastLsn.QuadPart < RecordHeader->ThisLsn.QuadPart ) {
  324. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  325. }
  326. RtlCopyMemory( Buffer,
  327. Add2Ptr( PageHeader, LogPageOffset, PVOID ),
  328. RemainingPageBytes );
  329. //
  330. // If there are no more bytes to transfer, we exit the loop.
  331. //
  332. if (RemainingTransferBytes == 0) {
  333. //
  334. // Our log record better not span this page.
  335. //
  336. if (!FlagOn( PageHeader->Flags, LOG_PAGE_LOG_RECORD_END )
  337. || (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
  338. && ( RecordHeader->ThisLsn.QuadPart > PageHeader->Header.Packed.LastEndLsn.QuadPart ))) {
  339. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  340. }
  341. break;
  342. }
  343. //
  344. // If the page header indicates that the log record ended on this page,
  345. // this is a disk corrupt condition. For a packed page it means
  346. // that the last Lsn and the last Ending Lsn are the same.
  347. //
  348. if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
  349. //
  350. // If there is no spanning log record this is an error.
  351. //
  352. if (( PageHeader->Copy.LastLsn.QuadPart == PageHeader->Header.Packed.LastEndLsn.QuadPart )
  353. || ( RecordHeader->ThisLsn.QuadPart > PageHeader->Copy.LastLsn.QuadPart )) {
  354. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  355. }
  356. //
  357. // For an unpacked page it simply means that the page
  358. // contains the end of a log record.
  359. //
  360. } else if (FlagOn( PageHeader->Flags, LOG_PAGE_LOG_RECORD_END )) {
  361. ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
  362. }
  363. //
  364. // We find the start of the next log page and the offset within
  365. // that page to start transferring bytes.
  366. //
  367. LfsNextLogPageOffset( Lfcb,
  368. LogPageFileOffset,
  369. &LogPageFileOffset,
  370. &Wrapped );
  371. LogPageOffset = (ULONG)Lfcb->LogPageDataOffset;
  372. //
  373. // We also adjust our pointer in the user's buffer to transfer
  374. // the next block to.
  375. //
  376. Buffer = Add2Ptr( Buffer, RemainingPageBytes, PVOID );
  377. }
  378. } finally {
  379. //
  380. // Unpin any previous buffer.
  381. //
  382. if (Bcb != NULL) {
  383. CcUnpinData( Bcb );
  384. Bcb = NULL;
  385. }
  386. DebugTrace( -1, Dbg, "LfsCopyReadLogRecord: Exit\n", 0 );
  387. }
  388. return;
  389. }
  390. VOID
  391. LfsFlushLfcb (
  392. IN PLFCB Lfcb,
  393. IN LSN TargetLsn,
  394. IN BOOLEAN RestartLsn
  395. )
  396. /*++
  397. Routine Description:
  398. This routine is called to flush the current Lbcbs in on the Lfcb
  399. work queue. It will flush up to the I/O which contains the desired
  400. TargetLsn. It should be called with LfsIoState already set - this will be cleared
  401. on finish. At periodic points the sync event will be pulsed to wake up waiter threads
  402. Arguments:
  403. Lfcb - This is the file control block for the log file.
  404. TargetLsn - This is the Lsn which is needed to be flushed to disk.
  405. if it is greater than the current lsn everything will get flushed
  406. Restart - if true the target lsn is an lfs restart pseudo lsn
  407. Return Value:
  408. None.
  409. --*/
  410. {
  411. PLBCB FirstLbcb;
  412. PLBCB ThisLbcb;
  413. PLBCB NextLbcb;
  414. PLBCB TargetLbcb;
  415. PULONG Signature;
  416. LONGLONG FileOffset;
  417. ULONG Length;
  418. BOOLEAN ValidLastLsn = FALSE;
  419. BOOLEAN ContainsLastEntry = FALSE;
  420. BOOLEAN LfsRestart;
  421. BOOLEAN UseTailCopy;
  422. ULONG IoBlocks;
  423. ULONG NewLfcbFlags = 0;
  424. PBCB MapPageBcb = NULL;
  425. LSN LastLsn;
  426. IO_STATUS_BLOCK Iosb;
  427. PBCB PageBcb = NULL;
  428. NTSTATUS FailedFlushStatus = STATUS_SUCCESS;
  429. LONGLONG FailedFlushOffset;
  430. KEVENT Event;
  431. PLFS_WAITER LfsWaiter;
  432. BOOLEAN OwnedExclusive;
  433. DebugTrace( +1, Dbg, "LfsFlushLfcb: Entered\n", 0 );
  434. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  435. //
  436. // We'd absolutely hate for this to happen on a read only volume.
  437. //
  438. ASSERT(!(BooleanFlagOn( Lfcb->Flags, LFCB_READ_ONLY )));
  439. //
  440. // Use a try-finally to facilitate cleanup.
  441. //
  442. OwnedExclusive = ExIsResourceAcquiredExclusiveLite( &Lfcb->Sync->Resource );
  443. try {
  444. //
  445. // If there are no elements on the list, we are done.
  446. //
  447. if (IsListEmpty( &Lfcb->LbcbWorkque )) {
  448. leave;
  449. }
  450. KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
  451. //
  452. // Convert max lsn to the last lsn currently on the list - current lsn will
  453. // not change since we hold the lfcb at least shared at this point and writers
  454. // need it exclusive
  455. //
  456. if (TargetLsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart) {
  457. ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbWorkque.Blink,
  458. LBCB,
  459. WorkqueLinks );
  460. TargetLsn.QuadPart = ThisLbcb->LastLsn.QuadPart;
  461. RestartLsn = (BOOLEAN) LfsLbcbIsRestart( ThisLbcb );
  462. }
  463. //
  464. // Remember the first Lbcb in the list.
  465. //
  466. FirstLbcb = CONTAINING_RECORD( Lfcb->LbcbWorkque.Flink,
  467. LBCB,
  468. WorkqueLinks );
  469. ASSERT( FirstLbcb != NULL );
  470. //
  471. // We continue looping and performing I/o for as long as possible.
  472. //
  473. while (!ContainsLastEntry) {
  474. ASSERT( FirstLbcb != NULL );
  475. //
  476. // Find the block of Lbcb's that make up the first I/O, remembering
  477. // how many there are. Also remember if this I/O contains the
  478. // last element on the list when we were called.
  479. //
  480. LfsFindFirstIo( Lfcb,
  481. TargetLsn,
  482. RestartLsn,
  483. FirstLbcb,
  484. &NextLbcb,
  485. &FileOffset,
  486. &ContainsLastEntry,
  487. &LfsRestart,
  488. &UseTailCopy,
  489. &IoBlocks );
  490. Length = IoBlocks * (ULONG) Lfcb->LogPageSize;
  491. if (UseTailCopy) {
  492. TargetLbcb = Lfcb->ActiveTail;
  493. Lfcb->ActiveTail = Lfcb->PrevTail;
  494. Lfcb->PrevTail = TargetLbcb;
  495. FileOffset = TargetLbcb->FileOffset;
  496. } else {
  497. TargetLbcb = FirstLbcb;
  498. }
  499. //
  500. // Give up the Lfcb unless we are looking at an active page.
  501. //
  502. if (!UseTailCopy) {
  503. LfsReleaseLfcb( Lfcb );
  504. }
  505. //
  506. // If this I/O involves the Lfs restart area, write it to the
  507. // cache pages.
  508. //
  509. if (LfsRestart) {
  510. PLFS_RESTART_PAGE_HEADER RestartPage;
  511. ASSERT( !UseTailCopy && IoBlocks == 1);
  512. //
  513. // Build the partial mdl to describe this lfs restart page from the permanently
  514. // mapped piece of the log
  515. //
  516. RestartPage = Add2Ptr( Lfcb->LogHeadBuffer, FileOffset, PLFS_RESTART_PAGE_HEADER );
  517. IoBuildPartialMdl( Lfcb->LogHeadMdl, Lfcb->LogHeadPartialMdl, RestartPage, (ULONG)Lfcb->LogPageSize );
  518. //
  519. // Initialize the restart page header.
  520. //
  521. Signature = (PULONG) &RestartPage->MultiSectorHeader.Signature;
  522. *Signature = LFS_SIGNATURE_RESTART_PAGE_ULONG;
  523. RestartPage->ChkDskLsn = LfsLi0;
  524. RestartPage->MultiSectorHeader.UpdateSequenceArrayOffset
  525. = Lfcb->RestartUsaOffset;
  526. RestartPage->MultiSectorHeader.UpdateSequenceArraySize
  527. = Lfcb->UsaArraySize;
  528. //
  529. // Maintain the illusion that all systems have log page == system page
  530. // on disk so we can migrate disks between different platforms
  531. //
  532. RestartPage->SystemPageSize = (ULONG)Lfcb->LogPageSize;
  533. RestartPage->LogPageSize = (ULONG)Lfcb->LogPageSize;
  534. RestartPage->RestartOffset = (USHORT) Lfcb->RestartDataOffset;
  535. RestartPage->MajorVersion = Lfcb->MajorVersion;
  536. RestartPage->MinorVersion = Lfcb->MinorVersion;
  537. //
  538. // If the Lfcb indicates that the file has wrapped, then clear the
  539. // first pass flag in the restart area.
  540. //
  541. if (FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED )) {
  542. ClearFlag( ((PLFS_RESTART_AREA) FirstLbcb->PageHeader)->Flags, RESTART_SINGLE_PAGE_IO );
  543. SetFlag( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO );
  544. }
  545. //
  546. // Write the page header into the page and mark the page dirty.
  547. //
  548. RtlCopyMemory( Add2Ptr( RestartPage, Lfcb->RestartDataOffset, PVOID ),
  549. FirstLbcb->PageHeader,
  550. (ULONG)FirstLbcb->Length );
  551. LastLsn = FirstLbcb->LastLsn;
  552. ValidLastLsn = TRUE;
  553. #ifdef LFS_CLUSTER_CHECK
  554. //
  555. // Update the Lsn range on the disk.
  556. //
  557. *(Add2Ptr( RestartPage, 0xe00 - sizeof( ULONG ), PULONG )) = Lfcb->LsnRangeIndex + 1;
  558. *(Add2Ptr( RestartPage, 0xe00 + (sizeof( LSN ) * Lfcb->LsnRangeIndex * 2), PLSN )) = Lfcb->LsnAtMount;
  559. *(Add2Ptr( RestartPage, 0xe00 + (sizeof( LSN ) * (Lfcb->LsnRangeIndex * 2 + 1)), PLSN )) = Lfcb->LastFlushedLsn;
  560. #endif
  561. //
  562. // Use a system page size as the length we need to flush.
  563. //
  564. Length = (ULONG)Lfcb->LogPageSize;
  565. //
  566. // Otherwise these are log record pages
  567. //
  568. } else {
  569. PLFS_RECORD_PAGE_HEADER RecordPageHeader;
  570. ULONG Count;
  571. //
  572. // Mark the last Lsn fields for the page headers and each
  573. // page's position in the transfer. Also unpin all of the
  574. // log pages.
  575. //
  576. ASSERT( UseTailCopy || FirstLbcb->FileOffset == FileOffset );
  577. ThisLbcb = FirstLbcb;
  578. for (Count=1; Count <= IoBlocks; Count++) {
  579. if (UseTailCopy) {
  580. //
  581. // Build the partial mdl to describe the tail (pin/pong) page
  582. // from the permanently mapped section of the log
  583. //
  584. RecordPageHeader = Add2Ptr( Lfcb->LogHeadBuffer, TargetLbcb->FileOffset, PLFS_RECORD_PAGE_HEADER );
  585. IoBuildPartialMdl( Lfcb->LogHeadMdl, Lfcb->LogHeadPartialMdl, RecordPageHeader, (ULONG)Lfcb->LogPageSize );
  586. //
  587. // Store the file offset of the real page in the header.
  588. // Also set the flag indicating the page is a tail copy.
  589. //
  590. RtlCopyMemory( RecordPageHeader,
  591. ThisLbcb->PageHeader,
  592. (ULONG)Lfcb->LogPageSize );
  593. RecordPageHeader->Copy.FileOffset = ThisLbcb->FileOffset;
  594. } else {
  595. PUSHORT SeqNumber;
  596. RecordPageHeader = (PLFS_RECORD_PAGE_HEADER) ThisLbcb->PageHeader;
  597. //
  598. // If the sequence number is zero then this is probably a
  599. // page of zeroes produced by the cache manager. In order
  600. // to insure that we don't have the same sequence number
  601. // on each page we will seed the sequence number.
  602. //
  603. SeqNumber = Add2Ptr( RecordPageHeader,
  604. Lfcb->LogRecordUsaOffset,
  605. PUSHORT );
  606. if (*SeqNumber == 0) {
  607. *SeqNumber = LfsUsaSeqNumber;
  608. LfsUsaSeqNumber += 1;
  609. }
  610. }
  611. //
  612. // We update all of fields as yet not updated.
  613. //
  614. RecordPageHeader->PagePosition = (USHORT) Count;
  615. RecordPageHeader->PageCount = (USHORT) IoBlocks;
  616. //
  617. // We set up the update sequence array for this structure.
  618. //
  619. Signature = (PULONG) &RecordPageHeader->MultiSectorHeader.Signature;
  620. *Signature = LFS_SIGNATURE_RECORD_PAGE_ULONG;
  621. RecordPageHeader->MultiSectorHeader.UpdateSequenceArrayOffset = Lfcb->LogRecordUsaOffset;
  622. RecordPageHeader->MultiSectorHeader.UpdateSequenceArraySize = Lfcb->UsaArraySize;
  623. //
  624. // Make sure the modified bit gets set in the pfn database. The
  625. // cache manager should do this even for files we told him not to
  626. // lazy write.
  627. //
  628. if (!UseTailCopy) {
  629. CcSetDirtyPinnedData( ThisLbcb->LogPageBcb, NULL );
  630. //
  631. // We unpin any buffers pinned on this page.
  632. //
  633. CcUnpinDataForThread( ThisLbcb->LogPageBcb, ThisLbcb->ResourceThread );
  634. ThisLbcb->LogPageBcb = NULL;
  635. }
  636. //
  637. // Remember the last lsn and its length if this is the final
  638. // page of an Lsn.
  639. //
  640. if (FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) {
  641. LastLsn = ThisLbcb->LastEndLsn;
  642. ValidLastLsn = TRUE;
  643. }
  644. //
  645. // Otherwise move to the next entry.
  646. //
  647. ThisLbcb = CONTAINING_RECORD( ThisLbcb->WorkqueLinks.Flink,
  648. LBCB,
  649. WorkqueLinks );
  650. }
  651. }
  652. //
  653. // Remember the range we are flushing and find the second half of a page
  654. // if necessary.
  655. //
  656. Lfcb->UserWriteData->FileOffset = FileOffset;
  657. Lfcb->UserWriteData->Length = Length;
  658. //
  659. // For the loghead pages (2 lfs restart pages and 2 ping pong pages
  660. // explicitly flush them down using the partial mdl we built
  661. // The regular cc logic w/ UserWriteData that pares the write down
  662. // to the correct offsets works here as well
  663. //
  664. if (LfsRestart || UseTailCopy) {
  665. NTSTATUS Status;
  666. ASSERT( IoBlocks == 1 );
  667. //
  668. // We can release the lfcb now that we've finished using the active page
  669. //
  670. if (UseTailCopy) {
  671. LfsReleaseLfcb( Lfcb );
  672. }
  673. Status = IoSynchronousPageWrite( Lfcb->FileObject,
  674. Lfcb->LogHeadPartialMdl,
  675. (PLARGE_INTEGER)&FileOffset,
  676. &Event,
  677. &Iosb );
  678. if (Status == STATUS_PENDING) {
  679. Status = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
  680. }
  681. if (!NT_SUCCESS( Status ) || !NT_SUCCESS( Iosb.Status)) {
  682. //
  683. // Record status if we haven't failed already and continue on
  684. //
  685. if (NT_SUCCESS( FailedFlushStatus )) {
  686. if (!NT_SUCCESS( Status )) {
  687. FailedFlushStatus = Status;
  688. } else if (!NT_SUCCESS( Iosb.Status )) {
  689. FailedFlushStatus = Iosb.Status;
  690. }
  691. FailedFlushOffset = FileOffset;
  692. #ifdef LFS_CLUSTER_CHECK
  693. //
  694. // Remember this to figure out naggin cluster problems.
  695. //
  696. if ((Status == STATUS_DEVICE_OFF_LINE) ||
  697. (Iosb.Status == STATUS_DEVICE_OFF_LINE)) {
  698. SetFlag( Lfcb->Flags, LFCB_DEVICE_OFFLINE_SEEN );
  699. }
  700. //
  701. // Remember all errors.
  702. //
  703. SetFlag( Lfcb->Flags, LFCB_FLUSH_FAILED );
  704. #endif
  705. }
  706. #ifdef LFS_CLUSTER_CHECK
  707. } else if (Iosb.Information != 0) {
  708. //
  709. // Once OFFLINE, always OFFLINE.
  710. //
  711. ASSERT( !FlagOn( Lfcb->Flags, LFCB_DEVICE_OFFLINE_SEEN ));
  712. //
  713. // Catch the first write after a failed IO.
  714. //
  715. if (LfsTestBreakOnAnyError &&
  716. FlagOn( Lfcb->Flags, LFCB_FLUSH_FAILED )) {
  717. ASSERT( !LfsTestBreakOnAnyError ||
  718. !FlagOn( Lfcb->Flags, LFCB_FLUSH_FAILED ));
  719. ClearFlag( Lfcb->Flags, LFCB_FLUSH_FAILED );
  720. }
  721. #endif
  722. }
  723. } else {
  724. //
  725. // This is a normal log page so flush through the cache
  726. //
  727. CcFlushCache( Lfcb->FileObject->SectionObjectPointer,
  728. (PLARGE_INTEGER)&FileOffset,
  729. Length,
  730. &Iosb );
  731. if (!NT_SUCCESS( Iosb.Status )) {
  732. LONG BytesRemaining = (LONG) Length;
  733. //
  734. // If we get an error then try each individual page.
  735. //
  736. while (BytesRemaining > 0) {
  737. //
  738. // Remember the range we are flushing and find the second half of a page
  739. // if necessary.
  740. //
  741. ASSERT( Length >= Lfcb->LogPageSize );
  742. Lfcb->UserWriteData->FileOffset = FileOffset;
  743. Lfcb->UserWriteData->Length = (ULONG)Lfcb->LogPageSize;
  744. CcFlushCache( Lfcb->FileObject->SectionObjectPointer,
  745. (PLARGE_INTEGER)&FileOffset,
  746. (ULONG)Lfcb->LogPageSize,
  747. &Iosb );
  748. if (!NT_SUCCESS( Iosb.Status )) {
  749. if (NT_SUCCESS( FailedFlushStatus )) {
  750. FailedFlushStatus = Iosb.Status;
  751. FailedFlushOffset = FileOffset;
  752. #ifdef LFS_CLUSTER_CHECK
  753. //
  754. // Remember this to figure out naggin cluster problems.
  755. //
  756. if (FailedFlushStatus == STATUS_DEVICE_OFF_LINE) {
  757. SetFlag( Lfcb->Flags, LFCB_DEVICE_OFFLINE_SEEN );
  758. }
  759. //
  760. // Remember all errors.
  761. //
  762. SetFlag( Lfcb->Flags, LFCB_FLUSH_FAILED );
  763. #endif
  764. }
  765. #ifdef LFS_CLUSTER_CHECK
  766. } else if (Iosb.Information != 0) {
  767. //
  768. // Once OFFLINE, always OFFLINE.
  769. //
  770. ASSERT( !FlagOn( Lfcb->Flags, LFCB_DEVICE_OFFLINE_SEEN ));
  771. //
  772. // Catch the first write after a failed IO.
  773. //
  774. if (LfsTestBreakOnAnyError &&
  775. FlagOn( Lfcb->Flags, LFCB_FLUSH_FAILED )) {
  776. ASSERT( !LfsTestBreakOnAnyError ||
  777. !FlagOn( Lfcb->Flags, LFCB_FLUSH_FAILED ));
  778. ClearFlag( Lfcb->Flags, LFCB_FLUSH_FAILED );
  779. }
  780. #endif
  781. }
  782. BytesRemaining -= (LONG)Lfcb->LogPageSize;
  783. FileOffset = FileOffset + Lfcb->LogPageSize;
  784. }
  785. }
  786. }
  787. //
  788. // Reacquire the Lfcb at the original state to modify fields within it
  789. //
  790. if (OwnedExclusive) {
  791. LfsAcquireLfcbExclusive( Lfcb );
  792. } else {
  793. LfsAcquireLfcbShared( Lfcb );
  794. }
  795. //
  796. // Update the last flushed Lsn value if its valid
  797. //
  798. if (ValidLastLsn) {
  799. //
  800. // Acquire synchronization to change the field
  801. //
  802. ExAcquireFastMutexUnsafe( &Lfcb->Sync->Mutex );
  803. if (LfsRestart) {
  804. Lfcb->LastFlushedRestartLsn = LastLsn;
  805. } else {
  806. Lfcb->LastFlushedLsn = LastLsn;
  807. }
  808. //
  809. // And also wake any waiters who have been satisfied
  810. //
  811. LfsWaiter = (PLFS_WAITER)Lfcb->WaiterList.Flink;
  812. while ((PVOID)LfsWaiter != &Lfcb->WaiterList) {
  813. if (LastLsn.QuadPart > LfsWaiter->Lsn.QuadPart ) {
  814. RemoveEntryList( &LfsWaiter->Waiters );
  815. KeSetEvent( &LfsWaiter->Event, 0, FALSE );
  816. LfsWaiter = (PLFS_WAITER)Lfcb->WaiterList.Flink;
  817. } else {
  818. break;
  819. }
  820. }
  821. ExReleaseFastMutexUnsafe( &Lfcb->Sync->Mutex );
  822. }
  823. if (LfsRestart) {
  824. //
  825. // Clear any neccessary flags on a successful operation.
  826. //
  827. if (NT_SUCCESS( FailedFlushStatus )) {
  828. ClearFlag( Lfcb->Flags, NewLfcbFlags );
  829. NewLfcbFlags = 0;
  830. }
  831. //
  832. // If this is the first write of a restart area and we have
  833. // updated the LogOpenCount then update the field in the Lfcb.
  834. //
  835. if (NT_SUCCESS( Iosb.Status ) &&
  836. (Lfcb->CurrentOpenLogCount != ((PLFS_RESTART_AREA) FirstLbcb->PageHeader)->RestartOpenLogCount)) {
  837. Lfcb->CurrentOpenLogCount = ((PLFS_RESTART_AREA) FirstLbcb->PageHeader)->RestartOpenLogCount;
  838. }
  839. }
  840. //
  841. // Walk through all the Lbcb's we flushed, deallocating the Lbcbs.
  842. //
  843. if (!UseTailCopy) {
  844. PLBCB TempLbcb;
  845. for (ThisLbcb = FirstLbcb; IoBlocks > 0; IoBlocks -= 1) {
  846. //
  847. // Remember the next entry on the list.
  848. //
  849. TempLbcb = CONTAINING_RECORD( ThisLbcb->WorkqueLinks.Flink,
  850. LBCB,
  851. WorkqueLinks );
  852. //
  853. // Remove it from the LbcbWorkque queue.
  854. //
  855. RemoveEntryList( &ThisLbcb->WorkqueLinks );
  856. //
  857. // Deallocate the structure.
  858. //
  859. LfsDeallocateLbcb( Lfcb, ThisLbcb );
  860. ThisLbcb = TempLbcb;
  861. }
  862. }
  863. //
  864. // Remember the starting Lbcb for the next I/O.
  865. //
  866. FirstLbcb = NextLbcb;
  867. }
  868. } finally {
  869. PLFCB_SYNC Sync = Lfcb->Sync;
  870. DebugUnwind( LfsFlushLfcb );
  871. //
  872. // I expect that we must at least own the lfcb shared at this point to
  873. // modify its fields
  874. //
  875. ASSERT( ExIsResourceAcquiredSharedLite( &Sync->Resource ) );
  876. //
  877. // Show that there is no Io in progress. Preset the event but with wait == true sinc
  878. // ownership of the event is indicated but the LfsIoState.
  879. // This leaves us with the dispatcher db locked so the whole operation is atomic
  880. // until we call delay execution thread
  881. //
  882. ExAcquireFastMutexUnsafe( &Lfcb->Sync->Mutex );
  883. Lfcb->LfsIoThread = 0;
  884. Sync->LfsIoState = LfsNoIoInProgress;
  885. //
  886. // Wake up any waiters that have been satified + 1 additional if there is one
  887. // who can continue flushing
  888. //
  889. LfsWaiter = (PLFS_WAITER)Lfcb->WaiterList.Flink;
  890. while ((PVOID)LfsWaiter != &Lfcb->WaiterList ) {
  891. LastLsn.QuadPart = max( Lfcb->LastFlushedLsn.QuadPart, Lfcb->LastFlushedRestartLsn.QuadPart );
  892. if (LastLsn.QuadPart >= LfsWaiter->Lsn.QuadPart) {
  893. RemoveEntryList( &LfsWaiter->Waiters );
  894. KeSetEvent( &LfsWaiter->Event, 0, FALSE );
  895. } else {
  896. RemoveEntryList( &LfsWaiter->Waiters );
  897. KeSetEvent( &LfsWaiter->Event, 0, FALSE );
  898. break;
  899. }
  900. LfsWaiter = (PLFS_WAITER)Lfcb->WaiterList.Flink;
  901. }
  902. ExReleaseFastMutexUnsafe( &Lfcb->Sync->Mutex );
  903. //
  904. // Make sure we didn't leave any pages pinned.
  905. //
  906. if (PageBcb != NULL) {
  907. CcUnpinData( PageBcb );
  908. }
  909. DebugTrace( -1, Dbg, "LfsFlushLfcb: Exit\n", 0 );
  910. }
  911. //
  912. // If the Io failed at some point, we log the error in the eventlog if possible
  913. // and note it in the lfs restart area
  914. //
  915. if (!NT_SUCCESS( FailedFlushStatus )) {
  916. PIO_ERROR_LOG_PACKET ErrorLogEntry;
  917. //
  918. // Note failure in restart area - acquire synchronization to access lastflushedlsn
  919. //
  920. ExAcquireFastMutexUnsafe( &Lfcb->Sync->Mutex );
  921. Lfcb->RestartArea->LastFailedFlushOffset = FailedFlushOffset;
  922. Lfcb->RestartArea->LastFailedFlushStatus = FailedFlushStatus;
  923. Lfcb->RestartArea->LastFailedFlushLsn = Lfcb->LastFlushedLsn;
  924. ExReleaseFastMutexUnsafe( &Lfcb->Sync->Mutex );
  925. if (Lfcb->ErrorLogPacket != NULL) {
  926. ErrorLogEntry = Lfcb->ErrorLogPacket;
  927. Lfcb->ErrorLogPacket = NULL;
  928. } else {
  929. ErrorLogEntry = IoAllocateErrorLogEntry( Lfcb->FileObject->DeviceObject, ERROR_LOG_MAXIMUM_SIZE );
  930. }
  931. if (ErrorLogEntry != NULL) {
  932. ErrorLogEntry->EventCategory = ELF_CATEGORY_DISK;
  933. ErrorLogEntry->ErrorCode = IO_WARNING_LOG_FLUSH_FAILED;
  934. ErrorLogEntry->FinalStatus = FailedFlushStatus;
  935. IoWriteErrorLogEntry( ErrorLogEntry );
  936. }
  937. }
  938. //
  939. // Try to preallocate another log packet if we don't have one already
  940. //
  941. if (Lfcb->ErrorLogPacket == NULL) {
  942. Lfcb->ErrorLogPacket = IoAllocateErrorLogEntry( Lfcb->FileObject->DeviceObject, ERROR_LOG_MAXIMUM_SIZE );
  943. }
  944. return;
  945. }
  946. BOOLEAN
  947. LfsReadRestart (
  948. IN PLFCB Lfcb,
  949. IN LONGLONG FileSize,
  950. IN BOOLEAN FirstRestart,
  951. OUT PLONGLONG RestartPageOffset,
  952. OUT PLFS_RESTART_PAGE_HEADER *RestartPage,
  953. OUT PBCB *RestartPageBcb,
  954. OUT PBOOLEAN ChkdskWasRun,
  955. OUT PBOOLEAN ValidPage,
  956. OUT PBOOLEAN UninitializedFile,
  957. OUT PBOOLEAN LogPacked,
  958. OUT PLSN LastLsn
  959. )
  960. /*++
  961. Routine Description:
  962. This routine will walk through 512 blocks of the file looking for a
  963. valid restart page header. It will stop the first time we find
  964. a valid page header.
  965. Arguments:
  966. Lfcb - This is the Lfcb for the log file.
  967. FileSize - Size in bytes for the log file.
  968. FirstRestart - Indicates if we are looking for the first valid
  969. restart area.
  970. RestartPageOffset - This is the location to store the offset in the
  971. file where the log page was found.
  972. RestartPage - This is the location to store the address of the
  973. pinned restart page.
  974. RestartPageBcb - This is the location to store the Bcb for this
  975. cache pin operation.
  976. ChkdskWasRun - Address to store whether checkdisk was run on this volume.
  977. ValidPage - Address to store whether there was valid data on this page.
  978. UninitializedFile - Address to store whether this is an uninitialized
  979. log file. Return value only valid if for the first restart area.
  980. LogPacked - Address to store whether the log file is packed.
  981. LastLsn - Address to store the last Lsn for this restart page. It will be the
  982. chkdsk value if checkdisk was run. Otherwise it is the LastFlushedLsn
  983. for this restart page.
  984. Return Value:
  985. BOOLEAN - TRUE if a restart area was found, FALSE otherwise.
  986. --*/
  987. {
  988. ULONG FileOffsetIncrement;
  989. LONGLONG FileOffset;
  990. PLFS_RESTART_AREA RestartArea;
  991. NTSTATUS Status;
  992. PLFS_RESTART_PAGE_HEADER ThisPage;
  993. PBCB ThisPageBcb = NULL;
  994. BOOLEAN FoundRestart = FALSE;
  995. PAGED_CODE();
  996. DebugTrace( +1, Dbg, "LfsReadRestart: Entered\n", 0 );
  997. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  998. //
  999. // Use a try-finally to facilitate cleanup.
  1000. //
  1001. *UninitializedFile = TRUE;
  1002. *ValidPage = FALSE;
  1003. *ChkdskWasRun = FALSE;
  1004. *LogPacked = FALSE;
  1005. try {
  1006. //
  1007. // Determine which restart area we are looking for.
  1008. //
  1009. if (FirstRestart) {
  1010. FileOffset = 0;
  1011. FileOffsetIncrement = SEQUENCE_NUMBER_STRIDE;
  1012. } else {
  1013. FileOffset = SEQUENCE_NUMBER_STRIDE;
  1014. FileOffsetIncrement = 0;
  1015. }
  1016. //
  1017. // We loop up to 16 pages until we succeed, pin a log record page
  1018. // or exhaust the number of possible tries.
  1019. //
  1020. while ( FileOffset < min( FileSize, 16 * PAGE_SIZE )) {
  1021. ULONG Signature;
  1022. BOOLEAN UsaError;
  1023. if (ThisPageBcb != NULL) {
  1024. CcUnpinData( ThisPageBcb );
  1025. ThisPageBcb = NULL;
  1026. }
  1027. //
  1028. // Attempt to pin a page header at the current offset.
  1029. //
  1030. Status = LfsPinOrMapData( Lfcb,
  1031. FileOffset,
  1032. SEQUENCE_NUMBER_STRIDE,
  1033. TRUE,
  1034. TRUE,
  1035. TRUE,
  1036. &UsaError,
  1037. (PVOID *)&ThisPage,
  1038. &ThisPageBcb );
  1039. //
  1040. //
  1041. // If we succeeded, we look at the 4 byte signature.
  1042. //
  1043. if (NT_SUCCESS( Status )) {
  1044. Signature = *((PULONG) &ThisPage->MultiSectorHeader.Signature);
  1045. //
  1046. // If the signature is a log record page, we will exit.
  1047. //
  1048. if (Signature == LFS_SIGNATURE_RECORD_PAGE_ULONG) {
  1049. *UninitializedFile = FALSE;
  1050. break;
  1051. }
  1052. //
  1053. // Continue analyzing the page if the signature is chkdsk or
  1054. // a restart page.
  1055. //
  1056. if (Signature == LFS_SIGNATURE_MODIFIED_ULONG ||
  1057. Signature == LFS_SIGNATURE_RESTART_PAGE_ULONG) {
  1058. *UninitializedFile = FALSE;
  1059. //
  1060. // Remember where we found this page.
  1061. //
  1062. *RestartPageOffset = FileOffset;
  1063. //
  1064. // Let's check the restart area if this is a valid page.
  1065. //
  1066. if (LfsIsRestartPageHeaderValid( FileOffset,
  1067. ThisPage,
  1068. LogPacked )
  1069. && LfsIsRestartAreaValid( ThisPage, *LogPacked )) {
  1070. //
  1071. // We have a valid restart page header and restart area.
  1072. // If chkdsk was run or we have no clients then
  1073. // we have no more checking to do.
  1074. //
  1075. RestartArea = Add2Ptr( ThisPage,
  1076. ThisPage->RestartOffset,
  1077. PLFS_RESTART_AREA );
  1078. if (Signature == LFS_SIGNATURE_RESTART_PAGE_ULONG
  1079. && RestartArea->ClientInUseList != LFS_NO_CLIENT) {
  1080. //
  1081. // Pin the entire restart area if we didn't have an earlier
  1082. //
  1083. CcUnpinData( ThisPageBcb );
  1084. ThisPageBcb = NULL;
  1085. Status = LfsPinOrMapData( Lfcb,
  1086. FileOffset,
  1087. ThisPage->SystemPageSize,
  1088. TRUE,
  1089. TRUE,
  1090. TRUE,
  1091. &UsaError,
  1092. (PVOID *)&ThisPage,
  1093. &ThisPageBcb );
  1094. if (NT_SUCCESS( Status )
  1095. && LfsIsClientAreaValid( ThisPage, *LogPacked, UsaError )) {
  1096. *ValidPage = TRUE;
  1097. RestartArea = Add2Ptr( ThisPage,
  1098. ThisPage->RestartOffset,
  1099. PLFS_RESTART_AREA );
  1100. }
  1101. } else {
  1102. *ValidPage = TRUE;
  1103. }
  1104. }
  1105. //
  1106. // If chkdsk was run then update the caller's values and return.
  1107. //
  1108. if (Signature == LFS_SIGNATURE_MODIFIED_ULONG) {
  1109. *ChkdskWasRun = TRUE;
  1110. *LastLsn = ThisPage->ChkDskLsn;
  1111. FoundRestart = TRUE;
  1112. *RestartPageBcb = ThisPageBcb;
  1113. *RestartPage = ThisPage;
  1114. ThisPageBcb = NULL;
  1115. break;
  1116. }
  1117. //
  1118. // If we have a valid page then copy the values we need from it.
  1119. //
  1120. if (*ValidPage) {
  1121. *LastLsn = RestartArea->CurrentLsn;
  1122. FoundRestart = TRUE;
  1123. *RestartPageBcb = ThisPageBcb;
  1124. *RestartPage = ThisPage;
  1125. ThisPageBcb = NULL;
  1126. break;
  1127. }
  1128. //
  1129. // Remember if the signature does not indicate uninitialized file.
  1130. //
  1131. } else if (Signature != LFS_SIGNATURE_UNINITIALIZED_ULONG) {
  1132. *UninitializedFile = FALSE;
  1133. }
  1134. }
  1135. //
  1136. // Move to the next possible log page.
  1137. //
  1138. FileOffset = FileOffset << 1;
  1139. (ULONG)FileOffset += FileOffsetIncrement;
  1140. FileOffsetIncrement = 0;
  1141. }
  1142. } finally {
  1143. DebugUnwind( LfsReadRestart );
  1144. //
  1145. // Unpin the log pages if pinned.
  1146. //
  1147. if (ThisPageBcb != NULL) {
  1148. CcUnpinData( ThisPageBcb );
  1149. }
  1150. DebugTrace( 0, Dbg, "RestartPageAddress (Low) -> %08lx\n", RestartPageAddress->LowPart );
  1151. DebugTrace( 0, Dbg, "RestartPageAddress (High) -> %08lx\n", RestartPageAddress->HighPart );
  1152. DebugTrace( 0, Dbg, "FirstRestartPage -> %08lx\n", *FirstRestartPage );
  1153. DebugTrace( -1, Dbg, "LfsReadRestart: Exit\n", 0 );
  1154. }
  1155. return FoundRestart;
  1156. }
  1157. //
  1158. // Local support routine
  1159. //
  1160. BOOLEAN
  1161. LfsIsRestartPageHeaderValid (
  1162. IN LONGLONG FileOffset,
  1163. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  1164. OUT PBOOLEAN LogPacked
  1165. )
  1166. /*++
  1167. Routine Description:
  1168. This routine is called to verify that the candidate for a restart page
  1169. has no corrupt values in the page header. It verifies that the restart and
  1170. system page size have only one bit set and are at least the value of
  1171. the update sequence array stride.
  1172. Arguments:
  1173. FileOffset - This is the offset in the file of the restart area to examine.
  1174. If this offset is not 0, then it should match the system page size.
  1175. PageHeader - This is the page to examine.
  1176. LogPacked - Address to store whether the log file is packed.
  1177. Return Value:
  1178. BOOLEAN - TRUE if there is no corruption in the pool header values.
  1179. FALSE otherwise.
  1180. --*/
  1181. {
  1182. ULONG SystemPage;
  1183. ULONG LogPageSize;
  1184. ULONG Mask;
  1185. ULONG BitCount;
  1186. USHORT EndOfUsa;
  1187. PAGED_CODE();
  1188. *LogPacked = FALSE;
  1189. //
  1190. // Copy the values from the page header into the local variables.
  1191. //
  1192. SystemPage = PageHeader->SystemPageSize;
  1193. LogPageSize = PageHeader->LogPageSize;
  1194. //
  1195. // The system page and log page sizes must be greater or equal to the
  1196. // update sequence stride.
  1197. //
  1198. if (SystemPage < SEQUENCE_NUMBER_STRIDE
  1199. || LogPageSize < SEQUENCE_NUMBER_STRIDE) {
  1200. return FALSE;
  1201. }
  1202. //
  1203. // Now we check that the Log page and system page are multiples of two.
  1204. // They should only have a single bit set.
  1205. //
  1206. for (Mask = 1, BitCount = 0; Mask != 0; Mask = Mask << 1) {
  1207. if (Mask & LogPageSize) {
  1208. BitCount += 1;
  1209. }
  1210. }
  1211. //
  1212. // If the bit count isn't 1, return false.
  1213. //
  1214. if (BitCount != 1) {
  1215. return FALSE;
  1216. }
  1217. //
  1218. // Now do the system page size.
  1219. //
  1220. for (Mask = 1, BitCount = 0; Mask != 0; Mask = Mask << 1) {
  1221. if (Mask & SystemPage) {
  1222. BitCount += 1;
  1223. }
  1224. }
  1225. //
  1226. // If the bit count isn't 1, return false.
  1227. //
  1228. if (BitCount != 1) {
  1229. return FALSE;
  1230. }
  1231. //
  1232. // Check that if the file offset isn't 0, it is the system page size.
  1233. //
  1234. if (( FileOffset != 0 )
  1235. && ((ULONG)FileOffset != SystemPage)) {
  1236. return FALSE;
  1237. }
  1238. //
  1239. // We only support major version numbers 0.x and 1.x
  1240. //
  1241. // Version number beyond 1.0 mean the log file is packed.
  1242. //
  1243. if (PageHeader->MajorVersion != 0
  1244. && PageHeader->MajorVersion != 1) {
  1245. return FALSE;
  1246. }
  1247. //
  1248. // Check that the restart area offset is within the system page and that
  1249. // the restart length field will fit within the system page size.
  1250. //
  1251. if (QuadAlign( PageHeader->RestartOffset ) != PageHeader->RestartOffset
  1252. || PageHeader->RestartOffset > (USHORT) PageHeader->SystemPageSize) {
  1253. return FALSE;
  1254. }
  1255. //
  1256. // Check that the restart offset will lie beyond the Usa Array for this page.
  1257. //
  1258. EndOfUsa = (USHORT) (UpdateSequenceArraySize( PageHeader->SystemPageSize )
  1259. * sizeof( UPDATE_SEQUENCE_NUMBER ));
  1260. EndOfUsa += PageHeader->MultiSectorHeader.UpdateSequenceArrayOffset;
  1261. if (PageHeader->RestartOffset < EndOfUsa) {
  1262. return FALSE;
  1263. }
  1264. //
  1265. // Check if the log pages are packed.
  1266. //
  1267. if (PageHeader->MajorVersion == 1
  1268. && PageHeader->MinorVersion > 0) {
  1269. *LogPacked = TRUE;
  1270. }
  1271. //
  1272. // Otherwise the page header is valid.
  1273. //
  1274. return TRUE;
  1275. }
  1276. //
  1277. // Local support routine
  1278. //
  1279. BOOLEAN
  1280. LfsIsRestartAreaValid (
  1281. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  1282. IN BOOLEAN LogPacked
  1283. )
  1284. /*++
  1285. Routine Description:
  1286. This routine is called to verify that the restart area attached to the
  1287. log page header is valid. The restart values must be contained within
  1288. the first Usa stride of the file. This is so we can restart successfully
  1289. after chkdsk.
  1290. Arguments:
  1291. PageHeader - This is the page to examine.
  1292. LogPacked - Indicates if the log file is packed.
  1293. Return Value:
  1294. BOOLEAN - TRUE if there is no corruption in the restart area values.
  1295. FALSE otherwise.
  1296. --*/
  1297. {
  1298. PLFS_RESTART_AREA RestartArea;
  1299. ULONG OffsetInRestart;
  1300. ULONG SeqNumberBits;
  1301. LONGLONG FileSize;
  1302. PAGED_CODE();
  1303. //
  1304. // The basic part of the restart area must fit into the first stride of
  1305. // the page. This will allow chkdsk to work even if there are Usa errors.
  1306. //
  1307. OffsetInRestart = FIELD_OFFSET( LFS_RESTART_AREA, FileSize );
  1308. if ((PageHeader->RestartOffset + OffsetInRestart) > FIRST_STRIDE) {
  1309. return FALSE;
  1310. }
  1311. RestartArea = Add2Ptr( PageHeader, PageHeader->RestartOffset, PLFS_RESTART_AREA );
  1312. //
  1313. // Everything in the restart area except the actual client array must also
  1314. // be in the first stride. If the structure is packed, then we can use
  1315. // a field in the restart area for the client offset.
  1316. //
  1317. if (LogPacked) {
  1318. OffsetInRestart = RestartArea->ClientArrayOffset;
  1319. } else {
  1320. //
  1321. // We shouldn't see any of the older disks now.
  1322. //
  1323. OffsetInRestart = FIELD_OFFSET( LFS_OLD_RESTART_AREA, LogClientArray );
  1324. }
  1325. if (QuadAlign( OffsetInRestart ) != OffsetInRestart
  1326. || (PageHeader->RestartOffset + OffsetInRestart) > FIRST_STRIDE) {
  1327. return FALSE;
  1328. }
  1329. //
  1330. // The full size of the restart area must fit in the system page specified by
  1331. // the page header. We compute the size of the restart area by calculating
  1332. // the space needed by all clients. We also check the given size of the
  1333. // restart area.
  1334. //
  1335. OffsetInRestart += (RestartArea->LogClients * sizeof( LFS_CLIENT_RECORD ));
  1336. if (OffsetInRestart > PageHeader->SystemPageSize ) {
  1337. return FALSE;
  1338. }
  1339. //
  1340. // If the log is packed, then check the restart length field and whether
  1341. // the entire restart area is contained in that length.
  1342. //
  1343. if (LogPacked
  1344. && ((ULONG) (PageHeader->RestartOffset + RestartArea->RestartAreaLength) > PageHeader->SystemPageSize
  1345. || OffsetInRestart > RestartArea->RestartAreaLength)) {
  1346. return FALSE;
  1347. }
  1348. //
  1349. // As a final check make sure that the in use list and the free list are either
  1350. // empty or point to a valid client.
  1351. //
  1352. if ((RestartArea->ClientFreeList != LFS_NO_CLIENT
  1353. && RestartArea->ClientFreeList >= RestartArea->LogClients)
  1354. || (RestartArea->ClientInUseList != LFS_NO_CLIENT
  1355. && RestartArea->ClientInUseList >= RestartArea->LogClients)) {
  1356. return FALSE;
  1357. }
  1358. //
  1359. // Make sure the sequence number bits match the log file size.
  1360. //
  1361. FileSize = RestartArea->FileSize;
  1362. for (SeqNumberBits = 0;
  1363. ( FileSize != 0 );
  1364. SeqNumberBits += 1,
  1365. FileSize = ((ULONGLONG)(FileSize)) >> 1 ) {
  1366. }
  1367. SeqNumberBits = (sizeof( LSN ) * 8) + 3 - SeqNumberBits;
  1368. if (SeqNumberBits != RestartArea->SeqNumberBits) {
  1369. return FALSE;
  1370. }
  1371. //
  1372. // We will check the fields that apply only to a packed log file.
  1373. //
  1374. if (LogPacked) {
  1375. //
  1376. // The log page data offset and record header length must be
  1377. // quad-aligned.
  1378. //
  1379. if ((QuadAlign( RestartArea->LogPageDataOffset ) != RestartArea->LogPageDataOffset ) ||
  1380. (QuadAlign( RestartArea->RecordHeaderLength ) != RestartArea->RecordHeaderLength )) {
  1381. return FALSE;
  1382. }
  1383. }
  1384. return TRUE;
  1385. }
  1386. //
  1387. // Local support routine
  1388. //
  1389. BOOLEAN
  1390. LfsIsClientAreaValid (
  1391. IN PLFS_RESTART_PAGE_HEADER PageHeader,
  1392. IN BOOLEAN LogPacked,
  1393. IN BOOLEAN UsaError
  1394. )
  1395. /*++
  1396. Routine Description:
  1397. This routine is called to verify that the client array is valid. We test
  1398. if the client lists are correctly chained. If the entire restart area is
  1399. within the first Usa stride, we will ignore any Usa errors.
  1400. Arguments:
  1401. PageHeader - This is the page to examine.
  1402. LogPacked - Indicates if the log file is packed.
  1403. UsaError - There was a Usa error in reading the full page.
  1404. Return Value:
  1405. BOOLEAN - TRUE if there is no corruption in client array values.
  1406. FALSE otherwise.
  1407. --*/
  1408. {
  1409. PLFS_RESTART_AREA RestartArea;
  1410. USHORT ThisClientIndex;
  1411. USHORT ClientCount;
  1412. PLFS_CLIENT_RECORD ClientArray;
  1413. PLFS_CLIENT_RECORD ThisClient;
  1414. ULONG LoopCount;
  1415. PAGED_CODE();
  1416. RestartArea = Add2Ptr( PageHeader, PageHeader->RestartOffset, PLFS_RESTART_AREA );
  1417. //
  1418. // If there was a Usa error and the restart area isn't contained in the
  1419. // first Usa stride, then we have an error.
  1420. //
  1421. if (UsaError
  1422. && (RestartArea->RestartAreaLength + PageHeader->RestartOffset) > FIRST_STRIDE) {
  1423. return FALSE;
  1424. }
  1425. //
  1426. // Find the start of the client array.
  1427. //
  1428. if (LogPacked) {
  1429. ClientArray = Add2Ptr( RestartArea,
  1430. RestartArea->ClientArrayOffset,
  1431. PLFS_CLIENT_RECORD );
  1432. } else {
  1433. //
  1434. // Handle the case where the offset of the client array is fixed.
  1435. //
  1436. ClientArray = Add2Ptr( RestartArea,
  1437. FIELD_OFFSET( LFS_OLD_RESTART_AREA,
  1438. LogClientArray ),
  1439. PLFS_CLIENT_RECORD );
  1440. }
  1441. //
  1442. // Start with the free list. Check that all the clients are valid and
  1443. // that there isn't a cycle. Do the in-use list on the second pass.
  1444. //
  1445. ThisClientIndex = RestartArea->ClientFreeList;
  1446. LoopCount = 2;
  1447. do {
  1448. BOOLEAN FirstClient;
  1449. FirstClient = TRUE;
  1450. ClientCount = RestartArea->LogClients;
  1451. while (ThisClientIndex != LFS_NO_CLIENT) {
  1452. //
  1453. // If the client count is zero then we must have hit a loop.
  1454. // If the client index is greater or equal to the log client
  1455. // count then the list is corrupt.
  1456. //
  1457. if (ClientCount == 0
  1458. || ThisClientIndex >= RestartArea->LogClients) {
  1459. return FALSE;
  1460. }
  1461. ClientCount -= 1;
  1462. ThisClient = ClientArray + ThisClientIndex;
  1463. ThisClientIndex = ThisClient->NextClient;
  1464. //
  1465. // If this is the first client, then the previous value
  1466. // should indicate no client.
  1467. //
  1468. if (FirstClient) {
  1469. FirstClient = FALSE;
  1470. if (ThisClient->PrevClient != LFS_NO_CLIENT) {
  1471. return FALSE;
  1472. }
  1473. }
  1474. }
  1475. ThisClientIndex = RestartArea->ClientInUseList;
  1476. } while (--LoopCount);
  1477. //
  1478. // The client list is valid.
  1479. //
  1480. return TRUE;
  1481. }
  1482. //
  1483. // Local support routine.
  1484. //
  1485. VOID
  1486. LfsFindFirstIo (
  1487. IN PLFCB Lfcb,
  1488. IN LSN TargetLsn,
  1489. IN BOOLEAN RestartLsn,
  1490. IN PLBCB FirstLbcb,
  1491. OUT PLBCB *NextLbcb,
  1492. OUT PLONGLONG FileOffset,
  1493. OUT PBOOLEAN ContainsLastEntry,
  1494. OUT PBOOLEAN LfsRestart,
  1495. OUT PBOOLEAN UseTailCopy,
  1496. OUT PULONG IoBlocks
  1497. )
  1498. /*++
  1499. Routine Description:
  1500. This routine walks through the linked Lbcb's for a Lfcb and groups
  1501. as many of them as can be grouped into a single I/O transfer.
  1502. It updates pointers to indicate the file offset and length of the
  1503. transfer, whether the I/O includes a particular Lbcb, whether the
  1504. transfer is a restart area or a log record page and the number of
  1505. Lbcb's included in the transfer. We only flush a single log page
  1506. if we are passing through the file for the first time.
  1507. Arguments:
  1508. Lfcb - This is the file control block for the log file.
  1509. TargetLsn - This is the Lsn that the caller wants to have included in
  1510. the transfer.
  1511. RestartLsn - whether the target lsn is a restart lsn
  1512. FirstLbcb - This is the first Lbcb to look at in the list.
  1513. NextLbcb - This is the Lbcb to look at first on the next call to this
  1514. routine.
  1515. FileOffset - Supplies the address where we store the offset in the
  1516. log file of this transfer.
  1517. ContainsLastEntry - Supplies the address where we store whether this
  1518. I/O includes the 'LastEntry' Lbcb.
  1519. LfsRestart - Supplies the address where we store whether this transfer
  1520. is a Lfs restart area.
  1521. UseTailCopy - Supplies the address where we store whether we should
  1522. use of page for a copy of the end of the log file.
  1523. IoBlocks - Supplies the address where we store the number of Lbcb's
  1524. for this transfer.
  1525. Return Value:
  1526. None.
  1527. --*/
  1528. {
  1529. ULONG MaxFlushCount = LFS_MAX_FLUSH_COUNT;
  1530. PAGED_CODE();
  1531. DebugTrace( +1, Dbg, "LfsFindFirstIo: Entered\n", 0 );
  1532. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  1533. //
  1534. // Initialize the file offset, length and io blocks values.
  1535. // Also assume the last entry is not contained here.
  1536. // Also assume we have no next Lbcb.
  1537. //
  1538. *FileOffset = FirstLbcb->FileOffset;
  1539. *IoBlocks = 1;
  1540. *LfsRestart = FALSE;
  1541. *UseTailCopy = FALSE;
  1542. *NextLbcb = NULL;
  1543. //
  1544. // Check if we have found the desired Lsn in a non restart page. We reject the match
  1545. // if the Lbcb indicates that we should flush the copy first. Also if this is a restart
  1546. // page the target the target should be one and vice versa
  1547. //
  1548. if ((FirstLbcb->LastEndLsn.QuadPart >= TargetLsn.QuadPart) &&
  1549. !FlagOn( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY ) &&
  1550. ((!RestartLsn && !LfsLbcbIsRestart( FirstLbcb ) &&
  1551. (FlagOn( FirstLbcb->Flags, LOG_PAGE_LOG_RECORD_END ))) ||
  1552. (RestartLsn && LfsLbcbIsRestart( FirstLbcb )))) {
  1553. *ContainsLastEntry = TRUE;
  1554. } else {
  1555. *ContainsLastEntry = FALSE;
  1556. }
  1557. //
  1558. // Check if this is a restart block or if we are passing through the log
  1559. // file for the first time or if this Lbcb is still in the active queue.
  1560. // If not, then group as many of the Lbcb's as can be part of a single Io.
  1561. //
  1562. if (LfsLbcbIsRestart( FirstLbcb )) {
  1563. *LfsRestart = TRUE;
  1564. #ifdef BENL_DBG
  1565. //
  1566. // if one is ever on the active queue will use the code at the bottom to remove it
  1567. //
  1568. ASSERT( !FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ) );
  1569. #endif
  1570. } else if (FlagOn( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY)) {
  1571. //
  1572. // Only packed logs - reuse the tail which cause us to need to flush a copy
  1573. //
  1574. ASSERT( FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) );
  1575. //
  1576. // This is going to be a tail copy and we will restart with same lbcb
  1577. // and remove the flag so its normal next time
  1578. //
  1579. *UseTailCopy = TRUE;
  1580. *NextLbcb = FirstLbcb;
  1581. ClearFlag( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY );
  1582. } else if (FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ) &&
  1583. FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
  1584. //
  1585. // This is a normal tail copy since its still active
  1586. //
  1587. *UseTailCopy = TRUE;
  1588. } else if (FlagOn( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO )) {
  1589. PLBCB EndOfPageLbcb = NULL;
  1590. ULONG EndOfPageIoBlocks;
  1591. //
  1592. // If we are not supporting a packed log file and this Lbcb is from
  1593. // the active queue, we need to check that losing the tail of the page
  1594. // will not swallow up any of our reserved space.
  1595. //
  1596. if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
  1597. LONGLONG CurrentAvail;
  1598. LONGLONG UnusedBytes;
  1599. //
  1600. // ISSUE: old code only removed the selected element from the active queue
  1601. // we'll remove any we hit before the selected lsn
  1602. //
  1603. //
  1604. // Find the unused bytes.
  1605. //
  1606. UnusedBytes = 0;
  1607. LfsCurrentAvailSpace( Lfcb,
  1608. &CurrentAvail,
  1609. (PULONG)&UnusedBytes );
  1610. CurrentAvail = CurrentAvail - Lfcb->TotalUndoCommitment;
  1611. if (UnusedBytes > CurrentAvail) {
  1612. DebugTrace( -1, Dbg, "Have to preserve these bytes for possible aborts\n", 0 );
  1613. ExRaiseStatus( STATUS_LOG_FILE_FULL );
  1614. }
  1615. //
  1616. // We want to make sure we don't write any more data into this
  1617. // page. Remove this from the active queue.
  1618. //
  1619. RemoveEntryList( &FirstLbcb->ActiveLinks );
  1620. ClearFlag( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
  1621. }
  1622. //
  1623. // We loop until there are no more blocks or they aren't
  1624. // contiguous in the file or we have found an entry on the
  1625. // active queue or we found an entry where we want to explicitly
  1626. // flush a copy first.
  1627. //
  1628. while ((FirstLbcb->WorkqueLinks.Flink != &Lfcb->LbcbWorkque) &&
  1629. !FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE ) &&
  1630. *IoBlocks < MaxFlushCount) {
  1631. LONGLONG ExpectedFileOffset;
  1632. PLBCB TempLbcb;
  1633. //
  1634. // Get the next Lbcb.
  1635. //
  1636. TempLbcb = CONTAINING_RECORD( FirstLbcb->WorkqueLinks.Flink,
  1637. LBCB,
  1638. WorkqueLinks );
  1639. //
  1640. // Break out of the loop if the file offset is not the
  1641. // expected value or the next entry is on the active queue.
  1642. //
  1643. ExpectedFileOffset = FirstLbcb->FileOffset + Lfcb->LogPageSize;
  1644. //
  1645. // We want to stop at this point if the next Lbcb is not
  1646. // the expected offset or we are packing the log file and
  1647. // the next Lbcb is on the active queue or we want to write
  1648. // a copy of the data before this page goes out.
  1649. //
  1650. if ((TempLbcb->FileOffset != ExpectedFileOffset) ||
  1651. (FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) &&
  1652. FlagOn( TempLbcb->LbcbFlags, LBCB_FLUSH_COPY | LBCB_ON_ACTIVE_QUEUE))) {
  1653. //
  1654. // Use the Lbcb at the end of a page if possible.
  1655. //
  1656. if (EndOfPageLbcb != NULL) {
  1657. FirstLbcb = EndOfPageLbcb;
  1658. *IoBlocks = EndOfPageIoBlocks;
  1659. }
  1660. break;
  1661. }
  1662. //
  1663. // We can add this to our I/o. Increment the Io blocks
  1664. // and length of the transfer. Also check if this entry
  1665. // contains the Last Entry specified by the caller.
  1666. //
  1667. *IoBlocks += 1;
  1668. if (FlagOn( TempLbcb->Flags, LOG_PAGE_LOG_RECORD_END ) &&
  1669. (TempLbcb->LastEndLsn.QuadPart >= TargetLsn.QuadPart) &&
  1670. !RestartLsn) {
  1671. *ContainsLastEntry = TRUE;
  1672. }
  1673. //
  1674. // Check if this Lbcb is at the end of a system page.
  1675. //
  1676. if (*ContainsLastEntry &&
  1677. (PAGE_SIZE != (ULONG) Lfcb->LogPageSize) &&
  1678. !FlagOn( ((ULONG) TempLbcb->FileOffset + (ULONG) Lfcb->LogPageSize),
  1679. PAGE_SIZE - 1 )) {
  1680. EndOfPageLbcb = TempLbcb;
  1681. EndOfPageIoBlocks = *IoBlocks;
  1682. }
  1683. //
  1684. // Use this entry as the current entry.
  1685. //
  1686. FirstLbcb = TempLbcb;
  1687. }
  1688. }
  1689. //
  1690. // If the current Lbcb is on the active queue and we aren't using
  1691. // a tail copy, then remove this from the active queue. If this
  1692. // not our target and removing this will cause us to swallow up
  1693. // part of our reserved quota then back up one Lbcb.
  1694. //
  1695. if (!(*UseTailCopy) && FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {
  1696. if (Lfcb->CurrentAvailable < Lfcb->TotalUndoCommitment) {
  1697. //
  1698. // Move back one file record.
  1699. //
  1700. *IoBlocks -= 1;
  1701. *NextLbcb = FirstLbcb;
  1702. //
  1703. // Otherwise remove it from the active queue.
  1704. //
  1705. } else {
  1706. ClearFlag( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
  1707. RemoveEntryList( &FirstLbcb->ActiveLinks );
  1708. }
  1709. }
  1710. //
  1711. // If we haven't found the Lbcb to restart from we will just use the
  1712. // next Lbcb after the last one found.
  1713. //
  1714. if ((*NextLbcb == NULL) && (FirstLbcb->WorkqueLinks.Flink != &Lfcb->LbcbWorkque)) {
  1715. *NextLbcb = CONTAINING_RECORD( FirstLbcb->WorkqueLinks.Flink,
  1716. LBCB,
  1717. WorkqueLinks );
  1718. }
  1719. ASSERT( *ContainsLastEntry || (*NextLbcb != NULL) );
  1720. DebugTrace( 0, Dbg, "File Offset (Low) -> %08lx\n", FileOffset->LowPart );
  1721. DebugTrace( 0, Dbg, "File Offset (High) -> %08lx\n", FileOffset->HighPart );
  1722. DebugTrace( 0, Dbg, "Contains Last Entry -> %08x\n", *ContainsLastEntry );
  1723. DebugTrace( 0, Dbg, "LfsRestart -> %08x\n", *LfsRestart );
  1724. DebugTrace( 0, Dbg, "IoBlocks -> %08lx\n", *IoBlocks );
  1725. DebugTrace( -1, Dbg, "LfsFindFirstIo: Exit\n", 0 );
  1726. return;
  1727. }