Reactos
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-2025 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 */
29BOOL WINAPI SdbWriteStringRefTag(PDB pdb, TAG tag, TAGID tagid);
30BOOL WINAPI SdbWriteStringTag(PDB pdb, TAG tag, LPCWSTR string);
31TAGID WINAPI SdbBeginWriteListTag(PDB pdb, TAG tag);
32BOOL WINAPI SdbEndWriteListTag(PDB pdb, TAGID tagid);
33
34/* sdbapi.c */
35void WINAPI SdbCloseDatabase(PDB);
36
37/* Copy data to the allocated database */
38static 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 */
55static 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 */
80static 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 */
104PDB 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 */
129void 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 */
145BOOL 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 */
163BOOL 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 */
182BOOL 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 */
201BOOL 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 */
220BOOL 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 */
253BOOL 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 */
273BOOL 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 */
294BOOL 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 */
321TAGID 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 */
343BOOL 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