|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
Regnccls.c
Abstract:
This file contains functions needed for handling change notifications in the classes portion of the registry
Author:
Adam P. Edwards (adamed) 14-Nov-1997
Key Functions:
BaseRegNotifyClassKey
Notes:
--*/
#ifdef LOCAL
#include <rpc.h>
#include <string.h>
#include <wchar.h>
#include "regrpc.h"
#include "localreg.h"
#include "regclass.h"
#include "regnccls.h"
#include <malloc.h>
NTSTATUS BaseRegNotifyClassKey( IN HKEY hKey, IN HANDLE hEvent, IN PIO_STATUS_BLOCK pLocalIoStatusBlock, IN DWORD dwNotifyFilter, IN BOOLEAN fWatchSubtree, IN BOOLEAN fAsynchronous) { NTSTATUS Status; HKEY hkUser; HKEY hkMachine; SKeySemantics KeyInfo; UNICODE_STRING EmptyString = {0, 0, 0}; BYTE rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(OBJECT_NAME_INFORMATION)]; OBJECT_ATTRIBUTES Obja; BOOL fAllocatedPath;
//
// Set buffer to store info about this key
//
KeyInfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf; KeyInfo._cbFullPath = sizeof(rgNameBuf); KeyInfo._fAllocedNameBuf = FALSE;
//
// get information about this key
//
Status = BaseRegGetKeySemantics(hKey, &EmptyString, &KeyInfo);
if (!NT_SUCCESS(Status)) { return Status; }
//
// Initialize conditionally freed resources
//
hkUser = NULL; hkMachine = NULL;
fAllocatedPath = FALSE; Obja.ObjectName = NULL;
//
// Now get handles for both user and machine versions of the key
//
Status = BaseRegGetUserAndMachineClass( &KeyInfo, hKey, KEY_NOTIFY, &hkUser, &hkMachine);
if (!NT_SUCCESS(Status)) { goto cleanup; }
if (fWatchSubtree || (hkUser && hkMachine)) {
//
// This will return the closest ancestor to the
// nonexistent translated key -- note that it allocates memory
// to the Obja.ObjectName member, so we need to free that on
// success
//
Status = BaseRegGetBestAncestor( &KeyInfo, hkUser, hkMachine, &Obja);
fAllocatedPath = Obja.ObjectName != NULL;
if (!NT_SUCCESS(Status)) { goto cleanup; }
//
// Ask for the notify on both user and machine keys (or
// the closest approximation). Note that we pass a full path --
// if we used an relative path with an object handle instead, we
// would never have an opportunity to close the object, so we would
// leak objects
//
//
Status = NtNotifyChangeMultipleKeys( hKey, 1, &Obja, hEvent, NULL, NULL, pLocalIoStatusBlock, dwNotifyFilter, fWatchSubtree, NULL, 0, fAsynchronous );
} else {
Status = NtNotifyChangeKey( hkUser ? hkUser : hkMachine, hEvent, NULL, NULL, pLocalIoStatusBlock, dwNotifyFilter, fWatchSubtree, NULL, 0, fAsynchronous ); }
cleanup:
//if (!NT_SUCCESS(Status)) {
if (hkUser && (hkUser != hKey)) { NtClose(hkUser); }
if (hkMachine && (hkMachine != hKey)) { NtClose(hkMachine); } //}
if (fAllocatedPath) { RegClassHeapFree(Obja.ObjectName); }
return Status; }
NTSTATUS BaseRegGetBestAncestor( IN SKeySemantics* pKeySemantics, IN HKEY hkUser, IN HKEY hkMachine, IN POBJECT_ATTRIBUTES pObja) /*++
Routine Description:
Finds a full object path for the closest ancestor for a key described by a key semantics structure
Arguments: pKeySemantics - contains information about a registry key hkUser - handle to a user class version of the key above hkMachine - handle to a machine class version of the key above pObja - Object Attributes structure to initialize with a full object path for the closest ancestor -- not that memory is allocated for the ObjectName member of the structure which must be freed by the caller -- caller should check this member to see if it's non-NULL, regardless of success code returned by function
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/ { USHORT PrefixLen; NTSTATUS Status; PUNICODE_STRING pKeyPath; USHORT uMaxLen;
//
// Allocate memory for the Obja's ObjectName member
//
uMaxLen = (USHORT) pKeySemantics->_pFullPath->NameLength + REG_CLASSES_SUBTREE_PADDING;
pKeyPath = RegClassHeapAlloc(uMaxLen + sizeof(*pKeyPath));
if (!(pKeyPath)) { return STATUS_NO_MEMORY; }
//
// Now initialize the structure
//
pKeyPath->MaximumLength = uMaxLen; pKeyPath->Buffer = (WCHAR*) (((PBYTE) pKeyPath) + sizeof(*pKeyPath));
//
// Now form a version of this key path in the opposite tree
//
if (pKeySemantics->_fUser) { Status = BaseRegTranslateToMachineClassKey( pKeySemantics, pKeyPath, &PrefixLen);
} else {
Status = BaseRegTranslateToUserClassKey( pKeySemantics, pKeyPath, &PrefixLen); } //
// Make sure the caller has a reference to allocated memory
//
pObja->ObjectName = pKeyPath;
if (!NT_SUCCESS(Status)) { goto cleanup; }
//
// Set up the object attributes with this translated key so
// we can use the structure to notify keys
//
InitializeObjectAttributes( pObja, pKeyPath, OBJ_CASE_INSENSITIVE, NULL, // using absolute path, no hkey
NULL);
//
// If we were supplied both keys, then they both exist,
// so we can simply use the translated path above
//
if (hkUser && hkMachine) { goto cleanup; }
//
// At this point, we know the translated path doesn't exist,
// since we only have a handle for one of the paths. Therefore
// we will attempt to find an approximation. Note that the
// manipulation of KeyPath below affects the Obja passed in since
// the Obja struct references KeyPath
//
do { WCHAR* pBufferEnd; HKEY hkExistingKey;
//
// Find the last pathsep in the current key path
//
pBufferEnd = wcsrchr(pKeyPath->Buffer, L'\\');
//
// We should never get NULL here, because all keys
// have the ancestory \Registry\User or \Registry\Machine,
// each which have two pathseps to spare -- the loop
// terminates once that path is shorter than those prefixes,
// so we should never encounter this situation
//
ASSERT(pBufferEnd);
//
// Now truncate the string
//
*pBufferEnd = L'\0';
//
// Adjust the unicode string structure to conform
// to the truncated string
//
RtlInitUnicodeString(pKeyPath, pKeyPath->Buffer);
//
// Now attempt to open with this truncated path
//
Status = NtOpenKey( &hkExistingKey, KEY_NOTIFY, pObja);
//
// If we do open it, we will close it and not pass this object
// since we want our obja to use a full path and not a relative
// path off a kernel object
//
if (NT_SUCCESS(Status)) { NtClose(hkExistingKey); break; }
//
// If we get any error besides a key not found error, then our reason
// for failing the open is not because the key did not exist, but because
// of some other error, most likely access denied.
//
if (STATUS_OBJECT_NAME_NOT_FOUND != Status) { break; }
} while (pKeyPath->Length > PrefixLen);
cleanup:
return Status; }
#endif // defined ( LOCAL )
|