in src/extensions/extension_manager/src/extension_manager.cpp [832:987]
ADUC_Result ExtensionManager::Download(
const ADUC_FileEntity* entity,
WorkflowHandle workflowHandle,
ExtensionManager_Download_Options* options,
ADUC_DownloadProgressCallback downloadProgressCallback,
ADUC_DownloadProcResolver downloadProcResolver)
{
void* lib = nullptr;
DownloadProc downloadProc = nullptr;
SHAversion algVersion;
ADUC_Result result = { /* .ResultCode = */ ADUC_Result_Failure, /* .ExtendedResultCode = */ 0 };
ADUC::StringUtils::STRING_HANDLE_wrapper targetUpdateFilePath{ nullptr };
if (!workflow_get_entity_workfolder_filepath(workflowHandle, entity, targetUpdateFilePath.address_of()))
{
Log_Error("Cannot construct child manifest file path.");
result = { /* .ResultCode = */ ADUC_Result_Failure,
/* .ExtendedResultCode = */ ADUC_ERC_CONTENT_DOWNLOADER_BAD_CHILD_MANIFEST_FILE_PATH };
goto done;
}
result = ExtensionManager::LoadContentDownloaderLibrary(&lib);
if (IsAducResultCodeFailure(result.ResultCode))
{
goto done;
}
if (!ADUC_ContractUtils_IsV1Contract(&ExtensionManager::_contentDownloaderContractVersion))
{
Log_Error(
"Unsupported contract version %d.%d",
ExtensionManager::_contentDownloaderContractVersion.majorVer,
ExtensionManager::_contentDownloaderContractVersion.minorVer);
result.ResultCode = ADUC_GeneralResult_Failure;
result.ExtendedResultCode = ADUC_ERC_CONTENT_DOWNLOADER_UNSUPPORTED_CONTRACT_VERSION;
goto done;
}
downloadProc = downloadProcResolver(lib);
if (downloadProc == nullptr)
{
result = { /* .ResultCode = */ ADUC_Result_Failure,
/* .ExtendedResultCode = */ ADUC_ERC_CONTENT_DOWNLOADER_INITIALIZEPROC_NOTIMP };
goto done;
}
if (!ADUC_HashUtils_GetShaVersionForTypeString(
ADUC_HashUtils_GetHashType(entity->Hash, entity->HashCount, 0), &algVersion))
{
Log_Error(
"FileEntity for %s has unsupported hash type %s",
targetUpdateFilePath.c_str(),
ADUC_HashUtils_GetHashType(entity->Hash, entity->HashCount, 0));
result.ExtendedResultCode = ADUC_ERC_CONTENT_DOWNLOADER_FILE_HASH_TYPE_NOT_SUPPORTED;
goto done;
}
// If file exists and has a valid hash, then skip download.
// Otherwise, delete an existing file, then download.
Log_Debug("Check whether '%s' has already been download into the work folder.", targetUpdateFilePath.c_str());
if (ADUCPAL_access(targetUpdateFilePath.c_str(), F_OK) == 0)
{
char* hashValue = ADUC_HashUtils_GetHashValue(entity->Hash, entity->HashCount, 0 /* index */);
if (hashValue == nullptr)
{
result = { /* .ResultCode = */ ADUC_Result_Failure,
/* .ExtendedResultCode = */ ADUC_ERC_CONTENT_DOWNLOADER_INVALID_FILE_ENTITY_NO_HASHES };
goto done;
}
// If target file exists, validate file hash.
// If file is valid, then skip the download.
bool validHash = ADUC_HashUtils_IsValidFileHash(
targetUpdateFilePath.c_str(), hashValue, algVersion, false /* suppressErrorLog */);
if (!validHash)
{
// Delete existing file.
if (remove(targetUpdateFilePath.c_str()) != 0)
{
Log_Error("Cannot delete existing file that has invalid hash.");
result.ExtendedResultCode = ADUC_ERC_CONTENT_DOWNLOADER_CANNOT_DELETE_EXISTING_FILE;
goto done;
}
}
result = { /* .ResultCode = */ ADUC_Result_Success, /* .ExtendedResultCode = */ 0 };
goto done;
}
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = 0;
// First, attempt to produce the update using download handler if
// download handler exists in the entity (metadata).
if (!IsNullOrEmpty(entity->DownloadHandlerId))
{
result = ProcessDownloadHandlerExtensibility(workflowHandle, entity, targetUpdateFilePath.c_str());
// continue on to fallback to full content download if necessary
}
// If no download handlers specified, or download handler failed to produce the target file.
if (IsAducResultCodeFailure(result.ResultCode)
|| result.ResultCode == ADUC_Result_Download_Handler_RequiredFullDownload)
{
// Either download handler id did not exist, or download handler failed and doing fallback here.
const char* workflowId = workflow_peek_id(workflowHandle);
cstr_wrapper workFolder{ workflow_get_workfolder(workflowHandle) };
Log_Info("Downloading full target update payload to '%s'", targetUpdateFilePath.c_str());
unsigned int timeoutInMinutes = GetDownloadTimeoutInMinutes(options);
// Note: extension manager download options max timeout is in minutes,
// but the content downloader contract version is in terms of seconds.
unsigned int timeoutInSeconds = 60 * timeoutInMinutes;
result = downloadProc(entity, workflowId, workFolder.get(), timeoutInSeconds, downloadProgressCallback);
if (IsAducResultCodeFailure(result.ResultCode))
{
goto done;
}
}
if (IsAducResultCodeSuccess(result.ResultCode))
{
if (!ADUC_HashUtils_IsValidFileHash(
targetUpdateFilePath.c_str(),
ADUC_HashUtils_GetHashValue(entity->Hash, entity->HashCount, 0),
algVersion,
false))
{
result.ResultCode = ADUC_Result_Failure;
result.ExtendedResultCode = ADUC_ERC_CONTENT_DOWNLOADER_INVALID_FILE_HASH;
Log_Error("Successful download of '%s' failed hash check.", targetUpdateFilePath.c_str());
workflow_add_erc(workflowHandle, result.ExtendedResultCode);
goto done;
}
}
else
{
// this is defensive in case a goto done was missed above.
goto done;
}
result.ResultCode = ADUC_GeneralResult_Success;
result.ExtendedResultCode = 0;
done:
return result;
}