diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/lfs | |
download | NT4.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')
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; +} + + + |