/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: SaveSchema.cpp Abstract: Implementation of the helper functions that are used to determine if a schema compilation is needed, and if needed they invoke the appropriate classes to create a schema extensions file (MD_SCHEMA_EXTENSION_FILE_NAMEW), that contains the schema extension descriptions, and invoke a schema compile to generate the schema bin format. Author: Varsha Jayasimha (varshaj) 30-Nov-1999 Revision History: --*/ #include "precomp.hxx" #define cMaxContainedClass 75 #define cMaxProperty 250 int _cdecl MyCompareStrings(const void *a, const void *b) { return _wcsicmp(*(LPWSTR*)a, *(LPWSTR*)b); } /***************************************************************************++ Routine Description: Helper function used by qsort. Compares to strings, but compares it only up until the first comma. --***************************************************************************/ int _cdecl MyCompareCommaDelimitedStrings(const void *a, const void *b) { LPWSTR wszStringAStart = ((DELIMITEDSTRING*)a)->pwszStringStart; LPWSTR wszStringBStart = ((DELIMITEDSTRING*)b)->pwszStringStart; LPWSTR wszStringAEnd = ((DELIMITEDSTRING*)a)->pwszStringEnd; LPWSTR wszStringBEnd = ((DELIMITEDSTRING*)b)->pwszStringEnd; int iret = 0; SIZE_T cchA = wszStringAEnd - wszStringAStart; SIZE_T cchB = wszStringBEnd - wszStringBStart; // // Do not attempt to null terminate the string because you may be // hitting a read-only page. // iret = _wcsnicmp(wszStringAStart, wszStringBStart, __min(cchA, cchB)); if((0 == iret) && (cchA != cchB) ) { iret = cchA < cchB ? -1 : 1; } return iret; } /***************************************************************************++ Routine Description: Gets the global helper object that has the pointer to the meta tables. Arguments: [in] Bool that indicates whether to fail or not. Return Value: HRESULT --***************************************************************************/ HRESULT InitializeGlobalISTHelper(BOOL i_bFailIfBinFileAbsent) { return ::GetGlobalHelper(i_bFailIfBinFileAbsent, &g_pGlobalISTHelper); } void ReleaseGlobalISTHelper() { if(NULL != g_pGlobalISTHelper) { delete g_pGlobalISTHelper; g_pGlobalISTHelper = NULL; } } /***************************************************************************++ Routine Description: This function saves the schema only if something has changed in the schema since the last save. Arguments: [in] Schema file name. [in] Security attributes for the file. Return Value: HRESULT --***************************************************************************/ HRESULT SaveSchemaIfNeeded(LPCWSTR i_wszSchemaFileName, PSECURITY_ATTRIBUTES i_pSecurityAtrributes) { HRESULT hr = S_OK; if(NULL == g_pGlobalISTHelper) { // // g_pGlobalISTHelper will not be initialized if // ReadAllDataFromXML is not called. This can happen // during an upgrade scneario i.e IIS5.0/5.1 to IIS6.0 // We attempt to initialize it here. Note that we do // not fail if bin file is absent - just go with // the shipped schema. // BOOL bFailIfBinFileAbsent = FALSE; hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent); } if(g_dwSchemaChangeNumber != g_dwLastSchemaChangeNumber) { DBGINFOW((DBG_CONTEXT, L"[SaveSchemaIfNeeded] Calling SaveSchema. Last save schema change number: %d. Current schema change number: %d.\n", g_dwLastSchemaChangeNumber, g_dwSchemaChangeNumber)); hr = SaveSchema(i_wszSchemaFileName, i_pSecurityAtrributes); if(SUCCEEDED(hr)) { g_dwLastSchemaChangeNumber = g_dwSchemaChangeNumber; // // SaveSchema will reinitialize the GlobalISTHelper if the schema has changed. // DBGINFOW((DBG_CONTEXT, L"[SaveSchemaIfNeeded] Done Saving schema. Updating last save schema change number to: %d. Current schema change number: %d.\n", g_dwLastSchemaChangeNumber, g_dwSchemaChangeNumber)); } } else { DBGINFOW((DBG_CONTEXT, L"[SaveSchemaIfNeeded] No need saving schema because schema has not changed since last save. Last save schema change number: %d. Current schema change number: %d.\n", g_dwLastSchemaChangeNumber, g_dwSchemaChangeNumber)); } return hr; } /***************************************************************************++ Routine Description: This function saves the schema and compiles schema information into the bin file. Arguments: [in] Schema file name. [in] Security attributes for the file. Return Value: HRESULT --***************************************************************************/ HRESULT SaveSchema(LPCWSTR i_wszSchemaFileName, PSECURITY_ATTRIBUTES i_pSecurityAtrributes) { HRESULT hr; CMDBaseObject* pObjSchema = NULL; CMDBaseObject* pObjProperties = NULL; CWriter* pCWriter = NULL; CMBSchemaWriter* pSchemaWriter = NULL; LPWSTR wszSchema = L"Schema"; LPWSTR wszProperties = L"Properties"; ISimpleTableDispenser2* pISTDisp = NULL; IMetabaseSchemaCompiler* pCompiler = NULL; // // Get a pointer to the compiler to get the bin file name. // hr = DllGetSimpleObjectByIDEx( eSERVERWIRINGMETA_TableDispenser, IID_ISimpleTableDispenser2, (VOID**)&pISTDisp, WSZ_PRODUCT_IIS ); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] DllGetSimpleObjectByIDEx failed with hr = 0x%x.\n",hr)); goto exit; } hr = pISTDisp->QueryInterface(IID_IMetabaseSchemaCompiler, (LPVOID*)&pCompiler); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] QueryInterface on compiler failed with hr = 0x%x.\n",hr)); goto exit; } // // Get the Properties object // pObjSchema = g_pboMasterRoot->GetChildObject((LPSTR&)wszSchema, &hr, TRUE); if(FAILED(hr) || (NULL == pObjSchema)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema. GetChildObject failed with hr = 0x%x.\n",hr)); goto exit; } pObjProperties = pObjSchema->GetChildObject((LPSTR&)wszProperties, &hr, TRUE); if(FAILED(hr) || (NULL == pObjProperties)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema/Properties. GetChildObject failed with hr = 0x%x.\n",hr)); goto exit; } // // Create the writer object // DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Initializing writer with write file: %s bin file: %s.\n", g_wszSchemaExtensionFile, g_pGlobalISTHelper->m_wszBinFileForMeta)); // // Assert the g_GlobalISTHelper are valid // MD_ASSERT(g_pGlobalISTHelper != NULL); pCWriter = new CWriter(); if(NULL == pCWriter) { hr = E_OUTOFMEMORY; } else { // // Bug #512868 If someone had attrib-ed an existing extenstion file // be read-only then we could land up in a state where we will not be // able to recreate the extensions file. Hence attrib the extensions // file to be read-write if it exists. An extensions file can be // around only if a previous compile has failed. // ResetFileAttributesIfNeeded((LPTSTR)g_wszSchemaExtensionFile, TRUE); hr = pCWriter->Initialize(g_wszSchemaExtensionFile, g_pGlobalISTHelper, NULL); } if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving schema tree. Cannot initialize writer. Failed with hr = 0x%x.\n", hr)); goto exit; } // // First create the IIsConfigObject collection // hr = CreateIISConfigObjectCollection(pObjProperties, pCWriter, &pSchemaWriter); if(FAILED(hr)) { goto exit; } // // Create all other collections // hr = CreateNonIISConfigObjectCollections(pObjSchema, pCWriter, &pSchemaWriter); if(FAILED(hr)) { goto exit; } if(pSchemaWriter) { // // If pSchemaWriter has a valid Value, then some extensions were found - write it. // hr = pCWriter->BeginWrite(eWriter_Schema, i_pSecurityAtrributes); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving schema tree. CWriter::BeginWrite failed with hr = 0x%x.\n", hr)); goto exit; } hr = pSchemaWriter->WriteSchema(); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving schema tree. CMBSchemaWriter::WriteSchema. Failed with hr = 0x%x.\n", hr)); goto exit; } hr = pCWriter->EndWrite(eWriter_Schema); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving schema tree. CWriter::EndWrite Failed with hr = 0x%x.\n", hr)); goto exit; } // // Trigger compilation // // // Must close the file prior to calling compile schema, else will get a sharing violation. // delete pCWriter; pCWriter = NULL; hr = CompileIntoBin(pCompiler, g_wszSchemaExtensionFile, i_wszSchemaFileName); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] CompileSchema from %s failed with hr = 0x%x.\n", g_wszSchemaExtensionFile, hr)); } else { if(!DeleteFileW(g_wszSchemaExtensionFile)) { hr = HRESULT_FROM_WIN32(GetLastError()); DBGINFOW((DBG_CONTEXT, L"[CompileIfNeeded] Compile from schema extensions file: %s succeeded, but cannot cleanup the extensions file:%s. Delete file failed with hr = 0x%x.\n", g_wszSchemaExtensionFile, g_wszSchemaExtensionFile, hr)); hr = S_OK; } goto exit; } } else { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] No extensions found. - Either schema tree was changed, but no extensions added, or all extensions were deleted.\n")); } // // If you reach here it means that: // A. Schema changes occured to the in-memory /Schema tree, but either // there were no extensions or all extensions were deleted. // (This is inferred when pSchemaWriter in NULL) // or // B. Schema compile failed. // For A: Compile from shipped schema. // For B: Check for an existing schema file. If found make sure that the // bin file is valid. If bin file is not valid try compiling from the // schema file. If schema file is not found, compile from shipped schema. // if(pSchemaWriter) { // // This is case B. // if(-1 != GetFileAttributesW(i_wszSchemaFileName)) { if(NULL == g_pGlobalISTHelper) { BOOL bFailIfBinFileAbsent = TRUE; hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent); if(SUCCEEDED(hr)) { goto exit; } else { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to get the the bin file name. (Assuming file missing or invalid). InitializeGlobalISTHelper failed with hr = 0x%x.\n",hr)); } } else { // // Schema file present and valid - goto exit // As long as we have a valid g_pGlobalISTHelper, it holds on // to a reference to the bin file and the bin file cannot be // invalidated. // goto exit; } DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Compiling from schema file %s\n", i_wszSchemaFileName)); hr = CompileIntoBinFromSchemaFile(pCompiler, i_wszSchemaFileName); if(SUCCEEDED(hr)) { goto exit; } } } // // If you reach here it is either case A or the last part of case B. // Recreate from shipped schema // DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Schema file not found. Compiling from shipped schema\n")); hr = CompileIntoBin(pCompiler, NULL, i_wszSchemaFileName); exit: if(SUCCEEDED(hr)) { if(NULL == g_pGlobalISTHelper) { BOOL bFailIfBinFileAbsent = TRUE; hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to get the the bin file name. (Assuming file is invalid). InitializeGlobalISTHelper failed with hr = 0x%x.\n",hr)); } } if(SUCCEEDED(hr)) { hr = UpdateTimeStamp((LPWSTR)i_wszSchemaFileName, g_pGlobalISTHelper->m_wszBinFileForMeta); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[CompileIfNeeded] UpdateTimeStamp failed with hr = 0x%x.\n",hr)); } } } if(NULL != pSchemaWriter) { delete pSchemaWriter; } if(NULL != pCWriter) { delete pCWriter; } if(NULL != pCompiler) { pCompiler->Release(); } if(NULL != pISTDisp) { pISTDisp->Release(); } return hr; } /***************************************************************************++ Routine Description: This function creates the non-IIsConfigObject collection *extensions* to the schema. Arguments: [in] Object that contains the schema tree. [in] The writter object. [in,out] The schema object - this gets created if it is not already created Return Value: HRESULT --***************************************************************************/ HRESULT CreateNonIISConfigObjectCollections(CMDBaseObject* i_pObjSchema, CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter) { CMDBaseObject* pObjClasses = NULL; CMDBaseObject* pObjClass = NULL; DWORD dwEnumClassIndex = 0; static LPCWSTR wszSeparator = L","; LPWSTR wszClasses = L"Classes"; HRESULT hr = S_OK; // // Open the Classes key // pObjClasses = i_pObjSchema->GetChildObject((LPSTR&)wszClasses, &hr, TRUE); if(FAILED(hr) || (NULL == pObjClasses)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema/Classes. GetChildObject failed with hr = 0x%x.\n",hr)); return hr; } for(dwEnumClassIndex=0, pObjClass=pObjClasses->EnumChildObject(dwEnumClassIndex++); (SUCCEEDED(hr)) && (pObjClass!=NULL); pObjClass=pObjClasses->EnumChildObject(dwEnumClassIndex++)) { // // Save all the properties for this class in temp variables // LPCWSTR wszOptProp = NULL; LPCWSTR wszMandProp = NULL; LPCWSTR wszContainedClassList = NULL; BOOL bContainer = FALSE; BOOL* pbContainer = &bContainer; LPCWSTR wszClassName = (LPCWSTR)pObjClass->GetName(TRUE); CMBCollectionWriter* pCollectionWriter = NULL; CMDBaseData* pObjData = NULL; DWORD dwEnumIndex = 0; for(dwEnumIndex=0, pObjData=pObjClass->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA); (SUCCEEDED(hr)) && (pObjData!=NULL); pObjData=pObjClass->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA)) { DWORD dwID = pObjData->GetIdentifier(); if(MD_SCHEMA_CLASS_OPT_PROPERTIES == dwID) { wszOptProp = (LPCWSTR)pObjData->GetData(TRUE); } else if(MD_SCHEMA_CLASS_MAND_PROPERTIES == dwID) { wszMandProp = (LPCWSTR)pObjData->GetData(TRUE); } else if(dwID == MD_SCHEMA_CLASS_CONTAINER) { pbContainer = (BOOL*)pObjData->GetData(TRUE); } else if(dwID == MD_SCHEMA_CLASS_CONTAINMENT) { wszContainedClassList = (LPCWSTR)pObjData->GetData(TRUE); } } // // Get collection writer for IIsConfigObject // // DBGINFOW((DBG_CONTEXT, // L"[CreateNonIISConfigObjectCollections] Class %s Mand Prop:%s. Opt Prop: %s\n", // wszClassName, // wszMandProp, // wszOptProp)); // Assert that pbContainer is non-null. MD_ASSERT(pbContainer != NULL); if(ClassDiffersFromShippedSchema(wszClassName, *pbContainer, (LPWSTR)wszContainedClassList) || ClassPropertiesDifferFromShippedSchema(wszClassName, (LPWSTR)wszOptProp, (LPWSTR)wszMandProp) ) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Saving collection: %s.\n",wszClassName)); hr = GetCollectionWriter(i_pCWriter, io_pSchemaWriter, &pCollectionWriter, wszClassName, *pbContainer, wszContainedClassList); if(FAILED(hr)) { return hr; } hr = ParseAndAddPropertiesToNonIISConfigObjectCollection(wszOptProp, FALSE, pCollectionWriter); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving classes tree. Could not add optional properties %s for class %s failed with hr = 0x%x.\n",wszOptProp, wszClassName, hr)); return hr; } hr = ParseAndAddPropertiesToNonIISConfigObjectCollection(wszMandProp, TRUE, pCollectionWriter); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving classes tree. Could not add manditory properties %s for class %s failed with hr = 0x%x.\n",wszMandProp, wszClassName, hr)); return hr; } } } return hr; } /***************************************************************************++ Routine Description: It parses the list of properties and adds it to a Non_IIsConfigObject collection. Arguments: [in] List of properties [in] Bool that indicates manditory or optional. [in,out] The collection object - this gets created if it is not already created Return Value: HRESULT --***************************************************************************/ HRESULT ParseAndAddPropertiesToNonIISConfigObjectCollection(LPCWSTR i_wszProperties, BOOL i_bManditory, CMBCollectionWriter* i_pCollectionWriter) { CMBPropertyWriter *pProperty = NULL; HRESULT hr = S_OK; static WCHAR wchSeparator = L','; WCHAR* pwszStartProperty = NULL; WCHAR* pwszEndProperty = NULL; if(NULL == i_wszProperties || 0 == *i_wszProperties) { return S_OK; } pwszStartProperty = (LPWSTR)i_wszProperties; for ( ; ; ) { pwszEndProperty = wcschr(pwszStartProperty, wchSeparator); if(0 != *pwszStartProperty) { hr = i_pCollectionWriter->GetMBPropertyWriter(pwszStartProperty, i_bManditory, &pProperty); if(FAILED(hr)) { return hr; } } if(NULL != pwszEndProperty) { pwszStartProperty = ++pwszEndProperty; } else { break; } } return hr; } /***************************************************************************++ Routine Description: Looks up the schema and determines if a property is in the shipped schema or not. Arguments: [in] Writer object. [in] property id. Return Value: HRESULT --***************************************************************************/ BOOL PropertyNotInShippedSchema(CWriter* i_pCWriter, DWORD i_dwIdentifier) { HRESULT hr = S_OK; ULONG aColSearch[] = {iCOLUMNMETA_Table, iCOLUMNMETA_ID }; ULONG cColSearch = sizeof(aColSearch)/sizeof(ULONG); LPVOID apvSearch[cCOLUMNMETA_NumberOfColumns]; apvSearch[iCOLUMNMETA_Table] = (LPVOID)i_pCWriter->m_pCWriterGlobalHelper->m_wszTABLE_IIsConfigObject; apvSearch[iCOLUMNMETA_ID] = (LPVOID)&i_dwIdentifier; BOOL bPropertyNotInShippedSchema = TRUE; ULONG iRow = 0; ULONG iStartRow = 0; ULONG iCol = iCOLUMNMETA_SchemaGeneratorFlags; DWORD* pdwMetaFlagsEx = NULL; hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTColumnMetaByTableAndID->GetRowIndexBySearch(iStartRow, cColSearch, aColSearch, NULL, apvSearch, &iRow); if(SUCCEEDED(hr)) { hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTColumnMetaByTableAndID->GetColumnValues(iRow, 1, &iCol, NULL, (LPVOID*)&pdwMetaFlagsEx); if(SUCCEEDED(hr) && (((*pdwMetaFlagsEx)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0)) { bPropertyNotInShippedSchema = FALSE; } } if(E_ST_NOMOREROWS == hr) { // // See if property is a shipped tag. // bPropertyNotInShippedSchema = TagNotInShippedSchema(i_pCWriter, i_dwIdentifier); } else if(FAILED(hr)) { // // Trace a message saying internal catalog error // DBGINFOW((DBG_CONTEXT, L"[PropertyNotInShippedSchema] Internal catalog error. Could not determine if property was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr)); } return bPropertyNotInShippedSchema; } // PropertyNotInShippedSchema /***************************************************************************++ Routine Description: Looks up the schema and determines if a tag is in the shipped schema or not. Arguments: [in] Writer object. [in] property id. Return Value: HRESULT --***************************************************************************/ BOOL TagNotInShippedSchema(CWriter* i_pCWriter, DWORD i_dwIdentifier) { HRESULT hr = S_OK; ULONG aColSearch[] = {iTAGMETA_Table, iTAGMETA_ID }; ULONG cColSearch = sizeof(aColSearch)/sizeof(ULONG); LPVOID apvSearch[cCOLUMNMETA_NumberOfColumns]; BOOL bTagNotInShippedSchema = TRUE; ULONG iStartRow = 0; ULONG iColIndex = iTAGMETA_ColumnIndex; DWORD* pdwColIndex = NULL; apvSearch[iTAGMETA_Table] = (LPVOID)g_pGlobalISTHelper->m_wszTABLE_IIsConfigObject; apvSearch[iTAGMETA_ID] = (LPVOID)&i_dwIdentifier; hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTTagMetaByTableAndID->GetRowIndexBySearch(iStartRow, cColSearch, aColSearch, NULL, apvSearch, &iStartRow); if(SUCCEEDED(hr)) { hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTTagMetaByTableAndID->GetColumnValues(iStartRow, 1, &iColIndex, NULL, (LPVOID*)&pdwColIndex); if(SUCCEEDED(hr)) { // // Lookup the property to see if it is shipped. // LPVOID a_Identity[] = {(LPVOID)g_pGlobalISTHelper->m_wszTABLE_IIsConfigObject, (LPVOID)pdwColIndex }; ULONG iRow=0; hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetRowIndexByIdentity(NULL, a_Identity, &iRow); if(SUCCEEDED(hr)) { DWORD* pdwExtended = NULL; ULONG iColPropertyMetaFlagsEx = iCOLUMNMETA_SchemaGeneratorFlags; hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetColumnValues(iRow, 1, &iColPropertyMetaFlagsEx, NULL, (LPVOID*)&pdwExtended); if(SUCCEEDED(hr) && (((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0)) { // // Found at least one property that is not in the shipped schema // bTagNotInShippedSchema = FALSE; } // // Else condition means it failed or it was a shipped property // if(FAILED(hr) || ( (((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0)) // } } } // If GetRowIndexBySearch succeeds return bTagNotInShippedSchema; } // TagNotInShippedSchema /***************************************************************************++ Routine Description: Looks up the schema and determines if a class has any extensions or if it an extended (ie new) class. Arguments: [in] Class name [in] Container class or not. [in] Container class list. Return Value: HRESULT --***************************************************************************/ BOOL ClassDiffersFromShippedSchema(LPCWSTR i_wszClassName, BOOL i_bIsContainer, LPWSTR i_wszContainedClassList) { HRESULT hr = S_OK; BOOL bClassDiffersFromShippedSchema = TRUE; ULONG aiCol [] = {iTABLEMETA_SchemaGeneratorFlags, iTABLEMETA_ContainerClassList }; ULONG cCol = sizeof(aiCol)/sizeof(ULONG); LPVOID apv[cTABLEMETA_NumberOfColumns]; ULONG iRow = 0; hr = g_pGlobalISTHelper->m_pISTTableMetaForMetabaseTables->GetRowIndexByIdentity(NULL, (LPVOID*)&i_wszClassName, &iRow); if(SUCCEEDED(hr)) { hr = g_pGlobalISTHelper->m_pISTTableMetaForMetabaseTables->GetColumnValues(iRow, cCol, aiCol, NULL, apv); if(SUCCEEDED(hr)) { if(((*(DWORD*)apv[iTABLEMETA_SchemaGeneratorFlags]) & (fTABLEMETA_EXTENDED|fTABLEMETA_USERDEFINED)) == 0) { if(MatchClass(i_bIsContainer, i_wszContainedClassList, apv) ) { bClassDiffersFromShippedSchema = FALSE; } } } else { // // Trace a message saying internal catalog error // DBGINFOW((DBG_CONTEXT, L"[ClassDiffersFromShippedSchema] Internal catalog error. Could not determine if class was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr)); } } else if(E_ST_NOMOREROWS != hr) { // // Trace a message saying internal catalog error // DBGINFOW((DBG_CONTEXT, L"[ClassDiffersFromShippedSchema] Internal catalog error. Could not determine if class was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr)); } return bClassDiffersFromShippedSchema; } // ClassDiffersFromShippedSchema /***************************************************************************++ Routine Description: Looks up the schema and matches a class. Arguments: [in] Container class or not. [in] Container class list. [in] Class attributes. Return Value: HRESULT --***************************************************************************/ BOOL MatchClass(BOOL i_bIsContainer, LPWSTR i_wszContainedClassList, LPVOID* i_apv) { BOOL bMatch = TRUE; DWORD fIsContained = (*(DWORD*)i_apv[iTABLEMETA_SchemaGeneratorFlags]) & fTABLEMETA_CONTAINERCLASS; // // Compare the container property 1st and only if they equal compare the container class list // if( i_bIsContainer && (fIsContained != fTABLEMETA_CONTAINERCLASS) ) { bMatch = FALSE; } else if (!i_bIsContainer && (fIsContained == fTABLEMETA_CONTAINERCLASS) ) { bMatch = FALSE; } else { bMatch = MatchCommaDelimitedStrings(i_wszContainedClassList, (LPWSTR)i_apv[iTABLEMETA_ContainerClassList]); } return bMatch; } // MatchClass /***************************************************************************++ Routine Description: Checks to see if two comma delimited strings match. Arguments: [in] Comma delimited string. [in] Comma delimited string. Return Value: HRESULT --***************************************************************************/ BOOL MatchCommaDelimitedStrings(LPWSTR i_wszString1, LPWSTR i_wszString2) { BOOL bMatch = FALSE; DELIMITEDSTRING aStringFixed1[cMaxContainedClass]; DELIMITEDSTRING aStringFixed2[cMaxContainedClass]; DELIMITEDSTRING* aString1 = aStringFixed1; DELIMITEDSTRING* aString2 = aStringFixed2; ULONG cString1 = cMaxContainedClass; ULONG cString2 = cMaxContainedClass; ULONG iString1 = 0; ULONG iString2 = 0; BOOL bReAlloced1 = FALSE; BOOL bReAlloced2 = FALSE; HRESULT hr = S_OK; if(NULL == i_wszString1 || 0 == *i_wszString1) { if(NULL == i_wszString2 || 0 == *i_wszString2) { bMatch = TRUE; } } else if(NULL == i_wszString2 || 0 == *i_wszString2) { bMatch = FALSE; // Means i_wszString1 != NULL and i_wszString2 == NULL } else if(wcscmp(i_wszString1, i_wszString2) == 0) { bMatch = TRUE; } else { // // Construct an array of individual strings // and compare the array // hr = CommaDelimitedStringToArray(i_wszString1, &aString1, &iString1, &cString1, &bReAlloced1); if(FAILED(hr)) { goto exit; } hr = CommaDelimitedStringToArray(i_wszString2, &aString2, &iString2, &cString2, &bReAlloced2); if(FAILED(hr)) { goto exit; } bMatch = MatchDelimitedStringArray(aString1, iString1, aString2, iString2); } exit: if(aString1 != aStringFixed1) { delete [] aString1; } if(aString2 != aStringFixed2) { delete [] aString2; } return bMatch; } // MatchCommaDelimitedStrings /***************************************************************************++ Routine Description: Converts a comma delimited string to an array. Arguments: [in] Comma delimited string. [in,out] Array. [in,out] Current index in the array. [in,out] Max count of the array. (i.e. max it can hold) [in,out] Bool which indecates if the array has been realloced. Return Value: HRESULT --***************************************************************************/ HRESULT CommaDelimitedStringToArray(LPWSTR i_wszString, DELIMITEDSTRING** io_apDelimitedString, ULONG* io_piDelimitedString, ULONG* io_pcMaxDelimitedString, BOOL* io_pbReAlloced) { LPWSTR wszSubStringStart = NULL; LPWSTR wszSubStringEnd = NULL; HRESULT hr = S_OK; wszSubStringStart = i_wszString; while(NULL != wszSubStringStart) { DELIMITEDSTRING DelimitedString; wszSubStringEnd = wcschr(wszSubStringStart, L','); DelimitedString.pwszStringStart = wszSubStringStart; if(NULL != wszSubStringEnd) { DelimitedString.pwszStringEnd = wszSubStringEnd; } else { // Point to the terminating NULL. DelimitedString.pwszStringEnd = wszSubStringStart + wcslen(wszSubStringStart); } hr = AddDelimitedStringToArray(&DelimitedString, io_piDelimitedString, io_pcMaxDelimitedString, io_pbReAlloced, io_apDelimitedString); if(FAILED(hr)) { return hr; } if(wszSubStringEnd != NULL) { wszSubStringStart = ++wszSubStringEnd; } else { wszSubStringStart = wszSubStringEnd; } } return S_OK; } /***************************************************************************++ Routine Description: Adds the string to the array. Arguments: [in] String to add. [in,out] Current index in the array. [in,out] Max count of the array. (i.e. max it can hold) [in,out] Bool which indecates if the array has been realloced. [in,out] Array. Return Value: HRESULT --***************************************************************************/ HRESULT AddDelimitedStringToArray(DELIMITEDSTRING* i_pDelimitedString, ULONG* io_piDelimitedString, ULONG* io_pcMaxDelimitedString, BOOL* io_pbReAlloced, DELIMITEDSTRING** io_apDelimitedString) { HRESULT hr = S_OK; if(*io_piDelimitedString >= *io_pcMaxDelimitedString) { hr = ReAllocate(*io_piDelimitedString, *io_pbReAlloced, io_apDelimitedString, io_pcMaxDelimitedString); if(FAILED(hr)) { return hr; } *io_pbReAlloced = TRUE; } (*io_apDelimitedString)[(*io_piDelimitedString)++] = (*i_pDelimitedString); return hr; } HRESULT ReAllocate(ULONG i_iDelimitedString, BOOL i_bReAlloced, DELIMITEDSTRING** io_apDelimitedString, ULONG* io_pcDelimitedString) { DELIMITEDSTRING* pSav = NULL; pSav = new DELIMITEDSTRING[*io_pcDelimitedString + cMaxContainedClass]; if(NULL == pSav) { return E_OUTOFMEMORY; } *io_pcDelimitedString = *io_pcDelimitedString + cMaxContainedClass; memset(pSav, 0, sizeof(DELIMITEDSTRING)*(*io_pcDelimitedString)); if(NULL != *io_apDelimitedString) { memcpy(pSav, *io_apDelimitedString, sizeof(DELIMITEDSTRING)*i_iDelimitedString); if(i_bReAlloced) { delete [] *io_apDelimitedString; *io_apDelimitedString = NULL; } } *io_apDelimitedString = pSav; return S_OK; } /***************************************************************************++ Routine Description: Compares two string arrays. Arguments: Return Value: HRESULT --***************************************************************************/ BOOL MatchDelimitedStringArray(DELIMITEDSTRING* i_aString1, ULONG i_cString1, DELIMITEDSTRING* i_aString2, ULONG i_cString2) { DBG_ASSERT((i_cString1 > 0) && (i_cString2 >0)); if(i_cString1 != i_cString2) { return FALSE; } qsort((void*)i_aString1, i_cString1, sizeof(DELIMITEDSTRING), MyCompareCommaDelimitedStrings); qsort((void*)i_aString2, i_cString2, sizeof(DELIMITEDSTRING), MyCompareCommaDelimitedStrings); for(ULONG i=0; im_pISTColumnMetaByTableAndName->GetRowIndexBySearch(iStartRowProperty, cColSearchProperty, aColSearchProperty, NULL, apvSearchProperty, &iStartRowProperty); if(SUCCEEDED(hr)) { hr = g_pGlobalISTHelper->m_pISTColumnMetaByTableAndName->GetColumnValues(iStartRowProperty, 1, &iColPropertyMetaFlagsEx, NULL, (LPVOID*)&pdwExtended); if(FAILED(hr) || ( ((*pdwExtended) & (fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0)) { // // Found at least one property that is not in the shipped schema // bClassPropertiesDifferFromShippedSchema = TRUE; } // // Else condition means it succeeded and it was a shipped property // if(SUCCEEDED(hr) && ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0)) // } else { // // May be its a tag value. Check if it is in the tag meta // ULONG aColSearchTag[] = {iTAGMETA_Table, iTAGMETA_InternalName }; ULONG cColSearchTag = sizeof(aColSearchTag)/sizeof(ULONG); LPVOID apvSearchTag[cTAGMETA_NumberOfColumns]; ULONG iStartRowTag = 0; ULONG iColIndex = iTAGMETA_ColumnIndex; DWORD* pdwColumnIndex = NULL; apvSearchTag[iTAGMETA_Table] = (LPVOID)i_wszClassName; apvSearchTag[iTAGMETA_InternalName] = (LPVOID)wszPropertyName; hr = g_pGlobalISTHelper->m_pISTTagMetaByTableAndName->GetRowIndexBySearch(iStartRowTag, cColSearchTag, aColSearchTag, NULL, apvSearchTag, &iStartRowTag); if(FAILED(hr)) { bClassPropertiesDifferFromShippedSchema = TRUE; } else { // // Check if the parent property of this tag is shipped, if it is not, // then this becomes a non-shipped tag // hr = g_pGlobalISTHelper->m_pISTTagMetaByTableAndName->GetColumnValues(iStartRowTag, 1, &iColIndex, NULL, (LPVOID*)&pdwColumnIndex); if(SUCCEEDED(hr)) { LPVOID a_Identity[] = {(LPVOID)i_wszClassName, (LPVOID)pdwColumnIndex }; ULONG iRow = 0; hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetRowIndexByIdentity(NULL, a_Identity, &iRow); if(SUCCEEDED(hr)) { hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetColumnValues(iRow, 1, &iColPropertyMetaFlagsEx, NULL, (LPVOID*)&pdwExtended); if(FAILED(hr) || ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0)) { // // Found at least one property that is not in the shipped schema // bClassPropertiesDifferFromShippedSchema = TRUE; } // // Else condition means it succeeded and it was a shipped property // if(SUCCEEDED(hr) && ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0)) // } } else { bClassPropertiesDifferFromShippedSchema = TRUE; } } } // // Restore the property name // *wszEnd = wchEndSav; if(FAILED(hr) || bClassPropertiesDifferFromShippedSchema) { goto exit; } } exit: if(FAILED(hr)) { bClassPropertiesDifferFromShippedSchema = TRUE; } if(aProperty != aPropertyFixed) { delete [] aProperty; } return bClassPropertiesDifferFromShippedSchema; } // ClassPropertiesDifferFromShippedSchema /***************************************************************************++ Routine Description: Creates the IIsConfigObject collection - This collection has complete definitions of all properties. Arguments: [in] Properties object. [in,out] Writer object. [in,out] Schema Writer object. Return Value: HRESULT --***************************************************************************/ HRESULT CreateIISConfigObjectCollection(CMDBaseObject* i_pObjProperties, CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter) { HRESULT hr = S_OK; CMBCollectionWriter* pCollectionWriter = NULL; hr = SaveNames(i_pObjProperties, i_pCWriter, io_pSchemaWriter, &pCollectionWriter); if(FAILED(hr)) { return hr; } hr = SaveTypes(i_pObjProperties, i_pCWriter, io_pSchemaWriter, &pCollectionWriter); if(FAILED(hr)) { return hr; } hr = SaveDefaults(i_pObjProperties, i_pCWriter, io_pSchemaWriter, &pCollectionWriter); if(FAILED(hr)) { return hr; } return hr; } /***************************************************************************++ Routine Description: Saves the extended roperty name Arguments: [in] Metabase properties object. [in,out] Writer object. [in,out] Schema Writer object. [in,out] Collection Writer object. Return Value: HRESULT --***************************************************************************/ HRESULT SaveNames(CMDBaseObject* i_pObjProperties, CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter, CMBCollectionWriter** io_pCollectionWriter) { HRESULT hr = S_OK; CMDBaseObject* pObjNames = NULL; CMDBaseData* pObjData = NULL; DWORD dwEnumIndex = 0; LPWSTR wszNames = L"Names"; // // Get the names object // pObjNames = i_pObjProperties->GetChildObject((LPSTR&)wszNames, &hr, TRUE); if(FAILED(hr) || (NULL == pObjNames)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema/Properties/Names. GetChildObject failed with hr = 0x%x.\n",hr)); return hr; } // // Populate the Column meta array by enumerating the names key. // for(dwEnumIndex=0, pObjData=pObjNames->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA); (SUCCEEDED(hr)) && (pObjData!=NULL); pObjData=pObjNames->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA)) { CMBPropertyWriter *pProperty = NULL; if(pObjData->GetDataType() != STRING_METADATA) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Encountered non-string data in the names tree of the schema. Ignoring entry for this ID: %d \n",pObjData->GetIdentifier())); continue; } if(PropertyNotInShippedSchema(i_pCWriter, pObjData->GetIdentifier()) ) { if(NULL == *io_pCollectionWriter) { hr = GetCollectionWriter(i_pCWriter, io_pSchemaWriter, io_pCollectionWriter, wszTABLE_IIsConfigObject, FALSE, NULL); if(FAILED(hr)) { return hr; } } hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(), &pProperty); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving names tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr)); return hr; } pProperty->AddNameToProperty((LPCWSTR)(pObjData->GetData(TRUE))); } } // // Must call create index else it will keep adding duplicate property entries. // return hr; } // SaveNames /***************************************************************************++ Routine Description: Saves the extended roperty type Arguments: [in] Metabase properties object. [in,out] Writer object. [in,out] Schema Writer object. [in,out] Collection Writer object. Return Value: HRESULT --***************************************************************************/ HRESULT SaveTypes(CMDBaseObject* i_pObjProperties, CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter, CMBCollectionWriter** io_pCollectionWriter) { HRESULT hr = S_OK; CMDBaseObject* pObjTypes = NULL; CMDBaseData* pObjData = NULL; DWORD dwEnumIndex = 0; LPWSTR wszTypes = L"Types"; // // Get the Types object // pObjTypes = i_pObjProperties->GetChildObject((LPSTR&)wszTypes, &hr, TRUE); if(FAILED(hr) || (NULL == pObjTypes)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema/Properties/Types. GetChildObject failed with hr = 0x%x.\n",hr)); return hr; } for(dwEnumIndex=0, pObjData=pObjTypes->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA); (SUCCEEDED(hr)) && (pObjData!=NULL); pObjData=pObjTypes->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA)) { CMBPropertyWriter *pProperty = NULL; if(pObjData->GetDataType() != BINARY_METADATA || pObjData->GetDataLen(TRUE) != sizeof(PropValue)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Encountered non-binary data in the type tree of the schema.\nIgnoring type entry for this ID: %d.\nType: %d.(Expected %d)\nLength: %d(Expected %d).\n", pObjData->GetIdentifier(), pObjData->GetDataType(), BINARY_METADATA, pObjData->GetDataLen(TRUE), sizeof(PropValue))); if(pObjData->GetDataType() == STRING_METADATA ) { DBGINFOW((DBG_CONTEXT, L"Data: %s.\n", pObjData->GetData(TRUE) )); } continue; } if(PropertyNotInShippedSchema(i_pCWriter, pObjData->GetIdentifier()) ) { if(NULL == *io_pCollectionWriter) { hr = GetCollectionWriter(i_pCWriter, io_pSchemaWriter, io_pCollectionWriter, wszTABLE_IIsConfigObject, FALSE, NULL); if(FAILED(hr)) { return hr; } } hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(), &pProperty); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving types tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr)); return hr; } hr = pProperty->AddTypeToProperty((PropValue*)(pObjData->GetData(TRUE))); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving types tree. AddTypeToProperty for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr)); return hr; } } } return hr; } /***************************************************************************++ Routine Description: Saves the extended roperty default Arguments: [in] Metabase properties object. [in,out] Writer object. [in,out] Schema Writer object. [in,out] Collection Writer object. Return Value: HRESULT --***************************************************************************/ HRESULT SaveDefaults(CMDBaseObject* i_pObjProperties, CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter, CMBCollectionWriter** io_pCollectionWriter) { HRESULT hr = S_OK; CMDBaseObject* pObjDefaults = NULL; CMDBaseData* pObjData = NULL; DWORD dwEnumIndex = 0; LPWSTR wszDefaults = L"Defaults"; // // Get the Defaults object // pObjDefaults = i_pObjProperties->GetChildObject((LPSTR&)wszDefaults, &hr, TRUE); if(FAILED(hr) || (NULL == pObjDefaults)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Unable to open /Schema/Properties/Defaults. GetChildObject failed with hr = 0x%x.\n",hr)); return hr; } for(dwEnumIndex=0, pObjData=pObjDefaults->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA); (SUCCEEDED(hr)) && (pObjData!=NULL); pObjData=pObjDefaults->EnumDataObject(dwEnumIndex++, 0, ALL_METADATA, ALL_METADATA)) { CMBPropertyWriter *pProperty = NULL; if(PropertyNotInShippedSchema(i_pCWriter, pObjData->GetIdentifier()) ) { if(NULL == *io_pCollectionWriter) { hr = GetCollectionWriter(i_pCWriter, io_pSchemaWriter, io_pCollectionWriter, wszTABLE_IIsConfigObject, FALSE, NULL); if(FAILED(hr)) { return hr; } } hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(), &pProperty); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving defaults tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr)); return hr; } hr = pProperty->AddDefaultToProperty((BYTE*)(pObjData->GetData(TRUE)), pObjData->GetDataLen(TRUE)); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving types tree. AddDefaultToProperty for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr)); return hr; } } } return hr; } /***************************************************************************++ Routine Description: Saves the extended roperty name Arguments: [in,out] Writer object. [in,out] Schema Writer object. [in,out] Collection Writer object. [in] Collection name [in] Bool that indicates container [in] Container class list Return Value: HRESULT --***************************************************************************/ HRESULT GetCollectionWriter(CWriter* i_pCWriter, CMBSchemaWriter** io_pSchemaWriter, CMBCollectionWriter** io_pCollectionWriter, LPCWSTR i_wszCollectionName, BOOL i_bContainer, LPCWSTR i_wszContainerClassList) { HRESULT hr = S_OK; if(NULL != *io_pCollectionWriter) { return S_OK; } // // Get the schema writer if it has not been created // if(NULL == *io_pSchemaWriter) { hr = i_pCWriter->GetMetabaseSchemaWriter(io_pSchemaWriter); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] Error while saving schema tree. Unable to get schema writer failed with hr = 0x%x.\n", hr)); return hr; } } // // Get collection writer for the collection // hr = (*io_pSchemaWriter)->GetCollectionWriter(i_wszCollectionName, i_bContainer, i_wszContainerClassList, io_pCollectionWriter); if(FAILED(hr)) { DBGINFOW((DBG_CONTEXT, L"[SaveSchema] GetCollectionWriter for %s failed with hr = 0x%x.\n", i_wszCollectionName, hr)); return hr; } return S_OK; }