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