summaryrefslogtreecommitdiffstats
path: root/private/ntos/lfs/registry.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/lfs/registry.c')
-rw-r--r--private/ntos/lfs/registry.c4234
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;
+}
+