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