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