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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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