xref: /php-src/win32/winutil.c (revision 9e80947e)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Zeev Suraski <zeev@php.net>                                  |
14    *         Pierre Joye <pierre@php.net>                                 |
15    +----------------------------------------------------------------------+
16  */
17 
18 #include "php.h"
19 #include "winutil.h"
20 #include "codepage.h"
21 #include <bcrypt.h>
22 #include <lmcons.h>
23 
24 
php_win32_error_to_msg(HRESULT error)25 PHP_WINUTIL_API char *php_win32_error_to_msg(HRESULT error)
26 {/*{{{*/
27 	wchar_t *bufw = NULL, *pw;
28 	char *buf;
29 
30 	DWORD ret = FormatMessageW(
31 		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
32 		NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),	(LPWSTR)&bufw, 0, NULL
33 	);
34 
35 	if (!ret || !bufw) {
36 		return "";
37 	}
38 
39 	/* strip trailing line breaks and periods */
40 	for (pw = bufw + wcslen(bufw) - 1; pw >= bufw && (*pw == L'\r' || *pw == L'\n' || *pw == L'.'); pw--);
41 	pw[1] = L'\0';
42 
43 	buf = php_win32_cp_conv_w_to_any(bufw, ret, PHP_WIN32_CP_IGNORE_LEN_P);
44 
45 	LocalFree(bufw);
46 
47 	return (buf ? buf : "");
48 }/*}}}*/
49 
php_win32_error_msg_free(char * msg)50 PHP_WINUTIL_API void php_win32_error_msg_free(char *msg)
51 {/*{{{*/
52 	if (msg && msg[0]) {
53 		free(msg);
54 	}
55 }/*}}}*/
56 
php_win32_check_trailing_space(const char * path,const size_t path_len)57 int php_win32_check_trailing_space(const char * path, const size_t path_len)
58 {/*{{{*/
59 	if (path_len > MAXPATHLEN - 1) {
60 		return 1;
61 	}
62 	if (path) {
63 		if (path[0] == ' ' || path[path_len - 1] == ' ') {
64 			return 0;
65 		} else {
66 			return 1;
67 		}
68 	} else {
69 		return 0;
70 	}
71 }/*}}}*/
72 
73 static BCRYPT_ALG_HANDLE bcrypt_algo;
74 static BOOL has_bcrypt_algo = 0;
75 
76 #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
77 
78 #ifdef PHP_EXPORTS
php_win32_shutdown_random_bytes(void)79 BOOL php_win32_shutdown_random_bytes(void)
80 {/*{{{*/
81 	BOOL ret = TRUE;
82 
83 	if (has_bcrypt_algo) {
84 		ret = NT_SUCCESS(BCryptCloseAlgorithmProvider(bcrypt_algo, 0));
85 		has_bcrypt_algo = 0;
86 	}
87 
88 	return ret;
89 }/*}}}*/
90 
php_win32_init_random_bytes(void)91 BOOL php_win32_init_random_bytes(void)
92 {/*{{{*/
93 	if (has_bcrypt_algo) {
94 		return TRUE;
95 	}
96 
97 	has_bcrypt_algo = NT_SUCCESS(BCryptOpenAlgorithmProvider(&bcrypt_algo, BCRYPT_RNG_ALGORITHM, NULL, 0));
98 
99 	return has_bcrypt_algo;
100 }/*}}}*/
101 #endif
102 
php_win32_get_random_bytes(unsigned char * buf,size_t size)103 PHP_WINUTIL_API int php_win32_get_random_bytes(unsigned char *buf, size_t size)
104 {  /* {{{ */
105 
106 	BOOL ret;
107 
108 #if 0
109 	/* Currently we fail on startup, with CNG API it shows no regressions so far and is secure.
110 		Should switch on and try to reinit, if it fails too often on startup. This means also
111 		bringing locks back. */
112 	if (has_bcrypt_algo == 0) {
113 		return FAILURE;
114 	}
115 #endif
116 
117 	/* No sense to loop here, the limit is huge enough. */
118 	ret = NT_SUCCESS(BCryptGenRandom(bcrypt_algo, buf, (ULONG)size, 0));
119 
120 	return ret ? SUCCESS : FAILURE;
121 }
122 /* }}} */
123 
124 
125 /*
126 * This functions based on the code from the UNIXem project under
127 * the BSD like license. Modified for PHP by ab@php.net
128 *
129 * Home:    http://synesis.com.au/software/
130 *
131 * Copyright (c) 2005-2010, Matthew Wilson and Synesis Software
132 */
133 
php_win32_code_to_errno(unsigned long w32Err)134 PHP_WINUTIL_API int php_win32_code_to_errno(unsigned long w32Err)
135 {/*{{{*/
136     size_t  i;
137 
138     struct code_to_errno_map
139     {
140         unsigned long   w32Err;
141         int             eerrno;
142     };
143 
144     static const struct code_to_errno_map errmap[] =
145     {
146         /*   1 */       {   ERROR_INVALID_FUNCTION          ,   EINVAL          }
147         /*   2 */   ,   {   ERROR_FILE_NOT_FOUND            ,   ENOENT          }
148         /*   3 */   ,   {   ERROR_PATH_NOT_FOUND            ,   ENOENT          }
149         /*   4 */   ,   {   ERROR_TOO_MANY_OPEN_FILES       ,   EMFILE          }
150         /*   5 */   ,   {   ERROR_ACCESS_DENIED             ,   EACCES          }
151         /*   6 */   ,   {   ERROR_INVALID_HANDLE            ,   EBADF           }
152         /*   7 */   ,   {   ERROR_ARENA_TRASHED             ,   ENOMEM          }
153         /*   8 */   ,   {   ERROR_NOT_ENOUGH_MEMORY         ,   ENOMEM          }
154         /*   9 */   ,   {   ERROR_INVALID_BLOCK             ,   ENOMEM          }
155         /*  10 */   ,   {   ERROR_BAD_ENVIRONMENT           ,   E2BIG           }
156         /*  11 */   ,   {   ERROR_BAD_FORMAT                ,   ENOEXEC         }
157         /*  12 */   ,   {   ERROR_INVALID_ACCESS            ,   EINVAL          }
158         /*  13 */   ,   {   ERROR_INVALID_DATA              ,   EINVAL          }
159         /*  14 */   ,   {   ERROR_OUTOFMEMORY               ,   ENOMEM          }
160         /*  15 */   ,   {   ERROR_INVALID_DRIVE             ,   ENOENT          }
161         /*  16 */   ,   {   ERROR_CURRENT_DIRECTORY         ,   ECURDIR         }
162         /*  17 */   ,   {   ERROR_NOT_SAME_DEVICE           ,   EXDEV           }
163         /*  18 */   ,   {   ERROR_NO_MORE_FILES             ,   ENOENT          }
164         /*  19 */   ,   {   ERROR_WRITE_PROTECT             ,   EROFS           }
165         /*  20 */   ,   {   ERROR_BAD_UNIT                  ,   ENXIO           }
166         /*  21 */   ,   {   ERROR_NOT_READY                 ,   EBUSY           }
167         /*  22 */   ,   {   ERROR_BAD_COMMAND               ,   EIO             }
168         /*  23 */   ,   {   ERROR_CRC                       ,   EIO             }
169         /*  24 */   ,   {   ERROR_BAD_LENGTH                ,   EIO             }
170         /*  25 */   ,   {   ERROR_SEEK                      ,   EIO             }
171         /*  26 */   ,   {   ERROR_NOT_DOS_DISK              ,   EIO             }
172         /*  27 */   ,   {   ERROR_SECTOR_NOT_FOUND          ,   ENXIO           }
173         /*  28 */   ,   {   ERROR_OUT_OF_PAPER              ,   EBUSY           }
174         /*  29 */   ,   {   ERROR_WRITE_FAULT               ,   EIO             }
175         /*  30 */   ,   {   ERROR_READ_FAULT                ,   EIO             }
176         /*  31 */   ,   {   ERROR_GEN_FAILURE               ,   EIO             }
177         /*  32 */   ,   {   ERROR_SHARING_VIOLATION         ,   EAGAIN          }
178         /*  33 */   ,   {   ERROR_LOCK_VIOLATION            ,   EACCES          }
179         /*  34 */   ,   {   ERROR_WRONG_DISK                ,   ENXIO           }
180         /*  35 */   ,   {   35                              ,   ENFILE          }
181         /*  36 */   ,   {   ERROR_SHARING_BUFFER_EXCEEDED   ,   ENFILE          }
182         /*  37 */   ,   {   ERROR_HANDLE_EOF                ,   EINVAL          }
183         /*  38 */   ,   {   ERROR_HANDLE_DISK_FULL          ,   ENOSPC          }
184 #if 0
185         /*  39 */   ,   {   0                               ,   0               }
186         /*  40 */   ,   {   0                               ,   0               }
187         /*  41 */   ,   {   0                               ,   0               }
188         /*  42 */   ,   {   0                               ,   0               }
189         /*  43 */   ,   {   0                               ,   0               }
190         /*  44 */   ,   {   0                               ,   0               }
191         /*  45 */   ,   {   0                               ,   0               }
192         /*  46 */   ,   {   0                               ,   0               }
193         /*  47 */   ,   {   0                               ,   0               }
194         /*  48 */   ,   {   0                               ,   0               }
195         /*  49 */   ,   {   0                               ,   0               }
196 #endif
197         /*  50 */   ,   {   ERROR_NOT_SUPPORTED             ,   ENOSYS          }
198 #if 0
199         /*  51 */   ,   {   0                               ,   0               }
200         /*  52 */   ,   {   0                               ,   0               }
201 #endif
202         /*  53 */   ,   {   ERROR_BAD_NETPATH               ,   ENOENT          }
203 #if 0
204         /*  54 */   ,   {   0                               ,   0               }
205         /*  55 */   ,   {   0                               ,   0               }
206         /*  56 */   ,   {   0                               ,   0               }
207         /*  57 */   ,   {   0                               ,   0               }
208         /*  58 */   ,   {   0                               ,   0               }
209         /*  59 */   ,   {   0                               ,   0               }
210         /*  60 */   ,   {   0                               ,   0               }
211         /*  61 */   ,   {   0                               ,   0               }
212         /*  62 */   ,   {   0                               ,   0               }
213         /*  63 */   ,   {   0                               ,   0               }
214         /*  64 */   ,   {   0                               ,   0               }
215 #endif
216         /*  65 */   ,   {   ERROR_NETWORK_ACCESS_DENIED     ,   EACCES          }
217 #if 0
218         /*  66 */   ,   {   0                               ,   0               }
219 #endif
220         /*  67 */   ,   {   ERROR_BAD_NET_NAME              ,   ENOENT          }
221 #if 0
222         /*  68 */   ,   {   0                               ,   0               }
223         /*  69 */   ,   {   0                               ,   0               }
224         /*  70 */   ,   {   0                               ,   0               }
225         /*  71 */   ,   {   0                               ,   0               }
226         /*  72 */   ,   {   0                               ,   0               }
227         /*  73 */   ,   {   0                               ,   0               }
228         /*  74 */   ,   {   0                               ,   0               }
229         /*  75 */   ,   {   0                               ,   0               }
230         /*  76 */   ,   {   0                               ,   0               }
231         /*  77 */   ,   {   0                               ,   0               }
232         /*  78 */   ,   {   0                               ,   0               }
233         /*  79 */   ,   {   0                               ,   0               }
234 #endif
235         /*  80 */   ,   {   ERROR_FILE_EXISTS               ,   EEXIST          }
236 #if 0
237         /*  81 */   ,   {   0                               ,   0               }
238 #endif
239         /*  82 */   ,   {   ERROR_CANNOT_MAKE               ,   EACCES          }
240         /*  83 */   ,   {   ERROR_FAIL_I24                  ,   EACCES          }
241 #if 0
242         /*  84 */   ,   {   0                               ,   0               }
243         /*  85 */   ,   {   0                               ,   0               }
244         /*  86 */   ,   {   0                               ,   0               }
245 #endif
246         /*  87 */   ,   {   ERROR_INVALID_PARAMETER         ,   EINVAL          }
247 #if 0
248         /*  88 */   ,   {   0                               ,   0               }
249 #endif
250         /*  89 */   ,   {   ERROR_NO_PROC_SLOTS             ,   EAGAIN          }
251 #if 0
252         /*  90 */   ,   {   0                               ,   0               }
253         /*  91 */   ,   {   0                               ,   0               }
254         /*  92 */   ,   {   0                               ,   0               }
255         /*  93 */   ,   {   0                               ,   0               }
256         /*  94 */   ,   {   0                               ,   0               }
257         /*  95 */   ,   {   0                               ,   0               }
258         /*  96 */   ,   {   0                               ,   0               }
259         /*  97 */   ,   {   0                               ,   0               }
260         /*  98 */   ,   {   0                               ,   0               }
261         /*  99 */   ,   {   0                               ,   0               }
262         /* 100 */   ,   {   0                               ,   0               }
263         /* 101 */   ,   {   0                               ,   0               }
264         /* 102 */   ,   {   0                               ,   0               }
265         /* 103 */   ,   {   0                               ,   0               }
266         /* 104 */   ,   {   0                               ,   0               }
267         /* 105 */   ,   {   0                               ,   0               }
268         /* 106 */   ,   {   0                               ,   0               }
269         /* 107 */   ,   {   0                               ,   0               }
270 #endif
271         /* 108 */   ,   {   ERROR_DRIVE_LOCKED              ,   EACCES          }
272         /* 109 */   ,   {   ERROR_BROKEN_PIPE               ,   EPIPE           }
273 #if 0
274         /* 110 */   ,   {   0                               ,   0               }
275 #endif
276         /* 111 */   ,   {   ERROR_BUFFER_OVERFLOW           ,   ENAMETOOLONG    }
277         /* 112 */   ,   {   ERROR_DISK_FULL                 ,   ENOSPC          }
278 #if 0
279         /* 113 */   ,   {   0                               ,   0               }
280 #endif
281         /* 114 */   ,   {   ERROR_INVALID_TARGET_HANDLE     ,   EBADF           }
282 #if 0
283         /* 115 */   ,   {   0                               ,   0               }
284         /* 116 */   ,   {   0                               ,   0               }
285         /* 117 */   ,   {   0                               ,   0               }
286         /* 118 */   ,   {   0                               ,   0               }
287         /* 119 */   ,   {   0                               ,   0               }
288         /* 120 */   ,   {   0                               ,   0               }
289         /* 121 */   ,   {   0                               ,   0               }
290 #endif
291         /* 122 */   ,   {   ERROR_INSUFFICIENT_BUFFER       ,   ERANGE          }
292         /* 123 */   ,   {   ERROR_INVALID_NAME              ,   ENOENT          }
293         /* 124 */   ,   {   ERROR_INVALID_HANDLE            ,   EINVAL          }
294 #if 0
295         /* 125 */   ,   {   0                               ,   0               }
296 #endif
297         /* 126 */   ,   {   ERROR_MOD_NOT_FOUND             ,   ENOENT          }
298         /* 127 */   ,   {   ERROR_PROC_NOT_FOUND            ,   ENOENT          }
299         /* 128 */   ,   {   ERROR_WAIT_NO_CHILDREN          ,   ECHILD          }
300         /* 129 */   ,   {   ERROR_CHILD_NOT_COMPLETE        ,   ECHILD          }
301         /* 130 */   ,   {   ERROR_DIRECT_ACCESS_HANDLE      ,   EBADF           }
302         /* 131 */   ,   {   ERROR_NEGATIVE_SEEK             ,   EINVAL          }
303         /* 132 */   ,   {   ERROR_SEEK_ON_DEVICE            ,   EACCES          }
304 #if 0
305         /* 133 */   ,   {   0                               ,   0               }
306         /* 134 */   ,   {   0                               ,   0               }
307         /* 135 */   ,   {   0                               ,   0               }
308         /* 136 */   ,   {   0                               ,   0               }
309         /* 137 */   ,   {   0                               ,   0               }
310         /* 138 */   ,   {   0                               ,   0               }
311         /* 139 */   ,   {   0                               ,   0               }
312         /* 140 */   ,   {   0                               ,   0               }
313         /* 141 */   ,   {   0                               ,   0               }
314         /* 142 */   ,   {   0                               ,   0               }
315         /* 143 */   ,   {   0                               ,   0               }
316         /* 144 */   ,   {   0                               ,   0               }
317 #endif
318         /* 145 */   ,   {   ERROR_DIR_NOT_EMPTY             ,   ENOTEMPTY       }
319 #if 0
320         /* 146 */   ,   {   0                               ,   0               }
321         /* 147 */   ,   {   0                               ,   0               }
322         /* 148 */   ,   {   0                               ,   0               }
323         /* 149 */   ,   {   0                               ,   0               }
324         /* 150 */   ,   {   0                               ,   0               }
325         /* 151 */   ,   {   0                               ,   0               }
326         /* 152 */   ,   {   0                               ,   0               }
327         /* 153 */   ,   {   0                               ,   0               }
328         /* 154 */   ,   {   0                               ,   0               }
329         /* 155 */   ,   {   0                               ,   0               }
330         /* 156 */   ,   {   0                               ,   0               }
331         /* 157 */   ,   {   0                               ,   0               }
332 #endif
333         /* 158 */   ,   {   ERROR_NOT_LOCKED                ,   EACCES          }
334 #if 0
335         /* 159 */   ,   {   0                               ,   0               }
336         /* 160 */   ,   {   0                               ,   0               }
337 #endif
338         /* 161 */   ,   {   ERROR_BAD_PATHNAME              ,   ENOENT          }
339 #if 0
340         /* 162 */   ,   {   0                               ,   0               }
341         /* 163 */   ,   {   0                               ,   0               }
342 #endif
343         /* 164 */   ,   {   ERROR_MAX_THRDS_REACHED         ,   EAGAIN          }
344 #if 0
345         /* 165 */   ,   {   0                               ,   0               }
346         /* 166 */   ,   {   0                               ,   0               }
347 #endif
348         /* 167 */   ,   {   ERROR_LOCK_FAILED               ,   EACCES          }
349 #if 0
350         /* 168 */   ,   {   0                               ,   0               }
351         /* 169 */   ,   {   0                               ,   0               }
352         /* 170 */   ,   {   0                               ,   0               }
353         /* 171 */   ,   {   0                               ,   0               }
354         /* 172 */   ,   {   0                               ,   0               }
355         /* 173 */   ,   {   0                               ,   0               }
356         /* 174 */   ,   {   0                               ,   0               }
357         /* 175 */   ,   {   0                               ,   0               }
358         /* 176 */   ,   {   0                               ,   0               }
359         /* 177 */   ,   {   0                               ,   0               }
360         /* 178 */   ,   {   0                               ,   0               }
361         /* 179 */   ,   {   0                               ,   0               }
362         /* 180 */   ,   {   0                               ,   0               }
363         /* 181 */   ,   {   0                               ,   0               }
364         /* 182 */   ,   {   0                               ,   0               }
365 #endif
366         /* 183 */   ,   {   ERROR_ALREADY_EXISTS            ,   EEXIST          }
367 #if 0
368         /* 184 */   ,   {   0                               ,   0               }
369         /* 185 */   ,   {   0                               ,   0               }
370         /* 186 */   ,   {   0                               ,   0               }
371         /* 187 */   ,   {   0                               ,   0               }
372         /* 188 */   ,   {   0                               ,   0               }
373         /* 189 */   ,   {   0                               ,   0               }
374         /* 190 */   ,   {   0                               ,   0               }
375         /* 191 */   ,   {   0                               ,   0               }
376         /* 192 */   ,   {   0                               ,   0               }
377         /* 193 */   ,   {   0                               ,   0               }
378         /* 194 */   ,   {   0                               ,   0               }
379         /* 195 */   ,   {   0                               ,   0               }
380         /* 196 */   ,   {   0                               ,   0               }
381         /* 197 */   ,   {   0                               ,   0               }
382         /* 198 */   ,   {   0                               ,   0               }
383         /* 199 */   ,   {   0                               ,   0               }
384 #endif
385 
386         /* 206 */   ,   {   ERROR_FILENAME_EXCED_RANGE      ,   ENAMETOOLONG    }
387 
388         /* 215 */   ,   {   ERROR_NESTING_NOT_ALLOWED       ,   EAGAIN          }
389 		/* 258 */   ,   { WAIT_TIMEOUT, ETIME}
390 
391         /* 267 */   ,   {   ERROR_DIRECTORY                 ,   ENOTDIR         }
392 		/* 336 */   ,   {   ERROR_DIRECTORY_NOT_SUPPORTED   ,   EISDIR          }
393 
394         /* 996 */   ,   {   ERROR_IO_INCOMPLETE             ,   EAGAIN          }
395         /* 997 */   ,   {   ERROR_IO_PENDING                ,   EAGAIN          }
396 
397         /* 1004 */   ,  {   ERROR_INVALID_FLAGS             ,   EINVAL          }
398         /* 1113 */   ,  {   ERROR_NO_UNICODE_TRANSLATION    ,   EINVAL          }
399         /* 1168 */   ,  {   ERROR_NOT_FOUND                 ,   ENOENT          }
400         /* 1224 */   ,  {   ERROR_USER_MAPPED_FILE          ,   EACCES          }
401         /* 1314 */   ,  {   ERROR_PRIVILEGE_NOT_HELD        ,   EACCES          }
402         /* 1816 */  ,   {   ERROR_NOT_ENOUGH_QUOTA          ,   ENOMEM          }
403 					,   {   ERROR_ABANDONED_WAIT_0          ,   EIO }
404 		/* 1464 */	,	{	ERROR_SYMLINK_NOT_SUPPORTED		,	EINVAL			}
405 		/* 4390 */	,	{	ERROR_NOT_A_REPARSE_POINT		,	EINVAL			}
406     };
407 
408     for(i = 0; i < sizeof(errmap)/sizeof(struct code_to_errno_map); ++i)
409     {
410         if(w32Err == errmap[i].w32Err)
411         {
412             return errmap[i].eerrno;
413         }
414     }
415 
416     assert(!"Unrecognised value");
417 
418     return EINVAL;
419 }/*}}}*/
420 
php_win32_get_username(void)421 PHP_WINUTIL_API char *php_win32_get_username(void)
422 {/*{{{*/
423 	wchar_t unamew[UNLEN + 1];
424 	size_t uname_len;
425 	char *uname;
426 	DWORD unsize = UNLEN;
427 
428 	GetUserNameW(unamew, &unsize);
429 	uname = php_win32_cp_conv_w_to_any(unamew, unsize - 1, &uname_len);
430 	if (!uname) {
431 		return NULL;
432 	}
433 
434 	/* Ensure the length doesn't overflow. */
435 	if (uname_len > UNLEN) {
436 		uname[uname_len] = '\0';
437 	}
438 
439 	return uname;
440 }/*}}}*/
441 
is_compatible(HMODULE handle,BOOL is_smaller,char * format,char ** err)442 static zend_always_inline BOOL is_compatible(HMODULE handle, BOOL is_smaller, char *format, char **err)
443 {/*{{{*/
444 	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) handle;
445 	PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((char *) dosHeader + dosHeader->e_lfanew);
446 
447 	DWORD major = pNTHeader->OptionalHeader.MajorLinkerVersion;
448 	DWORD minor = pNTHeader->OptionalHeader.MinorLinkerVersion;
449 
450 #if PHP_LINKER_MAJOR == 14
451 	/* VS 2015, 2017, 2019 and 2022 are binary compatible, but only forward compatible.
452 		It should be fine, if we load a module linked with an older one into
453 		the core linked with the newer one, but not the otherway round.
454 		Analogously, it should be fine, if a PHP build linked with an older version
455 		is used with a newer CRT, but not the other way round.
456 		Otherwise, if the linker major version is not same, it is an error, as
457 		per the current knowledge.
458 
459 		This check is to be extended as new VS versions come out. */
460 	DWORD core_minor = (DWORD)(PHP_LINKER_MINOR/10);
461 	DWORD comp_minor = (DWORD)(minor/10);
462 	if (14 == major && (is_smaller ? core_minor < comp_minor : core_minor > comp_minor) || PHP_LINKER_MAJOR != major)
463 #else
464 	if (PHP_LINKER_MAJOR != major)
465 #endif
466 	{
467 		char buf[MAX_PATH];
468 		if (GetModuleFileName(handle, buf, sizeof(buf)) != 0) {
469 			spprintf(err, 0, format, buf, major, minor, PHP_LINKER_MAJOR, PHP_LINKER_MINOR);
470 		} else {
471 			spprintf(err, 0, "Can't retrieve the module name (error %u)", GetLastError());
472 		}
473 		return FALSE;
474 	}
475 
476 	return TRUE;
477 }/*}}}*/
478 
php_win32_image_compatible(HMODULE handle,char ** err)479 PHP_WINUTIL_API BOOL php_win32_image_compatible(HMODULE handle, char **err)
480 {/*{{{*/
481 	return is_compatible(handle, TRUE, "Can't load module '%s' as it's linked with %u.%u, but the core is linked with %d.%d", err);
482 }/*}}}*/
483 
484 /* Expect a CRT module handle */
php_win32_crt_compatible(char ** err)485 PHP_WINUTIL_API BOOL php_win32_crt_compatible(char **err)
486 {/*{{{*/
487 #if PHP_LINKER_MAJOR == 14
488 	/* Extend for other CRT if needed. */
489 # if PHP_DEBUG
490 	const char *crt_name = "vcruntime140d.dll";
491 # else
492 	const char *crt_name = "vcruntime140.dll";
493 # endif
494 	HMODULE handle = GetModuleHandle(crt_name);
495 	if (handle == NULL) {
496 		spprintf(err, 0, "Can't get handle of module %s (error %u)", crt_name, GetLastError());
497 		return FALSE;
498 	}
499 	return is_compatible(handle, FALSE, "'%s' %u.%u is not compatible with this PHP build linked with %d.%d", err);
500 #endif
501 	return TRUE;
502 }/*}}}*/
503