summaryrefslogtreecommitdiffstats
path: root/private/ntos/lfs
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/lfs
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/lfs')
-rw-r--r--private/ntos/lfs/cachesup.c2165
-rw-r--r--private/ntos/lfs/dirs22
-rw-r--r--private/ntos/lfs/lbcbsup.c464
-rw-r--r--private/ntos/lfs/lfsdata.c102
-rw-r--r--private/ntos/lfs/lfsdata.h193
-rw-r--r--private/ntos/lfs/lfsdisk.h506
-rw-r--r--private/ntos/lfs/lfsprocs.h740
-rw-r--r--private/ntos/lfs/lfsques.txt190
-rw-r--r--private/ntos/lfs/lfsstruc.h614
-rw-r--r--private/ntos/lfs/logpgsup.c105
-rw-r--r--private/ntos/lfs/logrcsup.c658
-rw-r--r--private/ntos/lfs/lsnsup.c316
-rw-r--r--private/ntos/lfs/mp/makefile6
-rw-r--r--private/ntos/lfs/mp/sources27
-rw-r--r--private/ntos/lfs/nodetype.h54
-rw-r--r--private/ntos/lfs/querylog.c1245
-rw-r--r--private/ntos/lfs/registry.c4234
-rw-r--r--private/ntos/lfs/restart.c651
-rw-r--r--private/ntos/lfs/rstrtsup.c268
-rw-r--r--private/ntos/lfs/sources.inc61
-rw-r--r--private/ntos/lfs/strucsup.c418
-rw-r--r--private/ntos/lfs/sysinit.c130
-rw-r--r--private/ntos/lfs/up/makefile6
-rw-r--r--private/ntos/lfs/up/sources27
-rw-r--r--private/ntos/lfs/verfysup.c538
-rw-r--r--private/ntos/lfs/write.c475
26 files changed, 14215 insertions, 0 deletions
diff --git a/private/ntos/lfs/cachesup.c b/private/ntos/lfs/cachesup.c
new file mode 100644
index 000000000..93f7fdafa
--- /dev/null
+++ b/private/ntos/lfs/cachesup.c
@@ -0,0 +1,2165 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ CacheSup.c
+
+Abstract:
+
+ This module provides an interface with the cache manager.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CACHE_SUP)
+
+//
+// Following is used to generate a sequence number when the cache manager
+// gives us a page of zeroes. Otherwise all of the sequence numbers will
+// be 1.
+//
+
+USHORT LfsUsaSeqNumber;
+
+BOOLEAN
+LfsIsRestartPageHeaderValid (
+ IN LONGLONG FileOffset,
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ OUT PBOOLEAN LogPacked
+ );
+
+BOOLEAN
+LfsIsRestartAreaValid (
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ IN BOOLEAN LogPacked
+ );
+
+BOOLEAN
+LfsIsClientAreaValid (
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ IN BOOLEAN LogPacked,
+ IN BOOLEAN UsaError
+ );
+
+VOID
+LfsFindFirstIo (
+ IN PLFCB Lfcb,
+ IN PLBCB TargetLbcb,
+ IN PLBCB FirstLbcb,
+ OUT PLBCB *NextLbcb,
+ OUT PLONGLONG FileOffset,
+ OUT PULONG Length,
+ OUT PBOOLEAN ContainsLastEntry,
+ OUT PBOOLEAN LfsRestart,
+ OUT PBOOLEAN UseTailCopy,
+ OUT PULONG IoBlocks
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsCopyReadLogRecord)
+#pragma alloc_text(PAGE, LfsFindFirstIo)
+#pragma alloc_text(PAGE, LfsFlushLfcb)
+#pragma alloc_text(PAGE, LfsIsClientAreaValid)
+#pragma alloc_text(PAGE, LfsIsRestartAreaValid)
+#pragma alloc_text(PAGE, LfsIsRestartPageHeaderValid)
+#pragma alloc_text(PAGE, LfsPinOrMapData)
+#pragma alloc_text(PAGE, LfsPinOrMapLogRecordHeader)
+#pragma alloc_text(PAGE, LfsReadRestart)
+#endif
+
+
+NTSTATUS
+LfsPinOrMapData (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN PinData,
+ IN BOOLEAN AllowErrors,
+ IN BOOLEAN IgnoreUsaErrors,
+ OUT PBOOLEAN UsaError,
+ OUT PVOID *Buffer,
+ OUT PBCB *Bcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will pin or map a portion of the log file.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ FileOffset - This is the offset of the log page to pin.
+
+ Length - This is the length of the data to access.
+
+ PinData - Boolean indicating if we are to pin or map this data.
+
+ AllowErrors - This boolean indicates whether we should raise on an
+ I/O error or return on an I/O error.
+
+ IgnoreUsaErrors - Boolean indicating whether we will raise on Usa
+ errors.
+
+ UsaError - Address to store whether the Usa had an error.
+
+ Buffer - This is the address to store the address of the data.
+
+ Bcb - This is the Bcb for this operation.
+
+Return Value:
+
+ NTSTATUS - The result of the I/O.
+
+--*/
+
+{
+ volatile NTSTATUS Status;
+ ULONG Signature;
+
+ Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsPinReadLogPage: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "FileOffset (Low) -> %08lx\n", FileOffset.HighPart );
+ DebugTrace( 0, Dbg, "FileOffset (High) -> %08lx\n", FileOffset.LowPart );
+ DebugTrace( 0, Dbg, "Length -> %08lx\n", Length );
+ DebugTrace( 0, Dbg, "PinData -> %04x\n", PinData );
+ DebugTrace( 0, Dbg, "AllowErrors -> %08x\n", AllowErrors );
+ DebugTrace( 0, Dbg, "IgnoreUsaErrors -> %04x\n", IgnoreUsaErrors );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Use a try-except to catch cache manager errors.
+ //
+
+ try {
+
+ //
+ // We call the cache to perform the work.
+ //
+
+ if (PinData) {
+
+ CcPinRead( Lfcb->FileObject,
+ (PLARGE_INTEGER)&FileOffset,
+ Length,
+ TRUE,
+ Bcb,
+ Buffer );
+
+ } else {
+
+ CcMapData( Lfcb->FileObject,
+ (PLARGE_INTEGER)&FileOffset,
+ Length,
+ TRUE,
+ Bcb,
+ Buffer );
+ }
+
+ //
+ // Capture the signature now while we are within the
+ // exception filter.
+ //
+
+ Signature = *((PULONG) *Buffer);
+
+ } except( LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ *UsaError = FALSE;
+
+ //
+ // If an error occurred, we raise the status.
+ //
+
+ if (!NT_SUCCESS( Status )) {
+
+ if (!AllowErrors) {
+
+ DebugTrace( 0, Dbg, "Read on log page failed -> %08lx\n", Status );
+ ExRaiseStatus( Status );
+ }
+
+ //
+ // Check that the update sequence array for this
+ // page is valid.
+ //
+
+ } else if (Signature == LFS_SIGNATURE_BAD_USA_ULONG) {
+
+ //
+ // If we don't allow errors, raise an error status.
+ //
+
+ if (!IgnoreUsaErrors) {
+
+ DebugTrace( 0, Dbg, "Usa error on log page\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ *UsaError = TRUE;
+ }
+
+ } finally {
+
+ DebugUnwind( LfsPinOrMapData );
+
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
+ DebugTrace( 0, Dbg, "Bcb -> %08lx\n", *Bcb );
+
+ DebugTrace( -1, Dbg, "LfsPinOrMapData: Exit -> %08lx\n", Status );
+ }
+
+ return Status;
+}
+
+
+VOID
+LfsPinOrMapLogRecordHeader (
+ IN PLFCB Lfcb,
+ IN LSN Lsn,
+ IN BOOLEAN PinData,
+ IN BOOLEAN IgnoreUsaErrors,
+ OUT PBOOLEAN UsaError,
+ OUT PLFS_RECORD_HEADER *RecordHeader,
+ OUT PBCB *Bcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will pin or map a log record for read access.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ Lsn - This is the Lsn whose header should be pinned.
+
+ PinData - Boolean indicating if we are to pin or map this data.
+
+ IgnoreUsaErrors - Boolean indicating whether we will raise on Usa
+ errors.
+
+ UsaError - Address to store whether the Usa had an error.
+
+ RecordHeader - This is the address to store the address of the pinned data.
+
+ Bcb - This is the Bcb for this pin operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLFS_RECORD_PAGE_HEADER LogPageHeader;
+ LONGLONG LogPage;
+ ULONG PageOffset;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsPinOrMapLogRecordHeader: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.HighPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.LowPart );
+ DebugTrace( 0, Dbg, "PinData -> %04x\n", PinData );
+ DebugTrace( 0, Dbg, "IgnoreUsaErrors -> %04x\n", IgnoreUsaErrors );
+
+ //
+ // Compute the log page and the offset of the log record header
+ // in the log page.
+ //
+
+ LfsTruncateLsnToLogPage( Lfcb, Lsn, &LogPage );
+ PageOffset = LfsLsnToPageOffset( Lfcb, Lsn );
+
+ //
+ // Call the cache manager to pin the page.
+ //
+
+ LfsPinOrMapData( Lfcb,
+ LogPage,
+ (ULONG)Lfcb->LogPageSize,
+ PinData,
+ FALSE,
+ IgnoreUsaErrors,
+ UsaError,
+ (PVOID *) &LogPageHeader,
+ Bcb );
+
+ //
+ // The actual offset we need is at PageOffset from the start of the page.
+ //
+
+ *RecordHeader = Add2Ptr( LogPageHeader, PageOffset, PLFS_RECORD_HEADER );
+
+ DebugTrace( 0, Dbg, "Record Header -> %08lx\n", *RecordHeader );
+ DebugTrace( 0, Dbg, "Bcb -> %08lx\n", *Bcb );
+
+ DebugTrace( -1, Dbg, "LfsPinOrMapLogRecordHeader: Exit\n", 0 );
+
+ return;
+}
+
+
+VOID
+LfsCopyReadLogRecord (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_HEADER RecordHeader,
+ OUT PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routines copies a log record from the file to a buffer. The log
+ record may span several log pages and may even wrap in the file.
+
+Arguments:
+
+ Lfcb - A pointer to the control block for the log file.
+
+ RecordHeader - Pointer to the log record header for this log record.
+
+ Buffer - Pointer to the buffer to store the log record.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PBCB Bcb = NULL;
+ BOOLEAN UsaError;
+
+ PLFS_RECORD_PAGE_HEADER PageHeader;
+
+ LONGLONG LogPageFileOffset;
+ ULONG LogPageOffset;
+
+ ULONG RemainingTransferBytes;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsCopyReadLogRecord: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "RecordHeader -> %08lx\n", RecordHeader );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
+
+ //
+ // We find the file offset of the log page containing the start of
+ // this log record, the offset within the page to start the transfer from,
+ // the number of bytes to transfer on this page and the starting
+ // position in the buffer to begin the transfer to.
+ //
+
+ LfsTruncateLsnToLogPage( Lfcb, RecordHeader->ThisLsn, &LogPageFileOffset );
+ LogPageOffset = LfsLsnToPageOffset( Lfcb, RecordHeader->ThisLsn ) + Lfcb->RecordHeaderLength;
+
+ RemainingTransferBytes = RecordHeader->ClientDataLength;
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // While there are more bytes to transfer, we continue to attempt to
+ // perform the read.
+ //
+
+ while (TRUE) {
+
+ ULONG RemainingPageBytes;
+
+ BOOLEAN Wrapped;
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize - LogPageOffset;
+
+ //
+ // We compute the number of bytes to read from this log page and
+ // call the cache package to perform the transfer.
+ //
+
+ if (RemainingTransferBytes <= RemainingPageBytes) {
+
+ RemainingPageBytes = RemainingTransferBytes;
+ }
+
+ RemainingTransferBytes -= RemainingPageBytes;
+
+ //
+ // Unpin any previous buffer.
+ //
+
+ if (Bcb != NULL) {
+
+ CcUnpinData( Bcb );
+ Bcb = NULL;
+ }
+
+ LfsPinOrMapData( Lfcb,
+ LogPageFileOffset,
+ (ULONG)Lfcb->LogPageSize,
+ FALSE,
+ FALSE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &PageHeader,
+ &Bcb );
+
+ //
+ // The last Lsn on this page better be greater or equal to the Lsn we
+ // are copying.
+ //
+
+ if ( PageHeader->Copy.LastLsn.QuadPart < RecordHeader->ThisLsn.QuadPart ) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ RtlCopyMemory( Buffer,
+ Add2Ptr( PageHeader, LogPageOffset, PVOID ),
+ RemainingPageBytes );
+
+ //
+ // If there are no more bytes to transfer, we exit the loop.
+ //
+
+ if (RemainingTransferBytes == 0) {
+
+ //
+ // Our log record better not span this page.
+ //
+
+ if (!FlagOn( PageHeader->Flags, LOG_PAGE_LOG_RECORD_END )
+
+ || (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && ( RecordHeader->ThisLsn.QuadPart > PageHeader->Header.Packed.LastEndLsn.QuadPart ))) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ break;
+ }
+
+ //
+ // If the page header indicates that the log record ended on this page,
+ // this is a disk corrupt condition. For a packed page it means
+ // that the last Lsn and the last Ending Lsn are the same.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ //
+ // If there is no spanning log record this is an error.
+ //
+
+ if (( PageHeader->Copy.LastLsn.QuadPart == PageHeader->Header.Packed.LastEndLsn.QuadPart )
+
+ || ( RecordHeader->ThisLsn.QuadPart > PageHeader->Copy.LastLsn.QuadPart )) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // For an unpacked page it simply means that the page
+ // contains the end of a log record.
+ //
+
+ } else if (FlagOn( PageHeader->Flags, LOG_PAGE_LOG_RECORD_END )) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // We find the start of the next log page and the offset within
+ // that page to start transferring bytes.
+ //
+
+ LfsNextLogPageOffset( Lfcb,
+ LogPageFileOffset,
+ &LogPageFileOffset,
+ &Wrapped );
+
+ LogPageOffset = (ULONG)Lfcb->LogPageDataOffset;
+
+ //
+ // We also adjust our pointer in the user's buffer to transfer
+ // the next block to.
+ //
+
+ Buffer = Add2Ptr( Buffer, RemainingPageBytes, PVOID );
+ }
+
+ } finally {
+
+ //
+ // Unpin any previous buffer.
+ //
+
+ if (Bcb != NULL) {
+
+ CcUnpinData( Bcb );
+ Bcb = NULL;
+ }
+
+ DebugTrace( -1, Dbg, "LfsCopyReadLogRecord: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+VOID
+LfsFlushLfcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to flush the current Lbcbs in on the Lfcb
+ work queue. It will flush up to the I/O which contains the desired
+ Lbcb.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ Lbcb - This is the block which is needed to be flushed to disk.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLBCB FirstLbcb;
+ PLBCB ThisLbcb;
+ PLBCB NextLbcb;
+
+ PLBCB TargetLbcb;
+ PULONG Signature;
+
+ LONGLONG FileOffset;
+ ULONG Length;
+
+ BOOLEAN RaiseCorrupt = FALSE;
+ BOOLEAN ValidLastLsn = FALSE;
+
+ BOOLEAN ContainsLastEntry = FALSE;
+ BOOLEAN LfsRestart;
+ BOOLEAN UseTailCopy;
+
+ ULONG IoBlocks;
+ ULONG NewLfcbFlags = 0;
+
+ LSN LastLsn;
+
+ IO_STATUS_BLOCK Iosb;
+
+ PBCB PageBcb = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFlushLfcb: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // If there are no elements on the list, we are done.
+ //
+
+ if (IsListEmpty( &Lfcb->LbcbWorkque )) {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Mark the Lfcb as Io in progress.
+ //
+
+ Lfcb->LfsIoState = LfsClientThreadIo;
+
+ //
+ // Remember the first Lbcb in the list.
+ //
+
+ FirstLbcb = CONTAINING_RECORD( Lfcb->LbcbWorkque.Flink,
+ LBCB,
+ WorkqueLinks );
+
+ //
+ // We continue looping and performing I/o for as long as possible.
+ //
+
+ while (!ContainsLastEntry) {
+
+ //
+ // Reset the notify event for all of the waiting threads.
+ //
+
+ KeClearEvent( &Lfcb->Sync->Event );
+
+ //
+ // Find the block of Lbcb's that make up the first I/O, remembering
+ // how many there are. Also remember if this I/O contains the
+ // last element on the list when we were called.
+ //
+
+ LfsFindFirstIo( Lfcb,
+ Lbcb,
+ FirstLbcb,
+ &NextLbcb,
+ &FileOffset,
+ &Length,
+ &ContainsLastEntry,
+ &LfsRestart,
+ &UseTailCopy,
+ &IoBlocks );
+
+ if (UseTailCopy) {
+
+ TargetLbcb = Lfcb->ActiveTail;
+ Lfcb->ActiveTail = Lfcb->PrevTail;
+ Lfcb->PrevTail = TargetLbcb;
+
+ FileOffset = TargetLbcb->FileOffset;
+
+ } else {
+
+ TargetLbcb = FirstLbcb;
+ }
+
+ //
+ // Give up the Lfcb unless we are looking at an active page.
+ //
+
+ if (!UseTailCopy) {
+
+ LfsReleaseLfcb( Lfcb );
+ }
+
+ //
+ // If this I/O involves the Lfs restart area, write it to the
+ // cache pages.
+ //
+
+ if (LfsRestart) {
+
+ PLFS_RESTART_PAGE_HEADER RestartPage;
+ BOOLEAN UsaError;
+ NTSTATUS Status;
+
+ //
+ // If there was some error in initially reading the restart page then
+ // it is OK to settle for a page of zeroes.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_READ_FIRST_RESTART | LFCB_READ_SECOND_RESTART ) &&
+ ((FileOffset == 0) ?
+ FlagOn( Lfcb->Flags, LFCB_READ_FIRST_RESTART ) :
+ FlagOn( Lfcb->Flags, LFCB_READ_SECOND_RESTART ))) {
+
+ LfsPreparePinWriteData( Lfcb,
+ FileOffset,
+ (ULONG) Lfcb->SystemPageSize,
+ &RestartPage,
+ &PageBcb );
+
+ Status = STATUS_SUCCESS;
+
+ if (FileOffset == 0) {
+
+ SetFlag( NewLfcbFlags, LFCB_READ_FIRST_RESTART );
+
+ } else {
+
+ SetFlag( NewLfcbFlags, LFCB_READ_SECOND_RESTART );
+ }
+
+ } else {
+
+ Status = LfsPinOrMapData( Lfcb,
+ FileOffset,
+ (ULONG)Lfcb->SystemPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ &RestartPage,
+ &PageBcb );
+ }
+
+ if (NT_SUCCESS( Status )) {
+
+ //
+ // Initialize the restart page header.
+ //
+
+ Signature = (PULONG) &RestartPage->MultiSectorHeader.Signature;
+
+ *Signature = LFS_SIGNATURE_RESTART_PAGE_ULONG;
+ RestartPage->ChkDskLsn = LfsLi0;
+
+ RestartPage->MultiSectorHeader.UpdateSequenceArrayOffset
+ = Lfcb->RestartUsaOffset;
+
+ RestartPage->MultiSectorHeader.UpdateSequenceArraySize
+ = Lfcb->RestartUsaArraySize;
+
+ RestartPage->SystemPageSize = (ULONG)Lfcb->SystemPageSize;
+ RestartPage->LogPageSize = (ULONG)Lfcb->LogPageSize;
+
+ RestartPage->RestartOffset = (USHORT) Lfcb->RestartDataOffset;
+ RestartPage->MajorVersion = Lfcb->MajorVersion;
+ RestartPage->MinorVersion = Lfcb->MinorVersion;
+
+ //
+ // If the Lfcb indicates that the file has wrapped, then clear the
+ // first pass flag in the restart area.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED )) {
+
+ ClearFlag( ((PLFS_RESTART_AREA) FirstLbcb->PageHeader)->Flags, RESTART_SINGLE_PAGE_IO );
+ SetFlag( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO );
+ }
+
+ //
+ // Write the page header into the page and mark the page dirty.
+ //
+
+ RtlCopyMemory( Add2Ptr( RestartPage, Lfcb->RestartDataOffset, PVOID ),
+ FirstLbcb->PageHeader,
+ (ULONG)FirstLbcb->Length );
+
+ //
+ // Make sure the modified bit gets set in the pfn database. The
+ // cache manager should do this even for files we told him not to
+ // lazy write.
+ //
+
+ CcSetDirtyPinnedData( PageBcb, NULL );
+
+ //
+ // We unpin any buffers pinned on this page.
+ //
+
+ CcUnpinData( PageBcb );
+ PageBcb = NULL;
+
+ LastLsn = FirstLbcb->LastLsn;
+ ValidLastLsn = TRUE;
+
+ //
+ // Use a system page size as the length we need to flush.
+ //
+
+ Length = (ULONG)Lfcb->SystemPageSize;
+
+ } else {
+
+ RaiseCorrupt = TRUE;
+ }
+
+ //
+ // Otherwise these are log record pages
+ //
+
+ } else {
+
+ PLFS_RECORD_PAGE_HEADER RecordPageHeader;
+
+ ULONG Count;
+
+ //
+ // Mark the last Lsn fields for the page headers and each
+ // page's position in the transfer. Also unpin all of the
+ // log pages.
+ //
+
+ Count = 1;
+
+ ThisLbcb = FirstLbcb;
+
+ while (TRUE) {
+
+ //
+ // If we have to use the tail copy, then pin a page to use.
+ //
+
+ if (UseTailCopy) {
+
+ BOOLEAN UsaError;
+
+ if (!NT_SUCCESS( LfsPinOrMapData( Lfcb,
+ TargetLbcb->FileOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ &RecordPageHeader,
+ &PageBcb ))) {
+
+ RaiseCorrupt = TRUE;
+ break;
+ }
+
+ } else {
+
+ PUSHORT SeqNumber;
+
+ RecordPageHeader = (PLFS_RECORD_PAGE_HEADER) ThisLbcb->PageHeader;
+
+ //
+ // If the sequence number is zero then this is probably a
+ // page of zeroes produced by the cache manager. In order
+ // to insure that we don't have the same sequence number
+ // on each page we will seed the sequence number.
+ //
+
+ SeqNumber = Add2Ptr( RecordPageHeader,
+ Lfcb->LogRecordUsaOffset,
+ PUSHORT );
+
+ if (*SeqNumber == 0) {
+
+ *SeqNumber = LfsUsaSeqNumber;
+ LfsUsaSeqNumber += 1;
+ }
+ }
+
+ //
+ // Make sure the modified bit gets set in the pfn database. The
+ // cache manager should do this even for files we told him not to
+ // lazy write.
+ //
+
+ if (UseTailCopy) {
+
+ //
+ // Store the file offset of the real page in the header.
+ // Also set the flag indicating the page is a tail copy.
+ //
+
+ RtlCopyMemory( RecordPageHeader,
+ ThisLbcb->PageHeader,
+ (ULONG)Lfcb->LogPageSize );
+
+ RecordPageHeader->Copy.FileOffset = ThisLbcb->FileOffset;
+ }
+
+ //
+ // We update all of fields as yet not updated.
+ //
+
+ RecordPageHeader->PagePosition = (USHORT) Count;
+ RecordPageHeader->PageCount = (USHORT) IoBlocks;
+
+ //
+ // We set up the update sequence array for this structure.
+ //
+
+ Signature = (PULONG) &RecordPageHeader->MultiSectorHeader.Signature;
+ *Signature = LFS_SIGNATURE_RECORD_PAGE_ULONG;
+
+ RecordPageHeader->MultiSectorHeader.UpdateSequenceArrayOffset
+ = Lfcb->LogRecordUsaOffset;
+
+ RecordPageHeader->MultiSectorHeader.UpdateSequenceArraySize
+ = Lfcb->LogRecordUsaArraySize;
+
+ //
+ // Make sure the modified bit gets set in the pfn database. The
+ // cache manager should do this even for files we told him not to
+ // lazy write.
+ //
+
+ if (UseTailCopy) {
+
+ CcSetDirtyPinnedData( PageBcb, NULL );
+
+ CcUnpinData( PageBcb );
+ PageBcb = NULL;
+
+ } else {
+
+ CcSetDirtyPinnedData( ThisLbcb->LogPageBcb, NULL );
+
+ //
+ // We unpin any buffers pinned on this page.
+ //
+
+ CcUnpinDataForThread( ThisLbcb->LogPageBcb, ThisLbcb->ResourceThread );
+ }
+
+ //
+ // Remember the last lsn and its length if this is the final
+ // page of an Lsn.
+ //
+
+ if (FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) {
+
+ LastLsn = ThisLbcb->LastEndLsn;
+ ValidLastLsn = TRUE;
+ }
+
+ //
+ // Exit the loop if this is the last block.
+ //
+
+ if (Count == IoBlocks) {
+
+ break;
+ }
+
+ //
+ // Otherwise move to the next entry.
+ //
+
+ ThisLbcb = CONTAINING_RECORD( ThisLbcb->WorkqueLinks.Flink,
+ LBCB,
+ WorkqueLinks );
+
+ Count += 1;
+ }
+ }
+
+ //
+ // We are ready to do the I/O. Flush the pages to the log file.
+ //
+
+ CcFlushCache( Lfcb->FileObject->SectionObjectPointer,
+ (PLARGE_INTEGER)&FileOffset,
+ Length,
+ &Iosb );
+
+ if (!NT_SUCCESS( Iosb.Status )) {
+
+ LONG BytesRemaining = (LONG) Length;
+
+ //
+ // If we get an error then try each individual page.
+ //
+
+ while (BytesRemaining > 0) {
+
+ CcFlushCache( Lfcb->FileObject->SectionObjectPointer,
+ (PLARGE_INTEGER)&FileOffset,
+ (ULONG)Lfcb->SystemPageSize,
+ &Iosb );
+
+ if (!NT_SUCCESS( Iosb.Status )) {
+
+#ifdef NTFS_RESTART
+ ASSERT( FALSE );
+#endif
+ RaiseCorrupt = TRUE;
+ }
+
+ BytesRemaining -= (LONG)Lfcb->SystemPageSize;
+ FileOffset = FileOffset + Lfcb->SystemPageSize;
+ }
+ }
+
+ //
+ // Reacquire the Lfcb, remembering that we have it.
+ //
+
+ if (!UseTailCopy) {
+
+ LfsAcquireLfcb( Lfcb );
+ }
+
+ //
+ // Update the last flushed Lsn value if this isn't a
+ // restart write.
+ //
+
+ if (!LfsRestart) {
+
+ if (ValidLastLsn) {
+
+ Lfcb->LastFlushedLsn = LastLsn;
+ }
+
+ //
+ // Remember the Lsn we assigned to this restart area.
+ //
+
+ } else {
+
+ Lfcb->LastFlushedRestartLsn = LastLsn;
+
+ //
+ // Clear any neccessary flags on a successful operation.
+ //
+
+ if (!RaiseCorrupt) {
+
+ ClearFlag( Lfcb->Flags, NewLfcbFlags );
+ NewLfcbFlags = 0;
+ }
+ }
+
+ //
+ // Walk through all the Lbcb's we flushed, deallocating the Lbcbs.
+ //
+
+ if (!UseTailCopy) {
+
+ PLBCB TempLbcb;
+
+ ThisLbcb = FirstLbcb;
+
+ while (TRUE) {
+
+ //
+ // Remember the next entry on the list.
+ //
+
+ TempLbcb = CONTAINING_RECORD( ThisLbcb->WorkqueLinks.Flink,
+ LBCB,
+ WorkqueLinks );
+
+ //
+ // Remove it from the LbcbWorkque queue.
+ //
+
+ RemoveEntryList( &ThisLbcb->WorkqueLinks );
+
+ //
+ // Deallocate the structure.
+ //
+
+ LfsDeallocateLbcb( Lfcb, ThisLbcb );
+
+ if (IoBlocks-- == 1) {
+
+ break;
+ }
+
+ ThisLbcb = TempLbcb;
+ }
+ }
+
+ //
+ // If we flushed the Lbcb we were interested in, we are done.
+ // We will signal all waiting threads regardless
+ //
+
+ KeSetEvent( &Lfcb->Sync->Event, 0, FALSE );
+
+ //
+ // Remember the starting Lbcb for the next I/O.
+ //
+
+ FirstLbcb = NextLbcb;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ DebugUnwind( LfsFlushLfcb );
+
+ //
+ // Show that there is no Io in progress.
+ //
+
+ Lfcb->LfsIoState = LfsNoIoInProgress;
+
+ //
+ // Make sure we didn't leave any pages pinned.
+ //
+
+ if (PageBcb != NULL) {
+
+ CcUnpinData( PageBcb );
+ }
+
+ DebugTrace( -1, Dbg, "LfsFlushLfcb: Exit\n", 0 );
+ }
+
+ //
+ // If the Io failed at some point, we raise a corrupt disk error.
+ // The only exception is if this is the final flush of the log file.
+ // In this case the client may not be able to cleanup after this error.
+ //
+
+ if (RaiseCorrupt && (!FlagOn( Lfcb->Flags, LFCB_FINAL_SHUTDOWN ))) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ return;
+}
+
+
+BOOLEAN
+LfsReadRestart (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileSize,
+ IN BOOLEAN FirstRestart,
+ OUT PLONGLONG RestartPageOffset,
+ OUT PLFS_RESTART_PAGE_HEADER *RestartPage,
+ OUT PBCB *RestartPageBcb,
+ OUT PBOOLEAN ChkdskWasRun,
+ OUT PBOOLEAN ValidPage,
+ OUT PBOOLEAN UninitializedFile,
+ OUT PBOOLEAN LogPacked,
+ OUT PLSN LastLsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will walk through 512 blocks of the file looking for a
+ valid restart page header. It will stop the first time we find
+ a valid page header.
+
+Arguments:
+
+ Lfcb - This is the Lfcb for the log file.
+
+ FileSize - Size in bytes for the log file.
+
+ FirstRestart - Indicates if we are looking for the first valid
+ restart area.
+
+ RestartPageOffset - This is the location to store the offset in the
+ file where the log page was found.
+
+ RestartPage - This is the location to store the address of the
+ pinned restart page.
+
+ RestartPageBcb - This is the location to store the Bcb for this
+ cache pin operation.
+
+ ChkdskWasRun - Address to store whether checkdisk was run on this volume.
+
+ ValidPage - Address to store whether there was valid data on this page.
+
+ UninitializedFile - Address to store whether this is an uninitialized
+ log file. Return value only valid if for the first restart area.
+
+ LogPacked - Address to store whether the log file is packed.
+
+ LastLsn - Address to store the last Lsn for this restart page. It will be the
+ chkdsk value if checkdisk was run. Otherwise it is the LastFlushedLsn
+ for this restart page.
+
+Return Value:
+
+ BOOLEAN - TRUE if a restart area was found, FALSE otherwise.
+
+--*/
+
+{
+ ULONG FileOffsetIncrement;
+ LONGLONG FileOffset;
+
+ PLFS_RESTART_AREA RestartArea;
+
+ NTSTATUS Status;
+
+ PLFS_RESTART_PAGE_HEADER ThisPage;
+ PBCB ThisPageBcb = NULL;
+
+ BOOLEAN FoundRestart = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsReadRestart: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ *UninitializedFile = TRUE;
+ *ValidPage = FALSE;
+ *ChkdskWasRun = FALSE;
+ *LogPacked = FALSE;
+
+ try {
+
+ //
+ // Determine which restart area we are looking for.
+ //
+
+ if (FirstRestart) {
+
+ FileOffset = 0;
+ FileOffsetIncrement = SEQUENCE_NUMBER_STRIDE;
+
+ } else {
+
+ FileOffset = SEQUENCE_NUMBER_STRIDE;
+ FileOffsetIncrement = 0;
+ }
+
+ //
+ // We loop continuously until we succeed, pin a log record page
+ // or exhaust the number of possible tries.
+ //
+
+ while ( FileOffset < FileSize ) {
+
+ ULONG Signature;
+ BOOLEAN UsaError;
+
+ if (ThisPageBcb != NULL) {
+
+ CcUnpinData( ThisPageBcb );
+ ThisPageBcb = NULL;
+ }
+
+ //
+ // Attempt to pin a page header at the current offset.
+ //
+
+ Status = LfsPinOrMapData( Lfcb,
+ FileOffset,
+ SEQUENCE_NUMBER_STRIDE,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *)&ThisPage,
+ &ThisPageBcb );
+
+ //
+ //
+ // If we succeeded, we look at the 4 byte signature.
+ //
+
+ if (NT_SUCCESS( Status )) {
+
+ Signature = *((PULONG) &ThisPage->MultiSectorHeader.Signature);
+
+ //
+ // If the signature is a log record page, we will exit.
+ //
+
+ if (Signature == LFS_SIGNATURE_RECORD_PAGE_ULONG) {
+
+ *UninitializedFile = FALSE;
+ break;
+ }
+
+ //
+ // Continue analyzing the page if the signature is chkdsk or
+ // a restart page.
+ //
+
+ if (Signature == LFS_SIGNATURE_MODIFIED_ULONG
+ || Signature == LFS_SIGNATURE_RESTART_PAGE_ULONG) {
+
+ *UninitializedFile = FALSE;
+
+ //
+ // Remember where we found this page.
+ //
+
+ *RestartPageOffset = FileOffset;
+
+ //
+ // Let's check the restart area if this is a valid page.
+ //
+
+ if (LfsIsRestartPageHeaderValid( FileOffset,
+ ThisPage,
+ LogPacked )
+
+ && LfsIsRestartAreaValid( ThisPage, *LogPacked )) {
+
+ //
+ // We have a valid restart page header and restart area.
+ // If chkdsk was run or we have no clients then
+ // we have no more checking to do.
+ //
+
+ RestartArea = Add2Ptr( ThisPage,
+ ThisPage->RestartOffset,
+ PLFS_RESTART_AREA );
+
+ if (Signature == LFS_SIGNATURE_RESTART_PAGE_ULONG
+ && RestartArea->ClientInUseList != LFS_NO_CLIENT) {
+
+ //
+ // Pin the entire restart area if we didn't have an earlier
+ //
+
+ CcUnpinData( ThisPageBcb );
+ ThisPageBcb = NULL;
+
+ Status = LfsPinOrMapData( Lfcb,
+ FileOffset,
+ ThisPage->SystemPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *)&ThisPage,
+ &ThisPageBcb );
+
+ if (NT_SUCCESS( Status )
+ && LfsIsClientAreaValid( ThisPage, *LogPacked, UsaError )) {
+
+ *ValidPage = TRUE;
+
+ RestartArea = Add2Ptr( ThisPage,
+ ThisPage->RestartOffset,
+ PLFS_RESTART_AREA );
+ }
+
+ } else {
+
+ *ValidPage = TRUE;
+ }
+ }
+
+ //
+ // If chkdsk was run then update the caller's values and return.
+ //
+
+ if (Signature == LFS_SIGNATURE_MODIFIED_ULONG) {
+
+ *ChkdskWasRun = TRUE;
+
+ *LastLsn = ThisPage->ChkDskLsn;
+
+ FoundRestart = TRUE;
+
+ *RestartPageBcb = ThisPageBcb;
+ *RestartPage = ThisPage;
+
+ ThisPageBcb = NULL;
+ break;
+ }
+
+ //
+ // If we have a valid page then copy the values we need from it.
+ //
+
+ if (*ValidPage) {
+
+ *LastLsn = RestartArea->CurrentLsn;
+
+ FoundRestart = TRUE;
+
+ *RestartPageBcb = ThisPageBcb;
+ *RestartPage = ThisPage;
+
+ ThisPageBcb = NULL;
+ break;
+ }
+
+ //
+ // Remember if the signature does not indicate uninitialized file.
+ //
+
+ } else if (Signature != LFS_SIGNATURE_UNINITIALIZED_ULONG) {
+
+ *UninitializedFile = FALSE;
+ }
+ }
+
+ //
+ // Move to the next possible log page.
+ //
+
+ FileOffset = FileOffset << 1;
+
+ (ULONG)FileOffset += FileOffsetIncrement;
+
+ FileOffsetIncrement = 0;
+ }
+
+ } finally {
+
+ DebugUnwind( LfsReadRestart );
+
+ //
+ // Unpin the log pages if pinned.
+ //
+
+ if (ThisPageBcb != NULL) {
+
+ CcUnpinData( ThisPageBcb );
+ }
+
+ DebugTrace( 0, Dbg, "RestartPageAddress (Low) -> %08lx\n", RestartPageAddress->LowPart );
+ DebugTrace( 0, Dbg, "RestartPageAddress (High) -> %08lx\n", RestartPageAddress->HighPart );
+ DebugTrace( 0, Dbg, "FirstRestartPage -> %08lx\n", *FirstRestartPage );
+ DebugTrace( -1, Dbg, "LfsReadRestart: Exit\n", 0 );
+ }
+
+ return FoundRestart;
+}
+
+
+//
+// Local support routine
+//
+
+BOOLEAN
+LfsIsRestartPageHeaderValid (
+ IN LONGLONG FileOffset,
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ OUT PBOOLEAN LogPacked
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to verify that the candidate for a restart page
+ has no corrupt values in the page header. It verifies that the restart and
+ system page size have only one bit set and are at least the value of
+ the update sequence array stride.
+
+Arguments:
+
+ FileOffset - This is the offset in the file of the restart area to examine.
+ If this offset is not 0, then it should match the system page size.
+
+ PageHeader - This is the page to examine.
+
+ LogPacked - Address to store whether the log file is packed.
+
+Return Value:
+
+ BOOLEAN - TRUE if there is no corruption in the pool header values.
+ FALSE otherwise.
+
+--*/
+
+{
+ ULONG SystemPage;
+ ULONG LogPageSize;
+ ULONG Mask;
+ ULONG BitCount;
+
+ USHORT EndOfUsa;
+
+ PAGED_CODE();
+
+ *LogPacked = FALSE;
+
+ //
+ // Copy the values from the page header into the local variables.
+ //
+
+ SystemPage = PageHeader->SystemPageSize;
+ LogPageSize = PageHeader->LogPageSize;
+
+ //
+ // The system page and log page sizes must be greater or equal to the
+ // update sequence stride.
+ //
+
+ if (SystemPage < SEQUENCE_NUMBER_STRIDE
+ || LogPageSize < SEQUENCE_NUMBER_STRIDE) {
+
+ return FALSE;
+ }
+
+ //
+ // Now we check that the Log page and system page are multiples of two.
+ // They should only have a single bit set.
+ //
+
+ for (Mask = 1, BitCount = 0; Mask != 0; Mask = Mask << 1) {
+
+ if (Mask & LogPageSize) {
+
+ BitCount += 1;
+ }
+ }
+
+ //
+ // If the bit count isn't 1, return false.
+ //
+
+ if (BitCount != 1) {
+
+ return FALSE;
+ }
+
+ //
+ // Now do the system page size.
+ //
+
+ for (Mask = 1, BitCount = 0; Mask != 0; Mask = Mask << 1) {
+
+ if (Mask & SystemPage) {
+
+ BitCount += 1;
+ }
+ }
+
+ //
+ // If the bit count isn't 1, return false.
+ //
+
+ if (BitCount != 1) {
+
+ return FALSE;
+ }
+
+ //
+ // Check that if the file offset isn't 0, it is the system page size.
+ //
+
+ if (( FileOffset != 0 )
+ && ((ULONG)FileOffset != SystemPage)) {
+
+ return FALSE;
+ }
+
+ //
+ // We only support major version numbers 0.x and 1.x
+ //
+ // Version number beyond 1.0 mean the log file is packed.
+ //
+
+ if (PageHeader->MajorVersion != 0
+ && PageHeader->MajorVersion != 1) {
+
+ return FALSE;
+ }
+
+ //
+ // Check that the restart area offset is within the system page and that
+ // the restart length field will fit within the system page size.
+ //
+
+ if (QuadAlign( PageHeader->RestartOffset ) != PageHeader->RestartOffset
+ || PageHeader->RestartOffset > (USHORT) PageHeader->SystemPageSize) {
+
+ return FALSE;
+ }
+
+ //
+ // Check that the restart offset will lie beyond the Usa Array for this page.
+ //
+
+ EndOfUsa = (USHORT) (UpdateSequenceArraySize( PageHeader->SystemPageSize )
+ * sizeof( UPDATE_SEQUENCE_NUMBER ));
+
+ EndOfUsa += PageHeader->MultiSectorHeader.UpdateSequenceArrayOffset;
+
+ if (PageHeader->RestartOffset < EndOfUsa) {
+
+ return FALSE;
+ }
+
+ //
+ // Check if the log pages are packed.
+ //
+
+ if (PageHeader->MajorVersion == 1
+ && PageHeader->MinorVersion > 0) {
+
+ *LogPacked = TRUE;
+ }
+
+ //
+ // Otherwise the page header is valid.
+ //
+
+ return TRUE;
+}
+
+
+//
+// Local support routine
+//
+
+BOOLEAN
+LfsIsRestartAreaValid (
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ IN BOOLEAN LogPacked
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to verify that the restart area attached to the
+ log page header is valid. The restart values must be contained within
+ the first Usa stride of the file. This is so we can restart successfully
+ after chkdsk.
+
+Arguments:
+
+ PageHeader - This is the page to examine.
+
+ LogPacked - Indicates if the log file is packed.
+
+Return Value:
+
+ BOOLEAN - TRUE if there is no corruption in the restart area values.
+ FALSE otherwise.
+
+--*/
+
+{
+ PLFS_RESTART_AREA RestartArea;
+ ULONG OffsetInRestart;
+ ULONG SeqNumberBits;
+
+ LONGLONG FileSize;
+
+ PAGED_CODE();
+
+ //
+ // The basic part of the restart area must fit into the first stride of
+ // the page. This will allow chkdsk to work even if there are Usa errors.
+ //
+
+ OffsetInRestart = FIELD_OFFSET( LFS_RESTART_AREA, FileSize );
+
+ if ((PageHeader->RestartOffset + OffsetInRestart) > FIRST_STRIDE) {
+
+ return FALSE;
+ }
+
+ RestartArea = Add2Ptr( PageHeader, PageHeader->RestartOffset, PLFS_RESTART_AREA );
+
+ //
+ // Everything in the restart area except the actual client array must also
+ // be in the first stride. If the structure is packed, then we can use
+ // a field in the restart area for the client offset.
+ //
+
+ if (LogPacked) {
+
+ OffsetInRestart = RestartArea->ClientArrayOffset;
+
+ } else {
+
+ OffsetInRestart = FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray );
+ }
+
+ if (QuadAlign( OffsetInRestart ) != OffsetInRestart
+ || (PageHeader->RestartOffset + OffsetInRestart) > FIRST_STRIDE) {
+
+ return FALSE;
+ }
+
+ //
+ // The full size of the restart area must fit in the system page specified by
+ // the page header. We compute the size of the restart area by calculating
+ // the space needed by all clients. We also check the given size of the
+ // restart area.
+ //
+
+ OffsetInRestart += (RestartArea->LogClients * sizeof( LFS_CLIENT_RECORD ));
+
+ if (OffsetInRestart > PageHeader->SystemPageSize ) {
+
+ return FALSE;
+ }
+
+ //
+ // If the log is packed, then check the restart length field and whether
+ // the entire restart area is contained in that length.
+ //
+
+ if (LogPacked
+ && ((ULONG) (PageHeader->RestartOffset + RestartArea->RestartAreaLength) > PageHeader->SystemPageSize
+ || OffsetInRestart > RestartArea->RestartAreaLength)) {
+
+ return FALSE;
+ }
+
+ //
+ // As a final check make sure that the in use list and the free list are either
+ // empty or point to a valid client.
+ //
+
+ if ((RestartArea->ClientFreeList != LFS_NO_CLIENT
+ && RestartArea->ClientFreeList >= RestartArea->LogClients)
+
+ || (RestartArea->ClientInUseList != LFS_NO_CLIENT
+ && RestartArea->ClientInUseList >= RestartArea->LogClients)) {
+
+ return FALSE;
+ }
+
+ //
+ // Make sure the sequence number bits match the log file size.
+ //
+
+ FileSize = RestartArea->FileSize;
+
+ for (SeqNumberBits = 0;
+ ( FileSize != 0 );
+ SeqNumberBits += 1,
+ FileSize = ((ULONGLONG)(FileSize)) >> 1 ) {
+ }
+
+ SeqNumberBits = (sizeof( LSN ) * 8) + 3 - SeqNumberBits;
+
+ if (SeqNumberBits != RestartArea->SeqNumberBits) {
+
+ return FALSE;
+ }
+
+ //
+ // We will check the fields that apply only to a packed log file.
+ //
+
+ if (LogPacked) {
+
+ //
+ // The log page data offset and record header length must be
+ // quad-aligned.
+ //
+
+ if (QuadAlign( RestartArea->LogPageDataOffset != RestartArea->LogPageDataOffset )
+ || QuadAlign( RestartArea->RecordHeaderLength ) != RestartArea->RecordHeaderLength ) {
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//
+// Local support routine
+//
+
+BOOLEAN
+LfsIsClientAreaValid (
+ IN PLFS_RESTART_PAGE_HEADER PageHeader,
+ IN BOOLEAN LogPacked,
+ IN BOOLEAN UsaError
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to verify that the client array is valid. We test
+ if the client lists are correctly chained. If the entire restart area is
+ within the first Usa stride, we will ignore any Usa errors.
+
+Arguments:
+
+ PageHeader - This is the page to examine.
+
+ LogPacked - Indicates if the log file is packed.
+
+ UsaError - There was a Usa error in reading the full page.
+
+Return Value:
+
+ BOOLEAN - TRUE if there is no corruption in client array values.
+ FALSE otherwise.
+
+--*/
+
+{
+ PLFS_RESTART_AREA RestartArea;
+ USHORT ThisClientIndex;
+ USHORT ClientCount;
+
+ PLFS_CLIENT_RECORD ClientArray;
+ PLFS_CLIENT_RECORD ThisClient;
+
+ ULONG LoopCount;
+
+ PAGED_CODE();
+
+ RestartArea = Add2Ptr( PageHeader, PageHeader->RestartOffset, PLFS_RESTART_AREA );
+
+ //
+ // If there was a Usa error and the restart area isn't contained in the
+ // first Usa stride, then we have an error.
+ //
+
+ if (UsaError
+ && (RestartArea->RestartAreaLength + PageHeader->RestartOffset) > FIRST_STRIDE) {
+
+ return FALSE;
+ }
+
+ //
+ // Find the start of the client array.
+ //
+
+ if (LogPacked) {
+
+ ClientArray = Add2Ptr( RestartArea,
+ RestartArea->ClientArrayOffset,
+ PLFS_CLIENT_RECORD );
+
+ } else {
+
+ ClientArray = Add2Ptr( RestartArea,
+ FIELD_OFFSET( LFS_RESTART_AREA,
+ LogClientArray ),
+ PLFS_CLIENT_RECORD );
+ }
+
+ //
+ // Start with the free list. Check that all the clients are valid and
+ // that there isn't a cycle. Do the in-use list on the second pass.
+ //
+
+ ThisClientIndex = RestartArea->ClientFreeList;
+
+ LoopCount = 2;
+
+ do {
+
+ BOOLEAN FirstClient;
+
+ FirstClient = TRUE;
+
+ ClientCount = RestartArea->LogClients;
+
+ while (ThisClientIndex != LFS_NO_CLIENT) {
+
+ //
+ // If the client count is zero then we must have hit a loop.
+ // If the client index is greater or equal to the log client
+ // count then the list is corrupt.
+ //
+
+ if (ClientCount == 0
+ || ThisClientIndex >= RestartArea->LogClients) {
+
+ return FALSE;
+ }
+
+ ClientCount -= 1;
+
+ ThisClient = ClientArray + ThisClientIndex;
+ ThisClientIndex = ThisClient->NextClient;
+
+ //
+ // If this is the first client, then the previous value
+ // should indicate no client.
+ //
+
+ if (FirstClient) {
+
+ FirstClient = FALSE;
+
+ if (ThisClient->PrevClient != LFS_NO_CLIENT) {
+
+ return FALSE;
+ }
+ }
+ }
+
+ ThisClientIndex = RestartArea->ClientInUseList;
+
+ } while (--LoopCount);
+
+ //
+ // The client list is valid.
+ //
+
+ return TRUE;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsFindFirstIo (
+ IN PLFCB Lfcb,
+ IN PLBCB TargetLbcb,
+ IN PLBCB FirstLbcb,
+ OUT PLBCB *NextLbcb,
+ OUT PLONGLONG FileOffset,
+ OUT PULONG Length,
+ OUT PBOOLEAN ContainsLastEntry,
+ OUT PBOOLEAN LfsRestart,
+ OUT PBOOLEAN UseTailCopy,
+ OUT PULONG IoBlocks
+ )
+
+/*++
+
+Routine Description:
+
+ This routine walks through the linked Lbcb's for a Lfcb and groups
+ as many of them as can be grouped into a single I/O transfer.
+ It updates pointers to indicate the file offset and length of the
+ transfer, whether the I/O includes a particular Lbcb, whether the
+ transfer is a restart area or a log record page and the number of
+ Lbcb's included in the transfer. We only flush a single log page
+ if we are passing through the file for the first time.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ TargetLbcb - This is the Lbcb that the caller wants to have included in
+ the transfer.
+
+ FirstLbcb - This is the first Lbcb to look at in the list.
+
+ NextLbcb - This is the Lbcb to look at first on the next call to this
+ routine.
+
+ FileOffset - Supplies the address where we store the offset in the
+ log file of this transfer.
+
+ Length - Supplies the address where we store the length of this transfer.
+
+ ContainsLastEntry - Supplies the address where we store whether this
+ I/O includes the 'LastEntry' Lbcb.
+
+ LfsRestart - Supplies the address where we store whether this transfer
+ is a Lfs restart area.
+
+ UseTailCopy - Supplies the address where we store whether we should
+ use of page for a copy of the end of the log file.
+
+ IoBlocks - Supplies the address where we store the number of Lbcb's
+ for this transfer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindFirstIo: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // Initialize the file offset, length and io blocks values.
+ // Also assume the last entry is not contained here.
+ // Also assume we have no next Lbcb.
+ //
+
+ *FileOffset = FirstLbcb->FileOffset;
+ *Length = (ULONG)FirstLbcb->Length;
+ *IoBlocks = 1;
+
+ *LfsRestart = FALSE;
+ *UseTailCopy = FALSE;
+
+ *NextLbcb = NULL;
+
+ //
+ // Check if we have found the desired Lbcb. We reject the match
+ // if the Lbcb indicates that we should flush the copy first.
+ //
+
+ if (FirstLbcb == TargetLbcb
+ && !FlagOn( TargetLbcb->LbcbFlags, LBCB_FLUSH_COPY )) {
+
+ *ContainsLastEntry = TRUE;
+
+ } else {
+
+ *ContainsLastEntry = FALSE;
+ }
+
+ //
+ // Check if this is a restart block or if we are passing through the log
+ // file for the first time or if this Lbcb is still in the active queue.
+ // If not, then group as many of the Lbcb's as can be part of a single Io.
+ //
+
+ if (LfsLbcbIsRestart( FirstLbcb )) {
+
+ *LfsRestart = TRUE;
+
+ } else if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && (FlagOn( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY | LBCB_ON_ACTIVE_QUEUE ))) {
+
+ *UseTailCopy = TRUE;
+
+ //
+ // If we haven't found the last entry then we want to resume from
+ // this same Lbcb if we flushed a copy otherwise we will want to
+ // go to the next Lbcb.
+ //
+
+ if (FlagOn( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY )) {
+
+ *NextLbcb = FirstLbcb;
+ ClearFlag( FirstLbcb->LbcbFlags, LBCB_FLUSH_COPY );
+ }
+
+ } else if (FlagOn( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO )) {
+
+ //
+ // We loop until there are no more blocks or they aren't
+ // contiguous in the file or we have found an entry on the
+ // active queue or we found an entry where we want to explicitly
+ // flush a copy first.
+ //
+
+ while ((FirstLbcb->WorkqueLinks.Flink != &Lfcb->LbcbWorkque) &&
+ !FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {
+
+ LONGLONG ExpectedFileOffset;
+ PLBCB TempLbcb;
+
+ //
+ // Get the next Lbcb.
+ //
+
+ TempLbcb = CONTAINING_RECORD( FirstLbcb->WorkqueLinks.Flink,
+ LBCB,
+ WorkqueLinks );
+
+ //
+ // Break out of the loop if the file offset is not the
+ // expected value or the next entry is on the active queue.
+ //
+
+ ExpectedFileOffset = FirstLbcb->FileOffset + FirstLbcb->Length;
+
+ //
+ // We want to stop at this point if the next Lbcb is not
+ // the expected offset or we are packing the log file and
+ // the next Lbcb is on the active queue or we want to write
+ // a copy of the data before this page goes out.
+ //
+
+ if ((TempLbcb->FileOffset != ExpectedFileOffset) ||
+ (FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) &&
+ FlagOn( TempLbcb->LbcbFlags, LBCB_FLUSH_COPY | LBCB_ON_ACTIVE_QUEUE))) {
+
+ break;
+ }
+
+ //
+ // We can add this to our I/o. Increment the Io blocks
+ // and length of the transfer. Also check if this entry
+ // is the Last Entry specified by the caller.
+ //
+
+ *IoBlocks += 1;
+
+ *Length += (ULONG)TempLbcb->Length;
+
+ if (TempLbcb == TargetLbcb ) {
+
+ *ContainsLastEntry = TRUE;
+ }
+
+ //
+ // Use this entry as the current entry.
+ //
+
+ FirstLbcb = TempLbcb;
+ }
+ }
+
+ //
+ // If the current Lbcb is on the active queue and we aren't using
+ // a tail copy, then remove this from the active queue. If this
+ // not our target and removing this will cause us to swallow up
+ // part of our reserved quota then back up one Lbcb.
+ //
+
+ if (!(*UseTailCopy)
+ && FlagOn( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {
+
+ if ( Lfcb->CurrentAvailable < Lfcb->TotalUndoCommitment ) {
+
+ //
+ // Move back one file record.
+ //
+
+ *IoBlocks -= 1;
+ *Length -= (ULONG)FirstLbcb->Length;
+ *NextLbcb = FirstLbcb;
+
+ //
+ // Otherwise remove it from the active queue.
+ //
+
+ } else {
+
+ ClearFlag( FirstLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+ RemoveEntryList( &FirstLbcb->ActiveLinks );
+ }
+ }
+
+ //
+ // If we haven't found the Lbcb to restart from we will just use the
+ // next Lbcb after the last one found.
+ //
+
+ if (*NextLbcb == NULL) {
+
+ *NextLbcb = CONTAINING_RECORD( FirstLbcb->WorkqueLinks.Flink,
+ LBCB,
+ WorkqueLinks );
+ }
+
+ DebugTrace( 0, Dbg, "File Offset (Low) -> %08lx\n", FileOffset->LowPart );
+ DebugTrace( 0, Dbg, "File Offset (High) -> %08lx\n", FileOffset->HighPart );
+ DebugTrace( 0, Dbg, "Contains Last Entry -> %08x\n", *ContainsLastEntry );
+ DebugTrace( 0, Dbg, "LfsRestart -> %08x\n", *LfsRestart );
+ DebugTrace( 0, Dbg, "IoBlocks -> %08lx\n", *IoBlocks );
+ DebugTrace( -1, Dbg, "LfsFindFirstIo: Exit\n", 0 );
+
+ return;
+}
+
diff --git a/private/ntos/lfs/dirs b/private/ntos/lfs/dirs
new file mode 100644
index 000000000..81447efb1
--- /dev/null
+++ b/private/ntos/lfs/dirs
@@ -0,0 +1,22 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=mp
diff --git a/private/ntos/lfs/lbcbsup.c b/private/ntos/lfs/lbcbsup.c
new file mode 100644
index 000000000..71e601730
--- /dev/null
+++ b/private/ntos/lfs/lbcbsup.c
@@ -0,0 +1,464 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LbcbSup.c
+
+Abstract:
+
+ This module provides support for manipulating log buffer control blocks.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LBCB_SUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsFlushLbcb)
+#pragma alloc_text(PAGE, LfsFlushToLsnPriv)
+#pragma alloc_text(PAGE, LfsGetLbcb)
+#endif
+
+
+VOID
+LfsFlushLbcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to make sure the data within an Lbcb makes it out
+ to disk. The Lbcb must either already be in the workque or it must be
+ a restart Lbcb.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ Lbcb - This is the Lbcb to flush.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LSN LastLsn;
+ PLSN FlushedLsn;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFlushLbcb: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lbcb -> %08lx\n", Lbcb );
+
+ LastLsn = Lbcb->LastEndLsn;
+
+ //
+ // If this is a restart area we use the restart counter in the
+ // Lfcb. Otherwise we can use the LastFlushedLsn value in the
+ // Lfcb. This way we can determine that the Lbcb that interests
+ // us has made it out to disk.
+ //
+
+ if (LfsLbcbIsRestart( Lbcb )) {
+
+ FlushedLsn = &Lfcb->LastFlushedRestartLsn;
+
+ } else {
+
+ FlushedLsn = &Lfcb->LastFlushedLsn;
+ }
+
+ //
+ // We loop here until the desired Lsn has made it to disk.
+ // If we are able to do the I/O, we will perform it.
+ //
+
+ do {
+
+ //
+ //
+ // If we can do the Io, call down to flush the Lfcb.
+ //
+
+ if (Lfcb->LfsIoState == LfsNoIoInProgress) {
+
+ LfsFlushLfcb( Lfcb, Lbcb );
+
+ break;
+ }
+
+ //
+ // Otherwise we release the Lfcb and immediately wait on the event.
+ //
+
+ LfsReleaseLfcb( Lfcb );
+
+ KeWaitForSingleObject( &Lfcb->Sync->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ LfsAcquireLfcb( Lfcb );
+
+ } while ( LastLsn.QuadPart > FlushedLsn->QuadPart );
+
+ DebugTrace( -1, Dbg, "LfsFlushLbcb: Exit\n", 0 );
+ return;
+}
+
+
+VOID
+LfsFlushToLsnPriv (
+ IN PLFCB Lfcb,
+ IN LSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the worker routine which performs the work of flushing
+ a particular Lsn to disk. This routine is always called with the
+ Lfcb acquired. This routines makes no guarantee about whether the Lfcb
+ is acquired on exit.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ Lsn - This is the Lsn to flush to disk.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFlushToLsnPriv: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart );
+
+ //
+ // We check if the Lsn is in the valid range. Raising an
+ // exception if not.
+ //
+
+ if ( Lsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart ) {
+
+ DebugTrace( 0, Dbg, "Lsn is not in the file\n", 0 );
+ ExRaiseStatus( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // If the Lsn has already been flushed we are done.
+ // Otherwise we need to look through the workqueues and the
+ // active queue.
+ //
+
+ if ( Lsn.QuadPart > Lfcb->LastFlushedLsn.QuadPart ) {
+
+ PLIST_ENTRY ThisEntry;
+ PLBCB ThisLbcb;
+
+ //
+ // Check the workqueue first. We are looking for the last
+ // buffer block of a log page block which contains this
+ // Lsn.
+ //
+
+ ThisEntry = Lfcb->LbcbWorkque.Flink;
+
+ //
+ // We keep looping.
+ //
+
+ while (TRUE) {
+
+ ThisLbcb = CONTAINING_RECORD( ThisEntry,
+ LBCB,
+ WorkqueLinks );
+
+ //
+ // We pass over any restart areas. We also skip any
+ // Lbcb's which do not contain the end of a log record.
+ //
+
+ if (!LfsLbcbIsRestart( ThisLbcb )
+ && FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) {
+
+ //
+ // If the last complete Lsn in this Lbcb is greater or equal
+ // to the desired Lsn, we exit the loop.
+ //
+
+ if ( ThisLbcb->LastEndLsn.QuadPart >= Lsn.QuadPart ) {
+
+ break;
+ }
+ }
+
+ //
+ // Otherwise move to the next Lbcb.
+ //
+
+ ThisEntry = ThisEntry->Flink;
+
+ ASSERT( ThisEntry != &Lfcb->LbcbWorkque );
+ }
+
+ //
+ // If we are not supporting a packed log file and this Lbcb is from
+ // the active queue, we need to check that losing the tail of the
+ // will not swallow up any of our reserved space.
+ //
+
+ if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && FlagOn( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {
+
+ LONGLONG CurrentAvail;
+ LONGLONG UnusedBytes;
+
+ //
+ // Find the unused bytes.
+ //
+
+ UnusedBytes = 0;
+
+ LfsCurrentAvailSpace( Lfcb,
+ &CurrentAvail,
+ (PULONG)&UnusedBytes );
+
+ CurrentAvail = CurrentAvail - Lfcb->TotalUndoCommitment;
+
+ if ( UnusedBytes > CurrentAvail ) {
+
+ DebugTrace( -1, Dbg, "Have to preserve these bytes for possible aborts\n", 0 );
+
+ ExRaiseStatus( STATUS_LOG_FILE_FULL );
+ }
+
+ //
+ // We want to make sure we don't write any more data into this
+ // page. Remove this from the active queue.
+ //
+
+ RemoveEntryList( &ThisLbcb->ActiveLinks );
+ ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+ }
+
+ //
+ // We now have the Lbcb we want to flush to disk.
+ //
+
+ LfsFlushLbcb( Lfcb, ThisLbcb );
+ }
+
+ DebugTrace( -1, Dbg, "LfsFlushToLsnPriv: Exit\n", 0 );
+
+ return;
+}
+
+
+PLBCB
+LfsGetLbcb (
+ IN PLFCB Lfcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to add a Lbcb to the active queue.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+Return Value:
+
+ PLBCB - Pointer to the Lbcb allocated.
+
+--*/
+
+{
+ PLBCB Lbcb = NULL;
+ PVOID PageHeader;
+ PBCB PageHeaderBcb = NULL;
+
+ BOOLEAN WrappedOrUsaError;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsGetLbcb: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Pin the desired record page.
+ //
+
+ LfsPreparePinWriteData( Lfcb,
+ Lfcb->NextLogPage,
+ (ULONG)Lfcb->LogPageSize,
+ &PageHeader,
+ &PageHeaderBcb );
+
+ //
+ // Put our signature into the page so we won't fail if we
+ // see a previous 'BAAD' signature.
+ //
+
+ *((PULONG) PageHeader) = LFS_SIGNATURE_RECORD_PAGE_ULONG;
+
+ //
+ // Now allocate an Lbcb.
+ //
+
+ LfsAllocateLbcb( Lfcb, &Lbcb );
+
+ //
+ // If we are at the beginning of the file we test that the
+ // sequence number won't wrap to 0.
+ //
+
+ if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL )
+ && ( Lfcb->NextLogPage == Lfcb->FirstLogPage )) {
+
+ Lfcb->SeqNumber = Lfcb->SeqNumber + 1;
+
+ //
+ // If the sequence number is going from 0 to 1, then
+ // this is the first time the log file has wrapped. We want
+ // to remember this because it means that we can now do
+ // large spiral writes.
+ //
+
+ if (Int64ShllMod32( Lfcb->SeqNumber, Lfcb->FileDataBits ) == 0) {
+
+ DebugTrace( 0, Dbg, "Log sequence number about to wrap: Lfcb -> %08lx\n", Lfcb );
+ KeBugCheck( FILE_SYSTEM );
+ }
+
+ //
+ // If this number is greater or equal to the wrap sequence number in
+ // the Lfcb, set the wrap flag in the Lbcb.
+ //
+
+ if (!FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED )
+ && ( Lfcb->SeqNumber >= Lfcb->SeqNumberForWrap )) {
+
+ SetFlag( Lbcb->LbcbFlags, LBCB_LOG_WRAPPED );
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
+ }
+ }
+
+ //
+ // Now initialize the rest of the Lbcb fields.
+ //
+
+ Lbcb->FileOffset = Lfcb->NextLogPage;
+ Lbcb->SeqNumber = Lfcb->SeqNumber;
+ Lbcb->BufferOffset = Lfcb->LogPageDataOffset;
+
+ //
+ // Store the next page in the Lfcb.
+ //
+
+ LfsNextLogPageOffset( Lfcb,
+ Lfcb->NextLogPage,
+ &Lfcb->NextLogPage,
+ &WrappedOrUsaError );
+
+ Lbcb->Length = Lfcb->LogPageSize;
+ Lbcb->PageHeader = PageHeader;
+ Lbcb->LogPageBcb = PageHeaderBcb;
+
+ Lbcb->ResourceThread = ExGetCurrentResourceThread();
+
+ //
+ // If we are reusing a previous page then set a flag in
+ // the Lbcb to indicate that we should flush a copy
+ // first.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {
+
+ SetFlag( Lbcb->LbcbFlags, LBCB_FLUSH_COPY );
+ ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+
+ (ULONG)Lbcb->BufferOffset = Lfcb->ReusePageOffset;
+
+ Lbcb->Flags = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Flags;
+ Lbcb->LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn;
+ Lbcb->LastEndLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Header.Packed.LastEndLsn;
+ }
+
+ //
+ // Put the Lbcb on the active queue
+ //
+
+ InsertTailList( &Lfcb->LbcbActive, &Lbcb->ActiveLinks );
+
+ SetFlag( Lbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+
+ } finally {
+
+ DebugUnwind( LfsGetLbcb );
+
+ //
+ // If an error occurred, we need to clean up any blocks which
+ // have not been added to the active queue.
+ //
+
+ if (AbnormalTermination()) {
+
+ if (Lbcb != NULL) {
+
+ LfsDeallocateLbcb( Lfcb, Lbcb );
+ Lbcb = NULL;
+ }
+
+ //
+ // Unpin the system page if pinned.
+ //
+
+ if (PageHeaderBcb != NULL) {
+
+ CcUnpinData( PageHeaderBcb );
+ }
+ }
+
+ DebugTrace( -1, Dbg, "LfsGetLbcb: Exit\n", 0 );
+ }
+
+ return Lbcb;
+}
diff --git a/private/ntos/lfs/lfsdata.c b/private/ntos/lfs/lfsdata.c
new file mode 100644
index 000000000..53a22e58e
--- /dev/null
+++ b/private/ntos/lfs/lfsdata.c
@@ -0,0 +1,102 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LfsData.c
+
+Abstract:
+
+ This module declares the global data used by the Logging File Service.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+//
+// The global Lfs data record
+//
+
+LFS_DATA LfsData;
+
+//
+// Various large integer constants.
+//
+
+LARGE_INTEGER LfsLi0 = {0x00000000, 0x00000000};
+LARGE_INTEGER LfsLi1 = {0x00000001, 0x00000000};
+
+//
+// The following Lsn will never occur in a file, it is used to indicate
+// a non-lsn.
+//
+
+LSN LfsZeroLsn = {0x00000000, 0x00000000};
+
+#ifdef LFSDBG
+
+LONG LfsDebugTraceLevel = 0x0000000F;
+LONG LfsDebugTraceIndent = 0;
+
+#endif // LFSDBG
+
+
+LONG
+LfsExceptionFilter (
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to decide if we should or should not handle
+ an exception status that is being raised. It indicates that we should handle
+ the exception or bug check the system.
+
+Arguments:
+
+ ExceptionCode - Supplies the exception code to being checked.
+
+Return Value:
+
+ ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
+
+--*/
+
+{
+ NTSTATUS ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
+
+#ifdef NTFS_RESTART
+ ASSERT( (ExceptionCode != STATUS_DISK_CORRUPT_ERROR) &&
+ (ExceptionCode != STATUS_FILE_CORRUPT_ERROR) );
+#endif
+
+ //if (ExceptionCode != STATUS_LOG_FILE_FULL) {
+ //
+ // DbgPrint("Status not LOGFILE FULL, ExceptionPointers = %08lx\n", ExceptionPointer);
+ // DbgBreakPoint();
+ //}
+
+ if (!FsRtlIsNtstatusExpected( ExceptionCode )) {
+
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ } else {
+
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+}
diff --git a/private/ntos/lfs/lfsdata.h b/private/ntos/lfs/lfsdata.h
new file mode 100644
index 000000000..dbc21152f
--- /dev/null
+++ b/private/ntos/lfs/lfsdata.h
@@ -0,0 +1,193 @@
+/*++ BUILD Version: 0000 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ LfsData.c
+
+Abstract:
+
+ This module declares the global data used by the Log File Service.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#ifndef _LFSDATA_
+#define _LFSDATA_
+
+//
+// The global Lfs data record
+//
+
+extern LFS_DATA LfsData;
+
+//
+// Various large integer constants.
+//
+
+#define LfsMaximumFileSize (0x0000000100000000)
+
+extern LARGE_INTEGER LfsLi0;
+extern LARGE_INTEGER LfsLi1;
+
+//
+// The following Lsn is used as a starting point in the file.
+//
+
+extern LSN LfsStartingLsn;
+
+//
+// Turn on pseudo-asserts if NTFS_FREE_ASSERTS is defined.
+//
+
+#if !DBG
+#ifdef NTFS_FREE_ASSERTS
+#undef ASSERT
+#undef ASSERTMSG
+#define ASSERT(exp) if (!(exp)) { extern BOOLEAN KdDebuggerEnabled; DbgPrint("%s:%d %s\n",__FILE__,__LINE__,#exp); if (KdDebuggerEnabled) { DbgBreakPoint(); } }
+#define ASSERTMSG(msg,exp) if (!(exp)) { extern BOOLEAN KdDebuggerEnabled; DbgPrint("%s:%d %s %s\n",__FILE__,__LINE__,msg,#exp); if (KdDebuggerEnabled) { DbgBreakPoint(); } }
+#endif
+#endif
+
+//
+// The global Lfs debug level variable, its values are:
+//
+// 0x00000000 Always gets printed (used when about to bug check)
+//
+// 0x00000001 Error conditions
+// 0x00000002 Debug hooks
+// 0x00000004 Catch exceptions before completing Irp
+// 0x00000008 Unwinding during error conditions
+//
+// 0x00000010 Lfs initialization
+// 0x00000020 Lfs query log records
+// 0x00000040 Lfs write log records
+// 0x00000080 Lfs registry routines
+//
+// 0x00000100 Lfs worker thread routines
+// 0x00000200
+// 0x00000400
+// 0x00000800
+//
+// 0x00001000 Log page support routines
+// 0x00002000 Lsn support routines
+// 0x00004000 Miscellaneous support routines
+// 0x00008000 Support routines for cache operations
+//
+// 0x00010000 Structure support routines
+// 0x00020000 Verify/validate support routines
+// 0x00040000 Synchronization routines
+// 0x00080000 Log buffer support routines
+//
+// 0x00100000 Support routines for manipulating log records
+// 0x00200000 Support routines for manipulation lfs restart areas
+// 0x00400000 Support routines for client restart operations
+// 0x00800000
+//
+// 0x01000000
+// 0x02000000
+// 0x04000000
+// 0x08000000
+//
+// 0x10000000
+// 0x20000000
+// 0x40000000
+// 0x80000000
+//
+
+#ifdef LFSDBG
+
+#define DEBUG_TRACE_ERROR (0x00000001)
+#define DEBUG_TRACE_DEBUG_HOOKS (0x00000002)
+#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00000004)
+#define DEBUG_TRACE_UNWIND (0x00000008)
+#define DEBUG_TRACE_INITIALIZATION (0x00000010)
+#define DEBUG_TRACE_QUERY (0x00000020)
+#define DEBUG_TRACE_WRITE (0x00000040)
+#define DEBUG_TRACE_RESTART (0x00000080)
+#define DEBUG_TRACE_REGISTRY (0x00000100)
+#define DEBUG_TRACE_WORKER (0x00000200)
+#define DEBUG_TRACE_0x00000400 (0x00000400)
+#define DEBUG_TRACE_0x00000800 (0x00000800)
+#define DEBUG_TRACE_LOG_PAGE_SUP (0x00001000)
+#define DEBUG_TRACE_LSN_SUP (0x00002000)
+#define DEBUG_TRACE_MISC_SUP (0x00004000)
+#define DEBUG_TRACE_CACHE_SUP (0x00008000)
+#define DEBUG_TRACE_STRUC_SUP (0x00010000)
+#define DEBUG_TRACE_VERIFY_SUP (0x00020000)
+#define DEBUG_TRACE_SYNCH_SUP (0x00040000)
+#define DEBUG_TRACE_LBCB_SUP (0x00080000)
+#define DEBUG_TRACE_LOG_RECORD_SUP (0x00100000)
+#define DEBUG_TRACE_RESTART_SUP (0x00200000)
+#define DEBUG_TRACE_0x00400000 (0x00400000)
+#define DEBUG_TRACE_0x00800000 (0x00800000)
+#define DEBUG_TRACE_0x01000000 (0x01000000)
+#define DEBUG_TRACE_0x02000000 (0x02000000)
+#define DEBUG_TRACE_0x04000000 (0x04000000)
+#define DEBUG_TRACE_0x08000000 (0x08000000)
+#define DEBUG_TRACE_0x10000000 (0x10000000)
+#define DEBUG_TRACE_0x20000000 (0x20000000)
+#define DEBUG_TRACE_0x40000000 (0x40000000)
+#define DEBUG_TRACE_0x80000000 (0x80000000)
+
+extern LONG LfsDebugTraceLevel;
+extern LONG LfsDebugTraceIndent;
+
+#define DebugTrace(INDENT,LEVEL,X,Y) { \
+ LONG _i; \
+ if (((LEVEL) == 0) || (LfsDebugTraceLevel & (LEVEL))) { \
+ _i = (ULONG)PsGetCurrentThread(); \
+ DbgPrint("%08lx:",_i); \
+ if ((INDENT) < 0) { \
+ LfsDebugTraceIndent += (INDENT); \
+ } \
+ if (LfsDebugTraceIndent < 0) { \
+ LfsDebugTraceIndent = 0; \
+ } \
+ for (_i = 0; _i < LfsDebugTraceIndent; _i += 1) { \
+ DbgPrint(" "); \
+ } \
+ DbgPrint(X,Y); \
+ if ((INDENT) > 0) { \
+ LfsDebugTraceIndent += (INDENT); \
+ } \
+ } \
+}
+
+#define DebugDump(STR,LEVEL,PTR) { \
+ ULONG _i; \
+ VOID LfsDump(); \
+ if (((LEVEL) == 0) || (LfsDebugTraceLevel & (LEVEL))) { \
+ _i = (ULONG)PsGetCurrentThread(); \
+ DbgPrint("%08lx:",_i); \
+ DbgPrint(STR); \
+ if (PTR != NULL) {LfsDump(PTR);} \
+ DbgBreakPoint(); \
+ } \
+}
+
+#define DebugUnwind(X) { \
+ if (AbnormalTermination()) { \
+ DebugTrace(0, DEBUG_TRACE_UNWIND, #X ", Abnormal termination.\n", 0); \
+ } \
+}
+
+#define DebugDoit(X) {X;}
+
+#else
+
+#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;}
+#define DebugDump(STR,LEVEL,PTR) {NOTHING;}
+#define DebugUnwind(X) {NOTHING;}
+#define DebugDoit(X) {NOTHING;}
+
+#endif // LFSDBG
+
+#endif // _LFSDATA_
+
diff --git a/private/ntos/lfs/lfsdisk.h b/private/ntos/lfs/lfsdisk.h
new file mode 100644
index 000000000..198486340
--- /dev/null
+++ b/private/ntos/lfs/lfsdisk.h
@@ -0,0 +1,506 @@
+/*++ BUILD Version: 0000 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ LfsDisk.h
+
+Abstract:
+
+ This module defines the on-disk structures present in the log file.
+
+Author:
+
+ Brian Andrew [BrianAn] 13-June-1991
+
+Revision History:
+
+IMPORTANT NOTE:
+
+ The Log File Service will by used on systems that require that on-disk
+ structures guarantee the natural alignment of all arithmetic quantities
+ up to and including quad-word (64-bit) numbers. Therefore, all Lfs
+ on-disk structures are quad-word aligned, etc.
+
+--*/
+
+#ifndef _LFSDISK_
+#define _LFSDISK_
+
+#define MINIMUM_LFS_PAGES 0x00000030
+#define MINIMUM_LFS_CLIENTS 1
+
+//
+// The following macros are used to set and query with respect to the
+// update sequence arrays.
+//
+
+#define UpdateSequenceStructureSize( MSH ) \
+ ((((PMULTI_SECTOR_HEADER) (MSH))->UpdateSequenceArraySize - 1) * SEQUENCE_NUMBER_STRIDE)
+
+#define UpdateSequenceArraySize( STRUCT_SIZE ) \
+ ((STRUCT_SIZE) / SEQUENCE_NUMBER_STRIDE + 1)
+
+#define FIRST_STRIDE \
+ (SEQUENCE_NUMBER_STRIDE - sizeof( UPDATE_SEQUENCE_NUMBER ))
+
+
+//
+// Log client ID. This is used to uniquely identify a client for a
+// particular log file.
+//
+
+typedef struct _LFS_CLIENT_ID {
+
+ USHORT SeqNumber;
+ USHORT ClientIndex;
+
+} LFS_CLIENT_ID, *PLFS_CLIENT_ID;
+
+
+//
+// Log Record Header. This is the header that begins every Log Record in
+// the log file.
+//
+
+typedef struct _LFS_RECORD_HEADER {
+
+ //
+ // Log File Sequence Number of this log record.
+ //
+
+ LSN ThisLsn;
+
+ //
+ // The following fields are used to back link Lsn's. The ClientPrevious
+ // and ClientUndoNextLsn fields are used by a client to link his log
+ // records.
+ //
+
+ LSN ClientPreviousLsn;
+ LSN ClientUndoNextLsn;
+
+ //
+ // The following field is the size of data area for this record. The
+ // log record header will be padded if necessary to fill to a 64-bit
+ // boundary, so the client data will begin on a 64-bit boundary to
+ // insure that all of his data is 64-bit aligned. The below value
+ // has not been padded to 64 bits however.
+ //
+
+ ULONG ClientDataLength;
+
+ //
+ // Client ID. This identifies the owner of this log record. The owner
+ // is uniquely identified by his offset in the client array and the
+ // sequence number associated with that client record.
+ //
+
+ LFS_CLIENT_ID ClientId;
+
+ //
+ // This the Log Record type. This could be a commit protocol record,
+ // a client restart area or a client update record.
+ //
+
+ LFS_RECORD_TYPE RecordType;
+
+ //
+ // Transaction ID. This is used externally by a client (Transaction
+ // Manager) to group log file entries.
+ //
+
+ TRANSACTION_ID TransactionId;
+
+ //
+ // Log record flags.
+ //
+
+ USHORT Flags;
+
+ //
+ // Alignment field.
+ //
+
+ USHORT AlignWord;
+
+} LFS_RECORD_HEADER, *PLFS_RECORD_HEADER;
+
+#define LOG_RECORD_MULTI_PAGE (0x0001)
+
+#define LFS_RECORD_HEADER_SIZE QuadAlign( sizeof( LFS_RECORD_HEADER ))
+
+
+//
+// Following are the version specific fields in the record page header.
+//
+
+typedef struct _LFS_UNPACKED_RECORD_PAGE {
+
+ //
+ // This gives us the offset of the free space in the page.
+ //
+
+ USHORT NextRecordOffset;
+
+ USHORT WordAlign;
+
+ //
+ // Reserved. The following array is reserved for possible future use.
+ //
+
+ USHORT Reserved;
+
+ //
+ // Update Sequence Array. Used to protect the page block.
+ //
+
+ UPDATE_SEQUENCE_ARRAY UpdateSequenceArray;
+
+} LFS_UNPACKED_RECORD_PAGE, *PLFS_UNPACKED_RECORD_PAGE;
+
+typedef struct _LFS_PACKED_RECORD_PAGE {
+
+ //
+ // This gives us the offset of the free space in the page.
+ //
+
+ USHORT NextRecordOffset;
+
+ USHORT WordAlign;
+
+ ULONG DWordAlign;
+
+ //
+ // The following is the Lsn for the last log record which ends on the page.
+ //
+
+ LSN LastEndLsn;
+
+ //
+ // Update Sequence Array. Used to protect the page block.
+ //
+
+ UPDATE_SEQUENCE_ARRAY UpdateSequenceArray;
+
+} LFS_PACKED_RECORD_PAGE, *PLFS_PACKED_RECORD_PAGE;
+
+
+//
+// Log Record Page Header. This structure is present at the beginning of each
+// log file page in the client record section.
+//
+
+typedef struct _LFS_RECORD_PAGE_HEADER {
+
+ //
+ // Cache multisector protection header.
+ //
+
+ MULTI_SECTOR_HEADER MultiSectorHeader;
+
+ union {
+
+ //
+ // Highest Lsn in this log file page. This field is only for
+ // regular log pages.
+ //
+
+ LSN LastLsn;
+
+ //
+ // Log file offset. This is for the tail copies and indicates the
+ // location in the file where the original lays. In this case the
+ // LastLsn field above can be obtained from the last ending Lsn
+ // field in the PACKED_RECORD_PAGE structure.
+ //
+
+ LONGLONG FileOffset;
+
+ } Copy;
+
+ //
+ // Page Header Flags. These are the same flags that are stored in the
+ // Lbcb->Flags field.
+ //
+ // LOG_PAGE_LOG_RECORD_END - Page contains the end of a log record
+ //
+
+ ULONG Flags;
+
+ //
+ // I/O Page Position. The following fields are used to determine
+ // where this log page resides within a Lfs I/O transfer.
+ //
+
+ USHORT PageCount;
+ USHORT PagePosition;
+
+ //
+ // The following is the difference between version 1.1 and earlier.
+ //
+
+ union {
+
+ LFS_UNPACKED_RECORD_PAGE Unpacked;
+ LFS_PACKED_RECORD_PAGE Packed;
+
+ } Header;
+
+} LFS_RECORD_PAGE_HEADER, *PLFS_RECORD_PAGE_HEADER;
+
+#define LOG_PAGE_LOG_RECORD_END (0x00000001)
+
+#define LFS_UNPACKED_RECORD_PAGE_HEADER_SIZE ( \
+ FIELD_OFFSET( LFS_RECORD_PAGE_HEADER, Header.Unpacked.UpdateSequenceArray ) \
+)
+
+#define LFS_PACKED_RECORD_PAGE_HEADER_SIZE ( \
+ FIELD_OFFSET( LFS_RECORD_PAGE_HEADER, Header.Packed.UpdateSequenceArray ) \
+)
+
+
+//
+// Log Restart Page Header. This structure is at the head of the restart
+// areas in a log file.
+//
+
+typedef struct _LFS_RESTART_PAGE_HEADER {
+
+ //
+ // Cache multisector protection header.
+ //
+
+ MULTI_SECTOR_HEADER MultiSectorHeader;
+
+ //
+ // This is the last Lsn found by checkdisk for this volume.
+ //
+
+ LSN ChkDskLsn;
+
+ //
+ // System page size. This is the page size of the system which
+ // initialized the log file. Unless the log file has been gracefully
+ // shutdown (there are no clients with restart areas), it is a fatal
+ // error to attempt to write to a log file on a system with a differen
+ // page size.
+ //
+
+ ULONG SystemPageSize;
+
+ //
+ // Log Page Size. This is the log page size used for this log file.
+ // The entire Lfs restart area must fit on a single log page.
+ //
+
+ ULONG LogPageSize;
+
+ //
+ // Lfs restart area offset. This is the offset from the start of this
+ // structure to the Lfs restart area.
+ //
+
+ USHORT RestartOffset;
+
+ //
+ // The indicates major and minor versions. Note that the pre-release versions
+ // have -1 in both positions. Major version 0 indicates the transition
+ // from Beta to USA support.
+ //
+ // Major Version
+ //
+ // -1 Beta Version
+ // 0 Transition
+ // 1 Update sequence support.
+ //
+
+ SHORT MinorVersion;
+ SHORT MajorVersion;
+
+ //
+ // Update Sequence Array. Used to protect the page block.
+ //
+
+ UPDATE_SEQUENCE_ARRAY UpdateSequenceArray;
+
+} LFS_RESTART_PAGE_HEADER, *PLFS_RESTART_PAGE_HEADER;
+
+#define LFS_RESTART_PAGE_HEADER_SIZE ( \
+ FIELD_OFFSET( LFS_RESTART_PAGE_HEADER, UpdateSequenceArray ) \
+)
+
+//
+// Id strings for the page headers.
+//
+
+#define LFS_SIGNATURE_RESTART_PAGE "RSTR"
+#define LFS_SIGNATURE_RESTART_PAGE_ULONG 0x52545352
+#define LFS_SIGNATURE_RECORD_PAGE "RCRD"
+#define LFS_SIGNATURE_RECORD_PAGE_ULONG 0x44524352
+#define LFS_SIGNATURE_BAD_USA "BAAD"
+#define LFS_SIGNATURE_BAD_USA_ULONG 0x44414142
+#define LFS_SIGNATURE_MODIFIED "CHKD"
+#define LFS_SIGNATURE_MODIFIED_ULONG 0x444b4843
+#define LFS_SIGNATURE_UNINITIALIZED "\377\377\377\377"
+#define LFS_SIGNATURE_UNINITIALIZED_ULONG 0xffffffff
+
+
+//
+// Log Client Record. A log client record exists for each client user of
+// the log file. One of these is in each Lfs restart area.
+//
+
+#define LFS_NO_CLIENT 0xffff
+#define LFS_CLIENT_NAME_MAX 64
+
+typedef struct _LFS_CLIENT_RECORD {
+
+ //
+ // Oldest Lsn. This is the oldest Lsn that this client requires to
+ // be in the log file.
+ //
+
+ LSN OldestLsn;
+
+ //
+ // Client Restart Lsn. This is the Lsn of the latest client restart
+ // area written to the disk. A reserved Lsn will indicate that no
+ // restart area exists for this client.
+ //
+
+ LSN ClientRestartLsn;
+
+ //
+ //
+ // Previous/Next client area. These are the indexes into an array of
+ // Log Client Records for the previous and next client records.
+ //
+
+ USHORT PrevClient;
+ USHORT NextClient;
+
+ //
+ // Sequence Number. Incremented whenever this record is reused. This
+ // will happen whenever a client opens (reopens) the log file and has
+ // no current restart area.
+
+ USHORT SeqNumber;
+
+ //
+ // Alignment field.
+ //
+
+ USHORT AlignWord;
+
+ //
+ // Align the entire record.
+ //
+
+ ULONG AlignDWord;
+
+ //
+ // The following fields are used to describe the client name. A client
+ // name consists of at most 32 Unicode character (64 bytes). The Log
+ // file service will treat client names as case sensitive.
+ //
+
+ ULONG ClientNameLength;
+
+ WCHAR ClientName[LFS_CLIENT_NAME_MAX];
+
+} LFS_CLIENT_RECORD, *PLFS_CLIENT_RECORD;
+
+
+//
+// Lfs Restart Area. Two copies of these will exist at the beginning of the
+// log file.
+//
+
+typedef struct _LFS_RESTART_AREA {
+
+ //
+ // Current Lsn. This is periodic snapshot of the current logical end of
+ // log file to facilitate restart.
+ //
+
+ LSN CurrentLsn;
+
+ //
+ // Number of Clients. This is the maximum number of clients supported
+ // for this log file.
+ //
+
+ USHORT LogClients;
+
+ //
+ // The following are indexes into the client record arrays. The client
+ // records are linked into two lists. A free list of client records and
+ // an in-use list of records.
+ //
+
+ USHORT ClientFreeList;
+ USHORT ClientInUseList;
+
+ //
+ // Flag field.
+ //
+ // RESTART_SINGLE_PAGE_IO All log pages written 1 by 1
+ //
+
+ USHORT Flags;
+
+ //
+ // The following is the number of bits to use for the sequence number.
+ //
+
+ ULONG SeqNumberBits;
+
+ //
+ // Length of this restart area.
+ //
+
+ USHORT RestartAreaLength;
+
+ //
+ // Offset from the start of this structure to the client array.
+ // Ignored in versions prior to 1.1
+ //
+
+ USHORT ClientArrayOffset;
+
+ //
+ // Usable log file size. We will stop sharing the value in the page header.
+ //
+
+ LONGLONG FileSize;
+
+ //
+ // DataLength of last Lsn. This doesn't include the length of
+ // the Lfs header.
+ //
+
+ ULONG LastLsnDataLength;
+
+ //
+ // The following apply to log pages. This is the log page data offset and
+ // the length of the log record header. Ignored in versions prior to 1.1
+ //
+
+ USHORT RecordHeaderLength;
+ USHORT LogPageDataOffset;
+
+ //
+ // Client data.
+ //
+
+ LFS_CLIENT_RECORD LogClientArray[1];
+
+} LFS_RESTART_AREA, *PLFS_RESTART_AREA;
+
+#define RESTART_SINGLE_PAGE_IO (0x0001)
+
+#define LFS_RESTART_AREA_SIZE (FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray ))
+
+#endif // _LFSDISK_
diff --git a/private/ntos/lfs/lfsprocs.h b/private/ntos/lfs/lfsprocs.h
new file mode 100644
index 000000000..881c9c884
--- /dev/null
+++ b/private/ntos/lfs/lfsprocs.h
@@ -0,0 +1,740 @@
+/*++ BUILD Version: 0000 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ LfsProcs.h
+
+Abstract:
+
+ This module defines all of the globally used procedures in the Log
+ File Service.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#ifndef _LFSPROCS_
+#define _LFSPROCS_
+
+#include <ntos.h>
+#include <string.h>
+#include <zwapi.h>
+#include <lfs.h>
+#include <fsrtl.h>
+
+#include "nodetype.h"
+#include "LfsDisk.h"
+#include "LfsStruc.h"
+#include "LfsData.h"
+
+//
+// Tag all of our allocations if tagging is turned on
+//
+
+#undef FsRtlAllocatePool
+#undef FsRtlAllocatePoolWithQuota
+
+#define FsRtlAllocatePool(a,b) FsRtlAllocatePoolWithTag(a,b,' sfL')
+#define FsRtlAllocatePoolWithQuota(a,b) FsRtlAllocatePoolWithQuotaTag(a,b,' sfL')
+
+
+//
+// The following routines provide an interface with the cache package.
+// They are contained in 'CacheSup.c'.
+//
+
+NTSTATUS
+LfsPinOrMapData (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN PinData,
+ IN BOOLEAN AllowErrors,
+ IN BOOLEAN IgnoreUsaErrors,
+ OUT PBOOLEAN UsaError,
+ OUT PVOID *Buffer,
+ OUT PBCB *Bcb
+ );
+
+//
+// VOID
+// LfsPreparePinWriteData (
+// IN PLFCB Lfcb,
+// IN LONGLONG FileOffset,
+// IN ULONG Length,
+// OUT PVOID *Buffer,
+// OUT PBCB *Bcb
+// );
+//
+
+#define LfsPreparePinWriteData(L,FO,LEN,BUF,B) { \
+ LONGLONG _LocalFileOffset = (FO); \
+ CcPreparePinWrite( (L)->FileObject, \
+ (PLARGE_INTEGER)&_LocalFileOffset, \
+ (LEN), \
+ FALSE, \
+ TRUE, \
+ (B), \
+ (BUF) ); \
+}
+
+VOID
+LfsPinOrMapLogRecordHeader (
+ IN PLFCB Lfcb,
+ IN LSN Lsn,
+ IN BOOLEAN PinData,
+ IN BOOLEAN IgnoreUsaErrors,
+ OUT PBOOLEAN UsaError,
+ OUT PLFS_RECORD_HEADER *RecordHeader,
+ OUT PBCB *Bcb
+ );
+
+VOID
+LfsCopyReadLogRecord (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_HEADER RecordHeader,
+ OUT PVOID Buffer
+ );
+
+VOID
+LfsFlushLfcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ );
+
+BOOLEAN
+LfsReadRestart (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileSize,
+ IN BOOLEAN FirstRestart,
+ OUT PLONGLONG RestartPageOffset,
+ OUT PLFS_RESTART_PAGE_HEADER *RestartPage,
+ OUT PBCB *RestartPageBcb,
+ OUT PBOOLEAN ChkdskWasRun,
+ OUT PBOOLEAN ValidPage,
+ OUT PBOOLEAN UninitializedFile,
+ OUT PBOOLEAN LogPacked,
+ OUT PLSN LastLsn
+ );
+
+
+//
+// The following routines manipulate buffer control blocks. They are
+// contained in 'LbcbSup.c'
+//
+
+VOID
+LfsFlushLbcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ );
+
+VOID
+LfsFlushToLsnPriv (
+ IN PLFCB Lfcb,
+ IN LSN Lsn
+ );
+
+PLBCB
+LfsGetLbcb (
+ IN PLFCB Lfcb
+ );
+
+
+//
+// The following routines are in LfsData.c
+//
+
+LONG
+LfsExceptionFilter (
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ );
+
+
+//
+// Log page support routines. The following routines manipulate and
+// modify log pages. They are contained in 'LogPgSup.c'
+//
+
+//
+// VOID
+// LfsTruncateOffsetToLogPage (
+// IN PLFCB Lfcb,
+// IN LONGLONG LargeInt,
+// OUT PLONGLONG Result
+// );
+//
+// ULONG
+// LfsLogPageOffset (
+// IN PLFCB Lfcb,
+// IN ULONG Integer
+// );
+//
+
+#define LfsTruncateOffsetToLogPage(LFCB,LI,OUTLI) \
+ *(OUTLI) = LI; \
+ *((PULONG)(OUTLI)) &= (LFCB)->SystemPageInverseMask
+
+#define LfsLogPageOffset(LFCB,INT) \
+ (INT & (LFCB)->LogPageMask)
+
+VOID
+LfsNextLogPageOffset (
+ IN PLFCB Lfcb,
+ IN LONGLONG CurrentLogPageOffset,
+ OUT PLONGLONG NextLogPageOffset,
+ OUT PBOOLEAN Wrapped
+ );
+
+
+//
+// The following routines provide support for dealing with log records. They
+// are contained in 'LogRcSup.c'
+//
+
+BOOLEAN
+LfsWriteLogRecordIntoLogPage (
+ IN PLFCB Lfcb,
+ IN PLCH Lch,
+ IN ULONG NumberOfWriteEntries,
+ IN PLFS_WRITE_ENTRY WriteEntries,
+ IN LFS_RECORD_TYPE RecordType,
+ IN TRANSACTION_ID *TransactionId OPTIONAL,
+ IN LSN ClientUndoNextLsn OPTIONAL,
+ IN LSN ClientPreviousLsn OPTIONAL,
+ IN LONG UndoRequirement,
+ IN BOOLEAN ForceToDisk,
+ OUT PLSN Lsn
+ );
+
+
+//
+// Lsn support routines. The following routines provide support for
+// manipulating Lsn values. They are contained in 'LsnSup.c'
+//
+
+//
+// LSN
+// LfsFileOffsetToLsn (
+// IN PLFCB Lfcb,
+// IN LONGLONG FileOffset,
+// IN LONGLONG SequenceNumber
+// );
+//
+// BOOLEAN
+// LfsIsLsnInFile (
+// IN PLFCB Lfcb,
+// IN LSN Lsn
+// );
+//
+// LSN
+// LfsComputeLsnFromLbcb (
+// IN PLFCB Lfcb,
+// IN PLBCB Lbcb
+// );
+//
+// VOID
+// LfsTruncateLsnToLogPage (
+// IN PLFCB Lfcb,
+// IN LSN Lsn,
+// OUT PLONGLONG FileOffset
+// );
+//
+// LONGLONG
+// LfsLsnToFileOffset (
+// IN PLFCB Lfcb,
+// IN LSN Lsn
+// );
+//
+// LONGLONG
+// LfsLsnToSeqNumber (
+// IN PLFCB Lfcb,
+// IN LSN Lsn
+// );
+//
+// ULONG
+// LfsLsnToPageOffset (
+// IN PLFCB Lfcb,
+// IN LSN Lsn
+// );
+//
+
+#define LfsFileOffsetToLsn(LFCB,FO,SN) ( \
+ (((ULONGLONG)(FO)) >> 3) + Int64ShllMod32((SN), (LFCB)->FileDataBits) \
+)
+
+#define LfsIsLsnInFile(LFCB,LSN) \
+ (/*xxGeq*/( (LSN).QuadPart >= ((LFCB)->OldestLsn).QuadPart ) \
+ && /*xxLeq*/( (LSN).QuadPart <= ((LFCB)->RestartArea->CurrentLsn).QuadPart ))
+
+#define LfsComputeLsnFromLbcb(LFCB,LBCB) ( \
+ LfsFileOffsetToLsn( LFCB, \
+ (LBCB)->FileOffset + (LBCB)->BufferOffset, \
+ (LBCB)->SeqNumber ) \
+)
+
+#define LfsTruncateLsnToLogPage(LFCB,LSN,FO) { \
+ *(FO) = LfsLsnToFileOffset( LFCB, LSN ); \
+ *((PULONG)(FO)) &= (LFCB)->LogPageInverseMask; \
+}
+
+#define LfsLsnToFileOffset(LFCB,LSN) \
+ /*xxShr*/( ((ULONGLONG)/*xxShl*/( (LSN).QuadPart << (LFCB)->SeqNumberBits )) >> ((LFCB)->SeqNumberBits - 3) )
+
+#define LfsLsnToSeqNumber(LFCB,LSN) \
+ /*xxShr*/Int64ShrlMod32( ((ULONGLONG)(LSN).QuadPart), (LFCB)->FileDataBits )
+
+#define LfsLsnToPageOffset(LFCB,LSN) \
+ LfsLogPageOffset( LFCB, (LSN).LowPart << 3 )
+
+VOID
+LfsLsnFinalOffset (
+ IN PLFCB Lfcb,
+ IN LSN Lsn,
+ IN ULONG DataLength,
+ OUT PLONGLONG FinalOffset
+ );
+
+BOOLEAN
+LfsFindNextLsn (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_HEADER RecordHeader,
+ OUT PLSN Lsn
+ );
+
+
+//
+// The following routines support the Lfs restart areas. They are contained
+// in 'RstrtSup.c'
+//
+
+VOID
+LfsWriteLfsRestart (
+ IN PLFCB Lfcb,
+ IN ULONG ThisRestartSize,
+ IN BOOLEAN WaitForIo
+ );
+
+VOID
+LfsFindOldestClientLsn (
+ IN PLFS_RESTART_AREA RestartArea,
+ IN PLFS_CLIENT_RECORD ClientArray,
+ OUT PLSN OldestLsn
+ );
+
+
+//
+// The following routines are used for managing the structures allocated
+// by us. They are contained in 'StrucSup.c'
+//
+
+PLFCB
+LfsAllocateLfcb (
+ );
+
+
+VOID
+LfsDeallocateLfcb (
+ IN PLFCB Lfcb,
+ IN BOOLEAN CompleteTeardown
+ );
+
+VOID
+LfsAllocateLbcb (
+ IN PLFCB Lfcb,
+ OUT PLBCB *Lbcb
+ );
+
+VOID
+LfsDeallocateLbcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ );
+
+//
+// VOID
+// LfsAllocateLcb (
+// OUT PLCB *NewLcb
+// );
+//
+// VOID
+// LfsInitializeLcb (
+// IN PLCB Lcb,
+// IN LFS_CLIENT_ID ClientId,
+// IN LFS_CONTEXT_MODE ContextMode
+// );
+//
+// VOID
+// LfsDeallocateLcb (
+// IN PLCB Lcb
+// );
+//
+// VOID
+// LfsAllocateLch (
+// OUT PLCH *Lch
+// );
+//
+// VOID
+// LfsDeallocateLch (
+// IN PLCH Lch
+// );
+//
+// VOID
+// LfsAllocateRestartArea (
+// OUT PLFS_RESTART_AREA *RestartArea,
+// ULONG Size
+// );
+//
+// VOID
+// LfsDeallocateRestartArea (
+// IN PLFS_RESTART_AREA RestartArea
+// );
+//
+// BOOLEAN
+// LfsLbcbIsRestart (
+// IN PLBCB Lbcb
+// );
+//
+
+#define LfsAllocateLcb(NEW) { \
+ (*NEW) = FsRtlAllocatePool( PagedPool, sizeof( LCB )); \
+ RtlZeroMemory( (*NEW), sizeof( LCB )); \
+ (*NEW)->NodeTypeCode = LFS_NTC_LCB; \
+ (*NEW)->NodeByteSize = sizeof( LCB ); \
+}
+
+#define LfsInitializeLcb(LCB,ID,MODE) \
+ (LCB)->ClientId = ID; \
+ (LCB)->ContextMode = MODE
+
+#define LfsDeallocateLcb(LCB) { \
+ if ((LCB)->RecordHeaderBcb != NULL) { \
+ CcUnpinData( (LCB)->RecordHeaderBcb ); \
+ } \
+ if ((LCB)->CurrentLogRecord != NULL \
+ && (LCB)->AuxilaryBuffer == TRUE) { \
+ ExFreePool( (LCB)->CurrentLogRecord ); \
+ } \
+ ExFreePool( LCB ); \
+}
+
+#define LfsAllocateLch(NEW) { \
+ *(NEW) = FsRtlAllocatePool( PagedPool, sizeof( LCH )); \
+ RtlZeroMemory( (*NEW), sizeof( LCH )); \
+ (*(NEW))->NodeTypeCode = LFS_NTC_LCH; \
+ (*(NEW))->NodeByteSize = sizeof( LCH ); \
+}
+
+#define LfsDeallocateLch(LCH) \
+ ExFreePool( LCH )
+
+#define LfsAllocateRestartArea(RS,SIZE) \
+ *(RS) = FsRtlAllocatePool( PagedPool, (SIZE) ); \
+ RtlZeroMemory( *(RS), (SIZE) )
+
+#define LfsDeallocateRestartArea(RS) \
+ ExFreePool( RS )
+
+#define LfsLbcbIsRestart(LBCB) \
+ (FlagOn( (LBCB)->LbcbFlags, LBCB_RESTART_LBCB ))
+
+
+//
+// The following routines provide synchronization support for the Lfs
+// shared structures. They are contained in 'SyncSup.c'
+//
+
+//
+// VOID
+// LfsAcquireLfsData (
+// );
+//
+// VOID
+// LfsReleaseLfsData (
+// );
+//
+// VOID
+// LfsAcquireLfcb (
+// IN PLFCB Lfcb
+// );
+//
+// VOID
+// LfsReleaseLfcb (
+// IN PLFCB Lfcb
+// );
+//
+// VOID
+// LfsAcquireLch (
+// IN PLCH Lch
+// );
+//
+// VOID
+// LfsReleaseLfcb (
+// IN PLCH Lch
+// );
+//
+
+#define LfsAcquireLfsData() \
+ KeWaitForSingleObject( &LfsData.Event, \
+ Executive, \
+ KernelMode, \
+ FALSE, \
+ NULL )
+
+#define LfsReleaseLfsData() \
+ KeSetEvent( &LfsData.Event, 0 , FALSE )
+
+#define LfsAcquireLfcb(LFCB) \
+ ExAcquireResourceExclusive( &(LFCB)->Sync->Resource, TRUE )
+
+#define LfsReleaseLfcb(LFCB) \
+ if ((LFCB)->Sync->Resource.OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) {\
+ ExReleaseResource( &(LFCB)->Sync->Resource ); \
+ }
+
+#define LfsAcquireLch(LCH) \
+ ExAcquireResourceExclusive( &(LCH)->Sync->Resource, TRUE )
+
+#define LfsReleaseLch(LCH) \
+ if ((LCH)->Sync->Resource.OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) { \
+ ExReleaseResource( &(LCH)->Sync->Resource ); \
+ }
+
+
+//
+// The following routines are used to check various structures for validity
+// and comparability. They are contained in 'VerfySup.c'.
+//
+
+VOID
+LfsCurrentAvailSpace (
+ IN PLFCB Lfcb,
+ OUT PLONGLONG CurrentAvailSpace,
+ OUT PULONG CurrentPageBytes
+ );
+
+BOOLEAN
+LfsVerifyLogSpaceAvail (
+ IN PLFCB Lfcb,
+ IN PLCH Lch,
+ IN ULONG RemainingLogBytes,
+ IN LONG UndoRequirement,
+ IN BOOLEAN ForceToDisk
+ );
+
+VOID
+LfsFindCurrentAvail (
+ IN PLFCB Lfcb
+ );
+
+//
+// VOID
+// LfsValidateLch (
+// IN PLCH Lch
+// );
+//
+// VOID
+// LfsValidateClientId (
+// IN PLFCB Lfcb,
+// IN PLCH Lch
+// );
+//
+// BOOLEAN
+// LfsVerifyClientLsnInRange (
+// IN PLFCB Lfcb,
+// IN PLFS_CLIENT_RECORD ClientRecord,
+// IN LSN Lsn
+// );
+//
+// BOOLEAN
+// LfsClientIdMatch (
+// IN PLFS_CLIENT_ID ClientA,
+// IN PLFS_CLIENT_ID ClientB
+// )
+//
+// VOID
+// LfsValidateLcb (
+// IN PLFS_CONTEXT_BLOCK Lcb,
+// IN PLCH Lch
+// )
+//
+
+#define LfsValidateLch(LCH) \
+ if ((LCH) == NULL \
+ || (LCH)->NodeTypeCode != LFS_NTC_LCH \
+ || ((LCH)->Lfcb != NULL \
+ && (LCH)->Lfcb->NodeTypeCode != LFS_NTC_LFCB)) { \
+ \
+ ExRaiseStatus( STATUS_ACCESS_DENIED ); \
+ }
+
+#define LfsValidateClientId(LFCB,LCH) \
+ if ((LCH)->ClientId.ClientIndex >= (LFCB)->RestartArea->LogClients \
+ || (LCH)->ClientId.SeqNumber \
+ != Add2Ptr( Lfcb->ClientArray, \
+ (LCH)->ClientArrayByteOffset, \
+ PLFS_CLIENT_RECORD )->SeqNumber) { \
+ ExRaiseStatus( STATUS_ACCESS_DENIED ); \
+ }
+
+#define LfsVerifyClientLsnInRange(LFCB,CLIENT,LSN) \
+ (/*xxGeq*/( (LSN).QuadPart >= ((CLIENT)->OldestLsn).QuadPart ) \
+ && /*xxLeq*/( (LSN).QuadPart <= ((LFCB)->RestartArea->CurrentLsn).QuadPart ) \
+ && /*xxNeqZero*/( (LSN).QuadPart != 0 ))
+
+#define LfsClientIdMatch(CLIENT_A,CLIENT_B) \
+ ((BOOLEAN) ((CLIENT_A)->SeqNumber == (CLIENT_B)->SeqNumber \
+ && (CLIENT_A)->ClientIndex == (CLIENT_B)->ClientIndex))
+
+#define LfsValidateLcb(LCB,LCH) \
+ if (LCB == NULL \
+ || (LCB)->NodeTypeCode != LFS_NTC_LCB \
+ || !LfsClientIdMatch( &(LCB)->ClientId, &(LCH)->ClientId )) { \
+ ExRaiseStatus( STATUS_ACCESS_DENIED ); \
+ }
+
+
+//
+// Miscellaneous support routines
+//
+
+//
+// ULONG
+// FlagOn (
+// IN ULONG Flags,
+// IN ULONG SingleFlag
+// );
+//
+// BOOLEAN
+// BooleanFlagOn (
+// IN ULONG Flags,
+// IN ULONG SingleFlag
+// );
+//
+// VOID
+// SetFlag (
+// IN ULONG Flags,
+// IN ULONG SingleFlag
+// );
+//
+// VOID
+// ClearFlag (
+// IN ULONG Flags,
+// IN ULONG SingleFlag
+// );
+//
+
+//
+// This macro returns TRUE if a flag in a set of flags is on and FALSE
+// otherwise
+//
+
+#define FlagOn(F,SF) ( \
+ (((F) & (SF))) \
+)
+
+#define BooleanFlagOn(F,SF) ( \
+ (BOOLEAN)(((F) & (SF)) != 0) \
+)
+
+#define SetFlag(Flags,SingleFlag) { \
+ (Flags) |= (SingleFlag); \
+}
+
+#define ClearFlag(Flags,SingleFlag) { \
+ (Flags) &= ~(SingleFlag); \
+}
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up word
+// value
+//
+
+#define WordAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up longword
+// value
+//
+
+#define LongAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up quadword
+// value
+//
+
+#define QuadAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \
+ )
+
+//
+// This macro will up a 64 bit value to the next quad align boundary.
+//
+
+#define LiQuadAlign(LI,OUT) { \
+ *(OUT) = /*xxAdd*/( (LI) + 7 ); \
+ *((PULONG)(OUT)) &= 0xfffffff8; \
+}
+
+//
+// CAST
+// Add2Ptr (
+// IN PVOID Pointer,
+// IN ULONG Increment
+// IN (CAST)
+// );
+//
+// ULONG
+// PtrOffset (
+// IN PVOID BasePtr,
+// IN PVOID OffsetPtr
+// );
+//
+
+#define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC)))
+
+#define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG)(OFFSET) - (ULONG)(BASE)))
+
+
+//
+// The following macros are used to establish the semantics needed
+// to do a return from within a try-finally clause. As a rule every
+// try clause must end with a label call try_exit. For example,
+//
+// try {
+// :
+// :
+//
+// try_exit: NOTHING;
+// } finally {
+//
+// :
+// :
+// }
+//
+// Every return statement executed inside of a try clause should use the
+// try_return macro. If the compiler fully supports the try-finally construct
+// then the macro should be
+//
+// #define try_return(S) { return(S); }
+//
+// If the compiler does not support the try-finally construct then the macro
+// should be
+//
+// #define try_return(S) { S; goto try_exit; }
+//
+
+#define try_return(S) { S; goto try_exit; }
+
+#endif // _LFSPROCS_
diff --git a/private/ntos/lfs/lfsques.txt b/private/ntos/lfs/lfsques.txt
new file mode 100644
index 000000000..bb8e08816
--- /dev/null
+++ b/private/ntos/lfs/lfsques.txt
@@ -0,0 +1,190 @@
+What is the structure of a transaction ID. This is generated by the client
+(or transaction manager) and is passed through to the disk by Lfs.
+
+Can't store the next Lsn in a log record header because its exact location
+may not be known when a log record is being written (in-memory) or a
+page block is being written to disk. This is because the write to disk
+may occur before the next log record is received and there is no way to
+know if it will fit in the remaining part of the file or will wrap and
+be written to the beginning of the file.
+
+Log records that require multi-page allocation blocks will begin at the
+beginning of a page block. Then the fields which describe the data shift
+can be in a page header record and not a log record header.
+
+We restrict the data size of a log record to 32 bits.
+
+Log record types: We have an enumerated type for all log record, not
+a main type and subtypes. i.e. all commit protocol records are defined
+at the top level.
+
+Multi-page blocks: Page headers indicate whether they are the first
+or last page. During Lfs restart these pages may be walked through.
+As far as the analysis algorithm goes, these can be thought of as a
+single unit. All page(s) must be found or this will be considered
+a bad page(s), the next page then must be read to discover if this is
+a fatal error or the point where the system crashed.
+
+Looking up multipage via Lsn: Given Lsn pin appropriate page and look
+at log record. If single page then done. Otherwise, unpin page and
+pin multiple pages.
+
+For what operations do we need to check the update sequence array in
+the page headers.
+
+Log file header prior to restart areas. This will be a single system page
+and contain the log file base values which will allow both copies of the
+restart area to be found. This information will be duplicated in the restart
+areas so that if the sector containing the file header goes bad, an
+intelligent choice can be made for where to search for the first Lfs
+restart area. (Or we could duplicate it in the first x sectors).
+
+Should we checksum the log file header.
+
+Added client Id to the log record header.
+
+Is it reasonable to use a USHORT to index into the client array and a USHORT
+for the client sequence number.
+
+Do we even need a sequence number. Will the index number be sufficient to
+walk forward through the log records. When will the index be reused, only
+when a client closes his loghandle. In that case it will be impossible to
+start searching from an out of date Lsn.
+
+Change InitializeReadContext to LfsContextReadFirst and return the first Lsn.
+Read Next is LfsContextReadNext.
+
+May not need the LfsPreviousLsn in the log record header.
+
+Log record enumeration. Walking forwards, client will probably run
+out unexpectedly.
+
+Log record enumeration. Walking backwards, take Lsn, determine Log page
+boundary. Pin log page, if multiple pages unpin and repin multiple pages.
+
+Walking backwards, how likely are we to want to stay in the same log
+page for several records.
+
+Enumerating. It is possible we are pinning pages for read and may be trying
+to write them at the same time.
+
+If we have a page pinned multiple times, will that prevent it from being
+flushed with a ccflush call.
+
+Will a client want to look at several log records at once, or only one
+at a time. The current interface supports only one at a time.
+
+LOG CONTEXT BLOCK
+
+ No synchronization of access to the log context block. Client
+ shares the block at his own risk.
+
+ How to resolve that enumeration may pin (for a while) a block of
+ pages that are trying to be flushed.
+
+ Enumeration (in the forward direction) may be trying to read a lsn
+ as it is being written. Solution, write the log record before updating
+ the last Lsn in the page header.
+
+ This may make the LastLsn number in the context block obsolete as it
+ may be outdated. However, any later Lsn's wouldn't have existed when
+ the client started this call. Should we hide all new log records
+ from him.
+
+Log file. How big may it be (ULONG, LARGE_INT)
+
+Log buffer control block. Access via global spinlock. Sets up frame for
+next write and then releases it. Count is kept in block of active writers,
+can't be flushed until active count is zero. Does this make sense, how
+do we wait until active count is zero.
+
+Lsn should be 96 bits. 64 bit file offset and 32 bit sequence value.
+The interface with the Lfs provides that the address of this structure
+be passed.
+
+Is it expensive to allocate and deallocate an event each time a new buffer
+is used.
+
+Can the restart areas offset safely be a ULONG.
+
+How can log service initiate a sector revectoring.
+
+Start log file sequence number at 1, then 0 indicates we have just wrapped.
+
+Since write operations to the log file must be serialized, there is no
+reason not to let the worker thread perform all the writes.
+
+Operations:
+
+ Given an Lsn:
+
+ Find the next Lsn
+ Find the previous client Lsn
+ Find the client undo next lsn.
+
+ Given a log record.
+
+ Find the client owner of the record
+ Find the transaction ID for the record
+
+ Log context blocks
+
+ Allocate and deallocate
+ Write into the fields.
+ Determine if the next/prev Lsn resides in the same block of pages.
+ Pin and unpin pages in the log file.
+ Allocate and deallocate the user's buffer.
+ Synchronize access to the context block (is this needed)
+
+ Log client area
+
+ Allocate and deallocate
+ Verify the client as valid.
+
+ Log buffer control blocks
+
+ Allocate and deallocate
+ Link into control block list for a particular log file.
+ Allocate and deallocate buffer associated with control block.
+ Determine the size required for the buffer.
+ Determine where buffer fits in log file.
+ Determine where the next log record should be in the buffer.
+ Determine when all the authorized writes to the file have completed.
+ Syncronize access to the fields of the contol block.
+ Find the last Lsn for the page block.
+ Determine when the flush operation to the log file has completed.
+ Determine if the flush operation to the file was successful.
+ Determine if the next log record fits in this buffer.
+ Determine if user thread can be used to write log pages to the log file
+
+
+ Log file control block.
+
+ Allocate/Deallocate structure.
+ Link log file control blocks to global record
+ Know whether the sequence number has wrapped.
+ Modify the fields of the structure.
+ Write a restart area to the disk.
+ Change the current restart area.
+
+ Log data area
+
+ Allocate and Deallocate structure
+ Sychronize access to the fields.
+ Add and remove fields from the workque.
+
+We need a structure to use for a log file information block.
+
+July 1,
+
+How do we store the update sequence stride on disk to read on a different
+system??
+
+What do we do when there is an I/O error writing to the disk?? How does
+an operation know that a previous I/O error has occurred??
+
+Do multiple clients open the same log file with the same file object or
+the same file?? (We could always check if the context pointers match)
+
+How do we disable the update sequence mechanics in order to originally
+recognize the log file.
diff --git a/private/ntos/lfs/lfsstruc.h b/private/ntos/lfs/lfsstruc.h
new file mode 100644
index 000000000..e19a7e237
--- /dev/null
+++ b/private/ntos/lfs/lfsstruc.h
@@ -0,0 +1,614 @@
+/*++ BUILD Version: 0000 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ LfsStruc.h
+
+Abstract:
+
+ This module defines the data structures that make up the major internal
+ part of the Log File Service.
+
+Author:
+
+ Brian Andrew [BrianAn] 13-June-1991
+
+Revision History:
+
+--*/
+
+#ifndef _LFSSTRUC_
+#define _LFSSTRUC_
+
+typedef PVOID PBCB; //**** Bcb's are now part of the cache module
+
+
+//
+// Log Context Block. A pointer to this structure is returned to the user
+// when a client is reading a particular set of log records from the log
+// file.
+//
+
+typedef struct _LCB {
+
+ //
+ // The type and size of this record (must be LFS_NTC_LCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Log record header. This is the mapped log record header and bcb
+ // for the record header of the current Lsn.
+ //
+
+ struct _LFS_RECORD_HEADER *RecordHeader;
+ PBCB RecordHeaderBcb;
+
+ //
+ // Context Mode. This is the mode governing the log record lookup. We
+ // can look backwards via the ClientUndoNextLsn or ClientPreviousLsn.
+ // We can also look forwards by walking through all the log records and
+ // comparing ClientId fields.
+ //
+
+ LFS_CONTEXT_MODE ContextMode;
+
+ //
+ // Client Id. This is the client ID for the log records being returned.
+ //
+
+ LFS_CLIENT_ID ClientId;
+
+ //
+ // Log record pointer. This is the address returned to the user as the
+ // log record referred to by CurrentLsn. If we allocated a buffer to
+ // hold the record, we need to deallocate it as necessary.
+ //
+ // This field is either the actual mapped log record or a pointer to
+ // an auxilary buffer allocated by the Lfs.
+ //
+
+ PVOID CurrentLogRecord;
+ BOOLEAN AuxilaryBuffer;
+
+} LCB, *PLCB;
+
+
+//
+// Lfcb synchronization. This is the synchronization structure used by the Lfcb.
+//
+
+typedef struct _LFCB_SYNC {
+
+ //
+ // Principal Lfcb Resource.
+ //
+
+ ERESOURCE Resource;
+
+ //
+ // Notification Event. This event is set to the Signalled state when
+ // pages are flushed to the cache file. Any waiters will then check
+ // to see if the Lsn they're waiting for made it to disk.
+ //
+
+ KEVENT Event;
+
+ //
+ // User Count. Number of clients using this structure. We will deallocate
+ // when all clients are gone.
+ //
+
+ ULONG UserCount;
+
+} LFCB_SYNC, *PLFCB_SYNC;
+
+
+//
+// Log Client Structure. The Lfs allocates one of these for each active
+// client. The address of this structure will be returned to the user
+// as a log handle.
+//
+
+typedef struct _LCH {
+
+ //
+ // The type and size of this record (must be LFS_NTC_LCH)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Links for all the client handles on an Lfcb.
+ //
+
+ LIST_ENTRY LchLinks;
+
+ //
+ // Log File Control Block. This is the log file for this log handle.
+ //
+
+ struct _LFCB *Lfcb;
+
+ //
+ // Client Id. This refers to the client record for this client in the
+ // Lfs restart area.
+ //
+
+ LFS_CLIENT_ID ClientId;
+
+ //
+ // The following is the number of bytes this client has asked to
+ // have reserved in the log file. It includes the space
+ // for the log record headers.
+ //
+
+ LONGLONG ClientUndoCommitment;
+
+ //
+ // Byte offset in the client array.
+ //
+
+ ULONG ClientArrayByteOffset;
+
+ //
+ // Pointer to the resource in the Lfcb. We access the resource with
+ // this pointer for the times when the lfcb has been deleted.
+ //
+
+ PLFCB_SYNC Sync;
+
+} LCH, *PLCH;
+
+
+//
+// Log Buffer Control Block. A buffer control block is associated with
+// each of the log buffers. They are used to serialize access to the
+// log file.
+//
+
+typedef struct _LBCB {
+
+ //
+ // The type and size of this record (must be LFS_NTC_LBCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Buffer Block Links. These fields are used to link the buffer blocks
+ // together.
+ //
+
+ LIST_ENTRY WorkqueLinks;
+ LIST_ENTRY ActiveLinks;
+
+ //
+ // Log file position and length. This is the location in the log file to write
+ // out this buffer.
+ //
+
+ LONGLONG FileOffset;
+ LONGLONG Length;
+
+ //
+ // Sequence number. This is the sequence number for log records which
+ // begin on this page.
+ //
+
+ LONGLONG SeqNumber;
+
+ //
+ // Next Offset. This is the next offset to write a log record in the
+ // this log page. Stored as a large integer to facilitate large
+ // integer operations.
+ //
+
+ LONGLONG BufferOffset;
+
+ //
+ // Buffer. This field points to the buffer containing the log page
+ // for this block. For a log record page this is a pointer to
+ // a pinned cache buffer, for a log restart page, this is a pointer
+ // to an auxilary buffer.
+ //
+
+ PVOID PageHeader;
+
+ //
+ // Bcb for Log Page Block. This is the Bcb for the pinned data.
+ // If this buffer block describes an Lfs restart area, this field is NULL.
+ //
+
+ PBCB LogPageBcb;
+
+ //
+ // Last Lsn. This is the Lsn for the last log record on this page. We delay
+ // writing it until the page is flushed, storing it here instead.
+ //
+
+ LSN LastLsn;
+
+ //
+ // Last complete Lsn. This is the Lsn for the last log record which ends
+ // on this page.
+ //
+
+ LSN LastEndLsn;
+
+ //
+ // Page Flags. These are the flags associated with this log page.
+ // We store them in the Lbcb until the page is written. They flags
+ // to use are the same as in the log record page header.
+ //
+ // LOG_PAGE_LOG_RECORD_END - Page contains the end of a log record
+ // LOG_PAGE_PACKED - Page contains packed log records
+ // LOG_PAGE_TAIL_COPY - Page is a copy of the log file end
+ //
+
+ ULONG Flags;
+
+ //
+ // Lbcb flags. These are flags used to describe this Lbcb.
+ //
+ // LBCB_LOG_WRAPPED - Lbcb has wrapped the log file
+ // LBCB_ON_ACTIVE_QUEUE - Lbcb is on the active queue
+ // LBCB_NOT_EMPTY - Page has existing log record
+ // LBCB_FLUSH_COPY - Write copy of this page first
+ // LBCB_RESTART_LBCB - This Lbcb contains a restart page
+ //
+
+ ULONG LbcbFlags;
+
+ //
+ // This is the thread which has locked the log page.
+ //
+
+ ERESOURCE_THREAD ResourceThread;
+
+} LBCB, *PLBCB;
+
+#define LBCB_LOG_WRAPPED (0x00000001)
+#define LBCB_ON_ACTIVE_QUEUE (0x00000002)
+#define LBCB_NOT_EMPTY (0x00000004)
+#define LBCB_FLUSH_COPY (0x00000008)
+#define LBCB_RESTART_LBCB (0x00000020)
+
+
+//
+// Log file data. This data structure is used on a per-log file basis.
+//
+
+typedef enum _LFS_IO_STATE {
+
+ LfsNoIoInProgress = 0,
+ LfsClientThreadIo
+
+} LFS_IO_STATE;
+
+typedef struct _LFCB {
+
+ //
+ // The type and size of this record (must be LFS_NTC_LFCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Lfcb Links. The following links the file control blocks to the
+ // global data structure.
+ //
+
+ LIST_ENTRY LfcbLinks;
+
+ //
+ // Lch Links. The following links all of the handles for the Lfcb.
+ //
+
+ LIST_ENTRY LchLinks;
+
+ //
+ //
+ // File Object. This is the file object for the log file.
+ //
+
+ PFILE_OBJECT FileObject;
+
+ //
+ // Log File Size. This is the size of the log file.
+ // The second value is the size proposed by this open.
+ //
+
+ LONGLONG FileSize;
+
+ //
+ // System page size and masks.
+ //
+
+ LONGLONG SystemPageSize;
+ ULONG SystemPageMask;
+ ULONG SystemPageInverseMask;
+
+ //
+ // Log page size, masks and shift count to do multiplication and division
+ // of log pages.
+ //
+
+ LONGLONG LogPageSize;
+ ULONG LogPageMask;
+ ULONG LogPageInverseMask;
+ ULONG LogPageShift;
+
+ //
+ // First log page. This is the offset in the file of the first
+ // log page with log records.
+ //
+
+ LONGLONG FirstLogPage;
+
+ //
+ // Next log page offset. This is the offset of the next log page to use.
+ // If we are reusing this page we store the offset to begin with.
+ //
+
+ LONGLONG NextLogPage;
+ ULONG ReusePageOffset;
+
+ //
+ // Data Offset. This is the offset within a log page of the data that
+ // appears on that page. This will be the actual restart data for
+ // an Lfs restart page, or the beginning of log record data for a log
+ // record page.
+ //
+
+ ULONG RestartDataOffset;
+ LONGLONG LogPageDataOffset;
+
+ //
+ // Data Size. This is the amount of data that may be stored on a
+ // log page. It is included here because it is frequently used. It
+ // is simply the log page size minus the data offset.
+ //
+
+ ULONG RestartDataSize;
+ LONGLONG LogPageDataSize;
+
+ //
+ // Record header size. This is the size to use for the record headers
+ // when reading the log file.
+ //
+
+ USHORT RecordHeaderLength;
+
+ //
+ // Sequence number. This is the number of times we have cycled through
+ // the log file. The wrap sequence number is used to confirm that we
+ // have gone through the entire file at least once. When we write a
+ // log record page for an Lsn with this sequence number, then we have
+ // cycled through the file.
+ //
+
+ LONGLONG SeqNumber;
+ LONGLONG SeqNumberForWrap;
+ ULONG SeqNumberBits;
+ ULONG FileDataBits;
+
+ //
+ // Buffer Block Links. The following links the buffer blocks for this
+ // log file.
+ //
+
+ LIST_ENTRY LbcbWorkque;
+ LIST_ENTRY LbcbActive;
+
+ PLBCB ActiveTail;
+ PLBCB PrevTail;
+
+ //
+ // The enumerated type indicates if there is an active write for
+ // this log file and whether it is being done by an Lfs or
+ // client thread.
+ //
+
+ LFS_IO_STATE LfsIoState;
+
+ //
+ // Current Restart Area. The following is the in-memory image of the
+ // next restart area. We also store a pointer to the client data
+ // array in the restart area. The client array offset is from the start of
+ // the restart area.
+ //
+
+ PLFS_RESTART_AREA RestartArea;
+ PLFS_CLIENT_RECORD ClientArray;
+ USHORT ClientArrayOffset;
+ USHORT ClientNameOffset;
+
+ //
+ // Restart Area size. This is the usable size of the restart area.
+ //
+
+ ULONG RestartAreaSize;
+ USHORT LogClients;
+
+ //
+ // Initial Restart area. If true, then the in-memory restart area is to
+ // be written to the first position on the disk.
+ //
+
+ BOOLEAN InitialRestartArea;
+
+ //
+ // The following pseudo Lsn's are used to track when restart areas
+ // are flushed to the disk.
+ //
+
+ LSN NextRestartLsn;
+ LSN LastFlushedRestartLsn;
+
+ //
+ // The following is the earliest Lsn we will guarantee is still in the
+ // log file.
+ //
+
+ LSN OldestLsn;
+
+ //
+ // The following is the file offset of the oldest Lsn in the system.
+ // We redundantly store it in this form since we will be constantly
+ // checking if a new log record will write over part of the file
+ // we are trying to maintain.
+ //
+
+ LONGLONG OldestLsnOffset;
+
+ //
+ // Last Flushed Lsn. The following is the last Lsn guaranteed to
+ // be flushed to the disk.
+ //
+
+ LSN LastFlushedLsn;
+
+ //
+ //
+ // The following fields are used to track current usage in the log file.
+ //
+ // TotalAvailable - is the total number of bytes available for
+ // log records. It is the number of log pages times the
+ // data size of each page.
+ //
+ // TotalAvailInPages - is the total number of bytes in the log
+ // pages for log records. This is TotalAvailable without
+ // subtracting the size of the page headers.
+ //
+ // TotalUndoCommitment - is the number of bytes reserved for
+ // possible abort operations. This includes space for
+ // log record headers as well.
+ //
+ // MaxCurrentAvail - is the maximum available in all pages
+ // subtracting the page header and any reserved tail.
+ //
+ // CurrentAvailable - is the total number of bytes available in
+ // unused pages in the log file.
+ //
+ // ReservedLogPageSize - is the number of bytes on a page available
+ // for reservation.
+ //
+
+ LONGLONG TotalAvailable;
+ LONGLONG TotalAvailInPages;
+ LONGLONG TotalUndoCommitment;
+ LONGLONG MaxCurrentAvail;
+ LONGLONG CurrentAvailable;
+
+ LONGLONG ReservedLogPageSize;
+
+ //
+ // The following fields are used to store information about the
+ // update sequence arrays.
+ //
+
+ USHORT RestartUsaOffset;
+ USHORT RestartUsaArraySize;
+
+ USHORT LogRecordUsaOffset;
+ USHORT LogRecordUsaArraySize;
+
+ //
+ // Major and minor version numbers.
+ //
+
+ SHORT MajorVersion;
+ SHORT MinorVersion;
+
+ //
+ // Log File Flags.
+ //
+ // LFCB_LOG_WRAPPED - We found an Lbcb which wraps the log file
+ // LFCB_MULTIPLE_PAGE_IO - Write multiple pages if possible
+ // LFCB_NO_LAST_LSN - There are no log records to return
+ // LFCB_PACK_LOG - Pack the records into the pages
+ // LFCB_REUSE_TAIL - We will be reusing the tail of the log file after restart
+ // LFCB_NO_OLDEST_LSN - There is no oldest page being reserved
+ //
+
+ ULONG Flags;
+
+ //
+ // The following are the spare Lbcb's for the volume and a field with
+ // the count for these.
+ //
+
+ ULONG SpareLbcbCount;
+ LIST_ENTRY SpareLbcbList;
+
+ //
+ // The following structure synchronizes access to this structure.
+ //
+
+ PLFCB_SYNC Sync;
+
+} LFCB, *PLFCB;
+
+#define LFCB_LOG_WRAPPED (0x00000001)
+#define LFCB_MULTIPLE_PAGE_IO (0x00000002)
+#define LFCB_NO_LAST_LSN (0x00000004)
+#define LFCB_PACK_LOG (0x00000008)
+#define LFCB_REUSE_TAIL (0x00000010)
+#define LFCB_NO_OLDEST_LSN (0x00000020)
+#define LFCB_LOG_FILE_CORRUPT (0x00000040)
+#define LFCB_FINAL_SHUTDOWN (0x00000080)
+#define LFCB_READ_FIRST_RESTART (0x00000100)
+#define LFCB_READ_SECOND_RESTART (0x00000200)
+
+#define LFCB_RESERVE_LBCB_COUNT (5)
+#define LFCB_MAX_LBCB_COUNT (25)
+
+
+//
+// Global Log Data. The following structure has only one instance and
+// maintains global information for the entire logging service.
+//
+
+typedef struct _LFS_DATA {
+
+ //
+ // The type and size of this record (must be LFS_NTC_DATA)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // The following field links all of the Log File Control Blocks for
+ // the logging system.
+ //
+
+ LIST_ENTRY LfcbLinks;
+
+ //
+ // Flag field.
+ //
+
+ ULONG Flags;
+
+ //
+ // The following event controls access to this structure.
+ //
+
+ KEVENT Event;
+
+} LFS_DATA, *PLFS_DATA;
+
+#define LFS_DATA_INIT_FAILED 0x00000001
+#define LFS_DATA_INITIALIZED 0x00000002
+
+#endif // _LFSSTRUC_
+
diff --git a/private/ntos/lfs/logpgsup.c b/private/ntos/lfs/logpgsup.c
new file mode 100644
index 000000000..fadd2f912
--- /dev/null
+++ b/private/ntos/lfs/logpgsup.c
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LogPgSup.c
+
+Abstract:
+
+ This module implements support for manipulating log pages.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LOG_PAGE_SUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsNextLogPageOffset)
+#endif
+
+
+VOID
+LfsNextLogPageOffset (
+ IN PLFCB Lfcb,
+ IN LONGLONG CurrentLogPageOffset,
+ OUT PLONGLONG NextLogPageOffset,
+ OUT PBOOLEAN Wrapped
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will compute the offset in the log file of the next log
+ page.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ CurrentLogPageOffset - This is the file offset of the current log page.
+
+ NextLogPageOffset - Address to store the next log page to use.
+
+ Wrapped - This is a pointer to a boolean variable that, if present,
+ we use to indicate whether we wrapped in the log file.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsNextLogPageOffset: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "CurrentLogPageOffset (Low) -> %08lx\n", CurrentLogPageOffset.LowPart );
+ DebugTrace( 0, Dbg, "CurrentLogPageOffset (High) -> %08lx\n", CurrentLogPageOffset.HighPart );
+ DebugTrace( 0, Dbg, "Wrapped -> %08lx\n", Wrapped );
+
+ //
+ // We add the log page size to the current log offset.
+ //
+
+ LfsTruncateOffsetToLogPage( Lfcb, CurrentLogPageOffset, &CurrentLogPageOffset );
+ *NextLogPageOffset = CurrentLogPageOffset + Lfcb->LogPageSize; //**** xxAdd( CurrentLogPageOffset, Lfcb->LogPageSize );
+
+ //
+ // If the result is larger than the file, we use the first page offset
+ // in the file.
+ //
+
+ if ( *NextLogPageOffset >= Lfcb->FileSize ) { //**** xxGeq( *NextLogPageOffset, Lfcb->FileSize )
+
+ *NextLogPageOffset = Lfcb->FirstLogPage;
+
+ *Wrapped = TRUE;
+
+ } else {
+
+ *Wrapped = FALSE;
+ }
+
+ DebugTrace( 0, Dbg, "NextLogPageOffset (Low) -> %08lx\n", NextLogPageOffset->LowPart );
+ DebugTrace( 0, Dbg, "NextLogPageOffset (High) -> %08lx\n", NextLogPageOffset->HighPart );
+ DebugTrace( 0, Dbg, "Wrapped -> %08x\n", *Wrapped );
+ DebugTrace( -1, Dbg, "LfsNextLogPageOffset: Exit\n", 0 );
+
+ return;
+}
+
diff --git a/private/ntos/lfs/logrcsup.c b/private/ntos/lfs/logrcsup.c
new file mode 100644
index 000000000..76f8de3a2
--- /dev/null
+++ b/private/ntos/lfs/logrcsup.c
@@ -0,0 +1,658 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LogRcSup.c
+
+Abstract:
+
+ This module implements support for dealing with log records, both
+ writing and recovering them.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LOG_RECORD_SUP)
+
+VOID
+LfsPrepareLfcbForLogRecord (
+ IN OUT PLFCB Lfcb,
+ IN ULONG RemainingLogBytes
+ );
+
+VOID
+LfsTransferLogBytes (
+ IN PLBCB Lbcb,
+ IN OUT PLFS_WRITE_ENTRY *ThisWriteEntry,
+ IN OUT PCHAR *CurrentBuffer,
+ IN OUT PULONG CurrentByteCount,
+ IN OUT PULONG PadBytes,
+ IN OUT PULONG RemainingPageBytes,
+ IN OUT PULONG RemainingLogBytes
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsPrepareLfcbForLogRecord)
+#pragma alloc_text(PAGE, LfsTransferLogBytes)
+#pragma alloc_text(PAGE, LfsWriteLogRecordIntoLogPage)
+#endif
+
+
+BOOLEAN
+LfsWriteLogRecordIntoLogPage (
+ IN PLFCB Lfcb,
+ IN PLCH Lch,
+ IN ULONG NumberOfWriteEntries,
+ IN PLFS_WRITE_ENTRY WriteEntries,
+ IN LFS_RECORD_TYPE RecordType,
+ IN TRANSACTION_ID *TransactionId OPTIONAL,
+ IN LSN ClientUndoNextLsn OPTIONAL,
+ IN LSN ClientPreviousLsn OPTIONAL,
+ IN LONG UndoRequirement,
+ IN BOOLEAN ForceToDisk,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to write a log record into the log file
+ using the cache manager. If there is room in the current log
+ page it is added to that. Otherwise we allocate a new log page
+ and write the log record header for this log page. We then
+ write the log record into the remaining bytes of this page and
+ into any subsequent pages if needed.
+
+Arguments:
+
+ Lfcb - File control block for this log file.
+
+ Lch - This is the client handle, we may update the undo space for this
+ client.
+
+ NumberOfWriteEntries - Number of components of the log record.
+
+ WriteEntries - Pointer to an array of write entries.
+
+ UndoRequirement - Signed value indicating the requirement to write
+ an abort log record for this log record. A negative
+ value indicates that this is the abort record.
+
+ RecordType - The Lfs-defined type of this log record.
+
+ TransactionId - Pointer to the transaction structure containing the
+ Id for transaction containing this operation.
+
+ ClientUndoNextLsn - This is the Lsn provided by the client for use
+ in his restart. Will be the zero Lsn for
+ a restart log record.
+
+ ClientPreviousLsn - This is the Lsn provided by the client for use
+ in his restart. Will the the zero Lsn for a
+ restart log record.
+
+ UndoRequirement - This is the data size for the undo record for
+ this log record.
+
+ ForceToDisk - Indicates if this log record will be flushed immediately
+ to disk.
+
+ Lsn - A pointer to store the Lsn for this log record.
+
+Return Value:
+
+ BOOLEAN - Advisory, TRUE indicates that less than 1/4 of the log file is
+ available.
+
+--*/
+
+{
+ PLFS_WRITE_ENTRY ThisWriteEntry;
+
+ ULONG RemainingLogBytes;
+ ULONG OriginalLogBytes;
+
+ ULONG RemainingPageBytes;
+ ULONG HeaderAdjust;
+
+ PLBCB ThisLbcb;
+
+ LSN NextLsn;
+
+ PLFS_RECORD_HEADER RecordHeader;
+
+ PCHAR CurrentBuffer;
+ ULONG CurrentByteCount;
+ ULONG PadBytes;
+
+ BOOLEAN LogFileFull = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsWriteLogRecordIntoLogPage: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lch -> %08lx\n", Lch );
+ DebugTrace( 0, Dbg, "Number of Write Entries -> %08lx\n", NumberOfWriteEntries );
+ DebugTrace( 0, Dbg, "Write Entries -> %08lx\n", WriteEntries );
+ DebugTrace( 0, Dbg, "Record Type -> %08lx\n", RecordType );
+ DebugTrace( 0, Dbg, "Transaction Id -> %08lx\n", TransactionId );
+ DebugTrace( 0, Dbg, "ClientUndoNextLsn (Low) -> %08lx\n", ClientUndoNextLsn.LowPart );
+ DebugTrace( 0, Dbg, "ClientUndoNextLsn (High) -> %08lx\n", ClientUndoNextLsn.HighPart );
+ DebugTrace( 0, Dbg, "ClientPreviousLsn (Low) -> %08lx\n", ClientPreviousLsn.LowPart );
+ DebugTrace( 0, Dbg, "ClientPreviousLsn (High) -> %08lx\n", ClientPreviousLsn.HighPart );
+ DebugTrace( 0, Dbg, "UndoRequirement -> %08lx\n", UndoRequirement );
+ DebugTrace( 0, Dbg, "ForceToDisk -> %04x\n", ForceToDisk );
+
+ //
+ // We compute the size of this log record.
+ //
+
+ ThisWriteEntry = WriteEntries;
+
+ RemainingLogBytes = 0;
+
+ while (NumberOfWriteEntries--) {
+
+ RemainingLogBytes += QuadAlign( ThisWriteEntry->ByteLength );
+
+ ThisWriteEntry++;
+ }
+
+ OriginalLogBytes = RemainingLogBytes;
+
+ ThisWriteEntry = WriteEntries;
+
+ LogFileFull = LfsVerifyLogSpaceAvail( Lfcb,
+ Lch,
+ RemainingLogBytes,
+ UndoRequirement,
+ ForceToDisk );
+
+ //
+ // We update the Lfcb so that we can start putting the log record into
+ // the top of the Lbcb active list.
+ //
+
+ LfsPrepareLfcbForLogRecord( Lfcb,
+ RemainingLogBytes + Lfcb->RecordHeaderLength );
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize - (ULONG)ThisLbcb->BufferOffset;
+
+ //
+ // Compute the Lsn starting in the next log buffer.
+ //
+
+ NextLsn.QuadPart = LfsComputeLsnFromLbcb( Lfcb, ThisLbcb );
+
+ //
+ // We get a pointer to the log record header and the start of the
+ // log record in the pinned buffer.
+ //
+
+ RecordHeader = Add2Ptr( ThisLbcb->PageHeader,
+ (ULONG)ThisLbcb->BufferOffset,
+ PLFS_RECORD_HEADER );
+
+ //
+ // We update the record header.
+ //
+
+ //
+ // Zero out the structure initially.
+ //
+
+ RtlZeroMemory( RecordHeader, Lfcb->RecordHeaderLength );
+
+ //
+ // Update all the fields.
+ //
+
+ RecordHeader->ThisLsn = NextLsn;
+ RecordHeader->ClientPreviousLsn = ClientPreviousLsn;
+ RecordHeader->ClientUndoNextLsn = ClientUndoNextLsn;
+
+ if (TransactionId != NULL) {
+ RecordHeader->TransactionId = *TransactionId;
+ }
+
+ RecordHeader->ClientDataLength = RemainingLogBytes;
+ RecordHeader->ClientId = Lch->ClientId;
+ RecordHeader->RecordType = RecordType;
+
+ //
+ // Check if this is a multi-page record.
+ //
+
+ if (RemainingLogBytes + Lfcb->RecordHeaderLength > RemainingPageBytes) {
+
+ SetFlag( RecordHeader->Flags, LOG_RECORD_MULTI_PAGE );
+ }
+
+ RemainingPageBytes -= Lfcb->RecordHeaderLength;
+
+ //
+ // Update the buffer position in the Lbcb
+ //
+
+ (ULONG)ThisLbcb->BufferOffset += Lfcb->RecordHeaderLength;
+ HeaderAdjust = Lfcb->RecordHeaderLength;
+
+ //
+ // Remember the values in the current write entry.
+ //
+
+ CurrentBuffer = ThisWriteEntry->Buffer;
+ CurrentByteCount = ThisWriteEntry->ByteLength;
+
+ PadBytes = (8 - (CurrentByteCount & ~(0xfffffff8))) & ~(0xfffffff8);
+
+ //
+ // Continue to transfer bytes until all the client's data has
+ // been transferred.
+ //
+
+ while (RemainingLogBytes != 0) {
+
+ PLFS_RECORD_PAGE_HEADER PageHeader;
+
+ PageHeader = (PLFS_RECORD_PAGE_HEADER) ThisLbcb->PageHeader;
+
+ //
+ // If the Lbcb is empty and we are about to store data into it we
+ // subtract the data size of the page from the available space.
+ // Update all the information we want to put in the header.
+ //
+
+ if (!FlagOn( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY )) {
+
+ //
+ // We subtract this page from the available pages only if
+ // we are at the beginning of the page. Otherwise this
+ // could be a reuse page. In that case it has already
+ // been subtracted.
+ //
+
+ if ((ULONG)ThisLbcb->BufferOffset - HeaderAdjust == (ULONG)Lfcb->LogPageDataOffset) {
+
+
+ Lfcb->CurrentAvailable = Lfcb->CurrentAvailable - Lfcb->ReservedLogPageSize; //**** xxSub( Lfcb->CurrentAvailable, Lfcb->ReservedLogPageSize );
+ }
+
+ InsertTailList( &Lfcb->LbcbWorkque, &ThisLbcb->WorkqueLinks );
+ SetFlag( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY );
+ }
+
+ HeaderAdjust = 0;
+
+ //
+ // Compute the number of transfer bytes. Update the remaining
+ // page bytes, remaining log bytes and position in the write
+ // buffer array. This routine also copies the bytes into the buffer.
+ //
+
+ LfsTransferLogBytes( ThisLbcb,
+ &ThisWriteEntry,
+ &CurrentBuffer,
+ &CurrentByteCount,
+ &PadBytes,
+ &RemainingPageBytes,
+ &RemainingLogBytes );
+
+ //
+ // This log record ends on this page. Update the fields for the
+ // ending Lsn.
+ //
+
+ if (RemainingLogBytes == 0) {
+
+ SetFlag( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END );
+ ThisLbcb->LastEndLsn = NextLsn;
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ PageHeader->Header.Packed.LastEndLsn = NextLsn;
+ PageHeader->Header.Packed.NextRecordOffset = (USHORT)ThisLbcb->BufferOffset;
+ }
+ }
+
+ //
+ // We are done with this page, update the fields in the page header.
+ //
+
+ if (RemainingPageBytes == 0
+ || RemainingLogBytes == 0) {
+
+ //
+ // We are done with this page. Update the Lbcb and page header.
+ //
+
+ ThisLbcb->LastLsn = PageHeader->Copy.LastLsn = NextLsn;
+ PageHeader->Flags = ThisLbcb->Flags;
+
+ //
+ // We can't put any more log records on this page. Remove
+ // it from the active queue.
+ //
+
+ if (RemainingPageBytes < Lfcb->RecordHeaderLength) {
+
+ RemoveHeadList( &Lfcb->LbcbActive );
+ ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+
+ //
+ // If there are more log bytes then get the next Lbcb.
+ //
+
+ if (RemainingLogBytes != 0) {
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize
+ - (ULONG)ThisLbcb->BufferOffset;
+ }
+ }
+ }
+ }
+
+ *Lsn = NextLsn;
+
+ Lfcb->RestartArea->CurrentLsn = NextLsn;
+
+ Lfcb->RestartArea->LastLsnDataLength = OriginalLogBytes;
+
+ ClearFlag( Lfcb->Flags, LFCB_NO_LAST_LSN );
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsWriteLogRecordIntoLogPage: Exit\n", 0 );
+
+ return LogFileFull;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsPrepareLfcbForLogRecord (
+ IN OUT PLFCB Lfcb,
+ IN ULONG RemainingLogBytes
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to insure that the Lfcb has a Lbcb in the
+ active queue to perform the next log record transfer.
+ This condition is met when there is a least one buffer block and
+ the log record data will fit entirely on this page or this buffer
+ block contains no other data in the unpacked case. For the packed
+ case we just need to make sure that there are sufficient Lbcb's.
+
+Arguments:
+
+ Lfcb - File control block for this log file.
+
+ RemainingLogBytes - The number of bytes remaining for this log record.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLBCB ThisLbcb;
+ ULONG RemainingPageBytes;
+ PLIST_ENTRY LbcbLinks;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsPrepareLfcbForLogRecord: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "RemainingLogBytes -> %08lx\n", RemainingLogBytes );
+
+ //
+ // If there is no Lbcb in the active queue, we don't check it for size.
+ //
+
+ if (!IsListEmpty( &Lfcb->LbcbActive )) {
+
+ //
+ // If the log record won't fit in the remaining bytes of this page,
+ // we queue this log buffer.
+ //
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize
+ - (ULONG)ThisLbcb->BufferOffset;
+
+ //
+ // This log page won't do if the remaining bytes won't hold the data
+ // unless this is the first log record in the page or we are packing
+ // the log file.
+ //
+
+ if (RemainingLogBytes > RemainingPageBytes
+ && !FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && (ULONG)ThisLbcb->BufferOffset != (ULONG)Lfcb->LogPageDataOffset) {
+
+ RemoveHeadList( &Lfcb->LbcbActive );
+ ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+ }
+ }
+
+ //
+ // We now make sure we can allocate enough Lbcb's for all of the log pages
+ // we will need. We now include the bytes for the log record reader.
+ //
+
+ LbcbLinks = Lfcb->LbcbActive.Flink;
+
+ while (TRUE) {
+
+ //
+ // If the Lbcb link we have is the head of the list, we will need another
+ // Lbcb.
+ //
+
+ if (LbcbLinks == &Lfcb->LbcbActive) {
+
+ ThisLbcb = LfsGetLbcb( Lfcb );
+
+ } else {
+
+ ThisLbcb = CONTAINING_RECORD( LbcbLinks,
+ LBCB,
+ ActiveLinks );
+ }
+
+ //
+ // Remember the bytes remaining on this page. This will always be quad
+ // aligned.
+ //
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize - (ULONG)ThisLbcb->BufferOffset;
+
+ if (RemainingPageBytes >= RemainingLogBytes) {
+
+ break;
+ }
+
+ //
+ // Move to the next log record.
+ //
+
+ RemainingLogBytes -= RemainingPageBytes;
+
+ LbcbLinks = ThisLbcb->ActiveLinks.Flink;
+ }
+
+ DebugTrace( -1, Dbg, "LfsPrepareLfcbForLogRecord: Exit\n", 0 );
+
+ return;
+}
+
+
+VOID
+LfsTransferLogBytes (
+ IN PLBCB Lbcb,
+ IN OUT PLFS_WRITE_ENTRY *ThisWriteEntry,
+ IN OUT PCHAR *CurrentBuffer,
+ IN OUT PULONG CurrentByteCount,
+ IN OUT PULONG PadBytes,
+ IN OUT PULONG RemainingPageBytes,
+ IN OUT PULONG RemainingLogBytes
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to transfer the next block of bytes into
+ a log page. It is given a pointer to the current position in the
+ current Lfs write entry and the number of bytes remaining on that
+ log page. It will transfer as many of the client's bytes from the
+ current buffer that will fit and update various pointers.
+
+Arguments:
+
+ Lbcb - This is the buffer block for this log page.
+
+ ThisWriteEntry - This is a pointer to a pointer to the current Lfs
+ write entry.
+
+ CurrentBuffer - This is a pointer to a pointer to the current position
+ in the current write entry buffer. If this points to a NULL
+ value it means to put zero bytes into the log.
+
+ CurrentByteCount - This is a pointer to the number of bytes remaining
+ in the current buffer.
+
+ PadBytes - This is a pointer to the number of padding byes for
+ this write entry.
+
+ RemainingPageBytes - This is pointer to the number of bytes remaining
+ in this page.
+
+ RemainingLogBytes - This is the number of bytes remaining to transfer
+ for this log record.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PCHAR CurrentLogPagePosition;
+ PCHAR CurrentClientPosition;
+
+ ULONG TransferBytes;
+ ULONG ThisPadBytes;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsTransferLogBytes: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lbcb -> %08lx\n", Lbcb );
+ DebugTrace( 0, Dbg, "ThisWriteEntry -> %08lx\n", *ThisWriteEntry );
+ DebugTrace( 0, Dbg, "CurrentBuffer -> %08lx\n", *CurrentBuffer );
+ DebugTrace( 0, Dbg, "CurrentByteCount -> %08lx\n", *CurrentByteCount );
+ DebugTrace( 0, Dbg, "RemainingPageBytes -> %08lx\n", *RemainingPageBytes );
+ DebugTrace( 0, Dbg, "RemainingLogBytes -> %08lx\n", *RemainingLogBytes );
+
+ //
+ // Remember the current client buffer position and current position
+ // in log page.
+ //
+
+ CurrentLogPagePosition = Add2Ptr( Lbcb->PageHeader, (ULONG)Lbcb->BufferOffset, PCHAR );
+ CurrentClientPosition = *CurrentBuffer;
+
+ //
+ // The limiting factor is either the number of bytes remaining in a
+ // write entry or the number remaining in the log page.
+ //
+
+ if (*CurrentByteCount <= *RemainingPageBytes) {
+
+ TransferBytes = *CurrentByteCount;
+
+ ThisPadBytes = *PadBytes;
+
+ if (*RemainingLogBytes != (*CurrentByteCount + *PadBytes) ) {
+
+ (*ThisWriteEntry)++;
+
+ *CurrentBuffer = (*ThisWriteEntry)->Buffer;
+ *CurrentByteCount = (*ThisWriteEntry)->ByteLength;
+
+ *PadBytes = (8 - (*CurrentByteCount & ~(0xfffffff8))) & ~(0xfffffff8);
+ }
+
+ } else {
+
+ TransferBytes = *RemainingPageBytes;
+
+ ThisPadBytes = 0;
+
+ *CurrentByteCount -= TransferBytes;
+
+ if (*CurrentBuffer != NULL) {
+
+ *CurrentBuffer += TransferBytes;
+ }
+ }
+
+ //
+ // Transfer the requested bytes.
+ //
+
+ if (CurrentClientPosition != NULL) {
+
+ RtlCopyMemory( CurrentLogPagePosition, CurrentClientPosition, TransferBytes );
+
+ } else {
+
+ RtlZeroMemory( CurrentLogPagePosition, TransferBytes );
+ }
+
+ //
+ // Reduce the remaining page and log bytes by the transfer amount and
+ // move forward in the log page.
+ //
+
+ *RemainingLogBytes -= (TransferBytes + ThisPadBytes);
+ *RemainingPageBytes -= (TransferBytes + ThisPadBytes);
+
+ (ULONG)Lbcb->BufferOffset += (TransferBytes + ThisPadBytes);
+
+ DebugTrace( -1, Dbg, "LfsTransferLogBytes: Exit\n", 0 );
+
+ return;
+}
diff --git a/private/ntos/lfs/lsnsup.c b/private/ntos/lfs/lsnsup.c
new file mode 100644
index 000000000..429ef45b0
--- /dev/null
+++ b/private/ntos/lfs/lsnsup.c
@@ -0,0 +1,316 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ LsnSup.c
+
+Abstract:
+
+ This module implements support for manipulating Lsn's.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LSN_SUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsFindNextLsn)
+#pragma alloc_text(PAGE, LfsLsnFinalOffset)
+#endif
+
+
+VOID
+LfsLsnFinalOffset (
+ IN PLFCB Lfcb,
+ IN LSN Lsn,
+ IN ULONG DataLength,
+ OUT PLONGLONG FinalOffset
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will compute the final offset of the last byte of the log
+ record. It does this by computing how many bytes are on the current
+ page and then computing how many more pages will be needed.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ Lsn - This is the log record being considered.
+
+ DataLength - This is the length of the data for this log record. We will add the
+ header length here.
+
+ FinalOffset - Address to store the result.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG RemainingPageBytes;
+ ULONG PageOffset;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsLsnFinalOffset: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart );
+ DebugTrace( 0, Dbg, "DataLength -> %08lx\n", DataLength );
+
+ //
+ // We compute the starting log page file offset, the number of bytes
+ // remaining in the current log page and the position on this page
+ // before any data bytes.
+ //
+
+ LfsTruncateLsnToLogPage( Lfcb, Lsn, FinalOffset );
+
+ PageOffset = LfsLsnToPageOffset( Lfcb, Lsn );
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize - PageOffset;
+
+ PageOffset -= 1;
+
+ //
+ // Add the length of the header.
+ //
+
+ DataLength += Lfcb->RecordHeaderLength;
+
+ //
+ // If this Lsn is contained in this log page we are done.
+ // Otherwise we need to walk through several log pages.
+ //
+
+ if (DataLength > RemainingPageBytes) {
+
+ DataLength -= RemainingPageBytes;
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageDataSize;
+
+ PageOffset = (ULONG)Lfcb->LogPageDataOffset - 1;
+
+ while (TRUE) {
+
+ BOOLEAN Wrapped;
+
+ LfsNextLogPageOffset( Lfcb, *FinalOffset, FinalOffset, &Wrapped );
+
+ //
+ // We are done if the remaining bytes fit on this page.
+ //
+
+ if (DataLength <= RemainingPageBytes) {
+
+ break;
+ }
+
+ DataLength -= RemainingPageBytes;
+ }
+ }
+
+ //
+ // We add the remaining bytes to our starting position on this page
+ // and then add that value to the file offset of this log page.
+ //
+
+ *(PULONG)FinalOffset += (DataLength + PageOffset);
+
+ DebugTrace( 0, Dbg, "FinalOffset (Low) -> %08lx\n", LogPageFileOffset.LowPart );
+ DebugTrace( 0, Dbg, "FinalOffset (High) -> %08lx\n", LogPageFileOffset.HighPart );
+ DebugTrace( -1, Dbg, "LfsLsnFinalOffset: Exit\n", 0 );
+
+ return;
+}
+
+
+BOOLEAN
+LfsFindNextLsn (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_HEADER RecordHeader,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as a starting point the log record header of an
+ Lsn in the log file. It searches for the next Lsn in the file and
+ returns that value in the 'Lsn' argument. The boolean return value
+ indicates whether there is another Lsn in the file.
+
+Arguments:
+
+ Lfcb - This is the file control block for the log file.
+
+ RecordHeader - This is the log record for the Lsn starting point.
+
+ Lsn - This supplies the address to store the next Lsn, if found.
+
+Return Value:
+
+ BOOLEAN - Indicates whether the next Lsn was found.
+
+--*/
+
+{
+ BOOLEAN FoundNextLsn;
+
+ LONGLONG LsnOffset;
+ LONGLONG EndOfLogRecord;
+ LONGLONG LogHeaderOffset;
+
+ LONGLONG SequenceNumber;
+
+ PLFS_RECORD_PAGE_HEADER LogRecordPage;
+ PBCB LogRecordPageBcb;
+ BOOLEAN UsaError;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindNextLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Record Header -> %08lx\n", RecordHeader );
+
+ LogRecordPageBcb = NULL;
+ FoundNextLsn = FALSE;
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Find the file offset of the log page which contains the end
+ // of the log record for this Lsn.
+ //
+
+ LsnOffset = LfsLsnToFileOffset( Lfcb, RecordHeader->ThisLsn );
+
+ LfsLsnFinalOffset( Lfcb,
+ RecordHeader->ThisLsn,
+ RecordHeader->ClientDataLength,
+ &EndOfLogRecord );
+
+ LfsTruncateOffsetToLogPage( Lfcb, EndOfLogRecord, &LogHeaderOffset );
+
+ //
+ // Remember the sequence number for this page.
+ //
+
+ SequenceNumber = LfsLsnToSeqNumber( Lfcb, RecordHeader->ThisLsn );
+
+ //
+ // Remember if we wrapped.
+ //
+
+ if ( EndOfLogRecord <= LsnOffset ) { //**** xxLeq( EndOfLogRecord, LsnOffset )
+
+ SequenceNumber = SequenceNumber + 1; //**** xxAdd( SequenceNumber, LfsLi1 );
+ }
+
+ //
+ // Pin the log page header for this page.
+ //
+
+ LfsPinOrMapData( Lfcb,
+ LogHeaderOffset,
+ (ULONG)Lfcb->LogPageSize,
+ FALSE,
+ FALSE,
+ FALSE,
+ &UsaError,
+ (PVOID *)&LogRecordPage,
+ &LogRecordPageBcb );
+
+ //
+ // If the Lsn we were given was not the last Lsn on this page, then
+ // the starting offset for the next Lsn is on a quad word boundary
+ // following the last file offset for the current Lsn. Otherwise
+ // the file offset is the start of the data on the next page.
+ //
+
+ if ( RecordHeader->ThisLsn.QuadPart == LogRecordPage->Copy.LastLsn.QuadPart ) { //**** xxEql( RecordHeader->ThisLsn, LogRecordPage->Copy.LastLsn )
+
+ BOOLEAN Wrapped;
+
+ LfsNextLogPageOffset( Lfcb,
+ LogHeaderOffset,
+ &LogHeaderOffset,
+ &Wrapped );
+
+ LsnOffset = LogHeaderOffset + Lfcb->LogPageDataOffset; //**** xxAdd( LogHeaderOffset, Lfcb->LogPageDataOffset );
+
+ //
+ // If we wrapped, we need to increment the sequence number.
+ //
+
+ if (Wrapped) {
+
+ SequenceNumber = SequenceNumber + 1; //**** xxAdd( SequenceNumber, LfsLi1 );
+ }
+
+ } else {
+
+ LiQuadAlign( EndOfLogRecord, &LsnOffset );
+ }
+
+ //
+ // Compute the Lsn based on the file offset and the sequence count.
+ //
+
+ Lsn->QuadPart = LfsFileOffsetToLsn( Lfcb, LsnOffset, SequenceNumber );
+
+ //
+ // If this Lsn is within the legal range for the file, we return TRUE.
+ // Otherwise FALSE indicates that there are no more Lsn's.
+ //
+
+ if (LfsIsLsnInFile( Lfcb, *Lsn )) {
+
+ FoundNextLsn = TRUE;
+ }
+
+ } finally {
+
+ DebugUnwind( LfsFindNextLsn );
+
+ //
+ // Unpin the log page header if held.
+ //
+
+ if (LogRecordPageBcb != NULL) {
+
+ CcUnpinData( LogRecordPageBcb );
+ }
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsFindNextLsn: Exit -> %08x\n", FoundNextLsn );
+ }
+
+ return FoundNextLsn;
+}
+
diff --git a/private/ntos/lfs/mp/makefile b/private/ntos/lfs/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/lfs/mp/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/lfs/mp/sources b/private/ntos/lfs/mp/sources
new file mode 100644
index 000000000..301687c9a
--- /dev/null
+++ b/private/ntos/lfs/mp/sources
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+NT_UP=0
+
+!include ..\sources.inc
diff --git a/private/ntos/lfs/nodetype.h b/private/ntos/lfs/nodetype.h
new file mode 100644
index 000000000..57e2c604a
--- /dev/null
+++ b/private/ntos/lfs/nodetype.h
@@ -0,0 +1,54 @@
+/*++ BUILD Version: 0000 // Increment this if a change has global effects
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ NodeType.h
+
+Abstract:
+
+ This module defines all of the node type codes used in this development
+ shell. Every major data structure in the file system is assigned a node
+ type code that is. This code is the first CSHORT in the structure and is
+ followed by a CSHORT containing the size, in bytes, of the structure.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#ifndef _NODETYPE_
+#define _NODETYPE_
+
+typedef CSHORT NODE_TYPE_CODE;
+typedef NODE_TYPE_CODE *PNODE_TYPE_CODE;
+
+#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000)
+
+#define LFS_NTC_LCB ((NODE_TYPE_CODE)0x800)
+#define LFS_NTC_LCH ((NODE_TYPE_CODE)0x801)
+#define LFS_NTC_LBCB ((NODE_TYPE_CODE)0x802)
+#define LFS_NTC_LFCB ((NODE_TYPE_CODE)0x803)
+#define LFS_NTC_DATA ((NODE_TYPE_CODE)0x804)
+
+typedef CSHORT NODE_BYTE_SIZE;
+
+//
+// So all records start with
+//
+// typedef struct _RECORD_NAME {
+// NODE_TYPE_CODE NodeTypeCode;
+// NODE_BYTE_SIZE NodeByteSize;
+//
+// } RECORD_NAME;
+// typedef RECORD_NAME *PRECORD_NAME;
+//
+
+#define NodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr)))
+
+#endif // _NODETYPE_
+
diff --git a/private/ntos/lfs/querylog.c b/private/ntos/lfs/querylog.c
new file mode 100644
index 000000000..ef749ae8c
--- /dev/null
+++ b/private/ntos/lfs/querylog.c
@@ -0,0 +1,1245 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ QueryLog.c
+
+Abstract:
+
+ This module implements the user routines which query for log records
+ in a log file.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_QUERY)
+
+VOID
+LfsFindLogRecord (
+ IN PLFCB Lfcb,
+ IN OUT PLCB Lcb,
+ IN LSN Lsn,
+ OUT PLFS_RECORD_TYPE RecordType,
+ OUT TRANSACTION_ID *TransactionId,
+ OUT PLSN UndoNextLsn,
+ OUT PLSN PreviousLsn,
+ OUT PULONG BufferLength,
+ OUT PVOID *Buffer
+ );
+
+BOOLEAN
+LfsFindClientNextLsn (
+ IN PLFCB Lfcb,
+ IN PLCB Lcb,
+ OUT PLSN Lsn
+ );
+
+BOOLEAN
+LfsSearchForwardByClient (
+ IN PLFCB Lfcb,
+ IN OUT PLCB Lcb,
+ OUT PLSN Lsn
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsFindClientNextLsn)
+#pragma alloc_text(PAGE, LfsFindLogRecord)
+#pragma alloc_text(PAGE, LfsQueryLastLsn)
+#pragma alloc_text(PAGE, LfsReadLogRecord)
+#pragma alloc_text(PAGE, LfsReadNextLogRecord)
+#pragma alloc_text(PAGE, LfsSearchForwardByClient)
+#pragma alloc_text(PAGE, LfsTerminateLogQuery)
+#endif
+
+
+VOID
+LfsReadLogRecord (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN LSN FirstLsn,
+ IN LFS_CONTEXT_MODE ContextMode,
+ OUT PLFS_LOG_CONTEXT Context,
+ OUT PLFS_RECORD_TYPE RecordType,
+ OUT TRANSACTION_ID *TransactionId,
+ OUT PLSN UndoNextLsn,
+ OUT PLSN PreviousLsn,
+ OUT PULONG BufferLength,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initiates the query operation. It returns the log record
+ in question and a context structure used by the Lfs to return related
+ log records. The caller specifies what mode of query to use. He may
+ walk backwards through the file by Undo records or all records for
+ this client linked through the previous Lsn fields. He may also look
+ forwards through the file for all records for the issuing client.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ FirstLsn - Starting record for this query operation.
+
+ ContextMode - Method of query.
+
+ Context - Supplies the address to store a pointer to the Lfs created
+ context structure.
+
+ RecordType - Supplies the address to store the record type of this
+ log record.
+
+ TransactionId - Supplies the address to store the transaction Id of
+ this log record.
+
+ UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
+ log record.
+
+ PreviousLsn - Supplies the address to store the Previous Lsn for this
+ log record.
+
+ BufferLength - This is the length of the log data.
+
+ Buffer - This is a pointer to the start of the log data.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PLCB Lcb = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsReadLogRecord: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "First Lsn (Low) -> %08lx\n", FirstLsn.LowPart );
+ DebugTrace( 0, Dbg, "First Lsn (High) -> %08lx\n", FirstLsn.HighPart );
+ DebugTrace( 0, Dbg, "Context Mode -> %08lx\n", ContextMode );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the context mode is valid.
+ //
+
+ switch (ContextMode) {
+
+ case LfsContextUndoNext :
+ case LfsContextPrevious :
+ case LfsContextForward :
+
+ break;
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid context mode -> %08x\n", ContextMode );
+ ExRaiseStatus( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Check that the given Lsn is in the legal range for this client.
+ //
+
+ ClientRecord = Add2Ptr( Lfcb->ClientArray,
+ Lch->ClientArrayByteOffset,
+ PLFS_CLIENT_RECORD );
+
+ if (!LfsVerifyClientLsnInRange( Lfcb, ClientRecord, FirstLsn )) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // We can give up the Lfcb as we know the Lsn is within the file.
+ //
+
+ LfsReleaseLch( Lch );
+
+ //
+ // Allocate and initialize a context structure.
+ //
+
+ LfsAllocateLcb( &Lcb );
+
+ LfsInitializeLcb( Lcb,
+ Lch->ClientId,
+ ContextMode );
+
+ //
+ // Find the log record indicated by the given Lsn.
+ //
+
+ LfsFindLogRecord( Lfcb,
+ Lcb,
+ FirstLsn,
+ RecordType,
+ TransactionId,
+ UndoNextLsn,
+ PreviousLsn,
+ BufferLength,
+ Buffer );
+
+ //
+ // Update the client's arguments.
+ //
+
+ *Context = Lcb;
+ Lcb = NULL;
+
+ } finally {
+
+ DebugUnwind( LfsReadLogRecord );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ //
+ // Deallocate the context block if an error occurred.
+ //
+
+ if (Lcb != NULL) {
+
+ LfsDeallocateLcb( Lcb );
+ }
+
+ DebugTrace( 0, Dbg, "Context -> %08lx\n", *Context );
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
+ DebugTrace( -1, Dbg, "LfsReadLogRecord: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+BOOLEAN
+LfsReadNextLogRecord (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN OUT LFS_LOG_CONTEXT Context,
+ OUT PLFS_RECORD_TYPE RecordType,
+ OUT TRANSACTION_ID *TransactionId,
+ OUT PLSN UndoNextLsn,
+ OUT PLSN PreviousLsn,
+ OUT PLSN Lsn,
+ OUT PULONG BufferLength,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to continue a query operation. The Lfs uses
+ private information stored in the context structure to determine the
+ next log record to return to the caller.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ Context - Supplies the address to store a pointer to the Lfs created
+ context structure.
+
+ Lsn - Lsn for this log record.
+
+ RecordType - Supplies the address to store the record type of this
+ log record.
+
+ TransactionId - Supplies the address to store the transaction Id of
+ this log record.
+
+ UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
+ log record.
+
+ PreviousLsn - Supplies the address to store the Previous Lsn for this
+ log record.
+
+ BufferLength - This is the length of the log data.
+
+ Buffer - This is a pointer to the start of the log data.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PLCB Lcb;
+
+ BOOLEAN FoundNextLsn;
+
+ BOOLEAN UnwindRememberLcbFields;
+ PBCB UnwindRecordHeaderBcb;
+ PLFS_RECORD_HEADER UnwindRecordHeader;
+ PVOID UnwindCurrentLogRecord;
+ BOOLEAN UnwindAuxilaryBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsReadNextLogRecord: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Context -> %08lx\n", Context );
+
+ FoundNextLsn = FALSE;
+
+ UnwindRememberLcbFields = FALSE;
+
+ Lch = (PLCH) LogHandle;
+ Lcb = (PLCB) Context;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Check that the context structure is valid.
+ //
+
+ LfsValidateLcb( Lcb, Lch );
+
+ //
+ // Remember any context fields to be overwritten.
+ //
+
+ UnwindRememberLcbFields = TRUE;
+
+ UnwindRecordHeaderBcb = Lcb->RecordHeaderBcb;
+ Lcb->RecordHeaderBcb = NULL;
+
+ UnwindRecordHeader = Lcb->RecordHeader;
+ UnwindCurrentLogRecord = Lcb->CurrentLogRecord;
+
+ UnwindAuxilaryBuffer = Lcb->AuxilaryBuffer;
+ Lcb->AuxilaryBuffer = FALSE;
+
+ //
+ // Find the next Lsn number based on the current Lsn number in
+ // the context block.
+ //
+
+ if (LfsFindClientNextLsn( Lfcb, Lcb, Lsn )) {
+
+ //
+ // We can give up the Lfcb as we know the Lsn is within the file.
+ //
+
+ LfsReleaseLfcb( Lfcb );
+
+ //
+ // Cleanup the context block so we can do the next search.
+ //
+
+ Lcb->CurrentLogRecord = NULL;
+ Lcb->AuxilaryBuffer = FALSE;
+
+ //
+ // Perform the work of getting the log record.
+ //
+
+ LfsFindLogRecord( Lfcb,
+ Lcb,
+ *Lsn,
+ RecordType,
+ TransactionId,
+ UndoNextLsn,
+ PreviousLsn,
+ BufferLength,
+ Buffer );
+
+ FoundNextLsn = TRUE;
+ }
+
+ } finally {
+
+ DebugUnwind( LfsReadNextLogRecord );
+
+ //
+ // If we exited due to an error, we have to restore the context
+ // block.
+ //
+
+ if (UnwindRememberLcbFields) {
+
+ if (AbnormalTermination()) {
+
+ //
+ // If the record header in the context block is not
+ // the same as we started with. Then we unpin that
+ // data.
+ //
+
+ if (Lcb->RecordHeaderBcb != NULL) {
+
+ CcUnpinData( Lcb->RecordHeaderBcb );
+
+ }
+
+ if (Lcb->CurrentLogRecord != NULL
+ && Lcb->AuxilaryBuffer == TRUE) {
+
+ ExFreePool( Lcb->CurrentLogRecord );
+ }
+
+ Lcb->RecordHeaderBcb = UnwindRecordHeaderBcb;
+ Lcb->RecordHeader = UnwindRecordHeader;
+ Lcb->CurrentLogRecord = UnwindCurrentLogRecord;
+ Lcb->AuxilaryBuffer = UnwindAuxilaryBuffer;
+
+ //
+ // Otherwise, if we have successfully found the next Lsn,
+ // we free up any resources being held from the previous search.
+ //
+
+ } else if (FoundNextLsn ) {
+
+ if (UnwindRecordHeaderBcb != NULL) {
+
+ CcUnpinData( UnwindRecordHeaderBcb );
+ }
+
+ if (UnwindCurrentLogRecord != NULL
+ && UnwindAuxilaryBuffer == TRUE) {
+
+ ExFreePool( UnwindCurrentLogRecord );
+ }
+
+ //
+ // Restore the Bcb and auxilary buffer field for the final
+ // cleanup.
+ //
+
+ } else {
+
+ if (UnwindRecordHeaderBcb != NULL) {
+
+ if (Lcb->RecordHeaderBcb != NULL) {
+
+ CcUnpinData( UnwindRecordHeaderBcb );
+
+ } else {
+
+ Lcb->RecordHeaderBcb = UnwindRecordHeaderBcb;
+ }
+ }
+
+ if (UnwindAuxilaryBuffer) {
+
+ if (Lcb->CurrentLogRecord == UnwindCurrentLogRecord) {
+
+ Lcb->AuxilaryBuffer = TRUE;
+
+ } else {
+
+ ExFreePool( UnwindCurrentLogRecord );
+ }
+ }
+ }
+ }
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
+ DebugTrace( -1, Dbg, "LfsReadNextLogRecord: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return FoundNextLsn;
+}
+
+
+VOID
+LfsTerminateLogQuery (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN LFS_LOG_CONTEXT Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when a client has completed his query operation
+ and wishes to deallocate any resources acquired by the Lfs to
+ perform the log file query.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ Context - Supplies the address to store a pointer to the Lfs created
+ context structure.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLCH Lch;
+ PLCB Lcb;
+
+ PLFCB Lfcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsTerminateLogQuery: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Context -> %08lx\n", Context );
+
+ Lch = (PLCH) LogHandle;
+ Lcb = (PLCB) Context;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Check that the context structure is valid.
+ //
+
+ LfsValidateLcb( Lcb, Lch );
+
+ //
+ // Deallocate the context block.
+ //
+
+ LfsDeallocateLcb( Lcb );
+
+ try_exit: NOTHING;
+ } finally {
+
+ DebugUnwind( LfsTerminateLogQuery );
+
+ //
+ // Release the Lfcb if acquired.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( -1, Dbg, "LfsTerminateLogQuery: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+LSN
+LfsQueryLastLsn (
+ IN LFS_LOG_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will return the most recent Lsn for this log record.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+Return Value:
+
+ LSN - This is the last Lsn assigned in this log file.
+
+--*/
+
+{
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ LSN LastLsn;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsQueryLastLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Copy the last Lsn out of the Lfcb. If the last Lsn is
+ // does not correspond to a log record, we will return the
+ // zero Lsn.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN )) {
+
+ LastLsn = LfsZeroLsn;
+
+ } else {
+
+ LastLsn = Lfcb->RestartArea->CurrentLsn;
+ }
+
+ } finally {
+
+ DebugUnwind( LfsQueryLastLsn );
+
+ //
+ // Release the Lfcb if acquired.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( 0, Dbg, "Last Lsn (Low) -> %08lx\n", LastLsn.LowPart );
+ DebugTrace( 0, Dbg, "Last Lsn (High) -> %08lx\n", LastLsn.HighPart );
+ DebugTrace( -1, Dbg, "LfsQueryLastLsn: Exit\n", 0 );
+ }
+
+ return LastLsn;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsFindLogRecord (
+ IN PLFCB Lfcb,
+ IN OUT PLCB Lcb,
+ IN LSN Lsn,
+ OUT PLFS_RECORD_TYPE RecordType,
+ OUT TRANSACTION_ID *TransactionId,
+ OUT PLSN UndoNextLsn,
+ OUT PLSN PreviousLsn,
+ OUT PULONG BufferLength,
+ OUT PVOID *Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called recover a log record for a client.
+
+Arguments:
+
+ Lfcb - Log file control block for this file.
+
+ Lcb - Pointer to the context block to update.
+
+ Lsn - This is the Lsn for the log record.
+
+ RecordType - Supplies the address to store the record type of this
+ log record.
+
+ TransactionId - Supplies the address to store the transaction Id of
+ this log record.
+
+ UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
+ log record.
+
+ PreviousLsn - Supplies the address to store the Previous Lsn for this
+ log record.
+
+ BufferLength - Pointer to address to store the length in bytes of the
+ log record.
+
+ Buffer - Pointer to store the address where the log record data begins.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PCHAR NewBuffer;
+ BOOLEAN UsaError;
+ LONGLONG LogRecordLength;
+ ULONG PageOffset;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindLogRecord: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Context Block -> %08lx\n", Lcb );
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
+
+ NewBuffer = NULL;
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Map the record header for this Lsn if we haven't already.
+ //
+
+ if (Lcb->RecordHeader == NULL) {
+
+ LfsPinOrMapLogRecordHeader( Lfcb,
+ Lsn,
+ FALSE,
+ FALSE,
+ &UsaError,
+ &Lcb->RecordHeader,
+ &Lcb->RecordHeaderBcb );
+ }
+
+ //
+ // We now have the log record desired. If the Lsn in the
+ // log record doesn't match the desired Lsn then the disk is
+ // corrupt.
+ //
+
+ if ( Lsn.QuadPart != Lcb->RecordHeader->ThisLsn.QuadPart ) { //**** xxNeq( Lsn, Lcb->RecordHeader->ThisLsn )
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // Check that the length field isn't greater than the total available space
+ // in the log file.
+ //
+
+ LogRecordLength = Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength; //**** xxFromUlong( Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength );
+
+ if ( LogRecordLength >= Lfcb->TotalAvailable ) { //**** xxGeq( LogRecordLength, Lfcb->TotalAvailable )
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // If the entire log record is on this log page, put a pointer to
+ // the log record in the context block.
+ //
+
+ if (!FlagOn( Lcb->RecordHeader->Flags, LOG_RECORD_MULTI_PAGE )) {
+
+ //
+ // If client size indicates that we have to go beyond the end of the current
+ // page, we raise an error.
+ //
+
+ PageOffset = LfsLsnToPageOffset( Lfcb, Lsn );
+
+ if ((PageOffset + Lcb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength)
+ > (ULONG)Lfcb->LogPageSize) {
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ Lcb->CurrentLogRecord = Add2Ptr( Lcb->RecordHeader, LFS_RECORD_HEADER_SIZE, PVOID );
+ Lcb->AuxilaryBuffer = FALSE;
+
+ //
+ // Else we copy the data and remember that we allocated a buffer.
+ //
+
+ } else {
+
+ NewBuffer = FsRtlAllocatePool( PagedPool, Lcb->RecordHeader->ClientDataLength );
+
+ LfsCopyReadLogRecord( Lfcb,
+ Lcb->RecordHeader,
+ NewBuffer );
+
+ Lcb->CurrentLogRecord = NewBuffer;
+
+ Lcb->AuxilaryBuffer = TRUE;
+
+ NewBuffer = NULL;
+ }
+
+ //
+ // We need to update the caller's parameters and the context block.
+ //
+
+ *RecordType = Lcb->RecordHeader->RecordType;
+ *TransactionId = Lcb->RecordHeader->TransactionId;
+
+ *UndoNextLsn = Lcb->RecordHeader->ClientUndoNextLsn;
+ *PreviousLsn = Lcb->RecordHeader->ClientPreviousLsn;
+
+ *Buffer = Lcb->CurrentLogRecord;
+ *BufferLength = Lcb->RecordHeader->ClientDataLength;
+
+ } finally {
+
+ DebugUnwind( LfsFindLogRecord );
+
+ //
+ // If an error occurred we unpin the record header and the log
+ // We also free the buffer if allocated by us.
+ //
+
+ if (NewBuffer != NULL) {
+
+ ExFreePool( NewBuffer );
+ }
+
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
+ DebugTrace( -1, Dbg, "LfsFindLogRecord: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+BOOLEAN
+LfsFindClientNextLsn (
+ IN PLFCB Lfcb,
+ IN PLCB Lcb,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will attempt to find the next Lsn to return to a client
+ based on the context mode.
+
+Arguments:
+
+ Lfcb - File control block for this log file.
+
+ Lcb - Pointer to the context block for this query operation.
+
+ Lsn - Pointer to store the Lsn found (if any)
+
+Return Value:
+
+ BOOLEAN - TRUE if an Lsn is found, FALSE otherwise.
+
+--*/
+
+{
+ LSN NextLsn;
+ BOOLEAN NextLsnFound;
+
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindClientNextLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lcb -> %08lx\n", Lcb );
+
+ ClientRecord = Lfcb->ClientArray + Lcb->ClientId.ClientIndex;
+
+ //
+ // The context block has the last Lsn returned. If the user wanted
+ // one of the Lsn's in that log header then our job is simple.
+ //
+
+ switch (Lcb->ContextMode) {
+
+ case LfsContextUndoNext:
+ case LfsContextPrevious:
+
+ NextLsn = (Lcb->ContextMode == LfsContextUndoNext
+ ? Lcb->RecordHeader->ClientUndoNextLsn
+ : Lcb->RecordHeader->ClientPreviousLsn);
+
+ if ( NextLsn.QuadPart == 0 ) { //**** xxEqlZero( NextLsn )
+
+ NextLsnFound = FALSE;
+
+ } else if (LfsVerifyClientLsnInRange( Lfcb, ClientRecord, NextLsn )) {
+
+ BOOLEAN UsaError;
+
+ LfsPinOrMapLogRecordHeader( Lfcb,
+ NextLsn,
+ FALSE,
+ FALSE,
+ &UsaError,
+ &Lcb->RecordHeader,
+ &Lcb->RecordHeaderBcb );
+
+ NextLsnFound = TRUE;
+
+ } else {
+
+ NextLsnFound = FALSE;
+ }
+
+ break;
+
+ case LfsContextForward:
+
+ //
+ // We search forward for the next log record for this client.
+ //
+
+ NextLsnFound = LfsSearchForwardByClient( Lfcb, Lcb, &NextLsn );
+ break;
+
+ default:
+
+ NextLsnFound = FALSE;
+ break;
+ }
+
+ if (NextLsnFound) {
+
+ *Lsn = NextLsn;
+ }
+
+ DebugTrace( 0, Dbg, "NextLsn (Low) -> %08lx\n", NextLsn.LowPart );
+ DebugTrace( 0, Dbg, "NextLsn (High) -> %08lx\n", NextLsn.HighPart );
+ DebugTrace( -1, Dbg, "LfsFindClientNextLsn: Exit -> %08x\n", NextLsnFound );
+
+ return NextLsnFound;
+}
+
+
+//
+// Local support routine.
+//
+
+BOOLEAN
+LfsSearchForwardByClient (
+ IN PLFCB Lfcb,
+ IN OUT PLCB Lcb,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will attempt to find the next Lsn for this client by searching
+ forward in the file, looking for a match.
+
+Arguments:
+
+ Lfcb - Pointer to the file control block for this log file.
+
+ Lcb - Pointer to the context block for this query operation.
+
+ Lsn - Points to the location to store the next Lsn if found.
+
+Return Value:
+
+ BOOLEAN - TRUE if another Lsn for this client is found. FALSE otherwise.
+
+--*/
+
+{
+ PLFS_RECORD_HEADER CurrentRecordHeader;
+ PBCB CurrentBcb;
+
+ BOOLEAN FoundNextLsn;
+
+ LSN CurrentLsn;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsSearchForwardByClient: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lcb -> %08lx\n", Lcb );
+
+ //
+ // The log record header is in the log context
+ // block. We set the current Bcb to NULL so that we don't
+ // unpin the log record in the context block until we're sure
+ // of success.
+ //
+
+ CurrentRecordHeader = Lcb->RecordHeader;
+
+ CurrentBcb = NULL;
+
+ //
+ // We use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // We assume we won't find another Lsn.
+ //
+
+ FoundNextLsn = FALSE;
+
+ //
+ // Loop as long as another Lsn can be found.
+ //
+
+ while (LfsFindNextLsn( Lfcb, CurrentRecordHeader, &CurrentLsn )) {
+
+ BOOLEAN UsaError;
+
+ //
+ // Unpin the previous log record header.
+ //
+
+ if (CurrentBcb != NULL) {
+
+ CcUnpinData( CurrentBcb );
+ CurrentBcb = NULL;
+ }
+
+ //
+ // Pin the log record header for this Lsn.
+ //
+
+ LfsPinOrMapLogRecordHeader( Lfcb,
+ CurrentLsn,
+ FALSE,
+ FALSE,
+ &UsaError,
+ &CurrentRecordHeader,
+ &CurrentBcb );
+
+ //
+ // If the client values match, then we update the
+ // context block and exit.
+ //
+
+ if (LfsClientIdMatch( &CurrentRecordHeader->ClientId,
+ &Lcb->ClientId )
+ && CurrentRecordHeader->RecordType == LfsClientRecord) {
+
+ //
+ // We remember this one.
+ //
+
+ Lcb->RecordHeader = CurrentRecordHeader;
+ Lcb->RecordHeaderBcb = CurrentBcb;
+
+ CurrentBcb = NULL;
+ FoundNextLsn = TRUE;
+
+ *Lsn = CurrentLsn;
+ break;
+ }
+ }
+
+ } finally {
+
+ DebugUnwind( LfsSearchForwardByClient );
+
+ //
+ // Unpin any log record headers still pinned for no reason.
+ //
+
+ if (CurrentBcb != NULL) {
+
+ CcUnpinData( CurrentBcb );
+ }
+
+ DebugTrace( 0, Dbg, "NextLsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "NextLsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsSearchForwardByClient: Exit -> %08x\n", FoundNextLsn );
+ }
+
+ return FoundNextLsn;
+}
diff --git a/private/ntos/lfs/registry.c b/private/ntos/lfs/registry.c
new file mode 100644
index 000000000..384ede48d
--- /dev/null
+++ b/private/ntos/lfs/registry.c
@@ -0,0 +1,4234 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ Registry.c
+
+Abstract:
+
+ This module implements the routines which clients use to register
+ themselves with the Log File Service.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_REGISTRY)
+
+//
+// Set this to TRUE when ready to pack the log records.
+//
+
+BOOLEAN LfsPackLogRecords = TRUE;
+
+PLFCB
+LfsRestartLogFile (
+ IN PFILE_OBJECT LogFile,
+ IN USHORT MaximumClients,
+ IN ULONG LogPageSize OPTIONAL,
+ IN LONGLONG FileSize,
+ IN OUT PLFS_INFO LfsInfo
+ );
+
+VOID
+LfsNormalizeBasicLogFile (
+ IN OUT PLONGLONG FileSize,
+ IN OUT PULONG LogPageSize,
+ IN OUT PUSHORT LogClients
+ );
+
+VOID
+LfsUpdateLfcbFromPgHeader (
+ IN PLFCB Lfcb,
+ IN ULONG SystemPageSize,
+ IN ULONG LogPageSize,
+ IN SHORT MajorVersion,
+ IN SHORT MinorVersion,
+ IN BOOLEAN PackLog
+ );
+
+VOID
+LfsUpdateLfcbFromNoRestart (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileSize,
+ IN LSN LastLsn,
+ IN ULONG LogClients,
+ IN BOOLEAN LogFileWrapped,
+ IN BOOLEAN UseMultiplePageIo
+ );
+
+VOID
+LfsUpdateLfcbFromRestart (
+ IN PLFCB Lfcb,
+ IN PLFS_RESTART_AREA RestartArea,
+ IN USHORT RestartOffset
+ );
+
+VOID
+LfsUpdateRestartAreaFromLfcb (
+ IN PLFCB Lfcb,
+ IN PLFS_RESTART_AREA RestartArea
+ );
+
+VOID
+LfsInitializeLogFilePriv (
+ IN PLFCB Lfcb,
+ IN BOOLEAN ForceRestartToDisk,
+ IN ULONG RestartAreaSize,
+ IN LONGLONG StartOffsetForClear,
+ IN BOOLEAN ClearLogFile
+ );
+
+VOID
+LfsFindLastLsn (
+ IN OUT PLFCB Lfcb
+ );
+
+BOOLEAN
+LfsCheckSubsequentLogPage (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_PAGE_HEADER RecordPageHeader,
+ IN LONGLONG LogFileOffset,
+ IN LONGLONG SequenceNumber
+ );
+
+VOID
+LfsFlushLogPage (
+ IN PLFCB Lfcb,
+ PVOID LogPage,
+ IN LONGLONG FileOffset,
+ OUT PBCB *Bcb
+ );
+
+VOID
+LfsRemoveClientFromList (
+ IN PLFS_CLIENT_RECORD ClientArray,
+ IN PLFS_CLIENT_RECORD ClientRecord,
+ IN PUSHORT ListHead
+ );
+
+VOID
+LfsAddClientToList (
+ IN PLFS_CLIENT_RECORD ClientArray,
+ IN USHORT ClientIndex,
+ IN PUSHORT ListHead
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsAddClientToList)
+#pragma alloc_text(PAGE, LfsCheckSubsequentLogPage)
+#pragma alloc_text(PAGE, LfsCloseLogFile)
+#pragma alloc_text(PAGE, LfsDeleteLogHandle)
+#pragma alloc_text(PAGE, LfsFindLastLsn)
+#pragma alloc_text(PAGE, LfsFlushLogPage)
+#pragma alloc_text(PAGE, LfsInitializeLogFile)
+#pragma alloc_text(PAGE, LfsInitializeLogFilePriv)
+#pragma alloc_text(PAGE, LfsNormalizeBasicLogFile)
+#pragma alloc_text(PAGE, LfsOpenLogFile)
+#pragma alloc_text(PAGE, LfsReadLogFileInformation)
+#pragma alloc_text(PAGE, LfsRemoveClientFromList)
+#pragma alloc_text(PAGE, LfsResetUndoTotal)
+#pragma alloc_text(PAGE, LfsRestartLogFile)
+#pragma alloc_text(PAGE, LfsUpdateLfcbFromRestart)
+#pragma alloc_text(PAGE, LfsUpdateLfcbFromNoRestart)
+#pragma alloc_text(PAGE, LfsUpdateLfcbFromPgHeader)
+#pragma alloc_text(PAGE, LfsUpdateRestartAreaFromLfcb)
+#endif
+
+
+VOID
+LfsInitializeLogFile (
+ IN PFILE_OBJECT LogFile,
+ IN USHORT MaximumClients,
+ IN ULONG LogPageSize OPTIONAL,
+ IN LONGLONG FileSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to initialize a log file to prepare it for use
+ by log clients. Any previous data in the file will be overwritten.
+
+ Lfs will partition the file into 2 Lfs restart areas and as many log
+ pages as will fit in the file. The restart area size is determined
+ by computing the amount of space needed for the maximum number
+
+Arguments:
+
+ LogFile - This is the file object for the file to be used as a log file.
+
+ MaximumClients - This is the maximum number of clients that will be
+ active in the log file at any one time.
+
+ LogPageSize - If specified (not 0), this is the recommended size of
+ the log page. Lfs will use this as a guide in
+ determining the log page size.
+
+ FileSize - This is the available size of the log file.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLFCB Lfcb;
+
+ PLFS_RESTART_AREA RestartArea = NULL;
+
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsInitializeLogFile: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log File -> %08lx\n", LogFile );
+ DebugTrace( 0, Dbg, "Maximum Clients -> %04x\n", MaximumClients );
+ DebugTrace( 0, Dbg, "Log Page Size -> %08lx\n", LogPageSize );
+ DebugTrace( 0, Dbg, "File Size (Low) -> %08lx\n", FileSize.LowPart );
+ DebugTrace( 0, Dbg, "File Size (High) -> %08lx\n", FileSize.HighPart );
+
+ Lfcb = NULL;
+
+ //
+ // Protect this entry point with a try-except.
+ //
+
+ try {
+
+ //
+ // We grab the global data exclusively.
+ //
+
+ LfsAcquireLfsData();
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // We allocate an Lfcb and point it to the log file.
+ //
+
+ Lfcb = LfsAllocateLfcb();
+
+ LfsAcquireLfcb( Lfcb );
+
+ Lfcb->FileObject = LogFile;
+
+ //
+ // Call the Cache Manager to disable read ahead and write behind;
+ // we flush the log file explicitly.
+ //
+
+ CcSetAdditionalCacheAttributes( LogFile, TRUE, TRUE );
+
+ //
+ // Normalize the values passed in with this call.
+ //
+
+ LfsNormalizeBasicLogFile( &FileSize,
+ &LogPageSize,
+ &MaximumClients );
+
+ //
+ // We can go directly to version 1.1 since we can immediately pack
+ // the log file.
+ //
+
+ LfsUpdateLfcbFromPgHeader( Lfcb,
+ PAGE_SIZE,
+ LogPageSize,
+ 1,
+ (SHORT) (LfsPackLogRecords ? 1 : 0),
+ LfsPackLogRecords );
+
+ LfsUpdateLfcbFromNoRestart( Lfcb,
+ FileSize,
+ LfsLi0,
+ MaximumClients,
+ FALSE,
+ FALSE );
+
+ LfsAllocateRestartArea( &RestartArea, Lfcb->RestartDataSize );
+
+ LfsUpdateRestartAreaFromLfcb( Lfcb, RestartArea );
+
+ Lfcb->RestartArea = RestartArea;
+ Lfcb->ClientArray = Add2Ptr( RestartArea, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD );
+ RestartArea = NULL;
+
+ Lfcb->InitialRestartArea = TRUE;
+
+ //
+ // Force two restart areas to disk and reinitialize the file.
+ //
+
+ LfsInitializeLogFilePriv( Lfcb,
+ TRUE,
+ Lfcb->RestartDataSize,
+ Lfcb->FirstLogPage,
+ TRUE );
+
+ //
+ // Put the Lfcb into the global queue.
+ //
+
+ InsertHeadList( &LfsData.LfcbLinks, &Lfcb->LfcbLinks );
+
+ } finally {
+
+ DebugUnwind( LfsInitializeLogFile );
+
+ //
+ // If the Lfcb has been acquired, we release it now.
+ //
+
+ if (Lfcb != NULL) {
+
+ LfsReleaseLfcb( Lfcb );
+
+ //
+ // If an error occurred and we allocated an Lfcb. We deallocate
+ // it now.
+ //
+
+ if (AbnormalTermination()) {
+
+ LfsDeallocateLfcb( Lfcb, TRUE );
+ }
+ }
+
+ //
+ // Deallocate the restart area.
+ //
+
+ if (RestartArea != NULL) {
+
+ LfsDeallocateRestartArea( RestartArea );
+ }
+
+ //
+ // We release the global data.
+ //
+
+ LfsReleaseLfsData();
+
+ DebugTrace( -1, Dbg, "LfsInitializeLogFile: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+ULONG
+LfsOpenLogFile (
+ IN PFILE_OBJECT LogFile,
+ IN UNICODE_STRING ClientName,
+ IN USHORT MaximumClients,
+ IN ULONG LogPageSize OPTIONAL,
+ IN LONGLONG FileSize,
+ IN OUT PLFS_INFO LfsInfo,
+ OUT PLFS_LOG_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when a client wishes to register with logging
+ service. This can be a reregistration (i.e. restart after a crash)
+ or an initial registration. There can be no other active clients
+ with the same name. The Log Handle returned is then used for any
+ subsequent access by this client.
+
+ If an Lfs restart has not been done on the log file, it will be done
+ at this time.
+
+Arguments:
+
+ LogFile - A file object for a file previously initialized for use
+ as a log file.
+
+ ClientName - This unicode string is used to uniquely identify clients
+ of the logging service. A case-sensitive comparison is
+ used to check this name against active clients of the
+ log file.
+
+ MaximumClients - The maximum number of clients if the log file has
+ never been initialized.
+
+ LogPageSize - This is the recommeded size for the log page.
+
+ FileSize - This is the size of the log file.
+
+ LfsInfo - On entry, indicates the log file state the user may
+ know about. On exit, indicates the log file state that Lfs
+ knows about. This is a conduit for Lfs to communicate with its
+ clients.
+
+ LogHandle - The address to store the identifier the logging service
+ will use to identify this client in all other Lfs calls.
+
+Return Value:
+
+ ULONG - Amount to add to reservation value for header for log record.
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLIST_ENTRY Link;
+ PLFCB ThisLfcb = NULL;
+ PLFCB NewLfcb = NULL;
+
+ USHORT ThisClient;
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ PLCH Lch = NULL;
+
+ ULONG ReservedHeader;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsOpenLogFile: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log File -> %08lx\n", LogFile );
+ DebugTrace( 0, Dbg, "Client Name -> %08lx\n", &ClientName );
+ DebugTrace( 0, Dbg, "Maximum Clients -> %04x\n", MaximumClients );
+ DebugTrace( 0, Dbg, "Log Page Size -> %08lx\n", LogPageSize );
+ DebugTrace( 0, Dbg, "File Size (Low) -> %08lx\n", FileSize.LowPart );
+ DebugTrace( 0, Dbg, "File Size (High) -> %08lx\n", FileSize.HighPart );
+
+ //
+ // Check that the client name length is a legal length.
+ //
+
+ if (ClientName.Length > LFS_CLIENT_NAME_MAX) {
+
+ DebugTrace( 0, Dbg, "Illegal name length for client\n", 0 );
+ DebugTrace( -1, Dbg, "LfsOpenLogFile: Exit\n", 0 );
+ ExRaiseStatus( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Protect this call with a try-except.
+ //
+
+ try {
+
+ //
+ // Aqcuire the global data.
+ //
+
+ LfsAcquireLfsData();
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Walk through the list searching for this file object.
+ //
+
+ Link = LfsData.LfcbLinks.Flink;
+
+ while (Link != &LfsData.LfcbLinks) {
+
+ ThisLfcb = CONTAINING_RECORD( Link, LFCB, LfcbLinks );
+
+ if (ThisLfcb->FileObject == LogFile) {
+
+ DebugTrace( 0, Dbg, "Found matching log file\n", 0 );
+ break;
+ }
+
+ Link = Link->Flink;
+ }
+
+ //
+ // If the log file doesn't exist, create an Lfcb and perform an
+ // Lfs restart.
+ //
+
+ if (Link == &LfsData.LfcbLinks) {
+
+ //
+ // Call the Cache Manager to disable read ahead and write behind;
+ // we flush the log file explicitly.
+ //
+
+ CcSetAdditionalCacheAttributes( LogFile, TRUE, TRUE );
+
+ //
+ // Perform Lfs restart on this file object.
+ //
+
+ ThisLfcb = NewLfcb = LfsRestartLogFile( LogFile,
+ MaximumClients,
+ LogPageSize,
+ FileSize,
+ LfsInfo );
+
+ //
+ // Insert this Lfcb into the global list.
+ //
+
+ InsertHeadList( &LfsData.LfcbLinks, &ThisLfcb->LfcbLinks );
+ }
+
+ //
+ // At this point we have the log file control block for the file
+ // object given us. We first check whether the log file is fatally
+ // corrupt.
+ //
+
+ if (FlagOn( ThisLfcb->Flags, LFCB_LOG_FILE_CORRUPT )) {
+
+ //
+ // We leave the in-memory data alone and raise an error if
+ // anyone attempts to access this file.
+ //
+
+ DebugTrace( 0, Dbg, "The Lfcb is corrupt\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ //
+ // Search through and look for a client match.
+ //
+
+ ThisClient = ThisLfcb->RestartArea->ClientInUseList;
+
+ while (ThisClient != LFS_NO_CLIENT) {
+
+ ClientRecord = ThisLfcb->ClientArray + ThisClient;
+
+ if (ClientRecord->ClientNameLength == (ULONG) ClientName.Length
+ && RtlCompareMemory( ClientRecord->ClientName,
+ ClientName.Buffer,
+ ClientName.Length ) == (ULONG) ClientName.Length) {
+
+ DebugTrace( 0, Dbg, "Matching client name found\n", 0 );
+ break;
+ }
+
+ ThisClient = ClientRecord->NextClient;
+ }
+
+ //
+ // Allocate an Lch structure and link it into the Lfcb.
+ //
+
+ LfsAllocateLch( &Lch );
+ InsertTailList( &ThisLfcb->LchLinks, &Lch->LchLinks );
+
+ //
+ // Initialize the client handle with the data from the Lfcb.
+ //
+
+ Lch->Lfcb = ThisLfcb;
+ Lch->Sync = ThisLfcb->Sync;
+ Lch->Sync->UserCount += 1;
+
+ //
+ // If a match isn't found, take a client block off the free list
+ // if available.
+ //
+
+ if (ThisClient == LFS_NO_CLIENT) {
+
+ //
+ // Raise an error status if out of client blocks.
+ //
+
+ ThisClient = ThisLfcb->RestartArea->ClientFreeList;
+
+ if (ThisClient == LFS_NO_CLIENT) {
+
+ DebugTrace( 0, Dbg, "No free client records available\n", 0 );
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Initialize the client block.
+ //
+
+ ClientRecord = ThisLfcb->ClientArray + ThisClient;
+
+ LfsRemoveClientFromList( ThisLfcb->ClientArray,
+ ClientRecord,
+ &ThisLfcb->RestartArea->ClientFreeList );
+
+ ClientRecord->ClientRestartLsn = LfsZeroLsn;
+ ClientRecord->OldestLsn = ThisLfcb->OldestLsn;
+ ClientRecord->ClientNameLength = ClientName.Length;
+ RtlCopyMemory( ClientRecord->ClientName,
+ ClientName.Buffer,
+ ClientName.Length );
+
+ //
+ // Add it to the in use list.
+ //
+
+ LfsAddClientToList( ThisLfcb->ClientArray,
+ ThisClient,
+ &ThisLfcb->RestartArea->ClientInUseList );
+ }
+
+ //
+ // Update the client handle with the client block information.
+ //
+
+ Lch->ClientId.SeqNumber = ClientRecord->SeqNumber;
+ Lch->ClientId.ClientIndex = ThisClient;
+
+ Lch->ClientArrayByteOffset = PtrOffset( ThisLfcb->ClientArray,
+ ClientRecord );
+
+ *LogHandle = (LFS_LOG_HANDLE) Lch;
+
+ } finally {
+
+ DebugUnwind( LfsOpenLogFile );
+
+ //
+ // If the Lfcb has been acquired, we release it now.
+ //
+
+ if (ThisLfcb != NULL) {
+
+ //
+ // Pass information back to our caller for the number
+ // of bytes to add to the reserved amount for a
+ // log header.
+ //
+
+ ReservedHeader = ThisLfcb->RecordHeaderLength;
+ if (FlagOn( ThisLfcb->Flags, LFCB_PACK_LOG )) {
+
+ ReservedHeader *= 2;
+ }
+
+ LfsReleaseLfcb( ThisLfcb );
+ }
+
+ //
+ // If there is an error then deallocate the Lch and any new Lfcb.
+ //
+
+ if (AbnormalTermination()) {
+
+ if (Lch != NULL) {
+
+ LfsDeallocateLch( Lch );
+ ThisLfcb->Sync->UserCount -= 1;
+ }
+
+ if (NewLfcb != NULL) {
+
+ LfsDeallocateLfcb( NewLfcb, TRUE );
+ }
+ }
+
+ //
+ // Always free the global.
+ //
+
+ LfsReleaseLfsData();
+
+ DebugTrace( 0, Dbg, "Log Handle -> %08ln\n", *LogHandle );
+ DebugTrace( -1, Dbg, "LfsOpenLogFile: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return ReservedHeader;
+}
+
+
+VOID
+LfsCloseLogFile (
+ IN LFS_LOG_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when a client detaches itself from the log
+ file. On return, all prior references to this client in the log
+ file are inaccessible.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ USHORT ClientIndex;
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ BOOLEAN FlushRestart;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsCloseLogFile: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "LogHandle -> %08lx\n", LogHandle );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Protect this entry point with a try-except.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ //
+ // Acquire the global data block and the log file control block.
+ //
+
+ LfsAcquireLfsData();
+
+ try {
+
+ PLBCB ThisLbcb;
+
+ LfsAcquireLch( Lch );
+
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then return immediately.
+ //
+
+ if (Lfcb == NULL) {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Increment the client sequence number in the restart area.
+ // This will prevent anyone else from accessing this client block.
+ //
+
+ ClientIndex = Lch->ClientId.ClientIndex;
+
+ ClientRecord = Add2Ptr( Lfcb->ClientArray,
+ Lch->ClientArrayByteOffset,
+ PLFS_CLIENT_RECORD );
+
+ ClientRecord->SeqNumber++;
+
+ //
+ // Remember if this client wrote a restart area.
+ //
+
+ FlushRestart = (BOOLEAN) ( LfsZeroLsn.QuadPart != ClientRecord->ClientRestartLsn.QuadPart );
+
+ //
+ // Remove the client from the log file in use list.
+ //
+
+ LfsRemoveClientFromList( Lfcb->ClientArray,
+ ClientRecord,
+ &Lfcb->RestartArea->ClientInUseList );
+
+ //
+ // Add the client block to the log file free list
+ //
+
+ LfsAddClientToList( Lfcb->ClientArray,
+ ClientIndex,
+ &Lfcb->RestartArea->ClientFreeList );
+
+ //
+ // If this is the last client then move the last active Lbcb off
+ // the active queue.
+ //
+
+ if (Lfcb->RestartArea->ClientInUseList == LFS_NO_CLIENT) {
+
+ //
+ // Set the flag to indicate we are at the final close.
+ //
+
+ SetFlag( Lfcb->Flags, LFCB_FINAL_SHUTDOWN );
+
+ //
+ // Walk through the active queue and remove any Lbcb's with
+ // data from that queue. That will allow them to get out to disk.
+ //
+
+ while (!IsListEmpty( &Lfcb->LbcbActive )) {
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ RemoveEntryList( &ThisLbcb->ActiveLinks );
+ ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+
+ //
+ // If this page has some new entries, allow it to
+ // be flushed to disk. Otherwise deallocate it.
+ //
+
+ if (!FlagOn( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY )) {
+
+ RemoveEntryList( &ThisLbcb->WorkqueLinks );
+
+ if (ThisLbcb->LogPageBcb != NULL) {
+
+ CcUnpinDataForThread( ThisLbcb->LogPageBcb,
+ ThisLbcb->ResourceThread );
+ }
+
+ LfsDeallocateLbcb( Lfcb, ThisLbcb );
+ }
+ }
+
+ //
+ // It's possible that we have the two restart areas in the workque.
+ // They can be removed and the memory deallocated if we have no
+ // more clients.
+ //
+ // We skip this action if the there is Io in progress or the user
+ // had a restart area.
+ //
+
+ if (Lfcb->LfsIoState == LfsNoIoInProgress
+ && !FlushRestart) {
+
+ PLIST_ENTRY Links;
+
+ //
+ // Now walk through the workque list looking for a non-restart
+ // entry.
+ //
+
+ Links = Lfcb->LbcbWorkque.Flink;
+
+ while (Links != &Lfcb->LbcbWorkque) {
+
+ ThisLbcb = CONTAINING_RECORD( Links,
+ LBCB,
+ WorkqueLinks );
+
+ //
+ // If this is not a restart area, we exit and remember that
+ // we need to flush the restart areas.
+ //
+
+ if (!LfsLbcbIsRestart( ThisLbcb )) {
+
+ FlushRestart = TRUE;
+ break;
+ }
+
+ Links = Links->Flink;
+ }
+
+ //
+ // If we are still not to flush the restart areas remove
+ // all of the restart areas from the queue.
+ //
+
+ if (!FlushRestart) {
+
+ while (!IsListEmpty( &Lfcb->LbcbWorkque)) {
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbWorkque.Blink,
+ LBCB,
+ WorkqueLinks );
+
+ RemoveEntryList( &ThisLbcb->WorkqueLinks );
+
+ LfsDeallocateLbcb( Lfcb, ThisLbcb );
+ }
+ }
+
+ } else {
+
+ FlushRestart = TRUE;
+ }
+
+ //
+ // We will have to flush the restart area in this case.
+ //
+
+ } else {
+
+ FlushRestart = TRUE;
+ }
+
+ //
+ // Flush the new restart area if we need to.
+ //
+
+ if (FlushRestart) {
+
+ LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
+ LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
+ }
+
+ //
+ // Clear the Lfcb pointer in the client handle.
+ //
+
+ Lch->Lfcb = NULL;
+ RemoveEntryList( &Lch->LchLinks );
+
+ //
+ // If there are no active clients, we can remove this log file
+ // control block from the active queue.
+ //
+
+ if (Lfcb->RestartArea->ClientInUseList == LFS_NO_CLIENT) {
+
+ RemoveEntryList( &Lfcb->LfcbLinks );
+ LfsDeallocateLfcb( Lfcb, FALSE );
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ DebugUnwind( LfsCloseLogFile );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ //
+ // Release the global data block if held.
+ //
+
+ LfsReleaseLfsData();
+
+ DebugTrace( -1, Dbg, "LfsCloseLogFile: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ //
+ // We always let this operation succeed.
+ //
+
+ return;
+}
+
+VOID
+LfsDeleteLogHandle (
+ IN LFS_LOG_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when a client is tearing down the last of
+ his volume structures. There will be no more references to this
+ handle. If it is the last handle for the log file then we will
+ deallocate the Sync structure as well.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLCH Lch;
+
+ PAGED_CODE();
+
+ //
+ // If the log handle is null then return immediately.
+ //
+
+ Lch = (PLCH) LogHandle;
+
+ if ((Lch == NULL) ||
+ (Lch->NodeTypeCode != LFS_NTC_LCH)) {
+
+ return;
+ }
+
+ //
+ // Ignore all errors from now on.
+ //
+
+ try {
+
+ LfsAcquireLch( Lch );
+
+ Lch->Sync->UserCount -= 1;
+
+ //
+ // If we are the last user then deallocate the sync structure.
+ //
+
+ if (Lch->Sync->UserCount == 0) {
+
+ ExDeleteResource( &Lch->Sync->Resource );
+
+ ExFreePool( Lch->Sync );
+
+ } else {
+
+ LfsReleaseLch( Lch );
+ }
+
+ LfsDeallocateLch( Lch );
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ NOTHING;
+ }
+
+ return;
+}
+
+
+VOID
+LfsReadLogFileInformation (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN PLOG_FILE_INFORMATION Buffer,
+ IN OUT PULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns information about the current state of the log
+ file, primarily to aid the client perform its checkpoint processing.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ Buffer - Pointer to buffer to return the log file information.
+
+ Length - On input this is the length of the user's buffer. On output,
+ it is the amount of data stored by the Lfs in the buffer.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsReadLogFileInformation: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
+ DebugTrace( 0, Dbg, "Length -> %08lx\n", *Length );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then return immediately.
+ //
+
+ if (Lfcb == NULL) {
+
+ try_return( *Length = 0 );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // The buffer better be large enough.
+ //
+
+ if (*Length >= sizeof( LOG_FILE_INFORMATION )) {
+
+ PLOG_FILE_INFORMATION Information;
+ LONGLONG CurrentAvail;
+ ULONG UnusedBytes;
+
+ LfsCurrentAvailSpace( Lfcb,
+ &CurrentAvail,
+ &UnusedBytes );
+
+ //
+ // Cast a pointer to the buffer and fill in the
+ // data.
+ //
+
+ Information = (PLOG_FILE_INFORMATION) Buffer;
+
+ Information->TotalAvailable = Lfcb->TotalAvailable;
+ Information->CurrentAvailable = CurrentAvail;
+ Information->TotalUndoCommitment = Lfcb->TotalUndoCommitment;
+ Information->ClientUndoCommitment = Lch->ClientUndoCommitment;
+
+ Information->OldestLsn = Lfcb->OldestLsn;
+ Information->LastFlushedLsn = Lfcb->LastFlushedLsn;
+ Information->LastLsn = Lfcb->RestartArea->CurrentLsn;
+
+ *Length = sizeof( LOG_FILE_INFORMATION );
+
+ } else {
+
+ *Length = 0;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+ DebugUnwind( LfsReadLogFileInformation );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( -1, Dbg, "LfsReadLogFileInformation: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+VOID
+LfsResetUndoTotal (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN ULONG NumberRecords,
+ IN LONG ResetTotal
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to adjust the undo commitment for this client.
+ If the reset total is positive, then we absolutely set the
+ reserve value for the client using this as the basis. If the value
+ is negative, we will adjust the current value for the client.
+
+ To adjust the values in the Lfcb, we first return the Undo commitment
+ in the handle and then adjust by the values passed in.
+
+ To adjust the value in the client handle, we simply set it if
+ the reset value is positive, adjust it if the value is negative.
+
+ For a packed log file we just reserve the space requested. We
+ have already taken into account the loss of the tail of each page.
+ For an unpacked log file we double each value.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ NumberRecords - This is the number of records we should assume the
+ reset total covers. We allow an Lfs header for
+ each one.
+
+ ResetTotal - This is the amount to adjust (or set) the undo
+ commitment.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ LONGLONG AdjustedUndoTotal;
+ LONG LfsHeaderBytes;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsResetUndoTotal: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Number Records -> %08lx\n", NumberRecords );
+ DebugTrace( 0, Dbg, "ResetTotal -> %08lx\n", ResetTotal );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Compute the adjusted reset total. Start by computing the
+ // bytes needed for the Lfs log headers. Add (or subtract) this
+ // from the reset total and multiply by 2 (only if not packing the
+ // log).
+ //
+
+ LfsHeaderBytes = NumberRecords * Lfcb->RecordHeaderLength;
+ LfsHeaderBytes *= 2;
+
+ if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ ResetTotal *= 2;
+ }
+
+ //
+ // If the reset total is positive, add the header bytes.
+ //
+
+ if (ResetTotal > 0) {
+
+ //
+ // Subtract the client's current value from the TotalUndo
+ // commit if he is setting his value exactly.
+ //
+
+ Lfcb->TotalUndoCommitment = Lfcb->TotalUndoCommitment - Lch->ClientUndoCommitment;
+
+ //
+ // We can clear the values in the user's handle at this
+ // time.
+ //
+
+ Lch->ClientUndoCommitment = 0;
+
+
+ ResetTotal += LfsHeaderBytes;
+
+ //
+ // Otherwise subtract the value for the header bytes.
+ //
+
+ } else {
+
+ ResetTotal -= LfsHeaderBytes;
+ }
+
+ //
+ // Now we adjust the Lfcb and Lch values by the adjustment amount.
+ //
+
+ AdjustedUndoTotal = ResetTotal;
+
+ Lfcb->TotalUndoCommitment = Lfcb->TotalUndoCommitment + AdjustedUndoTotal;
+
+ Lch->ClientUndoCommitment = Lch->ClientUndoCommitment + AdjustedUndoTotal;
+
+ } finally {
+
+ DebugUnwind( LfsResetUndoTotal );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( -1, Dbg, "LfsResetUndoTotal: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+PLFCB
+LfsRestartLogFile (
+ IN PFILE_OBJECT LogFile,
+ IN USHORT MaximumClients,
+ IN ULONG LogPageSize OPTIONAL,
+ IN LONGLONG FileSize,
+ IN OUT PLFS_INFO LfsInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to process an existing log file when it opened
+ for the first time on a running system. We walk through the beginning
+ of the file looking for a valid restart area. Once we have a restart
+ area, we can find the next restart area and determine which is the
+ most recent. The data in the restart area will tell us if the system
+ has been gracefully shutdown and whether the log file in its current
+ state can run on the current system.
+
+ If the file is usable, we perform any necessary initialization on the
+ file to prepare it for operation.
+
+Arguments:
+
+ LogFile - This is the file to use as a log file.
+
+ MaximumClients - This is the maximum number of clients that will be
+ active in the log file at any one time.
+
+ LogPageSize - If specified (not 0), this is the recommended size of
+ the log page. Lfs will use this as a guide in
+ determining the log page size.
+
+ FileSize - This is the available size of the log file.
+
+ LfsInfo - On entry, indicates the log file state the user may
+ know about. On exit, indicates the log file state that Lfs
+ knows about. This is a conduit for Lfs to communicate with its
+ clients.
+
+Return Value:
+
+ PLFCB - A pointer to an initialized Lfcb to use for
+ this log file.
+
+--*/
+
+{
+ PLFCB ThisLfcb = NULL;
+ PLFS_RESTART_AREA RestartArea = NULL;
+ PLFS_RESTART_AREA DiskRestartArea;
+
+ BOOLEAN UninitializedFile;
+
+ LONGLONG FirstRestartOffset;
+ PLFS_RESTART_PAGE_HEADER FirstRestartPage;
+ BOOLEAN FirstChkdskWasRun;
+ BOOLEAN FirstValidPage;
+ BOOLEAN FirstLogPacked;
+ LSN FirstRestartLastLsn;
+
+ PBCB FirstRestartPageBcb = NULL;
+ PBCB SecondRestartPageBcb = NULL;
+
+ BOOLEAN PackLogFile;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsRestartLogFile: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "LogFile -> %08lx\n", LogFile );
+ DebugTrace( 0, Dbg, "Maximum Clients -> %04x\n", MaximumClients );
+ DebugTrace( 0, Dbg, "Log Page Size -> %08lx\n", LogPageSize );
+ DebugTrace( 0, Dbg, "File Size (Low) -> %08lx\n", FileSize.LowPart );
+ DebugTrace( 0, Dbg, "File Size (High) -> %08lx\n", FileSize.HighPart );
+ DebugTrace( 0, Dbg, "Pack Log -> %04x\n", *LfsInfo );
+
+ //
+ // Remember if we are to pack the log file. Once a log file has
+ // been packed we will attempt to keep it that way.
+ //
+
+ if (LfsPackLogRecords
+ && *LfsInfo >= LfsPackLog) {
+
+ PackLogFile = TRUE;
+
+ } else {
+
+ PackLogFile = FALSE;
+ }
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Normalize the values passed in with this call.
+ //
+
+ LfsNormalizeBasicLogFile( &FileSize,
+ &LogPageSize,
+ &MaximumClients );
+
+ //
+ // Allocate an Lfcb to use for this file.
+ //
+
+ ThisLfcb = LfsAllocateLfcb();
+
+ //
+ // Acquire the Lfcb and store it in the global queue.
+ //
+
+ LfsAcquireLfcb( ThisLfcb );
+
+ //
+ // Remember this log file in the Lfcb.
+ //
+
+ ThisLfcb->FileObject = LogFile;
+
+ SetFlag( ThisLfcb->Flags,
+ (LFCB_READ_FIRST_RESTART |
+ LFCB_READ_SECOND_RESTART) );
+
+ //
+ // Look for a restart area on the disk.
+ //
+
+ if (LfsReadRestart( ThisLfcb,
+ FileSize,
+ TRUE,
+ &FirstRestartOffset,
+ &FirstRestartPage,
+ &FirstRestartPageBcb,
+ &FirstChkdskWasRun,
+ &FirstValidPage,
+ &UninitializedFile,
+ &FirstLogPacked,
+ &FirstRestartLastLsn )) {
+
+ BOOLEAN DoubleRestart;
+
+ LONGLONG SecondRestartOffset;
+ PLFS_RESTART_PAGE_HEADER SecondRestartPage;
+ BOOLEAN SecondChkdskWasRun;
+ BOOLEAN SecondValidPage;
+ BOOLEAN SecondLogPacked;
+ LSN SecondRestartLastLsn;
+
+ //
+ // If the restart offset above wasn't zero then we
+ // won't look for a second restart.
+ //
+
+ if (FirstRestartOffset == 0) {
+
+ ClearFlag( ThisLfcb->Flags, LFCB_READ_FIRST_RESTART );
+
+ DoubleRestart = LfsReadRestart( ThisLfcb,
+ FileSize,
+ FALSE,
+ &SecondRestartOffset,
+ &SecondRestartPage,
+ &SecondRestartPageBcb,
+ &SecondChkdskWasRun,
+ &SecondValidPage,
+ &UninitializedFile,
+ &SecondLogPacked,
+ &SecondRestartLastLsn );
+
+ if (DoubleRestart) {
+
+ ClearFlag( ThisLfcb->Flags, LFCB_READ_SECOND_RESTART );
+ }
+
+ } else {
+
+ ClearFlag( ThisLfcb->Flags, LFCB_READ_SECOND_RESTART );
+ DoubleRestart = FALSE;
+ }
+
+ //
+ // Determine which restart area to use.
+ //
+
+ if (DoubleRestart
+ && (SecondRestartLastLsn.QuadPart > FirstRestartLastLsn.QuadPart)) {
+
+ BOOLEAN UseSecondPage = TRUE;
+ PULONG SecondPage;
+ PBCB SecondPageBcb = NULL;
+ BOOLEAN UsaError;
+
+ //
+ // In a very strange case we could have crashed on a system with
+ // a different page size and then run chkdsk on the new system.
+ // The second restart page may not have the chkdsk signature in
+ // that case but could have a higher final Lsn.
+ // We want to ignore the second restart area in that case.
+ //
+
+ if (FirstChkdskWasRun &&
+ (SecondRestartOffset != PAGE_SIZE)) {
+
+ if (NT_SUCCESS( LfsPinOrMapData( ThisLfcb,
+ PAGE_SIZE,
+ PAGE_SIZE,
+ FALSE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ &SecondPage,
+ &SecondPageBcb )) &&
+ (*SecondPage == LFS_SIGNATURE_MODIFIED_ULONG)) {
+
+ UseSecondPage = FALSE;
+ }
+
+ if (SecondPageBcb != NULL) {
+
+ CcUnpinData( SecondPageBcb );
+ }
+ }
+
+ if (UseSecondPage) {
+
+ FirstRestartOffset = SecondRestartOffset;
+ FirstRestartPage = SecondRestartPage;
+ FirstChkdskWasRun = SecondChkdskWasRun;
+ FirstValidPage = SecondValidPage;
+ FirstLogPacked = SecondLogPacked;
+ FirstRestartLastLsn = SecondRestartLastLsn;
+ }
+ }
+
+ //
+ // If the restart area is at offset 0, we want to write
+ // the second restart area out first.
+ //
+
+ if ( FirstRestartOffset != 0 ) {
+
+ ThisLfcb->InitialRestartArea = TRUE;
+ }
+
+ //
+ // If we have a valid page then grab a pointer to the restart area.
+ //
+
+ if (FirstValidPage) {
+
+ DiskRestartArea = Add2Ptr( FirstRestartPage, FirstRestartPage->RestartOffset, PLFS_RESTART_AREA );
+ }
+
+ //
+ // If checkdisk was run or there are no active clients,
+ // then we will begin at the start of the log file.
+ //
+
+ if (FirstChkdskWasRun
+ || DiskRestartArea->ClientInUseList == LFS_NO_CLIENT) {
+
+ SHORT MajorVersion = 1;
+ SHORT MinorVersion = 0;
+
+ BOOLEAN LogFileWrapped = FALSE;
+ BOOLEAN UseMultiplePageIo = FALSE;
+
+ BOOLEAN ForceRestartToDisk = TRUE;
+
+ BOOLEAN ClearLogFile = TRUE;
+
+ LONGLONG StartOffsetForClear;
+ StartOffsetForClear = PAGE_SIZE * 2;
+
+ //
+ // Version numbers. We always go to a packed version number
+ // unless the Lfs flag or the Ntfs flag says not to.
+ //
+
+ if (PackLogFile) {
+
+ MinorVersion = 1;
+ }
+
+ //
+ // Do some checks based on whether we have a valid log page.
+ //
+
+ if (FirstValidPage) {
+
+ //
+ // If the current restart was packed, we keep it packed.
+ // Keep the version number as well.
+ //
+
+ if (FirstLogPacked) {
+
+ PackLogFile = TRUE;
+ MinorVersion = 1;
+ }
+
+ //
+ // If the file size is changing we want to remember
+ // at which point we want to start clearing the file.
+ //
+
+ if ( FileSize != DiskRestartArea->FileSize ) {
+
+ StartOffsetForClear = FileSize;
+
+ //
+ // If the restart page size isn't changing then we want to
+ // check how much work we need to do.
+ //
+
+ } else if (PAGE_SIZE == FirstRestartPage->SystemPageSize) {
+
+ if (!FlagOn( DiskRestartArea->Flags, RESTART_SINGLE_PAGE_IO )) {
+
+ UseMultiplePageIo = TRUE;
+ LogFileWrapped = TRUE;
+ }
+
+ //
+ // If the page is valid we don't need to clear the log
+ // file or force the data to disk.
+ //
+
+ ForceRestartToDisk = FALSE;
+ ClearLogFile = FALSE;
+ }
+ }
+
+ //
+ // Initialize our Lfcb for the current log page values.
+ //
+
+ LfsUpdateLfcbFromPgHeader( ThisLfcb,
+ PAGE_SIZE,
+ LogPageSize,
+ MajorVersion,
+ MinorVersion,
+ PackLogFile );
+
+ LfsUpdateLfcbFromNoRestart( ThisLfcb,
+ FileSize,
+ FirstRestartLastLsn,
+ MaximumClients,
+ LogFileWrapped,
+ UseMultiplePageIo );
+
+ LfsAllocateRestartArea( &RestartArea, ThisLfcb->RestartDataSize );
+
+ LfsUpdateRestartAreaFromLfcb( ThisLfcb, RestartArea );
+
+ ThisLfcb->RestartArea = RestartArea;
+ ThisLfcb->ClientArray = Add2Ptr( RestartArea,
+ ThisLfcb->ClientArrayOffset,
+ PLFS_CLIENT_RECORD );
+ RestartArea = NULL;
+
+ //
+ // Unpin any pages pinned here.
+ //
+
+ if (FirstRestartPageBcb != NULL) {
+
+ CcUnpinData( FirstRestartPageBcb );
+ FirstRestartPageBcb = NULL;
+ }
+
+ if (SecondRestartPageBcb != NULL) {
+
+ CcUnpinData( SecondRestartPageBcb );
+ SecondRestartPageBcb = NULL;
+ }
+
+ //
+ // Put the restart areas out and initialize the log file
+ // as required.
+ //
+
+ LfsInitializeLogFilePriv( ThisLfcb,
+ ForceRestartToDisk,
+ ThisLfcb->RestartDataSize,
+ StartOffsetForClear,
+ ClearLogFile );
+
+ //
+ // If the log page or the system page sizes have changed,
+ // we can't use the log file.
+ //
+
+ } else if (PAGE_SIZE != FirstRestartPage->SystemPageSize
+ || LogPageSize != FirstRestartPage->LogPageSize) {
+
+ DebugTrace( 0, Dbg, "Page size mismatch\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+
+ //
+ // If the file size has shrunk then we won't mount it.
+ //
+
+ } else if ( FileSize < DiskRestartArea->FileSize ) {
+
+ DebugTrace( 0, Dbg, "Log file has shrunk without clean shutdown\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+
+ //
+ // Otherwise we have a restart area to deal with.
+ //
+
+ } else {
+
+ //
+ // We preserve the packed status from the disk.
+ //
+
+ PackLogFile = FirstLogPacked;
+
+ //
+ // Update the Lfcb from the values in the restart area
+ // page header and the active restart page.
+ //
+
+ LfsUpdateLfcbFromPgHeader( ThisLfcb,
+ PAGE_SIZE,
+ FirstRestartPage->LogPageSize,
+ FirstRestartPage->MajorVersion,
+ FirstRestartPage->MinorVersion,
+ FirstLogPacked );
+
+ LfsUpdateLfcbFromRestart( ThisLfcb,
+ DiskRestartArea,
+ FirstRestartPage->RestartOffset );
+
+ //
+ // Now allocate a restart area.
+ //
+
+ LfsAllocateRestartArea( &RestartArea, ThisLfcb->RestartDataSize );
+ RtlCopyMemory( RestartArea, DiskRestartArea, ThisLfcb->RestartAreaSize );
+ ThisLfcb->RestartArea = RestartArea;
+
+ ThisLfcb->ClientArray = Add2Ptr( RestartArea, ThisLfcb->ClientArrayOffset, PLFS_CLIENT_RECORD );
+ RestartArea = NULL;
+
+ //
+ // Unpin any pages pinned here.
+ //
+
+ if (FirstRestartPageBcb != NULL) {
+
+ CcUnpinData( FirstRestartPageBcb );
+ FirstRestartPageBcb = NULL;
+ }
+
+ if (SecondRestartPageBcb != NULL) {
+
+ CcUnpinData( SecondRestartPageBcb );
+ SecondRestartPageBcb = NULL;
+ }
+
+ //
+ // Now we need to walk through looking for the last
+ // Lsn.
+ //
+
+ LfsFindLastLsn( ThisLfcb );
+
+ //
+ // Recalculate the available pages in the Lfcb.
+ //
+
+ LfsFindCurrentAvail( ThisLfcb );
+
+ //
+ // Remember which restart area to write out first.
+ //
+
+ if ( FirstRestartOffset != 0 ) {
+
+ ThisLfcb->InitialRestartArea = TRUE;
+ }
+
+ //
+ // Queue the restart areas.
+ //
+
+ LfsInitializeLogFilePriv( ThisLfcb,
+ FALSE,
+ ThisLfcb->RestartDataSize,
+ 0,
+ FALSE );
+ }
+
+ //
+ // If the file is uninitialized, we will initialized it with new
+ // restart areas. We can move to version 1.0 where we use
+ // update sequence array support but don't have to force the values
+ // to disk.
+ //
+
+ } else if (UninitializedFile) {
+
+ //
+ // We go to a packed system if possible.
+ //
+
+ LfsUpdateLfcbFromPgHeader( ThisLfcb,
+ PAGE_SIZE,
+ LogPageSize,
+ 1,
+ (BOOLEAN) (PackLogFile ? 1 : 0),
+ PackLogFile );
+
+ LfsUpdateLfcbFromNoRestart( ThisLfcb,
+ FileSize,
+ LfsLi0,
+ MaximumClients,
+ FALSE,
+ FALSE );
+
+ LfsAllocateRestartArea( &RestartArea, ThisLfcb->RestartDataSize );
+
+ LfsUpdateRestartAreaFromLfcb( ThisLfcb, RestartArea );
+
+ ThisLfcb->RestartArea = RestartArea;
+ ThisLfcb->ClientArray = Add2Ptr( RestartArea, ThisLfcb->ClientArrayOffset, PLFS_CLIENT_RECORD );
+
+ ThisLfcb->InitialRestartArea = TRUE;
+ RestartArea = NULL;
+
+ //
+ // Put both restart areas in the queue to be flushed but don't
+ // force them to disk.
+ //
+
+ LfsInitializeLogFilePriv( ThisLfcb,
+ FALSE,
+ ThisLfcb->RestartDataSize,
+ 0,
+ FALSE );
+
+ //
+ // We didn't find a restart area but the file is not initialized.
+ // This is a corrupt disk.
+ //
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Log file has no restart area\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+ } finally {
+
+ DebugUnwind( LfsRestartLogFile );
+
+ //
+ // Free the Lfcb if allocated.
+ //
+
+ if (ThisLfcb != NULL) {
+
+ LfsReleaseLfcb( ThisLfcb );
+
+ //
+ // Free the Lfcb and Restart areas in the event of an error.
+ //
+
+ if (AbnormalTermination()) {
+
+ LfsDeallocateLfcb( ThisLfcb, TRUE );
+
+ if (RestartArea != NULL) {
+
+ LfsDeallocateRestartArea( RestartArea );
+ }
+ }
+ }
+
+ if (FirstRestartPageBcb != NULL) {
+
+ CcUnpinData( FirstRestartPageBcb );
+ }
+
+ if (SecondRestartPageBcb != NULL) {
+
+ CcUnpinData( SecondRestartPageBcb );
+ }
+
+ DebugTrace( -1, Dbg, "LfsRestartLogFile: Exit\n", 0 );
+ }
+
+ //
+ // Indicate whether the log is packed.
+ //
+
+ if (PackLogFile
+ && *LfsInfo < LfsPackLog) {
+
+ *LfsInfo = LfsPackLog;
+ }
+
+ return ThisLfcb;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsNormalizeBasicLogFile (
+ IN OUT PLONGLONG FileSize,
+ IN OUT PULONG LogPageSize,
+ IN OUT PUSHORT LogClients
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to normalize the values which describe the
+ log file. It will make the log page a multiple of the system page.
+ Finally we make sure the file size ends on a log page boundary.
+
+ On input all of the parameters have the requested values, on return
+ they have the values to use.
+
+Arguments:
+
+ FileSize - Stated size of the log file.
+
+ LogPageSize - Suggested size for the log page.
+
+ LogClients - Requested number of log clients.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG LocalLogPageSize;
+ LONGLONG RestartPageBytes;
+ LONGLONG LogPages;
+
+ USHORT MaximumClients;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsNormalizeBasicLogFile: Entered\n", 0 );
+
+ //
+ // We always make the log page the same as the system page size.
+ // This may change in the future.
+ //
+
+ *LogPageSize = PAGE_SIZE;
+
+ //
+ // If the log file is greater than the maximum log file size, we
+ // set the log file size to the maximum size.
+ //
+
+ if ( *FileSize > LfsMaximumFileSize ) {
+
+ *FileSize = LfsMaximumFileSize;
+ }
+
+ //
+ // We round the file size down to a system page boundary. This
+ // may also change if we allow non-system page sized log pages.
+ //
+
+ *(PULONG)FileSize &= ~(PAGE_SIZE - 1);
+
+ //
+ // There better be at least 2 restart pages.
+ //
+
+ RestartPageBytes = 2 * PAGE_SIZE;
+
+ if ( *FileSize <= RestartPageBytes ) {
+
+ DebugTrace( 0, Dbg, "Log file is too small\n", 0 );
+ DebugTrace( -1, Dbg, "LfsValidateBasicLogFile: Abnormal Exit\n", 0 );
+
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Now compute the number of log pages.
+ //
+
+ LogPages = *FileSize - RestartPageBytes;
+ LocalLogPageSize = *LogPageSize >> 1;
+
+ while (LocalLogPageSize) {
+
+ LocalLogPageSize = LocalLogPageSize >> 1;
+ LogPages = ((ULONGLONG)(LogPages)) >> 1;
+ }
+
+ //
+ // If there aren't enough log pages then raise an error condition.
+ //
+
+ if (((PLARGE_INTEGER)&LogPages)->HighPart == 0
+ && (ULONG)LogPages < MINIMUM_LFS_PAGES) {
+
+ DebugTrace( 0, Dbg, "Not enough log pages -> %08lx\n", LogPages.LowPart );
+ DebugTrace( -1, Dbg, "LfsValidateBasicLogFile: Abnormal Exit\n", 0 );
+
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Now we compute the amount of space available for log clients.
+ // We will limit the clients to half of the restart system page.
+ //
+
+ MaximumClients = (USHORT) ((PAGE_SIZE / 2) / sizeof( LFS_CLIENT_RECORD ));
+
+ if (*LogClients == 0) {
+
+ *LogClients = 1;
+
+ } else if (*LogClients > MaximumClients) {
+
+ *LogClients = MaximumClients;
+ }
+
+ DebugTrace( -1, Dbg, "LfsNormalizeBasicLogFile: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsUpdateLfcbFromPgHeader (
+ IN PLFCB Lfcb,
+ IN ULONG SystemPageSize,
+ IN ULONG LogPageSize,
+ IN SHORT MajorVersion,
+ IN SHORT MinorVersion,
+ IN BOOLEAN PackLog
+ )
+
+/*++
+
+Routine Description:
+
+ This routine updates the values in the Lfcb which depend on values in the
+ restart page header.
+
+Arguments:
+
+ Lfcb - Log file control block to update.
+
+ SystemPageSize - System page size to use.
+
+ LogPageSize - Log page size to use.
+
+ MajorVersion - Major version number for Lfs.
+
+ MinorVersion - Minor version number for Lfs.
+
+ PackLog - Indicates if we are packing the log file.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsUpdateLfcbFromPgHeader: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "System Page Size -> %08lx\n", SystemPageSize );
+ DebugTrace( 0, Dbg, "Log Page Size -> %08lx\n", LogPageSize );
+ DebugTrace( 0, Dbg, "Major Version -> %04x\n", MajorVersion );
+ DebugTrace( 0, Dbg, "Minor Version -> %04x\n", MinorVersion );
+
+ //
+ // Compute the restart page values.
+ //
+
+ Lfcb->SystemPageSize = SystemPageSize;
+ Lfcb->SystemPageMask = SystemPageSize - 1;
+ Lfcb->SystemPageInverseMask = ~Lfcb->SystemPageMask;
+
+ //
+ // Do the same for the log pages.
+ //
+
+ Lfcb->LogPageSize = LogPageSize;
+ Lfcb->LogPageMask = LogPageSize - 1;
+ Lfcb->LogPageInverseMask = ~Lfcb->LogPageMask;
+
+ Lfcb->LogPageShift = 0;
+
+ while (TRUE) {
+
+ LogPageSize = LogPageSize >> 1;
+
+ if (LogPageSize == 0) {
+
+ break;
+ }
+
+ Lfcb->LogPageShift += 1;
+ }
+
+ //
+ // If we are packing the log file then the first log page is page
+ // 4. Otherwise it is page 2. Use the PackLog value to determine the
+ // Usa values.
+ //
+
+ Lfcb->FirstLogPage = Lfcb->SystemPageSize << 1;
+
+ if (PackLog) {
+
+ Lfcb->FirstLogPage = ( Lfcb->LogPageSize << 1 ) + Lfcb->FirstLogPage;
+
+ Lfcb->LogRecordUsaOffset = (USHORT) LFS_PACKED_RECORD_PAGE_HEADER_SIZE;
+
+ SetFlag( Lfcb->Flags, LFCB_PACK_LOG );
+
+ } else {
+
+ Lfcb->LogRecordUsaOffset = (USHORT) LFS_UNPACKED_RECORD_PAGE_HEADER_SIZE;
+ }
+
+ //
+ // Remember the values for the version numbers.
+ //
+
+ Lfcb->MajorVersion = MajorVersion;
+ Lfcb->MinorVersion = MinorVersion;
+
+ //
+ // Compute the offsets for the update sequence arrays.
+ //
+
+ Lfcb->RestartUsaOffset = LFS_RESTART_PAGE_HEADER_SIZE;
+
+ Lfcb->RestartUsaArraySize = (USHORT) UpdateSequenceArraySize( (ULONG)Lfcb->SystemPageSize );
+ Lfcb->LogRecordUsaArraySize = (USHORT) UpdateSequenceArraySize( (ULONG)Lfcb->LogPageSize );
+
+ DebugTrace( -1, Dbg, "LfsUpdateLfcbFromPgHeader: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsUpdateLfcbFromNoRestart (
+ IN PLFCB Lfcb,
+ IN LONGLONG FileSize,
+ IN LSN LastLsn,
+ IN ULONG LogClients,
+ IN BOOLEAN LogFileWrapped,
+ IN BOOLEAN UseMultiplePageIo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine updates the values in the Lfcb in cases when we don't have a
+ restart area to use.
+
+Arguments:
+
+ Lfcb - Log file control block to update.
+
+ FileSize - Log file size. This is the usable size of the log file. It has
+ already been adjusted to the log page size.
+
+ LastLsn - This is the last Lsn to use for the disk.
+
+ LogClients - This is the number of clients supported.
+
+ LogFileWrapped - Indicates if the log file has wrapped.
+
+ UseMultiplePageIo - Indicates if we should be using large i/o transfers.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG Count;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsUpdateLfcbFromNoRestart: Entered\n", 0 );
+
+ Lfcb->FileSize = FileSize;
+
+ //
+ // We can compute the number of bits needed for the file size by shifting
+ // until the size is 0. We then can subtract 3 bits to account for
+ // quadaligning all file offsets for log records.
+ //
+
+ for (Count = 0;
+ ( FileSize != 0 );
+ Count += 1,
+ FileSize = ((ULONGLONG)(FileSize)) >> 1) {
+ }
+
+ Lfcb->FileDataBits = Count - 3;
+
+ Lfcb->SeqNumberBits = (sizeof( LSN ) * 8) - Lfcb->FileDataBits;
+
+ //
+ // We get a starting sequence number from the given Lsn.
+ // We add 2 to this for our starting sequence number.
+ //
+
+ Lfcb->SeqNumber = LfsLsnToSeqNumber( Lfcb, LastLsn ) + 2;
+
+ Lfcb->SeqNumberForWrap = Lfcb->SeqNumber + 1;
+
+ Lfcb->NextLogPage = Lfcb->FirstLogPage;
+
+ SetFlag( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_NO_OLDEST_LSN );
+
+ //
+ // The oldest Lsn is contructed from the sequence number.
+ //
+
+ Lfcb->OldestLsn.QuadPart = LfsFileOffsetToLsn( Lfcb, 0, Lfcb->SeqNumber );
+ Lfcb->OldestLsnOffset = 0;
+
+ Lfcb->LastFlushedLsn = Lfcb->OldestLsn;
+
+ //
+ // Set the correct flags for the I/O and indicate if we have wrapped.
+ //
+
+ if (LogFileWrapped) {
+
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
+ }
+
+ if (UseMultiplePageIo) {
+
+ SetFlag( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO );
+ }
+
+ //
+ // Compute the Log page values.
+ //
+
+ (ULONG)Lfcb->LogPageDataOffset = QuadAlign( Lfcb->LogRecordUsaOffset
+ + (sizeof( UPDATE_SEQUENCE_NUMBER ) * Lfcb->LogRecordUsaArraySize) );
+
+ Lfcb->LogPageDataSize = Lfcb->LogPageSize - Lfcb->LogPageDataOffset;
+ Lfcb->RecordHeaderLength = LFS_RECORD_HEADER_SIZE;
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ //
+ // Allocate the Lbcb for the tail of the packed log file.
+ //
+
+ LfsAllocateLbcb( Lfcb, &Lfcb->PrevTail );
+ Lfcb->PrevTail->FileOffset = Lfcb->FirstLogPage - Lfcb->LogPageSize;
+
+ LfsAllocateLbcb( Lfcb, &Lfcb->ActiveTail );
+ Lfcb->ActiveTail->FileOffset = Lfcb->PrevTail->FileOffset - Lfcb->LogPageSize;
+
+ //
+ // Remember the different page sizes for reservation.
+ //
+
+ (ULONG)Lfcb->ReservedLogPageSize = (ULONG)Lfcb->LogPageDataSize - Lfcb->RecordHeaderLength;
+
+ } else {
+
+ (ULONG)Lfcb->ReservedLogPageSize = (ULONG)Lfcb->LogPageDataSize;
+ }
+
+ //
+ // Compute the restart page values.
+ //
+
+ Lfcb->RestartDataOffset = QuadAlign( LFS_RESTART_PAGE_HEADER_SIZE
+ + (sizeof( UPDATE_SEQUENCE_NUMBER ) * Lfcb->RestartUsaArraySize) );
+
+ Lfcb->RestartDataSize = (ULONG)Lfcb->SystemPageSize - Lfcb->RestartDataOffset;
+
+ Lfcb->LogClients = (USHORT) LogClients;
+
+ Lfcb->ClientArrayOffset = FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray );
+
+ Lfcb->RestartAreaSize = Lfcb->ClientArrayOffset
+ + (sizeof( LFS_CLIENT_RECORD ) * Lfcb->LogClients );
+
+ //
+ // The total available log file space is the number of log file pages times
+ // the space available on each page.
+ //
+
+ Lfcb->TotalAvailInPages = Lfcb->FileSize - Lfcb->FirstLogPage;
+ Lfcb->TotalAvailable = Int64ShrlMod32(((ULONGLONG)(Lfcb->TotalAvailInPages)), Lfcb->LogPageShift);
+
+ //
+ // If the log file is packed we assume that we can't use the end of the
+ // page less than the file record size. Then we won't need to reserve more
+ // than the caller asks for.
+ //
+
+ Lfcb->MaxCurrentAvail = Lfcb->TotalAvailable * (ULONG)Lfcb->ReservedLogPageSize;
+
+ Lfcb->TotalAvailable = Lfcb->TotalAvailable * (ULONG)Lfcb->LogPageDataSize;
+
+ Lfcb->CurrentAvailable = Lfcb->MaxCurrentAvail;
+
+ DebugTrace( -1, Dbg, "LfsUpdateLfcbFromNoRestart: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsUpdateLfcbFromRestart (
+ IN OUT PLFCB Lfcb,
+ IN PLFS_RESTART_AREA RestartArea,
+ IN USHORT RestartOffset
+ )
+
+/*++
+
+Routine Description:
+
+ This routine updates the values in the Lfcb based on data in the
+ restart area.
+
+Arguments:
+
+ Lfcb - Log file control block to update.
+
+ RestartArea - Restart area to use to update the Lfcb.
+
+ RestartOffset - This is the offset to the restart area in the restart page.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONGLONG LsnFileOffset;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsUpdateLfcbFromRestartArea: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "RestartArea -> %08lx\n", RestartArea );
+
+ Lfcb->FileSize = RestartArea->FileSize;
+
+ //
+ // We get the sequence number bits from the restart area and compute the
+ // file data bits.
+ //
+
+ Lfcb->SeqNumberBits = RestartArea->SeqNumberBits;
+ Lfcb->FileDataBits = (sizeof( LSN ) * 8) - Lfcb->SeqNumberBits;
+
+ //
+ // We look at the last flushed Lsn to determine the current sequence count and
+ // the next log page to examine.
+ //
+
+ Lfcb->LastFlushedLsn = RestartArea->CurrentLsn;
+
+ Lfcb->SeqNumber = LfsLsnToSeqNumber( Lfcb, Lfcb->LastFlushedLsn );
+ Lfcb->SeqNumberForWrap = Lfcb->SeqNumber + 1;
+
+ //
+ // The restart area size depends on the number of clients and whether the
+ // the file is packed.
+ //
+
+ Lfcb->LogClients = RestartArea->LogClients;
+
+ //
+ // Compute the restart page values from the restart offset.
+ //
+
+ Lfcb->RestartDataOffset = RestartOffset;
+ Lfcb->RestartDataSize = (ULONG)Lfcb->SystemPageSize - RestartOffset;
+
+ //
+ // For a packed log file we can find the following values in the restart
+ // area. Otherwise we compute them from the current structure sizes.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ Lfcb->RecordHeaderLength = RestartArea->RecordHeaderLength;
+
+ Lfcb->ClientArrayOffset = RestartArea->ClientArrayOffset;
+
+ Lfcb->RestartAreaSize = RestartArea->RestartAreaLength;
+
+ (ULONG)Lfcb->LogPageDataOffset = RestartArea->LogPageDataOffset;
+ Lfcb->LogPageDataSize = Lfcb->LogPageSize - Lfcb->LogPageDataOffset;
+
+ //
+ // For packed files we allocate the tail Lbcbs.
+ //
+
+ //
+ // Allocate the Lbcb for the tail of the packed log file.
+ //
+
+ LfsAllocateLbcb( Lfcb, &Lfcb->PrevTail );
+ Lfcb->PrevTail->FileOffset = Lfcb->FirstLogPage - Lfcb->LogPageSize;
+
+ LfsAllocateLbcb( Lfcb, &Lfcb->ActiveTail );
+ Lfcb->ActiveTail->FileOffset = Lfcb->PrevTail->FileOffset - Lfcb->LogPageSize;
+
+ //
+ // Remember the different page sizes for reservation.
+ //
+
+ (ULONG)Lfcb->ReservedLogPageSize = (ULONG)Lfcb->LogPageDataSize - Lfcb->RecordHeaderLength;
+
+ } else {
+
+ Lfcb->RecordHeaderLength = LFS_RECORD_HEADER_SIZE;
+ Lfcb->ClientArrayOffset = FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray );
+
+ Lfcb->RestartAreaSize = Lfcb->ClientArrayOffset
+ + (sizeof( LFS_CLIENT_RECORD ) * Lfcb->LogClients);
+
+ (ULONG)Lfcb->LogPageDataOffset = QuadAlign( Lfcb->LogRecordUsaOffset
+ + (sizeof( UPDATE_SEQUENCE_NUMBER ) * Lfcb->LogRecordUsaArraySize) );
+
+ Lfcb->LogPageDataSize = Lfcb->LogPageSize - Lfcb->LogPageDataOffset;
+
+ (ULONG)Lfcb->ReservedLogPageSize = (ULONG)Lfcb->LogPageDataSize;
+ }
+
+ //
+ // If the current last flushed Lsn offset is before the first log page
+ // then this is a pseudo Lsn.
+ //
+
+ LsnFileOffset = LfsLsnToFileOffset( Lfcb, Lfcb->LastFlushedLsn );
+
+ if ( LsnFileOffset < Lfcb->FirstLogPage ) {
+
+ SetFlag( Lfcb->Flags, LFCB_NO_LAST_LSN );
+ Lfcb->NextLogPage = Lfcb->FirstLogPage;
+
+ //
+ // Otherwise look at the last Lsn to determine where it ends in the file.
+ //
+
+ } else {
+
+ LONGLONG LsnFinalOffset;
+ BOOLEAN Wrapped;
+
+ ULONG DataLength;
+ ULONG RemainingPageBytes;
+
+ DataLength = RestartArea->LastLsnDataLength;
+
+ //
+ // Find the end of this log record.
+ //
+
+ LfsLsnFinalOffset( Lfcb,
+ Lfcb->LastFlushedLsn,
+ DataLength,
+ &LsnFinalOffset );
+
+ //
+ // If we wrapped in the file then increment the sequence number.
+ //
+
+ if ( LsnFinalOffset <= LsnFileOffset ) {
+
+ Lfcb->SeqNumber = 1 + Lfcb->SeqNumber;
+
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
+ }
+
+ //
+ // Now compute the next log page to use. If we are packing the log file
+ // we will attempt to use the same page.
+ //
+
+ LfsTruncateOffsetToLogPage( Lfcb, LsnFinalOffset, &LsnFileOffset );
+
+ RemainingPageBytes = (ULONG)Lfcb->LogPageSize
+ - ((((ULONG)LsnFinalOffset) & Lfcb->LogPageMask) + 1);
+
+ //
+ // If we are packing the log file and we can fit another log record on the
+ // page, move back a page in the log file.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && (RemainingPageBytes >= Lfcb->RecordHeaderLength)) {
+
+ SetFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+ Lfcb->NextLogPage = LsnFileOffset;
+ Lfcb->ReusePageOffset = (ULONG)Lfcb->LogPageSize - RemainingPageBytes;
+
+ } else {
+
+ LfsNextLogPageOffset( Lfcb, LsnFileOffset, &Lfcb->NextLogPage, &Wrapped );
+ }
+ }
+
+ //
+ // Find the oldest client Lsn. Use the last flushed Lsn as a starting point.
+ //
+
+ Lfcb->OldestLsn = Lfcb->LastFlushedLsn;
+
+ LfsFindOldestClientLsn( RestartArea,
+ Add2Ptr( RestartArea, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD ),
+ &Lfcb->OldestLsn );
+
+ Lfcb->OldestLsnOffset = LfsLsnToFileOffset( Lfcb, Lfcb->OldestLsn );
+
+ //
+ // If there is no oldest client Lsn, then update the flag in the Lfcb.
+ //
+
+ if ( Lfcb->OldestLsnOffset < Lfcb->FirstLogPage ) {
+
+ SetFlag( Lfcb->Flags, LFCB_NO_OLDEST_LSN );
+ }
+
+ //
+ // We need to determine the flags for the Lfcb. These flags let us know
+ // if we wrapped in the file and if we are using multiple page I/O.
+ //
+
+ if (!FlagOn( RestartArea->Flags, RESTART_SINGLE_PAGE_IO )) {
+
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED | LFCB_MULTIPLE_PAGE_IO );
+ }
+
+ //
+ // The total available log file space is the number of log file pages times
+ // the space available on each page.
+ //
+
+ Lfcb->TotalAvailInPages = Lfcb->FileSize - Lfcb->FirstLogPage;
+
+ Lfcb->TotalAvailable = Int64ShrlMod32(((ULONGLONG)(Lfcb->TotalAvailInPages)), Lfcb->LogPageShift);
+
+ //
+ // If the log file is packed we assume that we can't use the end of the
+ // page less than the file record size. Then we won't need to reserve more
+ // than the caller asks for.
+ //
+
+ Lfcb->MaxCurrentAvail = Lfcb->TotalAvailable * (ULONG)Lfcb->ReservedLogPageSize;
+
+ Lfcb->TotalAvailable = Lfcb->TotalAvailable * (ULONG)Lfcb->LogPageDataSize;
+
+ LfsFindCurrentAvail( Lfcb );
+
+ DebugTrace( -1, Dbg, "LfsUpdateLfcbFromRestartArea: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsUpdateRestartAreaFromLfcb (
+ IN PLFCB Lfcb,
+ IN PLFS_RESTART_AREA RestartArea
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to update a restart area from the values stored
+ in the Lfcb. This is typically done in a case where we won't use
+ any of the current values in the restart area.
+
+Arguments:
+
+ Lfcb - Log file control block.
+
+ RestartArea - Restart area to update.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLFS_CLIENT_RECORD Client;
+ USHORT ClientIndex;
+ USHORT PrevClient = LFS_NO_CLIENT;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsUpdateRestartAreaFromLfcb: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // We can copy most of the fields directly out of Lfcb.
+ //
+
+ RestartArea->CurrentLsn = Lfcb->LastFlushedLsn;
+ RestartArea->LogClients = Lfcb->LogClients;
+
+ if (!FlagOn( Lfcb->Flags, LFCB_MULTIPLE_PAGE_IO )) {
+
+ SetFlag( RestartArea->Flags, RESTART_SINGLE_PAGE_IO );
+ }
+
+ RestartArea->SeqNumberBits = Lfcb->SeqNumberBits;
+
+ RestartArea->FileSize = Lfcb->FileSize;
+ RestartArea->LastLsnDataLength = 0;
+ RestartArea->ClientArrayOffset = Lfcb->ClientArrayOffset;
+ RestartArea->RestartAreaLength = (USHORT) Lfcb->RestartAreaSize;
+
+ RestartArea->RecordHeaderLength = Lfcb->RecordHeaderLength;
+ RestartArea->LogPageDataOffset = (USHORT)Lfcb->LogPageDataOffset;
+
+ //
+ // We set the in use list as empty and the free list as containing
+ // all of the client entries.
+ //
+
+ RestartArea->ClientInUseList = LFS_NO_CLIENT;
+ RestartArea->ClientFreeList = 0;
+
+ for (ClientIndex = 1,
+ Client = Add2Ptr( RestartArea, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD );
+ ClientIndex < Lfcb->LogClients;
+ ClientIndex += 1,
+ Client++) {
+
+ Client->PrevClient = PrevClient;
+ Client->NextClient = ClientIndex;
+
+ PrevClient = ClientIndex - 1;
+ }
+
+ //
+ // We're now at the last client.
+ //
+
+ Client->PrevClient = PrevClient;
+ Client->NextClient = LFS_NO_CLIENT;
+
+ DebugTrace( -1, Dbg, "LfsUpdateRestartAreaFromLfcb: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsInitializeLogFilePriv (
+ IN PLFCB Lfcb,
+ IN BOOLEAN ForceRestartToDisk,
+ IN ULONG RestartAreaSize,
+ IN LONGLONG StartOffsetForClear,
+ IN BOOLEAN ClearLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is our internal routine for initializing a log file.
+ This can be the case where we are updating the log file for
+ update sequence array, or differing page size or new log file size.
+
+Arguments:
+
+ Lfcb - This is the Lfcb for this log file. It should already have
+ the version number information stored.
+
+ ForceRestartToDisk - Indicates that we want to actually force restart
+ areas to disk instead of simply queueing them to the start of the
+ workqueue.
+
+ RestartAreaSize - This is the size for the restart areas. This may
+ be larger than the size in the Lfcb because we may be clearing
+ stale data out of the file.
+
+ StartOffsetForClear - If we are clearing the file we want to uninitialize
+ from this point.
+
+ ClearLogFile - Indicates if we want to uninitialize the log file to
+ remove stale data. This is done specifically when changing
+ system page sizes.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsInitializeLogFilePriv: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Force Restart -> %04x\n", ForceRestartToDisk );
+ DebugTrace( 0, Dbg, "RestartAreaSize -> %08lx\n", RestartAreaSize );
+ DebugTrace( 0, Dbg, "StartOffset (Low) -> %08lx\n", StartOffsetForClear.LowPart );
+ DebugTrace( 0, Dbg, "StartOffset (High) -> %08lx\n", StartOffsetForClear.HighPart );
+ DebugTrace( 0, Dbg, "Clear Log File -> %04x\n", ClearLogFile );
+
+ //
+ // We start by queueing the restart areas.
+ //
+
+ LfsWriteLfsRestart( Lfcb,
+ RestartAreaSize,
+ FALSE );
+
+ LfsWriteLfsRestart( Lfcb,
+ RestartAreaSize,
+ ForceRestartToDisk );
+
+ //
+ // If we are to clear the log file, we write all 0xff into the
+ // log pages beginning at the log page offset.
+ //
+
+ if (ClearLogFile) {
+
+ PCHAR LogPage;
+ PBCB LogPageBcb = NULL;
+
+ try {
+
+ while ( StartOffsetForClear < Lfcb->FileSize ) {
+
+ BOOLEAN UsaError;
+
+ //
+ // We'll do the best we can and ignore all errors.
+ //
+
+ if (NT_SUCCESS( LfsPinOrMapData( Lfcb,
+ StartOffsetForClear,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ FALSE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &LogPage,
+ &LogPageBcb ))) {
+
+ RtlFillMemoryUlong( (PVOID)LogPage,
+ (ULONG)Lfcb->LogPageSize,
+ LFS_SIGNATURE_UNINITIALIZED_ULONG );
+
+ LfsFlushLogPage( Lfcb,
+ LogPage,
+ StartOffsetForClear,
+ &LogPageBcb );
+
+ StartOffsetForClear = Lfcb->LogPageSize + StartOffsetForClear;
+ }
+ }
+
+ } finally {
+
+ if (LogPageBcb != NULL) {
+
+ CcUnpinData( LogPageBcb );
+ }
+ }
+ }
+
+ DebugTrace( -1, Dbg, "LfsInitializeLogFilePriv: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsFindLastLsn (
+ IN OUT PLFCB Lfcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine walks through the log pages for a file, searching for the
+ last log page written to the file. It updates the Lfcb and the current
+ restart area as well.
+
+ We proceed in the following manner.
+
+ 1 - Walk through and find all of the log pages successfully
+ flushed to disk. This search terminates when either we find
+ an error or when we find a previous page on the disk.
+
+ 2 - For the error case above, we want to insure that the error found
+ was due to a system crash and that there are no complete I/O
+ transfers after the bad region.
+
+ 3 - We will look at the 2 pages with the tail copies if the log file
+ is packed to check on pages with errors.
+
+ At the end of this routine we will repair the log file by copying the tail
+ copies back to their correct location in the log file.
+
+Arguments:
+
+ Lfcb - Log file control block for this log file.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ USHORT PageCount;
+ USHORT PagePosition;
+
+ LONGLONG CurrentLogPageOffset;
+ LONGLONG NextLogPageOffset;
+
+ LSN LastKnownLsn;
+
+ BOOLEAN Wrapped;
+ BOOLEAN WrappedLogFile = FALSE;
+
+ LONGLONG ExpectedSeqNumber;
+
+ LONGLONG FirstPartialIo;
+ ULONG PartialIoCount = 0;
+
+ PLFS_RECORD_PAGE_HEADER LogPageHeader;
+ PBCB LogPageHeaderBcb = NULL;
+
+ PLFS_RECORD_PAGE_HEADER TestPageHeader;
+ PBCB TestPageHeaderBcb = NULL;
+
+ LONGLONG FirstTailFileOffset;
+ PLFS_RECORD_PAGE_HEADER FirstTailPage;
+ LONGLONG FirstTailOffset = 0;
+ PBCB FirstTailPageBcb = NULL;
+
+ LONGLONG SecondTailFileOffset;
+ PLFS_RECORD_PAGE_HEADER SecondTailPage;
+ LONGLONG SecondTailOffset = 0;
+ PBCB SecondTailPageBcb = NULL;
+
+ PLFS_RECORD_PAGE_HEADER TailPage;
+
+ BOOLEAN UsaError;
+ BOOLEAN ReplacePage = FALSE;
+ BOOLEAN ValidFile = FALSE;
+
+ BOOLEAN InitialReusePage = FALSE;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindLastLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // The page count and page position are from the last page
+ // sucessfully read. Initialize these to indicate the
+ // 'previous' transfer was complete.
+ //
+
+ PageCount = 1;
+ PagePosition = 1;
+
+ //
+ // We have the current Lsn in the restart area. This is the last
+ // Lsn on a log page. We compute the next file offset and sequence
+ // number.
+ //
+
+ CurrentLogPageOffset = Lfcb->NextLogPage;
+
+ //
+ // If the next log page is the first log page in the file and
+ // the last Lsn represented a log record, then remember that we
+ // have wrapped in the log file.
+ //
+
+ if (( CurrentLogPageOffset == Lfcb->FirstLogPage )
+ && !FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL )) {
+
+ ExpectedSeqNumber = Lfcb->SeqNumber + 1;
+ WrappedLogFile = TRUE;
+
+ } else {
+
+ ExpectedSeqNumber = Lfcb->SeqNumber;
+ }
+
+ //
+ // If we are going to try to reuse the tail of the last known
+ // page, then remember the last Lsn on this page.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {
+
+ LastKnownLsn = Lfcb->LastFlushedLsn;
+
+ //
+ // There are some special conditions allowed for this page when
+ // we read it. It could be either the first or last of the transfer.
+ // It may also have a tail copy.
+ //
+
+ InitialReusePage = TRUE;
+
+ } else {
+
+ LastKnownLsn = LfsLi0;
+ }
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // If this is a packed log file, let's pin the two tail copy pages.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ //
+ // Start with the second page.
+ //
+
+ SecondTailFileOffset = Lfcb->FirstLogPage - Lfcb->LogPageSize;
+
+ if (NT_SUCCESS( LfsPinOrMapData( Lfcb,
+ SecondTailFileOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ &SecondTailPage,
+ &SecondTailPageBcb ))) {
+
+ //
+ // If this isn't a valid page then ignore it.
+ //
+
+ if (UsaError
+ || *((PULONG) &SecondTailPage->MultiSectorHeader.Signature) != LFS_SIGNATURE_RECORD_PAGE_ULONG) {
+
+ CcUnpinData( SecondTailPageBcb );
+ SecondTailPageBcb = SecondTailPage = NULL;
+
+ } else {
+
+ SecondTailOffset = SecondTailPage->Copy.FileOffset;
+ }
+ }
+
+ FirstTailFileOffset = SecondTailFileOffset - Lfcb->LogPageSize;
+
+ //
+ // Now try the first.
+ //
+
+ if (NT_SUCCESS( LfsPinOrMapData( Lfcb,
+ FirstTailFileOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ &FirstTailPage,
+ &FirstTailPageBcb ))) {
+
+ //
+ // If this isn't a valid page then ignore it.
+ //
+
+ if (UsaError
+ || *((PULONG) &FirstTailPage->MultiSectorHeader.Signature) != LFS_SIGNATURE_RECORD_PAGE_ULONG) {
+
+ CcUnpinData( FirstTailPageBcb );
+ FirstTailPageBcb = FirstTailPage = NULL;
+
+ } else {
+
+ FirstTailOffset = FirstTailPage->Copy.FileOffset;
+ }
+ }
+ }
+
+ //
+ // We continue walking through the file, log page by log page looking
+ // for the end of the data transferred. The loop below looks for
+ // a log page which contains the end of a log record. Each time a
+ // log record is successfully read from the disk, we update our in-memory
+ // structures to reflect this. We exit this loop when we are at a point
+ // where we don't want to find any subsequent pages. This occurs when
+ //
+ // - we get an I/O error reading a page
+ // - we get a Usa error reading a page
+ // - we have a tail copy with more recent data than contained on the page
+ //
+
+ while (TRUE) {
+
+ LONGLONG ActualSeqNumber;
+ TailPage = NULL;
+
+ //
+ // Pin the next log page, allowing errors.
+ //
+
+ Status = LfsPinOrMapData( Lfcb,
+ CurrentLogPageOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &LogPageHeader,
+ &LogPageHeaderBcb );
+
+ //
+ // Compute the next log page offset in the file.
+ //
+
+ LfsNextLogPageOffset( Lfcb,
+ CurrentLogPageOffset,
+ &NextLogPageOffset,
+ &Wrapped );
+
+ //
+ // If we are at the expected first page of a transfer
+ // check to see if either tail copy is at this offset.
+ // If this page is the last page of a transfer, check
+ // if we wrote a subsequent tail copy.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG ) &&
+ ((PageCount == PagePosition) ||
+ (PageCount == PagePosition + 1))) {
+
+ //
+ // Check if the offset matches either the first or second
+ // tail copy. It is possible it will match both.
+ //
+
+ if (CurrentLogPageOffset == FirstTailOffset) {
+
+ TailPage = FirstTailPage;
+ }
+
+ if (CurrentLogPageOffset == SecondTailOffset) {
+
+ //
+ // If we already matched on the first page then
+ // check the ending Lsn's.
+ //
+
+ if ((TailPage == NULL) ||
+ (SecondTailPage->Header.Packed.LastEndLsn.QuadPart >
+ FirstTailPage->Header.Packed.LastEndLsn.QuadPart )) {
+
+ TailPage = SecondTailPage;
+ }
+ }
+
+ //
+ // If we have a candidate for a tail copy, check and see if it is
+ // in the expected pass through the file. For that to be true we
+ // must be at the first page of an I/O block. Also the last Lsn on the
+ // copy page must match the last known flushed Lsn or the sequence
+ // number on the page must be the expected sequence number.
+ //
+
+ if (TailPage) {
+
+ if (LastKnownLsn.QuadPart < TailPage->Header.Packed.LastEndLsn.QuadPart) {
+
+ ActualSeqNumber = LfsLsnToSeqNumber( Lfcb, TailPage->Header.Packed.LastEndLsn );
+
+ //
+ // If the sequence number is not expected, then don't use the tail
+ // copy.
+ //
+
+ if (ExpectedSeqNumber != ActualSeqNumber) {
+
+ TailPage = NULL;
+ }
+
+ //
+ // If the last Lsn is greater than the one on this page
+ // then forget this tail.
+ //
+
+ } else if (LastKnownLsn.QuadPart > TailPage->Header.Packed.LastEndLsn.QuadPart) {
+
+ TailPage = NULL;
+ }
+ }
+ }
+
+ //
+ // If we have an error on the current page, we will break out of
+ // this loop.
+ //
+
+ if (!NT_SUCCESS( Status ) || UsaError) {
+
+ break;
+ }
+
+ //
+ // If the last Lsn on this page doesn't match the previous
+ // known last Lsn or the sequence number is not expected
+ // we are done.
+ //
+
+ ActualSeqNumber = LfsLsnToSeqNumber( Lfcb,
+ LogPageHeader->Copy.LastLsn );
+
+ if ((LastKnownLsn.QuadPart != LogPageHeader->Copy.LastLsn.QuadPart) &&
+ (ActualSeqNumber != ExpectedSeqNumber)) {
+
+ break;
+ }
+
+ //
+ // Check that the page position and page count values are correct.
+ // If this is the first page of a transfer the position must be
+ // 1 and the count will be unknown.
+ //
+
+ if (PageCount == PagePosition) {
+
+ //
+ // If the current page is the first page we are looking at
+ // and we are reusing this page then it can be either the
+ // first or last page of a transfer. Otherwise it can only
+ // be the first.
+ //
+
+ if ((LogPageHeader->PagePosition != 1) &&
+ (!InitialReusePage ||
+ (LogPageHeader->PagePosition != LogPageHeader->PageCount))) {
+
+ break;
+ }
+
+ //
+ // The page position better be 1 more than the last page position
+ // and the page count better match.
+ //
+
+ } else if ((LogPageHeader->PageCount != PageCount) ||
+ (LogPageHeader->PagePosition != PagePosition + 1)) {
+
+ break;
+ }
+
+ //
+ // We have a valid page in the file and may have a valid page in
+ // the tail copy area. If the tail page was written after
+ // the page in the file then break out of the loop.
+ //
+
+ if (TailPage &&
+ (TailPage->Header.Packed.LastEndLsn.QuadPart > LogPageHeader->Copy.LastLsn.QuadPart)) {
+
+ //
+ // Remember if we will replace the page.
+ //
+
+ ReplacePage = TRUE;
+ break;
+ }
+
+ TailPage = NULL;
+
+ //
+ // The log page is expected. If this contains the end of
+ // some log record we can update some fields in the Lfcb.
+ //
+
+ if (FlagOn( LogPageHeader->Flags, LOG_PAGE_LOG_RECORD_END )) {
+
+ //
+ // Since we have read this page we know the Lfcb sequence
+ // number is the same as our expected value. We also
+ // assume we will not reuse the tail.
+ //
+
+ Lfcb->SeqNumber = ExpectedSeqNumber;
+ ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ Lfcb->LastFlushedLsn = LogPageHeader->Header.Packed.LastEndLsn;
+
+ //
+ // If there is room on this page for another header then
+ // remember we want to reuse the page.
+ //
+
+ if (Lfcb->RecordHeaderLength <=
+ ((ULONG)Lfcb->LogPageSize - LogPageHeader->Header.Packed.NextRecordOffset )) {
+
+ SetFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+ Lfcb->ReusePageOffset = LogPageHeader->Header.Packed.NextRecordOffset;
+ }
+
+ } else {
+
+ Lfcb->LastFlushedLsn = LogPageHeader->Copy.LastLsn;
+ }
+
+ Lfcb->RestartArea->CurrentLsn = Lfcb->LastFlushedLsn;
+
+ ClearFlag( Lfcb->Flags, LFCB_NO_LAST_LSN );
+
+ //
+ // If we may try to reuse the current page then use
+ // that as the next page offset. Otherwise move to the
+ // next page in the file.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {
+
+ Lfcb->NextLogPage = CurrentLogPageOffset;
+
+ } else {
+
+ Lfcb->NextLogPage = NextLogPageOffset;
+ }
+
+ //
+ // If we wrapped the log file, then we set the bit indicating so.
+ //
+
+ if (WrappedLogFile) {
+
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
+ }
+ }
+
+ //
+ // Remember the last page count and position. Also remember
+ // the last known lsn.
+ //
+
+ PageCount = LogPageHeader->PageCount;
+ PagePosition = LogPageHeader->PagePosition;
+ LastKnownLsn = LogPageHeader->Copy.LastLsn;
+
+ //
+ // If we are wrapping to the beginning of the file then update
+ // the expected sequence number.
+ //
+
+ if (Wrapped) {
+
+ ExpectedSeqNumber = ExpectedSeqNumber + 1;
+ WrappedLogFile = TRUE;
+ }
+
+ CurrentLogPageOffset = NextLogPageOffset;
+
+ //
+ // Unpin the last log page pinned.
+ //
+
+ CcUnpinData( LogPageHeaderBcb );
+ LogPageHeaderBcb = NULL;
+
+ InitialReusePage = FALSE;
+ }
+
+ //
+ // At this point we expect that there will be no more new pages in
+ // the log file. We could have had an error of some sort on the most recent
+ // page or we may have found a tail copy for the current page.
+ // If the error occurred on the last Io to the file then
+ // this log file is useful. Otherwise the log file can't be used.
+ //
+
+ //
+ // If we have a tail copy page then update the values in the
+ // Lfcb and restart area.
+ //
+
+ if (TailPage != NULL) {
+
+ //
+ // Since we have read this page we know the Lfcb sequence
+ // number is the same as our expected value.
+ //
+
+ Lfcb->SeqNumber = ExpectedSeqNumber;
+
+ Lfcb->LastFlushedLsn = TailPage->Header.Packed.LastEndLsn;
+
+ Lfcb->RestartArea->CurrentLsn = Lfcb->LastFlushedLsn;
+
+ ClearFlag( Lfcb->Flags, LFCB_NO_LAST_LSN );
+
+ //
+ // If there is room on this page for another header then
+ // remember we want to reuse the page.
+ //
+
+ if (((ULONG)Lfcb->LogPageSize - TailPage->Header.Packed.NextRecordOffset )
+ >= Lfcb->RecordHeaderLength) {
+
+ SetFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+ Lfcb->NextLogPage = CurrentLogPageOffset;
+ Lfcb->ReusePageOffset = TailPage->Header.Packed.NextRecordOffset;
+
+ } else {
+
+ ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
+ Lfcb->NextLogPage = NextLogPageOffset;
+ }
+
+ //
+ // If we wrapped the log file, then we set the bit indicating so.
+ //
+
+ if (WrappedLogFile) {
+
+ SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
+ }
+ }
+
+ //
+ // Remember that the partial IO will start at the next page.
+ //
+
+ FirstPartialIo = NextLogPageOffset;
+
+ //
+ // If the next page is the first page of the file then update
+ // the sequence number for log records which begin on the next
+ // page.
+ //
+
+ if (Wrapped) {
+
+ ExpectedSeqNumber = ExpectedSeqNumber + 1;
+ }
+
+ //
+ // If we know the length of the transfer containing the page we stopped
+ // on we can just go to the page following the transfer and check
+ // the sequence number. If we replaced the page then we have already
+ // modified the numbers. If we know that only single pages were written
+ // to disk then we will munge the numbers now. If we were in the
+ // middle of a multi-page I/O then the numbers are already set up.
+ //
+
+ //
+ // If we have a tail copy or are performing single page I/O
+ // we can immediately look at the next page.
+ //
+
+ if (ReplacePage ||
+ FlagOn( Lfcb->RestartArea->Flags, RESTART_SINGLE_PAGE_IO )) {
+
+ //
+ // Fudge the counts to show that we don't need swallow any pages.
+ //
+
+ PageCount = 2;
+ PagePosition = 1;
+
+ //
+ // If the counts match it means the current page should be the first
+ // page of a transfer. We need to walk forward enough to guarantee
+ // that there was no subsequent transfer that made it out to disk.
+ //
+
+ } else if (PagePosition == PageCount) {
+
+ USHORT CurrentPosition;
+
+ //
+ // If the next page causes us to wrap to the beginning of the log
+ // file then we know which page to check next.
+ //
+
+ if (Wrapped) {
+
+ //
+ // Fudge the counts to show that we don't need swallow any pages.
+ //
+
+ PageCount = 2;
+ PagePosition = 1;
+
+ //
+ // Walk forward looking for a page which is from a different IO transfer
+ // from the page we failed on.
+ //
+
+ } else {
+
+ //
+ // We need to find a log page we know is not part of the log
+ // page which caused the original error.
+ //
+ // Maintain the count within the current transfer.
+ //
+
+ CurrentPosition = 2;
+
+ do {
+
+ //
+ // We walk through the file, reading log pages. If we find
+ // a readable log page that must lie in a subsequent Io block,
+ // we exit.
+ //
+
+ if (TestPageHeaderBcb != NULL) {
+
+ CcUnpinData( TestPageHeaderBcb );
+ TestPageHeaderBcb = NULL;
+ }
+
+ Status = LfsPinOrMapData( Lfcb,
+ NextLogPageOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &TestPageHeader,
+ &TestPageHeaderBcb );
+
+ //
+ // If we get a USA error then assume that we correctly
+ // found the end of the original transfer.
+ //
+
+ if (UsaError) {
+
+ ValidFile = TRUE;
+ break;
+
+ //
+ // If we were able to read the page, we examine it to see
+ // if it is in the same or different Io block.
+ //
+
+ } else if (NT_SUCCESS( Status )) {
+
+ //
+ // If this page is part of the error causing I/O, we will
+ // use the transfer length to determine the page to
+ // read for a subsequent error.
+ //
+
+ if (TestPageHeader->PagePosition == CurrentPosition
+ && LfsCheckSubsequentLogPage( Lfcb,
+ TestPageHeader,
+ NextLogPageOffset,
+ ExpectedSeqNumber )) {
+
+ PageCount = TestPageHeader->PageCount + 1;
+ PagePosition = TestPageHeader->PagePosition;
+
+ break;
+
+ //
+ // We found know the Io causing the error didn't
+ // complete. So we have no more checks to do.
+ //
+
+ } else {
+
+ ValidFile = TRUE;
+ break;
+ }
+
+ //
+ // Try the next page.
+ //
+
+ } else {
+
+ //
+ // Move to the next log page.
+ //
+
+ LfsNextLogPageOffset( Lfcb,
+ NextLogPageOffset,
+ &NextLogPageOffset,
+ &Wrapped );
+
+ //
+ // If the file wrapped then initialize the page count
+ // and position so that we will not skip over any
+ // pages in the final verification below.
+ //
+
+ if (Wrapped) {
+
+ ExpectedSeqNumber = ExpectedSeqNumber + 1;
+
+ PageCount = 2;
+ PagePosition = 1;
+ }
+
+ CurrentPosition += 1;
+ }
+
+ //
+ // This is one more page we will want to uninitialize.
+ //
+
+ PartialIoCount += 1;
+
+ } while( !Wrapped );
+ }
+ }
+
+ //
+ // If we are unsure whether the file is valid then we will have
+ // the count and position in the current transfer. We will walk through
+ // this transfer and read the subsequent page.
+ //
+
+ if (!ValidFile) {
+
+ ULONG RemainingPages;
+
+ //
+ // Skip over the remaining pages in this transfer.
+ //
+
+ RemainingPages = (PageCount - PagePosition) - 1;
+
+ PartialIoCount += RemainingPages;
+
+ while (RemainingPages--) {
+
+ LfsNextLogPageOffset( Lfcb,
+ NextLogPageOffset,
+ &NextLogPageOffset,
+ &Wrapped );
+
+ if (Wrapped) {
+
+ ExpectedSeqNumber = ExpectedSeqNumber + 1;
+ }
+ }
+
+ //
+ // Call our routine to check this log page.
+ //
+
+ if (TestPageHeaderBcb != NULL) {
+
+ CcUnpinData( TestPageHeaderBcb );
+ TestPageHeaderBcb = NULL;
+ }
+
+ Status = LfsPinOrMapData( Lfcb,
+ NextLogPageOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &TestPageHeader,
+ &TestPageHeaderBcb );
+
+ if (NT_SUCCESS( Status )
+ && !UsaError) {
+
+ if (LfsCheckSubsequentLogPage( Lfcb,
+ TestPageHeader,
+ NextLogPageOffset,
+ ExpectedSeqNumber )) {
+
+ DebugTrace( 0, Dbg, "Log file is fatally flawed\n", 0 );
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+ }
+
+ ValidFile = TRUE;
+ }
+
+ //
+ // Make sure the current page is unpinned.
+ //
+
+ if (LogPageHeaderBcb != NULL) {
+
+ CcUnpinData( LogPageHeaderBcb );
+ LogPageHeaderBcb = NULL;
+ }
+
+ //
+ // We have a valid file. The Lfcb is initialized to the point where
+ // the last log record was found. We possibly have a copy of the
+ // last page in the log file stored as a copy. Or we could just have
+ // a page that we would like to reuse the end of.
+ //
+
+ if (TailPage != NULL) {
+
+ //
+ // We will pin the correct page and copy the data from this
+ // page into it. We will then flush it out to disk.
+ //
+
+ LfsPinOrMapData( Lfcb,
+ TailPage->Copy.FileOffset,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ FALSE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &LogPageHeader,
+ &LogPageHeaderBcb );
+
+ RtlCopyMemory( LogPageHeader,
+ TailPage,
+ (ULONG)Lfcb->LogPageSize );
+
+ //
+ // Fill in last flushed lsn value flush the page.
+ //
+
+ LogPageHeader->Copy.LastLsn = TailPage->Header.Packed.LastEndLsn;
+
+ LfsFlushLogPage( Lfcb,
+ LogPageHeader,
+ TailPage->Copy.FileOffset,
+ &LogPageHeaderBcb );
+ }
+
+ //
+ // We also want to write over any partial I/O so it doesn't cause
+ // us problems on a subsequent restart. We have the starting offset
+ // and the number of blocks. We will simply write a Baad signature into
+ // each of these pages. Any subsequent reads will have a Usa error.
+ //
+
+ while (PartialIoCount--) {
+
+ if (NT_SUCCESS( LfsPinOrMapData( Lfcb,
+ FirstPartialIo,
+ (ULONG)Lfcb->LogPageSize,
+ TRUE,
+ TRUE,
+ TRUE,
+ &UsaError,
+ (PVOID *) &LogPageHeader,
+ &LogPageHeaderBcb ))) {
+
+ //
+ // Just store a the usa array header in the multi-section
+ // header.
+ //
+
+ *((PULONG) &LogPageHeader->MultiSectorHeader.Signature) = LFS_SIGNATURE_BAD_USA_ULONG;
+
+ LfsFlushLogPage( Lfcb,
+ LogPageHeader,
+ FirstPartialIo,
+ &LogPageHeaderBcb );
+ }
+
+ LfsNextLogPageOffset( Lfcb,
+ FirstPartialIo,
+ &FirstPartialIo,
+ &Wrapped );
+ }
+
+ //
+ // If we pinned any of the tail pages then write a bad Usa signature
+ // in the headers.
+ //
+
+ if (FirstTailPageBcb != NULL) {
+
+ *((PULONG) &FirstTailPage->MultiSectorHeader.Signature) = LFS_SIGNATURE_BAD_USA_ULONG;
+
+ LfsFlushLogPage( Lfcb,
+ FirstTailPage,
+ FirstTailFileOffset,
+ &FirstTailPageBcb );
+ }
+
+ if (SecondTailPageBcb != NULL) {
+
+ *((PULONG) &SecondTailPage->MultiSectorHeader.Signature) = LFS_SIGNATURE_BAD_USA_ULONG;
+
+ LfsFlushLogPage( Lfcb,
+ SecondTailPage,
+ SecondTailFileOffset,
+ &SecondTailPageBcb );
+ }
+
+ } finally {
+
+ DebugUnwind( LfsFindLastLsn );
+
+ //
+ // Unpin the tail pages is pinned.
+ //
+
+ if (SecondTailPageBcb != NULL) {
+
+ CcUnpinData( SecondTailPageBcb );
+ }
+
+ if (FirstTailPageBcb != NULL) {
+
+ CcUnpinData( FirstTailPageBcb );
+ }
+
+ //
+ // Unpin the log page header if neccessary.
+ //
+
+ if (LogPageHeaderBcb != NULL) {
+
+ CcUnpinData( LogPageHeaderBcb );
+ }
+
+ if (TestPageHeaderBcb != NULL) {
+
+ CcUnpinData( TestPageHeaderBcb );
+ }
+
+ DebugTrace( -1, Dbg, "LfsFindLastLsn: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+BOOLEAN
+LfsCheckSubsequentLogPage (
+ IN PLFCB Lfcb,
+ IN PLFS_RECORD_PAGE_HEADER RecordPageHeader,
+ IN LONGLONG LogFileOffset,
+ IN LONGLONG SequenceNumber
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to check that a particular log page could not
+ have been written after a prior Io transfer. What we are looking for
+ is the start of a transfer which was written after an Io which we
+ we cannot read from during restart. The presence of an additional
+ Io means that we cannot guarantee that we can recover all of the
+ restart data for the disk. This makes the disk unrecoverable.
+
+ We are given the sequence number of the Lsn that would occur on this page
+ (if it is not part of an Log record which spans the end of a file).
+ If we haven't wrapped the file and find an Lsn whose
+ sequence number matches this, then we have an error. If we have
+ wrapped the file, and the sequence number in the Lsn in the
+ first log page is
+ written subsequent to a previous failing Io.
+
+Arguments:
+
+ Lfcb - Log file control block for this log file.
+
+ RecordPageHeader - This is the header of a log page to check.
+
+ LogFileOffset - This is the offset in the log file of this page.
+
+ SequenceNumber - This is the sequence number that this log page should
+ not have. This will be the sequence number for
+ any log records which begin on this page if written
+ after the page that failed.
+
+Return Value:
+
+ BOOLEAN - TRUE if this log page was written after some previous page,
+ FALSE otherwise.
+
+--*/
+
+{
+ BOOLEAN IsSubsequent;
+
+ LSN Lsn;
+ LONGLONG LsnSeqNumber;
+ LONGLONG SeqNumberMinus1;
+ LONGLONG LogPageFileOffset;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsCheckSubsequentLogPage: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "RecordPageHeader -> %08lx\n", RecordPageHeader );
+ DebugTrace( 0, Dbg, "LogFileOffset (Low) -> %08lx\n", LogFileOffset.LowPart );
+ DebugTrace( 0, Dbg, "LogFileOffset (High) -> %08lx\n", LogFileOffset.HighPart );
+ DebugTrace( 0, Dbg, "SequenceNumber (Low) -> %08lx\n", SequenceNumber.LowPart );
+ DebugTrace( 0, Dbg, "SequenceNumber (High) -> %08lx\n", SequenceNumber.HighPart );
+
+ //
+ // If the page header is either 0 or -1 then we say this page was not written
+ // after some previous page.
+ //
+
+ if (*((PULONG) RecordPageHeader->MultiSectorHeader.Signature) == LFS_SIGNATURE_UNINITIALIZED_ULONG
+ || *((PULONG) RecordPageHeader->MultiSectorHeader.Signature) == 0) {
+
+ DebugTrace( -1, Dbg, "LfsCheckSubsequentLogPage: Exit -> %08x\n", FALSE );
+ return FALSE;
+ }
+
+ //
+ // If the last Lsn on the page occurs was
+ // written after the page that caused the original error. Then we
+ // have a fatal error.
+ //
+
+ Lsn = RecordPageHeader->Copy.LastLsn;
+
+ LfsTruncateLsnToLogPage( Lfcb, Lsn, &LogPageFileOffset );
+ LsnSeqNumber = LfsLsnToSeqNumber( Lfcb, Lsn );
+
+ SeqNumberMinus1 = SequenceNumber - 1;
+
+ //
+ // If the sequence number for the Lsn in the page is equal or greater than
+ // Lsn we expect, then this is a subsequent write.
+ //
+
+ if ( LsnSeqNumber >= SequenceNumber ) {
+
+ IsSubsequent = TRUE;
+
+ //
+ // If this page is the start of the file and the sequence number is 1 less
+ // than we expect and the Lsn indicates that we wrapped the file, then it
+ // is also part of a subsequent io.
+ //
+ // The following test checks
+ //
+ // 1 - The sequence number for the Lsn is from the previous pass
+ // through the file.
+ // 2 - We are at the first page in the file.
+ // 3 - The log record didn't begin on the current page.
+ //
+
+ } else if (( LsnSeqNumber == SeqNumberMinus1 )
+ && ( Lfcb->FirstLogPage == LogFileOffset )
+ && ( LogFileOffset != LogPageFileOffset )) {
+
+ IsSubsequent = TRUE;
+
+ } else {
+
+ IsSubsequent = FALSE;
+ }
+
+ DebugTrace( -1, Dbg, "LfsCheckSubsequentLogPage: Exit -> %08x\n", IsSubsequent );
+
+ return IsSubsequent;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsFlushLogPage (
+ IN PLFCB Lfcb,
+ PVOID LogPage,
+ IN LONGLONG FileOffset,
+ OUT PBCB *Bcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to write a single log page to the log file. We will
+ mark it dirty in the cache, unpin it and call our flush routine.
+
+Arguments:
+
+ Lfcb - Log file control block for this log file.
+
+ LogPage - Pointer to the log page in the cache.
+
+ FileOffset - Offset of the page in the stream.
+
+ Bcb - Address of the Bcb pointer for the cache.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Set the page dirty and unpin it.
+ //
+
+ CcSetDirtyPinnedData( *Bcb, NULL );
+ CcUnpinData( *Bcb );
+ *Bcb = NULL;
+
+ //
+ // Now flush the data.
+ //
+
+ CcFlushCache( Lfcb->FileObject->SectionObjectPointer,
+ (PLARGE_INTEGER) &FileOffset,
+ (ULONG) Lfcb->LogPageSize,
+ NULL );
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsRemoveClientFromList (
+ PLFS_CLIENT_RECORD ClientArray,
+ PLFS_CLIENT_RECORD ClientRecord,
+ IN PUSHORT ListHead
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to remove a client record from a client record
+ list in an Lfs restart area.
+
+Arguments:
+
+ ClientArray - Base of client records in restart area.
+
+ ClientRecord - A pointer to the record to add.
+
+ ListHead - A pointer to the beginning of the list. This points to a
+ USHORT which is the value of the first element in the list.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLFS_CLIENT_RECORD TempClientRecord;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsRemoveClientFromList: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Client Array -> %08lx\n", ClientArray );
+ DebugTrace( 0, Dbg, "Client Record -> %08lx\n", ClientRecord );
+ DebugTrace( 0, Dbg, "List Head -> %08lx\n", ListHead );
+
+ //
+ // If this is the first element in the list, then the head of the list
+ // points to the element after this record.
+ //
+
+ if (ClientRecord->PrevClient == LFS_NO_CLIENT) {
+
+ DebugTrace( 0, Dbg, "Element is first element in the list\n", 0 );
+ *ListHead = ClientRecord->NextClient;
+
+ //
+ // Otherwise the previous element points to the next element.
+ //
+
+ } else {
+
+ TempClientRecord = ClientArray + ClientRecord->PrevClient;
+ TempClientRecord->NextClient = ClientRecord->NextClient;
+ }
+
+ //
+ // If this is not the last element in the list, the previous element
+ // becomes the last element.
+ //
+
+ if (ClientRecord->NextClient != LFS_NO_CLIENT) {
+
+ TempClientRecord = ClientArray + ClientRecord->NextClient;
+ TempClientRecord->PrevClient = ClientRecord->PrevClient;
+ }
+
+ DebugTrace( -1, Dbg, "LfsRemoveClientFromList: Exit\n", 0 );
+
+ return;
+}
+
+
+//
+// Local support routine.
+//
+
+VOID
+LfsAddClientToList (
+ IN PLFS_CLIENT_RECORD ClientArray,
+ IN USHORT ClientIndex,
+ IN PUSHORT ListHead
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to add a client record to the start of a list.
+
+Arguments:
+
+ ClientArray - This is the base of the client record.
+
+ ClientIndex - The index for the record to add.
+
+ ListHead - A pointer to the beginning of the list. This points to a
+ USHORT which is the value of the first element in the list.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLFS_CLIENT_RECORD ClientRecord;
+ PLFS_CLIENT_RECORD TempClientRecord;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsAddClientToList: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Client Array -> %08lx\n", ClientArray );
+ DebugTrace( 0, Dbg, "Client Index -> %04x\n", ClientIndex );
+ DebugTrace( 0, Dbg, "List Head -> %08lx\n", ListHead );
+
+ ClientRecord = ClientArray + ClientIndex;
+
+ //
+ // This element will become the first element on the list.
+ //
+
+ ClientRecord->PrevClient = LFS_NO_CLIENT;
+
+ //
+ // The next element for this record is the previous head of the list.
+ //
+
+ ClientRecord->NextClient = *ListHead;
+
+ //
+ // If there is at least one element currently on the list, we point
+ // the first element to this new record.
+ //
+
+ if (*ListHead != LFS_NO_CLIENT) {
+
+ TempClientRecord = ClientArray + *ListHead;
+ TempClientRecord->PrevClient = ClientIndex;
+ }
+
+ //
+ // This index is now the head of the list.
+ //
+
+ *ListHead = ClientIndex;
+
+ DebugTrace( -1, Dbg, "LfsAddClientToList: Exit\n", 0 );
+
+ return;
+}
+
diff --git a/private/ntos/lfs/restart.c b/private/ntos/lfs/restart.c
new file mode 100644
index 000000000..7ee6891cf
--- /dev/null
+++ b/private/ntos/lfs/restart.c
@@ -0,0 +1,651 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ Restart.c
+
+Abstract:
+
+ This module implements the routines which access the client restart
+ areas.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_RESTART)
+
+VOID
+LfsSetBaseLsnPriv (
+ IN PLFCB Lfcb,
+ IN PLFS_CLIENT_RECORD ClientRecord,
+ IN LSN BaseLsn
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsReadRestartArea)
+#pragma alloc_text(PAGE, LfsSetBaseLsn)
+#pragma alloc_text(PAGE, LfsSetBaseLsnPriv)
+#pragma alloc_text(PAGE, LfsWriteRestartArea)
+#endif
+
+
+VOID
+LfsReadRestartArea (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN OUT PULONG BufferLength,
+ IN PVOID Buffer,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the client when he wishes to read his restart
+ area in the log file.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ BufferLength - On entry it is the length of the user buffer. On exit
+ it is the size of the data stored in the buffer.
+
+ Buffer - Pointer to the buffer where the client restart data is to be
+ copied.
+
+ Lsn - This is the Lsn for client restart area.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ BOOLEAN UsaError;
+
+ PLCH Lch;
+
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ PLFS_RECORD_HEADER RecordHeader;
+ PBCB RecordHeaderBcb;
+
+ PLFCB Lfcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsReadRestartArea: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
+
+ RecordHeaderBcb = NULL;
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ ClientRecord = Add2Ptr( Lfcb->ClientArray,
+ Lch->ClientArrayByteOffset,
+ PLFS_CLIENT_RECORD );
+
+ //
+ // If the client doesn't have a restart area, go ahead and exit
+ // now.
+ //
+
+ if ( ClientRecord->ClientRestartLsn.QuadPart == 0 ) { //**** xxEqlZero( ClientRecord->ClientRestartLsn )
+
+ //
+ // We show there is no restart area by returning a length
+ // of zero. We also set the Lsn value to zero so that
+ // we can catch it if the user tries to use the Lsn.
+ //
+
+ DebugTrace( 0, Dbg, "No client restart area exists\n", 0 );
+
+ *BufferLength = 0;
+ *Lsn = LfsZeroLsn;
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Release the Lfcb as we won't be modifying any fields in it.
+ //
+
+ LfsReleaseLfcb( Lfcb );
+
+ //
+ // Pin the log record for this Lsn.
+ //
+
+ LfsPinOrMapLogRecordHeader( Lfcb,
+ ClientRecord->ClientRestartLsn,
+ FALSE,
+ FALSE,
+ &UsaError,
+ &RecordHeader,
+ &RecordHeaderBcb );
+
+ //
+ // If the Lsn values don't match, then the disk is corrupt.
+ //
+
+ if ( ClientRecord->ClientRestartLsn.QuadPart != RecordHeader->ThisLsn.QuadPart ) { //**** xxNeq( ClientRecord->ClientRestartLsn, RecordHeader->ThisLsn )
+
+ ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
+ }
+
+
+ //
+ // Check that the user's buffer is big enough to hold the restart
+ // data. We raise an error status for this error.
+ //
+
+ if (RecordHeader->ClientDataLength > *BufferLength) {
+
+ DebugTrace( 0, Dbg, "Client buffer is too small\n", 0 );
+ *BufferLength = 0;
+ *Lsn = LfsZeroLsn;
+ ExRaiseStatus( STATUS_BUFFER_OVERFLOW );
+ }
+
+
+ //
+ // Use the cache manager to copy the data into the user's buffer.
+ //
+
+ LfsCopyReadLogRecord( Lfcb,
+ RecordHeader,
+ Buffer );
+
+ //
+ // Pass the length and the Lsn of the restart area back to the
+ // caller.
+ //
+
+ *BufferLength = RecordHeader->ClientDataLength;
+ *Lsn = RecordHeader->ThisLsn;
+
+ try_exit: NOTHING;
+ } finally {
+
+ DebugUnwind( LfsReadRestartArea );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ //
+ // Unpin the log record header for the client restart if pinned.
+ //
+
+ if (RecordHeaderBcb != NULL) {
+
+ CcUnpinData( RecordHeaderBcb );
+ }
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
+ DebugTrace( -1, Dbg, "LfsReadRestartArea: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+VOID
+LfsWriteRestartArea (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN ULONG BufferLength,
+ IN PVOID Buffer,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the client to write a restart area to the
+ disk. This routine will not return to the caller until the client
+ restart area and all prior Lsn's have been flushed and the Lfs
+ restart area on the disk has been updated.
+
+ On return, all log records up to and including 'Lsn' have been flushed
+ to the disk.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ BufferLength - On entry it is the length of the user buffer.
+
+ Buffer - Pointer to the buffer where the client restart data resides.
+
+ Lsn - This is the Lsn for this write operation. On input, this will be the
+ new Base Lsn for this client.
+
+ **** This was used to prevent adding an interface change to
+ the Beta release.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ LFS_WRITE_ENTRY WriteEntry;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsWriteRestartArea: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", BufferLength );
+ DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ ClientRecord = Add2Ptr( Lfcb->ClientArray,
+ Lch->ClientArrayByteOffset,
+ PLFS_CLIENT_RECORD );
+
+ //
+ // Go ahead and update the Base Lsn in the client area if the value
+ // given is not zero.
+ //
+
+ if ( Lsn->QuadPart != 0 ) { //**** xxNeqZero( *Lsn )
+
+ LfsSetBaseLsnPriv( Lfcb,
+ ClientRecord,
+ *Lsn );
+ }
+
+ //
+ // Write this restart area as a log record into a log page.
+ //
+
+ WriteEntry.Buffer = Buffer;
+ WriteEntry.ByteLength = BufferLength;
+
+ LfsWriteLogRecordIntoLogPage( Lfcb,
+ Lch,
+ 1,
+ &WriteEntry,
+ LfsClientRestart,
+ NULL,
+ LfsZeroLsn,
+ LfsZeroLsn,
+ 0,
+ TRUE,
+ Lsn );
+
+ //
+ // Update the restart area for the client.
+ //
+
+ ClientRecord->ClientRestartLsn = *Lsn;
+
+ //
+ // Write the restart area to the disk.
+ //
+
+ LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
+
+ } finally {
+
+ DebugUnwind( LfsWriteRestartArea );
+
+ //
+ // Release the log file control block if still held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Log (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsWriteRestartArea: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+VOID
+LfsSetBaseLsn (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN LSN BaseLsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the client to notify the log service of the
+ oldest Lsn he expects to need during restart. The Lfs is allowed to
+ reuse any part of the circular log file which logically precedes
+ this Lsn. A client may only specify a Lsn which follows the previous
+ Lsn specified by this client.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ BaseLsn - This is the oldest Lsn the client may require during a
+ restart.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PLFS_CLIENT_RECORD ClientRecord;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsSetBaseLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Base Lsn (Low) -> %08lx\n", BaseLsn.LowPart );
+ DebugTrace( 0, Dbg, "Base Lsn (High) -> %08lx\n", BaseLsn.HighPart );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ ClientRecord = Add2Ptr( Lfcb->ClientArray,
+ Lch->ClientArrayByteOffset,
+ PLFS_CLIENT_RECORD );
+
+ //
+ // We simply call the worker routine to advance the base lsn.
+ // If we moved forward in the file, we will put our restart area in the
+ // queue.
+ //
+
+ LfsSetBaseLsnPriv( Lfcb,
+ ClientRecord,
+ BaseLsn );
+
+ LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
+
+ } finally {
+
+ DebugUnwind( LfsSetBaseLsn );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( -1, Dbg, "LfsSetBaseLsn: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+LfsSetBaseLsnPriv (
+ IN PLFCB Lfcb,
+ IN PLFS_CLIENT_RECORD ClientRecord,
+ IN LSN BaseLsn
+ )
+
+/*++
+
+Routine Description:
+
+ This worker routine is called internally by Lfs to modify the
+ oldest Lsn a client expects to need during restart. The Lfs is allowed to
+ reuse any part of the circular log file which logically precedes
+ this Lsn. A client may only specify a Lsn which follows the previous
+ Lsn specified by this client.
+
+Arguments:
+
+ Lfcb - Log context block for this file.
+
+ ClientRecord - For the client whose base Lsn is being modified.
+
+ BaseLsn - This is the oldest Lsn the client may require during a
+ restart.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsSetBaseLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Base Lsn (Low) -> %08lx\n", BaseLsn.LowPart );
+ DebugTrace( 0, Dbg, "Base Lsn (High) -> %08lx\n", BaseLsn.HighPart );
+
+ //
+ // We only proceed if the client is moving forward in the file.
+ //
+
+ if ( BaseLsn.QuadPart > Lfcb->OldestLsn.QuadPart ) { //**** xxGtr( BaseLsn, Lfcb->OldestLsn )
+
+ if ( BaseLsn.QuadPart > ClientRecord->OldestLsn.QuadPart ) { //**** xxGtr( BaseLsn, ClientRecord->OldestLsn )
+
+ ClientRecord->OldestLsn = BaseLsn;
+ }
+
+ Lfcb->OldestLsn = BaseLsn;
+
+ //
+ // We walk through all the active clients and find the new
+ // oldest Lsn for the log file.
+ //
+
+ LfsFindOldestClientLsn( Lfcb->RestartArea,
+ Lfcb->ClientArray,
+ &Lfcb->OldestLsn );
+
+ Lfcb->OldestLsnOffset = LfsLsnToFileOffset( Lfcb, Lfcb->OldestLsn );
+ ClearFlag( Lfcb->Flags, LFCB_NO_OLDEST_LSN );
+
+ LfsFindCurrentAvail( Lfcb );
+ }
+
+ DebugTrace( -1, Dbg, "LfsSetBaseLsnPriv: Exit\n", 0 );
+
+ return;
+}
+
diff --git a/private/ntos/lfs/rstrtsup.c b/private/ntos/lfs/rstrtsup.c
new file mode 100644
index 000000000..1b023b237
--- /dev/null
+++ b/private/ntos/lfs/rstrtsup.c
@@ -0,0 +1,268 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ RstrtSup.c
+
+Abstract:
+
+ This module implements support for dealing with the Lfs restart area.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_RESTART_SUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsFindOldestClientLsn)
+#pragma alloc_text(PAGE, LfsWriteLfsRestart)
+#endif
+
+
+VOID
+LfsWriteLfsRestart (
+ IN PLFCB Lfcb,
+ IN ULONG ThisRestartSize,
+ IN BOOLEAN WaitForIo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine puts the Lfs restart area on the queue of operations to
+ write to the file. We do this by allocating a second restart area
+ and attaching it to the Lfcb. We also allocate a buffer control
+ block to use for this write. We look at the WaitForIo boolean to
+ determine whether this thread can perform the I/O. This also indicates
+ whether this thread gives up the Lfcb.
+
+Arguments:
+
+ Lfcb - A pointer to the log file control block for this operation.
+
+ ThisRestartSize - This is the size to use for the restart area.
+
+ WaitForIo - Indicates if this thread is to perform the work.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLBCB NewLbcb = NULL;
+ PLFS_RESTART_AREA NewRestart = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsWriteLfsRestart: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+ DebugTrace( 0, Dbg, "Write Chkdsk -> %04x\n", WriteChkdsk );
+ DebugTrace( 0, Dbg, "Restart Size -> %08lx\n", ThisRestartSize );
+ DebugTrace( 0, Dbg, "WaitForIo -> %08lx\n", WaitForIo );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ PLBCB ActiveLbcb;
+
+ //
+ // We allocate another restart area and
+ // copy the current area into it. Attach the new area to the Lfcb.
+ //
+
+ LfsAllocateRestartArea( &NewRestart, ThisRestartSize );
+
+ //
+ // We allocate a Lbcb structure and update the values to
+ // reflect this restart area.
+ //
+
+ LfsAllocateLbcb( Lfcb, &NewLbcb );
+ SetFlag( NewLbcb->LbcbFlags, LBCB_RESTART_LBCB );
+
+ //
+ // If this is the second page, then add a system page to the offset.
+ //
+
+ if (!Lfcb->InitialRestartArea) {
+
+ NewLbcb->FileOffset = Lfcb->SystemPageSize + NewLbcb->FileOffset;
+ }
+
+ (ULONG)NewLbcb->Length = ThisRestartSize;
+
+ NewLbcb->PageHeader = (PVOID) Lfcb->RestartArea;
+
+ //
+ // Lets put the current lsn in the Lbcb.
+ //
+
+ NewLbcb->LastEndLsn = NewLbcb->LastLsn = Lfcb->NextRestartLsn;
+ Lfcb->NextRestartLsn.QuadPart = 1 + Lfcb->NextRestartLsn.QuadPart;
+
+ //
+ // Copy the existing restart area into the new area.
+ //
+
+ RtlCopyMemory( NewRestart, Lfcb->RestartArea, ThisRestartSize );
+ Lfcb->RestartArea = NewRestart;
+
+ Lfcb->ClientArray = Add2Ptr( NewRestart, Lfcb->ClientArrayOffset, PLFS_CLIENT_RECORD );
+
+ NewRestart = NULL;
+
+ //
+ // Update the Lfcb to indicate that the other restart area
+ // on the disk is to be used.
+ //
+
+ Lfcb->InitialRestartArea = !Lfcb->InitialRestartArea;
+
+ //
+ // Add this Lbcb to the end of the workque and flush to that point.
+ //
+
+ InsertTailList( &Lfcb->LbcbWorkque, &NewLbcb->WorkqueLinks );
+
+ //
+ // If we don't support a packed log file then we need to make
+ // sure that all file records written out ahead of this
+ // restart area make it out to disk and we don't add anything
+ // to this page.
+ //
+
+ if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
+ && !IsListEmpty( &Lfcb->LbcbActive )) {
+
+ ActiveLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ if (FlagOn( ActiveLbcb->LbcbFlags, LBCB_NOT_EMPTY )) {
+
+ RemoveEntryList( &ActiveLbcb->ActiveLinks );
+ ClearFlag( ActiveLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
+ }
+ }
+
+ if (WaitForIo) {
+
+ LfsFlushLbcb( Lfcb, NewLbcb );
+ }
+
+ } finally {
+
+ DebugUnwind( LfsWriteLfsRestart );
+
+ if (NewRestart != NULL) {
+
+ ExFreePool( NewRestart );
+ }
+
+ DebugTrace( -1, Dbg, "LfsWriteLfsRestart: Exit\n", 0 );
+ }
+
+ return;
+}
+
+
+VOID
+LfsFindOldestClientLsn (
+ IN PLFS_RESTART_AREA RestartArea,
+ IN PLFS_CLIENT_RECORD ClientArray,
+ OUT PLSN OldestLsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine walks through the active clients to determine the oldest
+ Lsn the system must maintain.
+
+Arguments:
+
+ RestartArea - This is the Restart Area to examine.
+
+ ClientArray - This is the start of the client data array.
+
+ OldestLsn - We store the oldest Lsn we find in this value. It is
+ initialized with a starting value, we won't return a more recent
+ Lsn.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ USHORT NextClient;
+
+ PLFS_CLIENT_RECORD ClientBlock;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindOldestClientLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "RestartArea -> %08lx\n", RestartArea );
+ DebugTrace( 0, Dbg, "Base Lsn (Low) -> %08lx\n", BaseLsn.LowPart );
+ DebugTrace( 0, Dbg, "Base Lsn (High) -> %08lx\n", BaseLsn.HighPart );
+
+ //
+ // Take the first client off the in use list.
+ //
+
+ NextClient = RestartArea->ClientInUseList;
+
+ //
+ // While there are more clients, compare their oldest Lsn with the
+ // current oldest.
+ //
+
+ while (NextClient != LFS_NO_CLIENT) {
+
+ ClientBlock = ClientArray + NextClient;
+
+ //
+ // We ignore this block if it's oldest Lsn is 0.
+ //
+
+ if (( ClientBlock->OldestLsn.QuadPart != 0 )
+ && ( ClientBlock->OldestLsn.QuadPart < OldestLsn->QuadPart )) {
+
+ *OldestLsn = ClientBlock->OldestLsn;
+ }
+
+ //
+ // Try the next client block.
+ //
+
+ NextClient = ClientBlock->NextClient;
+ }
+
+ DebugTrace( 0, Dbg, "OldestLsn (Low) -> %08lx\n", BaseLsn.LowPart );
+ DebugTrace( 0, Dbg, "OldestLsn (High) -> %08lx\n", BaseLsn.HighPart );
+ DebugTrace( -1, Dbg, "LfsFindOldestClientLsn: Exit\n", 0 );
+
+ return;
+}
+
diff --git a/private/ntos/lfs/sources.inc b/private/ntos/lfs/sources.inc
new file mode 100644
index 000000000..c404373f9
--- /dev/null
+++ b/private/ntos/lfs/sources.inc
@@ -0,0 +1,61 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=lfs
+
+TARGETNAME=lfs
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..;..\..\inc
+
+C_DEFINES=$(C_DEFINES) -D_NTDRIVER_
+
+!IFDEF BUILD_FOR_3_51
+C_DEFINES= $(C_DEFINES) -D_NTIFS_
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+
+
+SOURCES=..\CacheSup.c \
+ ..\LbcbSup.c \
+ ..\LfsData.c \
+ ..\LogPgSup.c \
+ ..\LogRcSup.c \
+ ..\LsnSup.c \
+ ..\QueryLog.c \
+ ..\Registry.c \
+ ..\Restart.c \
+ ..\RstrtSup.c \
+ ..\StrucSup.c \
+ ..\SysInit.c \
+ ..\VerfySup.c \
+ ..\Write.c
+
+PRECOMPILED_INCLUDE=..\lfsprocs.h
+PRECOMPILED_PCH=lfsprocs.pch
+PRECOMPILED_OBJ=lfsprocs.obj
diff --git a/private/ntos/lfs/strucsup.c b/private/ntos/lfs/strucsup.c
new file mode 100644
index 000000000..53b8333e0
--- /dev/null
+++ b/private/ntos/lfs/strucsup.c
@@ -0,0 +1,418 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ StrucSup.c
+
+Abstract:
+
+ This module provides support routines for creation and deletion
+ of Lfs structures.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_STRUC_SUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsAllocateLbcb)
+#pragma alloc_text(PAGE, LfsAllocateLfcb)
+#pragma alloc_text(PAGE, LfsDeallocateLbcb)
+#pragma alloc_text(PAGE, LfsDeallocateLfcb)
+#endif
+
+
+PLFCB
+LfsAllocateLfcb (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initializes a log file control block.
+
+Arguments:
+
+Return Value:
+
+ PLFCB - A pointer to the log file control block just
+ allocated and initialized.
+
+--*/
+
+{
+ PLFCB Lfcb = NULL;
+ ULONG Count;
+ PLBCB NextLbcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsAllocateLfcb: Entered\n", 0 );
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Allocate and zero the structure for the Lfcb.
+ //
+
+ Lfcb = FsRtlAllocatePool( PagedPool, sizeof( LFCB ));
+
+ //
+ // Zero out the structure initially.
+ //
+
+ RtlZeroMemory( Lfcb, sizeof( LFCB ));
+
+ //
+ // Initialize the log file control block.
+ //
+
+ Lfcb->NodeTypeCode = LFS_NTC_LFCB;
+ Lfcb->NodeByteSize = sizeof( LFCB );
+
+ //
+ // Initialize the client links.
+ //
+
+ InitializeListHead( &Lfcb->LchLinks );
+
+ //
+ // Initialize the Lbcb links.
+ //
+
+ InitializeListHead( &Lfcb->LbcbWorkque );
+ InitializeListHead( &Lfcb->LbcbActive );
+
+ //
+ // Initialize and allocate the spare Lbcb queue.
+ //
+
+ InitializeListHead( &Lfcb->SpareLbcbList );
+
+ for (Count = 0; Count < LFCB_RESERVE_LBCB_COUNT; Count++ ) {
+
+ NextLbcb = ExAllocatePoolWithTag( PagedPool, sizeof( LBCB ), ' sfL' );
+
+ if (NextLbcb != NULL) {
+
+ InsertHeadList( &Lfcb->SpareLbcbList, (PLIST_ENTRY) NextLbcb );
+ Lfcb->SpareLbcbCount += 1;
+ }
+ }
+
+ //
+ // Allocate the Lfcb synchronization event.
+ //
+
+ Lfcb->Sync = FsRtlAllocatePool( NonPagedPool, sizeof( LFCB_SYNC ));
+
+ ExInitializeResource( &Lfcb->Sync->Resource );
+
+ //
+ // Initialize the pseudo Lsn for the restart Lbcb's
+ //
+
+ Lfcb->NextRestartLsn = LfsLi1;
+
+ //
+ // Initialize the event to the signalled state.
+ //
+
+ KeInitializeEvent( &Lfcb->Sync->Event, NotificationEvent, TRUE );
+
+ Lfcb->Sync->UserCount = 0;
+
+ } finally {
+
+ DebugUnwind( LfsAllocateFileControlBlock );
+
+ if (AbnormalTermination()
+ && Lfcb != NULL) {
+
+ LfsDeallocateLfcb( Lfcb, TRUE );
+ Lfcb = NULL;
+ }
+
+ DebugTrace( -1, Dbg, "LfsAllocateLfcb: Exit -> %08lx\n", Lfcb );
+ }
+
+ return Lfcb;
+}
+
+
+VOID
+LfsDeallocateLfcb (
+ IN PLFCB Lfcb,
+ IN BOOLEAN CompleteTeardown
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases the resources associated with a log file control
+ block.
+
+Arguments:
+
+ Lfcb - Supplies a pointer to the log file control block.
+
+ CompleteTeardown - Indicates if we are to completely remove this Lfcb.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLBCB NextLbcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsDeallocateLfcb: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
+
+ //
+ // Check that there are no buffer blocks.
+ //
+
+ ASSERT( IsListEmpty( &Lfcb->LbcbActive ));
+ ASSERT( IsListEmpty( &Lfcb->LbcbWorkque ));
+
+ //
+ // Check that we have no clients.
+ //
+
+ ASSERT( IsListEmpty( &Lfcb->LchLinks ));
+
+ //
+ // If there is a restart area we deallocate it.
+ //
+
+ if (Lfcb->RestartArea != NULL) {
+
+ LfsDeallocateRestartArea( Lfcb->RestartArea );
+ }
+
+ //
+ // If there are any of the tail Lbcb's, deallocate them now.
+ //
+
+ if (Lfcb->ActiveTail != NULL) {
+
+ LfsDeallocateLbcb( Lfcb, Lfcb->ActiveTail );
+ Lfcb->ActiveTail = NULL;
+ }
+
+ if (Lfcb->PrevTail != NULL) {
+
+ LfsDeallocateLbcb( Lfcb, Lfcb->PrevTail );
+ Lfcb->PrevTail = NULL;
+ }
+
+ //
+ // Only do the following if we are to remove the Lfcb completely.
+ //
+
+ if (CompleteTeardown) {
+
+ //
+ // If there is a resource structure we deallocate it.
+ //
+
+ if (Lfcb->Sync != NULL) {
+
+ ExDeleteResource( &Lfcb->Sync->Resource );
+
+ ExFreePool( Lfcb->Sync );
+ }
+ }
+
+ //
+ // Deallocate all of the spare Lbcb's.
+ //
+
+ while (!IsListEmpty( &Lfcb->SpareLbcbList )) {
+
+ NextLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink;
+
+ RemoveHeadList( &Lfcb->SpareLbcbList );
+
+ ExFreePool( NextLbcb );
+ }
+
+ //
+ // Discard the Lfcb structure.
+ //
+
+ ExFreePool( Lfcb );
+
+ DebugTrace( -1, Dbg, "LfsDeallocateLfcb: Exit\n", 0 );
+ return;
+}
+
+
+VOID
+LfsAllocateLbcb (
+ IN PLFCB Lfcb,
+ OUT PLBCB *Lbcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will allocate the next Lbcb. If the pool allocation fails
+ we will look at the private queue of Lbcb's.
+
+Arguments:
+
+ Lfcb - Supplies a pointer to the log file control block.
+
+ Lbcb - Address to store the allocated Lbcb.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PLBCB NewLbcb = NULL;
+
+ PAGED_CODE();
+
+ //
+ // If there are enough entries on the look-aside list then get one from
+ // there.
+ //
+
+ if (Lfcb->SpareLbcbCount > LFCB_RESERVE_LBCB_COUNT) {
+
+ NewLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink;
+
+ Lfcb->SpareLbcbCount -= 1;
+ RemoveHeadList( &Lfcb->SpareLbcbList );
+
+ //
+ // Otherwise try to allocate from pool.
+ //
+
+ } else {
+
+ NewLbcb = ExAllocatePoolWithTag( PagedPool, sizeof( LBCB ), ' sfL' );
+ }
+
+ //
+ // If we didn't get one then look at the look-aside list.
+ //
+
+ if (NewLbcb == NULL) {
+
+ if (Lfcb->SpareLbcbCount != 0) {
+
+ NewLbcb = (PLBCB) Lfcb->SpareLbcbList.Flink;
+
+ Lfcb->SpareLbcbCount -= 1;
+ RemoveHeadList( &Lfcb->SpareLbcbList );
+
+ } else {
+
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+ //
+ // Initialize the structure.
+ //
+
+ RtlZeroMemory( NewLbcb, sizeof( LBCB ));
+ NewLbcb->NodeTypeCode = LFS_NTC_LBCB;
+ NewLbcb->NodeByteSize = sizeof( LBCB );
+
+ //
+ // Return it to the user.
+ //
+
+ *Lbcb = NewLbcb;
+ return;
+}
+
+
+VOID
+LfsDeallocateLbcb (
+ IN PLFCB Lfcb,
+ IN PLBCB Lbcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will deallocate the Lbcb. If we need one for the look-aside
+ list we will put it there.
+
+Arguments:
+
+ Lfcb - Supplies a pointer to the log file control block.
+
+ Lbcb - This is the Lbcb to deallocate.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Deallocate any restart area attached to this Lbcb.
+ //
+
+ if (FlagOn( Lbcb->LbcbFlags, LBCB_RESTART_LBCB ) &&
+ (Lbcb->PageHeader != NULL)) {
+
+ LfsDeallocateRestartArea( Lbcb->PageHeader );
+ }
+
+ //
+ // Put this in the Lbcb queue if it is short.
+ //
+
+ if (Lfcb->SpareLbcbCount < LFCB_MAX_LBCB_COUNT) {
+
+ InsertHeadList( &Lfcb->SpareLbcbList, (PLIST_ENTRY) Lbcb );
+ Lfcb->SpareLbcbCount += 1;
+
+ //
+ // Otherwise just free the pool block.
+ //
+
+ } else {
+
+ ExFreePool( Lbcb );
+ }
+
+ return;
+}
diff --git a/private/ntos/lfs/sysinit.c b/private/ntos/lfs/sysinit.c
new file mode 100644
index 000000000..fb4710551
--- /dev/null
+++ b/private/ntos/lfs/sysinit.c
@@ -0,0 +1,130 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ SysInit.c
+
+Abstract:
+
+ This module implements the Log File Service initialization.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_INITIALIZATION)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsInitializeLogFileService)
+#endif
+
+extern USHORT LfsUsaSeqNumber;
+
+
+BOOLEAN
+LfsInitializeLogFileService (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine must be called during system initialization before the
+ first call to logging service, to allow the Log File Service to initialize
+ its global data structures. This routine has no dependencies on other
+ system components being initialized.
+
+ This routine will initialize the global structures used by the logging
+ service and start the Lfs worker thread.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ TRUE if initialization was successful
+
+--*/
+
+{
+ LARGE_INTEGER CurrentTime;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsInitializeLogFileService: Enter\n", 0 );
+
+ //
+ // If the structure has already been initialized then we can return
+ // immediately.
+ //
+
+ if (LfsData.NodeTypeCode == LFS_NTC_DATA
+ && LfsData.NodeByteSize == sizeof( LFS_DATA )
+ && FlagOn( LfsData.Flags, LFS_DATA_INITIALIZED )) {
+
+ DebugTrace( -1, Dbg, "LfsInitializeLogFileService: Exit -> %01x\n", TRUE );
+
+ return TRUE;
+ }
+
+ //
+ // Zero out the structure initially.
+ //
+
+ RtlZeroMemory( &LfsData, sizeof( LFS_DATA ));
+
+ //
+ // Assume the operation will fail.
+ //
+
+ LfsData.Flags = LFS_DATA_INIT_FAILED;
+
+ //
+ // Initialize the global structure for Lfs.
+ //
+
+ LfsData.NodeTypeCode = LFS_NTC_DATA;
+ LfsData.NodeByteSize = sizeof( LFS_DATA );
+
+ InitializeListHead( &LfsData.LfcbLinks );
+
+ //
+ // Allocate and initialize the synchronization objects.
+ //
+
+ KeInitializeEvent( &LfsData.Event,
+ SynchronizationEvent,
+ TRUE );
+
+ //
+ // Initialization has been successful.
+ //
+
+ ClearFlag( LfsData.Flags, LFS_DATA_INIT_FAILED );
+ SetFlag( LfsData.Flags, LFS_DATA_INITIALIZED );
+
+ //
+ // Get a random number as a seed for the Usa sequence numbers. Use the lower
+ // bits of the current time.
+ //
+
+ KeQuerySystemTime( &CurrentTime );
+ LfsUsaSeqNumber = (USHORT) CurrentTime.LowPart;
+
+ DebugTrace( -1, Dbg, "LfsInitializeLogFileService: Exit -> %01x\n", TRUE );
+
+ return TRUE;
+}
diff --git a/private/ntos/lfs/up/makefile b/private/ntos/lfs/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/lfs/up/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/lfs/up/sources b/private/ntos/lfs/up/sources
new file mode 100644
index 000000000..91036a15a
--- /dev/null
+++ b/private/ntos/lfs/up/sources
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+UP_DRIVER=yes
+
+!include ..\sources.inc
diff --git a/private/ntos/lfs/verfysup.c b/private/ntos/lfs/verfysup.c
new file mode 100644
index 000000000..68b1aa723
--- /dev/null
+++ b/private/ntos/lfs/verfysup.c
@@ -0,0 +1,538 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ VerfySup.c
+
+Abstract:
+
+ This module implements consistency checking and structure comparisions
+ on Lfs structures.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+#ifdef BRIANDBG
+BOOLEAN LfsRaiseFull = FALSE;
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsCurrentAvailSpace)
+#pragma alloc_text(PAGE, LfsFindCurrentAvail)
+#pragma alloc_text(PAGE, LfsVerifyLogSpaceAvail)
+#endif
+
+
+VOID
+LfsCurrentAvailSpace (
+ IN PLFCB Lfcb,
+ OUT PLONGLONG CurrentAvailSpace,
+ OUT PULONG CurrentPageBytes
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to determine the available log space in the log file.
+ It returns the total number of free bytes and the number available on the
+ active page if present. The total free bytes will reflect all of the empty
+ pages as well as the number in the active page.
+
+Arguments:
+
+ Lfcb - Lfcb for this log file.
+
+ CurrentAvailSpace - This is the number of bytes available for log
+ records.
+
+ CurrentPageBytes - This is the number of bytes remaining on the
+ current log page.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ *CurrentPageBytes = 0;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsCurrentAvailSpace: Entered\n", 0 );
+
+ //
+ // Get the total number from the Lfcb.
+ //
+
+ *CurrentAvailSpace = Lfcb->CurrentAvailable;
+
+ //
+ // We now look to see if there are any bytes available on the Lbcb in
+ // the active queue. We can add this to the bytes available in the
+ // log pages and also give this back to the caller.
+ //
+
+ if (!IsListEmpty( &Lfcb->LbcbActive )) {
+
+ PLBCB ThisLbcb;
+
+ ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
+ LBCB,
+ ActiveLinks );
+
+ //
+ // If the page is not empty or the page is empty but this is a
+ // restart page then add the remaining bytes on this page.
+ //
+
+ if (FlagOn( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY | LBCB_FLUSH_COPY )) {
+
+ *CurrentPageBytes = (ULONG)Lfcb->LogPageSize - (ULONG)ThisLbcb->BufferOffset;
+
+ *CurrentAvailSpace = *CurrentAvailSpace + *CurrentPageBytes; //**** xxAdd( *CurrentAvailSpace, xxFromUlong( *CurrentPageBytes ));
+ }
+ }
+
+ DebugTrace( +1, Dbg, "LfsCurrentAvailSpace: Exit\n", 0 );
+
+ return;
+}
+
+
+BOOLEAN
+LfsVerifyLogSpaceAvail (
+ IN PLFCB Lfcb,
+ IN PLCH Lch,
+ IN ULONG RemainingLogBytes,
+ IN LONG UndoRequirement,
+ IN BOOLEAN ForceToDisk
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to verify that we may write this log record into the
+ log file. We want to always leave room for each transaction to abort.
+
+ We determine how much space the current log record will take and the
+ worst case for its undo operation. If this space is available we
+ update the corresponding values in the Lfcb and Lch for bookkeeping.
+ Otherwise we raise a status indicating that the log file is full.
+
+ The disk usage is different for the packed and unpacked cases. Make the
+ following adjustments after finding the total available and amount still
+ remaining on the last active page,
+
+ Packed Case:
+
+ Size needed for log record is data size plus header size.
+
+ Undo requirement is the undo data size plus the header size.
+ We have already taken into account the end of the pages
+ except for the current page.
+
+ Add the log record size to the undo requirement to get the
+ log file usage. Compare this number with the actual available
+ space (Available - CommittedUndo). If the space is not
+ available, then raise LOG_FILE_FULL. Must take into account
+ any unused bytes at the end of the current page.
+
+ Unpacked Case:
+
+ Size needed is initially header size plus data size.
+
+ If the log record can't begin on the current page then
+ add the bytes being thrown away to the log record size.
+
+ If the page is being forced to disk then add any remaining
+ bytes on the last page. To the bytes being used.
+
+ Undo requirement is twice the sum of the header size and
+ undo size. We double the requested size since the log
+ record will always fit on a page. This can be a
+ positive or negative number.
+
+ Add the log record usage to the undo usage to get the log file
+ usage. Compare this number with the actual available
+ space (Available - CommittedUndo). If the space is not
+ available, then raise LOG_FILE_FULL.
+
+Arguments:
+
+ Lfcb - Lfcb for this log file.
+
+ Lch - Client handle
+
+ RemainingLogBytes - Number of bytes for the current log record
+
+ UndoRequirement - User's requirement for the undo record.
+
+ ForceToDisk - Indicates if this log record will be flushed to disk.
+
+Return Value:
+
+ BOOLEAN - Advisory, indicates that there is less than 1/4 of the log file available.
+
+--*/
+
+{
+ ULONG CurrentLogRecordSize;
+ ULONG LogRecordStart;
+ ULONG TailBytes;
+
+ LONGLONG CurrentAvailSpace;
+ ULONG CurrentPageBytes;
+
+ LONGLONG LogFileUsage;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsVerifyLogSpaceAvail: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08x\n", Lfcb );
+ DebugTrace( 0, Dbg, "Lch -> %08lx\n", Lch );
+ DebugTrace( 0, Dbg, "RemainingLogBytes -> %08lx\n", RemainingLogBytes );
+ DebugTrace( 0, Dbg, "UndoRequirement -> %08lx\n", UndoRequirement );
+ DebugTrace( 0, Dbg, "ForceToDisk -> %04x\n", ForceToDisk );
+
+ //
+ // Start by collecting the current data on the file.
+ //
+
+ LfsCurrentAvailSpace( Lfcb,
+ &CurrentAvailSpace,
+ &CurrentPageBytes );
+
+ //
+ // We compute the amount of space needed for the current log record by
+ // adding up the following:
+ //
+ // Space at end of current log page which won't be used.
+ // Size of header for log record.
+ // Size of client data in log record.
+ // Size of wasted portion of log page if this is forced to disk.
+ //
+
+ //
+ // Start with the size of the header and the client data.
+ //
+
+ CurrentLogRecordSize = RemainingLogBytes + Lfcb->RecordHeaderLength;
+
+ //
+ // If the log is packed and there are bytes on the current page we need
+ // to take into account any bytes at the end of the page which won't
+ // be used. This will happen if the log record spills into the end of
+ // the log page but doesn't use up the page. If the remaining bytes are
+ // less than a record header size we must throw them away.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ if (CurrentPageBytes != 0
+ && CurrentLogRecordSize < CurrentPageBytes
+ && (CurrentPageBytes - CurrentLogRecordSize) < Lfcb->RecordHeaderLength) {
+
+ CurrentLogRecordSize += (CurrentPageBytes - CurrentLogRecordSize);
+ }
+
+ //
+ // If this is the unpacked case we need to check for bytes being thrown away
+ // on the current page or the last page.
+ //
+
+ } else {
+
+ //
+ // If there is an active Lbcb, we need to add any bytes that
+ // would be thrown away at the end.
+ //
+
+ if (CurrentPageBytes != 0) {
+
+ //
+ // We won't use this log page unless the new log record will fit or
+ // unless this is the first log record in the page.
+ //
+
+ if ((CurrentPageBytes != (ULONG)Lfcb->LogPageDataSize)
+ && (CurrentLogRecordSize > CurrentPageBytes)) {
+
+ CurrentLogRecordSize += CurrentPageBytes;
+
+ //
+ // Remember that we will start this log record at the first
+ // byte in the data portion of a page.
+ //
+
+ LogRecordStart = 0;
+
+ //
+ // Otherwise this will start at the current offset into the
+ // data portion of the log page.
+ //
+
+ } else {
+
+ LogRecordStart = (ULONG)Lfcb->LogPageDataSize - CurrentPageBytes;
+ }
+
+ //
+ // If there was no Lbcb, then we know that we will start at the first
+ // byte of the data portion.
+ //
+
+ } else {
+
+ LogRecordStart = 0;
+ }
+
+ //
+ // We always assume that we will use up the rest of the bytes on the last page
+ // in computing whether the log record will fit in the available space. We
+ // only subtract that space from the available space if this is a force write.
+ //
+
+ if (ForceToDisk) {
+
+ //
+ // We take into account where we start on a log page and continue
+ // to subtract log pages until we know the amount on the last
+ // page.
+ //
+
+ TailBytes = RemainingLogBytes + Lfcb->RecordHeaderLength + LogRecordStart;
+
+ while (TailBytes > (ULONG)Lfcb->LogPageDataSize) {
+
+ TailBytes -= (ULONG)Lfcb->LogPageDataSize;
+ }
+
+ TailBytes = (ULONG)Lfcb->LogPageDataSize - TailBytes;
+
+ CurrentLogRecordSize += TailBytes;
+ }
+ }
+
+ //
+ // We now know the number of bytes needed for the current log page.
+ // Next we compute the number of bytes being reserved by UndoRequirement.
+ // If the UndoRequirement is positive, we will add to the amount reserved
+ // in the log file. If it is negative, we will subtract from the amount
+ // reserved in the log file.
+ //
+
+ //
+ // When we have an actual reserve amount, we convert it to positive
+ // and then reserve twice the space required to hold the data and
+ // its header (up to the maximum of a single page.
+ //
+
+ if (UndoRequirement != 0) {
+
+ if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {
+
+ UndoRequirement *= 2;
+ }
+
+ if (UndoRequirement < 0) {
+
+ UndoRequirement -= (2 * Lfcb->RecordHeaderLength);
+ } else {
+
+ UndoRequirement += (2 * Lfcb->RecordHeaderLength);
+ }
+ }
+
+ //
+ // Now compute the net log file usage. The result may be positive or
+ // negative.
+ //
+
+ LogFileUsage = ((LONG) CurrentLogRecordSize) + UndoRequirement; //**** xxFromLong( ((LONG) CurrentLogRecordSize) + UndoRequirement );
+
+ //
+ // The actual available space is the CurrentAvail minus the reserved
+ // undo value in the Lfcb.
+ //
+
+ CurrentAvailSpace = CurrentAvailSpace - Lfcb->TotalUndoCommitment; //**** xxSub( CurrentAvailSpace, Lfcb->TotalUndoCommitment );
+
+ //
+ // If this log file usage is greater than the available log file space
+ // then we raise a status code.
+ //
+
+#ifdef BRIANDBG
+ if (LfsRaiseFull) {
+
+ LfsRaiseFull = FALSE;
+ DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail: About to raise\n", 0 );
+ ExRaiseStatus( STATUS_LOG_FILE_FULL );
+ }
+#endif
+
+ if (LogFileUsage > CurrentAvailSpace) {
+
+ DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail: About to raise\n", 0 );
+ ExRaiseStatus( STATUS_LOG_FILE_FULL );
+ }
+
+ Lfcb->TotalUndoCommitment = Lfcb->TotalUndoCommitment + UndoRequirement; //**** xxAdd( Lfcb->TotalUndoCommitment, xxFromLong( UndoRequirement ));
+
+ Lch->ClientUndoCommitment = Lch->ClientUndoCommitment + UndoRequirement; //**** xxAdd( Lch->ClientUndoCommitment, xxFromLong( UndoRequirement ));
+
+ DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail: Exit\n", 0 );
+
+ //
+ // Now check if the log file is almost used up.
+ //
+
+ if ((CurrentAvailSpace - LogFileUsage) < (Lfcb->TotalAvailable >> 2)) {
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+VOID
+LfsFindCurrentAvail (
+ IN PLFCB Lfcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to calculate the number of bytes available for log
+ records which are in completely empty log record pages. It ignores any
+ partial pages in the active work queue and ignores any page which is
+ going to be reused.
+
+Arguments:
+
+ Lfcb - Lfcb for this log file.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONGLONG OldestPageOffset;
+ LONGLONG NextFreePageOffset;
+ LONGLONG FreeBytes;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFindCurrentAvail: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Lfcb -> %08x\n", Lfcb );
+
+ //
+ // If there is a last lsn in the restart area then we know
+ // that we will have to compute the free range.
+ //
+
+ if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN )) {
+
+ //
+ // If there is no oldest Lsn then start at the
+ // first page of the file.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_NO_OLDEST_LSN )) {
+
+ OldestPageOffset = Lfcb->FirstLogPage;
+
+ } else {
+
+ LfsTruncateOffsetToLogPage( Lfcb,
+ Lfcb->OldestLsnOffset,
+ &OldestPageOffset );
+ }
+
+ //
+ // We will use the next log page offset to compute the
+ // next free page. If we are going to reuse this page
+ // go to the next page, if we are at the first page then
+ // use the end of the file.
+ //
+
+ if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {
+
+ NextFreePageOffset = Lfcb->NextLogPage + Lfcb->LogPageSize; //**** xxAdd( Lfcb->NextLogPage, Lfcb->LogPageSize );
+
+ } else if ( Lfcb->NextLogPage == Lfcb->FirstLogPage ) { //**** xxEql( Lfcb->NextLogPage, Lfcb->FirstLogPage )
+
+ NextFreePageOffset = Lfcb->FileSize;
+
+ } else {
+
+ NextFreePageOffset = Lfcb->NextLogPage;
+ }
+
+ //
+ // If the two offsets are the same then there is no available space.
+ //
+
+ if ( OldestPageOffset == NextFreePageOffset ) { //**** xxEql( OldestPageOffset, NextFreePageOffset )
+
+ Lfcb->CurrentAvailable = 0;
+
+ } else {
+
+ //
+ // If the free offset follows the oldest offset then subtract
+ // this range from the total available pages.
+ //
+
+ if ( OldestPageOffset < NextFreePageOffset ) { //**** xxLtr( OldestPageOffset, NextFreePageOffset )
+
+ FreeBytes = Lfcb->TotalAvailInPages - ( NextFreePageOffset - OldestPageOffset ); //**** xxSub( Lfcb->TotalAvailInPages, xxSub( NextFreePageOffset, OldestPageOffset ));
+
+ } else {
+
+ FreeBytes = OldestPageOffset - NextFreePageOffset; //**** xxSub( OldestPageOffset, NextFreePageOffset );
+ }
+
+ //
+ // We now have the total bytes in the pages available. We
+ // now have to subtract the size of the page header to get
+ // the total available bytes.
+ //
+ // We will convert the bytes to pages and then multiple
+ // by the data size of each page.
+ //
+
+ FreeBytes = Int64ShrlMod32(((ULONGLONG)(FreeBytes)), Lfcb->LogPageShift);
+
+ Lfcb->CurrentAvailable = FreeBytes * (ULONG)Lfcb->ReservedLogPageSize; //**** xxXMul( FreeBytes, Lfcb->ReservedLogPageSize.LowPart );
+ }
+
+ //
+ // Otherwise the entire file is available.
+ //
+
+ } else {
+
+ Lfcb->CurrentAvailable = Lfcb->MaxCurrentAvail;
+ }
+
+ DebugTrace( -1, Dbg, "LfsFindCurrentAvail: Exit\n", 0 );
+
+ return;
+}
diff --git a/private/ntos/lfs/write.c b/private/ntos/lfs/write.c
new file mode 100644
index 000000000..580bbd10e
--- /dev/null
+++ b/private/ntos/lfs/write.c
@@ -0,0 +1,475 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ Write.c
+
+Abstract:
+
+ This module implements the user routines which write log records into
+ or flush portions of the log file.
+
+Author:
+
+ Brian Andrew [BrianAn] 20-June-1991
+
+Revision History:
+
+--*/
+
+#include "lfsprocs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WRITE)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, LfsFlushToLsn)
+#pragma alloc_text(PAGE, LfsForceWrite)
+#pragma alloc_text(PAGE, LfsWrite)
+#endif
+
+
+BOOLEAN
+LfsWrite (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN ULONG NumberOfWriteEntries,
+ IN PLFS_WRITE_ENTRY WriteEntries,
+ IN LFS_RECORD_TYPE RecordType,
+ IN TRANSACTION_ID *TransactionId OPTIONAL,
+ IN LSN UndoNextLsn,
+ IN LSN PreviousLsn,
+ IN LONG UndoRequirement,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by a client to write a log record to the log file.
+ The log record is lazy written and is not guaranteed to be on the disk
+ until a subsequent LfsForceWrie or LfsWriteRestartArea or until
+ an LfsFlushtoLsn is issued withan Lsn greater-than or equal to the Lsn
+ returned from this service.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ NumberOfWriteEntries - Number of components of the log record.
+
+ WriteEntries - Pointer to an array of write entries.
+
+ RecordType - Lfs defined type for this log record.
+
+ TransactionId - Id value used to group log records by complete transaction.
+
+ UndoNextLsn - Lsn of a previous log record which needs to be undone in
+ the event of a client restart.
+
+ PreviousLsn - Lsn of the immediately previous log record for this client.
+
+ Lsn - Lsn to be associated with this log record.
+
+Return Value:
+
+ BOOLEAN - Advisory, TRUE indicates that less than 1/4 of the log file is
+ available.
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ BOOLEAN LogFileFull = FALSE;
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsWrite: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "NumberOfWriteEntries -> %08lx\n", NumberOfWriteEntries );
+ DebugTrace( 0, Dbg, "WriteEntries -> %08lx\n", WriteEntries );
+ DebugTrace( 0, Dbg, "Record Type -> %08lx\n", RecordType );
+ DebugTrace( 0, Dbg, "Transaction Id -> %08lx\n", TransactionId );
+ DebugTrace( 0, Dbg, "UndoNextLsn (Low) -> %08lx\n", UndoNextLsn.LowPart );
+ DebugTrace( 0, Dbg, "UndoNextLsn (High) -> %08lx\n", UndoNextLsn.HighPart );
+ DebugTrace( 0, Dbg, "PreviousLsn (Low) -> %08lx\n", PreviousLsn.LowPart );
+ DebugTrace( 0, Dbg, "PreviousLsn (High) -> %08lx\n", PreviousLsn.HighPart );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Write the log record.
+ //
+
+ LogFileFull = LfsWriteLogRecordIntoLogPage( Lfcb,
+ Lch,
+ NumberOfWriteEntries,
+ WriteEntries,
+ RecordType,
+ TransactionId,
+ UndoNextLsn,
+ PreviousLsn,
+ UndoRequirement,
+ FALSE,
+ Lsn );
+
+ } finally {
+
+ DebugUnwind( LfsWrite );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsWrite: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return LogFileFull;
+}
+
+
+BOOLEAN
+LfsForceWrite (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN ULONG NumberOfWriteEntries,
+ IN PLFS_WRITE_ENTRY WriteEntries,
+ IN LFS_RECORD_TYPE RecordType,
+ IN TRANSACTION_ID *TransactionId,
+ IN LSN UndoNextLsn,
+ IN LSN PreviousLsn,
+ IN LONG UndoRequirement,
+ OUT PLSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by a client to write a log record to the log file.
+ This is idendical to LfsWrite except that on return the log record is
+ guaranteed to be on disk.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ NumberOfWriteEntries - Number of components of the log record.
+
+ WriteEntries - Pointer to an array of write entries.
+
+ RecordType - Lfs defined type for this log record.
+
+ TransactionId - Id value used to group log records by complete transaction.
+
+ UndoNextLsn - Lsn of a previous log record which needs to be undone in
+ the event of a client restart.
+
+ PreviousLsn - Lsn of the immediately previous log record for this client.
+
+ Lsn - Lsn to be associated with this log record.
+
+Return Value:
+
+ BOOLEAN - Advisory, TRUE indicates that less than 1/4 of the log file is
+ available.
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+ BOOLEAN LogFileFull = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsForceWrite: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "NumberOfWriteEntries -> %08lx\n", NumberOfWriteEntries );
+ DebugTrace( 0, Dbg, "WriteEntries -> %08lx\n", WriteEntries );
+ DebugTrace( 0, Dbg, "Record Type -> %08lx\n", RecordType );
+ DebugTrace( 0, Dbg, "Transaction Id -> %08lx\n", TransactionId );
+ DebugTrace( 0, Dbg, "UndoNextLsn (Low) -> %08lx\n", UndoNextLsn.LowPart );
+ DebugTrace( 0, Dbg, "UndoNextLsn (High) -> %08lx\n", UndoNextLsn.HighPart );
+ DebugTrace( 0, Dbg, "PreviousLsn (Low) -> %08lx\n", PreviousLsn.LowPart );
+ DebugTrace( 0, Dbg, "PreviousLsn (High) -> %08lx\n", PreviousLsn.HighPart );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the Log file has been closed then refuse access.
+ //
+
+ if (Lfcb == NULL) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Write the log record.
+ //
+
+ LogFileFull = LfsWriteLogRecordIntoLogPage( Lfcb,
+ Lch,
+ NumberOfWriteEntries,
+ WriteEntries,
+ RecordType,
+ TransactionId,
+ UndoNextLsn,
+ PreviousLsn,
+ UndoRequirement,
+ TRUE,
+ Lsn );
+
+ //
+ // The call to add this lbcb to the workque is guaranteed to release
+ // the Lfcb if this thread may do the Io.
+ //
+
+ LfsFlushToLsnPriv( Lfcb, *Lsn );
+
+ } finally {
+
+ DebugUnwind( LfsForceWrite );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
+ DebugTrace( -1, Dbg, "LfsForceWrite: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return LogFileFull;
+}
+
+
+VOID
+LfsFlushToLsn (
+ IN LFS_LOG_HANDLE LogHandle,
+ IN LSN Lsn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by a client to insure that all log records
+ to a certain point have been flushed to the file. This is done by
+ checking if the desired Lsn has even been written at all. If so we
+ check if it has been flushed to the file. If not, we simply write
+ the current restart area to the disk.
+
+Arguments:
+
+ LogHandle - Pointer to private Lfs structure used to identify this
+ client.
+
+ Lsn - This is the Lsn that must be on the disk on return from this
+ routine.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ volatile NTSTATUS Status = STATUS_SUCCESS;
+
+ PLCH Lch;
+
+ PLFCB Lfcb;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "LfsFlushToLsn: Entered\n", 0 );
+ DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
+ DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
+ DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart );
+
+ Lch = (PLCH) LogHandle;
+
+ //
+ // Check that the structure is a valid log handle structure.
+ //
+
+ LfsValidateLch( Lch );
+
+ //
+ // Use a try-except to catch errors.
+ //
+
+ try {
+
+ //
+ // Use a try-finally to facilitate cleanup.
+ //
+
+ try {
+
+ //
+ // Acquire the log file control block for this log file.
+ //
+
+ LfsAcquireLch( Lch );
+ Lfcb = Lch->Lfcb;
+
+ //
+ // If the log file has been closed we will assume the Lsn has been flushed.
+ //
+
+ if (Lfcb != NULL) {
+
+ //
+ // Check that the client Id is valid.
+ //
+
+ LfsValidateClientId( Lfcb, Lch );
+
+ //
+ // Call our common routine to perform the work.
+ //
+
+ LfsFlushToLsnPriv( Lfcb, Lsn );
+ }
+
+ } finally {
+
+ DebugUnwind( LfsFlushToLsn );
+
+ //
+ // Release the log file control block if held.
+ //
+
+ LfsReleaseLch( Lch );
+
+ DebugTrace( -1, Dbg, "LfsFlushToLsn: Exit\n", 0 );
+ }
+
+ } except (LfsExceptionFilter( GetExceptionInformation() )) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status != STATUS_SUCCESS) {
+
+ ExRaiseStatus( Status );
+ }
+
+ return;
+}
+
+
+