mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1220 lines
28 KiB
1220 lines
28 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
QueryLog.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the user routines which query for log records
|
|
in a log file.
|
|
|
|
Author:
|
|
|
|
Brian Andrew [BrianAn] 20-June-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lfsprocs.h"
|
|
|
|
//
|
|
// The debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_QUERY)
|
|
|
|
#undef MODULE_POOL_TAG
|
|
#define MODULE_POOL_TAG ('QsfL')
|
|
|
|
VOID
|
|
LfsFindLogRecord (
|
|
IN PLFCB Lfcb,
|
|
IN OUT PLEB Leb,
|
|
IN LSN Lsn,
|
|
OUT PLFS_RECORD_TYPE RecordType,
|
|
OUT TRANSACTION_ID *TransactionId,
|
|
OUT PLSN UndoNextLsn,
|
|
OUT PLSN PreviousLsn,
|
|
OUT PULONG BufferLength,
|
|
OUT PVOID *Buffer
|
|
);
|
|
|
|
BOOLEAN
|
|
LfsFindClientNextLsn (
|
|
IN PLFCB Lfcb,
|
|
IN PLEB Leb,
|
|
OUT PLSN Lsn
|
|
);
|
|
|
|
BOOLEAN
|
|
LfsSearchForwardByClient (
|
|
IN PLFCB Lfcb,
|
|
IN OUT PLEB Leb,
|
|
OUT PLSN Lsn
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, LfsFindClientNextLsn)
|
|
#pragma alloc_text(PAGE, LfsFindLogRecord)
|
|
#pragma alloc_text(PAGE, LfsQueryLastLsn)
|
|
#pragma alloc_text(PAGE, LfsReadLogRecord)
|
|
#pragma alloc_text(PAGE, LfsReadNextLogRecord)
|
|
#pragma alloc_text(PAGE, LfsSearchForwardByClient)
|
|
#pragma alloc_text(PAGE, LfsTerminateLogQuery)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
LfsReadLogRecord (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN LSN FirstLsn,
|
|
IN LFS_CONTEXT_MODE ContextMode,
|
|
OUT PLFS_LOG_CONTEXT Context,
|
|
OUT PLFS_RECORD_TYPE RecordType,
|
|
OUT TRANSACTION_ID *TransactionId,
|
|
OUT PLSN UndoNextLsn,
|
|
OUT PLSN PreviousLsn,
|
|
OUT PULONG BufferLength,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates the query operation. It returns the log record
|
|
in question and a context structure used by the Lfs to return related
|
|
log records. The caller specifies what mode of query to use. He may
|
|
walk backwards through the file by Undo records or all records for
|
|
this client linked through the previous Lsn fields. He may also look
|
|
forwards through the file for all records for the issuing client.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
FirstLsn - Starting record for this query operation.
|
|
|
|
ContextMode - Method of query.
|
|
|
|
Context - Supplies the address to store a pointer to the Lfs created
|
|
context structure.
|
|
|
|
RecordType - Supplies the address to store the record type of this
|
|
log record.
|
|
|
|
TransactionId - Supplies the address to store the transaction Id of
|
|
this log record.
|
|
|
|
UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
|
|
log record.
|
|
|
|
PreviousLsn - Supplies the address to store the Previous Lsn for this
|
|
log record.
|
|
|
|
BufferLength - This is the length of the log data.
|
|
|
|
Buffer - This is a pointer to the start of the log data.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLFS_CLIENT_RECORD ClientRecord;
|
|
|
|
PLCH Lch;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
PLEB Leb = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsReadLogRecord: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "First Lsn (Low) -> %08lx\n", FirstLsn.LowPart );
|
|
DebugTrace( 0, Dbg, "First Lsn (High) -> %08lx\n", FirstLsn.HighPart );
|
|
DebugTrace( 0, Dbg, "Context Mode -> %08lx\n", ContextMode );
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
|
|
//
|
|
// Check that the context mode is valid.
|
|
//
|
|
|
|
switch (ContextMode) {
|
|
|
|
case LfsContextUndoNext :
|
|
case LfsContextPrevious :
|
|
case LfsContextForward :
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugTrace( 0, Dbg, "Invalid context mode -> %08x\n", ContextMode );
|
|
ExRaiseStatus( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
//
|
|
// Use a try-except to catch errors.
|
|
//
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLch( Lch );
|
|
Lfcb = Lch->Lfcb;
|
|
|
|
//
|
|
// If the Log file has been closed then refuse access.
|
|
//
|
|
|
|
if (Lfcb == NULL) {
|
|
|
|
ExRaiseStatus( STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Check that the client Id is valid.
|
|
//
|
|
|
|
LfsValidateClientId( Lfcb, Lch );
|
|
|
|
//
|
|
// Check that the given Lsn is in the legal range for this client.
|
|
//
|
|
|
|
ClientRecord = Add2Ptr( Lfcb->ClientArray,
|
|
Lch->ClientArrayByteOffset,
|
|
PLFS_CLIENT_RECORD );
|
|
|
|
if (!LfsVerifyClientLsnInRange( Lfcb, ClientRecord, FirstLsn )) {
|
|
|
|
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
//
|
|
// We can give up the Lfcb as we know the Lsn is within the file.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
//
|
|
// Allocate and initialize an enumeration structure.
|
|
//
|
|
|
|
LfsAllocateLeb( Lfcb, &Leb );
|
|
|
|
LfsInitializeLeb( Leb,
|
|
Lch->ClientId,
|
|
ContextMode );
|
|
|
|
//
|
|
// Find the log record indicated by the given Lsn.
|
|
//
|
|
|
|
LfsFindLogRecord( Lfcb,
|
|
Leb,
|
|
FirstLsn,
|
|
RecordType,
|
|
TransactionId,
|
|
UndoNextLsn,
|
|
PreviousLsn,
|
|
BufferLength,
|
|
Buffer );
|
|
|
|
//
|
|
// Update the client's arguments.
|
|
//
|
|
|
|
*Context = Leb;
|
|
Leb = NULL;
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsReadLogRecord );
|
|
|
|
//
|
|
// Release the log file control block if held.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
//
|
|
// Deallocate the enumeration block if an error occurred.
|
|
//
|
|
|
|
if (Leb != NULL) {
|
|
|
|
LfsDeallocateLeb( Lfcb, Leb );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "Context -> %08lx\n", *Context );
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
|
|
DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
|
|
DebugTrace( -1, Dbg, "LfsReadLogRecord: Exit\n", 0 );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LfsReadNextLogRecord (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN OUT LFS_LOG_CONTEXT Context,
|
|
OUT PLFS_RECORD_TYPE RecordType,
|
|
OUT TRANSACTION_ID *TransactionId,
|
|
OUT PLSN UndoNextLsn,
|
|
OUT PLSN PreviousLsn,
|
|
OUT PLSN Lsn,
|
|
OUT PULONG BufferLength,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to continue a query operation. The Lfs uses
|
|
private information stored in the enumeration structure to determine the
|
|
next log record to return to the caller.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
Context - Supplies the address to store a pointer to the Lfs created
|
|
enumeration structure.
|
|
|
|
Lsn - Lsn for this log record.
|
|
|
|
RecordType - Supplies the address to store the record type of this
|
|
log record.
|
|
|
|
TransactionId - Supplies the address to store the transaction Id of
|
|
this log record.
|
|
|
|
UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
|
|
log record.
|
|
|
|
PreviousLsn - Supplies the address to store the Previous Lsn for this
|
|
log record.
|
|
|
|
BufferLength - This is the length of the log data.
|
|
|
|
Buffer - This is a pointer to the start of the log data.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLCH Lch;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
PLEB Leb;
|
|
|
|
BOOLEAN FoundNextLsn;
|
|
|
|
BOOLEAN UnwindRememberLebFields;
|
|
PBCB UnwindRecordHeaderBcb;
|
|
PLFS_RECORD_HEADER UnwindRecordHeader;
|
|
PVOID UnwindCurrentLogRecord;
|
|
BOOLEAN UnwindAuxilaryBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsReadNextLogRecord: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "Context -> %08lx\n", Context );
|
|
|
|
FoundNextLsn = FALSE;
|
|
|
|
UnwindRememberLebFields = FALSE;
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
Leb = (PLEB) Context;
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLch( Lch );
|
|
Lfcb = Lch->Lfcb;
|
|
|
|
//
|
|
// If the Log file has been closed then refuse access.
|
|
//
|
|
|
|
if (Lfcb == NULL) {
|
|
|
|
ExRaiseStatus( STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Check that the client Id is valid.
|
|
//
|
|
|
|
LfsValidateClientId( Lfcb, Lch );
|
|
|
|
//
|
|
// Check that the enumeration structure is valid.
|
|
//
|
|
|
|
LfsValidateLeb( Leb, Lch );
|
|
|
|
//
|
|
// Remember any enumeration fields to be overwritten.
|
|
//
|
|
|
|
UnwindRememberLebFields = TRUE;
|
|
|
|
UnwindRecordHeaderBcb = Leb->RecordHeaderBcb;
|
|
Leb->RecordHeaderBcb = NULL;
|
|
|
|
UnwindRecordHeader = Leb->RecordHeader;
|
|
UnwindCurrentLogRecord = Leb->CurrentLogRecord;
|
|
|
|
UnwindAuxilaryBuffer = Leb->AuxilaryBuffer;
|
|
Leb->AuxilaryBuffer = FALSE;
|
|
|
|
//
|
|
// Find the next Lsn number based on the current Lsn number in
|
|
// the enumeration block.
|
|
//
|
|
|
|
if (LfsFindClientNextLsn( Lfcb, Leb, Lsn )) {
|
|
|
|
//
|
|
// We can give up the Lfcb as we know the Lsn is within the file.
|
|
//
|
|
|
|
LfsReleaseLfcb( Lfcb );
|
|
|
|
//
|
|
// Cleanup the enumeration block so we can do the next search.
|
|
//
|
|
|
|
Leb->CurrentLogRecord = NULL;
|
|
Leb->AuxilaryBuffer = FALSE;
|
|
|
|
//
|
|
// Perform the work of getting the log record.
|
|
//
|
|
|
|
LfsFindLogRecord( Lfcb,
|
|
Leb,
|
|
*Lsn,
|
|
RecordType,
|
|
TransactionId,
|
|
UndoNextLsn,
|
|
PreviousLsn,
|
|
BufferLength,
|
|
Buffer );
|
|
|
|
FoundNextLsn = TRUE;
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsReadNextLogRecord );
|
|
|
|
//
|
|
// If we exited due to an error, we have to restore the enumeration
|
|
// block.
|
|
//
|
|
|
|
if (UnwindRememberLebFields) {
|
|
|
|
if (AbnormalTermination()) {
|
|
|
|
//
|
|
// If the record header in the enumeration block is not
|
|
// the same as we started with. Then we unpin that
|
|
// data.
|
|
//
|
|
|
|
if (Leb->RecordHeaderBcb != NULL) {
|
|
|
|
CcUnpinData( Leb->RecordHeaderBcb );
|
|
|
|
}
|
|
|
|
if (Leb->CurrentLogRecord != NULL
|
|
&& Leb->AuxilaryBuffer == TRUE) {
|
|
|
|
LfsFreeSpanningBuffer( Leb->CurrentLogRecord );
|
|
}
|
|
|
|
Leb->RecordHeaderBcb = UnwindRecordHeaderBcb;
|
|
Leb->RecordHeader = UnwindRecordHeader;
|
|
Leb->CurrentLogRecord = UnwindCurrentLogRecord;
|
|
Leb->AuxilaryBuffer = UnwindAuxilaryBuffer;
|
|
|
|
//
|
|
// Otherwise, if we have successfully found the next Lsn,
|
|
// we free up any resources being held from the previous search.
|
|
//
|
|
|
|
} else if (FoundNextLsn ) {
|
|
|
|
if (UnwindRecordHeaderBcb != NULL) {
|
|
|
|
CcUnpinData( UnwindRecordHeaderBcb );
|
|
}
|
|
|
|
if (UnwindCurrentLogRecord != NULL
|
|
&& UnwindAuxilaryBuffer == TRUE) {
|
|
|
|
LfsFreeSpanningBuffer( UnwindCurrentLogRecord );
|
|
}
|
|
|
|
//
|
|
// Restore the Bcb and auxilary buffer field for the final
|
|
// cleanup.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (UnwindRecordHeaderBcb != NULL) {
|
|
|
|
if (Leb->RecordHeaderBcb != NULL) {
|
|
|
|
CcUnpinData( UnwindRecordHeaderBcb );
|
|
|
|
} else {
|
|
|
|
Leb->RecordHeaderBcb = UnwindRecordHeaderBcb;
|
|
}
|
|
}
|
|
|
|
if (UnwindAuxilaryBuffer) {
|
|
|
|
if (Leb->CurrentLogRecord == UnwindCurrentLogRecord) {
|
|
|
|
Leb->AuxilaryBuffer = TRUE;
|
|
|
|
} else {
|
|
|
|
LfsFreeSpanningBuffer( UnwindCurrentLogRecord );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the log file control block if held.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
|
|
DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
|
|
DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
|
|
DebugTrace( -1, Dbg, "LfsReadNextLogRecord: Exit\n", 0 );
|
|
}
|
|
return FoundNextLsn;
|
|
}
|
|
|
|
|
|
VOID
|
|
LfsTerminateLogQuery (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN LFS_LOG_CONTEXT Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a client has completed his query operation
|
|
and wishes to deallocate any resources acquired by the Lfs to
|
|
perform the log file query.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
Context - Supplies the address to store a pointer to the Lfs created
|
|
enumeration structure.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLCH Lch;
|
|
PLEB Leb;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsTerminateLogQuery: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "Context -> %08lx\n", Context );
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
Leb = (PLEB) Context;
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLch( Lch );
|
|
Lfcb = Lch->Lfcb;
|
|
|
|
//
|
|
// If the Log file has been closed then refuse access.
|
|
//
|
|
|
|
if (Lfcb == NULL) {
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Check that the client Id is valid.
|
|
//
|
|
|
|
LfsValidateClientId( Lfcb, Lch );
|
|
|
|
//
|
|
// Check that the enumeration structure is valid.
|
|
//
|
|
|
|
LfsValidateLeb( Leb, Lch );
|
|
|
|
//
|
|
// Deallocate the enumeration block.
|
|
//
|
|
|
|
LfsDeallocateLeb( Lfcb, Leb );
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( LfsTerminateLogQuery );
|
|
|
|
//
|
|
// Release the Lfcb if acquired.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
DebugTrace( -1, Dbg, "LfsTerminateLogQuery: Exit\n", 0 );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
LSN
|
|
LfsQueryLastLsn (
|
|
IN LFS_LOG_HANDLE LogHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return the most recent Lsn for this log record.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
Return Value:
|
|
|
|
LSN - This is the last Lsn assigned in this log file.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLCH Lch;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
LSN LastLsn;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsQueryLastLsn: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLch( Lch );
|
|
Lfcb = Lch->Lfcb;
|
|
|
|
//
|
|
// If the Log file has been closed then refuse access.
|
|
//
|
|
|
|
if (Lfcb == NULL) {
|
|
|
|
ExRaiseStatus( STATUS_ACCESS_DENIED );
|
|
}
|
|
|
|
//
|
|
// Check that the client Id is valid.
|
|
//
|
|
|
|
LfsValidateClientId( Lfcb, Lch );
|
|
|
|
//
|
|
// Copy the last Lsn out of the Lfcb. If the last Lsn is
|
|
// does not correspond to a log record, we will return the
|
|
// zero Lsn.
|
|
//
|
|
|
|
if (FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN )) {
|
|
|
|
LastLsn = LfsZeroLsn;
|
|
|
|
} else {
|
|
|
|
LastLsn = Lfcb->RestartArea->CurrentLsn;
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsQueryLastLsn );
|
|
|
|
//
|
|
// Release the Lfcb if acquired.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
DebugTrace( 0, Dbg, "Last Lsn (Low) -> %08lx\n", LastLsn.LowPart );
|
|
DebugTrace( 0, Dbg, "Last Lsn (High) -> %08lx\n", LastLsn.HighPart );
|
|
DebugTrace( -1, Dbg, "LfsQueryLastLsn: Exit\n", 0 );
|
|
}
|
|
|
|
return LastLsn;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
VOID
|
|
LfsFindLogRecord (
|
|
IN PLFCB Lfcb,
|
|
IN OUT PLEB Leb,
|
|
IN LSN Lsn,
|
|
OUT PLFS_RECORD_TYPE RecordType,
|
|
OUT TRANSACTION_ID *TransactionId,
|
|
OUT PLSN UndoNextLsn,
|
|
OUT PLSN PreviousLsn,
|
|
OUT PULONG BufferLength,
|
|
OUT PVOID *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called recover a log record for a client.
|
|
|
|
Arguments:
|
|
|
|
Lfcb - Log file control block for this file.
|
|
|
|
Leb - Pointer to the enumeration block to update.
|
|
|
|
Lsn - This is the Lsn for the log record.
|
|
|
|
RecordType - Supplies the address to store the record type of this
|
|
log record.
|
|
|
|
TransactionId - Supplies the address to store the transaction Id of
|
|
this log record.
|
|
|
|
UndoNextLsn - Supplies the address to store the Undo Next Lsn for this
|
|
log record.
|
|
|
|
PreviousLsn - Supplies the address to store the Previous Lsn for this
|
|
log record.
|
|
|
|
BufferLength - Pointer to address to store the length in bytes of the
|
|
log record.
|
|
|
|
Buffer - Pointer to store the address where the log record data begins.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR NewBuffer;
|
|
BOOLEAN UsaError;
|
|
LONGLONG LogRecordLength;
|
|
ULONG PageOffset;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsFindLogRecord: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
|
|
DebugTrace( 0, Dbg, "Enumeration Block -> %08lx\n", Leb );
|
|
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
|
|
|
|
NewBuffer = NULL;
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Map the record header for this Lsn if we haven't already.
|
|
//
|
|
|
|
if (Leb->RecordHeader == NULL) {
|
|
|
|
LfsPinOrMapLogRecordHeader( Lfcb,
|
|
Lsn,
|
|
FALSE,
|
|
FALSE,
|
|
&UsaError,
|
|
&Leb->RecordHeader,
|
|
&Leb->RecordHeaderBcb );
|
|
}
|
|
|
|
//
|
|
// We now have the log record desired. If the Lsn in the
|
|
// log record doesn't match the desired Lsn then the disk is
|
|
// corrupt.
|
|
//
|
|
|
|
if ( Lsn.QuadPart != Leb->RecordHeader->ThisLsn.QuadPart ) { //**** xxNeq( Lsn, Leb->RecordHeader->ThisLsn )
|
|
|
|
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
//
|
|
// Check that the length field isn't greater than the total available space
|
|
// in the log file.
|
|
//
|
|
|
|
LogRecordLength = Leb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength; //**** xxFromUlong( Leb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength );
|
|
|
|
if ( LogRecordLength >= Lfcb->TotalAvailable ) { //**** xxGeq( LogRecordLength, Lfcb->TotalAvailable )
|
|
|
|
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
//
|
|
// If the entire log record is on this log page, put a pointer to
|
|
// the log record in the enumeration block.
|
|
//
|
|
|
|
if (!FlagOn( Leb->RecordHeader->Flags, LOG_RECORD_MULTI_PAGE )) {
|
|
|
|
//
|
|
// If client size indicates that we have to go beyond the end of the current
|
|
// page, we raise an error.
|
|
//
|
|
|
|
PageOffset = LfsLsnToPageOffset( Lfcb, Lsn );
|
|
|
|
if ((PageOffset + Leb->RecordHeader->ClientDataLength + Lfcb->RecordHeaderLength)
|
|
> (ULONG)Lfcb->LogPageSize) {
|
|
|
|
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
Leb->CurrentLogRecord = Add2Ptr( Leb->RecordHeader, LFS_RECORD_HEADER_SIZE, PVOID );
|
|
Leb->AuxilaryBuffer = FALSE;
|
|
|
|
//
|
|
// Else we copy the data and remember that we allocated a buffer.
|
|
//
|
|
|
|
} else {
|
|
|
|
NewBuffer = LfsAllocateSpanningBuffer( Lfcb, Leb->RecordHeader->ClientDataLength );
|
|
|
|
//
|
|
// Copy the data into the buffer returned.
|
|
//
|
|
|
|
LfsCopyReadLogRecord( Lfcb,
|
|
Leb->RecordHeader,
|
|
NewBuffer );
|
|
|
|
Leb->CurrentLogRecord = NewBuffer;
|
|
|
|
Leb->AuxilaryBuffer = TRUE;
|
|
|
|
NewBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// We need to update the caller's parameters and the enumeration block.
|
|
//
|
|
|
|
*RecordType = Leb->RecordHeader->RecordType;
|
|
*TransactionId = Leb->RecordHeader->TransactionId;
|
|
|
|
*UndoNextLsn = Leb->RecordHeader->ClientUndoNextLsn;
|
|
*PreviousLsn = Leb->RecordHeader->ClientPreviousLsn;
|
|
|
|
*Buffer = Leb->CurrentLogRecord;
|
|
*BufferLength = Leb->RecordHeader->ClientDataLength;
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsFindLogRecord );
|
|
|
|
//
|
|
// If an error occurred we unpin the record header and the log
|
|
// We also free the buffer if allocated by us.
|
|
//
|
|
|
|
if (NewBuffer != NULL) {
|
|
|
|
LfsFreeSpanningBuffer( NewBuffer );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
|
|
DebugTrace( 0, Dbg, "Buffer -> %08lx\n", *Buffer );
|
|
DebugTrace( -1, Dbg, "LfsFindLogRecord: Exit\n", 0 );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
BOOLEAN
|
|
LfsFindClientNextLsn (
|
|
IN PLFCB Lfcb,
|
|
IN PLEB Leb,
|
|
OUT PLSN Lsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to find the next Lsn to return to a client
|
|
based on the context mode.
|
|
|
|
Arguments:
|
|
|
|
Lfcb - File control block for this log file.
|
|
|
|
Leb - Pointer to the enumeration block for this query operation.
|
|
|
|
Lsn - Pointer to store the Lsn found (if any)
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if an Lsn is found, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
LSN NextLsn;
|
|
BOOLEAN NextLsnFound;
|
|
|
|
PLFS_CLIENT_RECORD ClientRecord;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsFindClientNextLsn: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Leb -> %08lx\n", Leb );
|
|
|
|
ClientRecord = Lfcb->ClientArray + Leb->ClientId.ClientIndex;
|
|
|
|
//
|
|
// The enumeration block has the last Lsn returned. If the user wanted
|
|
// one of the Lsn's in that log header then our job is simple.
|
|
//
|
|
|
|
switch (Leb->ContextMode) {
|
|
|
|
case LfsContextUndoNext:
|
|
case LfsContextPrevious:
|
|
|
|
NextLsn = (Leb->ContextMode == LfsContextUndoNext
|
|
? Leb->RecordHeader->ClientUndoNextLsn
|
|
: Leb->RecordHeader->ClientPreviousLsn);
|
|
|
|
if ( NextLsn.QuadPart == 0 ) { //**** xxEqlZero( NextLsn )
|
|
|
|
NextLsnFound = FALSE;
|
|
|
|
} else if (LfsVerifyClientLsnInRange( Lfcb, ClientRecord, NextLsn )) {
|
|
|
|
BOOLEAN UsaError;
|
|
|
|
LfsPinOrMapLogRecordHeader( Lfcb,
|
|
NextLsn,
|
|
FALSE,
|
|
FALSE,
|
|
&UsaError,
|
|
&Leb->RecordHeader,
|
|
&Leb->RecordHeaderBcb );
|
|
|
|
NextLsnFound = TRUE;
|
|
|
|
} else {
|
|
|
|
NextLsnFound = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case LfsContextForward:
|
|
|
|
//
|
|
// We search forward for the next log record for this client.
|
|
//
|
|
|
|
NextLsnFound = LfsSearchForwardByClient( Lfcb, Leb, &NextLsn );
|
|
break;
|
|
|
|
default:
|
|
|
|
NextLsnFound = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (NextLsnFound) {
|
|
|
|
*Lsn = NextLsn;
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "NextLsn (Low) -> %08lx\n", NextLsn.LowPart );
|
|
DebugTrace( 0, Dbg, "NextLsn (High) -> %08lx\n", NextLsn.HighPart );
|
|
DebugTrace( -1, Dbg, "LfsFindClientNextLsn: Exit -> %08x\n", NextLsnFound );
|
|
|
|
return NextLsnFound;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine.
|
|
//
|
|
|
|
BOOLEAN
|
|
LfsSearchForwardByClient (
|
|
IN PLFCB Lfcb,
|
|
IN OUT PLEB Leb,
|
|
OUT PLSN Lsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to find the next Lsn for this client by searching
|
|
forward in the file, looking for a match.
|
|
|
|
Arguments:
|
|
|
|
Lfcb - Pointer to the file control block for this log file.
|
|
|
|
Leb - Pointer to the enumeration block for this query operation.
|
|
|
|
Lsn - Points to the location to store the next Lsn if found.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if another Lsn for this client is found. FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLFS_RECORD_HEADER CurrentRecordHeader;
|
|
PBCB CurrentBcb;
|
|
|
|
BOOLEAN FoundNextLsn;
|
|
|
|
LSN CurrentLsn;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsSearchForwardByClient: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Leb -> %08lx\n", Leb );
|
|
|
|
//
|
|
// The log record header is in the log enumeration
|
|
// block. We set the current Bcb to NULL so that we don't
|
|
// unpin the log record in the enumeration block until we're sure
|
|
// of success.
|
|
//
|
|
|
|
CurrentRecordHeader = Leb->RecordHeader;
|
|
|
|
CurrentBcb = NULL;
|
|
|
|
//
|
|
// We use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// We assume we won't find another Lsn.
|
|
//
|
|
|
|
FoundNextLsn = FALSE;
|
|
|
|
//
|
|
// Loop as long as another Lsn can be found.
|
|
//
|
|
|
|
while (LfsFindNextLsn( Lfcb, CurrentRecordHeader, &CurrentLsn )) {
|
|
|
|
BOOLEAN UsaError;
|
|
|
|
//
|
|
// Unpin the previous log record header.
|
|
//
|
|
|
|
if (CurrentBcb != NULL) {
|
|
|
|
CcUnpinData( CurrentBcb );
|
|
CurrentBcb = NULL;
|
|
}
|
|
|
|
//
|
|
// Pin the log record header for this Lsn.
|
|
//
|
|
|
|
LfsPinOrMapLogRecordHeader( Lfcb,
|
|
CurrentLsn,
|
|
FALSE,
|
|
FALSE,
|
|
&UsaError,
|
|
&CurrentRecordHeader,
|
|
&CurrentBcb );
|
|
|
|
//
|
|
// If the client values match, then we update the
|
|
// enumeration block and exit.
|
|
//
|
|
|
|
if (LfsClientIdMatch( &CurrentRecordHeader->ClientId,
|
|
&Leb->ClientId )
|
|
&& CurrentRecordHeader->RecordType == LfsClientRecord) {
|
|
|
|
//
|
|
// We remember this one.
|
|
//
|
|
|
|
Leb->RecordHeader = CurrentRecordHeader;
|
|
Leb->RecordHeaderBcb = CurrentBcb;
|
|
|
|
CurrentBcb = NULL;
|
|
FoundNextLsn = TRUE;
|
|
|
|
*Lsn = CurrentLsn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsSearchForwardByClient );
|
|
|
|
//
|
|
// Unpin any log record headers still pinned for no reason.
|
|
//
|
|
|
|
if (CurrentBcb != NULL) {
|
|
|
|
CcUnpinData( CurrentBcb );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "NextLsn (Low) -> %08lx\n", Lsn->LowPart );
|
|
DebugTrace( 0, Dbg, "NextLsn (High) -> %08lx\n", Lsn->HighPart );
|
|
DebugTrace( -1, Dbg, "LfsSearchForwardByClient: Exit -> %08x\n", FoundNextLsn );
|
|
}
|
|
|
|
return FoundNextLsn;
|
|
}
|