1ebf2fee053716efc5c2a5d6211ef7dd7d8a985d
[reactos.git] / reactos / lib / kernel32 / file / bintype.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/bintype.c
6 * PURPOSE: Binary detection functions
7 * PROGRAMMER: Alexandre Julliard (WINE)
8 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
9 * UPDATE HISTORY:
10 * 02/05/2004 - Ported/Adapted from WINE
11 */
12
13 /* INCLUDES *****************************************************************/
14
15 #include <k32.h>
16
17 #define NDEBUG
18 #include "../include/debug.h"
19
20 /* FUNCTIONS ****************************************************************/
21
22 /* Check whether a file is an OS/2 or a very old Windows executable
23 * by testing on import of KERNEL.
24 *
25 * FIXME: is reading the module imports the only way of discerning
26 * old Windows binaries from OS/2 ones ? At least it seems so...
27 */
28 static DWORD STDCALL
29 InternalIsOS2OrOldWin(HANDLE hFile, IMAGE_DOS_HEADER *mz, IMAGE_OS2_HEADER *ne)
30 {
31 DWORD CurPos;
32 LPWORD modtab = NULL;
33 LPSTR nametab = NULL;
34 DWORD Read, Ret;
35 int i;
36
37 Ret = BINARY_OS216;
38 CurPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
39
40 /* read modref table */
41 if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_modtab, NULL, FILE_BEGIN) == (DWORD)-1) ||
42 (!(modtab = HeapAlloc(GetProcessHeap(), 0, ne->ne_cmod * sizeof(WORD)))) ||
43 (!(ReadFile(hFile, modtab, ne->ne_cmod * sizeof(WORD), &Read, NULL))) ||
44 (Read != (DWORD)ne->ne_cmod * sizeof(WORD)))
45 {
46 goto broken;
47 }
48
49 /* read imported names table */
50 if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_imptab, NULL, FILE_BEGIN) == (DWORD)-1) ||
51 (!(nametab = HeapAlloc(GetProcessHeap(), 0, ne->ne_enttab - ne->ne_imptab))) ||
52 (!(ReadFile(hFile, nametab, ne->ne_enttab - ne->ne_imptab, &Read, NULL))) ||
53 (Read != (DWORD)ne->ne_enttab - ne->ne_imptab))
54 {
55 goto broken;
56 }
57
58 for(i = 0; i < ne->ne_cmod; i++)
59 {
60 LPSTR module;
61 module = &nametab[modtab[i]];
62 if(!strncmp(&module[1], "KERNEL", module[0]))
63 {
64 /* very old windows file */
65 Ret = BINARY_WIN16;
66 goto done;
67 }
68 }
69
70 broken:
71 DPRINT("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
72
73 done:
74 HeapFree(GetProcessHeap(), 0, modtab);
75 HeapFree(GetProcessHeap(), 0, nametab);
76 SetFilePointer(hFile, CurPos, NULL, FILE_BEGIN);
77 return Ret;
78 }
79
80 static DWORD STDCALL
81 InternalGetBinaryType(HANDLE hFile)
82 {
83 union
84 {
85 struct
86 {
87 unsigned char magic[4];
88 unsigned char ignored[12];
89 unsigned short type;
90 } elf;
91 struct
92 {
93 unsigned long magic;
94 unsigned long cputype;
95 unsigned long cpusubtype;
96 unsigned long filetype;
97 } macho;
98 IMAGE_DOS_HEADER mz;
99 } Header;
100 char magic[4];
101 DWORD Read;
102
103 if((SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == (DWORD)-1) ||
104 (!ReadFile(hFile, &Header, sizeof(Header), &Read, NULL) ||
105 (Read != sizeof(Header))))
106 {
107 return BINARY_UNKNOWN;
108 }
109
110 if(!memcmp(Header.elf.magic, "\177ELF", sizeof(Header.elf.magic)))
111 {
112 /* FIXME: we don't bother to check byte order, architecture, etc. */
113 switch(Header.elf.type)
114 {
115 case 2:
116 return BINARY_UNIX_EXE;
117 case 3:
118 return BINARY_UNIX_LIB;
119 }
120 return BINARY_UNKNOWN;
121 }
122
123 /* Mach-o File with Endian set to Big Endian or Little Endian*/
124 if(Header.macho.magic == 0xFEEDFACE ||
125 Header.macho.magic == 0xECAFDEEF)
126 {
127 switch(Header.macho.filetype)
128 {
129 case 0x8:
130 /* MH_BUNDLE */
131 return BINARY_UNIX_LIB;
132 }
133 return BINARY_UNKNOWN;
134 }
135
136 /* Not ELF, try DOS */
137 if(Header.mz.e_magic == IMAGE_DOS_SIGNATURE)
138 {
139 /* We do have a DOS image so we will now try to seek into
140 * the file by the amount indicated by the field
141 * "Offset to extended header" and read in the
142 * "magic" field information at that location.
143 * This will tell us if there is more header information
144 * to read or not.
145 */
146 if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == (DWORD)-1) ||
147 (!ReadFile(hFile, magic, sizeof(magic), &Read, NULL) ||
148 (Read != sizeof(magic))))
149 {
150 return BINARY_DOS;
151 }
152
153 /* Reading the magic field succeeded so
154 * we will try to determine what type it is.
155 */
156 if(!memcmp(magic, "PE\0\0", sizeof(magic)))
157 {
158 IMAGE_FILE_HEADER FileHeader;
159 if(!ReadFile(hFile, &FileHeader, sizeof(IMAGE_FILE_HEADER), &Read, NULL) ||
160 (Read == sizeof(IMAGE_FILE_HEADER)))
161 {
162 return BINARY_DOS;
163 }
164
165 /* FIXME - detect 32/64 bit */
166
167 if(FileHeader.Characteristics & IMAGE_FILE_DLL)
168 return BINARY_PE_DLL32;
169 return BINARY_PE_EXE32;
170 }
171
172 if(!memcmp(magic, "NE", 1))
173 {
174 /* This is a Windows executable (NE) header. This can
175 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
176 * DOS program (running under a DOS extender). To decide
177 * which, we'll have to read the NE header.
178 */
179 IMAGE_OS2_HEADER ne;
180 if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == 1) ||
181 !ReadFile(hFile, &ne, sizeof(IMAGE_OS2_HEADER), &Read, NULL) ||
182 (Read == sizeof(IMAGE_OS2_HEADER)))
183 {
184 /* Couldn't read header, so abort. */
185 return BINARY_DOS;
186 }
187
188 switch(ne.ne_exetyp)
189 {
190 case 2:
191 return BINARY_WIN16;
192 case 5:
193 return BINARY_DOS;
194 default:
195 return InternalIsOS2OrOldWin(hFile, &Header.mz, &ne);
196 }
197 }
198 return BINARY_DOS;
199 }
200 return BINARY_UNKNOWN;
201 }
202
203 /*
204 * @implemented
205 */
206 BOOL
207 STDCALL
208 GetBinaryTypeW (
209 LPCWSTR lpApplicationName,
210 LPDWORD lpBinaryType
211 )
212 {
213 HANDLE hFile;
214 DWORD BinType;
215
216 if(!lpApplicationName || !lpBinaryType)
217 {
218 SetLastError(ERROR_INVALID_PARAMETER);
219 return FALSE;
220 }
221
222 hFile = CreateFileW(lpApplicationName, GENERIC_READ, FILE_SHARE_READ, NULL,
223 OPEN_EXISTING, 0, 0);
224 if(hFile == INVALID_HANDLE_VALUE)
225 {
226 return FALSE;
227 }
228
229 BinType = InternalGetBinaryType(hFile);
230 CloseHandle(hFile);
231
232 switch(BinType)
233 {
234 case BINARY_UNKNOWN:
235 {
236 WCHAR *dot;
237
238 /*
239 * guess from filename
240 */
241 if(!(dot = wcsrchr(lpApplicationName, L'.')))
242 {
243 return FALSE;
244 }
245 if(!lstrcmpiW(dot, L".COM"))
246 {
247 *lpBinaryType = SCS_DOS_BINARY;
248 return TRUE;
249 }
250 if(!lstrcmpiW(dot, L".PIF"))
251 {
252 *lpBinaryType = SCS_PIF_BINARY;
253 return TRUE;
254 }
255 return FALSE;
256 }
257 case BINARY_PE_EXE32:
258 case BINARY_PE_DLL32:
259 {
260 *lpBinaryType = SCS_32BIT_BINARY;
261 return TRUE;
262 }
263 case BINARY_PE_EXE64:
264 case BINARY_PE_DLL64:
265 {
266 *lpBinaryType = SCS_64BIT_BINARY;
267 return TRUE;
268 }
269 case BINARY_WIN16:
270 {
271 *lpBinaryType = SCS_WOW_BINARY;
272 return TRUE;
273 }
274 case BINARY_OS216:
275 {
276 *lpBinaryType = SCS_OS216_BINARY;
277 return TRUE;
278 }
279 case BINARY_DOS:
280 {
281 *lpBinaryType = SCS_DOS_BINARY;
282 return TRUE;
283 }
284 case BINARY_UNIX_EXE:
285 case BINARY_UNIX_LIB:
286 {
287 return FALSE;
288 }
289 }
290
291 DPRINT1("Invalid binary type returned!\n", BinType);
292 return FALSE;
293 }
294
295
296 /*
297 * @implemented
298 */
299 BOOL
300 STDCALL
301 GetBinaryTypeA (
302 LPCSTR lpApplicationName,
303 LPDWORD lpBinaryType
304 )
305 {
306 PWCHAR ApplicationNameW;
307
308 if(!lpApplicationName || !lpBinaryType)
309 {
310 SetLastError(ERROR_INVALID_PARAMETER);
311 return FALSE;
312 }
313
314 if (!(ApplicationNameW = FilenameA2W(lpApplicationName, FALSE)))
315 return FALSE;
316
317 return GetBinaryTypeW(ApplicationNameW, lpBinaryType);
318 }
319
320 /* EOF */