1 /* 2 * PROJECT: ReactOS Application compatibility module 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Shim database manipulation functions 5 * COPYRIGHT: Copyright 2011 André Hentschel 6 * Copyright 2013 Mislav Blažević 7 * Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org) 8 */ 9 10 #if !defined(SDBWRITE_HOSTTOOL) 11 #define WIN32_NO_STATUS 12 #include "windef.h" 13 #include "ntndk.h" 14 #include <appcompat/sdbtypes.h> 15 #include <appcompat/sdbtagid.h> 16 #else 17 #include <typedefs.h> 18 #include <guiddef.h> 19 #include <sdbtypes.h> 20 #include <sdbtagid.h> 21 #endif 22 23 24 #include "sdbpapi.h" 25 #include "sdbstringtable.h" 26 27 28 /* Local functions */ 29 BOOL WINAPI SdbWriteStringRefTag(PDB pdb, TAG tag, TAGID tagid); 30 BOOL WINAPI SdbWriteStringTag(PDB pdb, TAG tag, LPCWSTR string); 31 TAGID WINAPI SdbBeginWriteListTag(PDB pdb, TAG tag); 32 BOOL WINAPI SdbEndWriteListTag(PDB pdb, TAGID tagid); 33 34 /* sdbapi.c */ 35 void WINAPI SdbCloseDatabase(PDB); 36 37 /* Copy data to the allocated database */ 38 static void WINAPI SdbpWrite(PDB pdb, const void* data, DWORD size) 39 { 40 ASSERT(pdb->for_write); 41 if (pdb->write_iter + size > pdb->size) 42 { 43 DWORD oldSize = pdb->size; 44 /* Round to powers of two to prevent too many reallocations */ 45 while (pdb->size < pdb->write_iter + size) pdb->size <<= 1; 46 pdb->data = SdbReAlloc(pdb->data, pdb->size, oldSize); 47 } 48 49 memcpy(pdb->data + pdb->write_iter, data, size); 50 pdb->write_iter += size; 51 } 52 53 /* Add a string to the string table (creating it when it did not exist yet), 54 returning if it succeeded or not */ 55 static BOOL WINAPI SdbpGetOrAddStringRef(PDB pdb, LPCWSTR string, TAGID* tagid) 56 { 57 PDB buf = pdb->string_buffer; 58 ASSERT(pdb->for_write); 59 60 if (pdb->string_buffer == NULL) 61 { 62 pdb->string_buffer = buf = SdbAlloc(sizeof(DB)); 63 if (buf == NULL) 64 return FALSE; 65 buf->size = 128; 66 buf->data = SdbAlloc(buf->size); 67 buf->for_write = TRUE; 68 if (buf->data == NULL) 69 return FALSE; 70 } 71 72 *tagid = buf->write_iter + sizeof(TAG) + sizeof(DWORD); 73 if (SdbpAddStringToTable(&pdb->string_lookup, string, tagid)) 74 return SdbWriteStringTag(buf, TAG_STRINGTABLE_ITEM, string); 75 76 return pdb->string_lookup != NULL; 77 } 78 79 /* Write the in-memory stringtable to the specified db */ 80 static BOOL WINAPI SdbpWriteStringtable(PDB pdb) 81 { 82 TAGID table; 83 PDB buf = pdb->string_buffer; 84 if (buf == NULL || pdb->string_lookup == NULL) 85 return FALSE; 86 87 table = SdbBeginWriteListTag(pdb, TAG_STRINGTABLE); 88 SdbpWrite(pdb, buf->data, buf->write_iter); 89 return SdbEndWriteListTag(pdb, table); 90 } 91 92 /** 93 * Creates new shim database file 94 * 95 * If a file already exists on specified path, that file shall be overwritten. 96 * 97 * @note Use SdbCloseDatabaseWrite to close the database opened with this function. 98 * 99 * @param [in] path Path to the new shim database. 100 * @param [in] type Type of path. Either DOS_PATH or NT_PATH. 101 * 102 * @return Success: Handle to the newly created shim database, NULL otherwise. 103 */ 104 PDB WINAPI SdbCreateDatabase(LPCWSTR path, PATH_TYPE type) 105 { 106 static const DWORD version_major = 2, version_minor = 1; 107 static const char* magic = "sdbf"; 108 PDB pdb; 109 110 pdb = SdbpCreate(path, type, TRUE); 111 if (!pdb) 112 return NULL; 113 114 pdb->size = sizeof(DWORD) + sizeof(DWORD) + (DWORD)strlen(magic); 115 pdb->data = SdbAlloc(pdb->size); 116 117 SdbpWrite(pdb, &version_major, sizeof(DWORD)); 118 SdbpWrite(pdb, &version_minor, sizeof(DWORD)); 119 SdbpWrite(pdb, magic, (DWORD)strlen(magic)); 120 121 return pdb; 122 } 123 124 /** 125 * Closes specified database and writes data to file. 126 * 127 * @param [in] pdb Handle to the shim database. 128 */ 129 void WINAPI SdbCloseDatabaseWrite(PDB pdb) 130 { 131 ASSERT(pdb->for_write); 132 SdbpWriteStringtable(pdb); 133 SdbpFlush(pdb); 134 SdbCloseDatabase(pdb); 135 } 136 137 /** 138 * Writes a tag-only (NULL) entry to the specified shim database. 139 * 140 * @param [in] pdb Handle to the shim database. 141 * @param [in] tag A tag for the entry. 142 * 143 * @return TRUE if it succeeds, FALSE if it fails. 144 */ 145 BOOL WINAPI SdbWriteNULLTag(PDB pdb, TAG tag) 146 { 147 if (!SdbpCheckTagType(tag, TAG_TYPE_NULL)) 148 return FALSE; 149 150 SdbpWrite(pdb, &tag, sizeof(TAG)); 151 return TRUE; 152 } 153 154 /** 155 * Writes a WORD entry to the specified shim database. 156 * 157 * @param [in] pdb Handle to the shim database. 158 * @param [in] tag A tag for the entry. 159 * @param [in] data WORD entry which will be written to the database. 160 * 161 * @return TRUE if it succeeds, FALSE if it fails. 162 */ 163 BOOL WINAPI SdbWriteWORDTag(PDB pdb, TAG tag, WORD data) 164 { 165 if (!SdbpCheckTagType(tag, TAG_TYPE_WORD)) 166 return FALSE; 167 168 SdbpWrite(pdb, &tag, sizeof(TAG)); 169 SdbpWrite(pdb, &data, sizeof(data)); 170 return TRUE; 171 } 172 173 /** 174 * Writes a DWORD entry to the specified shim database. 175 * 176 * @param [in] pdb Handle to the shim database. 177 * @param [in] tag A tag for the entry. 178 * @param [in] data DWORD entry which will be written to the database. 179 * 180 * @return TRUE if it succeeds, FALSE if it fails. 181 */ 182 BOOL WINAPI SdbWriteDWORDTag(PDB pdb, TAG tag, DWORD data) 183 { 184 if (!SdbpCheckTagType(tag, TAG_TYPE_DWORD)) 185 return FALSE; 186 187 SdbpWrite(pdb, &tag, sizeof(TAG)); 188 SdbpWrite(pdb, &data, sizeof(data)); 189 return TRUE; 190 } 191 192 /** 193 * Writes a DWORD entry to the specified shim database. 194 * 195 * @param [in] pdb Handle to the shim database. 196 * @param [in] tag A tag for the entry. 197 * @param [in] data QWORD entry which will be written to the database. 198 * 199 * @return TRUE if it succeeds, FALSE if it fails. 200 */ 201 BOOL WINAPI SdbWriteQWORDTag(PDB pdb, TAG tag, QWORD data) 202 { 203 if (!SdbpCheckTagType(tag, TAG_TYPE_QWORD)) 204 return FALSE; 205 206 SdbpWrite(pdb, &tag, sizeof(TAG)); 207 SdbpWrite(pdb, &data, sizeof(data)); 208 return TRUE; 209 } 210 211 /** 212 * Writes a wide string entry to the specified shim database. 213 * 214 * @param [in] pdb Handle to the shim database. 215 * @param [in] tag A tag for the entry. 216 * @param [in] string Wide string entry which will be written to the database. 217 * 218 * @return TRUE if it succeeds, FALSE if it fails. 219 */ 220 BOOL WINAPI SdbWriteStringTag(PDB pdb, TAG tag, LPCWSTR string) 221 { 222 DWORD size; 223 224 if (SdbpCheckTagType(tag, TAG_TYPE_STRINGREF)) 225 { 226 TAGID tagid = 0; 227 if (!SdbpGetOrAddStringRef(pdb, string, &tagid)) 228 return FALSE; 229 230 return SdbWriteStringRefTag(pdb, tag, tagid); 231 } 232 233 if (!SdbpCheckTagType(tag, TAG_TYPE_STRING)) 234 return FALSE; 235 236 size = SdbpStrsize(string); 237 SdbpWrite(pdb, &tag, sizeof(TAG)); 238 SdbpWrite(pdb, &size, sizeof(size)); 239 SdbpWrite(pdb, string, size); 240 return TRUE; 241 } 242 243 /** 244 * Writes a stringref tag to specified database 245 * @note Reference (tagid) is not checked for validity. 246 * 247 * @param [in] pdb Handle to the shim database. 248 * @param [in] tag TAG which will be written. 249 * @param [in] tagid TAGID of the string tag refers to. 250 * 251 * @return TRUE if it succeeds, FALSE if it fails. 252 */ 253 BOOL WINAPI SdbWriteStringRefTag(PDB pdb, TAG tag, TAGID tagid) 254 { 255 if (!SdbpCheckTagType(tag, TAG_TYPE_STRINGREF)) 256 return FALSE; 257 258 SdbpWrite(pdb, &tag, sizeof(TAG)); 259 SdbpWrite(pdb, &tagid, sizeof(tagid)); 260 return TRUE; 261 } 262 263 /** 264 * Writes data the specified shim database. 265 * 266 * @param [in] pdb Handle to the shim database. 267 * @param [in] tag A tag for the entry. 268 * @param [in] data Pointer to data. 269 * @param [in] size Number of bytes to write. 270 * 271 * @return TRUE if it succeeds, FALSE if it fails. 272 */ 273 BOOL WINAPI SdbWriteBinaryTag(PDB pdb, TAG tag, const BYTE* data, DWORD size) 274 { 275 if (!SdbpCheckTagType(tag, TAG_TYPE_BINARY)) 276 return FALSE; 277 278 SdbpWrite(pdb, &tag, sizeof(TAG)); 279 SdbpWrite(pdb, &size, sizeof(size)); 280 SdbpWrite(pdb, data, size); 281 return TRUE; 282 } 283 284 #if !defined(SDBWRITE_HOSTTOOL) 285 /** 286 * Writes data from a file to the specified shim database. 287 * 288 * @param [in] pdb Handle to the shim database. 289 * @param [in] tag A tag for the entry. 290 * @param [in] path Path of the input file. 291 * 292 * @return TRUE if it succeeds, FALSE if it fails. 293 */ 294 BOOL WINAPI SdbWriteBinaryTagFromFile(PDB pdb, TAG tag, LPCWSTR path) 295 { 296 MEMMAPPED mapped; 297 298 if (!SdbpCheckTagType(tag, TAG_TYPE_BINARY)) 299 return FALSE; 300 301 if (!SdbpOpenMemMappedFile(path, &mapped)) 302 return FALSE; 303 304 SdbWriteBinaryTag(pdb, tag, mapped.view, mapped.size); 305 SdbpCloseMemMappedFile(&mapped); 306 return TRUE; 307 } 308 #endif 309 310 /** 311 * Writes a list tag to specified database All subsequent SdbWrite* functions shall write to 312 * newly created list untill TAGID of that list is passed to SdbEndWriteListTag. 313 * 314 * @param [in] pdb Handle to the shim database. 315 * @param [in] tag TAG for the list 316 * 317 * RETURNS Success: TAGID of the newly created list, or TAGID_NULL on failure. 318 * 319 * @return A TAGID. 320 */ 321 TAGID WINAPI SdbBeginWriteListTag(PDB pdb, TAG tag) 322 { 323 TAGID list_id; 324 DWORD dum = 0; 325 326 if (!SdbpCheckTagType(tag, TAG_TYPE_LIST)) 327 return TAGID_NULL; 328 329 list_id = pdb->write_iter; 330 SdbpWrite(pdb, &tag, sizeof(TAG)); 331 SdbpWrite(pdb, &dum, sizeof(dum)); /* reserve some memory for storing list size */ 332 return list_id; 333 } 334 335 /** 336 * Marks end of the specified list. 337 * 338 * @param [in] pdb Handle to the shim database. 339 * @param [in] tagid TAGID of the list. 340 * 341 * @return TRUE if it succeeds, FALSE if it fails. 342 */ 343 BOOL WINAPI SdbEndWriteListTag(PDB pdb, TAGID tagid) 344 { 345 ASSERT(pdb->for_write); 346 347 if (!SdbpCheckTagIDType(pdb, tagid, TAG_TYPE_LIST)) 348 return FALSE; 349 350 /* Write size of list to list tag header */ 351 *(DWORD*)&pdb->data[tagid + sizeof(TAG)] = pdb->write_iter - tagid - sizeof(TAG) - sizeof(TAGID); 352 return TRUE; 353 } 354 355