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.
2980 lines
67 KiB
2980 lines
67 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Regnckey.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the Win32 Registry APIs to notify a caller about
|
|
a changed Key value. That is:
|
|
|
|
- RegNotifyChangeKey
|
|
|
|
Author:
|
|
|
|
David J. Gilman (davegi) 10-Feb-1992
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
The RegNotifyChangeKey server creates an event and calls
|
|
NtNotifyChangeKey asynchronously with that event. It then
|
|
places the event (plus some other client information, such
|
|
as a named pipe and a client event) in a "Notification List"
|
|
and returns to the client.
|
|
|
|
A Notification List is a list of events controled by a
|
|
handler thread. The handler thread waits on the events in
|
|
the list. When an event is signaled the handler thread
|
|
identifies the client to which the event belongs, and
|
|
gives the client (via named pipe) the corresponding client
|
|
event.
|
|
|
|
Since there is a limit on the number of events on which a
|
|
thread can wait, there may be several Notification Lists.
|
|
|
|
Since all the calls to RegNotifyChangeKey in a client
|
|
process use the same named pipe, we maintain only one copy
|
|
of each pipe. Pipe information is maintained in a symbol table
|
|
for fast lookup.
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
02-Apr-1992 Ramon J. San Andres (ramonsa)
|
|
Changed to use RPC.
|
|
|
|
|
|
--*/
|
|
|
|
#include <rpc.h>
|
|
#include "regrpc.h"
|
|
#include "localreg.h"
|
|
#include <string.h>
|
|
|
|
|
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|
//
|
|
// Strings used for generating named pipe names
|
|
//
|
|
#define NAMED_PIPE_HERE L"\\Device\\NamedPipe\\"
|
|
#define NAMED_PIPE_THERE L"\\DosDevices\\UNC\\"
|
|
|
|
|
|
//
|
|
// Pipe names are maintained in a symbol table. The symbol table has
|
|
// one entry for each different pipe given by a client. The entry
|
|
// is maintained for as long as there is at least one entry in a
|
|
// Notification List referencing it.
|
|
//
|
|
typedef struct _PIPE_ENTRY *PPIPE_ENTRY;
|
|
|
|
typedef struct _PIPE_ENTRY {
|
|
|
|
PPIPE_ENTRY Previous;
|
|
PPIPE_ENTRY Next;
|
|
UNICODE_STRING PipeName;
|
|
DWORD ReferenceCount;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
|
|
} PIPE_ENTRY;
|
|
|
|
|
|
|
|
|
|
//
|
|
// The PIPE_SYMBOL_TABLE structure contains the symbol table for
|
|
// all the pipes being used by the clients of
|
|
// RegNotifyChangeKey
|
|
//
|
|
#define BUCKETS_IN_SYMBOL_TABLE 211
|
|
|
|
typedef struct _PIPE_SYMBOL_TABLE *PPIPE_SYMBOL_TABLE;
|
|
|
|
typedef struct _PIPE_SYMBOL_TABLE {
|
|
|
|
PPIPE_ENTRY Bucket[ BUCKETS_IN_SYMBOL_TABLE ];
|
|
|
|
} PIPE_SYMBOL_TABLE;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Information about a pending event is maintained in a
|
|
// NOTIFICATION_ENTRY structure.
|
|
//
|
|
typedef struct _NOTIFICATION_ENTRY *PNOTIFICATION_ENTRY;
|
|
|
|
typedef struct _NOTIFICATION_ENTRY {
|
|
|
|
DWORD ClientEvent; // Event in client side
|
|
HANDLE hKey; // Key handle
|
|
DWORD Flags; // Misc. flags
|
|
PPIPE_ENTRY PipeEntry; // Pipe Entry
|
|
|
|
} NOTIFICATION_ENTRY;
|
|
|
|
|
|
//
|
|
// Flag values
|
|
//
|
|
#define CLIENT_IS_DEAD 0x00000001
|
|
#define MUST_NOTIFY 0x00000002
|
|
#define NOTIFICATION_FAILED 0x00000004
|
|
|
|
|
|
|
|
//
|
|
// The pending events are maintained in notification lists. Each
|
|
// notification list contains:
|
|
//
|
|
// Previous - Previous in chain
|
|
// Next - Next in chain
|
|
// EventsInUse - Number of entries being used in this list
|
|
// EventHandle - Array of event handles
|
|
// ClientEvent - Array of events in client
|
|
// PipeEntry - Array of pointers to pipe entries in symbol table
|
|
//
|
|
//
|
|
// The first event in the EventHandle list is the event used to wake
|
|
// up the thread whenever we add new entries to the list.
|
|
//
|
|
// The array entries 0..EventsInUse-1 contain the pending events.
|
|
// New events are always added at position EventsInUse. When removing
|
|
// an event, all the arrays are shifted.
|
|
//
|
|
// Whenever EventsInUse == 1, the list is empty of client events and
|
|
// it can be removed (together with its thread).
|
|
//
|
|
//
|
|
// Notification Lists are kept in a doubly-linked list. A new
|
|
// Notification List is created and added to the chain whenever an
|
|
// event is added and all the existing lists are full. Notification
|
|
// lists are deleted when the last event in the list is signaled.
|
|
//
|
|
//
|
|
typedef struct _NOTIFICATION_LIST *PNOTIFICATION_LIST;
|
|
|
|
typedef struct _NOTIFICATION_LIST {
|
|
|
|
PNOTIFICATION_LIST Previous;
|
|
PNOTIFICATION_LIST Next;
|
|
DWORD EventsInUse;
|
|
HANDLE HandlerThread;
|
|
CLIENT_ID HandlerClientId;
|
|
DWORD PendingNotifications;
|
|
HANDLE EventHandle[ MAXIMUM_WAIT_OBJECTS ];
|
|
NOTIFICATION_ENTRY Event[ MAXIMUM_WAIT_OBJECTS ];
|
|
DWORD TimeOutCount;
|
|
BOOLEAN ResetCount;
|
|
|
|
} NOTIFICATION_LIST;
|
|
|
|
#define MAX_TIMEOUT_COUNT 128
|
|
|
|
|
|
#if DBG
|
|
#define BIGDBG 0
|
|
#else
|
|
#define BIGDBG 0
|
|
#endif
|
|
|
|
#define HASH(a,b) Hash(a,b)
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Static Variables
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
|
|
//
|
|
// Head of chain of Notification lists
|
|
//
|
|
PNOTIFICATION_LIST NotificationListChainHead;
|
|
|
|
//
|
|
// The critical sesction protects all the global structures.
|
|
//
|
|
RTL_CRITICAL_SECTION NotificationCriticalSection;
|
|
|
|
//
|
|
// Symbol table for named pipes in use.
|
|
//
|
|
PIPE_SYMBOL_TABLE PipeSymbolTable;
|
|
|
|
//
|
|
// Our machine name is used for determining if requests are local
|
|
// or remote.
|
|
//
|
|
WCHAR OurMachineNameBuffer[ MAX_PATH ];
|
|
UNICODE_STRING OurMachineName;
|
|
|
|
//
|
|
// The I/O Status Block is updated by the NtNotifyChangeKey API
|
|
// upon notification. We cannot put this structure on the stack
|
|
// because at notification time this stack might belong to someone
|
|
// else. We can use a single variable because we don't care about
|
|
// its contents so it's ok if several people mess with it at the
|
|
// same time.
|
|
//
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Local Prototypes
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
|
|
LONG
|
|
CreateNotificationList (
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
);
|
|
|
|
LONG
|
|
DeleteNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
LONG
|
|
AddEvent (
|
|
IN HKEY hKey,
|
|
IN HANDLE EventHandle,
|
|
IN DWORD ClientEvent,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
);
|
|
|
|
LONG
|
|
RemoveEvent (
|
|
IN HANDLE EventHandle,
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
LONG
|
|
GetAvailableNotificationList (
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
);
|
|
|
|
LONG
|
|
AddEntryToNotificationList(
|
|
IN OUT PNOTIFICATION_LIST NotificationList,
|
|
IN HKEY hKey,
|
|
IN HANDLE EventHandle,
|
|
IN DWORD ClientEvent,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL
|
|
);
|
|
|
|
LONG
|
|
RemoveEntryFromNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList,
|
|
IN DWORD EntryIndex
|
|
);
|
|
|
|
LONG
|
|
CompactNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
VOID
|
|
AddNotificationListToChain(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
VOID
|
|
RemoveNotificationListFromChain(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
LONG
|
|
GetFullPipeName(
|
|
IN PUNICODE_STRING MachineName,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN OUT PUNICODE_STRING FullPipeName
|
|
);
|
|
|
|
LONG
|
|
CreatePipeEntry (
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
);
|
|
|
|
LONG
|
|
DeletePipeEntry(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
);
|
|
|
|
LONG
|
|
AddPipe(
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
);
|
|
|
|
LONG
|
|
RemovePipe(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
);
|
|
|
|
LONG
|
|
AddPipeEntryToSymbolTable(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
);
|
|
|
|
LONG
|
|
RemovePipeEntryFromSymbolTable(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
);
|
|
|
|
LONG
|
|
LookForPipeEntryInSymbolTable(
|
|
IN PUNICODE_STRING PipeName,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
);
|
|
|
|
DWORD
|
|
Hash(
|
|
IN PUNICODE_STRING Symbol,
|
|
IN DWORD Buckets
|
|
);
|
|
|
|
VOID
|
|
NotificationHandler(
|
|
IN PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
DWORD
|
|
NotificationListMaintenance(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
);
|
|
|
|
LONG
|
|
SendEventToClient(
|
|
IN DWORD ClientEvent,
|
|
IN PPIPE_ENTRY PipeEntry
|
|
);
|
|
|
|
#if BIGDBG
|
|
VOID
|
|
DumpNotificationLists(
|
|
);
|
|
|
|
VOID
|
|
DumpPipeTable(
|
|
);
|
|
|
|
#endif
|
|
|
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// BaseRegNotifyChangeKeyValue
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
InitializeRegNotifyChangeKeyValue(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Initializes the static data structures used by the
|
|
RegNotifyChangeKeyValue server. Called once at program
|
|
initialization.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if successful.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef REMOTE_NOTIFICATION_DISABLED
|
|
|
|
return( TRUE );
|
|
|
|
#else // REMOTE_NOTIFICATION_DISABLED
|
|
|
|
NTSTATUS NtStatus;
|
|
DWORD Bucket;
|
|
DWORD MachineNameLength;
|
|
|
|
|
|
NotificationListChainHead = NULL;
|
|
|
|
//
|
|
// Determine our machine name
|
|
//
|
|
MachineNameLength = MAX_PATH;
|
|
if ( !GetComputerNameW( OurMachineNameBuffer, &MachineNameLength ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
OurMachineName.Buffer = OurMachineNameBuffer;
|
|
OurMachineName.Length = (USHORT)(MachineNameLength * sizeof(WCHAR));
|
|
OurMachineName.MaximumLength = (USHORT)(MAX_PATH * sizeof(WCHAR));
|
|
|
|
|
|
//
|
|
// Initialize Notification critical section
|
|
//
|
|
NtStatus = RtlInitializeCriticalSection(
|
|
&NotificationCriticalSection
|
|
);
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize the pipe symbol table
|
|
//
|
|
for ( Bucket = 0; Bucket < BUCKETS_IN_SYMBOL_TABLE; Bucket++ ) {
|
|
PipeSymbolTable.Bucket[Bucket] = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|
}
|
|
|
|
|
|
|
|
|
|
error_status_t
|
|
BaseRegNotifyChangeKeyValue(
|
|
IN HKEY hKey,
|
|
IN BOOLEAN fWatchSubtree,
|
|
IN DWORD dwNotifyFilter,
|
|
IN DWORD hEvent,
|
|
IN PUNICODE_STRING MachineName,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API is used to watch a key or sub-tree for changes. It is
|
|
asynchronous. It is possible to filter the criteria by which the
|
|
notification occurs.
|
|
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies a handle to a key that has been previously opened with
|
|
KEY_NOTIFY access.
|
|
|
|
fWatchSubtree - Supplies a boolean value that if TRUE causes the
|
|
system to monitor the key and all of its decsendants. A value of
|
|
FALSE causes the system to monitor only the specified key.
|
|
|
|
dwNotifyFilter - Supplies a set of flags that specify the filter
|
|
conditions the system uses to satisfy a change notification.
|
|
|
|
REG_NOTIFY_CHANGE_KEYNAME - Any key name changes that occur
|
|
in a key or subtree being watched will satisfy a
|
|
change notification wait. This includes creations
|
|
and deletions.
|
|
|
|
REG_NOTIFY_CHANGE_ATTRIBUTES - Any attribute changes that occur
|
|
in a key or subtree being watched will satisfy a
|
|
change notification.
|
|
|
|
REG_NOTIFY_CHANGE_LAST_WRITE - Any last write time changes that
|
|
occur in a key or subtree being watched will satisfy a
|
|
change notification.
|
|
|
|
REG_NOTIFY_CHANGE_SECURITY - Any security descriptor changes
|
|
that occur in a key or subtree being watched will
|
|
satisfy a change notification.
|
|
|
|
|
|
hEvent - Supplies a DWORD which represents an event that will have to
|
|
be communicated to the client (via named pipe) when a key
|
|
has to be notified.
|
|
|
|
|
|
PipeName - Supplies the name of the pipe used for communicating
|
|
the notification to the client.
|
|
|
|
pRpcSa - Supplies the optional security attributes of the named
|
|
pipe.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef REMOTE_NOTIFICATION_DISABLED
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
#else // REMOTE_NOTIFICATION_DISABLED
|
|
|
|
NTSTATUS NtStatus;
|
|
HANDLE EventHandle;
|
|
PNOTIFICATION_LIST NotificationList;
|
|
LONG Error;
|
|
UNICODE_STRING FullPipeName;
|
|
|
|
|
|
RPC_IMPERSONATE_CLIENT( NULL );
|
|
|
|
//
|
|
// Enter the critical section
|
|
//
|
|
NtStatus = RtlEnterCriticalSection( &NotificationCriticalSection );
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
return (error_status_t)RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
|
|
try {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: RegNotify entered\n" );
|
|
|
|
//DbgPrint( "WINREG: Notification requested. HKEY 0x%x, Client 0x%x, pipe %wZ\n",
|
|
// hKey, hEvent, PipeName );
|
|
//DbgPrint( " Watch subtree: 0x%x, filter 0x%x\n", fWatchSubtree, dwNotifyFilter );
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// Subtract the NULL from the Length of all the strings.
|
|
// This was added by the client so that RPC would transmit
|
|
// the whole thing.
|
|
//
|
|
if ( MachineName->Length > 0 ) {
|
|
MachineName->Length -= sizeof(UNICODE_NULL );
|
|
}
|
|
if ( PipeName->Length > 0 ) {
|
|
PipeName->Length -= sizeof(UNICODE_NULL );
|
|
}
|
|
|
|
//
|
|
// Construct the full pipe name based on the machine name
|
|
// and the pipe name given.
|
|
//
|
|
FullPipeName.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
MAX_PATH * sizeof(WCHAR)
|
|
);
|
|
|
|
if ( !FullPipeName.Buffer ) {
|
|
|
|
Error = ERROR_OUTOFMEMORY;
|
|
|
|
} else {
|
|
|
|
|
|
FullPipeName.Length = 0;
|
|
FullPipeName.MaximumLength = MAX_PATH * sizeof(WCHAR);
|
|
|
|
|
|
Error = GetFullPipeName(
|
|
MachineName,
|
|
PipeName,
|
|
&FullPipeName
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Create an event on which we will wait for completion of
|
|
// the API.
|
|
//
|
|
NtStatus = NtCreateEvent(
|
|
&EventHandle,
|
|
(ACCESS_MASK)EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|
|
|
//
|
|
// Add the event to a Notification List
|
|
//
|
|
Error = AddEvent(
|
|
hKey,
|
|
EventHandle,
|
|
hEvent,
|
|
&FullPipeName,
|
|
pRpcSa,
|
|
&NotificationList
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Call the NT API
|
|
//
|
|
NtStatus = NtNotifyChangeKey(
|
|
hKey,
|
|
EventHandle,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
dwNotifyFilter,
|
|
( BOOLEAN ) fWatchSubtree,
|
|
NULL,
|
|
0,
|
|
TRUE
|
|
);
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ||
|
|
(NtStatus == STATUS_PENDING) ) {
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Could not request notification, remove the
|
|
// event from the notification list.
|
|
//
|
|
Error = RemoveEvent(
|
|
EventHandle,
|
|
NotificationList
|
|
);
|
|
|
|
ASSERT( Error == ERROR_SUCCESS );
|
|
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Could not add the event to any notification
|
|
// list.
|
|
//
|
|
NtStatus = NtClose( EventHandle );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
}
|
|
|
|
} else {
|
|
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
}
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
FullPipeName.Buffer
|
|
);
|
|
}
|
|
|
|
} except ( NtStatus = GetExceptionCode() ) {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG Error: Exception %x in BaseRegNotifyChangeKeyValue\n",
|
|
NtStatus );
|
|
DbgBreakPoint();
|
|
#endif
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: RegNotify left\n" );
|
|
#endif
|
|
|
|
NtStatus = RtlLeaveCriticalSection( &NotificationCriticalSection );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
RPC_REVERT_TO_SELF();
|
|
return (error_status_t)Error;
|
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
CleanDeadClientInfo(
|
|
HKEY hKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When a client dies, this function searches the notification lists to
|
|
see if we the client has some pending notifications. We flag the
|
|
entries in the notification lists and signal the events so that
|
|
the notification handler can get rid of these orphans.
|
|
|
|
Arguments:
|
|
|
|
hKey - Client's hKey
|
|
|
|
Return Value:
|
|
|
|
BOOL - Returns TRUE unless something REALLY weird happened.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef REMOTE_NOTIFICATION_DISABLED
|
|
|
|
return( TRUE );
|
|
|
|
#else // REMOTE_NOTIFICATION_DISABLED
|
|
|
|
NTSTATUS NtStatus;
|
|
PNOTIFICATION_LIST NotificationList;
|
|
PNOTIFICATION_ENTRY Event;
|
|
DWORD Index;
|
|
BOOL Ok = TRUE;
|
|
BOOL FoundDeadClients;
|
|
|
|
//
|
|
// Enter the critical section
|
|
//
|
|
NtStatus = RtlEnterCriticalSection( &NotificationCriticalSection );
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Dead client, hKey 0x%x\n", hKey );
|
|
#endif
|
|
|
|
try {
|
|
|
|
//
|
|
// Traverse all the lists looking for orphans.
|
|
//
|
|
for ( NotificationList = NotificationListChainHead;
|
|
NotificationList;
|
|
NotificationList = NotificationList->Next ) {
|
|
|
|
FoundDeadClients = FALSE;
|
|
Event = NotificationList->Event;
|
|
|
|
//
|
|
// Examine all the entries of the list to see if any
|
|
// entry is an orphan.
|
|
//
|
|
for ( Index = 1;
|
|
Index < NotificationList->EventsInUse;
|
|
Index++ ) {
|
|
|
|
//
|
|
// If this entry is an orphan, flag it as such and
|
|
// signal the event so that the notification handler
|
|
// can clean it up.
|
|
//
|
|
if ( Event->hKey == hKey ) {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Found notification orphan, hKey 0x%x Client 0x%x\n",
|
|
hKey, Event->ClientEvent );
|
|
#endif
|
|
Event->Flags |= CLIENT_IS_DEAD;
|
|
|
|
FoundDeadClients = TRUE;
|
|
}
|
|
|
|
Event++;
|
|
}
|
|
|
|
if ( FoundDeadClients ) {
|
|
NtStatus = NtSetEvent( NotificationList->EventHandle[0], NULL );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
}
|
|
}
|
|
|
|
} except ( NtStatus = GetExceptionCode() ) {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG Error: Exception %x in CleanDeadClientInfo\n",
|
|
NtStatus );
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
Ok = FALSE;
|
|
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Dead client left\n" );
|
|
#endif
|
|
|
|
NtStatus = RtlLeaveCriticalSection( &NotificationCriticalSection );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
return Ok;
|
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|
}
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Notification List funcions
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
|
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|
|
|
LONG
|
|
CreateNotificationList (
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new Notification List and its handler thread.
|
|
|
|
Arguments:
|
|
|
|
NotificationListUsed - Supplies pointer to pointer to Notification List
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PNOTIFICATION_LIST NotificationList;
|
|
DWORD Index;
|
|
NTSTATUS NtStatus;
|
|
LONG Error;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Creating new notification list\n" );
|
|
#endif
|
|
|
|
//
|
|
// Allocate memory for the new Notification List
|
|
//
|
|
NotificationList = RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
sizeof( NOTIFICATION_LIST )
|
|
);
|
|
|
|
if ( !NotificationList ) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the "Wake up" event handle, which is used to wake
|
|
// up the handler thread whenever new events are added to
|
|
// the notification list.
|
|
//
|
|
NtStatus = NtCreateEvent(
|
|
&(NotificationList->EventHandle[0] ),
|
|
(ACCESS_MASK)EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE
|
|
);
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|
|
|
//
|
|
// Mark rest of entries as "available"
|
|
//
|
|
for ( Index = 1; Index < MAXIMUM_WAIT_OBJECTS; Index++ ) {
|
|
NotificationList->EventHandle[Index] = NULL;
|
|
}
|
|
|
|
//
|
|
// Set initial number of EventInUse to 1 (which is the
|
|
// index to the next available spot in the list).
|
|
//
|
|
NotificationList->EventsInUse = 1;
|
|
|
|
//
|
|
// Set chain links
|
|
//
|
|
NotificationList->Previous = NULL;
|
|
NotificationList->Next = NULL;
|
|
|
|
NotificationList->PendingNotifications = 0;
|
|
|
|
//
|
|
// Now that everything has been initialized, create the
|
|
// handler thread for the list.
|
|
//
|
|
NotificationList->HandlerThread =
|
|
CreateThread(
|
|
NULL,
|
|
(32 * 1024),
|
|
(LPTHREAD_START_ROUTINE)NotificationHandler,
|
|
NotificationList,
|
|
0,
|
|
(LPDWORD)&(NotificationList->HandlerClientId)
|
|
);
|
|
|
|
if ( NotificationList->HandlerThread != NULL ) {
|
|
|
|
*NotificationListUsed = NotificationList;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Could not create thread, close the event that we just
|
|
// created.
|
|
//
|
|
Error = GetLastError();
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG Error: Cannot create notification thread, error %d\n",
|
|
Error );
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
NtStatus = NtClose( NotificationList->EventHandle[0] );
|
|
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
}
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG Error: Cannot create notification event, status 0x%x\n",
|
|
NtStatus );
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
|
|
//
|
|
// If something went wrong, free up the notification list
|
|
//
|
|
if ( Error != ERROR_SUCCESS ) {
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
NotificationList
|
|
);
|
|
*NotificationListUsed = NULL;
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
DeleteNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes a Notification List. The handler thread is not terminated
|
|
because it is the handler thread who deletes notification lists,
|
|
commiting suicide afterwards.
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies pointer to Notification List
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS NtStatus;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Removing empty notification list\n" );
|
|
#endif
|
|
|
|
//
|
|
// The only event in the list must be the "wakeup" event
|
|
//
|
|
ASSERT( NotificationList->EventsInUse == 1 );
|
|
|
|
//
|
|
// Delete the "wake up" event
|
|
//
|
|
NtStatus = NtClose( NotificationList->EventHandle[0] );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
//
|
|
// Free up the heap used by the Notification List
|
|
//
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
NotificationList
|
|
);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
AddEvent (
|
|
IN HKEY hKey,
|
|
IN HANDLE EventHandle,
|
|
IN DWORD ClientEvent,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds an event to the first notification list with an available slot.
|
|
|
|
If no notification list has an available slot, a new notification list
|
|
(an its handler thread) is created.
|
|
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies registry key handle
|
|
|
|
EventHandle - Supplies an event on which the handler thread of the
|
|
Notification List has to wait.
|
|
|
|
ClientEvent - Supplies the event which has to be communicated to the
|
|
client when out EventHandle is signaled. This event is
|
|
communicated to the client via named pipe.
|
|
|
|
PipeNameU - Supplies the name of the pipe for communicating with the
|
|
client.
|
|
|
|
pRpcSa - Supplies the optional security attributes of the named
|
|
pipe.
|
|
|
|
NotificationListused - Supplies a pointer where the address of the
|
|
Notification List in which the event is put
|
|
is placed.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOTIFICATION_LIST NotificationList;
|
|
LONG Error;
|
|
NTSTATUS NtStatus;
|
|
|
|
ASSERT( EventHandle != NULL );
|
|
ASSERT( PipeName && PipeName->Buffer );
|
|
ASSERT( NotificationListUsed );
|
|
|
|
|
|
//
|
|
// Get a Notification List with an available entry.
|
|
//
|
|
Error = GetAvailableNotificationList(
|
|
&NotificationList
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Add the entry
|
|
//
|
|
Error = AddEntryToNotificationList(
|
|
NotificationList,
|
|
hKey,
|
|
EventHandle,
|
|
ClientEvent,
|
|
PipeName,
|
|
pRpcSa
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// A new entry has been added, we have to wake up the
|
|
// handler thread so that it will wait on the newly added
|
|
// event.
|
|
//
|
|
NtStatus = NtSetEvent( NotificationList->EventHandle[0], NULL );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
*NotificationListUsed = NotificationList;
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG: Could not add notification entry! Error %d\n ", Error);
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG: Could not get a notification list! Error %d\n ", Error);
|
|
#endif
|
|
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
RemoveEvent (
|
|
IN HANDLE EventHandle,
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes an event from the notification list. The caller must
|
|
make sure that the event handle given does live in the
|
|
Notification List specified.
|
|
|
|
This function is called if the notification is aborted for some
|
|
reason (e.g. the NT notification API fails).
|
|
|
|
Arguments:
|
|
|
|
EventHandle - Supplies the event to remove.
|
|
|
|
NotificationList - Supplies the Notification List in which
|
|
the event lives.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG Error;
|
|
DWORD EntryIndex;
|
|
|
|
//
|
|
// Search for the entry that we have to remove.
|
|
//
|
|
for ( EntryIndex = 1;
|
|
EntryIndex < NotificationList->EventsInUse;
|
|
EntryIndex++ ) {
|
|
|
|
if ( EventHandle == NotificationList->EventHandle[ EntryIndex ] ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT( EntryIndex < NotificationList->EventsInUse );
|
|
|
|
if ( EntryIndex < NotificationList->EventsInUse ) {
|
|
|
|
//
|
|
// Found entry, remove it
|
|
//
|
|
Error = RemoveEntryFromNotificationList(
|
|
NotificationList,
|
|
EntryIndex
|
|
);
|
|
|
|
//
|
|
// Note that we are leaving a hole in the Notification list,
|
|
// the handler will eventually compact it.
|
|
//
|
|
|
|
} else {
|
|
|
|
Error = ERROR_ARENA_TRASHED;
|
|
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
GetAvailableNotificationList (
|
|
OUT PNOTIFICATION_LIST *NotificationListUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets a Notification List with an available entry.
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies pointer to where the Notification
|
|
List pointer will be placed.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
{
|
|
LONG Error = ERROR_SUCCESS;
|
|
PNOTIFICATION_LIST NotificationList;
|
|
|
|
//
|
|
// Traverse the chain of Notification lists until we find a Notification
|
|
// list with an available entry.
|
|
//
|
|
for ( NotificationList = NotificationListChainHead;
|
|
NotificationList && NotificationList->EventsInUse >= MAXIMUM_WAIT_OBJECTS;
|
|
NotificationList = NotificationList->Next );
|
|
|
|
|
|
//
|
|
// If we did not find a Notification List with an available spot,
|
|
// create a new Notification List and add it to the chain.
|
|
//
|
|
if ( !NotificationList ) {
|
|
|
|
Error = CreateNotificationList( &NotificationList );
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
ASSERT( NotificationList );
|
|
|
|
AddNotificationListToChain( NotificationList );
|
|
}
|
|
}
|
|
|
|
*NotificationListUsed = NotificationList;
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
AddEntryToNotificationList(
|
|
IN OUT PNOTIFICATION_LIST NotificationList,
|
|
IN HKEY hKey,
|
|
IN HANDLE EventHandle,
|
|
IN DWORD ClientEvent,
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds an entry to a notification list.
|
|
|
|
Calls to this function must be protected by the critical
|
|
section of the Notification List.
|
|
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies pointer to Notification List
|
|
|
|
hKey - Supplies registry key handle
|
|
|
|
EventHandle - Supplies the event handle
|
|
|
|
ClientEvent - Supplies the client's event
|
|
|
|
PipeName - Supplies name of pipe.
|
|
|
|
pRpcSa - Supplies security attributes for the pipe
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
{
|
|
LONG Error;
|
|
PPIPE_ENTRY PipeEntry;
|
|
DWORD Index;
|
|
PNOTIFICATION_ENTRY Event;
|
|
|
|
|
|
//
|
|
// Add the pipe information to the pipe symbol table
|
|
//
|
|
Error = AddPipe( PipeName, pRpcSa, &PipeEntry );
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Add the event in the next available spot in the list,
|
|
// and increment the number of events in use by the
|
|
// list.
|
|
//
|
|
Index = NotificationList->EventsInUse++;
|
|
|
|
Event = &(NotificationList->Event[ Index ]);
|
|
|
|
NotificationList->EventHandle[ Index ] = EventHandle;
|
|
|
|
Event->ClientEvent = ClientEvent;
|
|
Event->hKey = hKey;
|
|
Event->Flags = 0;
|
|
Event->PipeEntry = PipeEntry;
|
|
|
|
} else {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Could not create pipe entry for %wZ\n",
|
|
PipeName );
|
|
#endif
|
|
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
RemoveEntryFromNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList,
|
|
IN DWORD EntryIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Removes an entry from a Notification List. It leaves a hole
|
|
in the list, i.e. the list is not compacted.
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies pointer to Notification List.
|
|
|
|
EntryIndex - Supplies index of entry to remove.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG Error;
|
|
|
|
ASSERT( EntryIndex < NotificationList->EventsInUse );
|
|
ASSERT( NotificationList->EventHandle[ EntryIndex ] != NULL );
|
|
|
|
//
|
|
// Remove the entry from the pipe symbol table.
|
|
//
|
|
Error = RemovePipe( NotificationList->Event[ EntryIndex ].PipeEntry );
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// We "remove" the entry from the notification list by
|
|
// invalidating its handle. Note that we don't decrement
|
|
// the counter of entries in the notification list because
|
|
// that is used for indexing the next available entry.
|
|
// The counter will be fixed by the compaction function.
|
|
//
|
|
NotificationList->EventHandle[ EntryIndex ] = NULL;
|
|
NotificationList->Event[ EntryIndex ].PipeEntry = NULL;
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
CompactNotificationList (
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Compacts (i.e. removes holes from) a Notification List.
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies pointer to Notification List.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD ToIndex;
|
|
DWORD FromIndex;
|
|
DWORD Index;
|
|
DWORD EntriesToMove;
|
|
PVOID Src;
|
|
PVOID Dst;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Compacting notification list\n" );
|
|
#endif
|
|
|
|
for ( ToIndex = 1; ToIndex < NotificationList->EventsInUse; ToIndex++ ) {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " - %d\n", ToIndex );
|
|
#endif
|
|
//
|
|
// If we find a hole, we compact the arrays i.e. shift them to
|
|
// remove the hole.
|
|
//
|
|
if ( NotificationList->EventHandle[ ToIndex ] == NULL ) {
|
|
|
|
//
|
|
// Found the beginning of a hole, search for the next
|
|
// entry in use.
|
|
//
|
|
for ( FromIndex = ToIndex+1;
|
|
(FromIndex < NotificationList->EventsInUse) &&
|
|
(NotificationList->EventHandle[ FromIndex ] == NULL );
|
|
FromIndex++ ) {
|
|
}
|
|
|
|
//
|
|
// If there is something to shift, shift it
|
|
//
|
|
if ( FromIndex < NotificationList->EventsInUse ) {
|
|
|
|
EntriesToMove = NotificationList->EventsInUse - FromIndex;
|
|
|
|
Src = (PVOID)&(NotificationList->EventHandle[ FromIndex ] );
|
|
Dst = (PVOID)&(NotificationList->EventHandle[ ToIndex ] );
|
|
|
|
RtlMoveMemory(
|
|
Dst,
|
|
Src,
|
|
EntriesToMove * sizeof( HANDLE )
|
|
);
|
|
|
|
Src = &(NotificationList->Event[ FromIndex ] );
|
|
Dst = &(NotificationList->Event[ ToIndex ] );
|
|
|
|
RtlMoveMemory(
|
|
Dst,
|
|
Src,
|
|
EntriesToMove * sizeof( NOTIFICATION_ENTRY )
|
|
);
|
|
|
|
//
|
|
// Clear the rest of the entries, just to keep things
|
|
// clean.
|
|
//
|
|
for ( Index = ToIndex + EntriesToMove;
|
|
Index < NotificationList->EventsInUse;
|
|
Index++ ) {
|
|
|
|
NotificationList->EventHandle[ Index ] = NULL;
|
|
}
|
|
|
|
NotificationList->EventsInUse -= (FromIndex - ToIndex);
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Nothing to shift, this will become the
|
|
// first available entry of the list.
|
|
//
|
|
NotificationList->EventsInUse = ToIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Compacted.\n" );
|
|
#endif
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AddNotificationListToChain(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a Notification list to the chain of Notification Lists.
|
|
|
|
The new list is put at the head of the chain.
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies the Notification list to add
|
|
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NotificationList->Previous = NULL;
|
|
NotificationList->Next = NotificationListChainHead;
|
|
|
|
if ( NotificationListChainHead ) {
|
|
NotificationListChainHead->Previous = NotificationList;
|
|
}
|
|
|
|
NotificationListChainHead = NotificationList;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
RemoveNotificationListFromChain(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a Notification list from the chain
|
|
|
|
|
|
Arguments:
|
|
|
|
NotificationList - Supplies the Notification list to remove
|
|
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if ( NotificationList->Previous ) {
|
|
(NotificationList->Previous)->Next = NotificationList->Next;
|
|
}
|
|
|
|
if ( NotificationList->Next ) {
|
|
(NotificationList->Next)->Previous = NotificationList->Previous;
|
|
}
|
|
|
|
|
|
//
|
|
// If this is at the head of the chain, Let the next
|
|
// list be the new head.
|
|
//
|
|
if ( NotificationListChainHead == NotificationList ) {
|
|
NotificationListChainHead = NotificationList->Next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Pipe Symbol Table functions
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
LONG
|
|
GetFullPipeName (
|
|
IN PUNICODE_STRING MachineName,
|
|
IN PUNICODE_STRING PipeName,
|
|
OUT PUNICODE_STRING FullPipeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes a fully qualified pipe name from the supplied machine
|
|
name and pipe name.
|
|
|
|
Arguments:
|
|
|
|
PipeName - Supplies the pipe name
|
|
|
|
MachineName - Supplies the client's machine name
|
|
|
|
FullPipeName - Supplies the full pipe name
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG Error = ERROR_SUCCESS;
|
|
NTSTATUS NtStatus;
|
|
|
|
ASSERT( PipeName->Buffer && MachineName->Buffer &&
|
|
PipeName->Length > 0 && MachineName->Length > 0 );
|
|
|
|
if( !PipeName->Buffer || !MachineName->Buffer ||
|
|
PipeName->Length == 0 || MachineName->Length == 0 ) {
|
|
Error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// If the client's machine name and our machine name match,
|
|
// then we form a local named pipe path, otherwise we
|
|
// form a remote named pipe path.
|
|
//
|
|
if ( RtlEqualUnicodeString(
|
|
MachineName,
|
|
&OurMachineName,
|
|
TRUE
|
|
) ) {
|
|
|
|
|
|
//
|
|
// Pipe is local
|
|
//
|
|
RtlMoveMemory(
|
|
FullPipeName->Buffer,
|
|
NAMED_PIPE_HERE,
|
|
sizeof( NAMED_PIPE_HERE )
|
|
);
|
|
|
|
FullPipeName->Length = sizeof( NAMED_PIPE_HERE ) - sizeof(UNICODE_NULL);
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// Pipe is remote
|
|
//
|
|
RtlMoveMemory(
|
|
FullPipeName->Buffer,
|
|
NAMED_PIPE_THERE,
|
|
sizeof( NAMED_PIPE_THERE )
|
|
);
|
|
|
|
FullPipeName->Length = sizeof( NAMED_PIPE_THERE ) - sizeof(UNICODE_NULL);
|
|
|
|
NtStatus = RtlAppendUnicodeStringToString(
|
|
FullPipeName,
|
|
MachineName
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|
|
|
NtStatus = RtlAppendUnicodeToString(
|
|
FullPipeName,
|
|
L"\\Pipe\\"
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
|
|
} else {
|
|
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
}
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
NtStatus = RtlAppendUnicodeStringToString(
|
|
FullPipeName,
|
|
PipeName
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LONG
|
|
CreatePipeEntry (
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a pipe entry
|
|
|
|
Arguments:
|
|
|
|
PipeName - Supplies the pipe name
|
|
|
|
pRpcSa - Supplies the optional security attributes for the pipe
|
|
|
|
PipeEntry - Supplies pointer to pointer to pipe entry.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PPIPE_ENTRY PipeEntry;
|
|
LONG Error;
|
|
ULONG LengthSd;
|
|
|
|
ASSERT( PipeName && PipeName->Buffer );
|
|
|
|
//
|
|
// Validate the security descriptor if one was provided
|
|
//
|
|
if ( pRpcSa ) {
|
|
if ( !RtlValidSecurityDescriptor(
|
|
pRpcSa->RpcSecurityDescriptor.lpSecurityDescriptor
|
|
) ) {
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate space for the Pipe Entry
|
|
//
|
|
PipeEntry = RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
sizeof( PIPE_ENTRY )
|
|
);
|
|
|
|
if ( !PipeEntry ) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate space for the pipe's name
|
|
//
|
|
PipeEntry->PipeName.Buffer = RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeName->Length + sizeof( UNICODE_NULL )
|
|
);
|
|
|
|
PipeEntry->PipeName.MaximumLength = PipeName->Length + (USHORT)sizeof( UNICODE_NULL );
|
|
|
|
if ( PipeEntry->PipeName.Buffer ) {
|
|
|
|
//
|
|
// Copy the pipe name
|
|
//
|
|
RtlCopyUnicodeString(
|
|
&(PipeEntry->PipeName),
|
|
PipeName
|
|
);
|
|
|
|
PipeEntry->Previous = NULL;
|
|
PipeEntry->Next = NULL;
|
|
|
|
PipeEntry->ReferenceCount = 0;
|
|
|
|
//
|
|
// Allocate space for the security descriptor if one
|
|
// is provided.
|
|
//
|
|
if ( pRpcSa ) {
|
|
|
|
LengthSd = RtlLengthSecurityDescriptor(
|
|
pRpcSa->RpcSecurityDescriptor.lpSecurityDescriptor
|
|
);
|
|
|
|
PipeEntry->SecurityDescriptor = RtlAllocateHeap(
|
|
RtlProcessHeap( ), 0,
|
|
LengthSd
|
|
);
|
|
|
|
|
|
if ( PipeEntry->SecurityDescriptor ) {
|
|
|
|
//
|
|
// Copy the security descriptor
|
|
//
|
|
RtlMoveMemory (
|
|
PipeEntry->SecurityDescriptor,
|
|
pRpcSa->RpcSecurityDescriptor.lpSecurityDescriptor,
|
|
LengthSd
|
|
);
|
|
|
|
*PipeEntryUsed = PipeEntry;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Error = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeEntry->PipeName.Buffer
|
|
);
|
|
|
|
} else {
|
|
|
|
PipeEntry->SecurityDescriptor = NULL;
|
|
|
|
*PipeEntryUsed = PipeEntry;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
Error = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeEntry
|
|
);
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
DeletePipeEntry(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Deletes a pipe entry
|
|
|
|
Arguments:
|
|
|
|
PipeEntry - Supplies pointer to pipe entry
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * In DeletePipeEntry\n" );
|
|
#endif
|
|
|
|
ASSERT( PipeEntry );
|
|
ASSERT( PipeEntry->PipeName.Buffer );
|
|
|
|
if ( PipeEntry->PipeName.Buffer ) {
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeEntry->PipeName.Buffer
|
|
);
|
|
}
|
|
|
|
if ( PipeEntry->SecurityDescriptor != NULL ) {
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeEntry->SecurityDescriptor
|
|
);
|
|
}
|
|
|
|
RtlFreeHeap(
|
|
RtlProcessHeap( ), 0,
|
|
PipeEntry
|
|
);
|
|
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Deleted PipeEntry.\n" );
|
|
#endif
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
AddPipe(
|
|
IN PUNICODE_STRING PipeName,
|
|
IN PRPC_SECURITY_ATTRIBUTES pRpcSa OPTIONAL,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a new entry to the pipe symbol table
|
|
|
|
Arguments:
|
|
|
|
PipeName - Supplies the pipe name
|
|
|
|
pRpcSa - Supplies the optional security attributes for the pipe
|
|
|
|
PipeEntry - Supplies pointer to pointer to pipe entry in the
|
|
symbol table.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PPIPE_ENTRY PipeEntry;
|
|
LONG Error;
|
|
|
|
|
|
//
|
|
// Look for the pipe name in the symbol table
|
|
//
|
|
Error = LookForPipeEntryInSymbolTable( PipeName, &PipeEntry );
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// If the pipe is not in the symbol table, add it
|
|
//
|
|
if ( !PipeEntry ) {
|
|
|
|
//
|
|
// Create a new pipe entry
|
|
//
|
|
Error = CreatePipeEntry(
|
|
PipeName,
|
|
pRpcSa,
|
|
&PipeEntry
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Add the entry to the symbol table
|
|
//
|
|
Error = AddPipeEntryToSymbolTable(
|
|
PipeEntry
|
|
);
|
|
|
|
if ( Error != ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Could not add pipe entry, delete it.
|
|
//
|
|
DeletePipeEntry( PipeEntry );
|
|
PipeEntry = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If got a pipe entry, increment its reference count
|
|
//
|
|
if ( PipeEntry ) {
|
|
|
|
PipeEntry->ReferenceCount++;
|
|
*PipeEntryUsed = PipeEntry;
|
|
}
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "Added Pipe %Z:\n", PipeName );
|
|
DumpPipeTable();
|
|
#endif
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
RemovePipe(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrements the reference count of a pipe entry and removes the
|
|
entry if the reference count reaches zero.
|
|
|
|
Arguments:
|
|
|
|
PipeEntry - Supplies pointer to pipe entry in the symbol table
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG Error = ERROR_SUCCESS;
|
|
PPIPE_ENTRY Entry = PipeEntry;
|
|
|
|
ASSERT( Entry );
|
|
ASSERT( Entry->ReferenceCount > 0 );
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * In RemovePipe - Ref. count %d\n", Entry->ReferenceCount );
|
|
#endif
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
Entry->ReferenceCount--;
|
|
|
|
//
|
|
// If the reference count is zero, we can delete the
|
|
// entry
|
|
//
|
|
if ( Entry->ReferenceCount == 0 ) {
|
|
|
|
//
|
|
// Remove the pipe entry from the symbol table
|
|
//
|
|
Error = RemovePipeEntryFromSymbolTable(
|
|
Entry
|
|
);
|
|
|
|
if ( Error == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// Delete the pipe entry
|
|
//
|
|
ASSERT( PipeEntry > (PPIPE_ENTRY)0x100 );
|
|
Error = DeletePipeEntry( Entry );
|
|
}
|
|
}
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Pipe Removed.\n" );
|
|
#endif
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
AddPipeEntryToSymbolTable(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Adds a pipe entry to the symbol table at the specified bucket.
|
|
|
|
Entries are always added at the head of the chain.
|
|
|
|
Calls to this function must be protected by the critical section
|
|
of the pipe symbol table.
|
|
|
|
Arguments:
|
|
|
|
PipeEntry - Supplies pointer to pipe entry
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Bucket;
|
|
|
|
Bucket = HASH( &(PipeEntry->PipeName), BUCKETS_IN_SYMBOL_TABLE );
|
|
|
|
PipeEntry->Previous = NULL;
|
|
PipeEntry->Next = PipeSymbolTable.Bucket[ Bucket ];
|
|
|
|
if ( PipeSymbolTable.Bucket[ Bucket ] ) {
|
|
(PipeSymbolTable.Bucket[ Bucket ])->Previous = PipeEntry;
|
|
}
|
|
|
|
PipeSymbolTable.Bucket[ Bucket ] = PipeEntry;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
RemovePipeEntryFromSymbolTable(
|
|
IN OUT PPIPE_ENTRY PipeEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Removes a pipe entry from the symbol table at the specified bucket
|
|
|
|
Calls to this function must be protected by the critical section
|
|
of the pipe symbol table.
|
|
|
|
Arguments:
|
|
|
|
PipeEntry - Supplies pointer to pipe entry
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Bucket;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * In RemovePipeEntryFromSymbolTable\n" );
|
|
#endif
|
|
|
|
ASSERT( PipeEntry > (PPIPE_ENTRY)0x100 );
|
|
|
|
Bucket = HASH( &(PipeEntry->PipeName), BUCKETS_IN_SYMBOL_TABLE );
|
|
|
|
ASSERT( PipeEntry > (PPIPE_ENTRY)0x100 );
|
|
ASSERT( Bucket < BUCKETS_IN_SYMBOL_TABLE );
|
|
|
|
//
|
|
// Remove the entry from the chain
|
|
//
|
|
if ( PipeEntry->Previous ) {
|
|
(PipeEntry->Previous)->Next = PipeEntry->Next;
|
|
}
|
|
|
|
if ( PipeEntry->Next ) {
|
|
(PipeEntry->Next)->Previous = PipeEntry->Previous;
|
|
}
|
|
|
|
|
|
//
|
|
// If this entry is at the head of the chain, Let the next
|
|
// entry be the new head.
|
|
//
|
|
ASSERT( PipeSymbolTable.Bucket[ Bucket ] != NULL );
|
|
if ( PipeSymbolTable.Bucket[ Bucket ] == PipeEntry ) {
|
|
PipeSymbolTable.Bucket[ Bucket ] = PipeEntry->Next;
|
|
}
|
|
|
|
PipeEntry->Next = NULL;
|
|
PipeEntry->Previous = NULL;
|
|
|
|
ASSERT( PipeEntry > (PPIPE_ENTRY)0x100 );
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Piped entry removed from symbol table.\n" );
|
|
#endif
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG
|
|
LookForPipeEntryInSymbolTable(
|
|
IN PUNICODE_STRING PipeName,
|
|
OUT PPIPE_ENTRY *PipeEntryUsed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks for an entry corresponding to the given name in a particular
|
|
bucket of the pipe symbol table.
|
|
|
|
Note that this function always returns ERROR_SUCCESS. To find out
|
|
if the pipe is in the chain or not the returned parameter has to
|
|
be checked.
|
|
|
|
Calls to this function must be protected by the critical section
|
|
of the pipe symbol table.
|
|
|
|
Arguments:
|
|
|
|
PipeName - Supplies the pipe name
|
|
|
|
Bucket - Supplies the bucket
|
|
|
|
PipeEntry - Supplies pointer to pointer to pipe entry.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
{
|
|
PPIPE_ENTRY PipeEntry;
|
|
DWORD Bucket;
|
|
|
|
Bucket = HASH( PipeName, BUCKETS_IN_SYMBOL_TABLE );
|
|
|
|
//
|
|
// Look for the entry
|
|
//
|
|
for ( PipeEntry = PipeSymbolTable.Bucket[ Bucket ];
|
|
PipeEntry && !RtlEqualUnicodeString( PipeName, &(PipeEntry->PipeName), TRUE);
|
|
PipeEntry = PipeEntry->Next );
|
|
|
|
|
|
*PipeEntryUsed = PipeEntry;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
Hash(
|
|
IN PUNICODE_STRING Symbol,
|
|
IN DWORD Buckets
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Obtains a hash value for a given symbol
|
|
|
|
Arguments:
|
|
|
|
|
|
Symbol - Supplies the symbol to hash
|
|
|
|
Buckets - Supplies the number of buckets in the sybol table.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD n;
|
|
DWORD HashValue;
|
|
WCHAR c;
|
|
LPWSTR s;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Hashing\n" );
|
|
#endif
|
|
|
|
n = Symbol->Length/sizeof(WCHAR);
|
|
s = Symbol->Buffer;
|
|
HashValue = 0;
|
|
|
|
while (n--) {
|
|
|
|
c = *s++;
|
|
|
|
HashValue = HashValue + (c << 1) + (c >> 1) + c;
|
|
}
|
|
|
|
return HashValue % Buckets;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Notification List Handler
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
|
|
VOID
|
|
NotificationHandler(
|
|
IN PNOTIFICATION_LIST NotificationList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler of a Notification List.
|
|
|
|
Arguments:
|
|
|
|
|
|
NotificationList - Supplies pointer to the Notification List
|
|
to handle.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS NtStatus;
|
|
DWORD NumberOfEvents;
|
|
HANDLE Thread;
|
|
BOOLEAN KeepOnGoing = TRUE;
|
|
DWORD Index;
|
|
LARGE_INTEGER TimeOut;
|
|
|
|
|
|
ASSERT( NotificationList );
|
|
|
|
//
|
|
// Initially we'll wait on only one event, i.e. the
|
|
// "wake up" event
|
|
//
|
|
NumberOfEvents = 1;
|
|
NotificationList->TimeOutCount = 0;
|
|
NotificationList->ResetCount = FALSE;
|
|
|
|
while ( KeepOnGoing ) {
|
|
|
|
TimeOut.QuadPart = Int32x32To64( -10000,
|
|
5000*NotificationList->TimeOutCount );
|
|
|
|
//
|
|
// Wait for some event
|
|
//
|
|
NtStatus = NtWaitForMultipleObjects(
|
|
(CHAR)NumberOfEvents,
|
|
NotificationList->EventHandle,
|
|
WaitAny,
|
|
FALSE,
|
|
(NotificationList->PendingNotifications > 0) ?
|
|
&TimeOut : NULL
|
|
);
|
|
|
|
Index = (DWORD)NtStatus;
|
|
|
|
if ( (Index < 0) || (Index >= NumberOfEvents) ) {
|
|
Index = 0;
|
|
}
|
|
|
|
ASSERT( Index < NumberOfEvents );
|
|
|
|
NtStatus = RtlEnterCriticalSection( &NotificationCriticalSection );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
#if BIGDBG
|
|
DbgPrint( "WINREG: Notification handler signaled, Index %d\n", Index );
|
|
#endif
|
|
|
|
try {
|
|
|
|
//
|
|
// If an event was triggered, mark it as a pending notification so
|
|
// that the NotificationListMaintenance function will notify
|
|
// the client.
|
|
//
|
|
if ( Index > 0 ) {
|
|
NotificationList->PendingNotifications++;
|
|
NotificationList->Event[Index].Flags |= MUST_NOTIFY;
|
|
}
|
|
|
|
//
|
|
// Notify all the clients with pending notifications and
|
|
// remove entries for dead clients.
|
|
//
|
|
NumberOfEvents = NotificationListMaintenance( NotificationList );
|
|
|
|
if( NotificationList->PendingNotifications != 0 ) {
|
|
if( NotificationList->ResetCount ) {
|
|
NotificationList->TimeOutCount = 1;
|
|
NotificationList->ResetCount = FALSE;
|
|
} else {
|
|
if( NotificationList->TimeOutCount == 0 ) {
|
|
NotificationList->TimeOutCount = 1;
|
|
} else {
|
|
if( NotificationList->TimeOutCount != MAX_TIMEOUT_COUNT ) {
|
|
NotificationList->TimeOutCount =
|
|
NotificationList->TimeOutCount << 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
NotificationList->TimeOutCount = 0;
|
|
}
|
|
|
|
//
|
|
// If the list is empty, then try to take it out of the chain, and
|
|
// if successful, our job is done.
|
|
//
|
|
if ( NumberOfEvents == 1 ) {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Removing the notification list!\n" );
|
|
#endif
|
|
//
|
|
// Make sure that the list is empty.
|
|
//
|
|
ASSERT( NotificationList->EventsInUse == 1 );
|
|
if (NotificationList->EventsInUse == 1) {
|
|
|
|
//
|
|
// The list is empty, remove the list from the chain
|
|
// and delete it.
|
|
//
|
|
RemoveNotificationListFromChain( NotificationList );
|
|
Thread = NotificationList->HandlerThread;
|
|
DeleteNotificationList( NotificationList );
|
|
|
|
//
|
|
// The list is gone, we can die.
|
|
//
|
|
KeepOnGoing = FALSE;
|
|
}
|
|
}
|
|
|
|
} except ( NtStatus = GetExceptionCode() ) {
|
|
|
|
#if DBG
|
|
DbgPrint( "WINREG Error: Exception %x in NotificationHandler\n",
|
|
NtStatus );
|
|
DbgBreakPoint();
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#if BIGDBG
|
|
if ( KeepOnGoing ) {
|
|
DbgPrint( "WINREG: Notification handler waiting...\n" );
|
|
} else {
|
|
DbgPrint( "WINREG: Notification handler dying...\n" );
|
|
}
|
|
#endif
|
|
|
|
NtStatus = RtlLeaveCriticalSection( &NotificationCriticalSection );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
}
|
|
|
|
//
|
|
// The list is gone, and so must we.
|
|
//
|
|
ExitThread( 0 );
|
|
|
|
ASSERT( FALSE );
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
NotificationListMaintenance(
|
|
IN OUT PNOTIFICATION_LIST NotificationList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs all the maintenance necessary in the notification list.
|
|
The maintenance consists of:
|
|
|
|
- Notifying all clients with pending notifications.
|
|
|
|
- Removing entries in the list for dead clients.
|
|
|
|
- Compacting the notification list.
|
|
|
|
Arguments:
|
|
|
|
|
|
NotificationList - Supplies pointer to the Notification List
|
|
|
|
Return Value:
|
|
|
|
DWORD - The new number of events in the list
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG Error;
|
|
DWORD NumberOfEvents;
|
|
DWORD Index;
|
|
BOOLEAN Remove;
|
|
PNOTIFICATION_ENTRY Event;
|
|
NTSTATUS NtStatus;
|
|
PPIPE_ENTRY PipeEntry;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * In NotificationListMaintenance\n" );
|
|
DumpNotificationLists();
|
|
#endif
|
|
|
|
//
|
|
// Traverse the list notifying clients if necessary and removing
|
|
// events that are no longer needed, either because they have
|
|
// already been notified or because the client is dead.
|
|
//
|
|
for (Index = 1; Index < NotificationList->EventsInUse; Index++ ) {
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " - %d\n", Index );
|
|
#endif
|
|
Remove = FALSE;
|
|
Event = &(NotificationList->Event[ Index ]);
|
|
|
|
if ( Event->Flags & CLIENT_IS_DEAD ) {
|
|
|
|
//
|
|
// No client, must remove the entry.
|
|
//
|
|
Remove = TRUE;
|
|
|
|
} else if ( Event->Flags & MUST_NOTIFY ) {
|
|
|
|
//
|
|
// Must notify this client
|
|
//
|
|
Error = SendEventToClient(
|
|
Event->ClientEvent,
|
|
Event->PipeEntry
|
|
);
|
|
|
|
if (Error == ERROR_SUCCESS) {
|
|
//
|
|
// If successfully notified, remove the entry.
|
|
//
|
|
Remove = TRUE;
|
|
Event->Flags &= ~NOTIFICATION_FAILED;
|
|
} else {
|
|
//
|
|
// If couldn't notify, set ResetCount if the notification
|
|
// failed for the first time
|
|
//
|
|
if( ( Event->Flags & NOTIFICATION_FAILED ) == 0 ) {
|
|
NotificationList->ResetCount = TRUE;
|
|
Event->Flags |= NOTIFICATION_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the entry if no longer needed.
|
|
//
|
|
if ( Remove ) {
|
|
|
|
//
|
|
// Remove the pipe entry
|
|
//
|
|
PipeEntry = Event->PipeEntry;
|
|
RemovePipe( PipeEntry );
|
|
|
|
Event->PipeEntry = NULL;
|
|
|
|
//
|
|
// Remove the event
|
|
//
|
|
#if BIGDBG
|
|
DbgPrint( " Cleanup\n" );
|
|
#endif
|
|
|
|
NtStatus = NtClose( NotificationList->EventHandle[ Index ] );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
NotificationList->EventHandle[ Index ] = NULL;
|
|
|
|
//
|
|
// If this was a pending notification, decrement the
|
|
// counter.
|
|
//
|
|
if ( Event->Flags & MUST_NOTIFY ) {
|
|
NotificationList->PendingNotifications--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Compact the list.
|
|
//
|
|
Error = CompactNotificationList( NotificationList );
|
|
|
|
ASSERT( Error == ERROR_SUCCESS );
|
|
|
|
//
|
|
// Get the new number of entries in the list
|
|
//
|
|
NumberOfEvents = NotificationList->EventsInUse;
|
|
|
|
#if BIGDBG
|
|
DbgPrint( " * Maintenance Done (%d)\n", NumberOfEvents );
|
|
#endif
|
|
|
|
return NumberOfEvents;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LONG
|
|
SendEventToClient(
|
|
IN DWORD ClientEvent,
|
|
IN PPIPE_ENTRY PipeEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends an event to the client via the client's named pipe
|
|
|
|
Arguments:
|
|
|
|
|
|
PipeEntry - Supplies the pipe entry for the client's named
|
|
pipe.
|
|
|
|
ClientEvent - Supplies the event that has to be sent to the
|
|
client.
|
|
|
|
Return Value:
|
|
|
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HANDLE Handle;
|
|
LONG Error = ERROR_SUCCESS;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS NtStatus;
|
|
|
|
ASSERT( PipeEntry != NULL );
|
|
ASSERT( PipeEntry->PipeName.Buffer != NULL );
|
|
|
|
//
|
|
// Initialize the Obja structure for the named pipe
|
|
//
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&(PipeEntry->PipeName),
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
PipeEntry->SecurityDescriptor
|
|
);
|
|
|
|
|
|
//
|
|
// Open our side of the pipe
|
|
//
|
|
NtStatus = NtOpenFile(
|
|
&Handle,
|
|
GENERIC_WRITE | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_WRITE,
|
|
FILE_NON_DIRECTORY_FILE
|
|
);
|
|
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|
|
|
//
|
|
// Write the event
|
|
//
|
|
NtStatus = NtWriteFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
&ClientEvent,
|
|
sizeof(ClientEvent),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( NtStatus == STATUS_PENDING ) {
|
|
NtStatus = NtWaitForSingleObject(
|
|
Handle,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|
#if BIGDBG
|
|
DbgPrint( " --> Client Notified, Event 0x%x\n", ClientEvent );
|
|
#endif
|
|
Error = ERROR_SUCCESS;
|
|
} else {
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
}
|
|
|
|
|
|
//
|
|
// Close our side of the pipe
|
|
//
|
|
NtStatus = NtClose( Handle );
|
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we couldn't open the pipe because the pipe does
|
|
// not exist, there's no point in keep trying.
|
|
//
|
|
if ( NtStatus == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
Error = ERROR_SUCCESS;
|
|
} else {
|
|
Error = RtlNtStatusToDosError( NtStatus );
|
|
#if DBG
|
|
DbgPrint( "WINREG: Cannot Open pipe %Z, event %x, status %x\n",
|
|
&(PipeEntry->PipeName), ClientEvent, NtStatus );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if (Error != ERROR_SUCCESS ) {
|
|
DbgPrint( "WINREG: Could not notify client, Error %d\n", Error );
|
|
}
|
|
#endif
|
|
|
|
return Error;
|
|
}
|
|
|
|
|
|
#if BIGDBG
|
|
|
|
// *****************************************************************
|
|
//
|
|
// Debug Stuff
|
|
//
|
|
// *****************************************************************
|
|
|
|
|
|
VOID
|
|
DumpNotificationLists(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dumps the notification lists
|
|
|
|
Arguments:
|
|
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOTIFICATION_LIST NotificationList;
|
|
PNOTIFICATION_ENTRY Event;
|
|
DWORD Index;
|
|
|
|
DbgPrint( " Notification list dump: \n\n" );
|
|
|
|
for ( NotificationList = NotificationListChainHead;
|
|
NotificationList;
|
|
NotificationList = NotificationList->Next ) {
|
|
|
|
DbgPrint( " Notification List at 0x%x\n", NotificationList );
|
|
DbgPrint( " Pending notifications: %d\n", NotificationList->PendingNotifications );
|
|
|
|
Event = &(NotificationList->Event[1]);
|
|
|
|
for ( Index = 1; Index < NotificationList->EventsInUse; Index++ ) {
|
|
|
|
DbgPrint( " Event %d EventHandle 0x%x Client 0x%x",
|
|
Index,
|
|
NotificationList->EventHandle[ Index ],
|
|
Event->ClientEvent );
|
|
|
|
if ( Event->Flags & CLIENT_IS_DEAD ) {
|
|
DbgPrint( " (Dead)\n" );
|
|
} else if ( Event->Flags & MUST_NOTIFY ) {
|
|
DbgPrint( " (Notify)\n" );
|
|
} else {
|
|
DbgPrint( "\n" );
|
|
}
|
|
|
|
Event++;
|
|
}
|
|
|
|
DbgPrint( "\n");
|
|
}
|
|
|
|
DbgPrint( "\n");
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpPipeTable(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dumps the pipe table
|
|
|
|
Arguments:
|
|
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
PPIPE_ENTRY Entry;
|
|
|
|
DbgPrint( "\n\n Pipes:\n\n" );
|
|
|
|
for ( i=0; i < BUCKETS_IN_SYMBOL_TABLE; i++ ) {
|
|
|
|
Entry = PipeSymbolTable.Bucket[i];
|
|
if ( Entry ) {
|
|
DbgPrint( " Bucket %d:\n",i );
|
|
while ( Entry ) {
|
|
DbgPrint( " %Z (%d)\n", &(Entry->PipeName), Entry->ReferenceCount );
|
|
Entry = Entry->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
DbgPrint( "\n" );
|
|
}
|
|
|
|
|
|
#endif // BIGDBG
|
|
|
|
#endif // REMOTE_NOTIFICATION_DISABLED
|