[DEVMGR] Hackfix CORE-5643
[reactos.git] / dll / appcompat / apphelp / sdbfileattr.c
1 /*
2 * PROJECT: ReactOS Application compatibility module
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Query file attributes used to match exe's
5 * COPYRIGHT: Copyright 2011 André Hentschel
6 * Copyright 2013 Mislav Blaževic
7 * Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org)
8 */
9
10 #define WIN32_NO_STATUS
11 #include "windef.h"
12 #include "apphelp.h"
13 #include "strsafe.h"
14 #include "winver.h"
15 #include "rtlfuncs.h"
16
17
18 #define NUM_ATTRIBUTES 28
19 enum APPHELP_MODULETYPE
20 {
21 MODTYPE_UNKNOWN = 0,
22 MODTYPE_DOS = 1,
23 MODTYPE_NE = 2,
24 MODTYPE_PE = 3,
25 };
26
27
28 static void WINAPI SdbpSetDWORDAttr(PATTRINFO attr, TAG tag, DWORD value)
29 {
30 attr->type = tag;
31 attr->flags = ATTRIBUTE_AVAILABLE;
32 attr->dwattr = value;
33 }
34
35 static void WINAPI SdbpSetQWORDAttr(PATTRINFO attr, TAG tag, QWORD value)
36 {
37 attr->type = tag;
38 attr->flags = ATTRIBUTE_AVAILABLE;
39 attr->qwattr = value;
40 }
41
42 static void WINAPI SdbpSetStringAttr(PATTRINFO attr, TAG tag, WCHAR *string)
43 {
44 if (!string)
45 {
46 attr->flags = ATTRIBUTE_FAILED;
47 return;
48 }
49
50 attr->type = tag;
51 attr->flags = ATTRIBUTE_AVAILABLE;
52 attr->lpattr = SdbpStrDup(string);
53 }
54
55 static void WINAPI SdbpSetAttrFail(PATTRINFO attr)
56 {
57 attr->flags = ATTRIBUTE_FAILED;
58 }
59
60 static WCHAR* WINAPI SdbpGetStringAttr(LPWSTR translation, LPCWSTR attr, PVOID file_info)
61 {
62 UINT size = 0;
63 PVOID buffer;
64 WCHAR value[128] = {0};
65
66 if (!file_info)
67 return NULL;
68
69 StringCchPrintfW(value, ARRAYSIZE(value), translation, attr);
70 if (VerQueryValueW(file_info, value, &buffer, &size) && size != 0)
71 return (WCHAR*)buffer;
72
73 return NULL;
74 }
75
76 static void WINAPI SdbpSetStringAttrFromAnsiString(PATTRINFO attr, TAG tag, PBYTE string, size_t len)
77 {
78 WCHAR* dest;
79 if (!string)
80 {
81 attr->flags = ATTRIBUTE_FAILED;
82 return;
83 }
84
85 attr->type = tag;
86 attr->flags = ATTRIBUTE_AVAILABLE;
87 dest = attr->lpattr = SdbAlloc((len+1) * sizeof(WCHAR));
88 while (len--)
89 *(dest++) = *(string++);
90 *dest = 0;
91 }
92
93 static void WINAPI SdbpSetStringAttrFromPascalString(PATTRINFO attr, TAG tag, PBYTE string)
94 {
95 if (!string)
96 {
97 attr->flags = ATTRIBUTE_FAILED;
98 return;
99 }
100
101 SdbpSetStringAttrFromAnsiString(attr, tag, string + 1, *string);
102 }
103
104 static void SdbpReadFileVersion(PATTRINFO attr_info, PVOID file_info)
105 {
106 static const WCHAR str_root[] = {'\\',0};
107
108 VS_FIXEDFILEINFO* fixed_info;
109 UINT size;
110 if (file_info && VerQueryValueW(file_info, str_root, (LPVOID*)&fixed_info, &size) && size)
111 {
112 if (fixed_info->dwSignature == VS_FFI_SIGNATURE)
113 {
114 LARGE_INTEGER version;
115 version.HighPart = fixed_info->dwFileVersionMS;
116 version.LowPart = fixed_info->dwFileVersionLS;
117 SdbpSetQWORDAttr(&attr_info[2], TAG_BIN_FILE_VERSION, version.QuadPart);
118 SdbpSetQWORDAttr(&attr_info[21], TAG_UPTO_BIN_FILE_VERSION, version.QuadPart);
119 version.HighPart = fixed_info->dwProductVersionMS;
120 version.LowPart = fixed_info->dwProductVersionLS;
121 SdbpSetQWORDAttr(&attr_info[3], TAG_BIN_PRODUCT_VERSION, version.QuadPart);
122 SdbpSetQWORDAttr(&attr_info[22], TAG_UPTO_BIN_PRODUCT_VERSION, version.QuadPart);
123
124 SdbpSetDWORDAttr(&attr_info[12], TAG_VERDATEHI, fixed_info->dwFileDateMS);
125 SdbpSetDWORDAttr(&attr_info[13], TAG_VERDATELO, fixed_info->dwFileDateLS);
126 SdbpSetDWORDAttr(&attr_info[14], TAG_VERFILEOS, fixed_info->dwFileOS); /* 0x000, 0x4, 0x40004, 0x40000, 0x10004, 0x10001*/
127 SdbpSetDWORDAttr(&attr_info[15], TAG_VERFILETYPE, fixed_info->dwFileType); /* VFT_APP, VFT_DLL, .... */
128 return;
129 }
130 }
131
132 SdbpSetAttrFail(&attr_info[2]);
133 SdbpSetAttrFail(&attr_info[3]);
134 SdbpSetAttrFail(&attr_info[12]);
135 SdbpSetAttrFail(&attr_info[13]);
136 SdbpSetAttrFail(&attr_info[14]);
137 SdbpSetAttrFail(&attr_info[15]);
138 SdbpSetAttrFail(&attr_info[21]);
139 SdbpSetAttrFail(&attr_info[22]);
140 }
141
142 static DWORD WINAPI SdbpCalculateFileChecksum(PMEMMAPPED mapping)
143 {
144 size_t n, size;
145 PDWORD data;
146 DWORD checks = 0, carry = 0;
147
148 if (mapping->size < 4)
149 return 0;
150
151 if (mapping->size >= 0x1000)
152 {
153 size = 0x1000;
154 if (mapping->size < 0x1200)
155 data = (PDWORD)(mapping->view + mapping->size - size);
156 else
157 data = (PDWORD)mapping->view + (0x200 / 4);
158 }
159 else
160 {
161 data = (PDWORD)mapping->view;
162 size = mapping->size;
163 }
164
165 for (n = 0; n < size / 4; ++n)
166 {
167 checks += *data;
168 carry = (checks & 1) ? 0x80000000 : 0;
169 checks >>= 1;
170 checks |= carry;
171 ++data;
172 }
173 return checks;
174 }
175
176 static DWORD WINAPI SdbpGetModuleType(PMEMMAPPED mapping, PIMAGE_NT_HEADERS* nt_headers)
177 {
178 PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapping->view;
179 PIMAGE_OS2_HEADER os2;
180
181 *nt_headers = NULL;
182
183 if (mapping->size < 2 || dos->e_magic != IMAGE_DOS_SIGNATURE)
184 return MODTYPE_UNKNOWN;
185
186 if (mapping->size < sizeof(IMAGE_DOS_HEADER) || mapping->size < (dos->e_lfanew+2))
187 return MODTYPE_DOS;
188
189 os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew);
190 if (os2->ne_magic == IMAGE_OS2_SIGNATURE || os2->ne_magic == IMAGE_OS2_SIGNATURE_LE)
191 {
192 *nt_headers = (PIMAGE_NT_HEADERS)os2;
193 return MODTYPE_NE;
194 }
195
196 if (mapping->size >= (dos->e_lfanew + 4) && ((PIMAGE_NT_HEADERS)os2)->Signature == IMAGE_NT_SIGNATURE)
197 {
198 *nt_headers = (PIMAGE_NT_HEADERS)os2;
199 return MODTYPE_PE;
200 }
201
202 return MODTYPE_DOS;
203 }
204
205 /**
206 * Frees attribute data allocated by SdbGetFileAttributes.
207 *
208 * @note Unlike Windows, this implementation will not crash if attr_info is NULL.
209 *
210 * @param [in] attr_info Pointer to array of ATTRINFO which will be freed.
211 *
212 * @return TRUE if it succeeds, FALSE if it fails.
213 */
214 BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info)
215 {
216 WORD i;
217
218 if (!attr_info)
219 return FALSE;
220
221 for (i = 0; i < NUM_ATTRIBUTES; i++)
222 if ((attr_info[i].type & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
223 SdbFree(attr_info[i].lpattr);
224 SdbFree(attr_info);
225 return TRUE;
226 }
227
228 /**
229 * Retrieves attribute data shim database requires to match a file with database entry
230 *
231 * @note You must free the attr_info allocated by this function by calling SdbFreeFileAttributes.
232 *
233 * @param [in] path Path to the file.
234 * @param [out] attr_info_ret Pointer to array of ATTRINFO. Contains attribute data.
235 * @param [out] attr_count Number of attributes in attr_info.
236 *
237 * @return TRUE if it succeeds, FALSE if it fails.
238 */
239 BOOL WINAPI SdbGetFileAttributes(LPCWSTR path, PATTRINFO *attr_info_ret, LPDWORD attr_count)
240 {
241 static const WCHAR str_tinfo[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0};
242 static const WCHAR str_trans[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\','%','0','4','x','%','0','4','x','\\','%','%','s',0};
243 static const WCHAR str_CompanyName[] = {'C','o','m','p','a','n','y','N','a','m','e',0};
244 static const WCHAR str_FileDescription[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0};
245 static const WCHAR str_FileVersion[] = {'F','i','l','e','V','e','r','s','i','o','n',0};
246 static const WCHAR str_InternalName[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0};
247 static const WCHAR str_LegalCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0};
248 static const WCHAR str_OriginalFilename[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0};
249 static const WCHAR str_ProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
250 static const WCHAR str_ProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
251
252 PIMAGE_NT_HEADERS headers;
253 MEMMAPPED mapped;
254 PBYTE mapping_end;
255 PVOID file_info = 0;
256 DWORD module_type;
257 WCHAR translation[128] = {0};
258 PATTRINFO attr_info;
259
260 struct LANGANDCODEPAGE {
261 WORD language;
262 WORD code_page;
263 } *lang_page;
264
265 if (!SdbpOpenMemMappedFile(path, &mapped))
266 {
267 SHIM_ERR("Error retrieving FILEINFO structure\n");
268 return FALSE;
269 }
270 mapping_end = mapped.view + mapped.size;
271
272 attr_info = (PATTRINFO)SdbAlloc(NUM_ATTRIBUTES * sizeof(ATTRINFO));
273
274 SdbpSetDWORDAttr(&attr_info[0], TAG_SIZE, mapped.size);
275 if (mapped.size)
276 SdbpSetDWORDAttr(&attr_info[1], TAG_CHECKSUM, SdbpCalculateFileChecksum(&mapped));
277 else
278 SdbpSetAttrFail(&attr_info[1]);
279 module_type = SdbpGetModuleType(&mapped, &headers);
280
281 if (module_type != MODTYPE_UNKNOWN)
282 SdbpSetDWORDAttr(&attr_info[16], TAG_MODULE_TYPE, module_type);
283 else
284 SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE */
285
286 if (headers && module_type == MODTYPE_PE && ((PBYTE)(headers+1) <= mapping_end))
287 {
288 DWORD info_size;
289 ULONG export_dir_size;
290 PIMAGE_EXPORT_DIRECTORY export_dir;
291
292 info_size = GetFileVersionInfoSizeW(path, NULL);
293 if (info_size != 0)
294 {
295 UINT page_size = 0;
296 file_info = SdbAlloc(info_size);
297 GetFileVersionInfoW(path, 0, info_size, file_info);
298 VerQueryValueW(file_info, str_tinfo, (LPVOID)&lang_page, &page_size);
299 StringCchPrintfW(translation, ARRAYSIZE(translation), str_trans, lang_page->language, lang_page->code_page);
300 }
301
302 /* Handles 2, 3, 12, 13, 14, 15, 21, 22 */
303 SdbpReadFileVersion(attr_info, file_info);
304
305 SdbpSetStringAttr(&attr_info[4], TAG_PRODUCT_VERSION, SdbpGetStringAttr(translation, str_ProductVersion, file_info));
306 SdbpSetStringAttr(&attr_info[5], TAG_FILE_DESCRIPTION, SdbpGetStringAttr(translation, str_FileDescription, file_info));
307 SdbpSetStringAttr(&attr_info[6], TAG_COMPANY_NAME, SdbpGetStringAttr(translation, str_CompanyName, file_info));
308 SdbpSetStringAttr(&attr_info[7], TAG_PRODUCT_NAME, SdbpGetStringAttr(translation, str_ProductName, file_info));
309 SdbpSetStringAttr(&attr_info[8], TAG_FILE_VERSION, SdbpGetStringAttr(translation, str_FileVersion, file_info));
310 SdbpSetStringAttr(&attr_info[9], TAG_ORIGINAL_FILENAME, SdbpGetStringAttr(translation, str_OriginalFilename, file_info));
311 SdbpSetStringAttr(&attr_info[10], TAG_INTERNAL_NAME, SdbpGetStringAttr(translation, str_InternalName, file_info));
312 SdbpSetStringAttr(&attr_info[11], TAG_LEGAL_COPYRIGHT, SdbpGetStringAttr(translation, str_LegalCopyright, file_info));
313
314 /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx */
315
316 SdbpSetDWORDAttr(&attr_info[17], TAG_PE_CHECKSUM, headers->OptionalHeader.CheckSum);
317
318 SdbpSetDWORDAttr(&attr_info[18], TAG_LINKER_VERSION, /* mislabeled! */
319 ((DWORD)headers->OptionalHeader.MajorImageVersion) << 16 | headers->OptionalHeader.MinorImageVersion);
320 SdbpSetAttrFail(&attr_info[19]); /* TAG_16BIT_DESCRIPTION */
321 SdbpSetAttrFail(&attr_info[20]); /* TAG_16BIT_MODULE_NAME */
322
323 SdbpSetDWORDAttr(&attr_info[23], TAG_LINK_DATE, headers->FileHeader.TimeDateStamp);
324 SdbpSetDWORDAttr(&attr_info[24], TAG_UPTO_LINK_DATE, headers->FileHeader.TimeDateStamp);
325
326 export_dir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(mapped.view, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &export_dir_size);
327 if (export_dir && ((PBYTE)(export_dir+1) <= mapping_end))
328 {
329 PIMAGE_SECTION_HEADER section = NULL;
330 PBYTE export_name = RtlImageRvaToVa(headers, mapped.view, export_dir->Name, &section);
331 if (export_name)
332 SdbpSetStringAttrFromAnsiString(&attr_info[25], TAG_EXPORT_NAME, export_name, strlen((char*)export_name));
333 else
334 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
335 }
336 else
337 {
338 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
339 }
340
341 if (info_size)
342 SdbpSetDWORDAttr(&attr_info[26], TAG_VER_LANGUAGE, lang_page->language);
343
344 SdbpSetDWORDAttr(&attr_info[27], TAG_EXE_WRAPPER, 0); /* boolean */
345 }
346 else
347 {
348 int n;
349 for (n = 2; n < NUM_ATTRIBUTES; ++n)
350 {
351 if (n != 16 && n != 26)
352 SdbpSetAttrFail(&attr_info[n]);
353 }
354 if (module_type == MODTYPE_NE)
355 {
356 PBYTE ptr;
357 PIMAGE_OS2_HEADER os2 = (PIMAGE_OS2_HEADER)headers;
358 if ((PBYTE)(os2 + 1) <= mapping_end)
359 {
360 ptr = mapped.view + os2->ne_nrestab;
361 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
362 SdbpSetStringAttrFromPascalString(&attr_info[19], TAG_16BIT_DESCRIPTION, ptr);
363 ptr = (PBYTE)os2 + os2->ne_restab;
364 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
365 SdbpSetStringAttrFromPascalString(&attr_info[20], TAG_16BIT_MODULE_NAME, ptr);
366 }
367 }
368 }
369
370 *attr_info_ret = attr_info;
371 *attr_count = NUM_ATTRIBUTES; /* As far as I know, this one is always 28 */
372
373 SdbFree(file_info);
374 SdbpCloseMemMappedFile(&mapped);
375 return TRUE;
376 }