diff options
Diffstat (limited to 'private/ntos/lfs/registry.c')
-rw-r--r-- | private/ntos/lfs/registry.c | 4234 |
1 files changed, 4234 insertions, 0 deletions
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; +} + |