xref: /reactos/dll/appcompat/apphelp/hsdb.c (revision e4930be4)
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 
SdbpFileExists(LPCWSTR path)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 */
SdbpMatchFileAttributes(PDB pdb,TAGID matching_file,PATTRINFO attribs,DWORD attr_count)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 */
SdbpMatchExe(PDB pdb,TAGID exe,const WCHAR * dir,PATTRINFO main_attribs,DWORD main_attr_count)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 */
SdbpAddDatabaseGuid(PDB pdb,PSDBQUERYRESULT 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 */
SdbpAddSingleLayerMatch(TAGREF layer,PSDBQUERYRESULT 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 */
SdbpAddNamedLayerMatch(HSDB hsdb,PCWSTR layerName,PSDBQUERYRESULT 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 */
SdbpAddExeLayers(HSDB hsdb,PDB pdb,TAGID tagExe,PSDBQUERYRESULT 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 */
SdbpAddExeMatch(HSDB hsdb,PDB pdb,TAGID tagExe,PSDBQUERYRESULT 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 */
SdbpAddLayerMatches(HSDB hsdb,PWSTR pwszLayers,DWORD pdwBytes,PSDBQUERYRESULT 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 
SdbpPropagateEnvLayers(HSDB hsdb,LPWSTR Environment,PSDBQUERYRESULT Result)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  */
SdbInitDatabase(DWORD flags,LPCWSTR path)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  */
SdbReleaseDatabase(HSDB hsdb)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  */
SdbGetMatchingExe(HSDB hsdb,LPCWSTR path,LPCWSTR module_name,LPCWSTR env,DWORD flags,PSDBQUERYRESULT result)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  */
SdbGetAppPatchDir(HSDB hsdb,LPWSTR path,DWORD size)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  */
SdbTagRefToTagID(HSDB hsdb,TAGREF trWhich,PDB * ppdb,TAGID * ptiWhich)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  */
SdbTagIDToTagRef(HSDB hsdb,PDB pdb,TAGID tiWhich,TAGREF * ptrWhich)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 */
SdbPackAppCompatData(HSDB hsdb,PSDBQUERYRESULT pQueryResult,PVOID * ppData,DWORD * pdwSize)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 
SdbUnpackAppCompatData(HSDB hsdb,LPCWSTR pszImageName,PVOID pData,PSDBQUERYRESULT pQueryResult)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 
SdbGetAppCompatDataSize(ShimData * pData)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 */
SdbQueryDataEx(HSDB hsdb,TAGREF trWhich,LPCWSTR lpszDataName,LPDWORD lpdwDataType,LPVOID lpBuffer,LPDWORD lpcbBufferSize,TAGREF * ptrData)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 */
SdbQueryData(HSDB hsdb,TAGREF trWhich,LPCWSTR lpszDataName,LPDWORD lpdwDataType,LPVOID lpBuffer,LPDWORD lpcbBufferSize)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