Windows2000/private/ntos/ioe/ioerr.c
2020-09-30 17:12:32 +02:00

1163 lines
36 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
ioerr.c
Abstract:
This module contains the code for the worker routines
Author:
Michael Tsang (MikeTs) 2-Sep-1998
Environment:
Kernel mode
--*/
#include "pch.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IoepHandleErrCase)
#pragma alloc_text(PAGE, IoepFindErrHandler)
#pragma alloc_text(PAGE, IoepMatchErrIDPath)
#pragma alloc_text(PAGE, IoepGetErrMessage)
#pragma alloc_text(PAGE, IoepCatMsgArg)
#pragma alloc_text(PAGE, IoepUnicodeStringCatN)
#pragma alloc_text(PAGE, IoepGetErrCaseDB)
#endif
HANDLE
IoepInitErrLog(
IN ULONG KeyType,
IN PVOID Key,
IN ULONG ulFlags
)
/*++
Routine Description:
This routine initializes an error logging session with the specified key.
Arguments:
KeyType - specifies the key type
Key - points to the key
ulFlags - log session flags
Return Value:
Success - returns the newly created error log handle.
Failure - returns NULL.
--*/
{
PROCNAME("IoepInitErrLog");
PERRLOG ErrLog;
ASSERT(Key != NULL);
ASSERT(ulFlags & ~LOGF_INITMASK == 0);
ENTER(2, ("(KeyType=%x,Key=%p,ulFlags=%x)\n", KeyType, Key, ulFlags));
ErrLog = ExAllocatePoolWithTag(NonPagedPool,
sizeof(ERRLOG),
IOETAG_ERRLOG);
if (ErrLog != NULL) {
PERRTHREAD ErrThread;
ErrThread = IoepFindErrThread(KeyType, Key);
if (ErrThread == NULL) {
ErrThread = IoepNewErrThread(KeyType, Key);
}
if (ErrThread != NULL) {
ErrLog->Signature = SIG_ERRLOG;
ErrLog->ulFlags = ulFlags & LOGF_INITMASK;
ErrLog->ErrThread = ErrThread;
ErrLog->ErrStack.Next = NULL;
ErrLog->ErrInfo = NULL;
ExInterlockedInsertHeadList(&ErrThread->ErrLogListHead,
&ErrLog->list,
&IoepErrListLock);
} else {
ExFreePool(ErrLog);
ErrLog = NULL;
}
} else {
DBGPRINT(("failed to allocate new error log\n"));
}
EXIT(2, ("=%p\n", ErrLog));
return ErrLog;
} //IoepInitErrLog
PERRTHREAD
IoepFindErrThread(
IN ULONG KeyType,
IN PVOID Key
)
/*++
Routine Description:
This routine finds the error thread keyed by the given Key.
Arguments:
KeyType - specifies the type of key.
Key - points to the key that is used to locate the logging session.
Return Value:
Success - returns the error thread handle found.
Failure - returns NULL.
--*/
{
PROCNAME("IoepFindErrThread");
PERRTHREAD ErrThread = NULL;
PLIST_ENTRY list;
KIRQL Irql;
ENTER(2, ("(KeyType=%x,Key=%p)\n", KeyType, Key));
ExAcquireSpinLock(&IoepErrListLock, &Irql);
for (list = IoepErrThreadListHead.Flink;
list != &IoepErrThreadListHead;
list = list->Flink) {
ErrThread = CONTAINING_RECORD(list, ERRTHREAD, list);
if (ErrThread->ThreadKeyType == KeyType) {
switch (KeyType) {
case THREADKEY_IRP:
if ((PIRP)Key == ErrThread->ThreadKey.IrpKey.Irp) {
break;
} else {
PIO_STACK_LOCATION IrpSp;
IrpSp = IoGetCurrentIrpStackLocation((PIRP)Key);
if ((IrpSp->MajorFunction ==
ErrThread->ThreadKey.IrpKey.MajorFunction) &&
(IrpSp->MinorFunction ==
ErrThread->ThreadKey.IrpKey.MinorFunction) &&
RtlEqualMemory(&IrpSp->Parameters.Others,
ErrThread->ThreadKey.IrpKey.Arguments,
sizeof(PVOID) * 4) &&
(IopDbgGetLowestDevice(IrpSp->DeviceObject) ==
ErrThread->ThreadKey.IrpKey.TargetDevice)) {
break;
}
}
break;
case THREADKEY_THREADID:
if ((PKTHREAD)Key == ErrThread->ThreadKey.ThIDKey.ThreadID) {
break;
}
break;
}
}
ErrThread = NULL;
}
ExReleaseSpinLock(&IoepErrListLock, Irql);
EXIT(2, ("=%p\n", ErrThread));
return ErrThread;
} //IoepFindErrThread
PERRTHREAD
IoepNewErrThread(
IN ULONG KeyType,
IN PVOID Key
)
/*++
Routine Description:
This routine creates a new error thread with a given key.
Arguments:
KeyType - specifies the type of key.
Key - points to the key that is used to create the new logging session.
Return Value:
Success - returns the error thread handle created.
Failure - returns NULL.
--*/
{
PROCNAME("IoepNewErrThread");
PERRTHREAD ErrThread;
ENTER(2, ("(KeyType=%x,Key=%p)\n", KeyType, Key));
ErrThread = ExAllocatePoolWithTag(NonPagedPool,
sizeof(ERRTHREAD),
IOETAG_ERRTHREAD);
if (ErrThread != NULL) {
PIO_STACK_LOCATION IrpSp;
InitializeListHead(&ErrThread->ErrLogListHead);
ErrThread->ThreadKeyType = KeyType;
switch (KeyType) {
case THREADKEY_IRP:
IrpSp = IoGetCurrentIrpStackLocation((PIRP)Key);
ErrThread->ThreadKey.IrpKey.Irp = (PIRP)Key;
ErrThread->ThreadKey.IrpKey.MajorFunction =
IrpSp->MajorFunction;
ErrThread->ThreadKey.IrpKey.MinorFunction =
IrpSp->MinorFunction;
RtlCopyMemory(ErrThread->ThreadKey.IrpKey.Arguments,
&IrpSp->Parameters.Others,
sizeof(PVOID) * 4);
ErrThread->ThreadKey.IrpKey.TargetDevice =
IopDbgGetLowestDevice(IrpSp->DeviceObject);
break;
case THREADKEY_THREADID:
ErrThread->ThreadKey.ThIDKey.ThreadID = (PKTHREAD)Key;
break;
}
ExInterlockedInsertHeadList(&IoepErrThreadListHead,
&ErrThread->list,
&IoepErrListLock);
} else {
DBGPRINT(("failed to allocate error thread\n"));
}
EXIT(2, ("=%p\n", ErrThread));
return ErrThread;
} //IoepNewErrThread
VOID
IoepLogErr(
IN ULONG KeyType,
IN PVOID Key,
IN CONST GUID *ComponentGuid,
IN ULONG ErrCode,
IN PWSTR TextData OPTIONAL,
IN ULONG DataBlkType,
IN ULONG DataBlkLen OPTIONAL,
IN PVOID DataBlock OPTIONAL,
IN CONST GUID *MofGuid OPTIONAL
)
/*++
Routine Description:
This routine logs the error data to the error log session identified by
the given key.
Arguments:
KeyType - specifies the type of key.
Key - points to the key that is used to locate the logging session.
ComponentGuid - points to the component GUID of the caller
ErrCode - unique error code
TextData - points to an optional WSTR of text data
DataBlkType - data type of the data block
DataBlkLen - length of the data block
DataBlock - points to the data block
MofGuid - points to the MOF GUID of the data block if applicable
Return Value:
None
--*/
{
PROCNAME("IoepLogErr");
PERRTHREAD ErrThread;
ASSERT(Key != NULL);
ASSERT(ComponentGuid != NULL);
ASSERT((DataBlkType == IOEDATA_NONE) ||
(DataBlkLen > 0) && (DataBlock != NULL));
ASSERT(DataBlkType <= IOEDATA_MAX);
ENTER(2, ("(KeyType=%x,Key=%p,pGuid=%p,ErrCode=%x,Text=%S,Type=%x,Len=%d,DataBlk=%p,MofGuid=%p)\n",
KeyType, Key, ComponentGuid, ErrCode, TextData ? TextData : L"",
DataBlkType, DataBlkLen, DataBlock, MofGuid));
ErrThread = IoepFindErrThread(KeyType, Key);
if (ErrThread != NULL) {
PERRENTRY Err;
ASSERT(!IsListEmpty(&ErrThread->ErrLogListHead));
Err = ExAllocatePoolWithTag(NonPagedPool,
sizeof(ERRENTRY),
IOETAG_ERRENTRY);
if (Err != NULL) {
BOOLEAN fOK = TRUE;
PVOID Data = NULL;
PWSTR pwstr = NULL;
RtlZeroMemory(Err, sizeof(ERRENTRY));
if (DataBlkType != IOEDATA_NONE) {
Data = ExAllocatePoolWithTag(NonPagedPool,
DataBlkLen,
IOETAG_DATABLOCK);
if (Data != NULL) {
RtlCopyMemory(Data, DataBlock, DataBlkLen);
} else {
fOK = FALSE;
DBGPRINT(("failed to allocate data block (len=%d)\n",
DataBlkLen));
}
} else {
// Make sure DataLen does not have garbage.
DataBlkLen = 0;
}
if (TextData != NULL) {
ULONG len;
len = wcslen(TextData) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
pwstr = ExAllocatePoolWithTag(NonPagedPool,
len,
IOETAG_DATATEXT);
if (pwstr != NULL) {
RtlCopyMemory(pwstr, TextData, len);
RtlInitUnicodeString(&Err->unicodeStr, pwstr);
} else {
fOK = FALSE;
DBGPRINT(("failed to allocate text data (len=%d)\n",
len));
}
}
if (fOK) {
PERRLOG ErrLog;
KIRQL Irql;
Err->ErrID.ComponentGuid = *ComponentGuid;
Err->ErrID.ErrCode = ErrCode;
Err->DataBlkType = DataBlkType;
Err->DataBlkLen = DataBlkLen;
Err->DataBlk = Data;
if (MofGuid != NULL) {
Err->MofGuid = *MofGuid;
}
ExAcquireSpinLock(&IoepErrListLock, &Irql);
ErrLog = CONTAINING_RECORD(ErrThread->ErrLogListHead.Flink,
ERRLOG,
list);
PushEntryList(&ErrLog->ErrStack, &Err->slist);
ExReleaseSpinLock(&IoepErrListLock, Irql);
} else {
if (Data != NULL) {
ExFreePool(Data);
}
if (pwstr != NULL) {
ExFreePool(pwstr);
}
ExFreePool(Err);
}
} else {
DBGPRINT(("failed to allocate error entry\n"));
}
} else {
DBGPRINT(("failed to find associated error thread (Type=%x,Key=%p)\n",
KeyType, Key));
}
EXIT(2, ("!\n"));
} //IoepLogErr
VOID
IoepFreeErrStack(
IN PERRENTRY ErrStack
)
/*++
Routine Description:
This routine frees an error stack and the associated data blocks.
Arguments:
ErrStack - points to the error stack to be freed
Return Value:
None
--*/
{
PROCNAME("IoepFreeErrStack");
PERRENTRY Err, ErrNext;
KIRQL Irql;
ASSERT(ErrStack != NULL);
ENTER(2, ("(ErrStack=%p)\n", ErrStack));
ExAcquireSpinLock(&IoepErrListLock, &Irql);
for (Err = ErrStack; Err != NULL; Err = ErrNext) {
if (Err->DataBlk != NULL) {
ExFreePool(Err->DataBlk);
Err->DataBlk = NULL;
}
RtlFreeUnicodeString(&Err->unicodeStr);
ErrNext = IoepGetNextErrEntry(Err);
Err->slist.Next = NULL;
ExFreePool(Err);
}
ExReleaseSpinLock(&IoepErrListLock, Irql);
EXIT(2, ("!\n"));
} //IoepFreeErrStack
PERRMODULE
IoepFindErrModule(
IN CONST GUID *ComponentGuid
)
/*++
Routine Description:
This routine finds a registered error module with a given module GUID.
Arguments:
ComponentGuid - points to the GUID of the error module
Return Value:
Success - returns pointer to error module found
Failure - returns NULL
--*/
{
PROCNAME("IoepFindErrModule");
PERRMODULE ErrModule = NULL;
PLIST_ENTRY list;
KIRQL Irql;
ENTER(2, ("(pGuid=%p)\n", ComponentGuid));
ExAcquireSpinLock(&IoepErrListLock, &Irql);
for (list = IoepErrModuleListHead.Flink;
list != &IoepErrModuleListHead;
list = list->Flink) {
ErrModule = CONTAINING_RECORD(list, ERRMODULE, list);
if (RtlEqualMemory(&ErrModule->ComponentGuid,
ComponentGuid,
sizeof(GUID))) {
break;
}
ErrModule = NULL;
}
ExReleaseSpinLock(&IoepErrListLock, Irql);
EXIT(2, ("=%p\n", ErrModule));
return ErrModule;
} //IoepFindErrModule
NTSTATUS
IoepExtractErrData(
IN PERRENTRY ErrStack,
OUT PVOID Buffer,
IN ULONG BuffSize,
OUT PULONG DataSize OPTIONAL
)
/*++
Routine Description:
This routine stores the error stack data into a flat buffer.
Arguments:
ErrStack - points to the error stack
Buffer - points to the buffer for the data
BuffSize - specifies the buffer size
DataSize - points to the variable to receive the actual data size
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
--*/
{
PROCNAME("IoepExtractErrData");
NTSTATUS status;
PERRENTRY Err;
ULONG len, i;
ASSERT(ErrStack != NULL);
ASSERT((Buffer != NULL) && (BuffSize > 0) || (DataSize != NULL));
ENTER(2, ("(ErrStack=%p,Buff=%p,BuffSize=%d,pDataSize=%p)\n",
ErrStack, Buffer, BuffSize, DataSize));
for (Err = ErrStack, len = sizeof(ERRINFO) - sizeof(ERRDATA), i = 0;
Err != NULL;
Err = IoepGetNextErrEntry(Err), i++) {
len += sizeof(ERRDATA) +
Err->DataBlkLen +
Err->unicodeStr.MaximumLength;
}
if (len <= BuffSize) {
if ((Buffer != NULL) && (len > 0)) {
PERRINFO ErrInfo = (PERRINFO)Buffer;
PUCHAR pBuff;
ErrInfo->Signature = SIG_ERRINFO;
ErrInfo->Version = IOE_ERRINFO_VERSION;
ErrInfo->Size = len;
ErrInfo->DataTag = NULL;
ErrInfo->TagFlags = 0;
ErrInfo->NumErrEntries = i;
pBuff = (PUCHAR)ErrInfo + sizeof(ERRINFO) + sizeof(ERRDATA)*(i - 1);
for (Err = ErrStack, i = 0;
Err != NULL;
Err = IoepGetNextErrEntry(Err), i++) {
ErrInfo->ErrEntries[i].ErrID = Err->ErrID;
ErrInfo->ErrEntries[i].DataBlkType = Err->DataBlkType;
ErrInfo->ErrEntries[i].DataBlkLen = Err->DataBlkLen;
ErrInfo->ErrEntries[i].MofGuid = Err->MofGuid;
if (Err->unicodeStr.MaximumLength > 0) {
ErrInfo->ErrEntries[i].TextDataOffset = pBuff -
(PUCHAR)ErrInfo;
RtlCopyMemory(pBuff,
Err->unicodeStr.Buffer,
Err->unicodeStr.MaximumLength);
pBuff += Err->unicodeStr.MaximumLength;
} else {
ErrInfo->ErrEntries[i].TextDataOffset = 0;
}
if (Err->DataBlk != NULL) {
ErrInfo->ErrEntries[i].DataBlkOffset = pBuff -
(PUCHAR)ErrInfo;
RtlCopyMemory(pBuff, Err->DataBlk, Err->DataBlkLen);
pBuff += Err->DataBlkLen;
} else {
ErrInfo->ErrEntries[i].DataBlkOffset = 0;
}
}
}
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
DBGPRINT(("buffer too small (size=%d,need=%d)\n", BuffSize, len));
}
if (DataSize != NULL) {
*DataSize = len;
}
EXIT(2, ("=%x(len=%d)\n", status, len));
return status;
} //IoepExtractErrData
NTSTATUS
IoepFireWMIEvent(
IN PERRINFO ErrInfo,
IN PWSTR InstanceName
)
/*++
Routine Description:
This routine fires a WMI event notifying the availability of an error info.
Arguments:
ErrInfo - points to the error info.
InstanceName - points to the instance name string
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
--*/
{
PROCNAME("IoepFireWMIEvent");
NTSTATUS status;
ENTER(2, ("(ErrInfo=%p,InstanceName=%S)\n", ErrInfo, InstanceName));
if (ErrInfo != NULL) {
ULONG NameLen, EventSize;
PWNODE_SINGLE_INSTANCE event;
NameLen = wcslen(InstanceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
EventSize = sizeof(WNODE_SINGLE_INSTANCE) + NameLen + ErrInfo->Size;
event = ExAllocatePoolWithTag(NonPagedPool,
EventSize,
IOETAG_WMIEVENT);
if (event != NULL) {
event->WnodeHeader.BufferSize = EventSize;
event->WnodeHeader.ProviderId = 0;
event->WnodeHeader.Guid = IoepGuid;
event->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
WNODE_FLAG_EVENT_ITEM;
KeQuerySystemTime(&event->WnodeHeader.TimeStamp);
event->OffsetInstanceName = sizeof(WNODE_SINGLE_INSTANCE);
event->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE) + NameLen;
event->SizeDataBlock = ErrInfo->Size;
RtlCopyMemory(&event->VariableData, InstanceName, NameLen);
RtlCopyMemory(event->VariableData + NameLen,
ErrInfo,
ErrInfo->Size);
status = IoWMIWriteEvent(event);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
DBGPRINT(("failed to allocate WMI event node (len=%d)\n",
EventSize));
}
} else {
status = STATUS_SUCCESS;
}
EXIT(2, ("=%x\n", status));
return status;
} //IoepFireWMIEvent
NTSTATUS
IoepHandleErrCase(
IN PERRINFO ErrInfo,
IN PERRCASE ErrCase,
IN ULONG Method,
OUT PUNICODE_STRING unicodeMsg OPTIONAL
)
/*++
Routine Description:
This routine handles an error case by executing the resolution method.
Arguments:
ErrInfo - points to the error info
ErrCase - points to the error case
Method - specifying what method to use to handle the error case
unicodeMsg - point to unicodeStr to receive the error message
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
--*/
{
PROCNAME("IoepHandleErrCase");
NTSTATUS status;
PERRCASEDB ErrCaseDB;
PAGED_CODE();
ENTER(2, ("(ErrInfo=%p,ErrCase=%p,Method=%x,unicodeMsg=%p)\n",
ErrInfo, ErrCase, Method, unicodeMsg));
ErrCaseDB = IoepGetErrCaseDB();
if (ErrCaseDB != NULL) {
PMETHOD MethodTable;
ULONG i;
PVOID MethodData;
UNICODE_STRING unicodeStr;
PERRHANDLER ErrHandler;
PWSTR MsgTemplate;
MethodTable = (PMETHOD)((ULONG_PTR)ErrCaseDB +
ErrCaseDB->DataBlkOffset +
ErrCase->MethodOffset);
for (i = 0, status = STATUS_NOT_FOUND;
(status == STATUS_NOT_FOUND) && (i < ErrCase->NumMethods);
++i) {
if ((Method != IOEMETHOD_ANY) &&
(Method != MethodTable[i].MethodType)) {
continue;
}
MethodData = (PVOID)((ULONG_PTR)ErrCaseDB +
ErrCaseDB->DataBlkOffset +
MethodTable[i].MethodDataOffset);
switch (MethodTable[i].MethodType) {
case IOEMETHOD_LONGMSG:
status = IoepGetErrMessage((PMSGDATA)MethodData,
ErrInfo,
unicodeMsg ? unicodeMsg :
&unicodeStr);
if (NT_SUCCESS(status) && (unicodeMsg == NULL)) {
IoRaiseInformationalHardError(STATUS_IOE_MESSAGE,
&unicodeStr,
NULL);
RtlFreeUnicodeString(&unicodeStr);
}
break;
case IOEMETHOD_SHORTMSG:
MsgTemplate =
(PWSTR)((ULONG_PTR)ErrCaseDB +
ErrCaseDB->DataBlkOffset +
((PMSGDATA)MethodData)->MsgTemplateOffset);
if (unicodeMsg == NULL) {
RtlInitUnicodeString(&unicodeStr, MsgTemplate);
IoRaiseInformationalHardError(STATUS_IOE_MESSAGE,
&unicodeStr,
NULL);
status = STATUS_SUCCESS;
} else {
ULONG len;
PWSTR pwstr;
len = wcslen(MsgTemplate) * sizeof(WCHAR) +
sizeof(UNICODE_NULL);
pwstr = ExAllocatePoolWithTag(PagedPool,
len,
IOETAG_DATAWSTR);
if (pwstr != NULL) {
RtlCopyMemory(pwstr, MsgTemplate, len);
RtlInitUnicodeString(unicodeMsg, pwstr);
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
DBGPRINT(("failed to allocate short message buffer (len=%d)\n",
len));
}
}
break;
case IOEMETHOD_HANDLER:
ErrHandler = IoepFindErrHandler(
&((PHANDLERDATA)MethodData)->ComponentGuid,
((PHANDLERDATA)MethodData)->HandlerIndex);
if (ErrHandler != NULL) {
status = ErrHandler(ErrInfo,
((PHANDLERDATA)MethodData)->Param);
}
break;
default:
DBGPRINT(("invalid method type %d\n",
MethodTable[i].MethodType));
}
}
} else {
status = STATUS_IOE_DATABASE_NOT_READY;
DBGPRINT(("error case database not ready\n"));
}
EXIT(2, ("=%x\n", status));
return status;
} //IoepHandleErrCase
PERRHANDLER
IoepFindErrHandler(
IN CONST GUID *ComponentGuid,
IN ULONG HandlerIndex
)
/*++
Routine Description:
This routine find the error handler with the given module GUID and
handler index.
Arguments:
ComponentGuid - points to the GUID of the handler module
HandlerIndex - index into handler table
Return Value:
Success - returns pointer to error handler found
Failure - returns NULL
--*/
{
PROCNAME("IoepFindErrHandler");
PERRHANDLER ErrHandler = NULL;
PERRMODULE ErrModule;
ENTER(2, ("(pGuid=%p,Index=%x)\n", ComponentGuid, HandlerIndex));
ErrModule = IoepFindErrModule(ComponentGuid);
if ((ErrModule != NULL) && (HandlerIndex < ErrModule->NumErrHandlers)) {
ErrHandler = ErrModule->HandlerTable[HandlerIndex];
}
EXIT(2, ("=%p\n", ErrHandler));
return ErrHandler;
} //IoepFindErrHandler
BOOLEAN
IoepMatchErrIDPath(
IN PERRINFO ErrInfo,
IN PERRID ErrIDPath,
IN ULONG NumErrIDs
)
/*++
Routine Description:
This routine compares the error ID paths for a match.
Arguments:
ErrInfo - points to the error info buffer
ErrIDPath - points to the error ID path of the error case
NumErrIDs - number of error IDs in the path
Return Value:
Success - returns TRUE: matched
Failure - returns FALSE: no match
--*/
{
PROCNAME("IoepMatchErrIDPath");
BOOLEAN rc = TRUE;
BOOLEAN fWildCard = FALSE;
ULONG i, j;
PAGED_CODE();
ENTER(2, ("(ErrInfo=%p,pErrID=%p,NumIDs=%d)\n",
ErrInfo, ErrIDPath, NumErrIDs));
for (i = j = 0; (i < NumErrIDs) && (j < ErrInfo->NumErrEntries);) {
if (ErrIDPath[i].ErrCode == 0) {
fWildCard = TRUE;
i++;
} else if (RtlEqualMemory(&ErrIDPath[i].ComponentGuid,
&ErrInfo->ErrEntries[j].ErrID.ComponentGuid,
sizeof(GUID)) &&
(ErrIDPath[i].ErrCode == ErrInfo->ErrEntries[j].ErrID.ErrCode)) {
i++;
j++;
fWildCard = FALSE;
} else if (fWildCard) {
j++;
} else {
rc = FALSE;
break;
}
}
if ((rc == TRUE) && (i < NumErrIDs)) {
// The database ID path is longer than the error stack ID path.
// This is not a good match, so let's move on to find another match.
// However, the reverse is OK though (i.e. error stack ID path is
// longer than the database ID path). In this case, the database
// ID path has an implicit wild card at the end.
rc = FALSE;
}
EXIT(2, ("=%x\n", rc));
return rc;
} //IoepMatchErrIDPath
NTSTATUS
IoepGetErrMessage(
IN PMSGDATA MsgData,
IN PERRINFO ErrInfo,
OUT PUNICODE_STRING unicodeMsg
)
/*++
Routine Description:
This routine constructs the error message from the message method data.
Arguments:
MsgData - points to the message method data
ErrInfo - points to the error info
unicodeMsg - points to the uninitialized unicode string message buffer
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
Note:
This routine will allocate the actual string buffer of the unicode message.
Therefore, it is the caller's responsibility to free the message buffer
via RtlFreeUnicodeString.
--*/
{
PROCNAME("IoepGetErrMessage");
NTSTATUS status = STATUS_SUCCESS;
PERRCASEDB ErrCaseDB;
PAGED_CODE();
ENTER(2, ("(pMsgData=%p,ErrInfo=%p,pMsg=%p)\n",
MsgData, ErrInfo, unicodeMsg));
ErrCaseDB = IoepGetErrCaseDB();
if (ErrCaseDB != NULL) {
PWSTR MsgTemplate, MsgBuff, pwstr;
MsgTemplate = (PWSTR)((ULONG_PTR)ErrCaseDB +
ErrCaseDB->DataBlkOffset +
MsgData->MsgTemplateOffset);
MsgBuff = ExAllocatePoolWithTag(PagedPool,
sizeof(WCHAR)*MAX_MSG_LEN +
sizeof(UNICODE_NULL),
IOETAG_MSGBUFF);
if (MsgBuff != NULL) {
MsgBuff[0] = L'\0';
RtlInitUnicodeString(unicodeMsg, MsgBuff);
unicodeMsg->MaximumLength = sizeof(WCHAR)*MAX_MSG_LEN +
sizeof(UNICODE_NULL);
while (NT_SUCCESS(status)) {
pwstr = wcschr(MsgTemplate, L'%');
if (pwstr == NULL) {
status = RtlAppendUnicodeToString(unicodeMsg, MsgTemplate);
if (!NT_SUCCESS(status)) {
DBGPRINT(("failed to append unicode string (rc=%x)\n",
status));
}
break;
} else if ((pwstr[1] >= L'0') && (pwstr[1] <= L'9')) {
ULONG ArgIndex;
PWSTR pwstr2;
ArgIndex = wcstoul(&pwstr[1], &pwstr2, 10);
status = IoepUnicodeStringCatN(unicodeMsg,
MsgTemplate,
pwstr - MsgTemplate);
if (NT_SUCCESS(status)) {
MsgTemplate = pwstr2;
status = IoepCatMsgArg(unicodeMsg,
&MsgData->Args[ArgIndex],
ErrInfo);
}
} else {
pwstr++;
status = IoepUnicodeStringCatN(unicodeMsg,
MsgTemplate,
pwstr - MsgTemplate);
if (NT_SUCCESS(status)) {
MsgTemplate = pwstr;
}
}
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
DBGPRINT(("failed to allocate message buffer\n"));
}
} else {
status = STATUS_IOE_DATABASE_NOT_READY;
DBGPRINT(("error case database not ready\n"));
}
EXIT(2, ("=%x(Msg=%S)\n",
status, unicodeMsg->Buffer ? unicodeMsg->Buffer : L""));
return status;
} //IoepGetErrMessage
NTSTATUS
IoepCatMsgArg(
IN OUT PUNICODE_STRING unicodeMsg,
IN PMSGARG MsgArg,
IN PERRINFO ErrInfo
)
/*++
Routine Description:
This routine appends the substituted message argument to the unicode
string if there is enough space. Otherwise, STATUS_BUFFER_TOO_SMALL is
returned.
Arguments:
unicodeMsg - points to the target unicode message
MsgArg - points to the message argument structure
ErrInfo - points to the error info.
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
--*/
{
PROCNAME("IoepCatMsgArg");
NTSTATUS status = STATUS_SUCCESS;
PERRCASEDB ErrCaseDB;
PAGED_CODE();
ENTER(2, ("(Msg=%S,pMsgArg=%p,ErrInfo=%p)\n",
unicodeMsg->Buffer, MsgArg, ErrInfo));
ErrCaseDB = IoepGetErrCaseDB();
if (ErrCaseDB != NULL) {
if (MsgArg->ErrIDIndex < ErrInfo->NumErrEntries) {
PERRDATA ErrData;
ANSI_STRING ansiStr;
UNICODE_STRING unicodeStr;
PHANDLERDATA HandlerData;
PERRHANDLER ErrHandler;
ErrData = &ErrInfo->ErrEntries[MsgArg->ErrIDIndex];
switch (MsgArg->ArgType) {
case IOEDATA_TEXT:
status = RtlAppendUnicodeToString(
unicodeMsg,
(PWSTR)((PUCHAR)ErrInfo +
ErrData->TextDataOffset));
if (!NT_SUCCESS(status)) {
DBGPRINT(("failed to append unicode string (rc=%x)\n",
status));
}
break;
case IOEDATA_WSTRING:
status = RtlAppendUnicodeToString(
unicodeMsg,
(PWSTR)((PUCHAR)ErrInfo +
ErrData->DataBlkOffset));
if (!NT_SUCCESS(status)) {
DBGPRINT(("failed to append unicode string argument (rc=%x)\n",
status));
}
break;
case IOEDATA_PRIVATE:
HandlerData = (PHANDLERDATA)((ULONG_PTR)ErrCaseDB +
ErrCaseDB->DataBlkOffset +
MsgArg->ArgDataOffset);
ErrHandler = IoepFindErrHandler(&HandlerData->ComponentGuid,
HandlerData->HandlerIndex);
if (ErrHandler != NULL) {
status = ErrHandler(ErrData, HandlerData->Param);
}
break;
default:
DBGPRINT(("invalid message argument type %d\n",
MsgArg->ArgType));
}
} else {
status = STATUS_UNSUCCESSFUL;
DBGPRINT(("failed to find error entry\n"));
}
} else {
status = STATUS_IOE_DATABASE_NOT_READY;
DBGPRINT(("error case database not ready\n"));
}
EXIT(2, ("=%x(Msg=%S)\n", status, unicodeMsg->Buffer));
return status;
} //IoepCatMsgArg
NTSTATUS
IoepUnicodeStringCatN(
IN OUT PUNICODE_STRING unicodeStr,
IN PWSTR pwstr,
IN ULONG len
)
/*++
Routine Description:
This routine appends the wchar string to the unicode string if there is
enough space. Otherwise, STATUS_BUFFER_TOO_SMALL is returned.
Arguments:
unicodeStr - points to the target unicode string
pwstr - points to the wchar string
len - length of wstr in bytes to append
Return Value:
Success - returns STATUS_SUCCESS
Failure - returns NT status code
--*/
{
PROCNAME("IoepUnicodeStringCatN");
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
ENTER(2, ("(Msg=%S,Str=%S,Len=%d)\n", unicodeStr->Buffer, pwstr, len));
if (len < (ULONG)(unicodeStr->MaximumLength - unicodeStr->Length)) {
wcsncpy(unicodeStr->Buffer, pwstr, len / sizeof(WCHAR));
unicodeStr->Length += (USHORT)len;
unicodeStr->Buffer[len / sizeof(WCHAR)] = L'\0';
} else {
status = STATUS_BUFFER_TOO_SMALL;
DBGPRINT(("unicode string too small\n"));
}
EXIT(2, ("=%x(Msg=%S)\n", status, unicodeStr->Buffer));
return status;
} //IoepUnicodeStringCatN
PERRCASEDB IoepGetErrCaseDB(VOID)
/*++
Routine Description:
This routine checks if the ErrCase database has been read into memory.
If it has, it returns the pointer to the error case database.
Otherwise, it tries to read the database in.
Return Value:
Success - returns pointer to the error case database
Failure - returns NULL
--*/
{
PROCNAME("IoepGetErrCaseDB");
PAGED_CODE();
ENTER(2, ("()\n"));
if (IoepErrCaseDB == NULL) {
OBJECT_ATTRIBUTES ObjAttr;
NTSTATUS status;
HANDLE hReg, hFile;
InitializeObjectAttributes(&ObjAttr, &IoepRegKeyStrIoErr, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ZwOpenKey(&hReg, KEY_READ, &ObjAttr);
if (NT_SUCCESS(status)) {
PKEY_VALUE_FULL_INFORMATION KeyValue;
status = IopGetRegistryValue(hReg, L"ErrCaseDB", &KeyValue);
if (NT_SUCCESS(status)) {
UNICODE_STRING unicodeStr;
IO_STATUS_BLOCK IoStatusBlk;
RtlInitUnicodeString(&unicodeStr, (PWSTR)((PUCHAR)KeyValue + KeyValue->DataOffset));
InitializeObjectAttributes(&ObjAttr, &unicodeStr, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ZwCreateFile(&hFile,
FILE_READ_DATA,
&ObjAttr,
&IoStatusBlk,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (NT_SUCCESS(status) && (IoStatusBlk.Information == FILE_OPENED)) {
ERRCASEDB DBHeader;
status = ZwReadFile(hFile, NULL, NULL, NULL, &IoStatusBlk, &DBHeader, sizeof(DBHeader), NULL, NULL);
if (NT_SUCCESS(status)) {
IoepErrCaseDB = ExAllocatePoolWithTag(PagedPool, DBHeader.Length, IOETAG_ERRCASEDB);
if (IoepErrCaseDB != NULL) {
RtlCopyMemory(IoepErrCaseDB, &DBHeader, sizeof(DBHeader));
status = ZwReadFile(hFile,
NULL,
NULL,
NULL,
&IoStatusBlk,
(PUCHAR)IoepErrCaseDB + sizeof(DBHeader),
DBHeader.Length - sizeof(DBHeader),
NULL,
NULL);
if (!NT_SUCCESS(status)) {
ExFreePool(IoepErrCaseDB);
IoepErrCaseDB = NULL;
DBGPRINT(("failed to read error case database (rc=%x)\n", status));
}
} else {
DBGPRINT(("failed to allocate storage for error case database (len=%d)\n", DBHeader.Length));
}
} else {
DBGPRINT(("failed to read database header (rc=%x)\n", status));
}
ZwClose(hFile);
} else {
DBGPRINT(("failed to open error case database (rc=%x)\n", status));
}
ExFreePool(KeyValue);
} else {
DBGPRINT(("failed to read registry value (rc=%x)\n", status));
}
ZwClose(hReg);
} else {
DBGPRINT(("failed to open registry key (rc=%x)\n", status));
}
}
EXIT(2, ("=%p\n", IoepErrCaseDB));
return IoepErrCaseDB;
} //IoepGetErrCaseDB