www.pudn.com > Ó²ÅÌµÄ¼à¿Ø.rar > fspyLib.c
/*++
Copyright (c) 1989-1999 Microsoft Corporation
Module Name:
fspyLib.c
Abstract:
This contains library support routines for FileSpy. These routines
do the main work for logging the I/O operations --- creating the log
records, recording the relevant information, attach/detach from
devices, etc.
Author:
Environment:
Kernel mode
Revision History:
--*/
#include
#include
#include "filespy.h"
#include "fspyKern.h"
//
// NameLookup Flags
//
// These are flags passed to the name lookup routine to identify different
// ways the name of a file can be obtained
//
#define NAMELOOKUPFL_CAN_GET_NAME_FROM_FILEOBJ 0x00000001
// if set, the filename can be retrieved from the file object
#define NAMELOOKUPFL_OPEN_BY_ID 0x00000002
// if set and we are looking up the name in the file object,
// the file object does not actually contain a name but it
// contains a file/object ID.
////////////////////////////////////////////////////////////////////////
// //
// Memory allocation routines //
// //
////////////////////////////////////////////////////////////////////////
DBGSTATIC
PVOID
SpyAllocateBuffer (
IN OUT PLONG Counter,
IN LONG MaxCounterValue,
OUT PULONG RecordType
)
/*++
Routine Description:
Allocates a new buffer from the gFreeBufferList if there is enough memory
to do so and Counter does not exceed MaxCounterValue. The RecordType
is set to one of the record type constants based on the allocation state.
Arguments:
Counter - (optional) the counter variable to test and increment if
we can allocate
MaxCounterValue - (ignored if Counter not given) the value which
Counter should not exceed
RecordType - (optional) set to one of the following:
RECORD_TYPE_NORMAL allocation suceeded
RECORD_TYPE_OUT_OF_MEMORY allocation failed because the system was
out of memory
RECORD_TYPE_EXCEED_MEMORY_ALLOWANCE allocation failed because the
counter exceeded its maximum value.
Return Value:
Pointer to the buffer allocate, or NULL if allocation failed (either
because system is out of memory or we have exceeded the MaxCounterValue).
--*/
{
PVOID newBuffer;
ULONG newRecordType = RECORD_TYPE_NORMAL;
#ifdef MEMORY_DBG
//
// When we are debugging the memory usage to make sure that we
// don't leak memory, we want to allocate the memory from pool
// so that we can use the Driver Verifier to help debug any
// memory problems.
//
newBuffer = ExAllocatePoolWithTag( NonPagedPool, RECORD_SIZE, MSFM_TAG );
#else
//
// When we are not debugging the memory usage, we use a lookaside
// list for better performance.
//
newBuffer = ExAllocateFromNPagedLookasideList( &gFreeBufferList );
#endif
if (newBuffer) {
if (Counter) {
if (*Counter < MaxCounterValue) {
InterlockedIncrement(Counter);
} else {
// We've exceed our driver's memory limit so note that
// and give back the record
newRecordType = RECORD_TYPE_STATIC |
RECORD_TYPE_EXCEED_MEMORY_ALLOWANCE;
#ifdef MEMORY_DBG
ExFreePool( newBuffer );
#else
ExFreeToNPagedLookasideList( &gFreeBufferList, newBuffer );
#endif
newBuffer = NULL;
}
}
} else {
newRecordType = RECORD_TYPE_STATIC |
RECORD_TYPE_OUT_OF_MEMORY;
}
if (RecordType) {
*RecordType = newRecordType;
}
return newBuffer;
}
DBGSTATIC
VOID
SpyFreeBuffer (
IN PVOID Buffer,
IN PLONG Counter
)
/*++
Routine Description:
Returns a Buffer to the gFreeBufferList.
Arguments:
Buffer - the buffer to return to the gFreeBufferList
Return Value:
None.
--*/
{
#ifdef MEMORY_DBG
ExFreePool( Buffer );
#else
ExFreeToNPagedLookasideList( &gFreeBufferList, Buffer );
#endif
//
// Update the count
//
if (Counter) {
InterlockedDecrement(Counter);
}
}
/***********************************************
Logging routines
***********************************************/
DBGSTATIC
PRECORD_LIST
SpyNewRecord (
IN ULONG AssignedSequenceNumber
)
/*++
Routine Description:
Allocates a new RECORD_LIST structure if there is enough memory to do so. A
sequence number is updated for each request for a new record.
Arguments:
AssignedSequenceNumber - 0 if you want this function to generate the
next sequence number; if not 0, the new record is assigned the
given sequence number.
Return Value:
Pointer to the RECORD_LIST allocated, or NULL if no memory is available.
--*/
{
PRECORD_LIST newRecord = NULL;
ULONG currentSequenceNumber;
KIRQL irql;
ULONG initialRecordType;
ULONG T = 1;
newRecord = (PRECORD_LIST) SpyAllocateBuffer( &gRecordsAllocated,
gMaxRecordsToAllocate,
&initialRecordType);
KeAcquireSpinLock(&gLogSequenceLock, &irql);
//
// Assign a new sequence number if 0 was passed in, otherwise use the
// number passed in
//
if (AssignedSequenceNumber == 0) {
gLogSequenceNumber++;
currentSequenceNumber = gLogSequenceNumber;
} else {
currentSequenceNumber = AssignedSequenceNumber;
}
if ((newRecord == NULL) &&
//???????????????????????
!InterlockedCompareExchange( (PVOID)&gStaticBufferInUse, &T, FALSE)) {
//
// Toggle on our gStaticBufferInUse flag and use the static out of memory
// buffer to record this log entry. This special log record is used
// to notify the user application that we are out of memory. Log
// request will be dropped until we can get more memory
//
newRecord = (PRECORD_LIST)gOutOfMemoryBuffer;
newRecord->LogRecord.RecordType = initialRecordType;
newRecord->LogRecord.Length = sizeof(LOG_RECORD);
newRecord->LogRecord.SequenceNumber = currentSequenceNumber;
} else if (newRecord) {
// We were able to allocate a new record so initialize it
// appropriately
newRecord->LogRecord.RecordType = initialRecordType;
newRecord->LogRecord.Length = sizeof(LOG_RECORD);
newRecord->LogRecord.SequenceNumber = currentSequenceNumber;
}
KeReleaseSpinLock(&gLogSequenceLock, irql);
return( newRecord );
}
DBGSTATIC
VOID
SpyFreeRecord (
IN PRECORD_LIST Record
)
/*++
Routine Description:
Frees a RECORD_LIST, which returns the memory to the gFreeBufferList lookaside
list and updates the gRecordsAllocated count.
Arguments:
Record - the record to free
Return Value:
None.
--*/
{
if (Record->LogRecord.RecordType & RECORD_TYPE_STATIC) {
// This is our static record, so reset our gStaticBufferInUse
// flag
InterlockedExchange( &gStaticBufferInUse, FALSE );
} else {
// This isn't our static memory buffer, so free the dynamically
// allocated memory
SpyFreeBuffer( Record, &gRecordsAllocated );
}
}
////////////////////////////////////////////////////////////////////////
// //
// Logging routines //
// //
////////////////////////////////////////////////////////////////////////
DBGSTATIC
VOID
SpyLogIrp (
IN PIRP Irp,
IN UCHAR LoggingFlags,
OUT PRECORD_LIST RecordList
)
/*++
Routine Description:
Records the Irp necessary information according to LoggingFlags in
RecordList. For any activity on the Irp path of a device being
logged, this function should get called twice: once on the Irp's
originating path and once on the Irp's completion path.
Arguments:
Irp - The Irp that contains the information we want to record.
LoggingFlags - The flags that say what to log.
RecordList - The PRECORD_LIST in which the Irp information is stored.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION pIrpStack;
PRECORD_IRP pRecordIrp;
PDEVICE_EXTENSION deviceExtension;
PUNICODE_STRING volumeName;
ULONG lookupFlags;
pRecordIrp = &RecordList->LogRecord.Record.RecordIrp;
pIrpStack = IoGetCurrentIrpStackLocation(Irp);
if (LoggingFlags & LOG_ORIGINATING_IRP) {
//
// Record the information we use for an originating Irp. We first
// need to initialize some of the RECORD_LIST and RECORD_IRP fields.
// Then get the interesting information from the Irp.
//
RecordList->LogRecord.RecordType |= RECORD_TYPE_IRP;
pRecordIrp->IrpMajor = pIrpStack->MajorFunction;
pRecordIrp->IrpMinor = pIrpStack->MinorFunction;
pRecordIrp->IrpFlags = Irp->Flags;
pRecordIrp->FileObject = (FILE_ID)pIrpStack->FileObject;
pRecordIrp->ProcessId = (FILE_ID)PsGetCurrentProcessId();
pRecordIrp->ThreadId = (FILE_ID)PsGetCurrentThreadId();
KeQuerySystemTime(&(pRecordIrp->OriginatingTime));
//
// Only set the volumeName if the next device is a file system
// since we only want to prepend the volumeName if we are on
// top of a local file system.
//
deviceExtension = pIrpStack->DeviceObject->DeviceExtension;
if (deviceExtension &&
(deviceExtension->NextDriverDeviceObject->DeviceType ==
FILE_DEVICE_DISK_FILE_SYSTEM)) {
volumeName = &(deviceExtension->DeviceName);
} else {
volumeName = NULL;
}
lookupFlags = 0;
if (pIrpStack->MajorFunction == IRP_MJ_CREATE) {
lookupFlags |= NAMELOOKUPFL_CAN_GET_NAME_FROM_FILEOBJ;
if (pIrpStack->Parameters.Create.Options & FILE_OPEN_BY_FILE_ID) {
lookupFlags |= NAMELOOKUPFL_OPEN_BY_ID;
}
}
SpyNameLookup(
RecordList,
pIrpStack->FileObject,
lookupFlags,
volumeName);
}
if (LoggingFlags & LOG_COMPLETION_IRP) {
//
// Record the information we use for a completion Irp.
//
pRecordIrp->ReturnStatus = Irp->IoStatus.Status;
pRecordIrp->ReturnInformation = Irp->IoStatus.Information;
KeQuerySystemTime(&(pRecordIrp->CompletionTime));
}
}
DBGSTATIC
PRECORD_LIST
SpyLogFastIoStart (
IN FASTIO_TYPE FastIoType,
IN UCHAR LoggingFlags,
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait
)
/*++
Routine Description:
Creates the log record if possible and records the necessary Fast I/O
information at the beginning of the fast I/O operation in RecordList
according to LoggingFlags.
The optional arguments are not recorded for all Fast I/O types. If
the argument is not needed for a given Fast I/O type, the parameter
was ignored.
Arguments:
FastIoType - The type of fast I/O we are logging (REQUIRED)
LoggingFlags - The flags that say what to log. (REQUIRED)
FileObject - Pointer to the file object this operation is on (OPTIONAL)
FileOffset - Pointer to the file offset for this operation (OPTIONAL)
Length - Length of the data for this operation (OPTIONAL)
Wait - Whether or not this operation can wait for a result (OPTIONAL)
ControlCode - The I/O control value for this operation (OPTIONAL)
Return Value:
The RECORD_LIST structure created with the appropriate information
filled in. If a RECORD_LIST structure couldn't be allocated, NULL
is returned.
--*/
{
PRECORD_LIST pRecordList;
PRECORD_FASTIO pRecordFastIo;
PUNICODE_STRING volumeName;
//
// Try to get a new record
//
pRecordList = SpyNewRecord(0);
//
// If we didn't get a RECORD_LIST, exit and return NULL
//
if (pRecordList == NULL) {
return NULL;
}
//
// We got a RECORD_LIST, so now fill in the appropriate information
//
pRecordFastIo = &pRecordList->LogRecord.Record.RecordFastIo;
//
// Perform the necessary book keeping for the RECORD_LIST
//
pRecordList->LogRecord.RecordType |= RECORD_TYPE_FASTIO;
//
// Set the RECORD_FASTIO fields that are set for all Fast I/O types
//
pRecordFastIo->Type = FastIoType;
KeQuerySystemTime(&(pRecordFastIo->StartTime));
//
// Get process and thread information
//
pRecordFastIo->ProcessId = (ULONG_PTR) PsGetCurrentProcessId();
pRecordFastIo->ThreadId = (ULONG_PTR) PsGetCurrentThreadId();
//
// Record the information that is appropriate based on the
// Fast I/O type
//
pRecordFastIo->FileObject = (FILE_ID)FileObject;
pRecordFastIo->FileOffset.QuadPart = ((FileOffset != NULL) ?
FileOffset->QuadPart :
0);
pRecordFastIo->Length = Length;
pRecordFastIo->Wait = Wait;
pRecordFastIo->Reserved = 0;
if (FileObject &&
FileObject->DeviceObject &&
FileObject->DeviceObject->DeviceExtension) {
volumeName =
&((PDEVICE_EXTENSION)
(FileObject->DeviceObject->DeviceExtension))->DeviceName ;
} else {
volumeName = NULL;
}
SpyNameLookup(pRecordList, FileObject, 0, volumeName);
return pRecordList;
}
DBGSTATIC
VOID
SpyLogFastIoComplete (
IN UCHAR LoggingFlags,
IN PFILE_OBJECT FileObject,
IN PIO_STATUS_BLOCK ReturnStatus,
IN PRECORD_LIST RecordList
)
/*++
Routine Description:
Records the necessary Fast I/O information in RecordList according to
LoggingFlags.
The optional arguments are not recorded for all Fast I/O types. If
the argument is not needed for a given Fast I/O type, the parameter
was ignored.
Arguments:
LoggingFlags - The flags that say what to log.
FileObject - Pointer to the file object this operation is on (OPTIONAL)
ReturnStatus - The return value of the operation (OPTIONAL)
RecordList - The PRECORD_LIST in which the Fast I/O information is stored.
Return Value:
None.
--*/
{
PRECORD_FASTIO pRecordFastIo;
ASSERT(RecordList);
pRecordFastIo = &RecordList->LogRecord.Record.RecordFastIo;
//
// Set the RECORD_FASTIO fields that are set for all Fast I/O types
//
KeQuerySystemTime(&(pRecordFastIo->CompletionTime));
if (ReturnStatus != NULL) {
pRecordFastIo->ReturnStatus = ReturnStatus->Status;
} else {
pRecordFastIo->ReturnStatus = 0;
}
SpyLog(RecordList);
}
DBGSTATIC
NTSTATUS
SpyLog (
IN PRECORD_LIST NewRecord
)
/*++
Routine Description:
This routine appends the completed log record to the gOutputBufferList.
Arguments:
NewRecord - The record to append to the gOutputBufferList
Return Value:
The function returns STATUS_SUCCESS.
--*/
{
KIRQL controlDeviceIrql;
KIRQL outputBufferIrql;
ExAcquireSpinLock( &gControlDeviceStateLock, &controlDeviceIrql );
if (gControlDeviceState == OPENED) {
//
// The device is still open so add this record onto the list
//
ExAcquireSpinLock(&gOutputBufferLock, &outputBufferIrql);
InsertTailList(&gOutputBufferList, &NewRecord->List);
ExReleaseSpinLock(&gOutputBufferLock, outputBufferIrql);
} else {
//
// We can no longer log this record, so free the record
//
SpyFreeRecord( NewRecord );
}
ExReleaseSpinLock( &gControlDeviceStateLock, controlDeviceIrql );
return STATUS_SUCCESS;
}
////////////////////////////////////////////////////////////////////////
// //
// FileName cache routines //
// //
////////////////////////////////////////////////////////////////////////
DBGSTATIC
PHASH_ENTRY
SpyHashBucketLookup (
IN PLIST_ENTRY ListHead,
IN PFILE_OBJECT FileObject
)
/*++
Routine Description:
This routine looks up the FileObject in the give hash bucket. This routine
does NOT lock the hash bucket.
Arguments:
ListHead - hash list to search
FileObject - the FileObject to look up.
Return Value:
A pointer to the hash table entry. NULL if not found
--*/
{
PHASH_ENTRY pHash;
PLIST_ENTRY pList;
pList = ListHead->Flink;
while(pList != ListHead){
pHash = CONTAINING_RECORD( pList, HASH_ENTRY, List );
if (FileObject == pHash->FileObject) {
return pHash;
}
pList = pList->Flink;
}
return NULL;
}
DBGSTATIC
USHORT
SpyNameLookup (
IN PRECORD_LIST RecordList,
IN PFILE_OBJECT FileObject,
IN ULONG LookupFlags,
IN PUNICODE_STRING VolumeName
)
/*++
Routine Description:
This routine looks up the FileObject in the hash table. If the FileObject
is found in the hash table, copy the associated file name to RecordList.
Otherwise, if LookupFlags has NAMELOOKUPFL_CAN_GET_NAME_FROM_FILEOBJ set:
1) copy the name out of the file object (or related file object chain)
into a newly created hash table entry
2) insert it into the hash table
If NAMELOOKUPFL_CAN_GET_NAME_FROM_FILEOBJ is not set, then return 0 if
there is no hash table hit.
Arguments:
RecordList - RecordList to copy name to.
FileObject - the FileObject to look up.
LookInFileObject - see routine description
VolumeName - Drive letter to prepend to the path (eg c:)
Return Value:
The number of bytes copied.
--*/
{
UINT_PTR hashIndex;
KIRQL oldIrql;
PHASH_ENTRY pHash;
PLIST_ENTRY pList;
PHASH_ENTRY newHash;
PLIST_ENTRY listHead;
PUNICODE_STRING newName;
PCHAR buffer;
SHORT length;
if (FileObject == NULL) {
return 0;
}
hashIndex = HASH_FUNC(FileObject);
gHashStat.Lookups++;
KeAcquireSpinLock(&gHashLockTable[hashIndex], &oldIrql);
listHead = &gHashTable[hashIndex];
pHash = SpyHashBucketLookup(&gHashTable[hashIndex], FileObject);
if (pHash) {
length = MINIMUM(
MAX_NAME_SPACE,
pHash->Name.Length + sizeof(WCHAR) // want the terminating NULL
);
ASSERT((length > 0) && (length <= MAX_NAME_SPACE));
RtlCopyMemory(RecordList->LogRecord.Name, pHash->Name.Buffer, length);
RecordList->LogRecord.Length += length;
KeReleaseSpinLock(&gHashLockTable[hashIndex], oldIrql);
gHashStat.LookupHits++;
return length;
}
KeReleaseSpinLock(&gHashLockTable[hashIndex], oldIrql);
if (!(LookupFlags & NAMELOOKUPFL_CAN_GET_NAME_FROM_FILEOBJ)) {
return 0;
}
//
// If it is not in the table, add it.
//
buffer = SpyAllocateBuffer(&gNamesAllocated, gMaxNamesToAllocate, NULL);
if(buffer){
newHash = (PHASH_ENTRY) buffer;
newName = &newHash->Name;
newName->MaximumLength = RECORD_SIZE - sizeof(HASH_ENTRY);
newName->Buffer = (PWCHAR) (buffer + sizeof(HASH_ENTRY));
newName->Length = SpyGetFullPathName(
FileObject,
(PCHAR) newName->Buffer,
newName->MaximumLength,
VolumeName,
LookupFlags);
if (newName->Length) {
newHash->FileObject = FileObject;
KeAcquireSpinLock(&gHashLockTable[hashIndex], &oldIrql);
//
// search again
//
pHash = SpyHashBucketLookup(&gHashTable[hashIndex], FileObject);
if (pHash) {
length = MINIMUM(
MAX_NAME_SPACE,
pHash->Name.Length + sizeof(WCHAR) // we want the
// terminating NULL
);
ASSERT(length > 0 && length <= MAX_NAME_SPACE);
RtlCopyMemory(
RecordList->LogRecord.Name,
pHash->Name.Buffer,
length);
RecordList->LogRecord.Length += length;
KeReleaseSpinLock(&gHashLockTable[hashIndex], oldIrql);
SpyFreeBuffer(buffer, &gNamesAllocated);
return length;
}
//
// It wasn't found, add the new entry
//
length = MINIMUM(
MAX_NAME_SPACE,
newHash->Name.Length
);
ASSERT(length > 0 && length <= MAX_NAME_SPACE);
RtlCopyMemory(
RecordList->LogRecord.Name,
newHash->Name.Buffer,
length);
RecordList->LogRecord.Length += length;
InsertHeadList(listHead, &newHash->List);
gHashCurrentCounters[hashIndex]++;
if (gHashCurrentCounters[hashIndex] > gHashMaxCounters[hashIndex]) {
gHashMaxCounters[hashIndex] = gHashCurrentCounters[hashIndex];
}
KeReleaseSpinLock(&gHashLockTable[hashIndex], oldIrql);
return length;
} else {
SpyFreeBuffer (buffer, &gNamesAllocated);
}
}
return 0;
}
DBGSTATIC
VOID
SpyNameDeleteAllNames (
VOID
)
/*++
Routine Description:
This will free all entries from the hash table
Arguments:
None
Return Value:
None
--*/
{
KIRQL oldIrql;
PHASH_ENTRY pHash;
PLIST_ENTRY pList;
int i;
for (i=0;i < HASH_SIZE;i++) {
KeAcquireSpinLock(&gHashLockTable[i], &oldIrql);
while (!IsListEmpty(&gHashTable[i])) {
pList = RemoveHeadList(&gHashTable[i]);
pHash = CONTAINING_RECORD( pList, HASH_ENTRY, List );
SpyFreeBuffer( pHash, &gNamesAllocated);
}
gHashCurrentCounters[i] = 0;
KeReleaseSpinLock(&gHashLockTable[i], oldIrql);
}
}
DBGSTATIC
VOID
SpyNameDelete (
IN PFILE_OBJECT FileObject
)
/*++
Routine Description:
This routine looks up the FileObject in the hash table. If it is found,
it deletes it and frees the memory.
Arguments:
FileObject - the FileObject to look up.
Return Value:
None
--*/
{
UINT_PTR hashIndex;
KIRQL oldIrql;
PHASH_ENTRY pHash;
PLIST_ENTRY pList;
PLIST_ENTRY listHead;
hashIndex = HASH_FUNC(FileObject);
gHashStat.DeleteLookups++;
KeAcquireSpinLock(&gHashLockTable[hashIndex], &oldIrql);
listHead = &gHashTable[hashIndex];
pList = listHead->Flink;
while(pList != listHead){
pHash = CONTAINING_RECORD( pList, HASH_ENTRY, List );
if (FileObject == pHash->FileObject) {
gHashStat.DeleteLookupHits++;
gHashCurrentCounters[hashIndex]--;
RemoveEntryList(pList);
SpyFreeBuffer( pHash, &gNamesAllocated );
break;
}
pList = pList->Flink;
}
KeReleaseSpinLock(&gHashLockTable[hashIndex], oldIrql);
}
DBGSTATIC
USHORT
SpyGetFullPathName (
IN PFILE_OBJECT FileObject,
IN PCHAR FName,
IN USHORT MaxLength,
IN PUNICODE_STRING VolumeName,
IN ULONG LookupFlags
)
/*++
Routine Description:
This routine retrieves the full pathname of the FileObject. Note that
the buffers containing pathname components may be stored in paged pool.
This returns the length of the name returned, this length includes the
trailing NULL character.
Arguments:
FileObject - Pointer to the FileObject to the get name of.
FileName - Pointer to a buffer to put the name in. It is assumed that
the caller allocates and frees the memory used by the string.
MaxLength - Length of FileName buffer in bytes.
Return Value:
The number of bytes copied into the callers buffer. Zero indicates an
error.
--*/
{
PFILE_OBJECT pRelatedFileObject;
PWSTR origNameBuffer = (PWSTR)FName;
PWSTR wFileName;
USHORT length;
USHORT volumeNameLength = 0;
USHORT i;
if (FileObject == NULL) {
return 0;
}
//
// Names buffers in file objects might be paged.
//
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
return 0;
}
if (VolumeName) {
volumeNameLength = VolumeName->Length;
}
//
// This is an operation on the volume
//
if (FileObject->FileName.Length == 0) {
length = MINIMUM(MaxLength,
(gVolumeString.MaximumLength + volumeNameLength));
if (volumeNameLength) {
RtlCopyMemory(origNameBuffer,VolumeName->Buffer,volumeNameLength);
}
RtlCopyMemory(
&origNameBuffer[volumeNameLength/sizeof(WCHAR)],
gVolumeString.Buffer,
length );
return length;
}
//
// Check for "Open by ID" and generate the names if we are
//
if (LookupFlags & NAMELOOKUPFL_OPEN_BY_ID) {
UNICODE_STRING uIdName;
ANSI_STRING aIdName;
char ansiBuffer[64]; // buffer to build string names into
PUCHAR idbuf;
# define OBJECT_ID_KEY_LENGTH 16
length = 0; // set default return length
if (volumeNameLength) { // set volume name if we have one
RtlCopyMemory(origNameBuffer,VolumeName->Buffer,volumeNameLength);
origNameBuffer += (volumeNameLength/sizeof(WCHAR));
length += volumeNameLength;
}
if (FileObject->FileName.Length == sizeof(LONGLONG)) {
// opening by FILE ID, generate a name
PLONGLONG fileref;
fileref = (PLONGLONG) FileObject->FileName.Buffer;
sprintf( ansiBuffer, "<%016I64x>", *fileref );
} else if ((FileObject->FileName.Length == OBJECT_ID_KEY_LENGTH) ||
(FileObject->FileName.Length == OBJECT_ID_KEY_LENGTH + sizeof(WCHAR))) {
// opening by Object ID, generate a name
idbuf = (PUCHAR)&FileObject->FileName.Buffer[0];
if (FileObject->FileName.Length != OBJECT_ID_KEY_LENGTH) {
// skip win32k backslash at start of buffer
idbuf = (PUCHAR)&FileObject->FileName.Buffer[1];
}
sprintf(ansiBuffer,"<%08x-%04hx-%04hx-%04hx-%04hx%08x>",
*(PULONG)&idbuf[0],
*(PUSHORT)&idbuf[0+4],
*(PUSHORT)&idbuf[0+4+2],
*(PUSHORT)&idbuf[0+4+2+2],
*(PUSHORT)&idbuf[0+4+2+2+2],
*(PULONG)&idbuf[0+4+2+2+2+2]);
} else {
// unknown ID format
sprintf(ansiBuffer,"",FileObject->FileName.Length);
}
//
// Convert the "sprintf" string to unicode
//
aIdName.Length = strlen(ansiBuffer);
aIdName.MaximumLength = aIdName.Length+1;
aIdName.Buffer = ansiBuffer;
uIdName.Length = 0;
uIdName.MaximumLength = (MaxLength - (USHORT)length);
uIdName.Buffer = origNameBuffer;
RtlAnsiStringToUnicodeString(&uIdName,&aIdName,FALSE);
return (USHORT)(uIdName.Length + length + sizeof(WCHAR));
} else if (FileObject->RelatedFileObject) {
//
// must be a relative open
//
// Since we have the leaf name, we are going to have to generate
// the name backwards. Start filling in at the end of the buffer
// and when we are done, copy it to the front of the buffer.
//
// The name in FileObject and FileObject->RelatedFileObject are accessible. Names further up
// the related file object chain (ie FileObject->RelatedFileObject->RelatedFileObject)
// may not be accessible. Given that, incomplete pathnames are possible.
//
pRelatedFileObject = FileObject;
wFileName = &origNameBuffer[MaxLength/sizeof(WCHAR)];
*--wFileName = UNICODE_NULL; // null terminate the string
//
// Only look at the name in FileObject and FileObject->RelatedFileObject.
//
for (i = 0; i < 2 && pRelatedFileObject; i++, pRelatedFileObject = pRelatedFileObject->RelatedFileObject) {
// add a seperator if we need one. Don't add one before the
// first object and don't add one if this is a "data stream"
// or if there is already a seperator.
if ( (pRelatedFileObject != FileObject) &&
(wFileName > origNameBuffer) &&
(*wFileName != L'\\') && // seperator at front of last name?
(*wFileName != L':') && // a data stream?
// seperator at end of next name?
(pRelatedFileObject->FileName.Buffer != NULL) &&
(pRelatedFileObject->FileName.Length > 0) &&
(pRelatedFileObject->FileName.Buffer[
(pRelatedFileObject->FileName.Length/
sizeof(WCHAR))-1] != L'\\')) {
*--wFileName = L'\\';
}
length = pRelatedFileObject->FileName.Length / sizeof(WCHAR);
if (wFileName-length < origNameBuffer) {
// includes NULL character
length = MINIMUM(MaxLength, gOverrunString.MaximumLength);
RtlCopyMemory( origNameBuffer, gOverrunString.Buffer, length );
return length;
}
wFileName -= length;
ASSERT(wFileName >= origNameBuffer);
RtlCopyMemory( wFileName,
pRelatedFileObject->FileName.Buffer,
pRelatedFileObject->FileName.Length);
}
//
// If this is not a full path name, prepend "...\" to it.
//
if (*wFileName != L'\\') {
PWSTR relativePrefix = L"...\\";
ULONG prefixLength;
prefixLength = wcslen(relativePrefix); // length in WCHARs
if (wFileName-prefixLength >= origNameBuffer) {
wFileName -= prefixLength;
RtlCopyMemory(wFileName, relativePrefix, prefixLength * sizeof(WCHAR));
}
}
if ((volumeNameLength > 0) &&
((wFileName-(volumeNameLength/sizeof(WCHAR))) >=
origNameBuffer)) {
wFileName -= (volumeNameLength / sizeof(WCHAR));
RtlCopyMemory(
wFileName,
VolumeName->Buffer,
volumeNameLength );
}
// move string to FRONT of buffer
length = (USHORT)((&origNameBuffer[MaxLength/sizeof(WCHAR)] - wFileName) *
sizeof(WCHAR));
RtlMoveMemory(origNameBuffer,wFileName,length);
return length; // including NULL
} else {
// absolute path
if ((volumeNameLength +
FileObject->FileName.Length +
sizeof(WCHAR)) <= MaxLength) {
if (volumeNameLength) {
RtlCopyMemory(
origNameBuffer,
VolumeName->Buffer,
volumeNameLength );
}
RtlCopyMemory(
&origNameBuffer[volumeNameLength/sizeof(WCHAR)],
FileObject->FileName.Buffer,
FileObject->FileName.Length);
// null terminate the name
origNameBuffer[(volumeNameLength + FileObject->FileName.Length)/
sizeof(WCHAR)] = 0;
// return length including NULL
return (
volumeNameLength +
FileObject->FileName.Length +
sizeof(WCHAR));
} else {
length = MINIMUM(MaxLength, gOverrunString.MaximumLength);
RtlCopyMemory( origNameBuffer, gOverrunString.Buffer, length );
return length;
}
}
}
//////////////////////////////////////////////////////////////////////////
// //
// Library support routines //
// //
//////////////////////////////////////////////////////////////////////////
DBGSTATIC
VOID
SpyReadDriverParameters (
IN PUNICODE_STRING RegistryPath,
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine tries to read the FileSpy-specific parameters from
the registry. These values will be found in the registry location
indicated by the RegistryPath passed in.
Arguments:
RegistryPath - the path key which contains the values that are
the FileSpy parameters
Return Value:
None.
--*/
{
OBJECT_ATTRIBUTES attributes;
HANDLE driverRegKey;
NTSTATUS status;
ULONG bufferSize, resultLength;
PVOID buffer = NULL;
ULONG keyIndex;
UNICODE_STRING valueName;
PKEY_VALUE_PARTIAL_INFORMATION pValuePartialInfo;
PWSTR attachDrives;
InitializeObjectAttributes(
&attributes,
RegistryPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenKey(
&driverRegKey,
KEY_READ,
&attributes);
if (!NT_SUCCESS(status)) {
driverRegKey = NULL;
goto SpyReadDriverParameters_Error;
}
bufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + ATTACH_BUFFER_SIZE;
buffer = ExAllocatePool(NonPagedPool, bufferSize);
if (NULL == buffer) {
goto SpyReadDriverParameters_Error;
}
//
// Read the gMaxRecordsToAllocate from the registry
//
RtlInitUnicodeString(&valueName, MAX_RECORDS_TO_ALLOCATE);
status = ZwQueryValueKey(
driverRegKey,
&valueName,
KeyValuePartialInformation,
buffer,
bufferSize,
&resultLength);
if (NT_SUCCESS(status)) {
pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
ASSERT(pValuePartialInfo->Type == REG_DWORD);
gMaxRecordsToAllocate = *((PLONG)&(pValuePartialInfo->Data));
} else {
gMaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;
}
//
// Read the gMaxNamesToAllocate from the registry
//
RtlInitUnicodeString(&valueName, MAX_NAMES_TO_ALLOCATE);
status = ZwQueryValueKey(
driverRegKey,
&valueName,
KeyValuePartialInformation,
buffer,
bufferSize,
&resultLength);
if (NT_SUCCESS(status)) {
pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
ASSERT(pValuePartialInfo->Type == REG_DWORD);
gMaxNamesToAllocate = *((PLONG)&(pValuePartialInfo->Data));
} else {
gMaxNamesToAllocate = DEFAULT_MAX_NAMES_TO_ALLOCATE;
}
#ifdef SPY_BOOT_DRIVER
//
// Read initial drives to attach to from the registry
//
RtlInitUnicodeString(&valueName, ATTACH_TO);
status = ZwQueryValueKey(
driverRegKey,
&valueName,
KeyValuePartialInformation,
buffer,
bufferSize,
&resultLength);
if (NT_SUCCESS(status)) {
pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
if(pValuePartialInfo->Type != REG_MULTI_SZ){
goto SpyReadDriverParameters_Exit;
}
attachDrives = (PWSTR)&pValuePartialInfo->Data;
if(*attachDrives){
//
// Register with the I/O System to be called again once all the
// devices in the system have been enumerated and started.
//
IoRegisterBootDriverReinitialization(DriverObject, SpyReinitDriver, buffer);
//
// In this instance, SpyReinitDriver will free buffer when it is done parsing and attaching.
//
buffer = NULL;
}
}
#endif
goto SpyReadDriverParameters_Exit;
SpyReadDriverParameters_Error:
gMaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;
gMaxNamesToAllocate = DEFAULT_MAX_NAMES_TO_ALLOCATE;
SpyReadDriverParameters_Exit:
if (NULL != buffer) {
ExFreePool(buffer);
}
if (NULL != driverRegKey) {
ZwClose(driverRegKey);
}
return;
}
BOOLEAN
SpyFindSubString (
IN PUNICODE_STRING String,
IN PUNICODE_STRING SubString
)
{
ULONG index;
//
// First, check to see if the strings are equal.
//
if (RtlEqualUnicodeString( String, SubString, TRUE )) {
return TRUE;
}
//
// String and SubString aren't equal, so now see if SubString
// in in String any where.
//
for (index = 0;
index + SubString->Length <= String->Length;
index++) {
if (_wcsnicmp(&(String->Buffer[index]),
SubString->Buffer,
SubString->Length) == 0) {
//
// SubString is found in String, so return TRUE.
//
return TRUE;
}
}
return FALSE;
}
PDEVICE_EXTENSION
SpyFindAttachedDevice (
IN PUNICODE_STRING devName
)
/*++
Routine Description:
This scans the list of devices we have physical attachments too and
sees if we already have an attachment to this device.
Arguments:
devName - the name of the device to check
Return Value:
The DEVICE_EXTENSION object for the given named object, NULL if it was
not found.
--*/
{
PDEVICE_EXTENSION devext;
PLIST_ENTRY link;
try {
ExAcquireFastMutex( &gSpyDeviceExtensionListLock );
for (link = gSpyDeviceExtensionList.Flink;
link != &gSpyDeviceExtensionList;
link = link->Flink) {
devext = CONTAINING_RECORD(link, DEVICE_EXTENSION, NextDevice);
if (SpyFindSubString(devName,&devext->DeviceName))
{
try_return(devext);
}
}
devext = NULL;
try_exit: NOTHING;
} finally {
ExReleaseFastMutex( &gSpyDeviceExtensionListLock );
return devext; // didn't find the name, return
}
}
BOOLEAN
SpyAlreadyAttached (
IN PDEVICE_OBJECT CandidateDeviceObject,
OUT PDEVICE_EXTENSION *DeviceExtension
)
/*++
Routine Description:
This routine looks through the list of FileSpy device extensions to
see if we have attached to CandidateDeviceObject's device.
This is necessary to work around attaching to network devices. All
network drives are represented by one device object. If we
have more that one network drive, we don't want to attach to this
device object more than once (if we attached more than once, we
would see all I/O to any network drive as many times as we are
attached).
Arguments:
CandidateDeviceObject - Device object that the FileSpy driver is
considering attaching to. We want to make sure that we haven't
already attached to this device object.
DeviceExtension - Gets set to the device extension already attached
to CandidateDeviceObject if we are already attached to
CandidateDeviceObject.
Return Value:
Returns TRUE if we have already attached and FALSE otherwise.
--*/
{
PLIST_ENTRY entry;
PDEVICE_EXTENSION currentExtension;
PDEVICE_OBJECT currentDevice;
BOOLEAN found = FALSE;
*DeviceExtension = NULL;
ExAcquireFastMutex( &gSpyDeviceExtensionListLock );
try {
//
// Look through our deviceExtension list and see if we have a device
// extension for CandidateDeviceObject already.
//
for (entry = gSpyDeviceExtensionList.Flink;
entry != &gSpyDeviceExtensionList;
entry = entry->Flink) {
currentExtension = CONTAINING_RECORD( entry, DEVICE_EXTENSION, NextDevice);
if (currentExtension->NextDriverDeviceObject == CandidateDeviceObject) {
//
// We do have a device extension that references this CandidateDeviceObject
// so return TRUE after setting the output parameter DeviceExtension.
//
*DeviceExtension = currentExtension;
try_return( found = TRUE );
} else {
//
// The NextDriverDeviceObject for this extension may be up the
// stack of CandidateDeviceObject. If so, we don't want to attach
// again. So we look through CandidateDeviceObject->AttachedDevice
// so that we catch this.
//
for (currentDevice = currentExtension->NextDriverDeviceObject->AttachedDevice;
currentDevice != NULL;
currentDevice = currentDevice->AttachedDevice) {
if (currentDevice == CandidateDeviceObject) {
//
// We found a deviceObject in the stack that we are attached to,
// so return this extension.
//
*DeviceExtension = currentExtension;
try_return( found = TRUE );
}
}
}
}
try_exit: NOTHING;
} finally {
ExReleaseFastMutex( &gSpyDeviceExtensionListLock );
}
return found;
}
NTSTATUS
SpyAttachDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PWSTR DeviceName
)
/*++
Routine Description:
This will attempt to physically attach to the given device (defined by
name). This checks to see if we have already physically attached to the
device, if so then simply flag the device that we should log. If not,
then do the physical attachment and flag the device that we should log.
Note: Since all network drives through LAN Manager are represented by _
one_ device object, we want to only attach to this device stack once
and use only one device extension to represent all these drives.
Since FileSpy does not do anything to filter I/O on the LAN Manager's
device object to only log the I/O to the requested drive, the user
will see all I/O to a network drive it he/she is attached to a
network drive.
Arguments:
DeviceObject - Device object for FILESPY driver
DeviceName - Name of device to attach to
Return Value:
Error status code
--*/
{
WCHAR nameBuf[DEVICE_NAME_SZ];
UNICODE_STRING volumeNameUnicodeString;
NTSTATUS status;
PDEVICE_OBJECT nextDriverDeviceObject;
PDEVICE_OBJECT attachedDeviceObject;
PFILE_OBJECT volumeFileObject;
HANDLE fileHandle;
OBJECT_ATTRIBUTES objectAttributes;
PDEVICE_EXTENSION devext;
IO_STATUS_BLOCK openStatus;
volumeNameUnicodeString.MaximumLength = sizeof(nameBuf);
volumeNameUnicodeString.Buffer = nameBuf;
volumeNameUnicodeString.Length= 0;
RtlAppendUnicodeToString(&volumeNameUnicodeString,DeviceName);
devext = SpyFindAttachedDevice(&volumeNameUnicodeString);
if (devext != NULL) {
devext->LogThisDevice = TRUE;
return STATUS_SUCCESS;
}
volumeNameUnicodeString.Length= 0;
RtlAppendUnicodeToString(&volumeNameUnicodeString, L"\\DosDevices\\");
RtlAppendUnicodeToString(&volumeNameUnicodeString,DeviceName);
InitializeObjectAttributes(
&objectAttributes,
&volumeNameUnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
// open the file object for the given device
status = ZwCreateFile(
&fileHandle,
SYNCHRONIZE|FILE_READ_DATA,
&objectAttributes,
&openStatus,
NULL,
0,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT/*|FILE_DIRECTORY_FILE*/,
NULL,
0);
if( !NT_SUCCESS( status ) ) {
return status;
}
// get a pointer to the volumes file object
status = ObReferenceObjectByHandle(
fileHandle,
FILE_READ_DATA,
*IoFileObjectType,
KernelMode,
&volumeFileObject,
NULL);
if( !NT_SUCCESS( status )) {
ZwClose( fileHandle );
return status;
}
// Get the device object we want to attach to (parent device object in chain)
nextDriverDeviceObject = IoGetRelatedDeviceObject( volumeFileObject );
if (nextDriverDeviceObject == NULL)
{
ObDereferenceObject( volumeFileObject );
ZwClose( fileHandle );
return status;
}
if (!SpyAlreadyAttached( nextDriverDeviceObject, &devext )) {
//
// We are not already attached, so attach to this device object.
//
// create a new device object so we can attach it in the call chain
status = IoCreateDevice(
DeviceObject->DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
nextDriverDeviceObject->DeviceType,
0,
FALSE,
&attachedDeviceObject);
if ( !NT_SUCCESS(status) ) {
ObDereferenceObject( volumeFileObject );
ZwClose( fileHandle );
return status;
}
devext = attachedDeviceObject->DeviceExtension;
devext->LogThisDevice = TRUE;
devext->Type = FILESPY_DEVICE_TYPE;
devext->Size = sizeof( DEVICE_EXTENSION );
devext->DeviceName.MaximumLength = sizeof(devext->NameBuffer);
devext->DeviceName.Buffer = devext->NameBuffer;
devext->DeviceName.Length= 0;
RtlAppendUnicodeToString(&devext->DeviceName, DeviceName);
// Add our device object to chain
devext->NextDriverDeviceObject = IoAttachDeviceToDeviceStack(
attachedDeviceObject,
nextDriverDeviceObject );
// see if we could not attach
if (devext->NextDriverDeviceObject == NULL) {
IoDeleteDevice(attachedDeviceObject);
ObDereferenceObject(volumeFileObject);
ZwClose(fileHandle);
return STATUS_UNSUCCESSFUL;
}
ExAcquireFastMutex(&gSpyDeviceExtensionListLock);
InsertTailList(&gSpyDeviceExtensionList, &devext->NextDevice);
ExReleaseFastMutex(&gSpyDeviceExtensionListLock);
if (nextDriverDeviceObject->Flags & DO_BUFFERED_IO) {
attachedDeviceObject->Flags |= DO_BUFFERED_IO;
}
if (nextDriverDeviceObject->Flags & DO_DIRECT_IO) {
attachedDeviceObject->Flags |= DO_DIRECT_IO;
}
attachedDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
} else {
//
// We are already attached, so just add this name to the
// device name for this device extension.
//
RtlAppendUnicodeToString( &devext->DeviceName, L"," );
RtlAppendUnicodeToString( &devext->DeviceName, DeviceName );
}
ObDereferenceObject( volumeFileObject );
ZwClose( fileHandle );
return STATUS_SUCCESS;
}
NTSTATUS
SpyDetachDevice (
IN PWSTR DeviceName
)
/*++
Routine Description:
This routine stop logging the specified device. Since you can not
physically detatch from devices, this routine simply sets a flag saying
to not log the device anymore.
Note: Since all network drives are represented by _one_ device object,
and, therefore, one device extension, if the user detaches from one
network drive, it has the affect of detaching from _all_ network
devices.
Arguments:
DeviceName - The name of the device to "detach" from.
Return Value:
NT Status code
--*/
{
WCHAR nameBuf[DEVICE_NAME_SZ];
UNICODE_STRING volumeNameUnicodeString;
PDEVICE_EXTENSION devext;
volumeNameUnicodeString.MaximumLength = sizeof(nameBuf);
volumeNameUnicodeString.Buffer = nameBuf;
volumeNameUnicodeString.Length= 0;
RtlAppendUnicodeToString(&volumeNameUnicodeString,DeviceName);
devext = SpyFindAttachedDevice(&volumeNameUnicodeString);
if (devext != NULL) {
devext->LogThisDevice = FALSE;
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER;
}
NTSTATUS
SpyGetAttachList (
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PULONG_PTR ReturnLength
)
/*++
Routine Description:
This returns an array of structure identifying all of the devices
we are currently physicall attached to and whether logging is on or
off for the given device
Arguments:
buffer - buffer to receive the attachment list
bufferSize - total size in bytes of the return buffer
returnLength - receives number of bytes we actually return
Return Value:
NT Status code
--*/
{
PLIST_ENTRY link;
PDEVICE_EXTENSION pDevext;
PATTACHED_DEVICE pAttDev;
ULONG retlen = 0;
pAttDev = Buffer;
ExAcquireFastMutex( &gSpyDeviceExtensionListLock );
for (link = gSpyDeviceExtensionList.Flink;
link != &gSpyDeviceExtensionList;
link = link->Flink)
{
pDevext = CONTAINING_RECORD(link, DEVICE_EXTENSION, NextDevice);
if (BufferSize < sizeof(ATTACHED_DEVICE))
break;
pAttDev->LogState = pDevext->LogThisDevice;
wcscpy(pAttDev->DeviceName,pDevext->NameBuffer);
retlen += sizeof(ATTACHED_DEVICE);
BufferSize -= sizeof(ATTACHED_DEVICE);
pAttDev++;
}
ExReleaseFastMutex( &gSpyDeviceExtensionListLock );
*ReturnLength = retlen;
return STATUS_SUCCESS;
}
VOID
SpyGetLog (
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
This function fills OutputBuffer with as many LOG_RECORDs as possible.
The LOG_RECORDs are variable sizes and are tightly packed in the
OutputBuffer.
Arguments:
OutputBuffer - the user's buffer to fill with the log data we have
collected
OutputBufferLength - the size in bytes of OutputBuffer
IoStatus - is set to the correct return status information for this
operation
Return Value:
None
--*/
{
PLIST_ENTRY pList = NULL;
ULONG length = OutputBufferLength;
PCHAR pOutBuffer = OutputBuffer;
PLOG_RECORD pLogRecord = NULL;
ULONG recordsAvailable = 0;
PRECORD_LIST pRecordList;
KIRQL oldIrql;
IoStatus->Information = 0;
ExAcquireSpinLock(&gOutputBufferLock, &oldIrql);
while (!IsListEmpty( &gOutputBufferList ) && (length > 0)) {
pList = RemoveHeadList( &gOutputBufferList );
pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List );
pLogRecord = &pRecordList->LogRecord;
recordsAvailable++;
// make sure that the filename is always NULL terminated
if (REMAINING_NAME_SPACE(pRecordList) == MAX_NAME_SPACE) {
// We don't have a name, so return an empty string
pLogRecord->Length += sizeof(UNICODE_NULL);
pLogRecord->Name[0] = UNICODE_NULL;
}
pLogRecord->Name[(MAX_NAME_SPACE/sizeof(WCHAR)) - 1] = UNICODE_NULL;
// put it back if we've run out of room
if (length < pLogRecord->Length) {
InsertHeadList( &gOutputBufferList, pList );
break;
}
ExReleaseSpinLock(&gOutputBufferLock, oldIrql);
RtlCopyMemory( pOutBuffer, pLogRecord, pLogRecord->Length );
IoStatus->Information += pLogRecord->Length;
length -= pLogRecord->Length;
pOutBuffer += pLogRecord->Length;
SpyFreeRecord( pRecordList );
ExAcquireSpinLock(&gOutputBufferLock, &oldIrql);
}
ExReleaseSpinLock(&gOutputBufferLock, oldIrql);
// no copies occured
if (length == OutputBufferLength && recordsAvailable > 0) {
IoStatus->Status = STATUS_BUFFER_TOO_SMALL;
}
return;
}
DBGSTATIC
VOID
SpyCloseControlDevice (
)
/*++
Routine Description:
This is the routine that is associated with IRP_MJ_
This routine does the cleanup involved in closing the ControlDevice.
On the close of the Control Device, we need to empty the queue of
logRecords that are waiting to be returned to the user.
Arguments:
None.
Return Value:
None.
--*/
{
PLIST_ENTRY pList;
PRECORD_LIST pRecordList;
KIRQL oldIrql;
//
// Set the gControlDeviceState to CLEANING_UP so that we can
// signal that we are cleaning up the device.
//
ExAcquireSpinLock( &gControlDeviceStateLock, &oldIrql );
gControlDeviceState = CLEANING_UP;
ExReleaseSpinLock( &gControlDeviceStateLock, oldIrql );
ExAcquireSpinLock( &gOutputBufferLock, &oldIrql );
while (!IsListEmpty( &gOutputBufferList )) {
pList = RemoveHeadList( &gOutputBufferList );
ExReleaseSpinLock( &gOutputBufferLock, oldIrql );
pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List );
SpyFreeRecord( pRecordList );
ExAcquireSpinLock( &gOutputBufferLock, &oldIrql );
}
ExReleaseSpinLock( &gOutputBufferLock, oldIrql );
SpyNameDeleteAllNames();
//
// All the cleanup is done, so set the gControlDeviceState
// to CLOSED.
//
ExAcquireSpinLock( &gControlDeviceStateLock, &oldIrql );
gControlDeviceState = CLOSED;
ExReleaseSpinLock( &gControlDeviceStateLock, oldIrql );
}