/*++ 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; }