1 /* 2 * PROJECT: ReactOS Application compatibility module 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Shim matching / data (un)packing 5 * COPYRIGHT: Copyright 2011 André Hentschel 6 * Copyright 2013 Mislav Blaževic 7 * Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org) 8 */ 9 10 #define WIN32_NO_STATUS 11 #include "windows.h" 12 #include "ntndk.h" 13 #include "strsafe.h" 14 #include "apphelp.h" 15 #include "compat_undoc.h" 16 17 #define MAX_LAYER_LENGTH 256 18 #define GPLK_USER 1 19 #define GPLK_MACHINE 2 20 21 typedef struct _ShimData 22 { 23 WCHAR szModule[MAX_PATH]; 24 DWORD dwSize; 25 DWORD dwMagic; 26 SDBQUERYRESULT Query; 27 WCHAR szLayer[MAX_LAYER_LENGTH]; 28 DWORD dwRosProcessCompatVersion; // ReactOS specific 29 } ShimData; 30 31 #define SHIMDATA_MAGIC 0xAC0DEDAB 32 #define REACTOS_COMPATVERSION_IGNOREMANIFEST 0xffffffff 33 34 C_ASSERT(SHIMDATA_MAGIC == REACTOS_SHIMDATA_MAGIC); 35 C_ASSERT(sizeof(ShimData) == sizeof(ReactOS_ShimData)); 36 C_ASSERT(offsetof(ShimData, dwMagic) == offsetof(ReactOS_ShimData, dwMagic)); 37 C_ASSERT(offsetof(ShimData, dwRosProcessCompatVersion) == offsetof(ReactOS_ShimData, dwRosProcessCompatVersion)); 38 39 40 static BOOL WINAPI SdbpFileExists(LPCWSTR path) 41 { 42 DWORD attr = GetFileAttributesW(path); 43 return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); 44 } 45 46 /* Given a 'MATCHING_FILE' tag and an ATTRINFO array, 47 check all tags defined in the MATCHING_FILE against the ATTRINFO */ 48 static BOOL SdbpMatchFileAttributes(PDB pdb, TAGID matching_file, PATTRINFO attribs, DWORD attr_count) 49 { 50 TAGID child; 51 52 for (child = SdbGetFirstChild(pdb, matching_file); 53 child != TAGID_NULL; child = SdbGetNextChild(pdb, matching_file, child)) 54 { 55 TAG tag = SdbGetTagFromTagID(pdb, child); 56 DWORD n; 57 58 /* Already handled! */ 59 if (tag == TAG_NAME) 60 continue; 61 62 if (tag == TAG_UPTO_BIN_FILE_VERSION || 63 tag == TAG_UPTO_BIN_PRODUCT_VERSION || 64 tag == TAG_UPTO_LINK_DATE) 65 { 66 SHIM_WARN("Unimplemented TAG_UPTO_XXXXX\n"); 67 continue; 68 } 69 70 for (n = 0; n < attr_count; ++n) 71 { 72 PATTRINFO attr = attribs + n; 73 if (attr->flags == ATTRIBUTE_AVAILABLE && attr->type == tag) 74 { 75 DWORD dwval; 76 WCHAR* lpval; 77 QWORD qwval; 78 switch (tag & TAG_TYPE_MASK) 79 { 80 case TAG_TYPE_DWORD: 81 dwval = SdbReadDWORDTag(pdb, child, 0); 82 if (dwval != attr->dwattr) 83 return FALSE; 84 break; 85 case TAG_TYPE_STRINGREF: 86 lpval = SdbGetStringTagPtr(pdb, child); 87 if (!lpval || _wcsicmp(attr->lpattr, lpval)) 88 return FALSE; 89 break; 90 case TAG_TYPE_QWORD: 91 qwval = SdbReadQWORDTag(pdb, child, 0); 92 if (qwval != attr->qwattr) 93 return FALSE; 94 break; 95 default: 96 SHIM_WARN("Unhandled type 0x%x MATCHING_FILE\n", (tag & TAG_TYPE_MASK)); 97 return FALSE; 98 } 99 } 100 } 101 if (n == attr_count) 102 SHIM_WARN("Unhandled tag %ws in MATCHING_FILE\n", SdbTagToString(tag)); 103 } 104 return TRUE; 105 } 106 107 /* Given an 'exe' tag and an ATTRINFO array (for the main file), 108 verify that the main file and any additional files match */ 109 static BOOL WINAPI SdbpMatchExe(PDB pdb, TAGID exe, const WCHAR* dir, PATTRINFO main_attribs, DWORD main_attr_count) 110 { 111 RTL_UNICODE_STRING_BUFFER FullPathName = { { 0 } }; 112 WCHAR FullPathBuffer[MAX_PATH]; 113 UNICODE_STRING UnicodeDir; 114 TAGID matching_file; 115 PATTRINFO attribs = NULL; 116 DWORD attr_count; 117 BOOL IsMatch = FALSE; 118 119 RtlInitUnicodeString(&UnicodeDir, dir); 120 RtlInitBuffer(&FullPathName.ByteBuffer, (PUCHAR)FullPathBuffer, sizeof(FullPathBuffer)); 121 122 for (matching_file = SdbFindFirstTag(pdb, exe, TAG_MATCHING_FILE); 123 matching_file != TAGID_NULL; matching_file = SdbFindNextTag(pdb, exe, matching_file)) 124 { 125 TAGID tagName = SdbFindFirstTag(pdb, matching_file, TAG_NAME); 126 UNICODE_STRING Name; 127 USHORT Len; 128 129 RtlInitUnicodeString(&Name, SdbGetStringTagPtr(pdb, tagName)); 130 131 if (!Name.Buffer) 132 goto Cleanup; 133 134 /* An '*' here means use the main executable' */ 135 if (!wcscmp(Name.Buffer, L"*")) 136 { 137 /* We already have these attributes, so we do not need to retrieve them */ 138 if (!SdbpMatchFileAttributes(pdb, matching_file, main_attribs, main_attr_count)) 139 goto Cleanup; 140 continue; 141 } 142 143 /* Technically, one UNICODE_NULL and one path separator. */ 144 Len = UnicodeDir.Length + Name.Length + sizeof(UNICODE_NULL) + sizeof(UNICODE_NULL); 145 if (!NT_SUCCESS(RtlEnsureBufferSize(RTL_SKIP_BUFFER_COPY, &FullPathName.ByteBuffer, Len))) 146 goto Cleanup; 147 148 if (Len > FullPathName.ByteBuffer.Size) 149 goto Cleanup; 150 151 RtlInitEmptyUnicodeString(&FullPathName.String, (PWCHAR)FullPathName.ByteBuffer.Buffer, FullPathName.ByteBuffer.Size); 152 153 RtlCopyUnicodeString(&FullPathName.String, &UnicodeDir); 154 RtlAppendUnicodeToString(&FullPathName.String, L"\\"); 155 RtlAppendUnicodeStringToString(&FullPathName.String, &Name); 156 157 /* If the file does not exist, do not bother trying to read it's attributes */ 158 if (!SdbpFileExists(FullPathName.String.Buffer)) 159 goto Cleanup; 160 161 /* Do we have some attributes from the previous iteration? */ 162 if (attribs) 163 SdbFreeFileAttributes(attribs); 164 165 if (!SdbGetFileAttributes(FullPathName.String.Buffer, &attribs, &attr_count)) 166 goto Cleanup; 167 168 if (!SdbpMatchFileAttributes(pdb, matching_file, attribs, attr_count)) 169 goto Cleanup; 170 } 171 172 IsMatch = TRUE; 173 174 Cleanup: 175 RtlFreeBuffer(&FullPathName.ByteBuffer); 176 if (attribs) 177 SdbFreeFileAttributes(attribs); 178 179 return IsMatch; 180 } 181 182 /* Add a database guid to the query result */ 183 static void SdbpAddDatabaseGuid(PDB pdb, PSDBQUERYRESULT result) 184 { 185 size_t n; 186 187 for (n = 0; n < _countof(result->rgGuidDB); ++n) 188 { 189 if (!memcmp(&result->rgGuidDB[n], &pdb->database_id, sizeof(pdb->database_id))) 190 return; 191 192 if (result->dwCustomSDBMap & (1<<n)) 193 continue; 194 195 memcpy(&result->rgGuidDB[n], &pdb->database_id, sizeof(result->rgGuidDB[n])); 196 result->dwCustomSDBMap |= (1<<n); 197 return; 198 } 199 } 200 201 /* Add one layer to the query result */ 202 static BOOL SdbpAddSingleLayerMatch(TAGREF layer, PSDBQUERYRESULT result) 203 { 204 size_t n; 205 206 for (n = 0; n < result->dwLayerCount; ++n) 207 { 208 if (result->atrLayers[n] == layer) 209 return FALSE; 210 } 211 212 if (n >= _countof(result->atrLayers)) 213 return FALSE; 214 215 result->atrLayers[n] = layer; 216 result->dwLayerCount++; 217 218 return TRUE; 219 } 220 221 /* Translate a layer name to a tagref + add it to the query result */ 222 static BOOL SdbpAddNamedLayerMatch(HSDB hsdb, PCWSTR layerName, PSDBQUERYRESULT result) 223 { 224 TAGID database, layer; 225 TAGREF tr; 226 PDB pdb = hsdb->pdb; 227 228 database = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE); 229 if (database == TAGID_NULL) 230 return FALSE; 231 232 layer = SdbFindFirstNamedTag(pdb, database, TAG_LAYER, TAG_NAME, layerName); 233 if (layer == TAGID_NULL) 234 return FALSE; 235 236 if (!SdbTagIDToTagRef(hsdb, pdb, layer, &tr)) 237 return FALSE; 238 239 if (!SdbpAddSingleLayerMatch(tr, result)) 240 return FALSE; 241 242 SdbpAddDatabaseGuid(pdb, result); 243 return TRUE; 244 } 245 246 /* Add all layers for the exe tag to the query result */ 247 static void SdbpAddExeLayers(HSDB hsdb, PDB pdb, TAGID tagExe, PSDBQUERYRESULT result) 248 { 249 TAGID layer = SdbFindFirstTag(pdb, tagExe, TAG_LAYER); 250 251 while (layer != TAGID_NULL) 252 { 253 TAGREF tr; 254 TAGID layerIdTag = SdbFindFirstTag(pdb, layer, TAG_LAYER_TAGID); 255 DWORD tagId = SdbReadDWORDTag(pdb, layerIdTag, TAGID_NULL); 256 257 if (layerIdTag != TAGID_NULL && 258 tagId != TAGID_NULL && 259 SdbTagIDToTagRef(hsdb, pdb, tagId, &tr)) 260 { 261 SdbpAddSingleLayerMatch(tr, result); 262 } 263 else 264 { 265 /* Try a name lookup */ 266 TAGID layerTag = SdbFindFirstTag(pdb, layer, TAG_NAME); 267 if (layerTag != TAGID_NULL) 268 { 269 LPCWSTR layerName = SdbGetStringTagPtr(pdb, layerTag); 270 if (layerName) 271 { 272 SdbpAddNamedLayerMatch(hsdb, layerName, result); 273 } 274 } 275 } 276 277 layer = SdbFindNextTag(pdb, tagExe, layer); 278 } 279 } 280 281 /* Add an exe tag to the query result */ 282 static void SdbpAddExeMatch(HSDB hsdb, PDB pdb, TAGID tagExe, PSDBQUERYRESULT result) 283 { 284 size_t n; 285 TAGREF tr; 286 287 if (!SdbTagIDToTagRef(hsdb, pdb, tagExe, &tr)) 288 return; 289 290 for (n = 0; n < result->dwExeCount; ++n) 291 { 292 if (result->atrExes[n] == tr) 293 return; 294 } 295 296 if (n >= _countof(result->atrExes)) 297 return; 298 299 result->atrExes[n] = tr; 300 result->dwExeCount++; 301 302 SdbpAddExeLayers(hsdb, pdb, tagExe, result); 303 304 SdbpAddDatabaseGuid(pdb, result); 305 } 306 307 /* Add all named layers to the query result */ 308 static ULONG SdbpAddLayerMatches(HSDB hsdb, PWSTR pwszLayers, DWORD pdwBytes, PSDBQUERYRESULT result) 309 { 310 PWSTR start = pwszLayers, p; 311 ULONG Added = 0; 312 313 const PWSTR end = pwszLayers + (pdwBytes / sizeof(WCHAR)); 314 while (start < end && (*start == L'!' || *start == L'#' || *start == L' ' || *start == L'\t')) 315 start++; 316 317 if (start == end) 318 return 0; 319 320 do 321 { 322 while (*start == L' ' || *start == L'\t') 323 ++start; 324 325 if (*start == UNICODE_NULL) 326 break; 327 p = wcspbrk(start, L" \t"); 328 329 if (p) 330 *p = UNICODE_NULL; 331 332 if (SdbpAddNamedLayerMatch(hsdb, start, result)) 333 Added++; 334 335 start = p + 1; 336 } while (start < end && p); 337 338 return Added; 339 } 340 341 static BOOL SdbpPropagateEnvLayers(HSDB hsdb, LPWSTR Environment, PSDBQUERYRESULT Result) 342 { 343 static const UNICODE_STRING EnvKey = RTL_CONSTANT_STRING(L"__COMPAT_LAYER"); 344 UNICODE_STRING EnvValue; 345 NTSTATUS Status; 346 WCHAR Buffer[MAX_LAYER_LENGTH]; 347 348 RtlInitEmptyUnicodeString(&EnvValue, Buffer, sizeof(Buffer)); 349 350 Status = RtlQueryEnvironmentVariable_U(Environment, &EnvKey, &EnvValue); 351 352 if (!NT_SUCCESS(Status)) 353 return FALSE; 354 355 return SdbpAddLayerMatches(hsdb, Buffer, EnvValue.Length, Result) > 0; 356 } 357 358 359 360 /** 361 * Opens specified shim database file. Handle returned by this function may only be used by 362 * functions which take HSDB param thus differing it from SdbOpenDatabase. 363 * 364 * @param [in] flags Specifies type of path or predefined database. 365 * @param [in] path Path to the shim database file. 366 * 367 * @return Success: Handle to the opened shim database, NULL otherwise. 368 */ 369 HSDB WINAPI SdbInitDatabase(DWORD flags, LPCWSTR path) 370 { 371 static const WCHAR shim[] = {'\\','s','y','s','m','a','i','n','.','s','d','b',0}; 372 static const WCHAR msi[] = {'\\','m','s','i','m','a','i','n','.','s','d','b',0}; 373 static const WCHAR drivers[] = {'\\','d','r','v','m','a','i','n','.','s','d','b',0}; 374 LPCWSTR name; 375 WCHAR buffer[128]; 376 HSDB hsdb; 377 378 hsdb = SdbAlloc(sizeof(SDB)); 379 if (!hsdb) 380 return NULL; 381 hsdb->auto_loaded = 0; 382 383 /* Check for predefined databases */ 384 if ((flags & HID_DATABASE_TYPE_MASK) && path == NULL) 385 { 386 switch (flags & HID_DATABASE_TYPE_MASK) 387 { 388 case SDB_DATABASE_MAIN_SHIM: name = shim; break; 389 case SDB_DATABASE_MAIN_MSI: name = msi; break; 390 case SDB_DATABASE_MAIN_DRIVERS: name = drivers; break; 391 default: 392 SdbReleaseDatabase(hsdb); 393 return NULL; 394 } 395 SdbGetAppPatchDir(NULL, buffer, _countof(buffer)); 396 StringCchCatW(buffer, _countof(buffer), name); 397 flags = HID_DOS_PATHS; 398 } 399 400 hsdb->pdb = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1); 401 402 /* If database could not be loaded, a handle doesn't make sense either */ 403 if (!hsdb->pdb) 404 { 405 SdbReleaseDatabase(hsdb); 406 return NULL; 407 } 408 409 return hsdb; 410 } 411 412 /** 413 * Closes shim database opened by SdbInitDatabase. 414 * 415 * @param [in] hsdb Handle to the shim database. 416 */ 417 void WINAPI SdbReleaseDatabase(HSDB hsdb) 418 { 419 if (hsdb) 420 { 421 SdbCloseDatabase(hsdb->pdb); 422 SdbFree(hsdb); 423 } 424 } 425 426 /** 427 * Queries database for a specified exe If hsdb is NULL default database shall be loaded and 428 * searched. 429 * 430 * @param [in] hsdb Handle to the shim database. 431 * @param [in] path Path to executable for which we query database. 432 * @param [in] module_name Unused. 433 * @param [in] env The environment block to use 434 * @param [in] flags 0 or SDBGMEF_IGNORE_ENVIRONMENT. 435 * @param [out] result Pointer to structure in which query result shall be stored. 436 * 437 * @return TRUE if it succeeds, FALSE if it fails. 438 */ 439 BOOL WINAPI SdbGetMatchingExe(HSDB hsdb, LPCWSTR path, LPCWSTR module_name, 440 LPCWSTR env, DWORD flags, PSDBQUERYRESULT result) 441 { 442 BOOL ret = FALSE; 443 TAGID database, iter, name; 444 PATTRINFO attribs = NULL; 445 DWORD attr_count; 446 RTL_UNICODE_STRING_BUFFER DosApplicationName = { { 0 } }; 447 WCHAR DosPathBuffer[MAX_PATH]; 448 ULONG PathType = 0; 449 LPWSTR file_name; 450 WCHAR wszLayers[MAX_LAYER_LENGTH]; 451 DWORD dwSize; 452 PDB pdb; 453 454 /* Load default database if one is not specified */ 455 if (!hsdb) 456 { 457 /* To reproduce windows behaviour HID_DOS_PATHS needs 458 * to be specified when loading default database */ 459 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL); 460 if (hsdb) 461 hsdb->auto_loaded = TRUE; 462 } 463 464 ZeroMemory(result, sizeof(*result)); 465 466 /* No database could be loaded */ 467 if (!hsdb || !path) 468 return FALSE; 469 470 /* We do not support multiple db's yet! */ 471 pdb = hsdb->pdb; 472 473 RtlInitUnicodeString(&DosApplicationName.String, path); 474 RtlInitBuffer(&DosApplicationName.ByteBuffer, (PUCHAR)DosPathBuffer, sizeof(DosPathBuffer)); 475 if (!NT_SUCCESS(RtlEnsureBufferSize(RTL_SKIP_BUFFER_COPY, &DosApplicationName.ByteBuffer, DosApplicationName.String.MaximumLength))) 476 { 477 SHIM_ERR("Failed to convert allocate buffer.\n"); 478 goto Cleanup; 479 } 480 /* Update the internal buffer to contain the string */ 481 memcpy(DosApplicationName.ByteBuffer.Buffer, path, DosApplicationName.String.MaximumLength); 482 /* Make sure the string uses our internal buffer (we want to modify the buffer, 483 and RtlNtPathNameToDosPathName does not always modify the String to point to the Buffer)! */ 484 DosApplicationName.String.Buffer = (PWSTR)DosApplicationName.ByteBuffer.Buffer; 485 486 if (!NT_SUCCESS(RtlNtPathNameToDosPathName(0, &DosApplicationName, &PathType, NULL))) 487 { 488 SHIM_ERR("Failed to convert %S to DOS Path.\n", path); 489 goto Cleanup; 490 } 491 492 493 /* Extract file name */ 494 file_name = wcsrchr(DosApplicationName.String.Buffer, '\\'); 495 if (!file_name) 496 { 497 SHIM_ERR("Failed to find Exe name in %wZ.\n", &DosApplicationName.String); 498 goto Cleanup; 499 } 500 501 /* We will use the buffer for exe name and directory. */ 502 *(file_name++) = UNICODE_NULL; 503 504 /* DATABASE is list TAG which contains all executables */ 505 database = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE); 506 if (database == TAGID_NULL) 507 { 508 goto Cleanup; 509 } 510 511 /* EXE is list TAG which contains data required to match executable */ 512 iter = SdbFindFirstTag(pdb, database, TAG_EXE); 513 514 /* Search for entry in database, we should look into indexing tags! */ 515 while (iter != TAGID_NULL) 516 { 517 LPWSTR foundName; 518 /* Check if exe name matches */ 519 name = SdbFindFirstTag(pdb, iter, TAG_NAME); 520 /* If this is a malformed DB, (no TAG_NAME), we should not crash. */ 521 foundName = SdbGetStringTagPtr(pdb, name); 522 if (foundName && !_wcsicmp(foundName, file_name)) 523 { 524 /* Get information about executable required to match it with database entry */ 525 if (!attribs) 526 { 527 if (!SdbGetFileAttributes(path, &attribs, &attr_count)) 528 goto Cleanup; 529 } 530 531 532 /* We have a null terminator before the application name, so DosApplicationName only contains the path. */ 533 if (SdbpMatchExe(pdb, iter, DosApplicationName.String.Buffer, attribs, attr_count)) 534 { 535 ret = TRUE; 536 SdbpAddExeMatch(hsdb, pdb, iter, result); 537 } 538 } 539 540 /* Continue iterating */ 541 iter = SdbFindNextTag(pdb, database, iter); 542 } 543 544 /* Restore the full path. */ 545 *(--file_name) = L'\\'; 546 547 dwSize = sizeof(wszLayers); 548 if (SdbGetPermLayerKeys(DosApplicationName.String.Buffer, wszLayers, &dwSize, GPLK_MACHINE | GPLK_USER)) 549 { 550 SdbpAddLayerMatches(hsdb, wszLayers, dwSize, result); 551 ret = TRUE; 552 } 553 554 if (!(flags & SDBGMEF_IGNORE_ENVIRONMENT)) 555 { 556 if (SdbpPropagateEnvLayers(hsdb, (LPWSTR)env, result)) 557 { 558 ret = TRUE; 559 result->dwFlags |= SHIMREG_HAS_ENVIRONMENT; 560 } 561 } 562 563 Cleanup: 564 RtlFreeBuffer(&DosApplicationName.ByteBuffer); 565 if (attribs) 566 SdbFreeFileAttributes(attribs); 567 if (hsdb->auto_loaded) 568 SdbReleaseDatabase(hsdb); 569 return ret; 570 } 571 572 /** 573 * Retrieves AppPatch directory. 574 * 575 * @param [in] pdb Handle to the shim database. 576 * @param [out] path Pointer to memory in which path shall be written. 577 * @param [in] size Size of the buffer in characters. 578 */ 579 HRESULT WINAPI SdbGetAppPatchDir(HSDB hsdb, LPWSTR path, DWORD size) 580 { 581 static WCHAR* default_dir = NULL; 582 static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0}; 583 584 /* In case function fails, path holds empty string */ 585 if (size > 0) 586 *path = 0; 587 588 if (!default_dir) 589 { 590 WCHAR* tmp; 591 HRESULT hr = E_FAIL; 592 UINT len = GetSystemWindowsDirectoryW(NULL, 0) + SdbpStrlen(szAppPatch); 593 tmp = SdbAlloc((len + 1)* sizeof(WCHAR)); 594 if (tmp) 595 { 596 UINT r = GetSystemWindowsDirectoryW(tmp, len+1); 597 if (r && r < len) 598 { 599 hr = StringCchCatW(tmp, len+1, szAppPatch); 600 if (SUCCEEDED(hr)) 601 { 602 if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL) 603 tmp = NULL; 604 } 605 } 606 if (tmp) 607 SdbFree(tmp); 608 } 609 if (!default_dir) 610 { 611 SHIM_ERR("Unable to obtain default AppPatch directory (0x%x)\n", hr); 612 return hr; 613 } 614 } 615 616 if (!hsdb) 617 { 618 return StringCchCopyW(path, size, default_dir); 619 } 620 else 621 { 622 SHIM_ERR("Unimplemented for hsdb != NULL\n"); 623 return E_NOTIMPL; 624 } 625 } 626 627 628 /** 629 * Translates the given trWhich to a specific database / tagid 630 * 631 * @param [in] hsdb Handle to the database. 632 * @param [in] trWhich Tagref to find 633 * @param [out,opt] ppdb The Shim database that trWhich belongs to. 634 * @param [out,opt] ptiWhich The tagid that trWhich corresponds to. 635 * 636 * @return TRUE if it succeeds, FALSE if it fails. 637 */ 638 BOOL WINAPI SdbTagRefToTagID(HSDB hsdb, TAGREF trWhich, PDB* ppdb, TAGID* ptiWhich) 639 { 640 if (trWhich & 0xf0000000) 641 { 642 SHIM_ERR("Multiple shim databases not yet implemented!\n"); 643 if (ppdb) 644 *ppdb = NULL; 645 if (ptiWhich) 646 *ptiWhich = TAG_NULL; 647 return FALSE; 648 } 649 650 /* There seems to be no range checking on trWhich.. */ 651 if (ppdb) 652 *ppdb = hsdb->pdb; 653 if (ptiWhich) 654 *ptiWhich = trWhich & 0x0fffffff; 655 656 return TRUE; 657 } 658 659 /** 660 * Translates the given trWhich to a specific database / tagid 661 * 662 * @param [in] hsdb Handle to the database. 663 * @param [in] pdb The Shim database that tiWhich belongs to. 664 * @param [in] tiWhich Path to executable for which we query database. 665 * @param [out,opt] ptrWhich The tagid that tiWhich corresponds to. 666 * 667 * @return TRUE if it succeeds, FALSE if it fails. 668 */ 669 BOOL WINAPI SdbTagIDToTagRef(HSDB hsdb, PDB pdb, TAGID tiWhich, TAGREF* ptrWhich) 670 { 671 if (pdb != hsdb->pdb) 672 { 673 SHIM_ERR("Multiple shim databases not yet implemented!\n"); 674 if (ptrWhich) 675 *ptrWhich = TAGREF_NULL; 676 return FALSE; 677 } 678 679 if (ptrWhich) 680 *ptrWhich = tiWhich & 0x0fffffff; 681 682 return TRUE; 683 } 684 685 686 /* Convert a query result to shim data that will be loaded in the child process */ 687 BOOL WINAPI SdbPackAppCompatData(HSDB hsdb, PSDBQUERYRESULT pQueryResult, PVOID* ppData, DWORD *pdwSize) 688 { 689 ShimData* pData; 690 HRESULT hr; 691 DWORD n; 692 BOOL bCloseDatabase = FALSE; 693 694 if (!pQueryResult || !ppData || !pdwSize) 695 { 696 SHIM_WARN("Invalid params: %p, %p, %p\n", pQueryResult, ppData, pdwSize); 697 return FALSE; 698 } 699 700 pData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ShimData)); 701 if (!pData) 702 { 703 SHIM_WARN("Unable to allocate %d bytes\n", sizeof(ShimData)); 704 return FALSE; 705 } 706 707 GetSystemWindowsDirectoryW(pData->szModule, _countof(pData->szModule)); 708 hr = StringCchCatW(pData->szModule, _countof(pData->szModule), L"\\system32\\apphelp.dll"); 709 if (!SUCCEEDED(hr)) 710 { 711 SHIM_ERR("Unable to append module name (0x%x)\n", hr); 712 RtlFreeHeap(RtlGetProcessHeap(), 0, pData); 713 return FALSE; 714 } 715 716 pData->dwSize = sizeof(*pData); 717 pData->dwMagic = SHIMDATA_MAGIC; 718 pData->Query = *pQueryResult; 719 pData->dwRosProcessCompatVersion = 0; 720 pData->szLayer[0] = UNICODE_NULL; /* TODO */ 721 722 SHIM_INFO("\ndwFlags 0x%x\ndwMagic 0x%x\ntrExe 0x%x\ntrLayer 0x%x\n", 723 pData->Query.dwFlags, pData->dwMagic, pData->Query.atrExes[0], pData->Query.atrLayers[0]); 724 725 /* Database List */ 726 /* 0x0 {GUID} NAME: Use to open HSDB */ 727 if (hsdb == NULL) 728 { 729 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL); 730 bCloseDatabase = TRUE; 731 } 732 733 for (n = 0; n < pQueryResult->dwLayerCount; ++n) 734 { 735 DWORD dwValue = 0, dwType; 736 DWORD dwValueSize = sizeof(dwValue); 737 SHIM_INFO("Layer 0x%x\n", pQueryResult->atrLayers[n]); 738 739 if (SdbQueryData(hsdb, pQueryResult->atrLayers[n], L"SHIMVERSIONNT", &dwType, &dwValue, &dwValueSize) == ERROR_SUCCESS && 740 dwType == REG_DWORD && dwValueSize == sizeof(dwValue)) 741 { 742 if (dwValue != REACTOS_COMPATVERSION_IGNOREMANIFEST) 743 dwValue = (dwValue % 100) | ((dwValue / 100) << 8); 744 if (dwValue > pData->dwRosProcessCompatVersion) 745 pData->dwRosProcessCompatVersion = dwValue; 746 } 747 } 748 749 if (pData->dwRosProcessCompatVersion) 750 SHIM_INFO("Setting ProcessCompatVersion 0x%x\n", pData->dwRosProcessCompatVersion); 751 752 if (bCloseDatabase) 753 SdbReleaseDatabase(hsdb); 754 755 *ppData = pData; 756 *pdwSize = pData->dwSize; 757 758 return TRUE; 759 } 760 761 BOOL WINAPI SdbUnpackAppCompatData(HSDB hsdb, LPCWSTR pszImageName, PVOID pData, PSDBQUERYRESULT pQueryResult) 762 { 763 ShimData* pShimData = pData; 764 765 if (!pShimData || pShimData->dwMagic != SHIMDATA_MAGIC || pShimData->dwSize < sizeof(ShimData)) 766 return FALSE; 767 768 if (!pQueryResult) 769 return FALSE; 770 771 /* szLayer? */ 772 773 *pQueryResult = pShimData->Query; 774 return TRUE; 775 } 776 777 DWORD WINAPI SdbGetAppCompatDataSize(ShimData* pData) 778 { 779 if (!pData || pData->dwMagic != SHIMDATA_MAGIC) 780 return 0; 781 782 return pData->dwSize; 783 } 784 785 786 /** 787 * Retrieve a Data entry 788 * 789 * @param [in] hsdb The multi-database. 790 * @param [in] trExe The tagRef to start at 791 * @param [in,opt] lpszDataName The name of the Data entry to find, or NULL to return all. 792 * @param [out,opt] lpdwDataType Any of REG_SZ, REG_QWORD, REG_DWORD, ... 793 * @param [out] lpBuffer The output buffer 794 * @param [in,out,opt] lpcbBufferSize The size of lpBuffer in bytes 795 * @param [out,opt] ptrData The tagRef of the data 796 * 797 * @return ERROR_SUCCESS 798 */ 799 DWORD WINAPI SdbQueryDataEx(HSDB hsdb, TAGREF trWhich, LPCWSTR lpszDataName, LPDWORD lpdwDataType, LPVOID lpBuffer, LPDWORD lpcbBufferSize, TAGREF *ptrData) 800 { 801 PDB pdb; 802 TAGID tiWhich, tiData; 803 DWORD dwResult; 804 805 if (!SdbTagRefToTagID(hsdb, trWhich, &pdb, &tiWhich)) 806 { 807 SHIM_WARN("Unable to translate trWhich=0x%x\n", trWhich); 808 return ERROR_NOT_FOUND; 809 } 810 811 dwResult = SdbQueryDataExTagID(pdb, tiWhich, lpszDataName, lpdwDataType, lpBuffer, lpcbBufferSize, &tiData); 812 813 if (dwResult == ERROR_SUCCESS && ptrData) 814 SdbTagIDToTagRef(hsdb, pdb, tiData, ptrData); 815 816 return dwResult; 817 } 818 819 820 /** 821 * Retrieve a Data entry 822 * 823 * @param [in] hsdb The multi-database. 824 * @param [in] trExe The tagRef to start at 825 * @param [in,opt] lpszDataName The name of the Data entry to find, or NULL to return all. 826 * @param [out,opt] lpdwDataType Any of REG_SZ, REG_QWORD, REG_DWORD, ... 827 * @param [out] lpBuffer The output buffer 828 * @param [in,out,opt] lpcbBufferSize The size of lpBuffer in bytes 829 * 830 * @return ERROR_SUCCESS 831 */ 832 DWORD WINAPI SdbQueryData(HSDB hsdb, TAGREF trWhich, LPCWSTR lpszDataName, LPDWORD lpdwDataType, LPVOID lpBuffer, LPDWORD lpcbBufferSize) 833 { 834 return SdbQueryDataEx(hsdb, trWhich, lpszDataName, lpdwDataType, lpBuffer, lpcbBufferSize, NULL); 835 } 836