[STORAHCI] Merge Storport Miniport driver by Aman Priyadarshi in GSoC.
[reactos.git] / reactos / dll / appcompat / apphelp / hsdb.c
1 /*
2 * Copyright 2011 André Hentschel
3 * Copyright 2013 Mislav Blažević
4 * Copyright 2015,2016 Mark Jansen
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #define WIN32_NO_STATUS
22 #include "windows.h"
23 #include "ntndk.h"
24 #include "strsafe.h"
25 #include "apphelp.h"
26
27 #include "wine/unicode.h"
28
29
30 static BOOL WINAPI SdbpFileExists(LPCWSTR path)
31 {
32 DWORD attr = GetFileAttributesW(path);
33 return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
34 }
35
36
37 /**
38 * Opens specified shim database file Handle returned by this function may only be used by
39 * functions which take HSDB param thus differing it from SdbOpenDatabase.
40 *
41 * @param [in] flags Specifies type of path or predefined database.
42 * @param [in] path Path to the shim database file.
43 *
44 * @return Success: Handle to the opened shim database, NULL otherwise.
45 */
46 HSDB WINAPI SdbInitDatabase(DWORD flags, LPCWSTR path)
47 {
48 static const WCHAR shim[] = {'\\','s','y','s','m','a','i','n','.','s','d','b',0};
49 static const WCHAR msi[] = {'\\','m','s','i','m','a','i','n','.','s','d','b',0};
50 static const WCHAR drivers[] = {'\\','d','r','v','m','a','i','n','.','s','d','b',0};
51 LPCWSTR name;
52 WCHAR buffer[128];
53 HSDB sdb;
54
55 sdb = SdbAlloc(sizeof(SDB));
56 if (!sdb)
57 return NULL;
58 sdb->auto_loaded = 0;
59
60 /* Check for predefined databases */
61 if ((flags & HID_DATABASE_TYPE_MASK) && path == NULL)
62 {
63 switch (flags & HID_DATABASE_TYPE_MASK)
64 {
65 case SDB_DATABASE_MAIN_SHIM: name = shim; break;
66 case SDB_DATABASE_MAIN_MSI: name = msi; break;
67 case SDB_DATABASE_MAIN_DRIVERS: name = drivers; break;
68 default: return NULL;
69 }
70 SdbGetAppPatchDir(NULL, buffer, 128);
71 memcpy(buffer + lstrlenW(buffer), name, SdbpStrlen(name));
72 }
73
74 sdb->db = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1);
75
76 /* If database could not be loaded, a handle doesn't make sense either */
77 if (!sdb->db)
78 {
79 SdbReleaseDatabase(sdb);
80 return NULL;
81 }
82
83 return sdb;
84 }
85
86 /**
87 * Closes shim database opened by SdbInitDatabase.
88 *
89 * @param [in] hsdb Handle to the shim database.
90 */
91 void WINAPI SdbReleaseDatabase(HSDB hsdb)
92 {
93 SdbCloseDatabase(hsdb->db);
94 SdbFree(hsdb);
95 }
96
97 /**
98 * Queries database for a specified exe If hsdb is NULL default database shall be loaded and
99 * searched.
100 *
101 * @param [in] hsdb Handle to the shim database.
102 * @param [in] path Path to executable for which we query database.
103 * @param [in] module_name Unused.
104 * @param [in] env Unused.
105 * @param [in] flags 0 or SDBGMEF_IGNORE_ENVIRONMENT.
106 * @param [out] result Pointer to structure in which query result shall be stored.
107 *
108 * @return TRUE if it succeeds, FALSE if it fails.
109 */
110 BOOL WINAPI SdbGetMatchingExe(HSDB hsdb, LPCWSTR path, LPCWSTR module_name,
111 LPCWSTR env, DWORD flags, PSDBQUERYRESULT result)
112 {
113 static const WCHAR fmt[] = {'%','s','%','s',0};
114 BOOL ok, ret;
115 TAGID database, iter, attr;
116 PATTRINFO attribs = NULL;
117 /*DWORD attr_count;*/
118 LPWSTR file_name;
119 WCHAR dir_path[128];
120 WCHAR buffer[256];
121 PDB db;
122
123 /* Load default database if one is not specified */
124 if (!hsdb)
125 {
126 /* To reproduce windows behaviour HID_DOS_PATHS needs
127 * to be specified when loading default database */
128 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
129 if(hsdb)
130 hsdb->auto_loaded = TRUE;
131 }
132
133 /* No database could be loaded */
134 if (!hsdb)
135 return FALSE;
136
137 db = hsdb->db;
138
139 /* Extract file name */
140 file_name = strrchrW(path, '\\') + 1;
141
142 /* Extract directory path */
143 memcpy(dir_path, path, (size_t)(file_name - path) * sizeof(WCHAR));
144
145 /* Get information about executable required to match it with database entry */
146 /*if (!SdbGetFileAttributes(path, &attribs, &attr_count))
147 return FALSE;*/
148
149 /* DATABASE is list TAG which contains all executables */
150 database = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
151 if (database == TAGID_NULL)
152 return FALSE;
153
154 /* EXE is list TAG which contains data required to match executable */
155 iter = SdbFindFirstTag(db, database, TAG_EXE);
156
157 /* Search for entry in database */
158 while (iter != TAGID_NULL)
159 {
160 /* Check if exe name matches */
161 attr = SdbFindFirstTag(db, iter, TAG_NAME);
162 if (lstrcmpiW(SdbGetStringTagPtr(db, attr), file_name) == 0)
163 {
164 /* Assume that entry is found (in case there are no "matching files") */
165 ok = TRUE;
166
167 /* Check if all "matching files" exist */
168 /* TODO: check size/checksum as well */
169 for (attr = SdbFindFirstTag(db, attr, TAG_MATCHING_FILE);
170 attr != TAGID_NULL; attr = SdbFindNextTag(db, iter, attr))
171 {
172 snprintfW(buffer, 256, fmt, dir_path, SdbGetStringTagPtr(db, attr));
173 if (!SdbpFileExists(buffer))
174 ok = FALSE;
175 }
176
177 /* Found it! */
178 if (ok)
179 {
180 /* TODO: fill result data */
181 /* TODO: there may be multiple matches */
182 ret = TRUE;
183 goto cleanup;
184 }
185 }
186
187 /* Continue iterating */
188 iter = SdbFindNextTag(db, database, iter);
189 }
190
191 /* Exe not found */
192 ret = FALSE;
193
194 cleanup:
195 SdbFreeFileAttributes(attribs);
196 if (hsdb->auto_loaded) SdbReleaseDatabase(hsdb);
197 return ret;
198 }
199
200 /**
201 * Retrieves AppPatch directory.
202 *
203 * @param [in] db Handle to the shim database.
204 * @param [out] path Pointer to memory in which path shall be written.
205 * @param [in] size Size of the buffer in characters.
206 */
207 BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size)
208 {
209 static WCHAR* default_dir = NULL;
210 static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0};
211
212 /* In case function fails, path holds empty string */
213 if (size > 0)
214 *path = 0;
215
216 if (!default_dir)
217 {
218 WCHAR* tmp = NULL;
219 UINT len = GetSystemWindowsDirectoryW(NULL, 0) + lstrlenW(szAppPatch);
220 tmp = SdbAlloc((len + 1)* sizeof(WCHAR));
221 if (tmp)
222 {
223 UINT r = GetSystemWindowsDirectoryW(tmp, len+1);
224 if (r && r < len)
225 {
226 if (SUCCEEDED(StringCchCatW(tmp, len+1, szAppPatch)))
227 {
228 if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL)
229 tmp = NULL;
230 }
231 }
232 if (tmp)
233 SdbFree(tmp);
234 }
235 if (!default_dir)
236 {
237 SHIM_ERR("Unable to obtain default AppPatch directory\n");
238 return FALSE;
239 }
240 }
241
242 if (!db)
243 {
244 return SUCCEEDED(StringCchCopyW(path, size, default_dir));
245 }
246 else
247 {
248 SHIM_ERR("Unimplemented for db != NULL\n");
249 return FALSE;
250 }
251 }
252
253
254 /**
255 * Translates the given trWhich to a specific database / tagid
256 *
257 * @param [in] hsdb Handle to the database.
258 * @param [in] trWhich Tagref to find
259 * @param [out,opt] ppdb The Shim database that trWhich belongs to.
260 * @param [out,opt] ptiWhich The tagid that trWhich corresponds to.
261 *
262 * @return TRUE if it succeeds, FALSE if it fails.
263 */
264 BOOL WINAPI SdbTagRefToTagID(HSDB hsdb, TAGREF trWhich, PDB* ppdb, TAGID* ptiWhich)
265 {
266 if (trWhich & 0xf0000000)
267 {
268 SHIM_ERR("Multiple shim databases not yet implemented!\n");
269 if (ppdb)
270 *ppdb = NULL;
271 if (ptiWhich)
272 *ptiWhich = TAG_NULL;
273 return FALSE;
274 }
275
276 /* There seems to be no range checking on trWhich.. */
277 if (ppdb)
278 *ppdb = hsdb->db;
279 if (ptiWhich)
280 *ptiWhich = trWhich & 0x0fffffff;
281
282 return TRUE;
283 }
284
285 /**
286 * Translates the given trWhich to a specific database / tagid
287 *
288 * @param [in] hsdb Handle to the database.
289 * @param [in] pdb The Shim database that tiWhich belongs to.
290 * @param [in] tiWhich Path to executable for which we query database.
291 * @param [out,opt] ptrWhich The tagid that tiWhich corresponds to.
292 *
293 * @return TRUE if it succeeds, FALSE if it fails.
294 */
295 BOOL WINAPI SdbTagIDToTagRef(HSDB hsdb, PDB pdb, TAGID tiWhich, TAGREF* ptrWhich)
296 {
297 if (pdb != hsdb->db)
298 {
299 SHIM_ERR("Multiple shim databases not yet implemented!\n");
300 if (ptrWhich)
301 *ptrWhich = TAGREF_NULL;
302 return FALSE;
303 }
304
305 if (ptrWhich)
306 *ptrWhich = tiWhich & 0x0fffffff;
307
308 return TRUE;
309 }
310
311
312