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;
|
||
}
|
||
|