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 */
SdbpWrite(PDB pdb,const void * data,DWORD size)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 */
SdbpGetOrAddStringRef(PDB pdb,LPCWSTR string,TAGID * tagid)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 */
SdbpWriteStringtable(PDB pdb)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 */
SdbCreateDatabase(LPCWSTR path,PATH_TYPE type)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 */
SdbCloseDatabaseWrite(PDB pdb)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 */
SdbWriteNULLTag(PDB pdb,TAG tag)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 */
SdbWriteWORDTag(PDB pdb,TAG tag,WORD data)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 */
SdbWriteDWORDTag(PDB pdb,TAG tag,DWORD data)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 */
SdbWriteQWORDTag(PDB pdb,TAG tag,QWORD data)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 */
SdbWriteStringTag(PDB pdb,TAG tag,LPCWSTR string)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 */
SdbWriteStringRefTag(PDB pdb,TAG tag,TAGID tagid)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 */
SdbWriteBinaryTag(PDB pdb,TAG tag,const BYTE * data,DWORD size)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 */
SdbWriteBinaryTagFromFile(PDB pdb,TAG tag,LPCWSTR path)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 */
SdbBeginWriteListTag(PDB pdb,TAG tag)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 */
SdbEndWriteListTag(PDB pdb,TAGID tagid)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