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
|