/****************************************************************************/ // keynode.cpp // // Copyright (C) 1997-1999 Microsoft Corp. /****************************************************************************/ #include #include "KeyNode.h" extern ULONG g_length_TERMSRV_USERREGISTRY_DEFAULT; extern ULONG g_length_TERMSRV_INSTALL; extern WCHAR g_debugFileName[MAX_PATH]; extern FILE *g_debugFilePointer; extern BOOLEAN g_debugIO; KeyBasicInfo::KeyBasicInfo(): pNameSz(NULL) { size = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH*sizeof(WCHAR); pInfo = ( KEY_BASIC_INFORMATION *)RtlAllocateHeap(RtlProcessHeap(), 0, size ); if (!pInfo) { status = STATUS_NO_MEMORY; pInfo=NULL; } else status = STATUS_SUCCESS; } KeyBasicInfo::~KeyBasicInfo() { if (pInfo) { RtlFreeHeap( RtlProcessHeap(), 0, pInfo); } if (pNameSz) { delete pNameSz; } } PCWSTR KeyBasicInfo::NameSz() { if (Ptr()->NameLength < 2 * MAX_PATH ) { if (!pNameSz) { pNameSz = new WCHAR [ MAX_PATH + 1 ]; } // the reason we re do this every call of NameSz() is because // Ptr() might changes, since KeyBasicInfo is being used as a // scratch pad and passed around for storing pointers to some // basic set of info on any key. // see if allocation was successful if ( pNameSz ) { for ( ULONG i=0; i < Ptr()->NameLength / sizeof(WCHAR) ; i++) { pNameSz[i] = ( (USHORT)Ptr()->Name[i] ); } pNameSz[i]=L'\0'; } } else { status = STATUS_NO_MEMORY; pNameSz[0]=L'\0'; } return pNameSz; } #if 0 // NOT USED yet! KeyNodeInfo::KeyNodeInfo() { size = sizeof(KEY_NODE_INFORMATION) + MAX_PATH*sizeof(WCHAR); pInfo = ( KEY_NODE_INFORMATION *)RtlAllocateHeap(RtlProcessHeap(), 0, size ); if (!pInfo) { status = STATUS_NO_MEMORY; pInfo=NULL; } else status = STATUS_SUCCESS; } KeyNodeInfo::~KeyNodeInfo() { if (pInfo) { RtlFreeHeap( RtlProcessHeap(), 0, pInfo); } } #endif KeyFullInfo::KeyFullInfo() : pInfo(NULL) { size = sizeof(KEY_FULL_INFORMATION) + MAX_PATH*sizeof(WCHAR); pInfo = ( KEY_FULL_INFORMATION *)RtlAllocateHeap(RtlProcessHeap(), 0, size ); if (!pInfo) { status = STATUS_NO_MEMORY; pInfo=NULL; } else status = STATUS_SUCCESS; } KeyFullInfo::~KeyFullInfo() { if (pInfo) { RtlFreeHeap( RtlProcessHeap(), 0, pInfo); } } KeyNode::KeyNode(HANDLE root, ACCESS_MASK access, PCWSTR name ) : root(NULL), hKey(NULL), accessMask(NULL),basic(NULL),full(NULL), pFullPath(NULL), pNameSz(NULL) { hKey = NULL; PCWSTR n = name; accessMask = access; RtlInitUnicodeString(&uniName, n); InitializeObjectAttributes(&ObjAttr, &uniName, OBJ_CASE_INSENSITIVE, root, NULL); status=STATUS_SUCCESS; } KeyNode::KeyNode(KeyNode *pParent, KeyBasicInfo *pInfo ) : root(NULL), hKey(NULL), accessMask(NULL),basic(NULL), full(NULL), pFullPath(NULL), pNameSz(NULL) { hKey = NULL; PCWSTR n = pInfo->NameSz(); accessMask = pParent->Masks(); RtlInitUnicodeString(&uniName, n); InitializeObjectAttributes(&ObjAttr, &uniName, OBJ_CASE_INSENSITIVE, pParent->Key(), NULL); status=STATUS_SUCCESS; } KeyNode::~KeyNode() { Close(); if (basic) { delete basic; } if (full) { delete full; } if (pFullPath) { RtlFreeHeap(RtlProcessHeap(), 0, pFullPath); } if( pNameSz ) { delete pNameSz; } } NTSTATUS KeyNode::Open() { status = NtOpenKey(&hKey, accessMask, &ObjAttr); if ( !NT_SUCCESS( status)) { hKey=NULL; // Debug(DBG_OPEN_FAILED ); } return status; } NTSTATUS KeyNode::Close() { if ( hKey ) { status = NtClose( hKey ); hKey = 0; } return status; } NTSTATUS KeyNode::Create( UNICODE_STRING *uClass) { ULONG ultmp; status = NtCreateKey(&hKey, accessMask, &ObjAttr, 0, uClass, REG_OPTION_NON_VOLATILE, &ultmp); // Debug(DBG_CREATE); return status; } // Recursively create the reg path given by the uniName member variable // Upon completion, open the reg key for access. NTSTATUS KeyNode::CreateEx( UNICODE_STRING *uClass) { ULONG wsize = uniName.Length/sizeof(WCHAR); PWCHAR pTmpFullPath = new WCHAR[ uniName.Length + sizeof( WCHAR ) ]; if(!pTmpFullPath) { status = STATUS_NO_MEMORY; return status; } wcsncpy(pTmpFullPath, uniName.Buffer , wsize); pTmpFullPath[ wsize ] = L'\0'; PWCHAR p; WCHAR sep[]= {L"\\"}; p = wcstok( pTmpFullPath, sep); // we know how many keys to create now. // start over again wcsncpy(pTmpFullPath, uniName.Buffer , wsize ); pTmpFullPath[ wsize ] = L'\0'; KeyNode *pKN1=NULL, *pKN2=NULL; p = wcstok( pTmpFullPath, sep); // the first item is "Registry", make it "\Registry" since we are opening // from the root. PWCHAR pTmpName = new WCHAR[ wcslen(p) + 2 ]; if(!pTmpName) { DELETE_AND_NULL(pTmpFullPath); status = STATUS_NO_MEMORY; return status; } wcscpy(pTmpName, L"\\"); wcscat( pTmpName , p ); NTSTATUS st = STATUS_SUCCESS; while( p != NULL ) { // @@@ // ADD error handling, else you will create keys in the wrong places instead of bailing out. // @@@ if ( pKN2 ) { // ---- STEP 3 --- // NOT-first time around p = wcstok( NULL, sep); if ( p ) // we have more sub keys { pKN1 = new KeyNode( pKN2->Key(), accessMask, p ); if (pKN1) { st = pKN1->Open(); // if Open fails, then key does not exist, so create it if ( !NT_SUCCESS( st )) { st = pKN1->Create(); } } else { status = STATUS_NO_MEMORY; break; } } } else { // ---- STEP 1 --- // First time around, we are opening \Registry node, use // pTmpName instead of "p" pKN1 = new KeyNode( NULL, accessMask , pTmpName ); if (pKN1) { st = pKN1->Open(); } else { status = STATUS_NO_MEMORY ; break; } } p = wcstok( NULL, sep); if (p) // we have more sub keys { // ---- STEP 2 --- pKN2 = new KeyNode( pKN1->Key(), accessMask, p ); if (pKN2 ) { st = pKN2->Open(); if ( !NT_SUCCESS( pKN2->Status() )) { st = pKN2->Create(); } } else { status = STATUS_NO_MEMORY; DELETE_AND_NULL (pKN1); break; } DELETE_AND_NULL (pKN1); pKN1 = pKN2; } } DELETE_AND_NULL( pKN2 ); // since the last node was created above, now we can open ourselfs incase // caller wants to use us. if ( NT_SUCCESS(status) ) { Open(); } DELETE_AND_NULL(pTmpName); DELETE_AND_NULL(pTmpFullPath); return status; } NTSTATUS KeyNode::Delete() { if (hKey) { status = NtDeleteKey( hKey ); // Debug(DBG_DELETE); } return status; } NTSTATUS KeyNode::DeleteSubKeys() { if (hKey && NT_SUCCESS( status )) { KeyBasicInfo basicInfo; status = basicInfo.Status(); if (NT_SUCCESS( status )) { status = EnumerateAndDeleteSubKeys( this, &basicInfo ); } } return status; } NTSTATUS KeyNode::EnumerateAndDeleteSubKeys( IN KeyNode *pSource, IN KeyBasicInfo *pBasicInfo ) { NTSTATUS st = STATUS_SUCCESS; ULONG ulCount=0; ULONG ultemp; while (NT_SUCCESS(st) && st != STATUS_NO_MORE_ENTRIES ) { ULONG ultemp; NTSTATUS st2; st = NtEnumerateKey( pSource->Key(), ulCount, pBasicInfo->Type(), pBasicInfo->Ptr(), pBasicInfo->Size(), &ultemp); if (NT_SUCCESS(st) && st != STATUS_NO_MORE_ENTRIES ) { pBasicInfo->Ptr()->Name[ pBasicInfo->Ptr()->NameLength/sizeof(WCHAR) ] = L'\0'; KeyNode SourcesubKey(pSource, pBasicInfo); if (NT_SUCCESS( SourcesubKey.Open() ) ) { // enumerate sub key down. st2 = EnumerateAndDeleteSubKeys( &SourcesubKey, pBasicInfo ); } st = SourcesubKey.Delete(); } } return st; } #if 0 NTSTATUS KeyNode::Query( KEY_NODE_INFORMATION **result , ULONG *resultSize) { if ( hKey ) { // first time around we allocate memory and keep using it // as our scratch pad if (!node ) { node = new KeyNodeInfo(); } status = NtQueryKey(hKey, node->Type(), // Keynode, node->Ptr(), node->Size(), resultSize); *result = node->Ptr(); } else status = STATUS_OBJECT_NAME_NOT_FOUND; // need to call open or key is not found return status; } #endif NTSTATUS KeyNode::Query( KEY_FULL_INFORMATION **result , ULONG *pResultSize) { if ( hKey ) { // first time around we allocate memory and keep using it // as our scratch pad if (!full ) { full = new KeyFullInfo(); } if (full) { status = NtQueryKey(hKey, full->Type(), // KeyFullInformation, full->Ptr(), full->Size(), pResultSize); *result = full->Ptr(); } else status = STATUS_NO_MEMORY ; } else status = STATUS_OBJECT_NAME_NOT_FOUND; // need to call open or key is not found return status; } //This will allocate and set pFullPath. pFullPath is a global variable which is deallocated //by the destructor, so this function should only be called if pFullPath has not already //been allocated for this object. NTSTATUS KeyNode::GenerateFullPath() { status = STATUS_SUCCESS; // A key handle or root directory was specified, so get its path if (hKey) { ULONG ultemp = 0; ultemp = sizeof(UNICODE_STRING) + (sizeof(WCHAR) * MAX_PATH * 2); pFullPath = RtlAllocateHeap(RtlProcessHeap(), 0, ultemp); // Got the buffer OK, query the path if (pFullPath) { // Get the path for key or root directory status = NtQueryObject(hKey , ObjectNameInformation, (PVOID)pFullPath, ultemp, NULL); if (!NT_SUCCESS(status)) RtlFreeHeap(RtlProcessHeap(), 0, pFullPath); } else status = STATUS_NO_MEMORY; } else status = STATUS_OBJECT_NAME_NOT_FOUND; // need to call open or key is not found return (status); } NTSTATUS KeyNode::GetPath(PWCHAR *pwch) { status = STATUS_SUCCESS; if (pFullPath == NULL) status = GenerateFullPath(); if (NT_SUCCESS(status)) { // Build the full path to the key to be created *pwch = ((PUNICODE_STRING)pFullPath)->Buffer; // Make sure the string is zero terminated ULONG ulWcharLength = 0; ulWcharLength = ((PUNICODE_STRING)pFullPath)->Length / sizeof(WCHAR); (*pwch)[ulWcharLength] = L'\0'; } return (status); } void KeyNode::Debug( DebugType type ) { if ( debug ) { ULONG i; switch( type ) { case DBG_DELETE : fwprintf( g_debugFilePointer , L"Deleted key=%lx; status=%lx, name=", status, hKey ); DbgPrint("Deleted key=%lx; status=%lx, name=", status, hKey ); break; case DBG_OPEN_FAILED: fwprintf( g_debugFilePointer, L"Unable to Open, status=%lx, name=", hKey, status ); DbgPrint("Unable to Open, status=%lx, name=", hKey, status ); break; case DBG_KEY_NAME: fwprintf( g_debugFilePointer, L"hKey=%lx, name=", hKey); DbgPrint("hKey=%lx, name=", hKey); break; case DBG_CREATE: fwprintf( g_debugFilePointer, L"Created hKey=%lx, status=%lx,name=", hKey, status); DbgPrint("Created hKey=%lx, status=%lx,name=", hKey, status ); break; } fwprintf( g_debugFilePointer, L"%s\n",NameSz() ); fflush( g_debugFilePointer ); DbgPrint("%s\n",(char *)NameSz() ); } } PCWSTR KeyNode::NameSz() { if (!pNameSz) { pNameSz = new WCHAR [ uniName.Length / sizeof(WCHAR) + 1 ]; if (pNameSz) { for ( ULONG i=0; i < uniName.Length / sizeof(WCHAR) ; i++) { pNameSz[i] = ( (USHORT)uniName.Buffer[i] ); } pNameSz[i]=L'\0'; } else { status = STATUS_NO_MEMORY; } } return pNameSz; } NTSTATUS KeyNode::GetFullInfo( KeyFullInfo **p) { // do a self query if ( !full ) { ULONG size; KEY_FULL_INFORMATION *tmp; Query( &tmp , &size ); } *p = full; return status; }