Beispiel #1
0
HRESULT ProductSyncValues(
    __in CFGDB_STRUCT *pcdb1,
    __in CFGDB_STRUCT *pcdb2,
    __in BOOL fAllowLocalToReceiveData,
    __in STRINGDICT_HANDLE shDictValuesSeen,
    __out CONFLICT_PRODUCT **ppcpProduct
    )
{
    HRESULT hr = S_OK;
    LPWSTR sczName = NULL;
    DWORD dwInserting = 0;
    SCE_QUERY_HANDLE sqhHandle = NULL;
    SCE_QUERY_RESULTS_HANDLE sqrhResults = NULL;
    SCE_ROW_HANDLE sceRow = NULL;
    DWORD dwFoundIndex = 0;
    DWORD dwSubsumeIndex = 0;
    BOOL fSame = FALSE;
    BOOL fFirstIsLocal = (NULL == pcdb1->pcdbLocal);

    CFG_ENUMERATION * valueHistory1 = NULL;
    DWORD dwCfgCount1 = 0;
    CFG_ENUMERATION * valueHistory2 = NULL;
    DWORD dwCfgCount2 = 0;

    hr = SceBeginQuery(pcdb1->psceDb, VALUE_INDEX_TABLE, 0, &sqhHandle);
    ExitOnFailure(hr, "Failed to begin query into value table");

    hr = SceSetQueryColumnDword(sqhHandle, pcdb1->dwAppID);
    ExitOnFailure(hr, "Failed to set query column dword to: %u", pcdb1->dwAppID);

    hr = SceRunQueryRange(&sqhHandle, &sqrhResults);
    if (E_NOTFOUND == hr)
    {
        ExitFunction1(hr = S_OK);
    }
    ExitOnFailure(hr, "Failed to enumerate values for product %u", pcdb1->dwAppID);

    hr = SceGetNextResultRow(sqrhResults, &sceRow);
    while (E_NOTFOUND != hr)
    {
        ExitOnFailure(hr, "Failed to get next row from query into value table");

        CfgReleaseEnumeration(valueHistory1);
        valueHistory1 = NULL;
        CfgReleaseEnumeration(valueHistory2);
        valueHistory2 = NULL;

        hr = SceGetColumnString(sceRow, VALUE_COMMON_NAME, &sczName);
        ExitOnFailure(hr, "Failed to get value name");

        if (NULL != shDictValuesSeen)
        {
            hr = DictKeyExists(shDictValuesSeen, sczName);
            if (E_NOTFOUND == hr)
            {
                hr = DictAddKey(shDictValuesSeen, sczName);
                ExitOnFailure(hr, "Failed to add to dictionary value: %ls", sczName);
            }
            else
            {
                ExitOnFailure(hr, "Failed to check if key exists: %ls", sczName);

                // This value was already synced; skip it!
                goto Skip;
            }
        }

        // Exclude legacy detect cache values, they should never be synced off the machine
        // TODO: when we support per-machine settings, migrate this to use that feature
        if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczName, lstrlenW(wzLegacyDetectCacheValuePrefix), wzLegacyDetectCacheValuePrefix, lstrlenW(wzLegacyDetectCacheValuePrefix)))
        {
            goto Skip;
        }

        // First check if the values are identical. Even if they were set by different folks at different times,
        // same value means nothing to sync.
        hr = ValueMatch(sczName, pcdb1, pcdb2, sceRow, &fSame);
        ExitOnFailure(hr, "Failed to check if values are identical");

        if (fSame)
        {
            goto Skip;
        }

        // Get history of the value in db2
        hr = EnumPastValues(pcdb2, sczName, &valueHistory2, &dwCfgCount2);
        if (E_NOTFOUND == hr)
        {
            hr = S_OK;
        }
        ExitOnFailure(hr, "Failed to enumerate previous values in db2");

        // Get history of the value in db1
        hr = EnumPastValues(pcdb1, sczName, &valueHistory1, &dwCfgCount1);
        ExitOnFailure(hr, "Found value in db1, but failed to enumerate previous values in db1 while searching for conflicts");

        if (0 == dwCfgCount2)
        {
            if (fFirstIsLocal || fAllowLocalToReceiveData)
            {
                hr = ValueTransferFromHistory(pcdb2, valueHistory1, 0, pcdb1);
                ExitOnFailure(hr, "Failed to transfer history (due to value not present) from db 2 to db 1 for value %ls", sczName);
            }

            goto Skip;
        }

        // Don't write anything to db2 if it's local and we're told not to
        if (fFirstIsLocal || fAllowLocalToReceiveData)
        {
            // We first check the latest value. However, if the previous value is identical (same type & value, just different source), check for subsumation of that too.
            // This reduces unnecessary conflicts in rare corner case scenarios.
            dwSubsumeIndex = dwCfgCount2;
            do
            {
                --dwSubsumeIndex;

                // Check if the last history entry for database 2 exists in the database 1 - if it does, database 2's changes are subsumed
                hr = EnumFindValueInHistory(valueHistory1, dwCfgCount1, valueHistory2->valueHistory.rgcValues + dwSubsumeIndex, &dwFoundIndex);
                if (S_OK == hr)
                {
                    // Database 2 is subsumed - pipe over all the newest history entries
                    hr = ValueTransferFromHistory(pcdb2, valueHistory1, dwFoundIndex + 1, pcdb1);
                    ExitOnFailure(hr, "Failed to transfer history (due to history subsumed) from db 1 to db 2 for value %ls", sczName);

                    goto Skip;
                }
                else if (E_NOTFOUND == hr)
                {
                    hr = S_OK;
                }
                else
                {
                    ExitOnFailure(hr, "Failed to check if db2's value history is subsumed by db1's value history");
                }

                if (0 < dwSubsumeIndex)
                {
                    hr = ValueCompare(valueHistory2->valueHistory.rgcValues + dwSubsumeIndex, valueHistory2->valueHistory.rgcValues + dwSubsumeIndex - 1, FALSE, &fSame);
                    ExitOnFailure(hr, "Failed to check if value and previous value in database 2 are equivalent");
                }
            }
            while (0 < dwSubsumeIndex && fSame);
        }

        // Don't write anything to db1 if it's local and we're told not to
        if (!fFirstIsLocal || fAllowLocalToReceiveData)
        {
            // We first check the latest value. However, if the previous value is identical (same type & value, just different source), check for subsumation of that too.
            // This reduces unnecessary conflicts in rare corner case scenarios.
            dwSubsumeIndex = dwCfgCount1;
            do
            {
                --dwSubsumeIndex;

                hr = EnumFindValueInHistory(valueHistory2, dwCfgCount2, valueHistory1->valueHistory.rgcValues + dwSubsumeIndex, &dwFoundIndex);
                if (S_OK == hr)
                {
                    // Database 1 is subsumed - pipe over all the newest history entries
                    hr = ValueTransferFromHistory(pcdb1, valueHistory2, dwFoundIndex + 1, pcdb2);
                    ExitOnFailure(hr, "Failed to transfer history (due to history subsumed) from db 2 to db 1 for value %ls", sczName);

                    goto Skip;
                }
                else if (E_NOTFOUND == hr)
                {
                    hr = S_OK;
                }
                else
                {
                    ExitOnFailure(hr, "Failed to check if db1's value history is subsumed by db2's value history");
                }

                if (0 < dwSubsumeIndex)
                {
                    hr = ValueCompare(valueHistory1->valueHistory.rgcValues + dwSubsumeIndex, valueHistory1->valueHistory.rgcValues + dwSubsumeIndex - 1, FALSE, &fSame);
                    ExitOnFailure(hr, "Failed to check if value and previous value in database 1 are equivalent");
                }
            }
            while (0 < dwSubsumeIndex && fSame);
        }

        // OK, we have a conflict. Report it.
        if (NULL == *ppcpProduct)
        {
            *ppcpProduct = static_cast<CONFLICT_PRODUCT *>(MemAlloc(sizeof(CONFLICT_PRODUCT), TRUE));
            ExitOnNull(*ppcpProduct, hr, E_OUTOFMEMORY, "Failed to allocate product conflict struct");

            (*ppcpProduct)->cValues = 1;
        }
        else
        {
            ++(*ppcpProduct)->cValues;
        }

        hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgcesValueEnumLocal), (*ppcpProduct)->cValues, sizeof(CFG_ENUMERATION *), 10);
        ExitOnFailure(hr, "Failed to ensure product local value conflict array size");
        hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgdwValueCountLocal), (*ppcpProduct)->cValues, sizeof(DWORD), 10);
        ExitOnFailure(hr, "Failed to ensure product local value conflict count array size");
        hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgcesValueEnumRemote), (*ppcpProduct)->cValues, sizeof(CFG_ENUMERATION *), 10);
        ExitOnFailure(hr, "Failed to ensure product remote value conflict array size");
        hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgdwValueCountRemote), (*ppcpProduct)->cValues, sizeof(DWORD), 10);
        ExitOnFailure(hr, "Failed to ensure product remote value conflict count array size");
        hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgrcValueChoices), (*ppcpProduct)->cValues, sizeof(RESOLUTION_CHOICE), 10);
        ExitOnFailure(hr, "Failed to ensure product value resolution choice array size");

        dwInserting = (*ppcpProduct)->cValues - 1;

        // Neither is subsumed by the other, so we have conflicts - report them
        if (fFirstIsLocal)
        {
            hr = ConflictGetList(reinterpret_cast<const CFG_ENUMERATION *>(valueHistory1), dwCfgCount1,
                reinterpret_cast<const CFG_ENUMERATION *>(valueHistory2), dwCfgCount2,
                &((*ppcpProduct)->rgcesValueEnumLocal[dwInserting]), &(*ppcpProduct)->rgdwValueCountLocal[dwInserting],
                &((*ppcpProduct)->rgcesValueEnumRemote[dwInserting]), &(*ppcpProduct)->rgdwValueCountRemote[dwInserting]);
        }
        else
        {
            hr = ConflictGetList(reinterpret_cast<const CFG_ENUMERATION *>(valueHistory2), dwCfgCount2,
                reinterpret_cast<const CFG_ENUMERATION *>(valueHistory1), dwCfgCount1,
                &((*ppcpProduct)->rgcesValueEnumLocal[dwInserting]), &(*ppcpProduct)->rgdwValueCountLocal[dwInserting],
                &((*ppcpProduct)->rgcesValueEnumRemote[dwInserting]), &(*ppcpProduct)->rgdwValueCountRemote[dwInserting]);
        }
        ExitOnFailure(hr, "Failed to get conflict list");

    Skip:
        ReleaseNullSceRow(sceRow);
        hr = SceGetNextResultRow(sqrhResults, &sceRow);
    }

    hr = S_OK;

LExit:
    ReleaseSceQuery(sqhHandle);
    ReleaseSceQueryResults(sqrhResults);
    ReleaseSceRow(sceRow);
    CfgReleaseEnumeration(valueHistory1);
    CfgReleaseEnumeration(valueHistory2);
    ReleaseStr(sczName);

    return hr;
}
 ordered_adaptor_iterator(void):
     container(NULL), current_index((std::numeric_limits<size_t>::max)()),
     unvisited_nodes(compare_by_heap_value(NULL, ValueCompare()))
 {}
 ordered_adaptor_iterator(const ContainerType * container, ValueCompare const & cmp):
     container(container), current_index(container->size()),
     unvisited_nodes(compare_by_heap_value(container, ValueCompare()))
 {}
 tree_iterator(void):
     adaptor_type(0), unvisited_nodes(ValueCompare())
 {}