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.
839 lines
25 KiB
839 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1992-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xsaction.c
|
|
|
|
Abstract:
|
|
|
|
Provides basic xscational services for cluster logging.
|
|
|
|
Author:
|
|
|
|
Sunita Shrivastava (sunitas) 17-Mar-1997
|
|
|
|
Revision History:
|
|
--*/
|
|
#include "service.h"
|
|
#include "lmp.h"
|
|
|
|
|
|
/****
|
|
@doc EXTERNAL INTERFACES CLUSSVC LM
|
|
****/
|
|
|
|
/****
|
|
@func HXSACTION | LogStartXsaction| Write a start transaction record to
|
|
the log.
|
|
|
|
@parm IN HLOG | hLog | Supplies the handle to the log.
|
|
|
|
@parm IN TRID | TrId | Supplies the transaction id.
|
|
|
|
@parm IN RMID | ResourceId | The resource id that identifies the
|
|
resource manager.
|
|
|
|
@parm IN RMTYPE | ResourceFlags | A dword of flags that the resource
|
|
manager may use to store any data it wants with this record.
|
|
|
|
@rdesc Returns a handle suitable for use in subsequent log calls.
|
|
NUll in case failure. Call GetLastError() to get the error.
|
|
|
|
@xref <f LogCommitXsaction> <f LogAbortXsaction>
|
|
****/
|
|
HXSACTION
|
|
LogStartXsaction(
|
|
IN HLOG hLog,
|
|
IN TRID TrId,
|
|
IN RMID ResourceId,
|
|
IN RMTYPE ResourceFlags
|
|
)
|
|
{
|
|
PLOG pLog;
|
|
DWORD dwError=ERROR_SUCCESS;
|
|
LSN Lsn = NULL_LSN;
|
|
PLOGRECORD pLogRecord;
|
|
DWORD dwNumPages;
|
|
PLOGPAGE pPage;
|
|
DWORD dwTotalSize;
|
|
BOOL bMaxFileSizeReached;
|
|
PXSACTION pXsaction = NULL;
|
|
|
|
|
|
GETLOG(pLog, hLog);
|
|
|
|
//write the record, dont allow resets to happen
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogStartXsaction : Entry TrId=%1!u! RmId=%2!u! RmType = %3!u!\r\n",
|
|
TrId, ResourceId, ResourceFlags);
|
|
|
|
EnterCriticalSection(&pLog->Lock);
|
|
|
|
pXsaction = (PXSACTION)LocalAlloc(LMEM_FIXED, sizeof(XSACTION));
|
|
if (!pXsaction)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto FnExit;
|
|
}
|
|
#if DBG
|
|
{
|
|
DWORD dwOldProtect;
|
|
DWORD Status;
|
|
BOOL VPWorked;
|
|
|
|
VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READWRITE, &dwOldProtect);
|
|
Status = GetLastError();
|
|
CL_ASSERT( VPWorked );
|
|
}
|
|
#endif
|
|
//reset the file
|
|
dwError = LogReset(hLog);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogStartXsaction : LogReset failed\r\n");
|
|
goto FnExit;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
DWORD dwOldProtect;
|
|
DWORD Status;
|
|
BOOL VPWorked;
|
|
|
|
VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READWRITE, &dwOldProtect);
|
|
Status = GetLastError();
|
|
CL_ASSERT( VPWorked );
|
|
}
|
|
#endif
|
|
|
|
|
|
CL_ASSERT(ResourceId > RMAny); // reserved for logger's use
|
|
|
|
dwTotalSize = sizeof(LOGRECORD) + 7 & ~7; // round up to qword size
|
|
|
|
|
|
pPage = LogpAppendPage(pLog, dwTotalSize, &pLogRecord, &bMaxFileSizeReached, &dwNumPages);
|
|
//we just reset the file, if it cant take the new startxsaction
|
|
//record, something is awfully wrong !!!
|
|
if (pPage == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogStartXsaction : LogpAppendPage failed.\r\n");
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
CL_ASSERT(((ULONG_PTR)pLogRecord & 0x7) == 0); // ensure qword alignment
|
|
Lsn = MAKELSN(pPage, pLogRecord);
|
|
|
|
//
|
|
// Fill in log record.
|
|
//
|
|
pLogRecord->Signature = LOGREC_SIG;
|
|
pLogRecord->ResourceManager = ResourceId;
|
|
pLogRecord->Transaction = TrId;
|
|
pLogRecord->XsactionType = TTStartXsaction;
|
|
pLogRecord->Flags = ResourceFlags;
|
|
GetSystemTimeAsFileTime(&pLogRecord->Timestamp);
|
|
pLogRecord->NumPages = dwNumPages;
|
|
pLogRecord->DataSize = 0;
|
|
|
|
pXsaction->XsactionSig = XSACTION_SIG;
|
|
pXsaction->TrId = TrId;
|
|
pXsaction->StartLsn = Lsn;
|
|
pXsaction->RmId = ResourceId;
|
|
|
|
FnExit:
|
|
#if DBG
|
|
{
|
|
DWORD dwOldProtect;
|
|
DWORD Status;
|
|
BOOL VPWorked;
|
|
|
|
VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READONLY, & dwOldProtect);
|
|
Status = GetLastError();
|
|
CL_ASSERT( VPWorked );
|
|
}
|
|
#endif
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogStartXsaction : Exit returning=0x%1!08lx!\r\n",
|
|
Lsn);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
if (pXsaction) {
|
|
LocalFree(pXsaction);
|
|
pXsaction = NULL;
|
|
}
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
LeaveCriticalSection(&pLog->Lock);
|
|
|
|
return((HXSACTION)pXsaction);
|
|
}
|
|
|
|
/****
|
|
@func LSN | LogWriteXsaction| Write a transaction unit record to
|
|
the log.
|
|
|
|
@parm IN HLOG | hLog | Supplies the handle to the log.
|
|
|
|
@parm IN HXSACTION | hXsaction | Supplies the handle to the transaction.
|
|
|
|
@parm IN RMTYPE | ResourceFlags | A dword of flags that the resource
|
|
manager may use to store any data it wants with this record.
|
|
|
|
@parm IN PVOID | LogData | Supplies a pointer to the data to be logged.
|
|
|
|
@parm DWORD | DataSize | Supplies the number of bytes of data pointed to by LogData
|
|
|
|
@rdesc The LSN of the log record that was created. NULL_LSN if something terrible happened.
|
|
GetLastError() will provide the error code.
|
|
|
|
@comm This should use a transaction handle obtained from LogStartXsaction. This
|
|
call is used to write the parts of a transaction to the quorum log.
|
|
|
|
@xref <f LogStartXsaction>
|
|
****/
|
|
LSN
|
|
LogWriteXsaction(
|
|
IN HLOG hLog,
|
|
IN HXSACTION hXsaction,
|
|
IN RMTYPE ResourceFlags,
|
|
IN PVOID pLogData,
|
|
IN DWORD dwDataSize)
|
|
{
|
|
PLOG pLog;
|
|
DWORD dwError=ERROR_SUCCESS;
|
|
LSN Lsn = NULL_LSN;
|
|
PLOGRECORD pLogRecord;
|
|
DWORD dwNumPages;
|
|
PLOGPAGE pPage;
|
|
DWORD dwTotalSize;
|
|
BOOL bMaxFileSizeReached;
|
|
PXSACTION pXsaction = NULL;
|
|
|
|
GETLOG(pLog, hLog);
|
|
|
|
GETXSACTION(pXsaction, hXsaction);
|
|
|
|
//write the record, dont allow resets to happen
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogWriteXsaction : Entry TrId=%1!u! RmId=%2!u! RmType = %3!u!\r\n",
|
|
pXsaction->TrId, pXsaction->RmId, ResourceFlags);
|
|
|
|
#if DBG
|
|
{
|
|
DWORD dwOldProtect;
|
|
DWORD Status;
|
|
BOOL VPWorked;
|
|
|
|
VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READWRITE, &dwOldProtect);
|
|
Status = GetLastError();
|
|
CL_ASSERT( VPWorked );
|
|
}
|
|
#endif
|
|
|
|
|
|
CL_ASSERT(pXsaction->RmId > RMAny); // reserved for logger's use
|
|
|
|
dwTotalSize = sizeof(LOGRECORD) + (dwDataSize+ 7) & ~7; // round up to qword size
|
|
|
|
EnterCriticalSection(&pLog->Lock);
|
|
|
|
pPage = LogpAppendPage(pLog, dwTotalSize, &pLogRecord, &bMaxFileSizeReached, &dwNumPages);
|
|
//we reset the file in logstartxsaction, if it cant take the new startxsaction
|
|
//record, something is awfully wrong !!!
|
|
if (pPage == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
//assert if a complete local xsaction extends the log beyond its max size
|
|
CL_ASSERT( dwError != ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE);
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogWriteXsaction : LogpAppendPage failed.\r\n");
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
CL_ASSERT(((ULONG_PTR)pLogRecord & 0x7) == 0); // ensure qword alignment
|
|
Lsn = MAKELSN(pPage, pLogRecord);
|
|
|
|
//
|
|
// Fill in log record.
|
|
//
|
|
pLogRecord->Signature = LOGREC_SIG;
|
|
pLogRecord->ResourceManager = pXsaction->RmId;
|
|
pLogRecord->Transaction = pXsaction->TrId;
|
|
pLogRecord->XsactionType = TTXsactionUnit;
|
|
pLogRecord->Flags = ResourceFlags;
|
|
GetSystemTimeAsFileTime(&pLogRecord->Timestamp);
|
|
pLogRecord->NumPages = dwNumPages;
|
|
pLogRecord->DataSize = dwDataSize;
|
|
if (dwNumPages < 1)
|
|
CopyMemory(&pLogRecord->Data, pLogData, dwDataSize);
|
|
else
|
|
{
|
|
if (LogpWriteLargeRecordData(pLog, pLogRecord, pLogData, dwDataSize)
|
|
!= ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogWriteXsaction : LogpWriteLargeRecordData failed. Lsn=0x%1!08lx!\r\n",
|
|
Lsn);
|
|
Lsn = NULL_LSN;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
#if DBG
|
|
{
|
|
DWORD dwOldProtect;
|
|
DWORD Status;
|
|
BOOL VPWorked;
|
|
|
|
VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READONLY, & dwOldProtect);
|
|
Status = GetLastError();
|
|
CL_ASSERT( VPWorked );
|
|
}
|
|
#endif
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogWriteXsaction : Exit returning=0x%1!08lx!\r\n",
|
|
Lsn);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
SetLastError(dwError);
|
|
LeaveCriticalSection(&pLog->Lock);
|
|
|
|
return(Lsn);
|
|
}
|
|
|
|
|
|
/****
|
|
@func DWORD | LogCommitXsaction | This writes the commit transaction
|
|
record to the log and flushes it.
|
|
|
|
@parm IN HLOG | hLog | Supplies the handle to the log.
|
|
|
|
@parm IN TRID | TrId | Supplies the transaction id.
|
|
|
|
@parm IN RMID | ResourceId | The resource id that identifies the
|
|
resource manager.
|
|
|
|
@parm IN RMTYPE | ResourceFlags | A dword of flags that the resource
|
|
manager may use to store any data it wants with this record.
|
|
|
|
@comm A commit record is written to the quorum log. The hXsaction handle
|
|
is invalidated at this point and should not be used after this call has
|
|
been made. The commit is record is used to identify committed transactions
|
|
during rollback.
|
|
|
|
@rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
|
|
|
|
@xref <f LogStartXsaction>
|
|
****/
|
|
DWORD WINAPI LogCommitXsaction(
|
|
IN HLOG hLog,
|
|
IN HXSACTION hXsaction,
|
|
IN RMTYPE ResourceFlags)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
LSN Lsn;
|
|
PXSACTION pXsaction;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogCommitXsaction : Entry, hXsaction=0x%1!08lx!\r\n",
|
|
hXsaction);
|
|
|
|
GETXSACTION(pXsaction, hXsaction);
|
|
|
|
Lsn = LogWrite(hLog, pXsaction->TrId, TTCommitXsaction,
|
|
pXsaction->RmId, ResourceFlags, NULL, 0);
|
|
|
|
if (Lsn == NULL_LSN)
|
|
{
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
FnExit:
|
|
//free up the transation memory
|
|
ZeroMemory(pXsaction, sizeof(XSACTION)); // just in case somebody tries to
|
|
LocalFree(pXsaction);
|
|
|
|
if ( dwError == ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE, "[LM] LogCommitXsaction: Exit with success\n");
|
|
} else
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[LM] LogCommitXsaction: Exit, dwError=0x%1!08lx!\n",
|
|
dwError);
|
|
}
|
|
|
|
return(dwError);
|
|
}
|
|
|
|
/****
|
|
@func DWORD | LogAbortXsaction | Marks a given transaction as aborted in the
|
|
quorum log file.
|
|
|
|
@parm IN HLOG | hLog | Supplies the handle to the log.
|
|
|
|
@parm IN HXSACTION | hXsaction | Supplies the handle to the transaction.
|
|
|
|
@parm IN RMTYPE | ResourceFlags | A dword of flags that the resource
|
|
manager may use to store any data it wants with this record.
|
|
|
|
@comm An abort transaction is written to the quorum log. This is used in
|
|
identifying aborted transactions during roll back. The hXsaction
|
|
handle is invalidated at this point and should not be used after this.
|
|
|
|
@rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
|
|
|
|
@xref <f LogStartXsaction> <f LogCommitXsaction>
|
|
****/
|
|
DWORD
|
|
LogAbortXsaction(
|
|
IN HLOG hLog,
|
|
IN HXSACTION hXsaction,
|
|
IN RMTYPE ResourceFlags
|
|
)
|
|
{
|
|
PXSACTION pXsaction;
|
|
LSN Lsn;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogAbortXsaction : Entry, hXsaction=0x%1!08lx!\r\n",
|
|
hXsaction);
|
|
|
|
GETXSACTION(pXsaction, hXsaction);
|
|
|
|
Lsn = LogWrite(hLog, pXsaction->TrId, TTAbortXsaction, pXsaction->RmId,
|
|
ResourceFlags, NULL, 0);
|
|
|
|
if (Lsn == NULL_LSN)
|
|
{
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
FnExit:
|
|
ZeroMemory(pXsaction, sizeof(XSACTION)); // just in case somebody tries to
|
|
LocalFree(pXsaction);
|
|
|
|
if ( dwError == ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE, "[LM] LogAbortXsaction: Exit with success\n");
|
|
} else
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL, "[LM] LogAbortXsaction: Exit, dwError=0x%1!08lx!\n",
|
|
dwError);
|
|
}
|
|
return(dwError);
|
|
}
|
|
|
|
/****
|
|
@func LSN | LogFindXsactionState | This fuctions scans the record and finds
|
|
the state of a given transaction.
|
|
|
|
@parm IN HLOG | hLog | Supplies the identifier of the log.
|
|
|
|
@parm IN LSN | StartXsactionLsn | The LSN of the start transaction record.
|
|
|
|
@parm IN TRID | XsactionId | The transaction id of the transaction.
|
|
|
|
@parm OUT TRSTATE | *pXsactionState | The state of the transaction.
|
|
|
|
@comm Transaction state is set to XsactionCommitted, XsactionAborted or XsactionUnknown.
|
|
depending on whether a commit record, or abort record or no record is found
|
|
for this record in the log.
|
|
|
|
@rdesc ERROR_SUCCESS, else returns the error code if something horrible happens.
|
|
|
|
@xref <f LogScanXsaction>
|
|
****/
|
|
DWORD
|
|
LogFindXsactionState(
|
|
IN HLOG hLog,
|
|
IN LSN StartXsactionLsn,
|
|
IN TRID XsactionId,
|
|
OUT TRSTATE *pXsactionState)
|
|
{
|
|
PLOG pLog;
|
|
PLOGRECORD pRecord, pEopRecord;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
int PageIndex, OldPageIndex;
|
|
RMID Resource;
|
|
TRID TrId;
|
|
TRTYPE TrType;
|
|
LSN Lsn, EopLsn;
|
|
PLOGPAGE pPage = NULL,pLargeBuffer = NULL;
|
|
DWORD dwBytesRead;
|
|
RMTYPE ResourceFlags;
|
|
BOOL bFound = FALSE;
|
|
|
|
GETLOG(pLog, hLog);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogWrite : Entry StartXLsn=0x%1!08lx! StartXId=%2!u!\r\n",
|
|
StartXsactionLsn, XsactionId);
|
|
|
|
EnterCriticalSection(&pLog->Lock);
|
|
|
|
|
|
if (StartXsactionLsn >= pLog->NextLsn)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
//read this record
|
|
dwBytesRead = 0;
|
|
if ((Lsn = LogRead( hLog, StartXsactionLsn, &Resource, &ResourceFlags, &TrId, &TrType,
|
|
NULL, &dwBytesRead)) == NULL_LSN)
|
|
{
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//check the record
|
|
if ((TrType != TTStartXsaction) ||
|
|
(TrId != XsactionId))
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
pPage = (PLOGPAGE)AlignAlloc(SECTOR_SIZE);
|
|
if (pPage == NULL) {
|
|
CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
//Lsn is now set to the next Lsn after the start
|
|
//initialize this to -1 so that the first page is always read
|
|
OldPageIndex = -1;
|
|
while (Lsn < pLog->NextLsn && !bFound)
|
|
{
|
|
//
|
|
// Scan From Next record to find either the commit or abort record
|
|
//
|
|
PageIndex = LSNTOPAGE(Lsn);
|
|
|
|
|
|
if (PageIndex != OldPageIndex)
|
|
{
|
|
//read the page
|
|
pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
|
|
pLog->Overlapped.OffsetHigh = 0;
|
|
|
|
dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
|
|
if (dwError)
|
|
{
|
|
goto FnExit;
|
|
}
|
|
//read was successful, no need to read the page unless the
|
|
//record falls on a different page
|
|
OldPageIndex = PageIndex;
|
|
}
|
|
pRecord = LSNTORECORD(pPage, Lsn);
|
|
|
|
//skip other log management records
|
|
//these are small records by definition
|
|
if (pRecord->ResourceManager < RMAny)
|
|
{
|
|
Lsn = GETNEXTLSN(pRecord, TRUE);
|
|
continue;
|
|
}
|
|
//if the transaction id is the same, check the xsaction type
|
|
if (pRecord->Transaction == XsactionId)
|
|
{
|
|
if ((pRecord->XsactionType == TTCommitXsaction) ||
|
|
(pRecord->XsactionType == TTStartXsaction))
|
|
{
|
|
bFound = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
//handle large records
|
|
if (pRecord->NumPages > 0)
|
|
{
|
|
EopLsn = GETNEXTLSN(pRecord,TRUE);
|
|
|
|
PageIndex = LSNTOPAGE(EopLsn);
|
|
|
|
//read the page
|
|
pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
|
|
pLog->Overlapped.OffsetHigh = 0;
|
|
|
|
dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
|
|
if (dwError)
|
|
{
|
|
goto FnExit;
|
|
}
|
|
OldPageIndex = PageIndex;
|
|
pEopRecord = (PLOGRECORD)((ULONG_PTR) pPage +
|
|
(EopLsn - (pLog->Overlapped).Offset));
|
|
//move to the next page
|
|
Lsn = GETNEXTLSN(pEopRecord, TRUE);
|
|
}
|
|
else
|
|
{
|
|
Lsn = GETNEXTLSN(pRecord, TRUE);
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
if (pRecord->XsactionType == TTCommitXsaction)
|
|
*pXsactionState = XsactionCommitted;
|
|
else
|
|
*pXsactionState = XsactionAborted;
|
|
}
|
|
else
|
|
{
|
|
*pXsactionState = XsactionUnknown;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogFindXsactionState : Exit,State=%1!u!\r\n",
|
|
*pXsactionState);
|
|
|
|
FnExit:
|
|
LeaveCriticalSection(&pLog->Lock);
|
|
if (pPage) AlignFree(pPage);
|
|
return(dwError);
|
|
}
|
|
|
|
|
|
/****
|
|
@func LSN | LogScanXsaction | This fuctions scans the multiple units
|
|
of a transaction.
|
|
|
|
@parm IN HLOG | hLog | Supplies the identifier of the log.
|
|
|
|
@parm IN LSN | StartXsacionLsn | The LSN of the start transaction record.
|
|
|
|
@parm IN TRID | XsactionId | The transaction id of the transaction.
|
|
|
|
@parm IN PLOG_SCANXSACTION_CALLBACK | CallbackRoutine | The routine to call
|
|
for every unit of a transaction.
|
|
|
|
@parm IN PVOID | pContext | The context to be passed to state of the transaction.
|
|
|
|
@comm Stops enumerating the transaction units if the callback function returns
|
|
FALSE, or if the abort or commit record for this transaction is found or
|
|
if the next transacion is found.
|
|
|
|
@rdesc ERROR_SUCCESS if the state is found, else returns the error code.
|
|
|
|
@xref <f LogFindXsactionState>
|
|
****/
|
|
DWORD
|
|
LogScanXsaction(
|
|
IN HLOG hLog,
|
|
IN LSN StartXsactionLsn,
|
|
IN TRID XsactionId,
|
|
IN PLOG_SCANXSACTION_CALLBACK CallbackRoutine,
|
|
IN PVOID pContext)
|
|
{
|
|
PLOG pLog;
|
|
PLOGRECORD pRecord;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
int PageIndex, OldPageIndex;
|
|
RMID Resource;
|
|
TRID TrId;
|
|
TRTYPE TrType;
|
|
LSN Lsn;
|
|
PLOGPAGE pPage = NULL;
|
|
PUCHAR pLargeBuffer;
|
|
DWORD dwBytesRead;
|
|
RMTYPE ResourceFlags;
|
|
|
|
GETLOG(pLog, hLog);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogScanXsaction : Entry StartXLsn=0x%1!08lx! StartXId=%2!u!\r\n",
|
|
StartXsactionLsn, XsactionId);
|
|
|
|
|
|
Lsn = StartXsactionLsn;
|
|
if (Lsn >= pLog->NextLsn)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
//read this record
|
|
dwBytesRead = 0;
|
|
if (LogRead( hLog, Lsn, &Resource, &ResourceFlags, &TrId, &TrType,
|
|
NULL, &dwBytesRead) == NULL_LSN)
|
|
{
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//check the record
|
|
if ((TrType != TTStartXsaction) ||
|
|
(TrId != XsactionId))
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
pPage = (PLOGPAGE)AlignAlloc(SECTOR_SIZE);
|
|
if (pPage == NULL) {
|
|
CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
//initialize this to -1 so that the first page is always read
|
|
OldPageIndex = -1;
|
|
while (Lsn < pLog->NextLsn)
|
|
{
|
|
//
|
|
// Scan From Next record to find either the commit or abort record
|
|
//
|
|
PageIndex = LSNTOPAGE(Lsn);
|
|
|
|
|
|
if (PageIndex != OldPageIndex)
|
|
{
|
|
//read the page
|
|
pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
|
|
pLog->Overlapped.OffsetHigh = 0;
|
|
|
|
dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
|
|
if (dwError)
|
|
{
|
|
goto FnExit;
|
|
}
|
|
//read was successful, no need to read the page unless the
|
|
//record falls on a different page
|
|
OldPageIndex = PageIndex;
|
|
}
|
|
pRecord = LSNTORECORD(pPage, Lsn);
|
|
|
|
//skip other log management records
|
|
if (pRecord->ResourceManager < RMAny)
|
|
{
|
|
Lsn = GETNEXTLSN(pRecord, TRUE);
|
|
continue;
|
|
}
|
|
|
|
//stop if next transaction record is encountered
|
|
if (pRecord->Transaction > XsactionId)
|
|
{
|
|
break;
|
|
}
|
|
//stop when a commit or abort record is found
|
|
if ((pRecord->Transaction == XsactionId) &&
|
|
((pRecord->XsactionType == TTCommitXsaction) ||
|
|
(pRecord->XsactionType == TTAbortXsaction)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//handle large records
|
|
if (pRecord->NumPages > 0)
|
|
{
|
|
//if the transaction id is the same
|
|
if ((pRecord->Transaction == XsactionId) &&
|
|
(pRecord->XsactionType == TTXsactionUnit))
|
|
{
|
|
//read the whole record
|
|
//for a large record you need to read in the entire data
|
|
pLargeBuffer = AlignAlloc(pRecord->NumPages * SECTOR_SIZE);
|
|
if (pLargeBuffer == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY ;
|
|
CL_LOGFAILURE(ERROR_NOT_ENOUGH_MEMORY);
|
|
break;
|
|
}
|
|
//read the pages
|
|
pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
|
|
pLog->Overlapped.OffsetHigh = 0;
|
|
|
|
dwError = LogpRead(pLog, pLargeBuffer, pRecord->NumPages *
|
|
pLog->SectorSize, &dwBytesRead);
|
|
//if it is the last page, then set the new page as the active
|
|
//page
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
CL_LOGFAILURE(dwError);
|
|
AlignFree(pLargeBuffer);
|
|
break;
|
|
}
|
|
pRecord = LSNTORECORD((PLOGPAGE)pLargeBuffer, Lsn);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogScanXsaction::Calling the scancb for Lsn=0x%1!08lx! Trid=%2!u! RecordSize=%3!u!\r\n",
|
|
Lsn, pRecord->Transaction, pRecord->DataSize);
|
|
|
|
//if the callback requests to stop scan
|
|
if (!(*CallbackRoutine)(pContext, Lsn, pRecord->ResourceManager,
|
|
pRecord->Flags, pRecord->Transaction,
|
|
pRecord->Data, pRecord->DataSize))
|
|
{
|
|
AlignFree(pLargeBuffer);
|
|
break;
|
|
}
|
|
}
|
|
//read the last page of the large record and advance
|
|
Lsn = GETNEXTLSN(pRecord,TRUE);
|
|
|
|
AlignFree(pLargeBuffer);
|
|
PageIndex = LSNTOPAGE(Lsn);
|
|
|
|
//read the page
|
|
pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
|
|
pLog->Overlapped.OffsetHigh = 0;
|
|
|
|
dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
|
|
if (dwError)
|
|
{
|
|
goto FnExit;
|
|
}
|
|
OldPageIndex = PageIndex;
|
|
pRecord = (PLOGRECORD)((ULONG_PTR) pPage +
|
|
(Lsn - (pLog->Overlapped).Offset));
|
|
CL_ASSERT(pRecord->ResourceManager == RMPageEnd);
|
|
//move to the next page
|
|
Lsn = GETNEXTLSN(pRecord, TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((pRecord->Transaction == XsactionId) &&
|
|
(pRecord->XsactionType == TTXsactionUnit))
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[LM] LogScanXsaction: Calling the scancb for Lsn=0x%1!08lx! Trid=%2!u! RecordSize=%3!u!\r\n",
|
|
Lsn, pRecord->Transaction, pRecord->DataSize);
|
|
|
|
//call the callback
|
|
if (!(*CallbackRoutine)(pContext, Lsn, pRecord->ResourceManager,
|
|
pRecord->Flags, pRecord->Transaction,
|
|
pRecord->Data, pRecord->DataSize))
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Lsn = GETNEXTLSN(pRecord, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
FnExit:
|
|
if (pPage) AlignFree(pPage);
|
|
return(dwError);
|
|
}
|
|
|