973 lines
26 KiB
C
973 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
cmnotify.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains support for NtNotifyChangeKey.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bryan M. Willman (bryanwi) 03-Feb-1992
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "cmp.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// "Back Side" of notify
|
|||
|
//
|
|||
|
|
|||
|
extern PCMHIVE CmpMasterHive;
|
|||
|
|
|||
|
VOID
|
|||
|
CmpReportNotifyHelper(
|
|||
|
IN PUNICODE_STRING Name,
|
|||
|
IN PHHIVE SearchHive,
|
|||
|
IN PHHIVE Hive,
|
|||
|
IN PCM_KEY_NODE Node,
|
|||
|
IN ULONG Filter
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE,CmpReportNotify)
|
|||
|
#pragma alloc_text(PAGE,CmpReportNotifyHelper)
|
|||
|
#pragma alloc_text(PAGE,CmpPostNotify)
|
|||
|
#pragma alloc_text(PAGE,CmpPostApc)
|
|||
|
#pragma alloc_text(PAGE,CmpPostApcRunDown)
|
|||
|
#pragma alloc_text(PAGE,CmNotifyRunDown)
|
|||
|
#pragma alloc_text(PAGE,CmpFlushNotify)
|
|||
|
#pragma alloc_text(PAGE,CmpNotifyChangeKey)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpReportNotify(
|
|||
|
UNICODE_STRING Name,
|
|||
|
PHHIVE Hive,
|
|||
|
HCELL_INDEX Cell,
|
|||
|
ULONG Filter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when a notifiable event occurs. It will
|
|||
|
apply CmpReportNotifyHelper to the hive the event occured in,
|
|||
|
and the master hive if different.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Name - canonical path name (as in a key control block) of the key
|
|||
|
at which the event occured. For create or delete this is
|
|||
|
the created or deleted key.
|
|||
|
|
|||
|
WARNING: Name's length field may be edited, though the
|
|||
|
buffer will not be.
|
|||
|
|
|||
|
Hive - pointer to hive containing cell of Key at which event occured.
|
|||
|
|
|||
|
Cell - cell of Key at which event occured
|
|||
|
|
|||
|
(hive and cell correspond with name.)
|
|||
|
|
|||
|
Filter - event to be reported
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_KEY_NODE pcell;
|
|||
|
ULONG flags;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CMLOG(CML_WORKER, CMS_NOTIFY) {
|
|||
|
KdPrint(("CmpReportNotify:\n"));
|
|||
|
KdPrint(("\tName = %wZ\n", &Name));
|
|||
|
KdPrint(("\tHive:%08lx Cell:%08lx Filter:%08lx\n", Hive, Cell, Filter));
|
|||
|
}
|
|||
|
|
|||
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|||
|
//
|
|||
|
// If the operation was create or delete, treat it as a change
|
|||
|
// to the parent.
|
|||
|
//
|
|||
|
if (Filter == REG_NOTIFY_CHANGE_NAME) {
|
|||
|
flags = pcell->Flags;
|
|||
|
Cell = pcell->Parent;
|
|||
|
if (flags & KEY_HIVE_ENTRY) {
|
|||
|
Hive = &(CmpMasterHive->Hive);
|
|||
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|||
|
}
|
|||
|
for ( i = (Name.Length/sizeof(WCHAR))-1;
|
|||
|
Name.Buffer[i] != OBJ_NAME_PATH_SEPARATOR;
|
|||
|
i--
|
|||
|
)
|
|||
|
{}
|
|||
|
ASSERT(i > 1);
|
|||
|
Name.Length = (USHORT)(i * sizeof(WCHAR));
|
|||
|
|
|||
|
//
|
|||
|
// if we're at an exit/link node, back up the real node
|
|||
|
// that MUST be it's parent.
|
|||
|
//
|
|||
|
if (pcell->Flags & KEY_HIVE_EXIT) {
|
|||
|
Cell = pcell->Parent;
|
|||
|
}
|
|||
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Report to notifies waiting on the event's hive
|
|||
|
//
|
|||
|
CmpReportNotifyHelper(&Name, Hive, Hive, pcell, Filter);
|
|||
|
|
|||
|
//
|
|||
|
// If containging hive is not the master hive, apply to master hive
|
|||
|
//
|
|||
|
if (Hive != &(CmpMasterHive->Hive)) {
|
|||
|
CmpReportNotifyHelper(&Name,
|
|||
|
&(CmpMasterHive->Hive),
|
|||
|
Hive,
|
|||
|
pcell,
|
|||
|
Filter);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpReportNotifyHelper(
|
|||
|
IN PUNICODE_STRING Name,
|
|||
|
IN PHHIVE SearchHive,
|
|||
|
IN PHHIVE Hive,
|
|||
|
IN PCM_KEY_NODE Node,
|
|||
|
IN ULONG Filter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Scan the list of active notifies for the specified hive. For
|
|||
|
any with scope including KeyControlBlock and filter matching
|
|||
|
Filter, and with proper security access, post the notify.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Name - canonical path name (as in a key control block) of the key
|
|||
|
at which the event occured. (This is the name for
|
|||
|
reporting purposes.)
|
|||
|
|
|||
|
SearchHive - hive to search for matches (which notify list to check)
|
|||
|
|
|||
|
Hive - Supplies hive containing node to match with.
|
|||
|
|
|||
|
Node - pointer to key to match with (and check access to)
|
|||
|
|
|||
|
Filter - type of event
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY NotifyPtr;
|
|||
|
PCM_NOTIFY_BLOCK NotifyBlock;
|
|||
|
PCMHIVE CmSearchHive;
|
|||
|
PUNICODE_STRING NotifyName;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CmSearchHive = CONTAINING_RECORD(SearchHive, CMHIVE, Hive);
|
|||
|
|
|||
|
NotifyPtr = &(CmSearchHive->NotifyList);
|
|||
|
|
|||
|
while (NotifyPtr->Flink != NULL) {
|
|||
|
NotifyPtr = NotifyPtr->Flink;
|
|||
|
|
|||
|
NotifyBlock = CONTAINING_RECORD(NotifyPtr, CM_NOTIFY_BLOCK, HiveList);
|
|||
|
NotifyName = &(NotifyBlock->KeyControlBlock->FullName);
|
|||
|
|
|||
|
if (NotifyName->Length > Name->Length) {
|
|||
|
//
|
|||
|
// list is length sorted, we're past all shorter entries
|
|||
|
//
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ( (
|
|||
|
(NotifyName->Length == Name->Length) ||
|
|||
|
(Name->Buffer[NotifyName->Length/sizeof(WCHAR)] ==
|
|||
|
OBJ_NAME_PATH_SEPARATOR)
|
|||
|
)
|
|||
|
&&
|
|||
|
( RtlPrefixString((PSTRING)NotifyName, (PSTRING)Name, TRUE) )
|
|||
|
&&
|
|||
|
( NotifyBlock->Filter & Filter )
|
|||
|
&&
|
|||
|
(
|
|||
|
(NotifyBlock->WatchTree == TRUE) ||
|
|||
|
(
|
|||
|
Node == NotifyBlock->KeyControlBlock->KeyNode
|
|||
|
)
|
|||
|
)
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// Name lengths match, or notifyname is proper length for
|
|||
|
// component point prefix (proper prefix) match of name
|
|||
|
// AND
|
|||
|
// Prefix characters actually match
|
|||
|
// AND
|
|||
|
// Filter matches, this event is relevent to this notify
|
|||
|
// AND
|
|||
|
// Either the notify spans the whole subtree, or the cell
|
|||
|
// (key) of interest is the one it applies to
|
|||
|
//
|
|||
|
// THEREFORE: The notify is relevent.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Correct scope, does caller have access?
|
|||
|
//
|
|||
|
if (CmpCheckNotifyAccess(NotifyBlock,Hive,Node)) {
|
|||
|
//
|
|||
|
// Notify block has KEY_NOTIFY access to the node
|
|||
|
// the event occured at. It is relevent. Therefore,
|
|||
|
// it gets to see this event. Post and be done.
|
|||
|
//
|
|||
|
CmpPostNotify(
|
|||
|
NotifyBlock,
|
|||
|
Name,
|
|||
|
Filter,
|
|||
|
STATUS_NOTIFY_ENUM_DIR
|
|||
|
);
|
|||
|
|
|||
|
} // else no KEY_NOTIFY access to node event occured at
|
|||
|
|
|||
|
} // else not relevent (wrong scope, filter, etc)
|
|||
|
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpPostNotify(
|
|||
|
PCM_NOTIFY_BLOCK NotifyBlock,
|
|||
|
PUNICODE_STRING Name OPTIONAL,
|
|||
|
ULONG Filter,
|
|||
|
NTSTATUS Status
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Actually report the notify event by signalling events, enqueing
|
|||
|
APCs, and so forth.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NotifyBlock - pointer to structure that describes the notify
|
|||
|
operation. (Where to post to)
|
|||
|
|
|||
|
Name - name of key at which event occurred.
|
|||
|
|
|||
|
Filter - nature of event
|
|||
|
|
|||
|
Status - completion status to report
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_POST_BLOCK PostBlock;
|
|||
|
|
|||
|
|
|||
|
Filter;
|
|||
|
Name;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CMLOG(CML_MAJOR, CMS_NOTIFY) {
|
|||
|
KdPrint(("CmpPostNotify:\n"));
|
|||
|
KdPrint(("\tNotifyBlock:%08lx ", NotifyBlock));
|
|||
|
KdPrint(("\tName = %wZ\n", Name));
|
|||
|
KdPrint(("\tFilter:%08lx Status=%08lx\n", Filter, Status));
|
|||
|
}
|
|||
|
ASSERT_CM_LOCK_OWNED();
|
|||
|
|
|||
|
if (IsListEmpty(&(NotifyBlock->PostList)) == TRUE) {
|
|||
|
//
|
|||
|
// Nothing to post, set a mark and return
|
|||
|
//
|
|||
|
NotifyBlock->NotifyPending = TRUE;
|
|||
|
return;
|
|||
|
}
|
|||
|
NotifyBlock->NotifyPending = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// IMPLEMENTATION NOTE:
|
|||
|
// If we ever want to actually implement the code that returns
|
|||
|
// names of things that changed, this is the place to add the
|
|||
|
// name and operation type to the buffer.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Pull and post all the entries in the post list
|
|||
|
//
|
|||
|
while (IsListEmpty(&(NotifyBlock->PostList)) == FALSE) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove from the notify block list, and enqueue the apc.
|
|||
|
// The apc will remove itself from the thread list
|
|||
|
//
|
|||
|
PostBlock = (PCM_POST_BLOCK)RemoveHeadList(&(NotifyBlock->PostList));
|
|||
|
PostBlock = CONTAINING_RECORD(PostBlock,
|
|||
|
CM_POST_BLOCK,
|
|||
|
NotifyList);
|
|||
|
|
|||
|
switch (PostBlock->NotifyType) {
|
|||
|
case PostSynchronous:
|
|||
|
//
|
|||
|
// This is a SYNC notify call. There will be no user event,
|
|||
|
// and no user apc routine. Quick exit here, just fill in
|
|||
|
// the Status and poke the event.
|
|||
|
//
|
|||
|
// Holder of the systemevent will wake up and free the
|
|||
|
// postblock. If we free it here, we get a race & bugcheck.
|
|||
|
//
|
|||
|
PostBlock->u.Sync.Status = Status;
|
|||
|
KeSetEvent(PostBlock->u.Sync.SystemEvent,
|
|||
|
0,
|
|||
|
FALSE);
|
|||
|
break;
|
|||
|
|
|||
|
case PostAsyncUser:
|
|||
|
//
|
|||
|
// Insert the APC into the queue
|
|||
|
//
|
|||
|
KeInsertQueueApc(PostBlock->u.AsyncUser.Apc,
|
|||
|
(PVOID)Status,
|
|||
|
(PVOID)PostBlock,
|
|||
|
0);
|
|||
|
break;
|
|||
|
|
|||
|
case PostAsyncKernel:
|
|||
|
//
|
|||
|
// Queue the work item, then free the post block.
|
|||
|
//
|
|||
|
if (PostBlock->u.AsyncKernel.WorkItem != NULL) {
|
|||
|
ExQueueWorkItem(PostBlock->u.AsyncKernel.WorkItem,
|
|||
|
PostBlock->u.AsyncKernel.QueueType);
|
|||
|
}
|
|||
|
//
|
|||
|
// Signal Event if present, and deref it.
|
|||
|
//
|
|||
|
if (PostBlock->u.AsyncKernel.Event != NULL) {
|
|||
|
KeSetEvent(PostBlock->u.AsyncKernel.Event,
|
|||
|
0,
|
|||
|
FALSE);
|
|||
|
ObDereferenceObject(PostBlock->u.AsyncKernel.Event);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// remove the post block from the thread list, and free it
|
|||
|
//
|
|||
|
RemoveEntryList(&(PostBlock->ThreadList));
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpPostApc(
|
|||
|
struct _KAPC *Apc,
|
|||
|
PKNORMAL_ROUTINE *NormalRoutine,
|
|||
|
PVOID *NormalContext,
|
|||
|
PVOID *SystemArgument1,
|
|||
|
PVOID *SystemArgument2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the kernel apc routine. It is called for all notifies,
|
|||
|
regardless of what form of notification the caller requested.
|
|||
|
|
|||
|
We compute the postblock address from the apc object address.
|
|||
|
IoStatus is set. SystemEvent and UserEvent will be signalled
|
|||
|
as appropriate. If the user requested an APC, then NormalRoutine
|
|||
|
will be set at entry and executed when we exit. The PostBlock
|
|||
|
is freed here.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Apc - pointer to apc object
|
|||
|
|
|||
|
NormalRoutine - Will be called when we return
|
|||
|
|
|||
|
NormalContext - will be 1st argument to normal routine, ApcContext
|
|||
|
passed in when NtNotifyChangeKey was called
|
|||
|
|
|||
|
SystemArgument1 - IN: Status value for IoStatusBlock
|
|||
|
OUT: Ptr to IoStatusBlock (2nd arg to user apc routine)
|
|||
|
|
|||
|
SystemArgument2 - Pointer to the PostBlock
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_POST_BLOCK PostBlock;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CMLOG(CML_MAJOR, CMS_NOTIFY) {
|
|||
|
KdPrint(("CmpPostApc:\n"));
|
|||
|
KdPrint(("\tApc:%08lx ", Apc));
|
|||
|
KdPrint(("NormalRoutine:%08lx\n", NormalRoutine));
|
|||
|
KdPrint(("\tNormalContext:%08lx", NormalContext));
|
|||
|
KdPrint(("\tSystemArgument1=IoStatusBlock:%08lx\n", SystemArgument1));
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(KeGetCurrentIrql() >= APC_LEVEL);
|
|||
|
|
|||
|
PostBlock = *(PCM_POST_BLOCK *)SystemArgument2;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in IO Status Block
|
|||
|
//
|
|||
|
// IMPLEMENTATION NOTE:
|
|||
|
// If we ever want to actually implement the code that returns
|
|||
|
// names of things that changed, this is the place to copy the
|
|||
|
// buffer into the caller's buffer.
|
|||
|
//
|
|||
|
try {
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Status = *((ULONG *)SystemArgument1);
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Information = 0L;
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
*SystemArgument1 = PostBlock->u.AsyncUser.IoStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// This is an Async notify, do all work here, including
|
|||
|
// cleaning up the post block
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Signal UserEvent if present, and deref it.
|
|||
|
//
|
|||
|
if (PostBlock->u.AsyncUser.UserEvent != NULL) {
|
|||
|
KeSetEvent(PostBlock->u.AsyncUser.UserEvent,
|
|||
|
0,
|
|||
|
FALSE);
|
|||
|
ObDereferenceObject(PostBlock->u.AsyncUser.UserEvent);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// remove the post block from the thread list, and free it
|
|||
|
//
|
|||
|
RemoveEntryList(&(PostBlock->ThreadList));
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpPostApcRunDown(
|
|||
|
struct _KAPC *Apc
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to clear away apcs in the apc queue
|
|||
|
of a thread that has been terminated.
|
|||
|
|
|||
|
Since the apc is in the apc queue, we know that it is NOT in
|
|||
|
any NotifyBlock's post list. It is, however, in the threads's
|
|||
|
PostBlockList.
|
|||
|
|
|||
|
Therefore, poke any user events so that waiters are not stuck,
|
|||
|
drop the references so the event can be cleaned up, delist the
|
|||
|
PostBlock and free it.
|
|||
|
|
|||
|
Since we are cleaning up the thread, SystemEvents are not interesting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Apc - pointer to apc object
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_POST_BLOCK PostBlock;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CMLOG(CML_MAJOR, CMS_NOTIFY) {
|
|||
|
KdPrint(("CmpApcRunDown:\n"));
|
|||
|
KdPrint(("\tApc:%08lx ", Apc));
|
|||
|
}
|
|||
|
|
|||
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|||
|
|
|||
|
PostBlock = (PCM_POST_BLOCK)Apc->SystemArgument2;
|
|||
|
|
|||
|
//
|
|||
|
// report status and wake up any threads that might otherwise
|
|||
|
// be stuck. also drop any event references we hold
|
|||
|
//
|
|||
|
try {
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Status = STATUS_NOTIFY_CLEANUP;
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Information = 0L;
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
if (PostBlock->u.AsyncUser.UserEvent != NULL) {
|
|||
|
KeSetEvent(
|
|||
|
PostBlock->u.AsyncUser.UserEvent,
|
|||
|
0,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
ObDereferenceObject(PostBlock->u.AsyncUser.UserEvent);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// delist the post block
|
|||
|
//
|
|||
|
RemoveEntryList(&(PostBlock->ThreadList));
|
|||
|
|
|||
|
//
|
|||
|
// Free the post block. Use Ex call because PostBlocks are NOT
|
|||
|
// part of the global registry pool computation, but are instead
|
|||
|
// part of NonPagedPool with Quota.
|
|||
|
//
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup procedure
|
|||
|
//
|
|||
|
VOID
|
|||
|
CmNotifyRunDown(
|
|||
|
PETHREAD Thread
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called from PspExitThread to clean up any pending
|
|||
|
notify requests.
|
|||
|
|
|||
|
It will traverse the thread's PostBlockList, for each PostBlock it
|
|||
|
finds, it will:
|
|||
|
|
|||
|
1. Remove it from the relevent NotifyBlock. This requires
|
|||
|
that we hold the Registry mutex.
|
|||
|
|
|||
|
2. Remove it from the thread's PostBlockList. This requires
|
|||
|
that we run at APC level.
|
|||
|
|
|||
|
3. By the time this procedure runs, user apcs are not interesting
|
|||
|
and neither are SystemEvents, so do not bother processing
|
|||
|
them.
|
|||
|
|
|||
|
UserEvents and IoStatusBlocks could be refered to by other
|
|||
|
threads in the same process, or even a different process,
|
|||
|
so process them so those threads know what happened, use
|
|||
|
status code of STATUS_NOTIFY_CLEANUP.
|
|||
|
|
|||
|
4. Free the post block.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Thread - pointer to the executive thread object for the thread
|
|||
|
we wish to do rundown on.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_POST_BLOCK PostBlock;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if ( IsListEmpty(&(Thread->PostBlockList)) == TRUE ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
CMLOG(CML_API, CMS_NTAPI) {
|
|||
|
KdPrint(("CmNotifyRunDown: ethread:%08lx\n", Thread));
|
|||
|
}
|
|||
|
|
|||
|
CmpLockRegistryExclusive();
|
|||
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|||
|
|
|||
|
while (IsListEmpty(&(Thread->PostBlockList)) == FALSE) {
|
|||
|
|
|||
|
//
|
|||
|
// remove from thread list
|
|||
|
//
|
|||
|
PostBlock = (PCM_POST_BLOCK)RemoveHeadList(&(Thread->PostBlockList));
|
|||
|
PostBlock = CONTAINING_RECORD(
|
|||
|
PostBlock,
|
|||
|
CM_POST_BLOCK,
|
|||
|
ThreadList
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// at this point, CmpReportNotify and friends will no longer
|
|||
|
// attempt to post this post block.
|
|||
|
//
|
|||
|
|
|||
|
if (PostBlock->NotifyType == PostAsyncUser) {
|
|||
|
//
|
|||
|
// report status and wake up any threads that might otherwise
|
|||
|
// be stuck. also drop any event references we hold
|
|||
|
//
|
|||
|
try {
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Status = STATUS_NOTIFY_CLEANUP;
|
|||
|
PostBlock->u.AsyncUser.IoStatusBlock->Information = 0L;
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
CMLOG(CML_API, CMS_EXCEPTION) {
|
|||
|
KdPrint(("!!CmNotifyRundown: code:%08lx\n", GetExceptionCode()));
|
|||
|
}
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
if (PostBlock->u.AsyncUser.UserEvent != NULL) {
|
|||
|
KeSetEvent(
|
|||
|
PostBlock->u.AsyncUser.UserEvent,
|
|||
|
0,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
ObDereferenceObject(PostBlock->u.AsyncUser.UserEvent);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Cancel the APC. Otherwise the rundown routine will also
|
|||
|
// free the post block if the APC happens to be queued at
|
|||
|
// this point. If the APC is queued, then the post block has
|
|||
|
// already been removed from the notify list, so don't remove
|
|||
|
// it again.
|
|||
|
//
|
|||
|
if (!KeRemoveQueueApc(PostBlock->u.AsyncUser.Apc)) {
|
|||
|
|
|||
|
//
|
|||
|
// remove from notify block's list
|
|||
|
//
|
|||
|
RemoveEntryList(&(PostBlock->NotifyList));
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// remove from notify block's list
|
|||
|
//
|
|||
|
RemoveEntryList(&(PostBlock->NotifyList));
|
|||
|
}
|
|||
|
//
|
|||
|
// Free the post block. Use Ex call because PostBlocks are NOT
|
|||
|
// part of the global registry pool computation, but are instead
|
|||
|
// part of NonPagedPool with Quota.
|
|||
|
//
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
}
|
|||
|
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
CmpUnlockRegistry();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CmpFlushNotify(
|
|||
|
PCM_KEY_BODY KeyBody
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Clean up notifyblock when a handle is closed or the key it refers
|
|||
|
to is deleted.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
KeyBody - supplies pointer to key object body for handle we
|
|||
|
are cleaning up.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_NOTIFY_BLOCK NotifyBlock;
|
|||
|
PHHIVE Hive;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
ASSERT_CM_LOCK_OWNED();
|
|||
|
|
|||
|
NotifyBlock = KeyBody->NotifyBlock;
|
|||
|
if (NotifyBlock == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Clean up all PostBlocks waiting on the NotifyBlock
|
|||
|
//
|
|||
|
if (IsListEmpty(&(NotifyBlock->PostList)) == FALSE) {
|
|||
|
CmpPostNotify(
|
|||
|
NotifyBlock,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
STATUS_NOTIFY_CLEANUP
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the subject context
|
|||
|
//
|
|||
|
SeReleaseSubjectContext(&NotifyBlock->SubjectContext);
|
|||
|
|
|||
|
//
|
|||
|
// IMPLEMENTATION NOTE:
|
|||
|
// If we ever do code to report names and types of events,
|
|||
|
// this is the place to free the buffer.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Remove the NotifyBlock from the hive chain
|
|||
|
//
|
|||
|
NotifyBlock->HiveList.Blink->Flink = NotifyBlock->HiveList.Flink;
|
|||
|
if (NotifyBlock->HiveList.Flink != NULL) {
|
|||
|
NotifyBlock->HiveList.Flink->Blink = NotifyBlock->HiveList.Blink;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// decrement the notify count
|
|||
|
//
|
|||
|
Hive = KeyBody->KeyControlBlock->KeyHive;
|
|||
|
|
|||
|
//
|
|||
|
// Free the block, clean up the KeyBody
|
|||
|
//
|
|||
|
CmpFree(NotifyBlock,sizeof(CM_NOTIFY_BLOCK));
|
|||
|
KeyBody->NotifyBlock = NULL;
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// "Front Side" of notify. See also Ntapi.c: ntnotifychangekey
|
|||
|
//
|
|||
|
NTSTATUS
|
|||
|
CmpNotifyChangeKey(
|
|||
|
IN PCM_KEY_BODY KeyBody,
|
|||
|
IN PCM_POST_BLOCK PostBlock,
|
|||
|
IN ULONG CompletionFilter,
|
|||
|
IN BOOLEAN WatchTree,
|
|||
|
IN PVOID Buffer,
|
|||
|
IN ULONG BufferSize
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets up the NotifyBlock, and attaches the PostBlock
|
|||
|
to it. When it returns, the Notify is visible to the system,
|
|||
|
and will receive event reports.
|
|||
|
|
|||
|
If there is already an event report pending, then the notify
|
|||
|
call will be satisified at once.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
KeyBody - pointer to key object that handle refers to, allows access
|
|||
|
to key control block, notify block, etc.
|
|||
|
|
|||
|
PostBlock - pointer to structure that describes how/where the caller
|
|||
|
is to be notified.
|
|||
|
|
|||
|
WARNING: PostBlock must come from Pool, THIS routine
|
|||
|
will keep it, back side will free it. This
|
|||
|
routine WILL free it in case of error.
|
|||
|
|
|||
|
CompletionFilter - what types of events the caller wants to see
|
|||
|
|
|||
|
WatchTree - TRUE to watch whole subtree, FALSE to watch only immediate
|
|||
|
key the notify is applied to
|
|||
|
|
|||
|
Buffer - pointer to area to recieve notify data
|
|||
|
|
|||
|
BufferSize - size of buffer, also size user would like to allocate
|
|||
|
for internal buffer
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCM_NOTIFY_BLOCK NotifyBlock;
|
|||
|
PCM_NOTIFY_BLOCK node;
|
|||
|
PLIST_ENTRY ptr;
|
|||
|
PCMHIVE Hive;
|
|||
|
KIRQL OldIrql;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
CMLOG(CML_WORKER, CMS_NOTIFY) {
|
|||
|
KdPrint(("CmpNotifyChangeKey:\n"));
|
|||
|
KdPrint(("\tKeyBody:%08lx PostBlock:%08lx ", KeyBody, PostBlock));
|
|||
|
KdPrint(("Filter:%08lx WatchTree:%08lx\n", CompletionFilter, WatchTree));
|
|||
|
}
|
|||
|
|
|||
|
CmpLockRegistryExclusive();
|
|||
|
|
|||
|
if (KeyBody->KeyControlBlock->Delete) {
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
CmpUnlockRegistry();
|
|||
|
return STATUS_KEY_DELETED;
|
|||
|
}
|
|||
|
|
|||
|
Hive = (PCMHIVE)KeyBody->KeyControlBlock->KeyHive;
|
|||
|
Hive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|||
|
NotifyBlock = KeyBody->NotifyBlock;
|
|||
|
|
|||
|
if (NotifyBlock == NULL) {
|
|||
|
//
|
|||
|
// Set up new notify session
|
|||
|
//
|
|||
|
NotifyBlock = CmpAllocateTag(sizeof(CM_NOTIFY_BLOCK),FALSE,CM_NOTIFYBLOCK_TAG);
|
|||
|
CMLOG(CML_MINOR, CMS_POOL) {
|
|||
|
KdPrint(("**CmpNotifyChangeKey: allocate:%08lx, ", sizeof(CM_NOTIFY_BLOCK)));
|
|||
|
KdPrint(("type:%d, at:%08lx\n", PagedPool, NotifyBlock));
|
|||
|
}
|
|||
|
|
|||
|
if (NotifyBlock == NULL) {
|
|||
|
CmpFreePostBlock(PostBlock);
|
|||
|
CmpUnlockRegistry();
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
NotifyBlock->KeyControlBlock = KeyBody->KeyControlBlock;
|
|||
|
NotifyBlock->Filter = CompletionFilter;
|
|||
|
NotifyBlock->WatchTree = WatchTree;
|
|||
|
NotifyBlock->NotifyPending = FALSE;
|
|||
|
InitializeListHead(&(NotifyBlock->PostList));
|
|||
|
KeyBody->NotifyBlock = NotifyBlock;
|
|||
|
NotifyBlock->KeyBody = KeyBody;
|
|||
|
|
|||
|
//
|
|||
|
// IMPLEMENTATION NOTE:
|
|||
|
// If we ever want to actually return the buffers full of
|
|||
|
// data, the buffer should be allocated and its address
|
|||
|
// stored in the notify block here.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Capture the subject context so we can do checking once the
|
|||
|
// notify goes off.
|
|||
|
//
|
|||
|
SeCaptureSubjectContext(&NotifyBlock->SubjectContext);
|
|||
|
|
|||
|
//
|
|||
|
// Attach notify block to hive in properly sorted order
|
|||
|
//
|
|||
|
ptr = &(Hive->NotifyList);
|
|||
|
while (TRUE) {
|
|||
|
if (ptr->Flink == NULL) {
|
|||
|
//
|
|||
|
// End of list, add self after ptr.
|
|||
|
//
|
|||
|
ptr->Flink = &(NotifyBlock->HiveList);
|
|||
|
NotifyBlock->HiveList.Flink = NULL;
|
|||
|
NotifyBlock->HiveList.Blink = ptr;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ptr = ptr->Flink;
|
|||
|
|
|||
|
node = CONTAINING_RECORD(ptr, CM_NOTIFY_BLOCK, HiveList);
|
|||
|
if (node->KeyControlBlock->FullName.Length >
|
|||
|
KeyBody->KeyControlBlock->FullName.Length)
|
|||
|
{
|
|||
|
//
|
|||
|
// ptr -> notify with longer name than us, insert in FRONT
|
|||
|
//
|
|||
|
NotifyBlock->HiveList.Flink = ptr;
|
|||
|
ptr->Blink->Flink = &(NotifyBlock->HiveList);
|
|||
|
NotifyBlock->HiveList.Blink = ptr->Blink;
|
|||
|
ptr->Blink = &(NotifyBlock->HiveList);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Add post block to front of notify block's list, and add it to thread list.
|
|||
|
//
|
|||
|
InsertHeadList(
|
|||
|
&(NotifyBlock->PostList),
|
|||
|
&(PostBlock->NotifyList)
|
|||
|
);
|
|||
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|||
|
InsertHeadList(
|
|||
|
&(PsGetCurrentThread()->PostBlockList),
|
|||
|
&(PostBlock->ThreadList)
|
|||
|
);
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// If there is a notify pending (will not be if we just created
|
|||
|
// the notify block) then post it at once. Note that this call
|
|||
|
// ALWAYS returns STATUS_PENDING unless it fails. Caller must
|
|||
|
// ALWAYS look in IoStatusBlock to see what happened.
|
|||
|
//
|
|||
|
if (NotifyBlock->NotifyPending == TRUE) {
|
|||
|
CmpPostNotify(
|
|||
|
NotifyBlock,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
STATUS_NOTIFY_ENUM_DIR
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
CmpUnlockRegistry();
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|