xref: /reactos/dll/appcompat/apphelp/sdbread.c (revision 9393fc32)
1 /*
2  * PROJECT:     ReactOS Application compatibility module
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Shim database query functions
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 #include "windef.h"
11 #include "apphelp.h"
12 
13 
SdbpReadData(PDB pdb,PVOID dest,DWORD offset,DWORD num)14 BOOL WINAPI SdbpReadData(PDB pdb, PVOID dest, DWORD offset, DWORD num)
15 {
16     DWORD size = offset + num;
17 
18     /* Either overflow or no data to read */
19     if (size <= offset)
20         return FALSE;
21 
22     /* Overflow */
23     if (pdb->size < size)
24         return FALSE;
25 
26     memcpy(dest, pdb->data + offset, num);
27     return TRUE;
28 }
29 
SdbpGetTagSize(PDB pdb,TAGID tagid)30 static DWORD WINAPI SdbpGetTagSize(PDB pdb, TAGID tagid)
31 {
32     WORD type;
33     DWORD size;
34 
35     type = SdbGetTagFromTagID(pdb, tagid) & TAG_TYPE_MASK;
36     if (type == TAG_NULL)
37         return 0;
38 
39     size = SdbGetTagDataSize(pdb, tagid);
40     if (type <= TAG_TYPE_STRINGREF)
41         return size += sizeof(TAG);
42     else size += (sizeof(TAG) + sizeof(DWORD));
43 
44     return size;
45 }
46 
SdbpGetString(PDB pdb,TAGID tagid,PDWORD size)47 LPWSTR WINAPI SdbpGetString(PDB pdb, TAGID tagid, PDWORD size)
48 {
49     TAG tag;
50     TAGID offset;
51 
52     tag = SdbGetTagFromTagID(pdb, tagid);
53     if (tag == TAG_NULL)
54         return NULL;
55 
56     if ((tag & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
57     {
58         /* No stringtable; all references are invalid */
59         if (pdb->stringtable == TAGID_NULL)
60             return NULL;
61 
62         /* TAG_TYPE_STRINGREF contains offset of string relative to stringtable */
63         if (!SdbpReadData(pdb, &tagid, tagid + sizeof(TAG), sizeof(TAGID)))
64             return NULL;
65 
66         offset = pdb->stringtable + tagid + sizeof(TAG) + sizeof(TAGID);
67     }
68     else if ((tag & TAG_TYPE_MASK) == TAG_TYPE_STRING)
69     {
70         offset = tagid + sizeof(TAG) + sizeof(TAGID);
71     }
72     else
73     {
74         SHIM_ERR("Tag 0x%u at tagid %u is neither a string or reference to string\n", tag, tagid);
75         return NULL;
76     }
77 
78     /* Optionally read string size */
79     if (size && !SdbpReadData(pdb, size, offset - sizeof(TAGID), sizeof(*size)))
80         return FALSE;
81 
82     return (LPWSTR)(&pdb->data[offset]);
83 }
84 
85 /**
86  * Searches shim database for the tag associated with specified tagid.
87  *
88  * @param [in]  pdb      Handle to the shim database.
89  * @param [in]  tagid   The TAGID of the tag.
90  *
91  * @return  Success: The tag associated with specified tagid, Failure: TAG_NULL.
92  */
SdbGetTagFromTagID(PDB pdb,TAGID tagid)93 TAG WINAPI SdbGetTagFromTagID(PDB pdb, TAGID tagid)
94 {
95     TAG data;
96     if (!SdbpReadData(pdb, &data, tagid, sizeof(data)))
97         return TAG_NULL;
98     return data;
99 }
100 
101 /**
102  * Retrieves size of data at specified tagid.
103  *
104  * @param [in]  pdb      Handle to the shim database.
105  * @param [in]  tagid   Tagid of tag whose size is queried.
106  *
107  * @return  Success: Size of data at specified tagid, Failure: 0.
108  */
SdbGetTagDataSize(PDB pdb,TAGID tagid)109 DWORD WINAPI SdbGetTagDataSize(PDB pdb, TAGID tagid)
110 {
111     /* sizes of data types with fixed size */
112     static const SIZE_T sizes[6] = {
113         0, /* NULL  */ 1, /* BYTE      */
114         2, /* WORD  */ 4, /* DWORD     */
115         8, /* QWORD */ 4  /* STRINGREF */
116     };
117     WORD type;
118     DWORD size;
119 
120     type = SdbGetTagFromTagID(pdb, tagid) & TAG_TYPE_MASK;
121     if (type == TAG_NULL)
122         return 0;
123 
124     if (type <= TAG_TYPE_STRINGREF)
125         return sizes[(type >> 12) - 1];
126 
127     /* tag with dynamic size (e.g. list): must read size */
128     if (!SdbpReadData(pdb, &size, tagid + sizeof(TAG), sizeof(size)))
129         return 0;
130 
131     return size;
132 }
133 
134 /**
135  * Searches shim database for a child of specified parent tag.
136  *
137  * @param [in]  pdb      Handle to the shim database.
138  * @param [in]  parent  TAGID of parent.
139  *
140  * @return  Success: TAGID of child tag, Failure: TAGID_NULL.
141  */
SdbGetFirstChild(PDB pdb,TAGID parent)142 TAGID WINAPI SdbGetFirstChild(PDB pdb, TAGID parent)
143 {
144     /* if we are at beginning of database */
145     if (parent == TAGID_ROOT)
146     {
147         /* header only database: no tags */
148         if (pdb->size <= _TAGID_ROOT)
149             return TAGID_NULL;
150         /* return *real* root tagid */
151         else return _TAGID_ROOT;
152     }
153 
154     /* only list tag can have children */
155     if ((SdbGetTagFromTagID(pdb, parent) & TAG_TYPE_MASK) != TAG_TYPE_LIST)
156         return TAGID_NULL;
157 
158     /* first child is sizeof(TAG) + sizeof(DWORD) bytes after beginning of list */
159     return parent + sizeof(TAG) + sizeof(DWORD);
160 }
161 
162 /**
163  * Searches shim database for next child of specified parent tag.
164  *
165  * @param [in]  pdb          Handle to the shim database.
166  * @param [in]  parent      TAGID of parent.
167  * @param [in]  prev_child  TAGID of previous child.
168  *
169  * @return  Success: TAGID of next child tag, Failure: TAGID_NULL.
170  */
SdbGetNextChild(PDB pdb,TAGID parent,TAGID prev_child)171 TAGID WINAPI SdbGetNextChild(PDB pdb, TAGID parent, TAGID prev_child)
172 {
173     TAGID next_child;
174     DWORD prev_child_size, parent_size;
175 
176     prev_child_size = SdbpGetTagSize(pdb, prev_child);
177     if (prev_child_size == 0)
178         return TAGID_NULL;
179 
180     /* Bound check */
181     next_child = prev_child + prev_child_size;
182     if (next_child >= pdb->size)
183         return TAGID_NULL;
184 
185     if (parent == TAGID_ROOT)
186         return next_child;
187 
188     parent_size = SdbpGetTagSize(pdb, parent);
189     if (parent_size == 0)
190         return TAGID_NULL;
191 
192     /* Specified parent has no more children */
193     if (next_child >= parent + parent_size)
194         return TAGID_NULL;
195 
196     return next_child;
197 }
198 
199 /**
200  * Searches shim database for a tag within specified domain.
201  *
202  * @param [in]  pdb      Handle to the shim database.
203  * @param [in]  parent  TAGID of parent.
204  * @param [in]  tag     TAG to be located.
205  *
206  * @return  Success: TAGID of first matching tag, Failure: TAGID_NULL.
207  */
SdbFindFirstTag(PDB pdb,TAGID parent,TAG tag)208 TAGID WINAPI SdbFindFirstTag(PDB pdb, TAGID parent, TAG tag)
209 {
210     TAGID iter;
211 
212     iter = SdbGetFirstChild(pdb, parent);
213     while (iter != TAGID_NULL)
214     {
215         if (SdbGetTagFromTagID(pdb, iter) == tag)
216             return iter;
217         iter = SdbGetNextChild(pdb, parent, iter);
218     }
219     return TAGID_NULL;
220 }
221 
222 /**
223  * Searches shim database for a next tag which matches prev_child within parent's domain.
224  *
225  * @param [in]  pdb          Handle to the shim database.
226  * @param [in]  parent      TAGID of parent.
227  * @param [in]  prev_child  TAGID of previous match.
228  *
229  * @return  Success: TAGID of next match, Failure: TAGID_NULL.
230  */
SdbFindNextTag(PDB pdb,TAGID parent,TAGID prev_child)231 TAGID WINAPI SdbFindNextTag(PDB pdb, TAGID parent, TAGID prev_child)
232 {
233     TAG tag;
234     TAGID iter;
235 
236     tag = SdbGetTagFromTagID(pdb, prev_child);
237     iter = SdbGetNextChild(pdb, parent, prev_child);
238 
239     while (iter != TAGID_NULL)
240     {
241         if (SdbGetTagFromTagID(pdb, iter) == tag)
242             return iter;
243         iter = SdbGetNextChild(pdb, parent, iter);
244     }
245     return TAGID_NULL;
246 }
247 
248 /**
249  * Searches shim database for string associated with specified tagid and copies string into a
250  * buffer.
251  *
252  * If size parameter is less than number of characters in string, this function shall fail and
253  * no data shall be copied.
254  *
255  * @param [in]  pdb      Handle to the shim database.
256  * @param [in]  tagid   TAGID of string or stringref associated with the string.
257  * @param [out] buffer  Buffer in which string will be copied.
258  * @param [in]  size    Number of characters to copy.
259  *
260  * @return  TRUE if string was successfully copied to the buffer FALSE if string was not copied
261  *          to the buffer.
262  */
SdbReadStringTag(PDB pdb,TAGID tagid,LPWSTR buffer,DWORD size)263 BOOL WINAPI SdbReadStringTag(PDB pdb, TAGID tagid, LPWSTR buffer, DWORD size)
264 {
265     LPWSTR string;
266     DWORD string_size;
267 
268     string = SdbpGetString(pdb, tagid, &string_size);
269     if (!string)
270         return FALSE;
271 
272     /* Check if buffer is too small */
273     if (size * sizeof(WCHAR) < string_size)
274         return FALSE;
275 
276     memcpy(buffer, string, string_size);
277     return TRUE;
278 }
279 
280 /**
281  * Reads WORD value at specified tagid.
282  *
283  * @param [in]  pdb      Handle to the shim database.
284  * @param [in]  tagid   TAGID of WORD value.
285  * @param [in]  ret     Default return value in case function fails.
286  *
287  * @return  Success: WORD value at specified tagid, or ret on failure.
288  */
SdbReadWORDTag(PDB pdb,TAGID tagid,WORD ret)289 WORD WINAPI SdbReadWORDTag(PDB pdb, TAGID tagid, WORD ret)
290 {
291     if (SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_WORD))
292         SdbpReadData(pdb, &ret, tagid + 2, sizeof(WORD));
293     return ret;
294 }
295 
296 /**
297  * Reads DWORD value at specified tagid.
298  *
299  * @param [in]  pdb      Handle to the shim database.
300  * @param [in]  tagid   TAGID of DWORD value.
301  * @param [in]  ret     Default return value in case function fails.
302  *
303  * @return  Success: DWORD value at specified tagid, otherwise ret.
304  */
SdbReadDWORDTag(PDB pdb,TAGID tagid,DWORD ret)305 DWORD WINAPI SdbReadDWORDTag(PDB pdb, TAGID tagid, DWORD ret)
306 {
307     if (SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_DWORD))
308         SdbpReadData(pdb, &ret, tagid + 2, sizeof(DWORD));
309     return ret;
310 }
311 
312 /**
313  * Reads QWORD value at specified tagid.
314  *
315  * @param [in]  pdb      Handle to the shim database.
316  * @param [in]  tagid   TAGID of QWORD value.
317  * @param [in]  ret     Default return value in case function fails.
318  *
319  * @return  Success: QWORD value at specified tagid, otherwise ret.
320  */
SdbReadQWORDTag(PDB pdb,TAGID tagid,QWORD ret)321 QWORD WINAPI SdbReadQWORDTag(PDB pdb, TAGID tagid, QWORD ret)
322 {
323     if (SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_QWORD))
324         SdbpReadData(pdb, &ret, tagid + sizeof(TAG), sizeof(QWORD));
325     return ret;
326 }
327 
328 /**
329  * Reads binary data at specified tagid.
330  *
331  * @param [in]  pdb      Handle to the shim database.
332  * @param [in]  tagid   TAGID of binary data.
333  * @param [out] buffer  Buffer in which data will be copied.
334  * @param [in]  size    Size of the buffer.
335  *
336  * @return  TRUE if data was successfully written, or FALSE otherwise.
337  */
SdbReadBinaryTag(PDB pdb,TAGID tagid,PBYTE buffer,DWORD size)338 BOOL WINAPI SdbReadBinaryTag(PDB pdb, TAGID tagid, PBYTE buffer, DWORD size)
339 {
340     DWORD data_size = 0;
341 
342     if (SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_BINARY))
343     {
344         SdbpReadData(pdb, &data_size, tagid + sizeof(TAG), sizeof(data_size));
345         if (size >= data_size)
346             return SdbpReadData(pdb, buffer, tagid + sizeof(TAG) + sizeof(data_size), data_size);
347     }
348 
349     return FALSE;
350 }
351 
352 /**
353  * Retrieves binary data at specified tagid.
354  *
355  * @param [in]  pdb      Handle to the shim database.
356  * @param [in]  tagid   TAGID of binary data.
357  *
358  * @return  Success: Pointer to binary data at specified tagid, or NULL on failure.
359  */
SdbGetBinaryTagData(PDB pdb,TAGID tagid)360 PVOID WINAPI SdbGetBinaryTagData(PDB pdb, TAGID tagid)
361 {
362     if (!SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_BINARY))
363         return NULL;
364     return &pdb->data[tagid + sizeof(TAG) + sizeof(DWORD)];
365 }
366 
367 /**
368  * Searches shim database for string associated with specified tagid.
369  *
370  * @param [in]  pdb      Handle to the shim database.
371  * @param [in]  tagid   TAGID of string or stringref associated with the string.
372  *
373  * @return  the LPWSTR associated with specified tagid, or NULL on failure.
374  */
SdbGetStringTagPtr(PDB pdb,TAGID tagid)375 LPWSTR WINAPI SdbGetStringTagPtr(PDB pdb, TAGID tagid)
376 {
377     return SdbpGetString(pdb, tagid, NULL);
378 }
379 
380 /**
381  * Reads binary data at specified tagid.
382  *
383  * @param [in]  pdb      Handle to the shim database.
384  * @param [out] Guid    Database ID.
385  *
386  * @return  true if the ID was found FALSE otherwise.
387  */
SdbGetDatabaseID(PDB pdb,GUID * Guid)388 BOOL WINAPI SdbGetDatabaseID(PDB pdb, GUID* Guid)
389 {
390     if(SdbIsNullGUID(&pdb->database_id))
391     {
392         TAGID root = SdbFindFirstTag(pdb, TAGID_ROOT, TAG_DATABASE);
393         if(root != TAGID_NULL)
394         {
395             TAGID id = SdbFindFirstTag(pdb, root, TAG_DATABASE_ID);
396             if(id != TAGID_NULL)
397             {
398                 if(!SdbReadBinaryTag(pdb, id, (PBYTE)&pdb->database_id, sizeof(pdb->database_id)))
399                 {
400                     memset(&pdb->database_id, 0, sizeof(pdb->database_id));
401                 }
402             }
403             else
404             {
405                 /* Should we silence this if we are opening a system db? */
406                 SHIM_ERR("Failed to get the database id\n");
407             }
408         }
409         else
410         {
411             /* Should we silence this if we are opening a system db? */
412             SHIM_ERR("Failed to get root tag\n");
413         }
414     }
415     if(!SdbIsNullGUID(&pdb->database_id))
416     {
417         memcpy(Guid, &pdb->database_id, sizeof(pdb->database_id));
418         return TRUE;
419     }
420     return FALSE;
421 }
422 
423