/*++ Copyright (c) 2000 Microsoft Corporation Module Name: schemadynamic.cpp Abstract: This file contains the implementation of the CDynSchema class. This class contains the dynamic schema structures. It also contains the rules for populating the schema structures. Author: Mohit Srivastava 28-Nov-00 Revision History: --*/ #include "iisprov.h" #define USE_DEFAULT_VALUES #define USE_DEFAULT_BINARY_VALUES CDynSchema* g_pDynSch = NULL; HRESULT CDynSchema::Initialize() /*++ Synopsis: If fails, object must be destroyed. If succeeds, object is ready for use. Return Value: --*/ { DBG_ASSERT(m_bInitCalled == false); DBG_ASSERT(m_bInitSuccessful == false); HRESULT hr = WBEM_S_NO_ERROR; m_bInitCalled = true; hr = m_hashProps.Wmi_Initialize(); if(FAILED(hr)) { goto exit; } hr = m_hashClasses.Wmi_Initialize(); if(FAILED(hr)) { goto exit; } hr = m_hashAssociations.Wmi_Initialize(); if(FAILED(hr)) { goto exit; } hr = m_hashKeyTypes.Wmi_Initialize(); if(FAILED(hr)) { goto exit; } hr = m_poolAssociations.Initialize(10); if(FAILED(hr)) { goto exit; } hr = m_poolClasses.Initialize(10); if(FAILED(hr)) { goto exit; } hr = m_poolProps.Initialize(10); if(FAILED(hr)) { goto exit; } hr = m_poolKeyTypes.Initialize(10); if(FAILED(hr)) { goto exit; } hr = m_poolKeyTypeNodes.Initialize(10); if(FAILED(hr)) { goto exit; } hr = m_spoolStrings.Initialize(); if(FAILED(hr)) { goto exit; } hr = m_apoolPMbp.Initialize(); if(FAILED(hr)) { goto exit; } hr = m_apoolBytes.Initialize(); if(FAILED(hr)) { goto exit; } exit: if(SUCCEEDED(hr)) { m_bInitSuccessful = true; } return hr; } void CDynSchema::LogConflicts(LPCWSTR wszSettingsClassName) // Logs an error in system events if a schema conflict occurs { WMI_ASSOCIATION *pWmiAssoc = NULL; WMI_CLASS* pWmiSettingsClass = NULL; if (SUCCEEDED(m_hashClasses.Wmi_GetByKey(wszSettingsClassName, &pWmiSettingsClass)) || SUCCEEDED(m_hashAssociations.Wmi_GetByKey(wszSettingsClassName, &pWmiAssoc))) { const WCHAR * EventLogStrings[2]; const LPCWSTR wszErrorString = L"WMI Schema warning - class already exists"; EventLogStrings[0] = wszSettingsClassName; EventLogStrings[1] = wszErrorString; EVENT_LOG m_EventLog(L"WMI Schema"); m_EventLog.LogEvent( TYPE_E_NAMECONFLICT, // message id 2, // count of strings EventLogStrings, // array of strings 0 // error code ); } } HRESULT CDynSchema::RulePopulateFromStatic() /*++ Synopsis: Populates hashtables with pointers to hardcoded schema. Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); HRESULT hr = WBEM_S_NO_ERROR; // // Populate Properties // if(METABASE_PROPERTY_DATA::s_MetabaseProperties != NULL) { METABASE_PROPERTY* pStatMbpCurrent; for(ULONG i = 0; ; i++) { pStatMbpCurrent = METABASE_PROPERTY_DATA::s_MetabaseProperties[i]; if(pStatMbpCurrent == NULL) { break; } hr = m_hashProps.Wmi_Insert(pStatMbpCurrent->pszPropName, pStatMbpCurrent); if(FAILED(hr)) { goto exit; } } } // // Populate KeyTypes // METABASE_KEYTYPE** apMetabaseKeyTypes; apMetabaseKeyTypes = METABASE_KEYTYPE_DATA::s_MetabaseKeyTypes; for(ULONG i = 0; apMetabaseKeyTypes[i] != NULL; i++) { if( apMetabaseKeyTypes[i]->m_pszName != NULL ) { apMetabaseKeyTypes[i]->m_pKtListInverseCCL = NULL; hr = m_hashKeyTypes.Wmi_Insert(apMetabaseKeyTypes[i]->m_pszName, apMetabaseKeyTypes[i]); if(FAILED(hr)) { goto exit; } } } exit: return hr; } HRESULT CDynSchema::Rule2PopulateFromStatic() /*++ Synopsis: Populates hashtables with pointers to hardcoded schema. Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); HRESULT hr = S_OK; int i; // // Populate Classes // WMI_CLASS* pStatWmiClassCurrent; for(i = 0; ; i++) { pStatWmiClassCurrent = WMI_CLASS_DATA::s_WmiClasses[i]; if(pStatWmiClassCurrent == NULL) { break; } hr = m_hashClasses.Wmi_Insert( pStatWmiClassCurrent->pszClassName, pStatWmiClassCurrent); if(FAILED(hr)) { goto exit; } } // // Populate Associations // WMI_ASSOCIATION* pStatWmiAssocCurrent; for(i = 0; ; i++) { pStatWmiAssocCurrent = WMI_ASSOCIATION_DATA::s_WmiAssociations[i]; if(pStatWmiAssocCurrent == NULL) { break; } hr = m_hashAssociations.Wmi_Insert( pStatWmiAssocCurrent->pszAssociationName, pStatWmiAssocCurrent); if(FAILED(hr)) { goto exit; } } exit: return hr; } HRESULT CDynSchema::RuleKeyType( const CTableMeta *i_pTableMeta) /*++ Synopsis: If not already in hashtable of keytypes, a keytype structure is allocated thru the keytype pool. Then, a pointer to it is inserted in hashtable. Arguments: [i_pTableMeta] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pTableMeta != NULL); HRESULT hr = WBEM_S_NO_ERROR; HRESULT hrTemp = WBEM_S_NO_ERROR; METABASE_KEYTYPE* pktNew; LPWSTR wszNew; hrTemp = m_hashKeyTypes.Wmi_GetByKey( i_pTableMeta->TableMeta.pInternalName, &pktNew); if(FAILED(hrTemp)) { hr = m_spoolStrings.GetNewString(i_pTableMeta->TableMeta.pInternalName, &wszNew); if(FAILED(hr)) { goto exit; } hr = m_poolKeyTypes.GetNewElement(&pktNew); if(FAILED(hr)) { goto exit; } pktNew->m_pszName = wszNew; pktNew->m_pKtListInverseCCL = NULL; m_hashKeyTypes.Wmi_Insert(wszNew, pktNew); if(FAILED(hr)) { goto exit; } } exit: return hr; } HRESULT CDynSchema::RuleWmiClassDescription( const CTableMeta* i_pTableMeta, WMI_CLASS* i_pElementClass, WMI_CLASS* i_pSettingClass) const /*++ Synopsis: Sets WMI_CLASS::pDescription if needed. This pointer will be invalid after initialization since it points to catalog. Arguments: [i_pTableMeta] - [i_pElementClass] - [i_pSettingClass] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pTableMeta != NULL); DBG_ASSERT(i_pElementClass != NULL); DBG_ASSERT(i_pSettingClass != NULL); HRESULT hr = WBEM_S_NO_ERROR; if(i_pTableMeta->TableMeta.pDescription != NULL) { i_pElementClass->pszDescription = i_pTableMeta->TableMeta.pDescription; i_pSettingClass->pszDescription = i_pTableMeta->TableMeta.pDescription; } return hr; } HRESULT CDynSchema::RuleWmiClass( const CTableMeta* i_pTableMeta, WMI_CLASS** o_ppElementClass, WMI_CLASS** o_ppSettingClass, DWORD io_adwIgnoredProps[], BOOL i_bUserDefined) /*++ Synopsis: Creates an Element and Setting class based on i_pTableMeta->TableMeta.pInternalName. If not in hashtable of classes, these classes are inserted. At bottom, RuleProperties is called to set up list of properties for each class. Arguments: [i_pTableMeta] - [o_ppElementClass] - can be NULL [o_ppSettingClass] - can be NULL Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pTableMeta != NULL); WMI_CLASS* pWmiClass = NULL; WMI_CLASS* pWmiSettingsClass = NULL; LPWSTR wszClassName, wszSettingsClassName; LPWSTR wszParentClassName, wszParentSettingsClassName; HRESULT hr = WBEM_S_NO_ERROR; HRESULT hrTemp = WBEM_S_NO_ERROR; ULONG cPropsAndTagsRW = 0; ULONG cPropsAndTagsRO = 0; ULONG iShipped = 0; CColumnMeta* pColumnMeta; ULONG cchTable; // // Ignore table if it has no name // if(i_pTableMeta->TableMeta.pInternalName == NULL) { hr = WBEM_S_NO_ERROR; goto exit; } // // Determine iShipped and Parent Classes // if(fTABLEMETA_USERDEFINED & *i_pTableMeta->TableMeta.pSchemaGeneratorFlags) { iShipped = USER_DEFINED_TO_REPOSITORY; DBG_ASSERT(iShipped != USER_DEFINED_NOT_TO_REPOSITORY); wszParentClassName = g_wszExtElementParent; wszParentSettingsClassName = g_wszExtSettingParent; } else { if(fTABLEMETA_EXTENDED & *i_pTableMeta->TableMeta.pSchemaGeneratorFlags) { iShipped = EXTENDED; } else { iShipped = SHIPPED_TO_MOF; } wszParentClassName = g_wszElementParent; wszParentSettingsClassName = g_wszSettingParent; } // // Determine number of RO and RW properties // for(ULONG idxProps = 0; idxProps < i_pTableMeta->ColCount(); idxProps++) { pColumnMeta = i_pTableMeta->paColumns[idxProps]; if(*pColumnMeta->ColumnMeta.pSchemaGeneratorFlags & fCOLUMNMETA_CACHE_PROPERTY_MODIFIED) { cPropsAndTagsRW += pColumnMeta->cNrTags + 1; } else { cPropsAndTagsRO += pColumnMeta->cNrTags + 1; } } cchTable = wcslen(i_pTableMeta->TableMeta.pInternalName); // // The keytype should already exist. // METABASE_KEYTYPE* pktTemp; hrTemp = m_hashKeyTypes.Wmi_GetByKey(i_pTableMeta->TableMeta.pInternalName, &pktTemp); if( FAILED(hrTemp) ) { goto exit; } // // The Element class (named PrefixC) // hr = m_spoolStrings.GetNewArray(g_cchIIs_+cchTable+1, &wszClassName); if(FAILED(hr)) { goto exit; } memcpy(wszClassName, g_wszIIs_, sizeof(WCHAR)*g_cchIIs_); memcpy(&wszClassName[g_cchIIs_], i_pTableMeta->TableMeta.pInternalName, sizeof(WCHAR)*(cchTable+1)); if (i_bUserDefined) { LogConflicts(wszClassName); } if(FAILED(m_hashClasses.Wmi_GetByKey(wszClassName, &pWmiClass))) { hr = m_poolClasses.GetNewElement(&pWmiClass); if(FAILED(hr)) { goto exit; } pWmiClass->pkt = pktTemp; pWmiClass->pszClassName = wszClassName; pWmiClass->pszMetabaseKey = L"/LM"; pWmiClass->pszKeyName = L"Name"; pWmiClass->ppMethod = NULL; pWmiClass->pszParentClass = wszParentClassName; pWmiClass->bCreateAllowed = true; pWmiClass->pszDescription = NULL; hr = m_hashClasses.Wmi_Insert(wszClassName, pWmiClass); if(FAILED(hr)) { goto exit; } } pWmiClass->ppmbp = NULL; pWmiClass->dwExtended = iShipped; // // The Settings class (named PrefixCSetting) // hr = m_spoolStrings.GetNewArray(g_cchIIs_+cchTable+g_cchSettings+1, &wszSettingsClassName); if(FAILED(hr)) { goto exit; } memcpy(wszSettingsClassName, g_wszIIs_, sizeof(WCHAR)*g_cchIIs_); memcpy(&wszSettingsClassName[g_cchIIs_], i_pTableMeta->TableMeta.pInternalName, sizeof(WCHAR)*cchTable); memcpy(&wszSettingsClassName[g_cchIIs_+cchTable], g_wszSettings, sizeof(WCHAR)*(g_cchSettings+1)); if (i_bUserDefined) { LogConflicts(wszSettingsClassName); } if(FAILED(m_hashClasses.Wmi_GetByKey(wszSettingsClassName, &pWmiSettingsClass))) { hr = m_poolClasses.GetNewElement(&pWmiSettingsClass); if(FAILED(hr)) { goto exit; } pWmiSettingsClass->pkt = pktTemp; pWmiSettingsClass->pszClassName = wszSettingsClassName; pWmiSettingsClass->pszMetabaseKey = L"/LM"; pWmiSettingsClass->pszKeyName = L"Name"; pWmiSettingsClass->ppMethod = NULL; pWmiSettingsClass->pszParentClass = wszParentSettingsClassName; pWmiSettingsClass->bCreateAllowed = true; pWmiSettingsClass->pszDescription = NULL; hr = m_hashClasses.Wmi_Insert(wszSettingsClassName, pWmiSettingsClass); if(FAILED(hr)) { goto exit; } } pWmiSettingsClass->ppmbp = NULL; pWmiSettingsClass->dwExtended = iShipped; // // Fill in the ppmbp field // hr = RuleProperties( i_pTableMeta, cPropsAndTagsRO, pWmiClass, cPropsAndTagsRW, pWmiSettingsClass, io_adwIgnoredProps); if(FAILED(hr)) { goto exit; } exit: if(SUCCEEDED(hr)) { if(o_ppElementClass != NULL) { *o_ppElementClass = pWmiClass; } if(o_ppSettingClass != NULL) { *o_ppSettingClass = pWmiSettingsClass; } } return hr; } HRESULT CDynSchema::RuleProperties( const CTableMeta* i_pTableMeta, ULONG i_cPropsAndTagsRO, WMI_CLASS* io_pWmiClass, ULONG i_cPropsAndTagsRW, WMI_CLASS* io_pWmiSettingsClass, DWORD io_adwIgnoredProps[]) /*++ Synopsis: Given i_pTableMeta, puts the properties either under the Element class or under the Setting class. Arguments: [i_pTableMeta] - [i_cPropsAndTagsRO] - [o_papMbp] - [i_cPropsAndTagsRW] - [o_papMbpSettings] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pTableMeta != NULL); DBG_ASSERT(io_pWmiClass != NULL); DBG_ASSERT(io_pWmiSettingsClass != NULL); // DBG_ASSERT(sizeof(io_awszIgnoredProps) >= sizeof(g_awszPropIgnoreList)); HRESULT hr = WBEM_S_NO_ERROR; CColumnMeta* pColumnMeta = NULL; METABASE_PROPERTY* pMbp; ULONG idxProps = 0; ULONG idxTags = 0; ULONG idxPropsAndTagsRO = 0; ULONG idxPropsAndTagsRW = 0; METABASE_PROPERTY*** papMbp = &io_pWmiClass->ppmbp; METABASE_PROPERTY*** papMbpSettings = &io_pWmiSettingsClass->ppmbp; // // Allocate enough memory for the RO properties // if(i_cPropsAndTagsRO > 0) { hr = m_apoolPMbp.GetNewArray(i_cPropsAndTagsRO+1, papMbp); if(FAILED(hr)) { goto exit; } memset(*papMbp, 0, (1+i_cPropsAndTagsRO)*sizeof(METABASE_PROPERTY*)); } // // Allocate enough memory for the RW properties // if(i_cPropsAndTagsRW > 0) { hr = m_apoolPMbp.GetNewArray(i_cPropsAndTagsRW+1, papMbpSettings); if(FAILED(hr)) { goto exit; } memset(*papMbpSettings, 0, (1+i_cPropsAndTagsRW)*sizeof(METABASE_PROPERTY*)); } // // Walk thru all the properties // for (idxProps=0, idxPropsAndTagsRO = 0, idxPropsAndTagsRW = 0; idxProps < i_pTableMeta->ColCount(); ++idxProps) { pColumnMeta = i_pTableMeta->paColumns[idxProps]; // // Ignore property if its in g_adwPropIgnoreList and store the prop in // io_adwIgnoredProps // if( (*pColumnMeta->ColumnMeta.pSchemaGeneratorFlags & fCOLUMNMETA_HIDDEN) || IgnoreProperty(io_pWmiClass->pkt, *(pColumnMeta->ColumnMeta.pID), io_adwIgnoredProps) ) { continue; } // // Call RulePropertiesHelper if Property is not already in the // properties hashtable // if(FAILED(m_hashProps.Wmi_GetByKey(pColumnMeta->ColumnMeta.pInternalName, &pMbp))) { hr = RulePropertiesHelper(pColumnMeta, &pMbp, NULL); if(FAILED(hr)) { goto exit; } } // // If RW, put pointer to property in Setting class, else in Element // class. // if(*pColumnMeta->ColumnMeta.pSchemaGeneratorFlags & fCOLUMNMETA_CACHE_PROPERTY_MODIFIED) { (*papMbpSettings)[idxPropsAndTagsRW] = pMbp; idxPropsAndTagsRW++; } else { (*papMbp)[idxPropsAndTagsRO] = pMbp; idxPropsAndTagsRO++; } // // Same steps as above, except for the tags. // for(idxTags=0; idxTags < pColumnMeta->cNrTags; idxTags++) { if(FAILED(m_hashProps.Wmi_GetByKey(pColumnMeta->paTags[idxTags]->pInternalName, &pMbp))) { hr = RulePropertiesHelper(pColumnMeta, &pMbp, &idxTags); if(FAILED(hr)) { goto exit; } } if(*pColumnMeta->ColumnMeta.pSchemaGeneratorFlags & fCOLUMNMETA_CACHE_PROPERTY_MODIFIED) { (*papMbpSettings)[idxPropsAndTagsRW] = pMbp; idxPropsAndTagsRW++; } else { (*papMbp)[idxPropsAndTagsRO] = pMbp; idxPropsAndTagsRO++; } } } exit: return hr; } HRESULT CDynSchema::RulePropertiesHelper( const CColumnMeta* i_pColumnMeta, METABASE_PROPERTY** o_ppMbp, ULONG* i_idxTag) /*++ Synopsis: This class creates a property and inserts it into the hashtable of props. PRECONDITION: The property does not exist in the hashtable yet. i_idxTag is null if you want to insert the property. else you want to insert a tag, and *i_idxTag is the index of the tag Arguments: [i_pColumnMeta] - [o_ppMbp] - [i_idxTag] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pColumnMeta != NULL); DBG_ASSERT(o_ppMbp != NULL); HRESULT hr = WBEM_S_NO_ERROR; METABASE_PROPERTY* pMbp = NULL; hr = m_poolProps.GetNewElement(&pMbp); if(FAILED(hr)) { goto exit; } if(i_idxTag == NULL) { pMbp->dwMDMask = 0; hr = m_spoolStrings.GetNewString( i_pColumnMeta->ColumnMeta.pInternalName, &(pMbp->pszPropName)); if(FAILED(hr)) { goto exit; } } else { pMbp->dwMDMask = *(i_pColumnMeta->paTags[*i_idxTag]->pValue); hr = m_spoolStrings.GetNewString( i_pColumnMeta->paTags[*i_idxTag]->pInternalName, &(pMbp->pszPropName)); if(FAILED(hr)) { goto exit; } } pMbp->dwMDIdentifier = *(i_pColumnMeta->ColumnMeta.pID); pMbp->dwMDUserType = *(i_pColumnMeta->ColumnMeta.pUserType); switch(*(i_pColumnMeta->ColumnMeta.pType)) { case eCOLUMNMETA_int32: if(fCOLUMNMETA_BOOL & *(i_pColumnMeta->ColumnMeta.pMetaFlags)) { if(pMbp->dwMDMask == 0) { pMbp->dwMDMask = ALL_BITS_ON; } } pMbp->dwMDDataType = DWORD_METADATA; pMbp->pDefaultValue = NULL; #ifdef USE_DEFAULT_VALUES if(i_pColumnMeta->ColumnMeta.pDefaultValue != NULL) { pMbp->dwDefaultValue = *((DWORD*)(i_pColumnMeta->ColumnMeta.pDefaultValue)); pMbp->pDefaultValue = &pMbp->dwDefaultValue; } #endif break; case eCOLUMNMETA_String: if(fCOLUMNMETA_MULTISTRING & *(i_pColumnMeta->ColumnMeta.pMetaFlags)) { pMbp->dwMDDataType = MULTISZ_METADATA; } else if(fCOLUMNMETA_EXPANDSTRING & *(i_pColumnMeta->ColumnMeta.pMetaFlags)) { pMbp->dwMDDataType = EXPANDSZ_METADATA; } else { pMbp->dwMDDataType = STRING_METADATA; } // // Default values. // pMbp->pDefaultValue = NULL; #ifdef USE_DEFAULT_VALUES if(i_pColumnMeta->ColumnMeta.pDefaultValue != NULL) { if(pMbp->dwMDDataType != MULTISZ_METADATA) { hr = m_spoolStrings.GetNewString( (LPWSTR)i_pColumnMeta->ColumnMeta.pDefaultValue, (LPWSTR*)&pMbp->pDefaultValue); if(FAILED(hr)) { goto exit; } } else { bool bLastCharNull = false; ULONG idx = 0; ULONG iLen = 0; LPWSTR msz = (LPWSTR)i_pColumnMeta->ColumnMeta.pDefaultValue; do { bLastCharNull = msz[idx] == L'\0' ? true : false; } while( !(msz[++idx] == L'\0' && bLastCharNull) ); iLen = idx+1; hr = m_spoolStrings.GetNewArray( iLen, (LPWSTR*)&pMbp->pDefaultValue); if(FAILED(hr)) { goto exit; } memcpy( pMbp->pDefaultValue, i_pColumnMeta->ColumnMeta.pDefaultValue, sizeof(WCHAR)*iLen); } } #endif break; case eCOLUMNMETA_BYTES: pMbp->dwMDDataType = BINARY_METADATA; pMbp->pDefaultValue = NULL; #ifdef USE_DEFAULT_VALUES #ifdef USE_DEFAULT_BINARY_VALUES if( i_pColumnMeta->ColumnMeta.pDefaultValue != NULL ) { hr = m_apoolBytes.GetNewArray( i_pColumnMeta->cbDefaultValue, (BYTE**)&pMbp->pDefaultValue); if(FAILED(hr)) { goto exit; } memcpy( pMbp->pDefaultValue, i_pColumnMeta->ColumnMeta.pDefaultValue, i_pColumnMeta->cbDefaultValue); // // Use dwDefaultValue to store the length. // pMbp->dwDefaultValue = i_pColumnMeta->cbDefaultValue; } #endif #endif break; default: pMbp->dwMDDataType = -1; pMbp->pDefaultValue = NULL; break; } pMbp->dwMDAttributes = *(i_pColumnMeta->ColumnMeta.pAttributes); if(*i_pColumnMeta->ColumnMeta.pSchemaGeneratorFlags & fCOLUMNMETA_CACHE_PROPERTY_MODIFIED) { pMbp->fReadOnly = FALSE; } else { pMbp->fReadOnly = TRUE; } hr = m_hashProps.Wmi_Insert(pMbp->pszPropName, pMbp); if(FAILED(hr)) { goto exit; } exit: if(SUCCEEDED(hr)) { *o_ppMbp = pMbp; } return hr; } bool CDynSchema::IgnoreProperty( METABASE_KEYTYPE* io_pkt, DWORD i_dwPropId, DWORD io_adwIgnored[]) /*++ Synopsis: Checks to see if i_wszProp is in g_adwPropIgnoreList. If it is, sets next free element in io_adwIgnored to point to this. Arguments: [i_wszProp] - [io_adwIgnored] - Must be as big as g_adwPropIgnoreList. Allocated AND must be memset to 0 by caller. Return Value: true if property is in the ignore list. false otherwise. --*/ { DBG_ASSERT(io_pkt); if(g_adwPropIgnoreList == NULL) { return false; } if( io_pkt == &METABASE_KEYTYPE_DATA::s_IIsObject && i_dwPropId == MD_KEY_TYPE ) { return false; } for(ULONG i = 0; i < g_cElemPropIgnoreList; i++) { if(i_dwPropId == g_adwPropIgnoreList[i]) { for(ULONG j = 0; j < g_cElemPropIgnoreList; j++) { if(io_adwIgnored[j] == NULL) { io_adwIgnored[j] = g_adwPropIgnoreList[i]; break; } } return true; } } return false; } #if 0 bool CDynSchema::IgnoreProperty(LPCWSTR i_wszProp) /*++ Synopsis: Checks to see if i_wszProp is in g_adwPropIgnoreList Arguments: [i_wszProp] - Return Value: --*/ { DBG_ASSERT(i_wszProp != NULL); if(g_adwPropIgnoreList == NULL) { return false; } for(ULONG i = 0; i < g_cElemPropIgnoreList; i++) { if(_wcsicmp(i_wszProp, g_adwPropIgnoreList[i]) == 0) { return true; } } return false; } #endif HRESULT CDynSchema::RuleGenericAssociations( WMI_CLASS* i_pcElement, WMI_CLASS* i_pcSetting, WMI_ASSOCIATION_TYPE* i_pAssocType, ULONG i_iShipped) /*++ Synopsis: Create the Element/Setting association. Arguments: [i_pcElement] - [i_pcSetting] - [i_iShipped] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pcElement != NULL); DBG_ASSERT(i_pcSetting != NULL); DBG_ASSERT(i_pAssocType != NULL); HRESULT hr = WBEM_S_NO_ERROR; LPWSTR wszElement = i_pcElement->pszClassName; LPWSTR wszSetting = i_pcSetting->pszClassName; LPWSTR wszParent = NULL; LPWSTR wszAssocName; WMI_ASSOCIATION* pWmiAssoc; ULONG cchElement = wcslen(wszElement); ULONG cchSetting = wcslen(wszSetting); hr = m_spoolStrings.GetNewArray(cchElement+cchSetting+2+1, &wszAssocName); if(FAILED(hr)) { goto exit; } wcscpy(wszAssocName, wszElement); wcscat(wszAssocName, L"_"); wcscat(wszAssocName, wszSetting); hr = m_poolAssociations.GetNewElement(&pWmiAssoc); if(FAILED(hr)) { goto exit; } if(i_iShipped == USER_DEFINED_TO_REPOSITORY || i_iShipped == USER_DEFINED_NOT_TO_REPOSITORY) { wszParent = i_pAssocType->pszExtParent; } else { wszParent = i_pAssocType->pszParent; } pWmiAssoc->pszAssociationName = wszAssocName; pWmiAssoc->pcLeft = i_pcElement; pWmiAssoc->pcRight = i_pcSetting; pWmiAssoc->pType = i_pAssocType; pWmiAssoc->fFlags = 0; pWmiAssoc->pszParentClass = wszParent; pWmiAssoc->dwExtended = i_iShipped; hr = m_hashAssociations.Wmi_Insert(wszAssocName, pWmiAssoc); if(FAILED(hr)) { goto exit; } exit: return hr; } void CDynSchema::RuleWmiClassServices( WMI_CLASS* i_pElement, WMI_CLASS* i_pSetting) /*++ Synopsis: Sets the bCreateAllowed fields to false if necessary. i_pSetting must be the corresponding Setting class to i_pElement. Also sets i_pElement->pszParentClass Arguments: [i_pElement] - [i_pSetting] - Return Value: --*/ { DBG_ASSERT(i_pElement != NULL); DBG_ASSERT(i_pSetting != NULL); DBG_ASSERT(m_bInitSuccessful == true); // // Element Class Suffixes for which Create will be disallowed // static LPCWSTR const wszService = L"Service"; static const ULONG cchService = wcslen(wszService); // // We only care about shipped classes // if( i_pElement->dwExtended != SHIPPED_TO_MOF && i_pElement->dwExtended != SHIPPED_NOT_TO_MOF ) { return; } ULONG cchElement = wcslen(i_pElement->pszClassName); if( cchElement >= cchService && _wcsicmp(wszService, &i_pElement->pszClassName[cchElement-cchService]) == 0 ) { i_pElement->bCreateAllowed = false; i_pElement->pszParentClass = L"Win32_Service"; i_pSetting->bCreateAllowed = false; } } HRESULT CDynSchema::RuleWmiClassInverseCCL( const METABASE_KEYTYPE* pktGroup, METABASE_KEYTYPE* pktPart) /*++ Synopsis: Adds pktGroup to pktPart's inverse container class list Arguments: [pktGroup] - [pktPart] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(pktGroup != NULL); DBG_ASSERT(pktPart != NULL); HRESULT hr = WBEM_S_NO_ERROR; METABASE_KEYTYPE_NODE* pktnode = NULL; hr = m_poolKeyTypeNodes.GetNewElement(&pktnode); if(FAILED(hr)) { goto exit; } pktnode->m_pKt = pktGroup; pktnode->m_pKtNext = pktPart->m_pKtListInverseCCL; pktPart->m_pKtListInverseCCL = pktnode; exit: return hr; } HRESULT CDynSchema::RuleGroupPartAssociations( const CTableMeta *i_pTableMeta) /*++ Synopsis: Walks thru container class list to create Group/Part associations. Also calls RuleWmiClassInverseCCL for each contained class to create inverse container class list. Arguments: [i_pTableMeta] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pTableMeta != NULL); HRESULT hr = WBEM_S_NO_ERROR; WMI_ASSOCIATION *pWmiAssoc; WMI_CLASS *pWmiClassLeft; WMI_CLASS *pWmiClassRight; LPWSTR wszCCL = NULL; // Needs to be cleaned up LPWSTR wszGroupClass = NULL; // Ptr to catalog LPWSTR wszPartClass = NULL; // Ptr to catalog LPWSTR wszAssocName = NULL; // Ptr to pool LPWSTR wszTemp = NULL; // Needs to be cleaned up static LPCWSTR wszSeps = L", "; ULONG cchGroupClass = 0; ULONG cchPartClass = 0; ULONG cchCCL = 0; wszGroupClass = i_pTableMeta->TableMeta.pInternalName; cchGroupClass = wcslen(wszGroupClass); hr = m_hashClasses.Wmi_GetByKey(wszGroupClass, &pWmiClassLeft); if(FAILED(hr)) { goto exit; } if(i_pTableMeta->TableMeta.pContainerClassList && i_pTableMeta->TableMeta.pContainerClassList[0] != L'\0') { // // Make copy of CCL so we can wcstok // cchCCL = wcslen(i_pTableMeta->TableMeta.pContainerClassList); wszCCL = new WCHAR[cchCCL+1]; if(wszCCL == NULL) { hr = WBEM_E_OUT_OF_MEMORY; goto exit; } memcpy(wszCCL, i_pTableMeta->TableMeta.pContainerClassList, sizeof(WCHAR)*(cchCCL+1)); // // we will use wszTemp to construct assoc name (GroupClass_PartClass) // wszTemp = new WCHAR[cchGroupClass+1+cchCCL+1]; if(wszTemp == NULL) { hr = WBEM_E_OUT_OF_MEMORY; goto exit; } for(wszPartClass = wcstok(wszCCL, wszSeps); wszPartClass != NULL; wszPartClass = wcstok(NULL, wszSeps)) { hr = m_hashClasses.Wmi_GetByKey(wszPartClass, &pWmiClassRight); if(FAILED(hr)) { // // This just means there is a class in the container list that // doesn't exist. // hr = WBEM_S_NO_ERROR; continue; } // // Construct association name // cchPartClass = wcslen(wszPartClass); memcpy(wszTemp, wszGroupClass, sizeof(WCHAR)*cchGroupClass); memcpy(wszTemp+cchGroupClass, L"_", sizeof(WCHAR)); memcpy( wszTemp + cchGroupClass + 1, wszPartClass, sizeof(WCHAR)*(cchPartClass+1)); hr = m_hashAssociations.Wmi_GetByKey(wszTemp, &pWmiAssoc); if(SUCCEEDED(hr)) { if( pWmiClassLeft->dwExtended != USER_DEFINED_TO_REPOSITORY && pWmiClassLeft->dwExtended != USER_DEFINED_NOT_TO_REPOSITORY && pWmiClassRight->dwExtended != USER_DEFINED_TO_REPOSITORY && pWmiClassRight->dwExtended != USER_DEFINED_NOT_TO_REPOSITORY ) { // // This means we already put this shipped association in, but it is // not a conflict. // We need this because this method is called twice for each // group class. // continue; } } hr = WBEM_S_NO_ERROR; // // TODO: Move this outside? // hr = RuleWmiClassInverseCCL(pWmiClassLeft->pkt, pWmiClassRight->pkt); if(FAILED(hr)) { goto exit; } hr = m_spoolStrings.GetNewString( wszTemp, cchGroupClass+1+cchPartClass, // cch &wszAssocName); if(FAILED(hr)) { goto exit; } hr = m_poolAssociations.GetNewElement(&pWmiAssoc); if(FAILED(hr)) { goto exit; } pWmiAssoc->pszAssociationName = wszAssocName; pWmiAssoc->pcLeft = pWmiClassLeft; pWmiAssoc->pcRight = pWmiClassRight; pWmiAssoc->pType = &WMI_ASSOCIATION_TYPE_DATA::s_Component; pWmiAssoc->fFlags = 0; if( pWmiClassLeft->dwExtended == EXTENDED || pWmiClassLeft->dwExtended == USER_DEFINED_TO_REPOSITORY || pWmiClassRight->dwExtended == EXTENDED || pWmiClassRight->dwExtended == USER_DEFINED_TO_REPOSITORY) { pWmiAssoc->pszParentClass = g_wszExtGroupPartAssocParent; pWmiAssoc->dwExtended = USER_DEFINED_TO_REPOSITORY; } else { pWmiAssoc->pszParentClass = g_wszGroupPartAssocParent; pWmiAssoc->dwExtended = SHIPPED_TO_MOF; } hr = m_hashAssociations.Wmi_Insert(wszAssocName, pWmiAssoc); if(FAILED(hr)) { goto exit; } } } exit: delete [] wszCCL; delete [] wszTemp; return hr; } HRESULT CDynSchema::RuleSpecialAssociations( DWORD i_adwIgnoredProps[], WMI_CLASS* i_pElement) /*++ Synopsis: Creates IPSecurity and AdminACL associations Arguments: [i_adwIgnoredProps[]] - [i_pElement] - Return Value: --*/ { DBG_ASSERT(i_pElement != NULL); HRESULT hr = WBEM_S_NO_ERROR; bool bCreateIPSecAssoc = false; bool bCreateAdminACLAssoc = false; DWORD dwExtended = SHIPPED_TO_MOF; if(i_pElement->dwExtended != SHIPPED_TO_MOF && i_pElement->dwExtended != EXTENDED && i_pElement->dwExtended != USER_DEFINED_TO_REPOSITORY) { return hr; } if (USER_DEFINED_TO_REPOSITORY == i_pElement->dwExtended) { dwExtended = USER_DEFINED_TO_REPOSITORY; } for(ULONG i = 0; i < g_cElemPropIgnoreList && i_adwIgnoredProps[i] != 0; i++) { if(i_adwIgnoredProps[i] == MD_IP_SEC) { bCreateIPSecAssoc = true; } else if(i_adwIgnoredProps[i] == MD_ADMIN_ACL) { bCreateAdminACLAssoc = true; } } if(bCreateIPSecAssoc) { hr = RuleGenericAssociations( i_pElement, &WMI_CLASS_DATA::s_IPSecurity, &WMI_ASSOCIATION_TYPE_DATA::s_IPSecurity, dwExtended); if(FAILED(hr)) { return hr; } } if(bCreateAdminACLAssoc) { hr = RuleGenericAssociations( i_pElement, &WMI_CLASS_DATA::s_AdminACL, &WMI_ASSOCIATION_TYPE_DATA::s_AdminACL, dwExtended); if(FAILED(hr)) { return hr; } } return hr; } HRESULT CDynSchema::ConstructFlatInverseContainerList() /*++ Synopsis: Constructs an "inverse flat container class list". This list is stored in m_abKtContainers, an array of size iNumKeys*iNumKeys. The first iNumKeys entries are for Key #1 and then so on. Let's call this row 1. In row 1, entry i corresponds to Key #i. This entry [1,i] is set to true if Key #1 can be contained somewhere under Key #i. For instance, [IIsWebDirectory, IIsWebService] is true since an IIsWebService can contain an IIsWebServer which can contain an IIsWebDirectory. Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); ULONG iNumKeys = m_hashKeyTypes.Wmi_GetNumElements(); m_abKtContainers = new bool[iNumKeys * iNumKeys]; if(m_abKtContainers == NULL) { return WBEM_E_OUT_OF_MEMORY; } memset(m_abKtContainers, 0, iNumKeys * iNumKeys * sizeof(bool)); CHashTable::iterator iter; CHashTable::iterator iterEnd = m_hashKeyTypes.end(); for (iter = m_hashKeyTypes.begin(); iter != iterEnd; ++iter) { CHashTable::Record* pRec = iter.Record(); METABASE_KEYTYPE_NODE* pktnode = pRec->m_data->m_pKtListInverseCCL; while(pktnode != NULL) { ConstructFlatInverseContainerListHelper( pktnode->m_pKt, &m_abKtContainers[pRec->m_idx * iNumKeys]); pktnode = pktnode->m_pKtNext; } } return WBEM_S_NO_ERROR; } // // TODO: Prove this will always terminate. // void CDynSchema::ConstructFlatInverseContainerListHelper( const METABASE_KEYTYPE* i_pkt, bool* io_abList) /*++ Synopsis: This walks the inverse container class list of i_pkt. For each entry, we call ConstructFlatInverseContainerListHelper and mark all the keytypes we see on the way. We terminate when we hit a keytype we've already seen or if there are no more keytypes in the inverse container class list. Arguments: [i_pkt] - [io_abList] - --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pkt != NULL); DBG_ASSERT(io_abList != NULL); ULONG idx; METABASE_KEYTYPE* pktDummy; HRESULT hr = WBEM_S_NO_ERROR; hr = m_hashKeyTypes.Wmi_GetByKey(i_pkt->m_pszName, &pktDummy, &idx); if(FAILED(hr)) { DBG_ASSERT(false && "Keytype should be in hashtable of keytypes"); return; } if(io_abList[idx] == true) return; io_abList[idx] = true; METABASE_KEYTYPE_NODE* pktnode = i_pkt->m_pKtListInverseCCL; while(pktnode != NULL) { ConstructFlatInverseContainerListHelper(pktnode->m_pKt, io_abList); pktnode = pktnode->m_pKtNext; } } bool CDynSchema::IsContainedUnder(METABASE_KEYTYPE* i_pktParent, METABASE_KEYTYPE* i_pktChild) /*++ Synopsis: Uses m_abKtContainers described above to determine whether i_pktChild can be contained somewhere under i_pktParent. Arguments: [i_pktParent] - [i_pktChild] - Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pktParent != NULL); DBG_ASSERT(i_pktChild != NULL); HRESULT hr = WBEM_S_NO_ERROR; METABASE_KEYTYPE* pktDummy; ULONG idxParent; ULONG idxChild; hr = m_hashKeyTypes.Wmi_GetByKey(i_pktParent->m_pszName, &pktDummy, &idxParent); if(FAILED(hr)) { return false; } hr = m_hashKeyTypes.Wmi_GetByKey(i_pktChild->m_pszName, &pktDummy, &idxChild); if(FAILED(hr)) { return false; } return m_abKtContainers[idxChild * m_hashKeyTypes.Wmi_GetNumElements() + idxParent]; } void CDynSchema::ToConsole() { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); /*CHashTableElement* pElement; m_hashClasses.Enum(NULL, &pElement); while(pElement != NULL) { wprintf(L"%s\n", pElement->m_data->pszClassName); // wprintf(L"\tShipped: %d\n", pElement->m_iShipped); wprintf(L"\tKT: %s\n", pElement->m_data->pkt->m_pszName); wprintf(L"\tKN: %s\n", pElement->m_data->pszKeyName); wprintf(L"\tMK: %s\n", pElement->m_data->pszMetabaseKey); METABASE_PROPERTY** ppmbp = pElement->m_data->ppmbp; for(ULONG q = 0; ppmbp != NULL && ppmbp[q] != NULL; q++) { wprintf(L"\tProp: %s\n", ppmbp[q]->pszPropName); } pElement = pElement->m_pNext; } ULONG i; m_hashKeyTypes.ToConsole(); WMI_CLASS *pWmiClass; for(i = 0; i < m_poolClasses.GetUsed(); i++) { pWmiClass = m_poolClasses.Lookup(i); wprintf( L"%s KT: %d\n", pWmiClass->pszClassName, pWmiClass->pkt ); for(ULONG j = 0; ; j++) { if(pWmiClass->ppmbp[j] == NULL) { break; } wprintf(L"\t%s\tId: %d\tUT: %d\tDT: %d\tMSK: %d\tAttr: %d\tRO: %d\n", pWmiClass->ppmbp[j]->pszPropName, pWmiClass->ppmbp[j]->dwMDIdentifier, pWmiClass->ppmbp[j]->dwMDUserType, pWmiClass->ppmbp[j]->dwMDDataType, pWmiClass->ppmbp[j]->dwMDMask, pWmiClass->ppmbp[j]->dwMDAttributes, pWmiClass->ppmbp[j]->fReadOnly); } } WMI_ASSOCIATION *pWmiAssoc; for(i = 0; i < m_poolAssociations.GetUsed(); i++) { pWmiAssoc = m_poolAssociations.Lookup(i); wprintf(L"%s\n", pWmiAssoc->pszAssociationName); wprintf(L"\t%s\n\t%s\n", pWmiAssoc->pcLeft->pszClassName, pWmiAssoc->pcRight->pszClassName); } for(unsigned int q = 0; q < m_poolProps.GetUsed(); q++) { METABASE_PROPERTY* qt = m_poolProps.Lookup(q); wprintf(L"%s\n", qt->pszPropName); }*/ } HRESULT CDynSchema::RulePopulateFromDynamic( CSchemaExtensions* i_pCatalog, BOOL i_bUserDefined) { DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pCatalog != NULL); HRESULT hr = WBEM_S_NO_ERROR; ULONG i = 0; CTableMeta* pTableMeta = NULL; WMI_CLASS* pElementClass = NULL; WMI_CLASS* pSettingClass = NULL; DWORD adwIgnoredProps[g_cElemPropIgnoreList]; DWORD dwUserDefined = 0; while(pTableMeta = i_pCatalog->EnumTables(&i)) { dwUserDefined = (fTABLEMETA_USERDEFINED & *pTableMeta->TableMeta.pSchemaGeneratorFlags); if( (i_bUserDefined && !dwUserDefined) || (!i_bUserDefined && dwUserDefined) ) { continue; } memset(adwIgnoredProps, 0, g_cElemPropIgnoreList*sizeof(DWORD)); pElementClass = NULL; pSettingClass = NULL; hr = RuleKeyType(pTableMeta); if(FAILED(hr)) { return hr; } DBG_ASSERT(pTableMeta->TableMeta.pInternalName); hr = RuleWmiClass( pTableMeta, &pElementClass, &pSettingClass, adwIgnoredProps, i_bUserDefined); if(FAILED(hr)) { return hr; } DBG_ASSERT(pElementClass != NULL); DBG_ASSERT(pSettingClass != NULL); if ( (NULL == pElementClass) || (NULL == pSettingClass) ) { return E_FAIL; } hr = RuleGenericAssociations( pElementClass, pSettingClass, &WMI_ASSOCIATION_TYPE_DATA::s_ElementSetting, pElementClass->dwExtended); if(FAILED(hr)) { return hr; } hr = RuleSpecialAssociations( adwIgnoredProps, pElementClass); if(FAILED(hr)) { return hr; } RuleWmiClassServices(pElementClass, pSettingClass); hr = RuleWmiClassDescription(pTableMeta, pElementClass, pSettingClass); if(FAILED(hr)) { return hr; } } i = 0; while(pTableMeta = i_pCatalog->EnumTables(&i)) { dwUserDefined = (fTABLEMETA_USERDEFINED & *pTableMeta->TableMeta.pSchemaGeneratorFlags); if(!i_bUserDefined && dwUserDefined) { continue; } hr = RuleGroupPartAssociations(pTableMeta); if(FAILED(hr)) { return hr; } } return hr; } HRESULT CDynSchema::RunRules( CSchemaExtensions* i_pCatalog, bool i_bUseExtensions) /*++ Synopsis: Does all the work Arguments: [i_pCatalog] - This function calls Initialize. Don't call Init outside this function. Return Value: --*/ { DBG_ASSERT(m_bInitCalled == true); DBG_ASSERT(m_bInitSuccessful == true); DBG_ASSERT(i_pCatalog != NULL); HRESULT hr = S_OK; ULONG i = 0; // // TODO: Don't think I need this // if(m_bRulesRun) { return hr; } hr = RulePopulateFromStatic(); if(FAILED(hr)) { return hr; } hr = Rule2PopulateFromStatic(); if(FAILED(hr)) { return hr; } hr = i_pCatalog->Initialize(i_bUseExtensions); if(FAILED(hr)) { return hr; } hr = RulePopulateFromDynamic( i_pCatalog, false); // shipped schema if(FAILED(hr)) { return hr; } hr = RulePopulateFromDynamic( i_pCatalog, true); // user-defined schema if(FAILED(hr)) { return hr; } hr = ConstructFlatInverseContainerList(); if(SUCCEEDED(hr)) { m_bRulesRun = true; } return hr; }