[SHELL32_APITEST] Follow-up to #6796 (25e2f5f)
[reactos.git] / dll / appcompat / apphelp / hsdb.c
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 }