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