1114 lines
28 KiB
C
1114 lines
28 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Regnckey.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the client side wrappers for 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 implementation of RegNotifyChangeKeyValue involves >= 4 threads: 2 on
|
|||
|
the client side and >= 2 on the server side.
|
|||
|
|
|||
|
Client:
|
|||
|
|
|||
|
Thread 1.- The user's thread executing the RegNotifyChangeKeyValue.
|
|||
|
This threads does:
|
|||
|
|
|||
|
- If thread #2 has not been created yet, it creates a
|
|||
|
named pipe and thread #2.
|
|||
|
|
|||
|
- Does a synchronous RPC to the server
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Thread 2.- This thread reads events from the named pipe and signals
|
|||
|
them. The writers to the pipe are the RPC servers which
|
|||
|
thread 1 has called.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Server:
|
|||
|
|
|||
|
Thread 1.- This thread services the RPC from the client side. It
|
|||
|
calls the NT notification API and adds the notification
|
|||
|
handle to a "notification list".
|
|||
|
|
|||
|
Thread 2.- This thread waits on part of the "notification list",
|
|||
|
telling the original client (via named pipe) what events
|
|||
|
need to be signaled.
|
|||
|
|
|||
|
Threads 3... etc. Same as thread 2.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
02-Apr-1992 Ramon J. San Andres (ramonsa)
|
|||
|
Changed to use RPC.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include <rpc.h>
|
|||
|
#include "regrpc.h"
|
|||
|
#include "client.h"
|
|||
|
#include <stdlib.h>
|
|||
|
|
|||
|
NTSTATUS BaseRegNotifyClassKey(
|
|||
|
IN HKEY hKey,
|
|||
|
IN HANDLE hEvent,
|
|||
|
IN PIO_STATUS_BLOCK pLocalIoStatusBlock,
|
|||
|
IN DWORD dwNotifyFilter,
|
|||
|
IN BOOLEAN fWatchSubtree,
|
|||
|
IN BOOLEAN fAsynchronous);
|
|||
|
|
|||
|
//
|
|||
|
// Used by local call to NtNotifyChangeKey.
|
|||
|
//
|
|||
|
|
|||
|
IO_STATUS_BLOCK LocalIoStatusBlock;
|
|||
|
|
|||
|
|
|||
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|||
|
//
|
|||
|
// Named pipe full paths.
|
|||
|
//
|
|||
|
#define NAMED_PIPE_HERE L"\\Device\\NamedPipe\\"
|
|||
|
|
|||
|
//
|
|||
|
// Maximum number of times we will retry to create a pipe if there are
|
|||
|
// name conflicts.
|
|||
|
//
|
|||
|
#define MAX_PIPE_RETRIES 1000
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local variables.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Critical section to control access to notification structures
|
|||
|
//
|
|||
|
RTL_CRITICAL_SECTION NotificationCriticalSection;
|
|||
|
|
|||
|
//
|
|||
|
// Our machine name
|
|||
|
//
|
|||
|
UNICODE_STRING OurMachineName;
|
|||
|
WCHAR OurMachineNameBuffer[ MAX_PATH ];
|
|||
|
|
|||
|
//
|
|||
|
// Named pipe used for notification
|
|||
|
//
|
|||
|
UNICODE_STRING NotificationPipeName;
|
|||
|
WCHAR NotificationPipeNameBuffer[ MAX_PATH ];
|
|||
|
HANDLE NotificationPipeHandle;
|
|||
|
RPC_SECURITY_ATTRIBUTES NotificationPipeSaRpc;
|
|||
|
|
|||
|
//
|
|||
|
// Security descriptor used in the named pipe
|
|||
|
//
|
|||
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|||
|
PACL Acl;
|
|||
|
BOOL SecurityDescriptorInitialized;
|
|||
|
|
|||
|
//
|
|||
|
// Notification thread
|
|||
|
//
|
|||
|
HANDLE NotificationThread;
|
|||
|
DWORD NotificationClientId;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local prototypes
|
|||
|
//
|
|||
|
LONG
|
|||
|
CreateNotificationPipe(
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
NotificationHandler(
|
|||
|
);
|
|||
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|||
|
|
|||
|
|
|||
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|||
|
|
|||
|
LONG
|
|||
|
InitializeNotificationPipeSecurityDescriptor(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize the security descriptor (global variable) to be attached to
|
|||
|
the named pipe.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
LONG - Returns a win32 error code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|||
|
ULONG AclLength;
|
|||
|
PSID WorldSid;
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize global variables
|
|||
|
//
|
|||
|
SecurityDescriptorInitialized = FALSE;
|
|||
|
Acl = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Get World SID
|
|||
|
//
|
|||
|
NtStatus = RtlAllocateAndInitializeSid( &WorldSidAuthority,
|
|||
|
1,
|
|||
|
SECURITY_WORLD_RID,
|
|||
|
0, 0, 0, 0, 0, 0, 0,
|
|||
|
&WorldSid
|
|||
|
);
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
if ( !NT_SUCCESS( NtStatus )) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to allocate and initialize SID, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
return( RtlNtStatusToDosError( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Allocate buffer for ACL.
|
|||
|
// This buffer should be big enough for the ACL header and for each ACE.
|
|||
|
// Each ACE needs an ACE header.
|
|||
|
//
|
|||
|
AclLength = sizeof( ACL ) +
|
|||
|
sizeof( ACCESS_ALLOWED_ACE ) +
|
|||
|
GetLengthSid( WorldSid ) +
|
|||
|
sizeof( DWORD );
|
|||
|
|
|||
|
Acl = RtlAllocateHeap( RtlProcessHeap(), 0, AclLength );
|
|||
|
ASSERT( Acl != NULL );
|
|||
|
if( Acl == NULL ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to allocate memory, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
RtlFreeSid( WorldSid );
|
|||
|
return( ERROR_OUTOFMEMORY );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build ACL: World has all access
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = RtlCreateAcl( (PACL)Acl,
|
|||
|
AclLength,
|
|||
|
ACL_REVISION2
|
|||
|
);
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
if ( !NT_SUCCESS( NtStatus )) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to create ACL, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
RtlFreeSid( WorldSid );
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|||
|
return( RtlNtStatusToDosError( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = RtlAddAccessAllowedAce( (PACL)Acl,
|
|||
|
ACL_REVISION2,
|
|||
|
SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
|||
|
WorldSid
|
|||
|
);
|
|||
|
|
|||
|
RtlFreeSid( WorldSid );
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
if ( !NT_SUCCESS( NtStatus )) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to add ACE, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|||
|
return( RtlNtStatusToDosError( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build security descriptor
|
|||
|
//
|
|||
|
NtStatus = RtlCreateSecurityDescriptor( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
if ( !NT_SUCCESS( NtStatus )) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to create security descriptor, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|||
|
return( RtlNtStatusToDosError( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
if( !RtlValidAcl( (PACL )Acl ) ) {
|
|||
|
DbgPrint( "WINREG: Acl is invalid \n" );
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|||
|
return( ERROR_INVALID_ACL );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
NtStatus = RtlSetDaclSecurityDescriptor ( &SecurityDescriptor,
|
|||
|
TRUE,
|
|||
|
(PACL)Acl,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
if ( !NT_SUCCESS( NtStatus )) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG: Unable to set DACL, NtStatus = %x \n", NtStatus );
|
|||
|
#endif
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, Acl );
|
|||
|
return( RtlNtStatusToDosError( NtStatus ) );
|
|||
|
}
|
|||
|
SecurityDescriptorInitialized = TRUE;
|
|||
|
return( ERROR_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
InitializeRegNotifyChangeKeyValue(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Initializes the static data structures used by the
|
|||
|
RegNotifyChangeKeyValue client. Called once at DLL
|
|||
|
initialization.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if successful.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
|
|||
|
NtStatus = RtlInitializeCriticalSection(
|
|||
|
&NotificationCriticalSection
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize our machine name. Note that the actual
|
|||
|
// name is only obtained when the notification API
|
|||
|
// is first invoked.
|
|||
|
//
|
|||
|
OurMachineName.Length = 0;
|
|||
|
OurMachineName.MaximumLength = MAX_PATH * sizeof(WCHAR);
|
|||
|
OurMachineName.Buffer = OurMachineNameBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize named pipe data
|
|||
|
//
|
|||
|
NotificationPipeName.Length = 0;
|
|||
|
NotificationPipeName.MaximumLength = MAX_PATH * sizeof(WCHAR);
|
|||
|
NotificationPipeName.Buffer = NotificationPipeNameBuffer;
|
|||
|
|
|||
|
NotificationThread = NULL;
|
|||
|
NotificationPipeHandle = NULL;
|
|||
|
|
|||
|
NotificationPipeSaRpc.RpcSecurityDescriptor.lpSecurityDescriptor = NULL;
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
CleanupRegNotifyChangeKeyValue(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Performs any cleanup of the static data structures used
|
|||
|
by the RegNotifyChangeKeyValue client. Called once at
|
|||
|
process termination.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if successful.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Terminate notification thread if there is one running
|
|||
|
//
|
|||
|
if ( NotificationThread != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Close the named pipe
|
|||
|
//
|
|||
|
if ( NotificationPipeHandle != NULL ) {
|
|||
|
|
|||
|
NtStatus = NtClose( NotificationPipeHandle );
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
TerminateThread( NotificationThread, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete the notification critical section
|
|||
|
//
|
|||
|
NtStatus = RtlDeleteCriticalSection(
|
|||
|
&NotificationCriticalSection
|
|||
|
);
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
|
|||
|
if ( NotificationPipeSaRpc.RpcSecurityDescriptor.lpSecurityDescriptor ) {
|
|||
|
RtlFreeHeap(
|
|||
|
RtlProcessHeap( ), 0,
|
|||
|
NotificationPipeSaRpc.RpcSecurityDescriptor.lpSecurityDescriptor
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|||
|
|
|||
|
|
|||
|
|
|||
|
LONG
|
|||
|
RegNotifyChangeKeyValue(
|
|||
|
HKEY hKey,
|
|||
|
BOOL fWatchSubtree,
|
|||
|
DWORD dwNotifyFilter,
|
|||
|
HANDLE hEvent,
|
|||
|
BOOL fAsynchronous
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API is used to watch a key or sub-tree for changes. It can be
|
|||
|
called either synchronously or asynchronously. In the latter case the
|
|||
|
caller must supply an event that is signalled when changes occur. In
|
|||
|
either case 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 an optional event handle. This parameter is ignored
|
|||
|
if fAsynchronus is set to FALSE.
|
|||
|
|
|||
|
fAsynchronous - Supplies a flag which if FALSE causes the API to not
|
|||
|
return until something has changed. If TRUE, the API returns
|
|||
|
immediately and changes are reported via the supplied event. It
|
|||
|
is an error for this parameter to be TRUE and hEvent to be NULL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
LONG - Returns ERROR_SUCCESS (0); error-code for failure.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
If the supplied hKey is closed the event is signalled.
|
|||
|
Therefore it is possible to return from a wait on the event and then
|
|||
|
have subsequent APIs fail.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HKEY Handle;
|
|||
|
HANDLE EventHandle;
|
|||
|
LONG Error = ERROR_SUCCESS;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PRPC_SECURITY_ATTRIBUTES pRpcSa;
|
|||
|
HKEY TempHandle = NULL;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( BreakPointOnEntry ) {
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Limit the capabilities associated with HKEY_PERFORMANCE_DATA.
|
|||
|
//
|
|||
|
|
|||
|
if( hKey == HKEY_PERFORMANCE_DATA ) {
|
|||
|
return ERROR_INVALID_HANDLE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Validate the dependency between fAsynchronus and hEvent.
|
|||
|
//
|
|||
|
if (( fAsynchronous ) && ( ! ARGUMENT_PRESENT( hEvent ))) {
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
Handle = MapPredefinedHandle( hKey, &TempHandle );
|
|||
|
if ( Handle == NULL ) {
|
|||
|
CLOSE_LOCAL_HANDLE(TempHandle);
|
|||
|
return ERROR_INVALID_HANDLE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Notification is not supported on remote handles.
|
|||
|
//
|
|||
|
if( !IsLocalHandle( Handle ) ) {
|
|||
|
CLOSE_LOCAL_HANDLE(TempHandle);
|
|||
|
return ERROR_INVALID_HANDLE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If its a local handle, make an Nt API call and return.
|
|||
|
//
|
|||
|
|
|||
|
if (IsSpecialClassesHandle( Handle )) {
|
|||
|
|
|||
|
//
|
|||
|
// We call a special function for class keys
|
|||
|
//
|
|||
|
NtStatus = BaseRegNotifyClassKey(
|
|||
|
Handle,
|
|||
|
hEvent,
|
|||
|
&LocalIoStatusBlock,
|
|||
|
dwNotifyFilter,
|
|||
|
( BOOLEAN ) fWatchSubtree,
|
|||
|
( BOOLEAN ) fAsynchronous
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
NtStatus = NtNotifyChangeKey(
|
|||
|
Handle,
|
|||
|
hEvent,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&LocalIoStatusBlock,
|
|||
|
dwNotifyFilter,
|
|||
|
( BOOLEAN ) fWatchSubtree,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
( BOOLEAN ) fAsynchronous
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if( NT_SUCCESS( NtStatus ) ||
|
|||
|
( NtStatus == STATUS_PENDING ) ) {
|
|||
|
Error = (error_status_t)ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
Error = (error_status_t) RtlNtStatusToDosError( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
CLOSE_LOCAL_HANDLE(TempHandle);
|
|||
|
return Error;
|
|||
|
}
|
|||
|
|
|||
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|||
|
|
|||
|
// NOTE: THE FOLLOWING CODE IS DISABLED BY THE CHECK FOR
|
|||
|
// IsLocalHandle AT THE BEGINNING OF THE FUNCTION.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// If this is an asynchronous call, we use the user-provided
|
|||
|
// event and will let the user wait on it him/herself.
|
|||
|
// Otherwise we have to create our own event and wait on
|
|||
|
// it ourselves.
|
|||
|
//
|
|||
|
// This is because the server side of the API is always
|
|||
|
// asynchronous.
|
|||
|
//
|
|||
|
if ( fAsynchronous ) {
|
|||
|
|
|||
|
EventHandle = hEvent;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NtStatus = NtCreateEvent(
|
|||
|
&EventHandle,
|
|||
|
EVENT_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
NotificationEvent,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
return RtlNtStatusToDosError( NtStatus );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See if the notification thread is already running
|
|||
|
// and create it if not. We have to protect this
|
|||
|
// with a critical section because there might be
|
|||
|
// several instances of this API doing this check
|
|||
|
// at the same time.
|
|||
|
//
|
|||
|
NtStatus = RtlEnterCriticalSection( &NotificationCriticalSection );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
Error = RtlNtStatusToDosError( NtStatus );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We are now inside the critical section
|
|||
|
//
|
|||
|
if ( NotificationThread == NULL ) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Create a named pipe for the notification thread
|
|||
|
// to use.
|
|||
|
//
|
|||
|
Error = CreateNotificationPipe( );
|
|||
|
|
|||
|
if ( Error == ERROR_SUCCESS ) {
|
|||
|
|
|||
|
//
|
|||
|
// Create the notification thread
|
|||
|
//
|
|||
|
NotificationThread = CreateThread(
|
|||
|
NULL,
|
|||
|
(16 * 1024),
|
|||
|
(LPTHREAD_START_ROUTINE)NotificationHandler,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&NotificationClientId
|
|||
|
);
|
|||
|
|
|||
|
if ( NotificationThread == NULL ) {
|
|||
|
//
|
|||
|
// Could not create thread, remove the named pipe.
|
|||
|
//
|
|||
|
Error = GetLastError();
|
|||
|
NtClose( NotificationPipeHandle );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = RtlLeaveCriticalSection( &NotificationCriticalSection );
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
if ( Error == ERROR_SUCCESS ) {
|
|||
|
|
|||
|
//
|
|||
|
// Let the server side do its work. Remember that this call
|
|||
|
// is always asynchronous.
|
|||
|
//
|
|||
|
if ( NotificationPipeSaRpc.RpcSecurityDescriptor.lpSecurityDescriptor ) {
|
|||
|
pRpcSa = &NotificationPipeSaRpc;
|
|||
|
} else {
|
|||
|
pRpcSa = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//NotificationPipeName.Length += sizeof(UNICODE_NULL);
|
|||
|
//OurMachineName.Length += sizeof(UNICODE_NULL );
|
|||
|
|
|||
|
// DbgPrint(" Waiting for notification, handle %x\n", EventHandle );
|
|||
|
|
|||
|
Error = (LONG)BaseRegNotifyChangeKeyValue(
|
|||
|
DereferenceRemoteHandle( Handle ),
|
|||
|
(BOOLEAN)fWatchSubtree,
|
|||
|
dwNotifyFilter,
|
|||
|
(DWORD)EventHandle,
|
|||
|
&OurMachineName,
|
|||
|
&NotificationPipeName,
|
|||
|
pRpcSa
|
|||
|
);
|
|||
|
|
|||
|
//NotificationPipeName.Length -= sizeof(UNICODE_NULL);
|
|||
|
//OurMachineName.Length -= sizeof(UNICODE_NULL );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the call went ok. and we are in synchronous mode, we have
|
|||
|
// to wait on the event.
|
|||
|
//
|
|||
|
if ( (Error == ERROR_SUCCESS) && !fAsynchronous ) {
|
|||
|
|
|||
|
NtStatus = NtWaitForSingleObject(
|
|||
|
EventHandle,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
Error = RtlNtStatusToDosError( NtStatus );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If we created an event, we must close it now.
|
|||
|
//
|
|||
|
if ( !fAsynchronous ) {
|
|||
|
|
|||
|
NtStatus = NtClose( EventHandle );
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ));
|
|||
|
}
|
|||
|
|
|||
|
return Error;
|
|||
|
#endif // REMOTE_NOTIFICATION_DISABLED
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifndef REMOTE_NOTIFICATION_DISABLED
|
|||
|
|
|||
|
|
|||
|
LONG
|
|||
|
CreateNotificationPipe(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
Creates the notification named pipe and sets the appropriate
|
|||
|
global variables.
|
|||
|
|
|||
|
Note that the NotificationPipeName set by this function is
|
|||
|
server-relative, so that no conversion is required on the
|
|||
|
server side.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Error code.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
UNICODE_STRING PipeName;
|
|||
|
WCHAR PipeNameBuffer[ MAX_PATH ];
|
|||
|
USHORT OrgSize;
|
|||
|
DWORD Sequence;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
LARGE_INTEGER Timeout;
|
|||
|
OBJECT_ATTRIBUTES Obja;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
DWORD MachineNameLength;
|
|||
|
LONG WinStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Get our machine name
|
|||
|
//
|
|||
|
MachineNameLength = MAX_PATH;
|
|||
|
if ( !GetComputerNameW( OurMachineNameBuffer, &MachineNameLength ) ) {
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
OurMachineName.Buffer = OurMachineNameBuffer;
|
|||
|
OurMachineName.Length = (USHORT)(MachineNameLength * sizeof(WCHAR));
|
|||
|
OurMachineName.MaximumLength = (USHORT)(MAX_PATH * sizeof(WCHAR));
|
|||
|
|
|||
|
//
|
|||
|
// Get the "here" name
|
|||
|
//
|
|||
|
RtlMoveMemory(
|
|||
|
PipeNameBuffer,
|
|||
|
NAMED_PIPE_HERE,
|
|||
|
sizeof( NAMED_PIPE_HERE)
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
PipeName.MaximumLength = MAX_PATH * sizeof(WCHAR);
|
|||
|
PipeName.Buffer = PipeNameBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Remember the size of the base portion of the pipe name, so
|
|||
|
// we can patch it later when we attempt to create the full
|
|||
|
// name.
|
|||
|
//
|
|||
|
OrgSize = (USHORT)(sizeof(NAMED_PIPE_HERE) - sizeof(UNICODE_NULL));
|
|||
|
|
|||
|
//
|
|||
|
// Create the named pipe, if the name is already being used,
|
|||
|
// keep trying with different names.
|
|||
|
//
|
|||
|
Sequence = 0;
|
|||
|
|
|||
|
Timeout.QuadPart = Int32x32To64( -10 * 1000, 50 );
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the security descriptor that will be set in the named pipe
|
|||
|
//
|
|||
|
WinStatus = InitializeNotificationPipeSecurityDescriptor();
|
|||
|
if( WinStatus != ERROR_SUCCESS ) {
|
|||
|
return( WinStatus );
|
|||
|
}
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Get a semi-unique name
|
|||
|
//
|
|||
|
if ( !MakeSemiUniqueName( &NotificationPipeName, Sequence++ ) ) {
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Patch the full pipe name, in case this is not our first
|
|||
|
// try.
|
|||
|
//
|
|||
|
PipeName.Buffer[OrgSize/sizeof(WCHAR)] = UNICODE_NULL;
|
|||
|
PipeName.Length = OrgSize;
|
|||
|
|
|||
|
//
|
|||
|
// Now get the full path of the pipe name
|
|||
|
//
|
|||
|
NtStatus = RtlAppendUnicodeStringToString(
|
|||
|
&PipeName,
|
|||
|
&NotificationPipeName
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&Obja,
|
|||
|
&PipeName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if( SecurityDescriptorInitialized ) {
|
|||
|
Obja.SecurityDescriptor = &SecurityDescriptor;
|
|||
|
}
|
|||
|
|
|||
|
NtStatus = NtCreateNamedPipeFile (
|
|||
|
&NotificationPipeHandle,
|
|||
|
SYNCHRONIZE | GENERIC_READ | FILE_WRITE_ATTRIBUTES,
|
|||
|
&Obja,
|
|||
|
&IoStatusBlock,
|
|||
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|||
|
FILE_CREATE,
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|||
|
FILE_PIPE_MESSAGE_TYPE,
|
|||
|
FILE_PIPE_MESSAGE_MODE,
|
|||
|
FILE_PIPE_QUEUE_OPERATION,
|
|||
|
1,
|
|||
|
0,
|
|||
|
0,
|
|||
|
&Timeout
|
|||
|
);
|
|||
|
|
|||
|
} while ( (NtStatus == STATUS_OBJECT_NAME_EXISTS) &&
|
|||
|
(Sequence <= MAX_PIPE_RETRIES )
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// At this point we don't need the security descriptor anymore.
|
|||
|
// Free the memory allocated for the ACL
|
|||
|
//
|
|||
|
if( SecurityDescriptorInitialized ) {
|
|||
|
RtlFreeHeap( RtlProcessHeap( ), 0, Acl );
|
|||
|
Acl = NULL;
|
|||
|
SecurityDescriptorInitialized = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
return RtlNtStatusToDosError( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
NotificationPipeName.Length += sizeof(UNICODE_NULL);
|
|||
|
OurMachineName.Length += sizeof(UNICODE_NULL );
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NotificationHandler(
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
This function is the entry point of the notification thread.
|
|||
|
The notification thread is created the first time that
|
|||
|
the RegNotifyChangeKeyValue API is called by the process,
|
|||
|
and keeps on running until the process terminates.
|
|||
|
|
|||
|
This function creates a named pipe whose name is given by
|
|||
|
RegNotifyChangeKeyValue to all its servers. The servers
|
|||
|
then use the pipe to indicate that a particular event has
|
|||
|
to be signaled.
|
|||
|
117
|
|||
|
Note that this single thread is in charge of signaling the
|
|||
|
events for all the RegNotifyChangeKeyValue invocations of
|
|||
|
the process. However no state has to be maintained by this
|
|||
|
thread because all the state information is provided by the
|
|||
|
server through the named pipe.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
HANDLE EventHandle;
|
|||
|
|
|||
|
|
|||
|
ASSERT( NotificationPipeHandle != NULL );
|
|||
|
|
|||
|
while ( TRUE ) {
|
|||
|
|
|||
|
//
|
|||
|
// Wait for a connection
|
|||
|
//
|
|||
|
NtStatus = NtFsControlFile(
|
|||
|
NotificationPipeHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&IoStatusBlock,
|
|||
|
FSCTL_PIPE_LISTEN,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
if ( NtStatus == STATUS_PENDING ) {
|
|||
|
|
|||
|
NtStatus = NtWaitForSingleObject(
|
|||
|
NotificationPipeHandle,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ||
|
|||
|
( NtStatus == STATUS_PIPE_CONNECTED ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Read an event handle from the pipe
|
|||
|
//
|
|||
|
NtStatus = NtReadFile(
|
|||
|
NotificationPipeHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&IoStatusBlock,
|
|||
|
( PVOID )&EventHandle,
|
|||
|
sizeof( HANDLE ),
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if ( NtStatus == STATUS_PENDING ) {
|
|||
|
|
|||
|
NtStatus = NtWaitForSingleObject(
|
|||
|
NotificationPipeHandle,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Signal the Event.
|
|||
|
//
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
ASSERT( IoStatusBlock.Information == sizeof( HANDLE ) );
|
|||
|
|
|||
|
//
|
|||
|
// Signal the event
|
|||
|
//
|
|||
|
//DbgPrint(" WINREG: Signaling handle %x\n", EventHandle );
|
|||
|
NtStatus = NtSetEvent( EventHandle, NULL );
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
DbgPrint( "WINREG: Cannot signal notification event 0x%x, status %x\n",
|
|||
|
EventHandle, NtStatus );
|
|||
|
}
|
|||
|
#endif
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
|
|||
|
} else if ( NtStatus != STATUS_PIPE_BROKEN ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG (Notification handler) error reading pipe\n" );
|
|||
|
DbgPrint( " status 0x%x\n", NtStatus );
|
|||
|
#endif
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
}
|
|||
|
|
|||
|
} else if ( NtStatus != STATUS_PIPE_BROKEN &&
|
|||
|
NtStatus != STATUS_PIPE_CLOSING) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "WINREG (Notification): FsControlFile (Connect) status 0x%x\n",
|
|||
|
NtStatus );
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ||
|
|||
|
NtStatus == STATUS_PIPE_BROKEN ||
|
|||
|
NtStatus == STATUS_PIPE_CLOSING ||
|
|||
|
NtStatus == STATUS_PIPE_LISTENING ||
|
|||
|
NtStatus == STATUS_PIPE_BUSY ) {
|
|||
|
|
|||
|
//
|
|||
|
// Disconnect
|
|||
|
//
|
|||
|
NtStatus = NtFsControlFile(
|
|||
|
NotificationPipeHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&IoStatusBlock,
|
|||
|
FSCTL_PIPE_DISCONNECT,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
if ( NtStatus == STATUS_PENDING) {
|
|||
|
|
|||
|
NtStatus = NtWaitForSingleObject(
|
|||
|
NotificationPipeHandle,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( !NT_SUCCESS( NtStatus ) ) {
|
|||
|
DbgPrint( "WINREG (Notification): FsControlFile (Disconnect) status 0x%x\n",
|
|||
|
NtStatus );
|
|||
|
}
|
|||
|
#endif
|
|||
|
ASSERT( NT_SUCCESS( NtStatus ) );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif // REMOTE_NOTIFICATION_DISABLED
|