4333 lines
121 KiB
C
4333 lines
121 KiB
C
/*++
|
||
|
||
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)
|
||
#define MODULE_POOL_TAG ('rsfL')
|
||
|
||
PLFCB
|
||
LfsRestartLogFile (
|
||
IN PFILE_OBJECT LogFile,
|
||
IN USHORT MaximumClients,
|
||
IN ULONG LogPageSize OPTIONAL,
|
||
IN LONGLONG FileSize,
|
||
IN OUT PLFS_INFO LfsInfo,
|
||
OUT PLFS_WRITE_DATA WriteData
|
||
);
|
||
|
||
VOID
|
||
LfsNormalizeBasicLogFile (
|
||
IN OUT PLONGLONG FileSize,
|
||
IN OUT PULONG LogPageSize,
|
||
IN OUT PUSHORT LogClients,
|
||
IN BOOLEAN UseDefaultLogPage
|
||
);
|
||
|
||
VOID
|
||
LfsUpdateLfcbFromPgHeader (
|
||
IN PLFCB Lfcb,
|
||
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 ULONG OpenLogCount,
|
||
IN BOOLEAN LogFileWrapped,
|
||
IN BOOLEAN UseMultiplePageIo
|
||
);
|
||
|
||
VOID
|
||
LfsUpdateLfcbFromRestart (
|
||
IN PLFCB Lfcb,
|
||
IN LONGLONG FileSize,
|
||
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
|
||
);
|
||
|
||
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, 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)
|
||
#pragma alloc_text(PAGE, LfsVerifyLogFile)
|
||
#endif
|
||
|
||
|
||
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,
|
||
OUT PLFS_WRITE_DATA WriteData
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
WriteData - Pointer to WRITE_DATA in caller's data structure.
|
||
|
||
Return Value:
|
||
|
||
ULONG - Amount to add to reservation value for header for log record.
|
||
|
||
--*/
|
||
|
||
{
|
||
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 );
|
||
}
|
||
|
||
|
||
//
|
||
// 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,
|
||
WriteData );
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
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;
|
||
BOOLEAN ExitLoop;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "LfsCloseLogFile: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "LogHandle -> %08lx\n", LogHandle );
|
||
|
||
Lch = (PLCH) LogHandle;
|
||
|
||
//
|
||
// Enclose this in a loop. We will loop as long as there are waiters or there is an IO
|
||
// in progress.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Always assume we exit the loop.
|
||
//
|
||
|
||
ExitLoop = TRUE;
|
||
|
||
//
|
||
// 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 there are no waiters or IO in progress before proceeding.
|
||
//
|
||
|
||
if ((Lfcb->Waiters != 0) ||
|
||
(Lfcb->LfsIoState != LfsNoIoInProgress)) {
|
||
|
||
ExitLoop = FALSE;
|
||
Lfcb->Waiters += 1;
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Check that the client Id is valid.
|
||
//
|
||
|
||
LfsValidateClientId( Lfcb, Lch );
|
||
|
||
ClientRecord = Add2Ptr( Lfcb->ClientArray,
|
||
Lch->ClientArrayByteOffset,
|
||
PLFS_CLIENT_RECORD );
|
||
|
||
//
|
||
// Remember if this client wrote a restart area.
|
||
//
|
||
|
||
FlushRestart = (BOOLEAN) ( LfsZeroLsn.QuadPart != ClientRecord->ClientRestartLsn.QuadPart );
|
||
|
||
//
|
||
// 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 elsewhere. Otherwise deallocate it
|
||
// here. We set LBCB_NOT_EMPTY when we first put data into
|
||
// the page and add it to the workqueue.
|
||
//
|
||
|
||
if (!FlagOn( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY )) {
|
||
|
||
ASSERT( NULL == ThisLbcb->WorkqueLinks.Flink );
|
||
|
||
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;
|
||
|
||
//
|
||
// We don't expect to see any workitems queued on readonly volumes.
|
||
//
|
||
|
||
ASSERT(!BooleanFlagOn( Lfcb->Flags, LFCB_READ_ONLY ) ||
|
||
(Links == &Lfcb->LbcbWorkque));
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Flush the new restart area if we need to.
|
||
//
|
||
|
||
if (FlushRestart && (!FlagOn( Lfcb->Flags, LFCB_READ_ONLY ))) {
|
||
|
||
SetFlag( Lfcb->RestartArea->Flags, LFS_CLEAN_SHUTDOWN );
|
||
|
||
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
|
||
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
|
||
}
|
||
#ifdef SUPW_DBG
|
||
if (FlushRestart && FlagOn( Lfcb->Flags, LFCB_READ_ONLY )) {
|
||
DbgPrint("INFO: Not writing restart areas in CloseLog for READONLY lfcb %8lx (ok)\n", Lfcb);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
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();
|
||
}
|
||
|
||
//
|
||
// Test if we want to exit the loop now.
|
||
//
|
||
|
||
if (ExitLoop) { break; }
|
||
|
||
//
|
||
// Wait for the io to complete.
|
||
//
|
||
|
||
KeWaitForSingleObject( &Lfcb->Sync->Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
LfsAcquireLfcb( Lfcb );
|
||
Lfcb->Waiters -= 1;
|
||
LfsReleaseLfcb( Lfcb );
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
ExDeleteResourceLite( &Lch->Sync->Resource );
|
||
ExFreePool( Lch->Sync );
|
||
Lch->Sync = NULL;
|
||
if (Lch->Lfcb) {
|
||
Lch->Lfcb->Sync = NULL;
|
||
}
|
||
|
||
} 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;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
LfsVerifyLogFile (
|
||
IN LFS_LOG_HANDLE LogHandle,
|
||
IN PVOID LogFileHeader,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a client to verify that the volume has not been removed
|
||
from the system and then reattached. We will verify the log file open count on
|
||
disk matches the value in the user's handle.
|
||
|
||
Arguments:
|
||
|
||
LogHandle - Pointer to private Lfs structure used to identify this
|
||
client.
|
||
|
||
LogFileHeader - Pointer to start of log file.
|
||
|
||
Length - Number bytes returned with the read.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the log file has not been altered externally, FALSE if we
|
||
fail for any reason.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN ValidLogFile = FALSE;
|
||
PLCH Lch;
|
||
PLFCB Lfcb;
|
||
|
||
PLFS_RESTART_PAGE_HEADER RestartPage = LogFileHeader;
|
||
|
||
PAGED_CODE();
|
||
|
||
Lch = (PLCH) LogHandle;
|
||
|
||
//
|
||
// Check that the structure is a valid log handle structure.
|
||
//
|
||
|
||
if ((Lch == NULL) ||
|
||
(Lch->NodeTypeCode != LFS_NTC_LCH) ||
|
||
((Lch->Lfcb != NULL) &&
|
||
(Lch->Lfcb->NodeTypeCode != LFS_NTC_LFCB))) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
|
||
LfsReleaseLch( Lch );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check that we have at least one page and that the page is valid.
|
||
//
|
||
|
||
if ((Length >= (ULONG) Lfcb->LogPageSize) &&
|
||
(*((PULONG) RestartPage) == LFS_SIGNATURE_RESTART_PAGE_ULONG) &&
|
||
((RestartPage->RestartOffset + sizeof( LFS_RESTART_AREA )) < (ULONG) Lfcb->LogPageSize) &&
|
||
((Add2Ptr( RestartPage, RestartPage->RestartOffset, PLFS_RESTART_AREA ))->RestartOpenLogCount == Lfcb->CurrentOpenLogCount)) {
|
||
|
||
ValidLogFile = TRUE;
|
||
}
|
||
|
||
LfsReleaseLfcb( Lfcb );
|
||
return ValidLogFile;
|
||
}
|
||
|
||
|
||
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,
|
||
OUT PLFS_WRITE_DATA WriteData
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
WriteData - Pointer to WRITE_DATA in caller's data structure.
|
||
|
||
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 OriginalFileSize = FileSize;
|
||
LONGLONG FirstRestartOffset;
|
||
PLFS_RESTART_PAGE_HEADER FirstRestartPage;
|
||
BOOLEAN FirstChkdskWasRun;
|
||
BOOLEAN FirstValidPage;
|
||
BOOLEAN FirstLogPacked;
|
||
LSN FirstRestartLastLsn;
|
||
|
||
PBCB FirstRestartPageBcb = NULL;
|
||
PBCB SecondRestartPageBcb = NULL;
|
||
|
||
BOOLEAN PackLogFile = TRUE;
|
||
BOOLEAN UseDefaultLogPage = FALSE;
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
BOOLEAN ForceRestartToDisk = FALSE;
|
||
BOOLEAN ClearLogFile = FALSE;
|
||
LONGLONG StartOffsetForClear = 0;
|
||
|
||
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.
|
||
//
|
||
|
||
ASSERT( LfsInfo->LfsClientInfo >= LfsPackLog );
|
||
|
||
|
||
if (LogPageSize == 0) {
|
||
UseDefaultLogPage = TRUE;
|
||
}
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Normalize the values passed in with this call.
|
||
//
|
||
|
||
LfsNormalizeBasicLogFile( &FileSize,
|
||
&LogPageSize,
|
||
&MaximumClients,
|
||
UseDefaultLogPage );
|
||
|
||
//
|
||
// Allocate an Lfcb to use for this file.
|
||
//
|
||
|
||
ThisLfcb = LfsAllocateLfcb( LogPageSize, FileSize );
|
||
|
||
//
|
||
// 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) );
|
||
|
||
if (LfsInfo->ReadOnly) {
|
||
|
||
SetFlag( ThisLfcb->Flags, LFCB_READ_ONLY );
|
||
}
|
||
|
||
//
|
||
// Look for a restart area on the disk.
|
||
//
|
||
|
||
if (!LfsInfo->BadRestart &&
|
||
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;
|
||
}
|
||
|
||
}
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
//
|
||
// Capture the current position out of the page.
|
||
//
|
||
|
||
ThisLfcb->LsnRangeIndex = *(Add2Ptr( FirstRestartPage, 0xe00 - sizeof( ULONG ), PULONG ));
|
||
|
||
if (ThisLfcb->LsnRangeIndex >= 0x20) {
|
||
|
||
ThisLfcb->LsnRangeIndex = 0;
|
||
}
|
||
#endif
|
||
//
|
||
// 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) {
|
||
|
||
//
|
||
// Default version is 1.1.
|
||
//
|
||
|
||
SHORT MajorVersion = 1;
|
||
SHORT MinorVersion = 1;
|
||
|
||
BOOLEAN LogFileWrapped = FALSE;
|
||
BOOLEAN UseMultiplePageIo = FALSE;
|
||
|
||
//
|
||
// We want to do a complete initialization
|
||
//
|
||
|
||
ForceRestartToDisk = TRUE;
|
||
ClearLogFile = TRUE;
|
||
StartOffsetForClear = LogPageSize * 2;
|
||
|
||
//
|
||
// Do some checks based on whether we have a valid log page.
|
||
//
|
||
|
||
if (FirstValidPage) {
|
||
|
||
CurrentTime.LowPart = DiskRestartArea->RestartOpenLogCount;
|
||
|
||
//
|
||
// If the restart page size isn't changing then we want to
|
||
// check how much work we need to do.
|
||
//
|
||
|
||
if (LogPageSize == FirstRestartPage->SystemPageSize) {
|
||
|
||
//
|
||
// 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 = DiskRestartArea->FileSize;
|
||
|
||
} else {
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
KeQuerySystemTime( &CurrentTime );
|
||
}
|
||
|
||
//
|
||
// Initialize our Lfcb for the current log page values.
|
||
//
|
||
|
||
LfsUpdateLfcbFromPgHeader( ThisLfcb,
|
||
LogPageSize,
|
||
MajorVersion,
|
||
MinorVersion,
|
||
PackLogFile );
|
||
|
||
LfsUpdateLfcbFromNoRestart( ThisLfcb,
|
||
FileSize,
|
||
FirstRestartLastLsn,
|
||
MaximumClients,
|
||
CurrentTime.LowPart,
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Now update the caller's WRITE_DATA structure.
|
||
//
|
||
|
||
ThisLfcb->UserWriteData = WriteData;
|
||
WriteData->LfsStructureSize = LogPageSize;
|
||
WriteData->Lfcb = ThisLfcb;
|
||
|
||
//
|
||
// If we are doing a read only mount and we need to
|
||
// write restart areas to disk, we are hosed.
|
||
//
|
||
|
||
if (LfsInfo->ReadOnly && ForceRestartToDisk) {
|
||
|
||
ASSERTMSG("INFO: ReadOnly Mount, Cant write restart. Raising\n", FALSE);
|
||
ExRaiseStatus( STATUS_MEDIA_WRITE_PROTECTED );
|
||
}
|
||
|
||
//
|
||
// If the log page or the system page sizes have changed,
|
||
// we can't use the log file. We must use the system
|
||
// page size instead of the default size if there is not
|
||
// a clean shutdown.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (LogPageSize != FirstRestartPage->SystemPageSize) {
|
||
|
||
FileSize = OriginalFileSize;
|
||
LfsNormalizeBasicLogFile( &FileSize,
|
||
&LogPageSize,
|
||
&MaximumClients,
|
||
(BOOLEAN) (FirstRestartPage->SystemPageSize == LFS_DEFAULT_LOG_PAGE_SIZE) );
|
||
}
|
||
|
||
if ((LogPageSize != FirstRestartPage->SystemPageSize) ||
|
||
(LogPageSize != FirstRestartPage->LogPageSize)) {
|
||
|
||
DebugTrace( 0, Dbg, "Page size mismatch\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,
|
||
LogPageSize,
|
||
FirstRestartPage->MajorVersion,
|
||
FirstRestartPage->MinorVersion,
|
||
FirstLogPacked );
|
||
|
||
LfsUpdateLfcbFromRestart( ThisLfcb,
|
||
FileSize,
|
||
DiskRestartArea,
|
||
FirstRestartPage->RestartOffset );
|
||
|
||
//
|
||
// Now allocate a restart area.
|
||
//
|
||
|
||
LfsAllocateRestartArea( &RestartArea, ThisLfcb->RestartDataSize );
|
||
|
||
//
|
||
// We may need to grow the restart area to allow room for the open
|
||
// log file count.
|
||
//
|
||
|
||
if (ThisLfcb->ClientArrayOffset == FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray )) {
|
||
|
||
RtlCopyMemory( RestartArea, DiskRestartArea, ThisLfcb->RestartAreaSize );
|
||
|
||
} else {
|
||
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
//
|
||
// Copy the start of the restart area over.
|
||
//
|
||
|
||
RtlCopyMemory( RestartArea, DiskRestartArea, ThisLfcb->ClientArrayOffset );
|
||
|
||
//
|
||
// Now copy over the client data to its new location.
|
||
//
|
||
|
||
RtlCopyMemory( RestartArea->LogClientArray,
|
||
Add2Ptr( DiskRestartArea, ThisLfcb->ClientArrayOffset, PVOID ),
|
||
DiskRestartArea->RestartAreaLength - ThisLfcb->ClientArrayOffset );
|
||
|
||
//
|
||
// Update the system open count.
|
||
//
|
||
|
||
KeQuerySystemTime( &CurrentTime );
|
||
|
||
ThisLfcb->CurrentOpenLogCount =
|
||
RestartArea->RestartOpenLogCount = CurrentTime.LowPart;
|
||
|
||
//
|
||
// Now update the numbers in the Lfcb and restart area.
|
||
//
|
||
|
||
ThisLfcb->ClientArrayOffset = FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray );
|
||
ThisLfcb->RestartAreaSize = ThisLfcb->ClientArrayOffset
|
||
+ (sizeof( LFS_CLIENT_RECORD ) * ThisLfcb->LogClients );
|
||
|
||
RestartArea->ClientArrayOffset = ThisLfcb->ClientArrayOffset;
|
||
RestartArea->RestartAreaLength = (USHORT) ThisLfcb->RestartAreaSize;
|
||
}
|
||
|
||
//
|
||
// clear the clean shutdown flag from on disk
|
||
//
|
||
|
||
ClearFlag( RestartArea->Flags, LFS_CLEAN_SHUTDOWN );
|
||
|
||
//
|
||
// Update the log file open count.
|
||
//
|
||
|
||
RestartArea->RestartOpenLogCount += 1;
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// update the caller's WRITE_DATA structure before finding
|
||
// last LSN which may flush a log page.
|
||
//
|
||
|
||
ThisLfcb->UserWriteData = WriteData;
|
||
WriteData->LfsStructureSize = LogPageSize;
|
||
WriteData->Lfcb = ThisLfcb;
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
|
||
if (FirstValidPage) {
|
||
|
||
//
|
||
// Copy the tail of the page to the restart area.
|
||
//
|
||
|
||
RtlCopyMemory( Add2Ptr( ThisLfcb->RestartArea,
|
||
0xe00 - sizeof( ULONG ) - ThisLfcb->RestartDataOffset,
|
||
PVOID ),
|
||
Add2Ptr( FirstRestartPage,
|
||
0xe00 - sizeof( ULONG ),
|
||
PVOID ),
|
||
0x200 + sizeof( ULONG ));
|
||
|
||
DiskRestartArea = Add2Ptr( FirstRestartPage, FirstRestartPage->RestartOffset, PLFS_RESTART_AREA );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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 {
|
||
|
||
|
||
//
|
||
// Need to determine if we are in restart.
|
||
// If so, treat the file as if it is not initialized.
|
||
//
|
||
|
||
if (!UninitializedFile && !LfsInfo->InRestart) {
|
||
|
||
//
|
||
// We didn't find a restart area but the file is not initialized.
|
||
// This is a corrupt disk.
|
||
//
|
||
|
||
DebugTrace( 0, Dbg, "Log file has no restart area\n", 0 );
|
||
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// We need to fail the mount if the media is readonly.
|
||
//
|
||
|
||
if (LfsInfo->ReadOnly) {
|
||
|
||
ExRaiseStatus( STATUS_MEDIA_WRITE_PROTECTED );
|
||
}
|
||
|
||
//
|
||
// We go to a packed system if possible.
|
||
//
|
||
|
||
LfsUpdateLfcbFromPgHeader( ThisLfcb,
|
||
LogPageSize,
|
||
1,
|
||
1,
|
||
PackLogFile );
|
||
|
||
KeQuerySystemTime( &CurrentTime );
|
||
LfsUpdateLfcbFromNoRestart( ThisLfcb,
|
||
FileSize,
|
||
LfsLi0,
|
||
MaximumClients,
|
||
CurrentTime.LowPart,
|
||
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;
|
||
|
||
//
|
||
// update the caller's WRITE_DATA structure.
|
||
//
|
||
|
||
ThisLfcb->UserWriteData = WriteData;
|
||
WriteData->LfsStructureSize = LogPageSize;
|
||
WriteData->Lfcb = ThisLfcb;
|
||
}
|
||
|
||
//
|
||
// Common initialization for all cases
|
||
//
|
||
|
||
//
|
||
// Allocate buffer for the first 4 pages - this includes the 2 restart pages and the 2
|
||
// ping - pong pages
|
||
//
|
||
|
||
ThisLfcb->LogHeadBuffer = LfsAllocatePool( NonPagedPool, (ULONG)(ThisLfcb->LogPageSize * 4) );
|
||
ThisLfcb->LogHeadMdl = IoAllocateMdl( ThisLfcb->LogHeadBuffer,
|
||
(ULONG)(ThisLfcb->LogPageSize * 4),
|
||
FALSE,
|
||
FALSE,
|
||
NULL );
|
||
|
||
if (ThisLfcb->LogHeadMdl == NULL) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
ThisLfcb->LogHeadPartialMdl = IoAllocateMdl( ThisLfcb->LogHeadBuffer,
|
||
(ULONG)(ThisLfcb->LogPageSize),
|
||
FALSE,
|
||
FALSE,
|
||
NULL );
|
||
|
||
if (ThisLfcb->LogHeadPartialMdl == NULL) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
MmBuildMdlForNonPagedPool( ThisLfcb->LogHeadMdl );
|
||
ThisLfcb->ErrorLogPacket = IoAllocateErrorLogEntry( ThisLfcb->FileObject->DeviceObject, ERROR_LOG_MAXIMUM_SIZE );
|
||
|
||
//
|
||
// Put both restart areas in the queue to be flushed but don't
|
||
// force them to disk.
|
||
//
|
||
|
||
if (!LfsInfo->ReadOnly) {
|
||
LfsInitializeLogFilePriv( ThisLfcb,
|
||
ForceRestartToDisk,
|
||
ThisLfcb->RestartDataSize,
|
||
StartOffsetForClear,
|
||
ClearLogFile );
|
||
}
|
||
|
||
} 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->LfsClientInfo < LfsPackLog) {
|
||
|
||
LfsInfo->LfsClientInfo = LfsPackLog;
|
||
}
|
||
|
||
#ifdef LFS_CLUSTER_CHECK
|
||
ThisLfcb->LsnAtMount = ThisLfcb->LastFlushedLsn;
|
||
#endif
|
||
|
||
return ThisLfcb;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
LfsNormalizeBasicLogFile (
|
||
IN OUT PLONGLONG FileSize,
|
||
IN OUT PULONG LogPageSize,
|
||
IN OUT PUSHORT LogClients,
|
||
IN BOOLEAN UseDefaultLogPage
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
UseDefaultLogPage - Indicates if we should use the hardwired log page size or base
|
||
it on the system page size.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG LocalLogPageSize;
|
||
LONGLONG RestartPageBytes;
|
||
LONGLONG LogPages;
|
||
|
||
USHORT MaximumClients;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "LfsNormalizeBasicLogFile: Entered\n", 0 );
|
||
|
||
if (!UseDefaultLogPage) {
|
||
if (*LogPageSize == 0) {
|
||
*LogPageSize = PAGE_SIZE;
|
||
}
|
||
} else {
|
||
*LogPageSize = LFS_DEFAULT_LOG_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 &= ~(*LogPageSize - 1);
|
||
|
||
//
|
||
// There better be at least 2 restart pages.
|
||
//
|
||
|
||
RestartPageBytes = 2 * *LogPageSize;
|
||
|
||
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) ((*LogPageSize / 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 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.
|
||
|
||
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 );
|
||
|
||
//
|
||
// 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 (in log pages). Otherwise it is page 2. Use the PackLog value to determine the
|
||
// Usa values.
|
||
//
|
||
|
||
|
||
if (PackLog) {
|
||
|
||
Lfcb->FirstLogPage = Lfcb->LogPageSize << 2;
|
||
Lfcb->LogRecordUsaOffset = (USHORT) LFS_PACKED_RECORD_PAGE_HEADER_SIZE;
|
||
SetFlag( Lfcb->Flags, LFCB_PACK_LOG );
|
||
|
||
} else {
|
||
|
||
Lfcb->FirstLogPage = Lfcb->LogPageSize << 1;
|
||
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->UsaArraySize = (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 ULONG OpenLogCount,
|
||
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.
|
||
|
||
OpenLogCount - This is the current count of opens for this log file.
|
||
|
||
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->UsaArraySize) );
|
||
|
||
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->UsaArraySize) );
|
||
|
||
Lfcb->RestartDataSize = (ULONG)Lfcb->LogPageSize - Lfcb->RestartDataOffset;
|
||
|
||
Lfcb->LogClients = (USHORT) LogClients;
|
||
|
||
Lfcb->ClientArrayOffset = FIELD_OFFSET( LFS_RESTART_AREA, LogClientArray );
|
||
|
||
Lfcb->RestartAreaSize = Lfcb->ClientArrayOffset
|
||
+ (sizeof( LFS_CLIENT_RECORD ) * Lfcb->LogClients );
|
||
|
||
Lfcb->CurrentOpenLogCount = OpenLogCount;
|
||
|
||
//
|
||
// 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 LONGLONG FileSize,
|
||
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 );
|
||
|
||
//
|
||
// Never set the filesize bigger than the actual filesize
|
||
// on clean shutdowns use the real size - on non clean shutdowns
|
||
// let the filesize shrink but not expand - we're stuck the the file data bits
|
||
// until the next boot when we can adjust it for the new size
|
||
//
|
||
|
||
if (FlagOn( RestartArea->Flags, LFS_CLEAN_SHUTDOWN)) {
|
||
Lfcb->FileSize = FileSize;
|
||
} else {
|
||
Lfcb->FileSize = min( 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->LogPageSize - 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.
|
||
//
|
||
|
||
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_OLD_RESTART_AREA, LogClientArray );
|
||
|
||
Lfcb->RestartAreaSize = Lfcb->ClientArrayOffset
|
||
+ (sizeof( LFS_CLIENT_RECORD ) * Lfcb->LogClients);
|
||
|
||
(ULONG)Lfcb->LogPageDataOffset = QuadAlign( Lfcb->LogRecordUsaOffset + (sizeof( UPDATE_SEQUENCE_NUMBER ) * Lfcb->UsaArraySize) );
|
||
|
||
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 );
|
||
}
|
||
|
||
//
|
||
// Remember the current open log count from the disk. We may be plucking random data out
|
||
// of the client area if the restart area hasn't been grown yet but we will detect that
|
||
// elsewhere.
|
||
//
|
||
|
||
Lfcb->CurrentOpenLogCount = RestartArea->RestartOpenLogCount;
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Use the current value out of the Lfcb to stamp this usage of the log file.
|
||
//
|
||
|
||
RestartArea->RestartOpenLogCount = Lfcb->CurrentOpenLogCount + 1;
|
||
|
||
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;
|
||
}
|
||
|
||
} else if (SecondTailPageBcb != NULL) {
|
||
|
||
CcUnpinData( SecondTailPageBcb );
|
||
SecondTailPageBcb = SecondTailPage = NULL;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
} else if (FirstTailPageBcb != NULL) {
|
||
|
||
CcUnpinData( FirstTailPageBcb );
|
||
FirstTailPageBcb = FirstTailPage = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
#ifdef SUPW_DBG
|
||
if ((TailPage != NULL) && FlagOn( Lfcb->Flags, LFCB_READ_ONLY )) {
|
||
DbgPrint("INFO: TailPage isn't getting written because of READ-ONLY (ok)\n");
|
||
}
|
||
|
||
if (PartialIoCount && FlagOn( Lfcb->Flags, LFCB_READ_ONLY )) {
|
||
DbgPrint("INFO: PartialIoCount = 0x%x, not writing because of READ-ONLY (ok)\n");
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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 (!FlagOn( Lfcb->Flags, LFCB_READ_ONLY )) {
|
||
|
||
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--) {
|
||
|
||
//
|
||
// Make sure the current page is unpinned.
|
||
//
|
||
|
||
if (LogPageHeaderBcb != NULL) {
|
||
|
||
CcUnpinData( LogPageHeaderBcb );
|
||
LogPageHeaderBcb = NULL;
|
||
}
|
||
|
||
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 );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We used to invalidate any tail pages we reused, now we let them
|
||
// be recopied every restart even if we fail a little later
|
||
//
|
||
|
||
} 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();
|
||
|
||
//
|
||
// We'd absolutely hate for this to happen on a read only volume.
|
||
//
|
||
|
||
ASSERT(!(FlagOn( Lfcb->Flags, LFCB_READ_ONLY )));
|
||
|
||
//
|
||
// Set the page dirty and unpin it.
|
||
//
|
||
|
||
CcSetDirtyPinnedData( *Bcb, NULL );
|
||
CcUnpinData( *Bcb );
|
||
*Bcb = NULL;
|
||
|
||
//
|
||
// Now flush the data.
|
||
//
|
||
|
||
Lfcb->UserWriteData->FileOffset = FileOffset;
|
||
Lfcb->UserWriteData->Length = (ULONG) Lfcb->LogPageSize;
|
||
|
||
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;
|
||
}
|
||
|