mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
8.5 KiB
475 lines
8.5 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
object.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT Watchdog driver implementation.
|
|
|
|
Author:
|
|
|
|
Michael Maciesowicz (mmacie) 02-May-2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "wd.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, WdFlushRegistryKey)
|
|
#pragma alloc_text (PAGE, WdInitializeObject)
|
|
#endif
|
|
|
|
|
|
//
|
|
// Exports.
|
|
//
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdCompleteEvent(
|
|
IN PVOID pWatch,
|
|
IN PKTHREAD pThread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function *MUST* be called from client handler for watchdog timeout event
|
|
before exiting. It removes references from watchdog and thread objects.
|
|
It also reenables watchdog event generation for deferred watchdog objects.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pThread - Points to KTHREAD object for spinning thread.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Note: pThread is NULL for recovery events.
|
|
//
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
//
|
|
// Resume event generation for deferred watchdog.
|
|
//
|
|
|
|
if (WdDeferredWatchdog == ((PWATCHDOG_OBJECT)pWatch)->ObjectType)
|
|
{
|
|
InterlockedExchange(&(((PDEFERRED_WATCHDOG)pWatch)->Trigger), 0);
|
|
}
|
|
|
|
//
|
|
// Drop reference counts.
|
|
//
|
|
|
|
if (NULL != pThread)
|
|
{
|
|
ObDereferenceObject(pThread);
|
|
}
|
|
|
|
WdDereferenceObject(pWatch);
|
|
|
|
return;
|
|
} // WdCompleteEvent()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdDereferenceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function decreases reference count of watchdog object.
|
|
If remaining count is zero we will remove object here, since it's
|
|
been freed already.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
ASSERT(pWatchdogObject->ReferenceCount > 0);
|
|
|
|
//
|
|
// Drop reference count and remove the object if fully dereferenced.
|
|
//
|
|
|
|
if (InterlockedDecrement(&(pWatchdogObject->ReferenceCount)) == 0)
|
|
{
|
|
//
|
|
// Object already freed - remove it now.
|
|
//
|
|
|
|
WdRemoveObject(pWatchdogObject);
|
|
}
|
|
|
|
return;
|
|
} // WdDereferenceObject()
|
|
|
|
WATCHDOGAPI
|
|
PDEVICE_OBJECT
|
|
WdGetDeviceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return pointer to device object associated with watchdog object.
|
|
This function increases reference count on DEVICE_OBJECT so the caller must call
|
|
ObDereferenceObject() once DEVICE_OBJECT pointer is not needed any more.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Pointer to DEVICE_OBJECT.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject;
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
ObReferenceObject(pDeviceObject);
|
|
return pDeviceObject;
|
|
} // WdGetDeviceObject()
|
|
|
|
WATCHDOGAPI
|
|
WD_EVENT_TYPE
|
|
WdGetLastEvent(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return last event associated with watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Last event type.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
return ((PWATCHDOG_OBJECT)pWatch)->LastEvent;
|
|
} // WdGetLastEvent()
|
|
|
|
WATCHDOGAPI
|
|
PDEVICE_OBJECT
|
|
WdGetLowestDeviceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return pointer to the lowest (most likely PDO) DEVICE_OBJECT
|
|
associated with watchdog object. This function increases reference count on
|
|
returned DEVICE_OBJECT - the caller must call ObDereferenceObject() once
|
|
DEVICE_OBJECT pointer is not needed any more.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Pointer to DEVICE_OBJECT.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
//
|
|
// Note: No need to bump reference count here, it is always done when
|
|
// watchdog object is created.
|
|
//
|
|
|
|
pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject;
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
//
|
|
// Now get the pointer to the lowest device object in the stack.
|
|
// Note: This call automatically bumps a reference count on returned object.
|
|
//
|
|
|
|
pDeviceObject = IoGetDeviceAttachmentBaseRef(pDeviceObject);
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
return pDeviceObject;
|
|
} // WdGetLowestDeviceObject()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdReferenceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function increases reference count of watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
if (InterlockedIncrement(&(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount)) == 1)
|
|
{
|
|
//
|
|
// Somebody referenced removed object.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check for overflow.
|
|
//
|
|
|
|
ASSERT(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount > 0);
|
|
|
|
return;
|
|
} // WdReferenceObject()
|
|
|
|
//
|
|
// Non-exports.
|
|
//
|
|
|
|
NTSTATUS
|
|
WdFlushRegistryKey(
|
|
IN PVOID pWatch,
|
|
IN PCWSTR pwszKeyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function forces a registry key to be committed to disk.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pwszKeyName - Points to key name string.
|
|
|
|
Return Value:
|
|
|
|
Status code.
|
|
|
|
--*/
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeKeyName;
|
|
HANDLE keyHandle;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
UNREFERENCED_PARAMETER(pWatch);
|
|
ASSERT(NULL != pwszKeyName);
|
|
|
|
RtlInitUnicodeString(&unicodeKeyName, pwszKeyName);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
ntStatus = ZwOpenKey(&keyHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = ZwFlushKey(keyHandle);
|
|
ZwClose(keyHandle);
|
|
}
|
|
|
|
return ntStatus;
|
|
} // WdFlushRegistryKey()
|
|
|
|
VOID
|
|
WdInitializeObject(
|
|
IN PVOID pWatch,
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN WD_OBJECT_TYPE objectType,
|
|
IN WD_TIME_TYPE timeType,
|
|
IN ULONG ulTag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
|
|
|
|
objectType - Type of watchdog object.
|
|
|
|
timeType - Kernel, User, Both thread time to monitor.
|
|
|
|
ulTag - A tag identifying owner.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(NULL != pWatch);
|
|
ASSERT(NULL != pDeviceObject);
|
|
ASSERT((objectType == WdStandardWatchdog) || (objectType == WdDeferredWatchdog));
|
|
ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
//
|
|
// Set initial state of watchdog object.
|
|
//
|
|
|
|
pWatchdogObject->ObjectType = objectType;
|
|
pWatchdogObject->ReferenceCount = 1;
|
|
pWatchdogObject->OwnerTag = ulTag;
|
|
pWatchdogObject->DeviceObject = pDeviceObject;
|
|
pWatchdogObject->TimeType = timeType;
|
|
pWatchdogObject->LastEvent = WdNoEvent;
|
|
pWatchdogObject->LastQueuedThread = NULL;
|
|
|
|
//
|
|
// Bump reference count on device object.
|
|
//
|
|
|
|
ObReferenceObject(pDeviceObject);
|
|
|
|
//
|
|
// Initialize encapsulated KSPIN_LOCK object.
|
|
//
|
|
|
|
KeInitializeSpinLock(&(pWatchdogObject->SpinLock));
|
|
|
|
return;
|
|
} // WdInitializeObject()
|
|
|
|
VOID
|
|
WdRemoveObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function unconditionally removes watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
ASSERT(0 == ((PWATCHDOG_OBJECT)pWatch)->ReferenceCount);
|
|
|
|
//
|
|
// Drop reference count on device object.
|
|
//
|
|
|
|
ObDereferenceObject(((PWATCHDOG_OBJECT)pWatch)->DeviceObject);
|
|
|
|
//
|
|
// We are freeing non-paged pool, it's OK to be at IRQL <= DISPATCH_LEVEL.
|
|
//
|
|
|
|
ExFreePool(pWatch);
|
|
|
|
return;
|
|
} // WdRemoveObject()
|