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