| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | |
| 6 | // function definitions |
| 7 | |
| 8 | extern "C" HRESULT ContainersParseFromXml( |
| 9 | __in BURN_CONTAINERS* pContainers, |
| 10 | __in IXMLDOMNode* pixnBundle |
| 11 | ) |
| 12 | { |
| 13 | HRESULT hr = S_OK; |
| 14 | IXMLDOMNodeList* pixnNodes = NULL; |
| 15 | IXMLDOMNode* pixnNode = NULL; |
| 16 | DWORD cNodes = 0; |
| 17 | LPWSTR scz = NULL; |
| 18 | BOOL fXmlFound = FALSE; |
| 19 | |
| 20 | // select container nodes |
| 21 | hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes); |
| 22 | ExitOnFailure(hr, "Failed to select container nodes."); |
| 23 | |
| 24 | // get container node count |
| 25 | hr = pixnNodes->get_length((long*)&cNodes); |
| 26 | ExitOnFailure(hr, "Failed to get container node count."); |
| 27 | |
| 28 | if (!cNodes) |
| 29 | { |
| 30 | ExitFunction(); |
| 31 | } |
| 32 | |
| 33 | // allocate memory for searches |
| 34 | pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE); |
| 35 | ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs."); |
| 36 | |
| 37 | pContainers->cContainers = cNodes; |
| 38 | |
| 39 | // parse container elements |
| 40 | for (DWORD i = 0; i < cNodes; ++i) |
| 41 | { |
| 42 | BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; |
| 43 | |
| 44 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); |
| 45 | ExitOnFailure(hr, "Failed to get next node."); |
| 46 | |
| 47 | // TODO: Read type from manifest. Today only CABINET is supported. |
| 48 | pContainer->type = BURN_CONTAINER_TYPE_CABINET; |
| 49 | |
| 50 | // @Id |
| 51 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); |
| 52 | ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id."); |
| 53 | |
| 54 | // @Attached |
| 55 | hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); |
| 56 | ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get @Attached."); |
| 57 | |
| 58 | // Attached containers are always found attached to the current process, so use the current proccess's |
| 59 | // name instead of what may be in the manifest. |
| 60 | if (pContainer->fAttached) |
| 61 | { |
| 62 | // @AttachedIndex |
| 63 | hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex); |
| 64 | ExitOnRequiredXmlQueryFailure(hr, "Failed to get @AttachedIndex."); |
| 65 | |
| 66 | hr = PathForCurrentProcess(&scz, NULL); |
| 67 | ExitOnFailure(hr, "Failed to get path to current process for attached container."); |
| 68 | |
| 69 | LPCWSTR wzFileName = PathFile(scz); |
| 70 | |
| 71 | hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0); |
| 72 | ExitOnFailure(hr, "Failed to set attached container file path."); |
| 73 | } |
| 74 | else |
| 75 | { |
| 76 | // @FilePath |
| 77 | hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); |
| 78 | ExitOnRequiredXmlQueryFailure(hr, "Failed to get @FilePath."); |
| 79 | } |
| 80 | |
| 81 | // The source path starts as the file path. |
| 82 | hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0); |
| 83 | ExitOnFailure(hr, "Failed to copy @FilePath"); |
| 84 | |
| 85 | // @DownloadUrl |
| 86 | hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); |
| 87 | ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get @DownloadUrl."); |
| 88 | |
| 89 | // @Hash |
| 90 | hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); |
| 91 | ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Hash."); |
| 92 | |
| 93 | hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); |
| 94 | ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); |
| 95 | |
| 96 | // @FileSize |
| 97 | hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); |
| 98 | ExitOnRequiredXmlQueryFailure(hr, "Failed to get @FileSize."); |
| 99 | |
| 100 | hr = StrStringToUInt64(scz, 0, &pContainer->qwFileSize); |
| 101 | ExitOnFailure(hr, "Failed to parse @FileSize."); |
| 102 | |
| 103 | if (!pContainer->qwFileSize) |
| 104 | { |
| 105 | ExitWithRootFailure(hr, E_INVALIDDATA, "File size is required when verifying by hash for container: %ls", pContainer->sczId); |
| 106 | } |
| 107 | |
| 108 | pContainer->verification = BURN_CONTAINER_VERIFICATION_HASH; |
| 109 | |
| 110 | // prepare next iteration |
| 111 | ReleaseNullObject(pixnNode); |
| 112 | } |
| 113 | |
| 114 | hr = S_OK; |
| 115 | |
| 116 | LExit: |
| 117 | ReleaseObject(pixnNodes); |
| 118 | ReleaseObject(pixnNode); |
| 119 | ReleaseStr(scz); |
| 120 | |
| 121 | return hr; |
| 122 | } |
| 123 | |
| 124 | extern "C" HRESULT ContainersInitialize( |
| 125 | __in BURN_CONTAINERS* pContainers, |
| 126 | __in BURN_SECTION* pSection |
| 127 | ) |
| 128 | { |
| 129 | HRESULT hr = S_OK; |
| 130 | DWORD64 qwSize = 0; |
| 131 | |
| 132 | if (pContainers->rgContainers) |
| 133 | { |
| 134 | for (DWORD i = 0; i < pContainers->cContainers; ++i) |
| 135 | { |
| 136 | BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; |
| 137 | |
| 138 | // If the container is attached, make sure the information in the section matches what the |
| 139 | // manifest contained and get the offset to the container. |
| 140 | if (pContainer->fAttached) |
| 141 | { |
| 142 | hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &qwSize, &pContainer->fActuallyAttached); |
| 143 | ExitOnFailure(hr, "Failed to get attached container information."); |
| 144 | |
| 145 | if (qwSize != pContainer->qwFileSize) |
| 146 | { |
| 147 | ExitOnFailure(hr, "Attached container '%ls' size '%llu' didn't match size from manifest: '%llu'", pContainer->sczId, qwSize, pContainer->qwFileSize); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | LExit: |
| 154 | return hr; |
| 155 | } |
| 156 | |
| 157 | extern "C" void ContainersUninitialize( |
| 158 | __in BURN_CONTAINERS* pContainers |
| 159 | ) |
| 160 | { |
| 161 | if (pContainers->rgContainers) |
| 162 | { |
| 163 | for (DWORD i = 0; i < pContainers->cContainers; ++i) |
| 164 | { |
| 165 | BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; |
| 166 | |
| 167 | ReleaseStr(pContainer->sczId); |
| 168 | ReleaseStr(pContainer->sczHash); |
| 169 | ReleaseStr(pContainer->sczSourcePath); |
| 170 | ReleaseStr(pContainer->sczFilePath); |
| 171 | ReleaseMem(pContainer->pbHash); |
| 172 | ReleaseStr(pContainer->downloadSource.sczUrl); |
| 173 | ReleaseStr(pContainer->downloadSource.sczUser); |
| 174 | ReleaseStr(pContainer->downloadSource.sczPassword); |
| 175 | ReleaseStr(pContainer->sczUnverifiedPath); |
| 176 | ReleaseStr(pContainer->sczFailedLocalAcquisitionPath); |
| 177 | ReleaseDict(pContainer->sdhPayloads); |
| 178 | } |
| 179 | MemFree(pContainers->rgContainers); |
| 180 | } |
| 181 | |
| 182 | // clear struct |
| 183 | memset(pContainers, 0, sizeof(BURN_CONTAINERS)); |
| 184 | } |
| 185 | |
| 186 | extern "C" HRESULT ContainerOpenUX( |
| 187 | __in BURN_SECTION* pSection, |
| 188 | __in BURN_CONTAINER_CONTEXT* pContext |
| 189 | ) |
| 190 | { |
| 191 | HRESULT hr = S_OK; |
| 192 | BURN_CONTAINER container = { }; |
| 193 | LPWSTR sczExecutablePath = NULL; |
| 194 | |
| 195 | // open attached container |
| 196 | container.type = BURN_CONTAINER_TYPE_CABINET; |
| 197 | container.fAttached = TRUE; |
| 198 | container.dwAttachedIndex = 0; |
| 199 | |
| 200 | hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached); |
| 201 | ExitOnFailure(hr, "Failed to get container information for UX container."); |
| 202 | |
| 203 | AssertSz(container.fActuallyAttached, "The BA container must always be found attached."); |
| 204 | |
| 205 | hr = PathForCurrentProcess(&sczExecutablePath, NULL); |
| 206 | ExitOnFailure(hr, "Failed to get path for executing module."); |
| 207 | |
| 208 | hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath); |
| 209 | ExitOnFailure(hr, "Failed to open attached container."); |
| 210 | |
| 211 | LExit: |
| 212 | ReleaseStr(sczExecutablePath); |
| 213 | |
| 214 | return hr; |
| 215 | } |
| 216 | |
| 217 | extern "C" HRESULT ContainerOpen( |
| 218 | __in BURN_CONTAINER_CONTEXT* pContext, |
| 219 | __in BURN_CONTAINER* pContainer, |
| 220 | __in HANDLE hContainerFile, |
| 221 | __in_z LPCWSTR wzFilePath |
| 222 | ) |
| 223 | { |
| 224 | HRESULT hr = S_OK; |
| 225 | LARGE_INTEGER li = { }; |
| 226 | |
| 227 | // initialize context |
| 228 | pContext->type = pContainer->type; |
| 229 | pContext->qwSize = pContainer->qwFileSize; |
| 230 | pContext->qwOffset = pContainer->qwAttachedOffset; |
| 231 | |
| 232 | // If the handle to the container is not open already, open container file |
| 233 | if (INVALID_HANDLE_VALUE == hContainerFile) |
| 234 | { |
| 235 | pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); |
| 236 | ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath); |
| 237 | } |
| 238 | else // use the container file handle. |
| 239 | { |
| 240 | if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) |
| 241 | { |
| 242 | ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // If it is a container attached to an executable, seek to the container offset. |
| 247 | if (pContainer->fAttached) |
| 248 | { |
| 249 | li.QuadPart = (LONGLONG)pContext->qwOffset; |
| 250 | } |
| 251 | |
| 252 | if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN)) |
| 253 | { |
| 254 | ExitWithLastError(hr, "Failed to move file pointer to container offset."); |
| 255 | } |
| 256 | |
| 257 | // open the archive |
| 258 | switch (pContext->type) |
| 259 | { |
| 260 | case BURN_CONTAINER_TYPE_CABINET: |
| 261 | hr = CabExtractOpen(pContext, wzFilePath); |
| 262 | break; |
| 263 | } |
| 264 | ExitOnFailure(hr, "Failed to open container."); |
| 265 | |
| 266 | LExit: |
| 267 | return hr; |
| 268 | } |
| 269 | |
| 270 | extern "C" HRESULT ContainerNextStream( |
| 271 | __in BURN_CONTAINER_CONTEXT* pContext, |
| 272 | __inout_z LPWSTR* psczStreamName |
| 273 | ) |
| 274 | { |
| 275 | HRESULT hr = S_OK; |
| 276 | |
| 277 | switch (pContext->type) |
| 278 | { |
| 279 | case BURN_CONTAINER_TYPE_CABINET: |
| 280 | hr = CabExtractNextStream(pContext, psczStreamName); |
| 281 | break; |
| 282 | } |
| 283 | |
| 284 | //LExit: |
| 285 | return hr; |
| 286 | } |
| 287 | |
| 288 | extern "C" HRESULT ContainerStreamToFile( |
| 289 | __in BURN_CONTAINER_CONTEXT* pContext, |
| 290 | __in_z LPCWSTR wzFileName |
| 291 | ) |
| 292 | { |
| 293 | HRESULT hr = S_OK; |
| 294 | |
| 295 | switch (pContext->type) |
| 296 | { |
| 297 | case BURN_CONTAINER_TYPE_CABINET: |
| 298 | hr = CabExtractStreamToFile(pContext, wzFileName); |
| 299 | break; |
| 300 | } |
| 301 | |
| 302 | //LExit: |
| 303 | return hr; |
| 304 | } |
| 305 | |
| 306 | extern "C" HRESULT ContainerStreamToBuffer( |
| 307 | __in BURN_CONTAINER_CONTEXT* pContext, |
| 308 | __out BYTE** ppbBuffer, |
| 309 | __out SIZE_T* pcbBuffer |
| 310 | ) |
| 311 | { |
| 312 | HRESULT hr = S_OK; |
| 313 | |
| 314 | switch (pContext->type) |
| 315 | { |
| 316 | case BURN_CONTAINER_TYPE_CABINET: |
| 317 | hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); |
| 318 | break; |
| 319 | |
| 320 | default: |
| 321 | *ppbBuffer = NULL; |
| 322 | *pcbBuffer = 0; |
| 323 | } |
| 324 | |
| 325 | //LExit: |
| 326 | return hr; |
| 327 | } |
| 328 | |
| 329 | extern "C" HRESULT ContainerSkipStream( |
| 330 | __in BURN_CONTAINER_CONTEXT* pContext |
| 331 | ) |
| 332 | { |
| 333 | HRESULT hr = S_OK; |
| 334 | |
| 335 | switch (pContext->type) |
| 336 | { |
| 337 | case BURN_CONTAINER_TYPE_CABINET: |
| 338 | hr = CabExtractSkipStream(pContext); |
| 339 | break; |
| 340 | } |
| 341 | |
| 342 | //LExit: |
| 343 | return hr; |
| 344 | } |
| 345 | |
| 346 | extern "C" HRESULT ContainerClose( |
| 347 | __in BURN_CONTAINER_CONTEXT* pContext |
| 348 | ) |
| 349 | { |
| 350 | HRESULT hr = S_OK; |
| 351 | |
| 352 | // close container |
| 353 | switch (pContext->type) |
| 354 | { |
| 355 | case BURN_CONTAINER_TYPE_CABINET: |
| 356 | hr = CabExtractClose(pContext); |
| 357 | ExitOnFailure(hr, "Failed to close cabinet."); |
| 358 | break; |
| 359 | } |
| 360 | |
| 361 | LExit: |
| 362 | ReleaseFile(pContext->hFile); |
| 363 | |
| 364 | if (SUCCEEDED(hr)) |
| 365 | { |
| 366 | memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT)); |
| 367 | } |
| 368 | |
| 369 | return hr; |
| 370 | } |
| 371 | |
| 372 | extern "C" HRESULT ContainerFindById( |
| 373 | __in BURN_CONTAINERS* pContainers, |
| 374 | __in_z LPCWSTR wzId, |
| 375 | __out BURN_CONTAINER** ppContainer |
| 376 | ) |
| 377 | { |
| 378 | HRESULT hr = S_OK; |
| 379 | BURN_CONTAINER* pContainer = NULL; |
| 380 | |
| 381 | for (DWORD i = 0; i < pContainers->cContainers; ++i) |
| 382 | { |
| 383 | pContainer = &pContainers->rgContainers[i]; |
| 384 | |
| 385 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1)) |
| 386 | { |
| 387 | *ppContainer = pContainer; |
| 388 | ExitFunction1(hr = S_OK); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | hr = E_NOTFOUND; |
| 393 | |
| 394 | LExit: |
| 395 | return hr; |
| 396 | } |