/*++ 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 #include #include #include "regrpc.h" #include "localreg.h" #include "regclass.h" #include "regnccls.h" #include 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 )