extern "C" HRESULT DependencyAddIgnoreDependencies( __in STRINGDICT_HANDLE sdIgnoreDependencies, __in_z LPCWSTR wzAddIgnoreDependencies ) { HRESULT hr = S_OK; LPWSTR wzContext = NULL; // Parse through the semicolon-delimited tokens and add to the array. for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) { hr = DictKeyExists(sdIgnoreDependencies, wzToken); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); } else { hr = DictAddKey(sdIgnoreDependencies, wzToken); ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); } } LExit: return hr; }
extern "C" HRESULT DependencyPlanPackageBegin( __in BOOL fPerMachine, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; STRINGDICT_HANDLE sdIgnoredDependents = NULL; DEPENDENCY* rgDependents = NULL; UINT cDependents = 0; HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; // Make sure the package defines at least one provider. if (0 == pPackage->cDependencyProviders) { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); ExitFunction1(hr = S_OK); } // Make sure the package is in the same scope as the bundle. if (fPerMachine != pPackage->fPerMachine) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); ExitFunction1(hr = S_OK); } // If we're uninstalling the package, check if any dependents are registered. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { // Build up a list of dependents to ignore, including the current bundle. hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); ExitOnFailure(hr, "Failed to build the list of ignored dependents."); // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. hr = DictKeyExists(sdIgnoredDependents, L"ALL"); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); } else { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); if (E_FILENOTFOUND != hr) { ExitOnFailure1(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); } else { hr = S_OK; } } } } // Calculate the dependency actions before the package itself is planned. CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); // If dependents were found, change the action to not uninstall the package. if (0 < cDependents) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); for (DWORD i = 0; i < cDependents; ++i) { const DEPENDENCY* pDependency = &rgDependents[i]; LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); } pPackage->fDependencyManagerWasHere = TRUE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } else // use the calculated dependency actions as the provider actions if there are any non-imported providers { // that will need to be registered. BOOL fAllImportedProviders = TRUE; // assume all providers were imported. for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; if (!pProvider->fImported) { fAllImportedProviders = FALSE; break; } } if (!fAllImportedProviders) { pPackage->providerExecute = dependencyExecuteAction; pPackage->providerRollback = dependencyRollbackAction; } } // If the package will be removed, add its providers to the growing list in the plan. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); ExitOnFailure1(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); } } pPackage->dependencyExecute = dependencyExecuteAction; pPackage->dependencyRollback = dependencyRollbackAction; LExit: ReleaseDependencyArray(rgDependents, cDependents); ReleaseDict(sdIgnoredDependents); return hr; }
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; }
static HRESULT ProductDbToMachine( __in CFGDB_STRUCT *pcdb, __in LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession ) { HRESULT hr = S_OK; SCE_QUERY_HANDLE sqhHandle = NULL; SCE_QUERY_RESULTS_HANDLE sqrhResults = NULL; SCE_ROW_HANDLE sceRow = NULL; BOOL fIgnore = FALSE; LPWSTR sczName = NULL; BOOL fHandled = FALSE; CONFIG_VALUE cvValue = { }; // First handle all special values hr = RegSpecialsProductWrite(pcdb, pSyncProductSession); ExitOnFailure(hr, "Failed to write specially handled values back to registry"); // Now handle all the regular values, minus exceptions from when we processed special values hr = SceBeginQuery(pcdb->psceDb, VALUE_INDEX_TABLE, 0, &sqhHandle); ExitOnFailure(hr, "Failed to begin query into VALUE_INDEX_TABLE table"); hr = SceSetQueryColumnDword(sqhHandle, pcdb->dwAppID); ExitOnFailure(hr, "Failed to set AppID for query"); hr = SceRunQueryRange(&sqhHandle, &sqrhResults); if (E_NOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to run query into VALUE_INDEX_TABLE table for AppID: %u", pcdb->dwAppID); hr = SceGetNextResultRow(sqrhResults, &sceRow); while (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get next results row from VALUE_INDEX_TABLE table"); hr = SceGetColumnString(sceRow, VALUE_COMMON_NAME, &sczName); ExitOnFailure(hr, "Failed to get name from row while querying VALUE_INDEX_TABLE table"); hr = FilterCheckValue(&pSyncProductSession->product, sczName, &fIgnore, NULL); ExitOnFailure(hr, "Failed to check if cfg setting should be ignored: %ls", sczName); if (!fIgnore) { fHandled = FALSE; hr = DictKeyExists(pSyncProductSession->product.shRegistrySpeciallyHandled, sczName); if (S_OK == hr) { // On a registry value exception, simply skip handling this value fHandled = TRUE; } else if (E_NOTFOUND == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to check if registry value exists in reg value exceptions dictionary"); ReleaseNullCfgValue(cvValue); hr = ValueRead(pcdb, sceRow, &cvValue); ExitOnFailure(hr, "Failed to read value %ls", sczName); if (!fHandled) { hr = RegDefaultWriteValue(&pSyncProductSession->product, sczName, &cvValue, &fHandled); ExitOnFailure(hr, "Failed to write value through registry default handler: %ls", sczName); } if (!fHandled) { hr = IniFileSetValue(pSyncProductSession, sczName, &cvValue, &fHandled); ExitOnFailure(hr, "Failed to write registry value through ini handler: %ls", sczName); } if (!fHandled) { hr = DirDefaultWriteFile(&pSyncProductSession->product, sczName, &cvValue, &fHandled); ExitOnFailure(hr, "Failed to write file through default handler: %ls", sczName); } } ReleaseNullSceRow(sceRow); hr = SceGetNextResultRow(sqrhResults, &sceRow); } hr = S_OK; ReleaseNullSceQueryResults(sqrhResults); for (DWORD i = 0; i < pSyncProductSession->cIniFiles; ++i) { hr = IniFileWrite(pSyncProductSession->rgIniFiles + i); ExitOnFailure(hr, "Failed to write INI file"); } if (!pSyncProductSession->fRegistered) { hr = DeleteEmptyRegistryKeys(pSyncProductSession); ExitOnFailure(hr, "Failed to delete empty registry keys"); hr = DeleteEmptyDirectories(pSyncProductSession); ExitOnFailure(hr, "Failed to delete empty directories"); } LExit: ReleaseStr(sczName); ReleaseSceQuery(sqhHandle); ReleaseSceQueryResults(sqrhResults); ReleaseSceRow(sceRow); ReleaseCfgValue(cvValue); return hr; }
HRESULT LegacySyncPullDeletedValues( __in CFGDB_STRUCT *pcdb, __in LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession ) { HRESULT hr = S_OK; SCE_QUERY_HANDLE sqhHandle = NULL; SCE_QUERY_RESULTS_HANDLE sqrhResults = NULL; SCE_ROW_HANDLE sceRow = NULL; CONFIG_VALUE cvExistingValue = { }; CONFIG_VALUE cvNewValue = { }; LPWSTR sczName = NULL; if (pcdb->dwAppID == pcdb->dwCfgAppID) { hr = E_INVALIDARG; ExitOnFailure(hr, "Error - tried to pull deleted values for dwCfgAppID!"); } hr = SceBeginQuery(pcdb->psceDb, VALUE_INDEX_TABLE, 0, &sqhHandle); ExitOnFailure(hr, "Failed to begin query into VALUE_INDEX_TABLE table"); hr = SceSetQueryColumnDword(sqhHandle, pcdb->dwAppID); ExitOnFailure(hr, "Failed to set AppID for query"); hr = SceRunQueryRange(&sqhHandle, &sqrhResults); if (E_NOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to run query into VALUE_INDEX_TABLE table for AppID: %u", pcdb->dwAppID); hr = SceGetNextResultRow(sqrhResults, &sceRow); while (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get next result row from VALUE_INDEX_TABLE table"); hr = SceGetColumnString(sceRow, VALUE_COMMON_NAME, &sczName); ExitOnFailure(hr, "Failed to get name from row while querying VALUE_INDEX_TABLE table"); hr = DictKeyExists(pSyncProductSession->shDictValuesSeen, sczName); if (E_NOTFOUND == hr) { hr = S_OK; ReleaseNullCfgValue(cvExistingValue); hr = ValueRead(pcdb, sceRow, &cvExistingValue); ExitOnFailure(hr, "Failed to read value into memory while querying VALUE_INDEX_TABLE table"); if (VALUE_DELETED != cvExistingValue.cvType) { hr = ValueSetDelete(NULL, pcdb->sczGuid, &cvNewValue); ExitOnFailure(hr, "Failed to set deleted value in memory"); hr = ValueWrite(pcdb, pcdb->dwAppID, sczName, &cvNewValue, TRUE, NULL); ExitOnFailure(hr, "Failed to write deleted value to db: %ls", sczName); } } ExitOnFailure(hr, "Failed to check if registry value exists in reg values seen database"); ReleaseNullSceRow(sceRow); hr = SceGetNextResultRow(sqrhResults, &sceRow); } if (E_NOTFOUND == hr) { hr = S_OK; } LExit: ReleaseSceRow(sceRow); ReleaseSceQuery(sqhHandle); ReleaseSceQueryResults(sqrhResults); ReleaseStr(sczName); ReleaseCfgValue(cvExistingValue); ReleaseCfgValue(cvNewValue); return hr; }
static HRESULT ReadFileWriteLegacyDb( __in CFGDB_STRUCT *pcdb, __in LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession, __in LEGACY_FILE *pFile, __in_z LPCWSTR wzFilePath, __in BOOL fVirtualStoreCheck ) { HRESULT hr = S_OK; LPCWSTR wzSubPath = wzFilePath; LPWSTR sczVirtualStorePath = NULL; BOOL fContinueProcessing = FALSE; BOOL fWritePermission = TRUE; if (lstrlenW(pFile->sczExpandedPath) > lstrlenW(wzFilePath)) { hr = E_INVALIDARG; ExitOnFailure(hr, "Legacy file path %ls was longer than the file path sent to ReadFileWriteLegacyDb, %ls", pFile->sczExpandedPath, wzFilePath); } // Make the key reflect only the sub-portion under the original base key path wzSubPath += lstrlenW(pFile->sczExpandedPath); while (L'\\' == wzSubPath[0]) { ++wzSubPath; } if (fVirtualStoreCheck) { hr = UtilTestWriteAccess(pcdb->hToken, wzFilePath); if (E_ACCESSDENIED == hr) { fWritePermission = FALSE; hr = S_OK; } else if (E_PATHNOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to check for write access to directory of file: %ls", wzFilePath); if (!fWritePermission) { hr = UtilConvertToVirtualStorePath(wzFilePath, &sczVirtualStorePath); ExitOnFailure(hr, "Failed to convert file path to virtualstore path: %ls", wzFilePath); // Pass the value up to check for special handling first hr = DirSpecialFileRead(pcdb, pSyncProductSession, pFile, sczVirtualStorePath, wzSubPath, &fContinueProcessing); ExitOnFailure(hr, "Failed to appropriately check for and handle special directory file under virtualstore, file path: %ls", wzFilePath); // If special handling tells us to avoid normal processing for this file, skip it if (!fContinueProcessing) { ExitFunction1(hr = S_OK); } hr = DirDefaultReadFile(pcdb, pSyncProductSession, pFile->sczName, sczVirtualStorePath, NULL); ExitOnFailure(hr, "Failed to read virtualstore file with default handler, path: %ls", sczVirtualStorePath); // Check if DirDefaultReadFile found the file or not hr = DictKeyExists(pSyncProductSession->shDictValuesSeen, pFile->sczName); if (E_NOTFOUND == hr) { hr = S_OK; } else { ExitOnFailure(hr, "Failed to check if file was seen under virtual store path: %ls", pFile->sczName); // It saw the file, so let's not proceed to check the non-virtualstore path ExitFunction1(hr = S_OK); } } } // Pass the value up to check for special handling first hr = DirSpecialFileRead(pcdb, pSyncProductSession, pFile, wzFilePath, wzSubPath, &fContinueProcessing); ExitOnFailure(hr, "Failed to appropriately check for and handle special directory file, file path: %ls", wzFilePath); // If special handling tells us to avoid normal processing for this file, skip it if (!fContinueProcessing) { ExitFunction1(hr = S_OK); } hr = DirDefaultReadFile(pcdb, pSyncProductSession, pFile->sczName, wzFilePath, wzSubPath); ExitOnFailure(hr, "Failed to read file with default handler, path: %ls", wzFilePath); LExit: ReleaseStr(sczVirtualStorePath); return hr; }