xref: /reactos/dll/appcompat/apphelp/sdbwrite.c (revision 9393fc32)
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