Leaked source code of windows server 2003
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.
 
 
 
 
 
 

761 lines
19 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
vfirplog.c
Abstract:
This module manages IRP logs for the verifier.
Author:
Adrian J. Oney (adriao) 09-May-1998
Environment:
Kernel mode
Revision History:
--*/
//
// Disable W4 level warnings generated by public headers.
//
#include "vfpragma.h"
#include "..\io\iop.h" // Includes vfdef.h
#include "viirplog.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEVRFY, VfIrpLogInit)
#pragma alloc_text(PAGEVRFY, ViIrpLogDatabaseFindPointer)
#pragma alloc_text(PAGEVRFY, ViIrpLogExposeWmiCallback)
#pragma alloc_text(PAGEVRFY, VfIrpLogRecordEvent)
#pragma alloc_text(PAGEVRFY, VfIrpLogGetIrpDatabaseSiloCount)
#pragma alloc_text(PAGEVRFY, VfIrpLogLockDatabase)
#pragma alloc_text(PAGEVRFY, VfIrpLogRetrieveWmiData)
#pragma alloc_text(PAGEVRFY, VfIrpLogUnlockDatabase)
#pragma alloc_text(PAGEVRFY, VfIrpLogDeleteDeviceLogs)
#endif
PIRPLOG_HEAD ViIrpLogDatabase;
KSPIN_LOCK ViIrpLogDatabaseLock;
LONG ViIrpLogDdiLock = DDILOCK_UNREGISTERED;
#define POOLTAG_IRPLOG_DATABASE 'dIfV'
#define POOLTAG_IRPLOG_DATA 'eIfV'
#define POOLTAG_IRPLOG_TEMP 'tIfV'
#define POOLTAG_IRPLOG_WORKITEM 'wIfV'
#define INSTANCE_NAME_PROLOG L"VERIFIER"
VOID
VfIrpLogInit(
VOID
)
/*++
Description:
This routine initializes all the important structures we use to log IRPs.
Arguments:
None
Return Value:
None
--*/
{
ULONG i;
PAGED_CODE();
KeInitializeSpinLock(&ViIrpLogDatabaseLock);
//
// As this is system startup code, it is one of the very few places
// where it's ok to use MustSucceed.
//
ViIrpLogDatabase = (PIRPLOG_HEAD) ExAllocatePoolWithTag(
NonPagedPoolMustSucceed,
VI_IRPLOG_DATABASE_HASH_SIZE * sizeof(IRPLOG_HEAD),
POOLTAG_IRPLOG_DATABASE
);
for(i=0; i < VI_IRPLOG_DATABASE_HASH_SIZE; i++) {
ViIrpLogDatabase[i].Locked = FALSE;
InitializeListHead(&ViIrpLogDatabase[i].ListHead);
}
}
PIRPLOG_DATA
FASTCALL
ViIrpLogDatabaseFindPointer(
IN PDEVICE_OBJECT DeviceObject,
OUT PIRPLOG_HEAD *HashHead
)
/*++
Description:
This routine returns a pointer to a pointer to the per-device object.
data. This function is meant to be called by other routines in this file.
N.B. The verifier devobj database lock is assumed to be held by the caller.
Arguments:
DeviceObject - Device object to locate in the tracking table.
HashHead - If return is non-null, points to the hash head that should
be used to insert the tracking data.
Return Value:
IrpLogData iff found, NULL otherwise.
--*/
{
PIRPLOG_DATA irpLogData;
PLIST_ENTRY listEntry, listHead;
UINT_PTR hashIndex;
hashIndex = VI_IRPLOG_CALCULATE_DATABASE_HASH(DeviceObject);
ASSERT_SPINLOCK_HELD(&ViIrpLogDatabaseLock);
*HashHead = &ViIrpLogDatabase[hashIndex];
listHead = &ViIrpLogDatabase[hashIndex].ListHead;
for(listEntry = listHead;
listEntry->Flink != listHead;
listEntry = listEntry->Flink) {
irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
if (irpLogData->DeviceObject == DeviceObject) {
return irpLogData;
}
}
return NULL;
}
VOID
ViIrpLogExposeWmiCallback(
IN PVOID Context
)
{
PWORK_QUEUE_ITEM workQueueItem;
PAGED_CODE();
workQueueItem = (PWORK_QUEUE_ITEM) Context;
VfDdiExposeWmiObjects();
ViIrpLogDdiLock = DDILOCK_REGISTERED;
ExFreePool(workQueueItem);
}
VOID
VfIrpLogRecordEvent(
IN PVERIFIER_SETTINGS_SNAPSHOT VerifierSettingsSnapshot,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIRPLOG_HEAD hashHead;
PIRPLOG_DATA irpLogData;
IRPLOG_SNAPSHOT irpLogSnapshot;
PWORK_QUEUE_ITEM workQueueItem;
KIRQL oldIrql;
LONG oldVal;
ULONG maxElementCount;
ULONG elementCount;
LOGICAL logEntry;
if (!VfSettingsIsOptionEnabled(VerifierSettingsSnapshot,
VERIFIER_OPTION_EXPOSE_IRP_HISTORY)) {
return;
}
if (ViIrpLogDdiLock != DDILOCK_REGISTERED) {
oldVal = InterlockedCompareExchange( &ViIrpLogDdiLock,
DDILOCK_REGISTERING,
DDILOCK_UNREGISTERED );
if (oldVal == DDILOCK_UNREGISTERED) {
workQueueItem = (PWORK_QUEUE_ITEM) ExAllocatePoolWithTag(
NonPagedPool,
sizeof(WORK_QUEUE_ITEM),
POOLTAG_IRPLOG_WORKITEM
);
if (workQueueItem) {
ExInitializeWorkItem(
workQueueItem,
ViIrpLogExposeWmiCallback,
workQueueItem
);
ExQueueWorkItem(
workQueueItem,
DelayedWorkQueue
);
} else {
ViIrpLogDdiLock = DDILOCK_UNREGISTERED;
}
}
}
ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
irpLogData = ViIrpLogDatabaseFindPointer(DeviceObject, &hashHead);
if (hashHead->Locked) {
//
// The current logs are being drained. Since logs are lossy anyway,
// dump this one on the floor.
//
ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
return;
}
if (irpLogData == NULL) {
VfSettingsGetValue(
VerifierSettingsSnapshot,
VERIFIER_VALUE_IRPLOG_COUNT,
&maxElementCount
);
irpLogData = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(IRPLOG_DATA)+(maxElementCount-1)*sizeof(IRPLOG_SNAPSHOT),
POOLTAG_IRPLOG_DATA
);
if (irpLogData != NULL) {
ObReferenceObject(DeviceObject);
irpLogData->DeviceObject = DeviceObject;
irpLogData->Flags = 0;
irpLogData->DeviceType = DeviceObject->DeviceType;
irpLogData->Head = 0;
irpLogData->MaximumElementCount = maxElementCount;
InsertHeadList(&hashHead->ListHead, &irpLogData->HashLink);
}
}
if (irpLogData != NULL) {
if (!(irpLogData->Flags & (IRPLOG_FLAG_DELETED | IRPLOG_FLAG_NAMELESS))) {
if (irpLogData->Flags == IRPLOG_FLAG_FULL) {
elementCount = irpLogData->MaximumElementCount;
} else {
elementCount = irpLogData->Head;
}
logEntry = VfMajorBuildIrpLogEntry(
Irp,
elementCount,
&irpLogData->SnapshotArray[irpLogData->Head],
&irpLogSnapshot
);
if (logEntry) {
irpLogData->SnapshotArray[irpLogData->Head] = irpLogSnapshot;
irpLogData->Head++;
if (irpLogData->Head == irpLogData->MaximumElementCount) {
irpLogData->Flags |= IRPLOG_FLAG_FULL;
irpLogData->Head = 0;
}
}
}
}
ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
}
ULONG
VfIrpLogGetIrpDatabaseSiloCount(
VOID
)
{
return VI_IRPLOG_DATABASE_HASH_SIZE;
}
NTSTATUS
VfIrpLogLockDatabase(
IN ULONG SiloNumber
)
{
NTSTATUS status;
KIRQL oldIrql;
ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
//
// Take the database offline. From this point on, changes can still be made
// but new entries cannot be added, nor can old ones be removed from the
// tree. We do this under a lock to ensure all current inserts/removals
// have drained with respect to the state change.
//
ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
if (ViIrpLogDatabase[SiloNumber].Locked) {
//
// Reentrancy attempt - we don't try to do anything clever.
//
status = STATUS_RETRY;
} else {
ViIrpLogDatabase[SiloNumber].Locked = TRUE;
status = STATUS_SUCCESS;
}
ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
return status;
}
NTSTATUS
VfIrpLogRetrieveWmiData(
IN ULONG SiloNumber,
OUT PUCHAR OutputBuffer OPTIONAL,
OUT ULONG *OffsetInstanceNameOffsets,
OUT ULONG *InstanceCount,
OUT ULONG *DataBlockOffset,
OUT ULONG *TotalRequiredSize
)
{
PIRPLOG_DATA irpLogData;
PLIST_ENTRY listEntry, listHead;
ULONG instances;
POBJECT_NAME_INFORMATION objectName;
ULONG currentNameSize, neededNameSize;
ULONG totalDataSize;
ULONG nameOffsetArrayOffset;
ULONG instanceLengthArrayOffset;
ULONG nameStringBufferOffset;
ULONG instanceDataOffset;
ULONG individualStringLengthInBytes;
ULONG individualStringLengthInChars;
ULONG elementCount;
PULONG nameOffsetBuffer;
POFFSETINSTANCEDATAANDLENGTH instanceLengthBuffer;
PUSHORT nameStringBuffer;
PUCHAR instanceDataBuffer;
NTSTATUS status;
//
// The irp log database must be locked for this query.
//
ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
ASSERT(ViIrpLogDatabase[SiloNumber].Locked);
//
// Preinit for error.
//
*OffsetInstanceNameOffsets = 0;
*InstanceCount = 0;
*DataBlockOffset = 0;
*TotalRequiredSize = 0;
//
// Allocate an initial buffer.
//
currentNameSize = sizeof(OBJECT_NAME_INFORMATION);
objectName = ExAllocatePoolWithTag(
PagedPool,
currentNameSize,
POOLTAG_IRPLOG_TEMP
);
if (objectName == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Walk through the database and start retrieving information. First count
// the instances.
//
instances = 0;
listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
for(listEntry = listHead;
listEntry->Flink != listHead;
listEntry = listEntry->Flink) {
irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
#ifdef MAX_INSTANCE_COUNT
if (instances == MAX_INSTANCE_COUNT) {
break;
}
#endif
instances++;
}
//
// First, since we have dynamic named, we need to allocate space for the
// ULONG-sized array of offset pointers to each string.
//
//
// The buffer will look like this:
//
// [WNODE_ALL_DATA]
// [Array of per-instance DataOffset+DataLength entries)
// [Array of per-instance NameOffset entries]
// [names]
// [data]
//
instanceLengthArrayOffset = FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength);
nameOffsetArrayOffset = instanceLengthArrayOffset + instances*sizeof(OFFSETINSTANCEDATAANDLENGTH);
nameStringBufferOffset = nameOffsetArrayOffset + instances*sizeof(ULONG);
nameOffsetBuffer = (PULONG) (OutputBuffer + nameOffsetArrayOffset);
instanceLengthBuffer = (POFFSETINSTANCEDATAANDLENGTH) (OutputBuffer + instanceLengthArrayOffset);
nameStringBuffer = (PUSHORT) (OutputBuffer + nameStringBufferOffset);
//
// So far the required size only accounts for the array of offsets to names
//
totalDataSize = nameStringBufferOffset;
//
// Now start collecting the names.
//
status = STATUS_SUCCESS;
instances = 0;
listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
for(listEntry = listHead;
listEntry->Flink != listHead;
listEntry = listEntry->Flink) {
irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
//
// Retrieve the name
//
status = ObQueryNameString(
irpLogData->DeviceObject,
objectName,
currentNameSize,
&neededNameSize
);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
ExFreePool(objectName);
objectName = ExAllocatePoolWithTag(
PagedPool,
neededNameSize,
POOLTAG_IRPLOG_TEMP
);
if (objectName == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
currentNameSize = neededNameSize;
status = ObQueryNameString(
irpLogData->DeviceObject,
objectName,
currentNameSize,
&neededNameSize
);
}
}
if (!NT_SUCCESS(status)) {
break;
}
if (objectName->Name.Length == 0) {
irpLogData->Flags |= IRPLOG_FLAG_NAMELESS;
continue;
}
#ifdef MAX_INSTANCE_COUNT
if (instances == MAX_INSTANCE_COUNT) {
break;
}
#endif
instances++;
//
// Write the appropriate offset into the name offset array
//
if (ARGUMENT_PRESENT(OutputBuffer)) {
*nameOffsetBuffer = totalDataSize;
}
nameOffsetBuffer++;
//
// Add in memory for each "counted" string. WMI counted strings are
// of the form [USHORT LenInBytesIncludingTerminator]
// [WCHAR Array w/NULL terminator]
//
// The string is of the form VERIFIER\Device\Foo (The terminating NULL
// is accounted for by sizeof().
//
individualStringLengthInBytes = objectName->Name.Length + sizeof(INSTANCE_NAME_PROLOG);
individualStringLengthInChars = individualStringLengthInBytes/sizeof(WCHAR);
//
// Write out the counted string, starting with the length
//
ASSERT(OutputBuffer + totalDataSize == (PUCHAR) nameStringBuffer);
if (ARGUMENT_PRESENT(OutputBuffer)) {
*nameStringBuffer = (USHORT) individualStringLengthInBytes;
}
nameStringBuffer++;
totalDataSize += sizeof(USHORT);
if (ARGUMENT_PRESENT(OutputBuffer)) {
RtlCopyMemory(
nameStringBuffer,
INSTANCE_NAME_PROLOG,
sizeof(INSTANCE_NAME_PROLOG)-sizeof(UNICODE_NULL)
);
RtlCopyMemory(
nameStringBuffer + ((sizeof(INSTANCE_NAME_PROLOG) - sizeof(UNICODE_NULL))/sizeof(WCHAR)),
objectName->Name.Buffer,
objectName->Name.Length
);
nameStringBuffer[individualStringLengthInChars-1] = UNICODE_NULL;
}
nameStringBuffer += individualStringLengthInChars;
totalDataSize += individualStringLengthInBytes;
}
if (objectName) {
ExFreePool(objectName);
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// Now collect the instance data
//
totalDataSize = ALIGN_UP_ULONG(totalDataSize, 8);
instanceDataOffset = totalDataSize;
instanceDataBuffer = (OutputBuffer + instanceDataOffset);
instances = 0;
listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
for(listEntry = listHead;
listEntry->Flink != listHead;
listEntry = listEntry->Flink) {
irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
if (irpLogData->Flags & IRPLOG_FLAG_NAMELESS) {
continue;
}
#ifdef MAX_INSTANCE_COUNT
if (instances == MAX_INSTANCE_COUNT) {
break;
}
#endif
instances++;
if (irpLogData->Flags & IRPLOG_FLAG_FULL) {
elementCount = irpLogData->MaximumElementCount;
} else {
elementCount = irpLogData->Head;
}
if (ARGUMENT_PRESENT(OutputBuffer)) {
//
// Update the array of per-instance Offset/Length information
//
instanceLengthBuffer->OffsetInstanceData = totalDataSize;
instanceLengthBuffer->LengthInstanceData =
sizeof(ULONG)*2 + (elementCount * sizeof(IRPLOG_SNAPSHOT));
instanceLengthBuffer++;
//
// Write out the device type.
//
*((PULONG) instanceDataBuffer) = irpLogData->DeviceType;
instanceDataBuffer += sizeof(ULONG);
//
// Write out the instance data count
//
*((PULONG) instanceDataBuffer) = elementCount;
instanceDataBuffer += sizeof(ULONG);
//
// Don't bother with reordering the data appropriately. Also note
// that we have 8 byte alignment here - very important!!!
//
RtlCopyMemory(
instanceDataBuffer,
irpLogData->SnapshotArray,
elementCount * sizeof(IRPLOG_SNAPSHOT)
);
instanceDataBuffer += elementCount * sizeof(IRPLOG_SNAPSHOT);
}
totalDataSize += sizeof(ULONG)*2;
totalDataSize += elementCount * sizeof(IRPLOG_SNAPSHOT);
}
*OffsetInstanceNameOffsets = nameOffsetArrayOffset;
*InstanceCount = instances;
*DataBlockOffset = instanceDataOffset;
*TotalRequiredSize = totalDataSize;
return STATUS_SUCCESS;
}
VOID
VfIrpLogUnlockDatabase(
IN ULONG SiloNumber
)
{
KIRQL oldIrql;
PIRPLOG_DATA irpLogData;
PLIST_ENTRY listEntry, listHead;
ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
//
// Reenable logging to present devices
//
ViIrpLogDatabase[SiloNumber].Locked = FALSE;
//
// Clean up any lingering deleted device data
//
ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
for(listEntry = listHead;
listEntry->Flink != listHead;
listEntry = listEntry->Flink) {
irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
if (irpLogData->Flags & IRPLOG_FLAG_DELETED) {
ObDereferenceObject(irpLogData->DeviceObject);
RemoveEntryList(&irpLogData->HashLink);
ExFreePool(irpLogData);
}
}
ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
}
VOID
VfIrpLogDeleteDeviceLogs(
IN PDEVICE_OBJECT DeviceObject
)
{
PIRPLOG_DATA irpLogData;
PIRPLOG_HEAD hashHead;
KIRQL oldIrql;
ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
irpLogData = ViIrpLogDatabaseFindPointer(DeviceObject, &hashHead);
if (irpLogData != NULL) {
if (!hashHead->Locked) {
ObDereferenceObject(irpLogData->DeviceObject);
RemoveEntryList(&irpLogData->HashLink);
ExFreePool(irpLogData);
} else {
irpLogData->Flags |= IRPLOG_FLAG_DELETED;
}
}
ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
}