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