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.

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