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