xref: /openbsd/gnu/usr.bin/perl/win32/win32.c (revision e0680481)
1 /* WIN32.C
2  *
3  * (c) 1995 Microsoft Corporation. All rights reserved.
4  * 		Developed by hip communications inc.
5  * Portions (c) 1993 Intergraph Corporation. All rights reserved.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10 #define PERLIO_NOT_STDIO 0
11 #define WIN32_LEAN_AND_MEAN
12 #define WIN32IO_IS_STDIO
13 /* for CreateSymbolicLinkA() etc */
14 #define _WIN32_WINNT 0x0601
15 #include <tchar.h>
16 
17 #ifdef __GNUC__
18 #  define Win32_Winsock
19 #endif
20 
21 #include <windows.h>
22 
23 #ifndef HWND_MESSAGE
24 #  define HWND_MESSAGE ((HWND)-3)
25 #endif
26 
27 #ifndef PROCESSOR_ARCHITECTURE_AMD64
28 #  define PROCESSOR_ARCHITECTURE_AMD64 9
29 #endif
30 
31 #ifndef WC_NO_BEST_FIT_CHARS
32 #  define WC_NO_BEST_FIT_CHARS 0x00000400
33 #endif
34 
35 #include <winnt.h>
36 #include <commctrl.h>
37 #include <tlhelp32.h>
38 #include <io.h>
39 #include <signal.h>
40 #include <winioctl.h>
41 #include <winternl.h>
42 
43 /* #include "config.h" */
44 
45 
46 #define PerlIO FILE
47 
48 #include <sys/stat.h>
49 #include "EXTERN.h"
50 #include "perl.h"
51 
52 #define NO_XSLOCKS
53 #define PERL_NO_GET_CONTEXT
54 #include "XSUB.h"
55 
56 #include <fcntl.h>
57 #ifndef __GNUC__
58 /* assert.h conflicts with #define of assert in perl.h */
59 #  include <assert.h>
60 #endif
61 
62 #include <string.h>
63 #include <stdarg.h>
64 #include <float.h>
65 #include <time.h>
66 #include <sys/utime.h>
67 #include <wchar.h>
68 
69 #ifdef __GNUC__
70 /* Mingw32 defaults to globing command line
71  * So we turn it off like this:
72  */
73 int _CRT_glob = 0;
74 #endif
75 
76 #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1)
77 /* Mingw32-1.1 is missing some prototypes */
78 START_EXTERN_C
79 FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode);
80 FILE * _wfdopen(int nFd, LPCWSTR wszMode);
81 FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream);
82 int _flushall();
83 int _fcloseall();
84 END_EXTERN_C
85 #endif
86 
87 #define EXECF_EXEC 1
88 #define EXECF_SPAWN 2
89 #define EXECF_SPAWN_NOWAIT 3
90 
91 #if defined(PERL_IMPLICIT_SYS)
92 #  undef getlogin
93 #  define getlogin g_getlogin
94 #endif
95 
96 #ifdef _MSC_VER
97 #  define SET_INVALID_PARAMETER_HANDLER
98 #endif
99 
100 #ifdef SET_INVALID_PARAMETER_HANDLER
101 static BOOL	set_silent_invalid_parameter_handler(BOOL newvalue);
102 static void	my_invalid_parameter_handler(const wchar_t* expression,
103                         const wchar_t* function, const wchar_t* file,
104                         unsigned int line, uintptr_t pReserved);
105 #endif
106 
107 #ifndef WIN32_NO_REGISTRY
108 static char*	get_regstr_from(HKEY hkey, const char *valuename, SV **svp);
109 static char*	get_regstr(const char *valuename, SV **svp);
110 #endif
111 
112 static char*	get_emd_part(SV **prev_pathp, STRLEN *const len,
113                         const char *trailing, ...);
114 static char*	win32_get_xlib(const char *pl,
115                         WIN32_NO_REGISTRY_M_(const char *xlib)
116                         const char *libname, STRLEN *const len);
117 
118 static BOOL	has_shell_metachars(const char *ptr);
119 static long	tokenize(const char *str, char **dest, char ***destv);
120 static int	get_shell(void);
121 static char*	find_next_space(const char *s);
122 static int	do_spawn2(pTHX_ const char *cmd, int exectype);
123 static int	do_spawn2_handles(pTHX_ const char *cmd, int exectype,
124                         const int *handles);
125 static int	do_spawnvp_handles(int mode, const char *cmdname,
126                         const char * const *argv, const int *handles);
127 static PerlIO * do_popen(const char *mode, const char *command, IV narg,
128                          SV **args);
129 static long	find_pid(pTHX_ int pid);
130 static void	remove_dead_process(long child);
131 static int	terminate_process(DWORD pid, HANDLE process_handle, int sig);
132 static int	my_killpg(int pid, int sig);
133 static int	my_kill(int pid, int sig);
134 static void	out_of_memory(void);
135 static char*	wstr_to_str(const wchar_t* wstr);
136 static long	filetime_to_clock(PFILETIME ft);
137 static BOOL	filetime_from_time(PFILETIME ft, time_t t);
138 static char*	create_command_line(char *cname, STRLEN clen,
139                                     const char * const *args);
140 static char*	qualified_path(const char *cmd, bool other_exts);
141 static void	ansify_path(void);
142 static LRESULT	win32_process_message(HWND hwnd, UINT msg,
143                         WPARAM wParam, LPARAM lParam);
144 
145 #ifdef USE_ITHREADS
146 static long	find_pseudo_pid(pTHX_ int pid);
147 static void	remove_dead_pseudo_process(long child);
148 static HWND	get_hwnd_delay(pTHX, long child, DWORD tries);
149 #endif
150 
151 #ifdef HAVE_INTERP_INTERN
152 static void	win32_csighandler(int sig);
153 #endif
154 
155 static void translate_to_errno(void);
156 
157 START_EXTERN_C
158 HANDLE	w32_perldll_handle = INVALID_HANDLE_VALUE;
159 char	w32_module_name[MAX_PATH+1];
160 END_EXTERN_C
161 
162 static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""};
163 
164 #ifndef WIN32_NO_REGISTRY
165 /* initialized by Perl_win32_init/PERL_SYS_INIT */
166 static HKEY HKCU_Perl_hnd;
167 static HKEY HKLM_Perl_hnd;
168 #endif
169 
170 /* the time_t epoch start time as a filetime expressed as a large integer */
171 static ULARGE_INTEGER time_t_epoch_base_filetime;
172 
173 static const SYSTEMTIME time_t_epoch_base_systemtime = {
174     1970,    /* wYear         */
175     1,       /* wMonth        */
176     0,       /* wDayOfWeek    */
177     1,       /* wDay          */
178     0,       /* wHour         */
179     0,       /* wMinute       */
180     0,       /* wSecond       */
181     0        /* wMilliseconds */
182 };
183 
184 #define FILETIME_CHUNKS_PER_SECOND (10000000UL)
185 
186 #ifdef USE_ITHREADS
187 static perl_mutex win32_read_console_mutex;
188 #endif
189 
190 #ifdef SET_INVALID_PARAMETER_HANDLER
191 static BOOL silent_invalid_parameter_handler = FALSE;
192 
193 static BOOL
set_silent_invalid_parameter_handler(BOOL newvalue)194 set_silent_invalid_parameter_handler(BOOL newvalue)
195 {
196     BOOL oldvalue = silent_invalid_parameter_handler;
197 #  ifdef _DEBUG
198     silent_invalid_parameter_handler = newvalue;
199 #  endif
200     return oldvalue;
201 }
202 
203 static void
my_invalid_parameter_handler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)204 my_invalid_parameter_handler(const wchar_t* expression,
205     const wchar_t* function,
206     const wchar_t* file,
207     unsigned int line,
208     uintptr_t pReserved)
209 {
210 #  ifdef _DEBUG
211     char* ansi_expression;
212     char* ansi_function;
213     char* ansi_file;
214     if (silent_invalid_parameter_handler)
215         return;
216     ansi_expression = wstr_to_str(expression);
217     ansi_function = wstr_to_str(function);
218     ansi_file = wstr_to_str(file);
219     fprintf(stderr, "Invalid parameter detected in function %s. "
220                     "File: %s, line: %d\n", ansi_function, ansi_file, line);
221     fprintf(stderr, "Expression: %s\n", ansi_expression);
222     free(ansi_expression);
223     free(ansi_function);
224     free(ansi_file);
225 #  endif
226 }
227 #endif
228 
229 EXTERN_C void
set_w32_module_name(void)230 set_w32_module_name(void)
231 {
232     /* this function may be called at DLL_PROCESS_ATTACH time */
233     char* ptr;
234     HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
235                                ? GetModuleHandle(NULL)
236                                : w32_perldll_handle);
237 
238     WCHAR modulename[MAX_PATH];
239     WCHAR fullname[MAX_PATH];
240     char *ansi;
241 
242     DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) =
243         (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD))
244         GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW");
245 
246     GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR));
247 
248     /* Make sure we get an absolute pathname in case the module was loaded
249      * explicitly by LoadLibrary() with a relative path. */
250     GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL);
251 
252     /* Make sure we start with the long path name of the module because we
253      * later scan for pathname components to match "5.xx" to locate
254      * compatible sitelib directories, and the short pathname might mangle
255      * this path segment (e.g. by removing the dot on NTFS to something
256      * like "5xx~1.yy") */
257     if (pfnGetLongPathNameW)
258         pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR));
259 
260     /* remove \\?\ prefix */
261     if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0)
262         memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR));
263 
264     ansi = win32_ansipath(fullname);
265     my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name));
266     win32_free(ansi);
267 
268     /* normalize to forward slashes */
269     ptr = w32_module_name;
270     while (*ptr) {
271         if (*ptr == '\\')
272             *ptr = '/';
273         ++ptr;
274     }
275 }
276 
277 #ifndef WIN32_NO_REGISTRY
278 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
279 static char*
get_regstr_from(HKEY handle,const char * valuename,SV ** svp)280 get_regstr_from(HKEY handle, const char *valuename, SV **svp)
281 {
282     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
283     DWORD type;
284     char *str = NULL;
285     long retval;
286     DWORD datalen;
287 
288     retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
289     if (retval == ERROR_SUCCESS
290         && (type == REG_SZ || type == REG_EXPAND_SZ))
291     {
292         dTHX;
293         if (!*svp)
294             *svp = sv_2mortal(newSVpvs(""));
295         SvGROW(*svp, datalen);
296         retval = RegQueryValueEx(handle, valuename, 0, NULL,
297                                  (PBYTE)SvPVX(*svp), &datalen);
298         if (retval == ERROR_SUCCESS) {
299             str = SvPVX(*svp);
300             SvCUR_set(*svp,datalen-1);
301         }
302     }
303     return str;
304 }
305 
306 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
307 static char*
get_regstr(const char * valuename,SV ** svp)308 get_regstr(const char *valuename, SV **svp)
309 {
310     char *str;
311     if (HKCU_Perl_hnd) {
312         str = get_regstr_from(HKCU_Perl_hnd, valuename, svp);
313         if (!str)
314             goto try_HKLM;
315     }
316     else {
317         try_HKLM:
318         if (HKLM_Perl_hnd)
319             str = get_regstr_from(HKLM_Perl_hnd, valuename, svp);
320         else
321             str = NULL;
322     }
323     return str;
324 }
325 #endif /* ifndef WIN32_NO_REGISTRY */
326 
327 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
328 static char *
get_emd_part(SV ** prev_pathp,STRLEN * const len,const char * trailing_path,...)329 get_emd_part(SV **prev_pathp, STRLEN *const len, const char *trailing_path, ...)
330 {
331     char base[10];
332     va_list ap;
333     char mod_name[MAX_PATH+1];
334     char *ptr;
335     char *optr;
336     char *strip;
337     STRLEN baselen;
338 
339     va_start(ap, trailing_path);
340     strip = va_arg(ap, char *);
341 
342     sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
343     baselen = strlen(base);
344 
345     if (!*w32_module_name) {
346         set_w32_module_name();
347     }
348     strcpy(mod_name, w32_module_name);
349     ptr = strrchr(mod_name, '/');
350     while (ptr && strip) {
351         /* look for directories to skip back */
352         optr = ptr;
353         *ptr = '\0';
354         ptr = strrchr(mod_name, '/');
355         /* avoid stripping component if there is no slash,
356          * or it doesn't match ... */
357         if (!ptr || stricmp(ptr+1, strip) != 0) {
358             /* ... but not if component matches m|5\.$patchlevel.*| */
359             if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
360                           && strnEQ(strip, base, baselen)
361                           && strnEQ(ptr+1, base, baselen)))
362             {
363                 *optr = '/';
364                 ptr = optr;
365             }
366         }
367         strip = va_arg(ap, char *);
368     }
369     if (!ptr) {
370         ptr = mod_name;
371         *ptr++ = '.';
372         *ptr = '/';
373     }
374     va_end(ap);
375     strcpy(++ptr, trailing_path);
376 
377     /* only add directory if it exists */
378     if (GetFileAttributes(mod_name) != (DWORD) -1) {
379         /* directory exists */
380         dTHX;
381         if (!*prev_pathp)
382             *prev_pathp = sv_2mortal(newSVpvs(""));
383         else if (SvPVX(*prev_pathp))
384             sv_catpvs(*prev_pathp, ";");
385         sv_catpv(*prev_pathp, mod_name);
386         if(len)
387             *len = SvCUR(*prev_pathp);
388         return SvPVX(*prev_pathp);
389     }
390 
391     return NULL;
392 }
393 
394 EXTERN_C char *
win32_get_privlib(WIN32_NO_REGISTRY_M_ (const char * pl)STRLEN * const len)395 win32_get_privlib(WIN32_NO_REGISTRY_M_(const char *pl) STRLEN *const len)
396 {
397     const char *stdlib = "lib";
398     SV *sv = NULL;
399 #ifndef WIN32_NO_REGISTRY
400     char buffer[MAX_PATH+1];
401 
402     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
403     sprintf(buffer, "%s-%s", stdlib, pl);
404     if (!get_regstr(buffer, &sv))
405         (void)get_regstr(stdlib, &sv);
406 #endif
407 
408     /* $stdlib .= ";$EMD/../../lib" */
409     return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL);
410 }
411 
412 static char *
win32_get_xlib(const char * pl,WIN32_NO_REGISTRY_M_ (const char * xlib)const char * libname,STRLEN * const len)413 win32_get_xlib(const char *pl, WIN32_NO_REGISTRY_M_(const char *xlib)
414                const char *libname, STRLEN *const len)
415 {
416 #ifndef WIN32_NO_REGISTRY
417     char regstr[40];
418 #endif
419     char pathstr[MAX_PATH+1];
420     SV *sv1 = NULL;
421     SV *sv2 = NULL;
422 
423 #ifndef WIN32_NO_REGISTRY
424     /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
425     sprintf(regstr, "%s-%s", xlib, pl);
426     (void)get_regstr(regstr, &sv1);
427 #endif
428 
429     /* $xlib .=
430      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
431     sprintf(pathstr, "%s/%s/lib", libname, pl);
432     (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
433 
434 #ifndef WIN32_NO_REGISTRY
435     /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
436     (void)get_regstr(xlib, &sv2);
437 #endif
438 
439     /* $xlib .=
440      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
441     sprintf(pathstr, "%s/lib", libname);
442     (void)get_emd_part(&sv2, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
443 
444     if (!sv1 && !sv2)
445         return NULL;
446     if (!sv1) {
447         sv1 = sv2;
448     } else if (sv2) {
449         dTHX;
450         sv_catpvs(sv1, ";");
451         sv_catsv(sv1, sv2);
452     }
453 
454     if (len)
455         *len = SvCUR(sv1);
456     return SvPVX(sv1);
457 }
458 
459 EXTERN_C char *
win32_get_sitelib(const char * pl,STRLEN * const len)460 win32_get_sitelib(const char *pl, STRLEN *const len)
461 {
462     return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("sitelib") "site", len);
463 }
464 
465 #ifndef PERL_VENDORLIB_NAME
466 #  define PERL_VENDORLIB_NAME	"vendor"
467 #endif
468 
469 EXTERN_C char *
win32_get_vendorlib(const char * pl,STRLEN * const len)470 win32_get_vendorlib(const char *pl, STRLEN *const len)
471 {
472     return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("vendorlib") PERL_VENDORLIB_NAME, len);
473 }
474 
475 static BOOL
has_shell_metachars(const char * ptr)476 has_shell_metachars(const char *ptr)
477 {
478     int inquote = 0;
479     char quote = '\0';
480 
481     /*
482      * Scan string looking for redirection (< or >) or pipe
483      * characters (|) that are not in a quoted string.
484      * Shell variable interpolation (%VAR%) can also happen inside strings.
485      */
486     while (*ptr) {
487         switch(*ptr) {
488         case '%':
489             return TRUE;
490         case '\'':
491         case '\"':
492             if (inquote) {
493                 if (quote == *ptr) {
494                     inquote = 0;
495                     quote = '\0';
496                 }
497             }
498             else {
499                 quote = *ptr;
500                 inquote++;
501             }
502             break;
503         case '>':
504         case '<':
505         case '|':
506             if (!inquote)
507                 return TRUE;
508         default:
509             break;
510         }
511         ++ptr;
512     }
513     return FALSE;
514 }
515 
516 #if !defined(PERL_IMPLICIT_SYS)
517 /* since the current process environment is being updated in util.c
518  * the library functions will get the correct environment
519  */
520 PerlIO *
Perl_my_popen(pTHX_ const char * cmd,const char * mode)521 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
522 {
523     PERL_FLUSHALL_FOR_CHILD;
524     return win32_popen(cmd, mode);
525 }
526 
527 long
Perl_my_pclose(pTHX_ PerlIO * fp)528 Perl_my_pclose(pTHX_ PerlIO *fp)
529 {
530     return win32_pclose(fp);
531 }
532 #endif
533 
534 DllExport unsigned long
win32_os_id(void)535 win32_os_id(void)
536 {
537     return (unsigned long)g_osver.dwPlatformId;
538 }
539 
540 DllExport int
win32_getpid(void)541 win32_getpid(void)
542 {
543 #ifdef USE_ITHREADS
544     dTHX;
545     if (w32_pseudo_id)
546         return -((int)w32_pseudo_id);
547 #endif
548     return _getpid();
549 }
550 
551 /* Tokenize a string.  Words are null-separated, and the list
552  * ends with a doubled null.  Any character (except null and
553  * including backslash) may be escaped by preceding it with a
554  * backslash (the backslash will be stripped).
555  * Returns number of words in result buffer.
556  */
557 static long
tokenize(const char * str,char ** dest,char *** destv)558 tokenize(const char *str, char **dest, char ***destv)
559 {
560     char *retstart = NULL;
561     char **retvstart = 0;
562     int items = -1;
563     if (str) {
564         int slen = strlen(str);
565         char *ret;
566         char **retv;
567         Newx(ret, slen+2, char);
568         Newx(retv, (slen+3)/2, char*);
569 
570         retstart = ret;
571         retvstart = retv;
572         *retv = ret;
573         items = 0;
574         while (*str) {
575             *ret = *str++;
576             if (*ret == '\\' && *str)
577                 *ret = *str++;
578             else if (*ret == ' ') {
579                 while (*str == ' ')
580                     str++;
581                 if (ret == retstart)
582                     ret--;
583                 else {
584                     *ret = '\0';
585                     ++items;
586                     if (*str)
587                         *++retv = ret+1;
588                 }
589             }
590             else if (!*str)
591                 ++items;
592             ret++;
593         }
594         retvstart[items] = NULL;
595         *ret++ = '\0';
596         *ret = '\0';
597     }
598     *dest = retstart;
599     *destv = retvstart;
600     return items;
601 }
602 
603 static const char
604 cmd_opts[] = "/x/d/c";
605 
606 static const char
607 shell_cmd[] = "cmd.exe";
608 
609 static int
get_shell(void)610 get_shell(void)
611 {
612     dTHX;
613     if (!w32_perlshell_tokens) {
614         /* we don't use COMSPEC here for two reasons:
615          *  1. the same reason perl on UNIX doesn't use SHELL--rampant and
616          *     uncontrolled unportability of the ensuing scripts.
617          *  2. PERL5SHELL could be set to a shell that may not be fit for
618          *     interactive use (which is what most programs look in COMSPEC
619          *     for).
620          */
621         const char *shell = PerlEnv_getenv("PERL5SHELL");
622         if (shell) {
623             w32_perlshell_items = tokenize(shell,
624                                            &w32_perlshell_tokens,
625                                            &w32_perlshell_vec);
626         }
627         else {
628             /* tokenize does some Unix-ish like things like
629                \\ escaping that don't work well here
630             */
631             char shellbuf[MAX_PATH];
632             UINT len = GetSystemDirectoryA(shellbuf, sizeof(shellbuf));
633             if (len == 0) {
634                 translate_to_errno();
635                 return -1;
636             }
637             else if (len >= MAX_PATH) {
638                 /* buffer too small */
639                 errno = E2BIG;
640                 return -1;
641             }
642             if (shellbuf[len-1] != '\\') {
643                 my_strlcat(shellbuf, "\\", sizeof(shellbuf));
644                 ++len;
645             }
646             if (len + sizeof(shell_cmd) > sizeof(shellbuf)) {
647                 errno = E2BIG;
648                 return -1;
649             }
650             my_strlcat(shellbuf, shell_cmd, sizeof(shellbuf));
651             len += sizeof(shell_cmd)-1;
652 
653             Newx(w32_perlshell_vec, 3, char *);
654             Newx(w32_perlshell_tokens, len + 1 + sizeof(cmd_opts), char);
655 
656             my_strlcpy(w32_perlshell_tokens, shellbuf, len+1);
657             my_strlcpy(w32_perlshell_tokens + len +1, cmd_opts,
658                        sizeof(cmd_opts));
659 
660             w32_perlshell_vec[0] = w32_perlshell_tokens;
661             w32_perlshell_vec[1] = w32_perlshell_tokens + len + 1;
662             w32_perlshell_vec[2] = NULL;
663 
664             w32_perlshell_items = 2;
665         }
666     }
667     return 0;
668 }
669 
670 int
Perl_do_aspawn(pTHX_ SV * really,SV ** mark,SV ** sp)671 Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
672 {
673     const char **argv;
674     char *str;
675     int status;
676     int flag = P_WAIT;
677     int index = 0;
678     int eno;
679 
680     PERL_ARGS_ASSERT_DO_ASPAWN;
681 
682     if (sp <= mark)
683         return -1;
684 
685     if (get_shell() < 0)
686         return -1;
687 
688     Newx(argv, (sp - mark) + w32_perlshell_items + 2, const char*);
689 
690     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
691         ++mark;
692         flag = SvIVx(*mark);
693     }
694 
695     while (++mark <= sp) {
696         if (*mark && (str = SvPV_nolen(*mark)))
697             argv[index++] = str;
698         else
699             argv[index++] = "";
700     }
701     argv[index++] = 0;
702 
703     status = win32_spawnvp(flag,
704                            (const char*)(really ? SvPV_nolen(really) : argv[0]),
705                            (const char* const*)argv);
706 
707     if (status < 0 && (eno = errno, (eno == ENOEXEC || eno == ENOENT))) {
708         /* possible shell-builtin, invoke with shell */
709         int sh_items;
710         sh_items = w32_perlshell_items;
711         while (--index >= 0)
712             argv[index+sh_items] = argv[index];
713         while (--sh_items >= 0)
714             argv[sh_items] = w32_perlshell_vec[sh_items];
715 
716         status = win32_spawnvp(flag,
717                                (const char*)(really ? SvPV_nolen(really) : argv[0]),
718                                (const char* const*)argv);
719     }
720 
721     if (flag == P_NOWAIT) {
722         PL_statusvalue = -1;	/* >16bits hint for pp_system() */
723     }
724     else {
725         if (status < 0) {
726             if (ckWARN(WARN_EXEC))
727                 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't spawn \"%s\": %s", argv[0], strerror(errno));
728             status = 255 * 256;
729         }
730         else
731             status *= 256;
732         PL_statusvalue = status;
733     }
734     Safefree(argv);
735     return (status);
736 }
737 
738 /* returns pointer to the next unquoted space or the end of the string */
739 static char*
find_next_space(const char * s)740 find_next_space(const char *s)
741 {
742     bool in_quotes = FALSE;
743     while (*s) {
744         /* ignore doubled backslashes, or backslash+quote */
745         if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
746             s += 2;
747         }
748         /* keep track of when we're within quotes */
749         else if (*s == '"') {
750             s++;
751             in_quotes = !in_quotes;
752         }
753         /* break it up only at spaces that aren't in quotes */
754         else if (!in_quotes && isSPACE(*s))
755             return (char*)s;
756         else
757             s++;
758     }
759     return (char*)s;
760 }
761 
762 static int
do_spawn2(pTHX_ const char * cmd,int exectype)763 do_spawn2(pTHX_ const char *cmd, int exectype) {
764     return do_spawn2_handles(aTHX_ cmd, exectype, NULL);
765 }
766 
767 static int
do_spawn2_handles(pTHX_ const char * cmd,int exectype,const int * handles)768 do_spawn2_handles(pTHX_ const char *cmd, int exectype, const int *handles)
769 {
770     char **a;
771     char *s;
772     char **argv;
773     int status = -1;
774     BOOL needToTry = TRUE;
775     char *cmd2;
776 
777     /* Save an extra exec if possible. See if there are shell
778      * metacharacters in it */
779     if (!has_shell_metachars(cmd)) {
780         Newx(argv, strlen(cmd) / 2 + 2, char*);
781         Newx(cmd2, strlen(cmd) + 1, char);
782         strcpy(cmd2, cmd);
783         a = argv;
784         for (s = cmd2; *s;) {
785             while (*s && isSPACE(*s))
786                 s++;
787             if (*s)
788                 *(a++) = s;
789             s = find_next_space(s);
790             if (*s)
791                 *s++ = '\0';
792         }
793         *a = NULL;
794         if (argv[0]) {
795             switch (exectype) {
796             case EXECF_SPAWN:
797                 status = win32_spawnvp(P_WAIT, argv[0],
798                                        (const char* const*)argv);
799                 break;
800             case EXECF_SPAWN_NOWAIT:
801                 status = do_spawnvp_handles(P_NOWAIT, argv[0],
802                                             (const char* const*)argv, handles);
803                 break;
804             case EXECF_EXEC:
805                 status = win32_execvp(argv[0], (const char* const*)argv);
806                 break;
807             }
808             if (status != -1 || errno == 0)
809                 needToTry = FALSE;
810         }
811         Safefree(argv);
812         Safefree(cmd2);
813     }
814     if (needToTry) {
815         char **argv;
816         int i = -1;
817         if (get_shell() < 0)
818             return -1;
819         Newx(argv, w32_perlshell_items + 2, char*);
820         while (++i < w32_perlshell_items)
821             argv[i] = w32_perlshell_vec[i];
822         argv[i++] = (char *)cmd;
823         argv[i] = NULL;
824         switch (exectype) {
825         case EXECF_SPAWN:
826             status = win32_spawnvp(P_WAIT, argv[0],
827                                    (const char* const*)argv);
828             break;
829         case EXECF_SPAWN_NOWAIT:
830             status = do_spawnvp_handles(P_NOWAIT, argv[0],
831                                         (const char* const*)argv, handles);
832             break;
833         case EXECF_EXEC:
834             status = win32_execvp(argv[0], (const char* const*)argv);
835             break;
836         }
837         cmd = argv[0];
838         Safefree(argv);
839     }
840     if (exectype == EXECF_SPAWN_NOWAIT) {
841         PL_statusvalue = -1;	/* >16bits hint for pp_system() */
842     }
843     else {
844         if (status < 0) {
845             if (ckWARN(WARN_EXEC))
846                 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
847                      (exectype == EXECF_EXEC ? "exec" : "spawn"),
848                      cmd, strerror(errno));
849             status = 255 * 256;
850         }
851         else
852             status *= 256;
853         PL_statusvalue = status;
854     }
855     return (status);
856 }
857 
858 int
Perl_do_spawn(pTHX_ char * cmd)859 Perl_do_spawn(pTHX_ char *cmd)
860 {
861     PERL_ARGS_ASSERT_DO_SPAWN;
862 
863     return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
864 }
865 
866 int
Perl_do_spawn_nowait(pTHX_ char * cmd)867 Perl_do_spawn_nowait(pTHX_ char *cmd)
868 {
869     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
870 
871     return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
872 }
873 
874 bool
Perl_do_exec(pTHX_ const char * cmd)875 Perl_do_exec(pTHX_ const char *cmd)
876 {
877     PERL_ARGS_ASSERT_DO_EXEC;
878 
879     do_spawn2(aTHX_ cmd, EXECF_EXEC);
880     return FALSE;
881 }
882 
883 /* The idea here is to read all the directory names into a string table
884  * (separated by nulls) and when one of the other dir functions is called
885  * return the pointer to the current file name.
886  */
887 DllExport DIR *
win32_opendir(const char * filename)888 win32_opendir(const char *filename)
889 {
890     dTHXa(NULL);
891     DIR			*dirp;
892     long		len;
893     long		idx;
894     char		scanname[MAX_PATH+3];
895     WCHAR		wscanname[sizeof(scanname)];
896     WIN32_FIND_DATAW	wFindData;
897     char		buffer[MAX_PATH*2];
898     BOOL		use_default;
899 
900     len = strlen(filename);
901     if (len == 0) {
902         errno = ENOENT;
903         return NULL;
904     }
905     if (len > MAX_PATH) {
906         errno = ENAMETOOLONG;
907         return NULL;
908     }
909 
910     /* Get us a DIR structure */
911     Newxz(dirp, 1, DIR);
912 
913     /* Create the search pattern */
914     strcpy(scanname, filename);
915 
916     /* bare drive name means look in cwd for drive */
917     if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
918         scanname[len++] = '.';
919         scanname[len++] = '/';
920     }
921     else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
922         scanname[len++] = '/';
923     }
924     scanname[len++] = '*';
925     scanname[len] = '\0';
926 
927     /* do the FindFirstFile call */
928     MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR));
929     aTHXa(PERL_GET_THX);
930     dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData);
931 
932     if (dirp->handle == INVALID_HANDLE_VALUE) {
933         DWORD err = GetLastError();
934         /* FindFirstFile() fails on empty drives! */
935         switch (err) {
936         case ERROR_FILE_NOT_FOUND:
937             return dirp;
938         case ERROR_NO_MORE_FILES:
939         case ERROR_PATH_NOT_FOUND:
940             errno = ENOENT;
941             break;
942         case ERROR_NOT_ENOUGH_MEMORY:
943             errno = ENOMEM;
944             break;
945         default:
946             errno = EINVAL;
947             break;
948         }
949         Safefree(dirp);
950         return NULL;
951     }
952 
953     use_default = FALSE;
954     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
955                         wFindData.cFileName, -1,
956                         buffer, sizeof(buffer), NULL, &use_default);
957     if (use_default && *wFindData.cAlternateFileName) {
958         WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
959                             wFindData.cAlternateFileName, -1,
960                             buffer, sizeof(buffer), NULL, NULL);
961     }
962 
963     /* now allocate the first part of the string table for
964      * the filenames that we find.
965      */
966     idx = strlen(buffer)+1;
967     if (idx < 256)
968         dirp->size = 256;
969     else
970         dirp->size = idx;
971     Newx(dirp->start, dirp->size, char);
972     strcpy(dirp->start, buffer);
973     dirp->nfiles++;
974     dirp->end = dirp->curr = dirp->start;
975     dirp->end += idx;
976     return dirp;
977 }
978 
979 
980 /* Readdir just returns the current string pointer and bumps the
981  * string pointer to the nDllExport entry.
982  */
983 DllExport struct direct *
win32_readdir(DIR * dirp)984 win32_readdir(DIR *dirp)
985 {
986     long         len;
987 
988     if (dirp->curr) {
989         /* first set up the structure to return */
990         len = strlen(dirp->curr);
991         strcpy(dirp->dirstr.d_name, dirp->curr);
992         dirp->dirstr.d_namlen = len;
993 
994         /* Fake an inode */
995         dirp->dirstr.d_ino = dirp->curr - dirp->start;
996 
997         /* Now set up for the next call to readdir */
998         dirp->curr += len + 1;
999         if (dirp->curr >= dirp->end) {
1000             BOOL res;
1001             char buffer[MAX_PATH*2];
1002 
1003             if (dirp->handle == INVALID_HANDLE_VALUE) {
1004                 res = 0;
1005             }
1006             /* finding the next file that matches the wildcard
1007              * (which should be all of them in this directory!).
1008              */
1009             else {
1010                 WIN32_FIND_DATAW wFindData;
1011                 res = FindNextFileW(dirp->handle, &wFindData);
1012                 if (res) {
1013                     BOOL use_default = FALSE;
1014                     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1015                                         wFindData.cFileName, -1,
1016                                         buffer, sizeof(buffer), NULL, &use_default);
1017                     if (use_default && *wFindData.cAlternateFileName) {
1018                         WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1019                                             wFindData.cAlternateFileName, -1,
1020                                             buffer, sizeof(buffer), NULL, NULL);
1021                     }
1022                 }
1023             }
1024             if (res) {
1025                 long endpos = dirp->end - dirp->start;
1026                 long newsize = endpos + strlen(buffer) + 1;
1027                 /* bump the string table size by enough for the
1028                  * new name and its null terminator */
1029                 while (newsize > dirp->size) {
1030                     long curpos = dirp->curr - dirp->start;
1031                     Renew(dirp->start, dirp->size * 2, char);
1032                     dirp->size *= 2;
1033                     dirp->curr = dirp->start + curpos;
1034                 }
1035                 strcpy(dirp->start + endpos, buffer);
1036                 dirp->end = dirp->start + newsize;
1037                 dirp->nfiles++;
1038             }
1039             else {
1040                 dirp->curr = NULL;
1041                 if (dirp->handle != INVALID_HANDLE_VALUE) {
1042                     FindClose(dirp->handle);
1043                     dirp->handle = INVALID_HANDLE_VALUE;
1044                 }
1045             }
1046         }
1047         return &(dirp->dirstr);
1048     }
1049     else
1050         return NULL;
1051 }
1052 
1053 /* Telldir returns the current string pointer position */
1054 DllExport long
win32_telldir(DIR * dirp)1055 win32_telldir(DIR *dirp)
1056 {
1057     return dirp->curr ? (dirp->curr - dirp->start) : -1;
1058 }
1059 
1060 
1061 /* Seekdir moves the string pointer to a previously saved position
1062  * (returned by telldir).
1063  */
1064 DllExport void
win32_seekdir(DIR * dirp,long loc)1065 win32_seekdir(DIR *dirp, long loc)
1066 {
1067     /* Ensure dirp->curr remains within `dirp->start` buffer. */
1068     if (loc >= 0 && dirp->end - dirp->start > (ptrdiff_t) loc) {
1069         dirp->curr = dirp->start + loc;
1070     } else {
1071         dirp->curr = NULL;
1072     }
1073 }
1074 
1075 /* Rewinddir resets the string pointer to the start */
1076 DllExport void
win32_rewinddir(DIR * dirp)1077 win32_rewinddir(DIR *dirp)
1078 {
1079     dirp->curr = dirp->start;
1080 }
1081 
1082 /* free the memory allocated by opendir */
1083 DllExport int
win32_closedir(DIR * dirp)1084 win32_closedir(DIR *dirp)
1085 {
1086     if (dirp->handle != INVALID_HANDLE_VALUE)
1087         FindClose(dirp->handle);
1088     Safefree(dirp->start);
1089     Safefree(dirp);
1090     return 1;
1091 }
1092 
1093 /* duplicate a open DIR* for interpreter cloning */
1094 DllExport DIR *
win32_dirp_dup(DIR * const dirp,CLONE_PARAMS * const param)1095 win32_dirp_dup(DIR *const dirp, CLONE_PARAMS *const param)
1096 {
1097     PerlInterpreter *const from = param->proto_perl;
1098     PerlInterpreter *const to   = (PerlInterpreter *)PERL_GET_THX;
1099 
1100     long pos;
1101     DIR *dup;
1102 
1103     /* switch back to original interpreter because win32_readdir()
1104      * might Renew(dirp->start).
1105      */
1106     if (from != to) {
1107         PERL_SET_THX(from);
1108     }
1109 
1110     /* mark current position; read all remaining entries into the
1111      * cache, and then restore to current position.
1112      */
1113     pos = win32_telldir(dirp);
1114     while (win32_readdir(dirp)) {
1115         /* read all entries into cache */
1116     }
1117     win32_seekdir(dirp, pos);
1118 
1119     /* switch back to new interpreter to allocate new DIR structure */
1120     if (from != to) {
1121         PERL_SET_THX(to);
1122     }
1123 
1124     Newx(dup, 1, DIR);
1125     memcpy(dup, dirp, sizeof(DIR));
1126 
1127     Newx(dup->start, dirp->size, char);
1128     memcpy(dup->start, dirp->start, dirp->size);
1129 
1130     dup->end = dup->start + (dirp->end - dirp->start);
1131     if (dirp->curr)
1132         dup->curr = dup->start + (dirp->curr - dirp->start);
1133 
1134     return dup;
1135 }
1136 
1137 /*
1138  * various stubs
1139  */
1140 
1141 
1142 /* Ownership
1143  *
1144  * Just pretend that everyone is a superuser. NT will let us know if
1145  * we don\'t really have permission to do something.
1146  */
1147 
1148 #define ROOT_UID    ((uid_t)0)
1149 #define ROOT_GID    ((gid_t)0)
1150 
1151 uid_t
getuid(void)1152 getuid(void)
1153 {
1154     return ROOT_UID;
1155 }
1156 
1157 uid_t
geteuid(void)1158 geteuid(void)
1159 {
1160     return ROOT_UID;
1161 }
1162 
1163 gid_t
getgid(void)1164 getgid(void)
1165 {
1166     return ROOT_GID;
1167 }
1168 
1169 gid_t
getegid(void)1170 getegid(void)
1171 {
1172     return ROOT_GID;
1173 }
1174 
1175 int
setuid(uid_t auid)1176 setuid(uid_t auid)
1177 {
1178     return (auid == ROOT_UID ? 0 : -1);
1179 }
1180 
1181 int
setgid(gid_t agid)1182 setgid(gid_t agid)
1183 {
1184     return (agid == ROOT_GID ? 0 : -1);
1185 }
1186 
1187 EXTERN_C char *
getlogin(void)1188 getlogin(void)
1189 {
1190     dTHX;
1191     char *buf = w32_getlogin_buffer;
1192     DWORD size = sizeof(w32_getlogin_buffer);
1193     if (GetUserName(buf,&size))
1194         return buf;
1195     return (char*)NULL;
1196 }
1197 
1198 int
chown(const char * path,uid_t owner,gid_t group)1199 chown(const char *path, uid_t owner, gid_t group)
1200 {
1201     /* XXX noop */
1202     return 0;
1203 }
1204 
1205 /*
1206  * XXX this needs strengthening  (for PerlIO)
1207  *   -- BKS, 11-11-200
1208 */
1209 #if((!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4) && \
1210     (!defined(__MINGW32_MAJOR_VERSION) || __MINGW32_MAJOR_VERSION < 3 || \
1211      (__MINGW32_MAJOR_VERSION == 3 && __MINGW32_MINOR_VERSION < 21)))
mkstemp(const char * path)1212 int mkstemp(const char *path)
1213 {
1214     dTHX;
1215     char buf[MAX_PATH+1];
1216     int i = 0, fd = -1;
1217 
1218 retry:
1219     if (i++ > 10) { /* give up */
1220         errno = ENOENT;
1221         return -1;
1222     }
1223     if (!GetTempFileNameA((LPCSTR)path, "plr", 1, buf)) {
1224         errno = ENOENT;
1225         return -1;
1226     }
1227     fd = PerlLIO_open3(buf, O_CREAT|O_RDWR|O_EXCL, 0600);
1228     if (fd == -1)
1229         goto retry;
1230     return fd;
1231 }
1232 #endif
1233 
1234 static long
find_pid(pTHX_ int pid)1235 find_pid(pTHX_ int pid)
1236 {
1237     long child = w32_num_children;
1238     while (--child >= 0) {
1239         if ((int)w32_child_pids[child] == pid)
1240             return child;
1241     }
1242     return -1;
1243 }
1244 
1245 static void
remove_dead_process(long child)1246 remove_dead_process(long child)
1247 {
1248     if (child >= 0) {
1249         dTHX;
1250         CloseHandle(w32_child_handles[child]);
1251         Move(&w32_child_handles[child+1], &w32_child_handles[child],
1252              (w32_num_children-child-1), HANDLE);
1253         Move(&w32_child_pids[child+1], &w32_child_pids[child],
1254              (w32_num_children-child-1), DWORD);
1255         w32_num_children--;
1256     }
1257 }
1258 
1259 #ifdef USE_ITHREADS
1260 static long
find_pseudo_pid(pTHX_ int pid)1261 find_pseudo_pid(pTHX_ int pid)
1262 {
1263     long child = w32_num_pseudo_children;
1264     while (--child >= 0) {
1265         if ((int)w32_pseudo_child_pids[child] == pid)
1266             return child;
1267     }
1268     return -1;
1269 }
1270 
1271 static void
remove_dead_pseudo_process(long child)1272 remove_dead_pseudo_process(long child)
1273 {
1274     if (child >= 0) {
1275         dTHX;
1276         CloseHandle(w32_pseudo_child_handles[child]);
1277         Move(&w32_pseudo_child_handles[child+1], &w32_pseudo_child_handles[child],
1278              (w32_num_pseudo_children-child-1), HANDLE);
1279         Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child],
1280              (w32_num_pseudo_children-child-1), DWORD);
1281         Move(&w32_pseudo_child_message_hwnds[child+1], &w32_pseudo_child_message_hwnds[child],
1282              (w32_num_pseudo_children-child-1), HWND);
1283         Move(&w32_pseudo_child_sigterm[child+1], &w32_pseudo_child_sigterm[child],
1284              (w32_num_pseudo_children-child-1), char);
1285         w32_num_pseudo_children--;
1286     }
1287 }
1288 
1289 void
win32_wait_for_children(pTHX)1290 win32_wait_for_children(pTHX)
1291 {
1292     if (w32_pseudo_children && w32_num_pseudo_children) {
1293         long child = 0;
1294         long count = 0;
1295         HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1296 
1297         for (child = 0; child < w32_num_pseudo_children; ++child) {
1298             if (!w32_pseudo_child_sigterm[child])
1299                 handles[count++] = w32_pseudo_child_handles[child];
1300         }
1301         /* XXX should use MsgWaitForMultipleObjects() to continue
1302          * XXX processing messages while we wait.
1303          */
1304         WaitForMultipleObjects(count, handles, TRUE, INFINITE);
1305 
1306         while (w32_num_pseudo_children)
1307             CloseHandle(w32_pseudo_child_handles[--w32_num_pseudo_children]);
1308     }
1309 }
1310 #endif
1311 
1312 static int
terminate_process(DWORD pid,HANDLE process_handle,int sig)1313 terminate_process(DWORD pid, HANDLE process_handle, int sig)
1314 {
1315     switch(sig) {
1316     case 0:
1317         /* "Does process exist?" use of kill */
1318         return 1;
1319     case 2:
1320         if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid))
1321             return 1;
1322         break;
1323     case SIGBREAK:
1324     case SIGTERM:
1325         if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid))
1326             return 1;
1327         break;
1328     default: /* For now be backwards compatible with perl 5.6 */
1329     case 9:
1330         /* Note that we will only be able to kill processes owned by the
1331          * current process owner, even when we are running as an administrator.
1332          * To kill processes of other owners we would need to set the
1333          * 'SeDebugPrivilege' privilege before obtaining the process handle.
1334          */
1335         if (TerminateProcess(process_handle, sig))
1336             return 1;
1337         break;
1338     }
1339     return 0;
1340 }
1341 
1342 /* returns number of processes killed */
1343 static int
my_killpg(int pid,int sig)1344 my_killpg(int pid, int sig)
1345 {
1346     HANDLE process_handle;
1347     HANDLE snapshot_handle;
1348     int killed = 0;
1349 
1350     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1351     if (process_handle == NULL)
1352         return 0;
1353 
1354     killed += terminate_process(pid, process_handle, sig);
1355 
1356     snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
1357     if (snapshot_handle != INVALID_HANDLE_VALUE) {
1358         PROCESSENTRY32 entry;
1359 
1360         entry.dwSize = sizeof(entry);
1361         if (Process32First(snapshot_handle, &entry)) {
1362             do {
1363                 if (entry.th32ParentProcessID == (DWORD)pid)
1364                     killed += my_killpg(entry.th32ProcessID, sig);
1365                 entry.dwSize = sizeof(entry);
1366             }
1367             while (Process32Next(snapshot_handle, &entry));
1368         }
1369         CloseHandle(snapshot_handle);
1370     }
1371     CloseHandle(process_handle);
1372     return killed;
1373 }
1374 
1375 /* returns number of processes killed */
1376 static int
my_kill(int pid,int sig)1377 my_kill(int pid, int sig)
1378 {
1379     int retval = 0;
1380     HANDLE process_handle;
1381 
1382     if (sig < 0)
1383         return my_killpg(pid, -sig);
1384 
1385     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1386     /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */
1387     if (process_handle != NULL) {
1388         retval = terminate_process(pid, process_handle, sig);
1389         CloseHandle(process_handle);
1390     }
1391     return retval;
1392 }
1393 
1394 #ifdef USE_ITHREADS
1395 /* Get a child pseudo-process HWND, with retrying and delaying/yielding.
1396  * The "tries" parameter is the number of retries to make, with a Sleep(1)
1397  * (waiting and yielding the time slot) between each try. Specifying 0 causes
1398  * only Sleep(0) (no waiting and potentially no yielding) to be used, so is not
1399  * recommended
1400  * Returns an hwnd != INVALID_HANDLE_VALUE (so be aware that NULL can be
1401  * returned) or croaks if the child pseudo-process doesn't schedule and deliver
1402  * a HWND in the time period allowed.
1403  */
1404 static HWND
get_hwnd_delay(pTHX,long child,DWORD tries)1405 get_hwnd_delay(pTHX, long child, DWORD tries)
1406 {
1407     HWND hwnd = w32_pseudo_child_message_hwnds[child];
1408     if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1409 
1410     /* Pseudo-process has not yet properly initialized since hwnd isn't set.
1411      * Fast sleep: On some NT kernels/systems, a Sleep(0) won't deschedule a
1412      * thread 100% of the time since threads are attached to a CPU for NUMA and
1413      * caching reasons, and the child thread was attached to a different CPU
1414      * therefore there is no workload on that CPU and Sleep(0) returns control
1415      * without yielding the time slot.
1416      * https://github.com/Perl/perl5/issues/11267
1417      */
1418     Sleep(0);
1419     win32_async_check(aTHX);
1420     hwnd = w32_pseudo_child_message_hwnds[child];
1421     if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1422 
1423     {
1424         unsigned int count = 0;
1425         /* No Sleep(1) if tries==0, just fail instead if we get this far. */
1426         while (count++ < tries) {
1427             Sleep(1);
1428             win32_async_check(aTHX);
1429             hwnd = w32_pseudo_child_message_hwnds[child];
1430             if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1431         }
1432     }
1433 
1434     Perl_croak(aTHX_ "panic: child pseudo-process was never scheduled");
1435 }
1436 #endif
1437 
1438 DllExport int
win32_kill(int pid,int sig)1439 win32_kill(int pid, int sig)
1440 {
1441     dTHX;
1442     long child;
1443 #ifdef USE_ITHREADS
1444     if (pid < 0) {
1445         /* it is a pseudo-forked child */
1446         child = find_pseudo_pid(aTHX_ -pid);
1447         if (child >= 0) {
1448             HANDLE hProcess = w32_pseudo_child_handles[child];
1449             switch (sig) {
1450                 case 0:
1451                     /* "Does process exist?" use of kill */
1452                     return 0;
1453 
1454                 case 9: {
1455                     /* kill -9 style un-graceful exit */
1456                     /* Do a wait to make sure child starts and isn't in DLL
1457                      * Loader Lock */
1458                     HWND hwnd = get_hwnd_delay(aTHX, child, 5);
1459                     if (TerminateThread(hProcess, sig)) {
1460                         /* Allow the scheduler to finish cleaning up the other
1461                          * thread.
1462                          * Otherwise, if we ExitProcess() before another context
1463                          * switch happens we will end up with a process exit
1464                          * code of "sig" instead of our own exit status.
1465                          * https://rt.cpan.org/Ticket/Display.html?id=66016#txn-908976
1466                          */
1467                         Sleep(0);
1468                         remove_dead_pseudo_process(child);
1469                         return 0;
1470                     }
1471                     break;
1472                 }
1473 
1474                 default: {
1475                     HWND hwnd = get_hwnd_delay(aTHX, child, 5);
1476                     /* We fake signals to pseudo-processes using Win32
1477                      * message queue. */
1478                     if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) ||
1479                         PostThreadMessage(-pid, WM_USER_KILL, sig, 0))
1480                     {
1481                         /* Don't wait for child process to terminate after we send a
1482                          * SIGTERM because the child may be blocked in a system call
1483                          * and never receive the signal.
1484                          */
1485                         if (sig == SIGTERM) {
1486                             Sleep(0);
1487                             w32_pseudo_child_sigterm[child] = 1;
1488                         }
1489                         /* It might be us ... */
1490                         PERL_ASYNC_CHECK();
1491                         return 0;
1492                     }
1493                     break;
1494                 }
1495             } /* switch */
1496         }
1497     }
1498     else
1499 #endif
1500     {
1501         child = find_pid(aTHX_ pid);
1502         if (child >= 0) {
1503             if (my_kill(pid, sig)) {
1504                 DWORD exitcode = 0;
1505                 if (GetExitCodeProcess(w32_child_handles[child], &exitcode) &&
1506                     exitcode != STILL_ACTIVE)
1507                 {
1508                     remove_dead_process(child);
1509                 }
1510                 return 0;
1511             }
1512         }
1513         else {
1514             if (my_kill(pid, sig))
1515                 return 0;
1516         }
1517     }
1518     errno = EINVAL;
1519     return -1;
1520 }
1521 
1522 PERL_STATIC_INLINE
1523 time_t
translate_ft_to_time_t(FILETIME ft)1524 translate_ft_to_time_t(FILETIME ft) {
1525     SYSTEMTIME st;
1526     struct tm pt;
1527     time_t retval;
1528     dTHX;
1529 
1530     if (!FileTimeToSystemTime(&ft, &st))
1531         return -1;
1532 
1533     Zero(&pt, 1, struct tm);
1534     pt.tm_year = st.wYear - 1900;
1535     pt.tm_mon = st.wMonth - 1;
1536     pt.tm_mday = st.wDay;
1537     pt.tm_hour = st.wHour;
1538     pt.tm_min = st.wMinute;
1539     pt.tm_sec = st.wSecond;
1540 
1541     MKTIME_LOCK;
1542     retval = _mkgmtime(&pt);
1543     MKTIME_UNLOCK;
1544 
1545     return retval;
1546 }
1547 
1548 typedef DWORD (__stdcall *pGetFinalPathNameByHandleA_t)(HANDLE, LPSTR, DWORD, DWORD);
1549 
1550 /* Adapted from:
1551 
1552 https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
1553 
1554 Renamed to avoid conflicts, apparently some SDKs define this
1555 structure.
1556 
1557 Hoisted the symlink and mount point data into a new type to allow us
1558 to make a pointer to it, and to avoid C++ scoping issues.
1559 
1560 */
1561 
1562 typedef struct {
1563     USHORT SubstituteNameOffset;
1564     USHORT SubstituteNameLength;
1565     USHORT PrintNameOffset;
1566     USHORT PrintNameLength;
1567     ULONG  Flags;
1568     WCHAR  PathBuffer[MAX_PATH*3];
1569 } MY_SYMLINK_REPARSE_BUFFER, *PMY_SYMLINK_REPARSE_BUFFER;
1570 
1571 typedef struct {
1572     USHORT SubstituteNameOffset;
1573     USHORT SubstituteNameLength;
1574     USHORT PrintNameOffset;
1575     USHORT PrintNameLength;
1576     WCHAR  PathBuffer[MAX_PATH*3];
1577 } MY_MOUNT_POINT_REPARSE_BUFFER;
1578 
1579 typedef struct {
1580   ULONG  ReparseTag;
1581   USHORT ReparseDataLength;
1582   USHORT Reserved;
1583   union {
1584     MY_SYMLINK_REPARSE_BUFFER SymbolicLinkReparseBuffer;
1585     MY_MOUNT_POINT_REPARSE_BUFFER MountPointReparseBuffer;
1586     struct {
1587       UCHAR DataBuffer[1];
1588     } GenericReparseBuffer;
1589   } Data;
1590 } MY_REPARSE_DATA_BUFFER, *PMY_REPARSE_DATA_BUFFER;
1591 
1592 #ifndef IO_REPARSE_TAG_SYMLINK
1593 #  define IO_REPARSE_TAG_SYMLINK                  (0xA000000CL)
1594 #endif
1595 #ifndef IO_REPARSE_TAG_AF_UNIX
1596 #  define IO_REPARSE_TAG_AF_UNIX 0x80000023
1597 #endif
1598 #ifndef IO_REPARSE_TAG_LX_FIFO
1599 #  define IO_REPARSE_TAG_LX_FIFO 0x80000024
1600 #endif
1601 #ifndef IO_REPARSE_TAG_LX_CHR
1602 #  define IO_REPARSE_TAG_LX_CHR  0x80000025
1603 #endif
1604 #ifndef IO_REPARSE_TAG_LX_BLK
1605 #  define IO_REPARSE_TAG_LX_BLK  0x80000026
1606 #endif
1607 
1608 static int
win32_stat_low(HANDLE handle,const char * path,STRLEN len,Stat_t * sbuf,DWORD reparse_type)1609 win32_stat_low(HANDLE handle, const char *path, STRLEN len, Stat_t *sbuf,
1610                DWORD reparse_type) {
1611     DWORD type = GetFileType(handle);
1612     BY_HANDLE_FILE_INFORMATION bhi;
1613 
1614     Zero(sbuf, 1, Stat_t);
1615 
1616     if (reparse_type) {
1617         /* Lie to get to the right place */
1618         type = FILE_TYPE_DISK;
1619     }
1620 
1621     type &= ~FILE_TYPE_REMOTE;
1622 
1623     switch (type) {
1624     case FILE_TYPE_DISK:
1625         if (GetFileInformationByHandle(handle, &bhi)) {
1626             sbuf->st_dev = bhi.dwVolumeSerialNumber;
1627             sbuf->st_ino = bhi.nFileIndexHigh;
1628             sbuf->st_ino <<= 32;
1629             sbuf->st_ino |= bhi.nFileIndexLow;
1630             sbuf->st_nlink = bhi.nNumberOfLinks;
1631             sbuf->st_uid = 0;
1632             sbuf->st_gid = 0;
1633             /* ucrt sets this to the drive letter for
1634                stat(), lets not reproduce that mistake */
1635             sbuf->st_rdev = 0;
1636             sbuf->st_size = bhi.nFileSizeHigh;
1637             sbuf->st_size <<= 32;
1638             sbuf->st_size |= bhi.nFileSizeLow;
1639 
1640             sbuf->st_atime = translate_ft_to_time_t(bhi.ftLastAccessTime);
1641             sbuf->st_mtime = translate_ft_to_time_t(bhi.ftLastWriteTime);
1642             sbuf->st_ctime = translate_ft_to_time_t(bhi.ftCreationTime);
1643 
1644             if (reparse_type) {
1645                 /* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
1646                    describes all of these as WSL only, but the AF_UNIX tag
1647                    is known to be used for AF_UNIX sockets without WSL.
1648                 */
1649                 switch (reparse_type) {
1650                 case IO_REPARSE_TAG_AF_UNIX:
1651                     sbuf->st_mode = _S_IFSOCK;
1652                     break;
1653 
1654                 case IO_REPARSE_TAG_LX_FIFO:
1655                     sbuf->st_mode = _S_IFIFO;
1656                     break;
1657 
1658                 case IO_REPARSE_TAG_LX_CHR:
1659                     sbuf->st_mode = _S_IFCHR;
1660                     break;
1661 
1662                 case IO_REPARSE_TAG_LX_BLK:
1663                     sbuf->st_mode = _S_IFBLK;
1664                     break;
1665 
1666                 default:
1667                     /* Is there anything else we can do here? */
1668                     errno = EINVAL;
1669                     return -1;
1670                 }
1671             }
1672             else if (bhi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1673                 sbuf->st_mode = _S_IFDIR | _S_IREAD | _S_IEXEC;
1674                 /* duplicate the logic from the end of the old win32_stat() */
1675                 if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
1676                     sbuf->st_mode |= S_IWRITE;
1677                 }
1678             }
1679             else {
1680                 char path_buf[MAX_PATH+1];
1681                 sbuf->st_mode = _S_IFREG;
1682 
1683                 if (!path) {
1684                     pGetFinalPathNameByHandleA_t pGetFinalPathNameByHandleA =
1685                         (pGetFinalPathNameByHandleA_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetFinalPathNameByHandleA");
1686                     if (pGetFinalPathNameByHandleA) {
1687                         len = pGetFinalPathNameByHandleA(handle, path_buf, sizeof(path_buf), 0);
1688                     }
1689                     else {
1690                         len = 0;
1691                     }
1692 
1693                     /* < to ensure there's space for the \0 */
1694                     if (len && len < sizeof(path_buf)) {
1695                         path = path_buf;
1696                     }
1697                 }
1698 
1699                 if (path && len > 4 &&
1700                     (_stricmp(path + len - 4, ".exe") == 0 ||
1701                      _stricmp(path + len - 4, ".bat") == 0 ||
1702                      _stricmp(path + len - 4, ".cmd") == 0 ||
1703                      _stricmp(path + len - 4, ".com") == 0)) {
1704                     sbuf->st_mode |= _S_IEXEC;
1705                 }
1706                 if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
1707                     sbuf->st_mode |= _S_IWRITE;
1708                 }
1709                 sbuf->st_mode |= _S_IREAD;
1710             }
1711         }
1712         else {
1713             translate_to_errno();
1714             return -1;
1715         }
1716         break;
1717 
1718     case FILE_TYPE_CHAR:
1719     case FILE_TYPE_PIPE:
1720         sbuf->st_mode = (type == FILE_TYPE_CHAR) ? _S_IFCHR : _S_IFIFO;
1721         if (handle == GetStdHandle(STD_INPUT_HANDLE) ||
1722             handle == GetStdHandle(STD_OUTPUT_HANDLE) ||
1723             handle == GetStdHandle(STD_ERROR_HANDLE)) {
1724             sbuf->st_mode |= _S_IWRITE | _S_IREAD;
1725         }
1726         break;
1727 
1728     default:
1729         return -1;
1730     }
1731 
1732     /* owner == user == group */
1733     sbuf->st_mode |= (sbuf->st_mode & 0700) >> 3;
1734     sbuf->st_mode |= (sbuf->st_mode & 0700) >> 6;
1735 
1736     return 0;
1737 }
1738 
1739 /* https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points */
1740 #define SYMLINK_FOLLOW_LIMIT 63
1741 
1742 /*
1743 
1744 Given a pathname, required to be a symlink, follow it until we find a
1745 non-symlink path.
1746 
1747 This should only be called when the symlink() chain doesn't lead to a
1748 normal file, which should have been caught earlier.
1749 
1750 On success, returns a HANDLE to the target and sets *reparse_type to
1751 the ReparseTag of the target.
1752 
1753 Returns INVALID_HANDLE_VALUE on error, which might be that the symlink
1754 chain is broken, or requires too many links to resolve.
1755 
1756 */
1757 
1758 static HANDLE
S_follow_symlinks_to(pTHX_ const char * pathname,DWORD * reparse_type)1759 S_follow_symlinks_to(pTHX_ const char *pathname, DWORD *reparse_type) {
1760     char link_target[MAX_PATH];
1761     SV *work_path = newSVpvn(pathname, strlen(pathname));
1762     int link_count = 0;
1763     int link_len;
1764     HANDLE handle;
1765 
1766     *reparse_type = 0;
1767 
1768     while ((link_len = win32_readlink(SvPVX(work_path), link_target,
1769                                       sizeof(link_target))) > 0) {
1770         if (link_count++ >= SYMLINK_FOLLOW_LIMIT) {
1771             /* Windows doesn't appear to ever return ELOOP,
1772                let's do better ourselves
1773             */
1774             SvREFCNT_dec(work_path);
1775             errno = ELOOP;
1776             return INVALID_HANDLE_VALUE;
1777         }
1778         /* Adjust the linktarget based on the link source or current
1779            directory as needed.
1780         */
1781         if (link_target[0] == '\\'
1782             || link_target[0] == '/'
1783             || (link_len >=2 && link_target[1] == ':')) {
1784             /* link is absolute */
1785             sv_setpvn(work_path, link_target, link_len);
1786         }
1787         else {
1788             STRLEN work_len;
1789             const char *workp = SvPV(work_path, work_len);
1790             const char *final_bslash =
1791                 (const char *)my_memrchr(workp, '\\', work_len);
1792             const char *final_slash =
1793                 (const char *)my_memrchr(workp, '/', work_len);
1794             const char *path_sep = NULL;
1795             if (final_bslash && final_slash)
1796                 path_sep = final_bslash > final_slash ? final_bslash : final_slash;
1797             else if (final_bslash)
1798                 path_sep = final_bslash;
1799             else if (final_slash)
1800                 path_sep = final_slash;
1801 
1802             if (path_sep) {
1803                 SV *new_path = newSVpv(workp, path_sep - workp + 1);
1804                 sv_catpvn(new_path, link_target, link_len);
1805                 SvREFCNT_dec(work_path);
1806                 work_path = new_path;
1807             }
1808             else {
1809                 /* should only get here the first time around */
1810                 assert(link_count == 1);
1811                 char path_temp[MAX_PATH];
1812                 DWORD path_len = GetCurrentDirectoryA(sizeof(path_temp), path_temp);
1813                 if (!path_len || path_len > sizeof(path_temp)) {
1814                     SvREFCNT_dec(work_path);
1815                     errno = EINVAL;
1816                     return INVALID_HANDLE_VALUE;
1817                 }
1818 
1819                 SV *new_path = newSVpvn(path_temp, path_len);
1820                 if (path_temp[path_len-1] != '\\') {
1821                     sv_catpvs(new_path, "\\");
1822                 }
1823                 sv_catpvn(new_path, link_target, link_len);
1824                 SvREFCNT_dec(work_path);
1825                 work_path = new_path;
1826             }
1827         }
1828     }
1829 
1830     handle =
1831         CreateFileA(SvPVX(work_path), GENERIC_READ, 0, NULL, OPEN_EXISTING,
1832                     FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
1833     SvREFCNT_dec(work_path);
1834     if (handle != INVALID_HANDLE_VALUE) {
1835         MY_REPARSE_DATA_BUFFER linkdata;
1836         DWORD linkdata_returned;
1837 
1838         if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
1839                              &linkdata, sizeof(linkdata),
1840                              &linkdata_returned, NULL)) {
1841             translate_to_errno();
1842             CloseHandle(handle);
1843             return INVALID_HANDLE_VALUE;
1844         }
1845         *reparse_type = linkdata.ReparseTag;
1846         return handle;
1847     }
1848     else {
1849         translate_to_errno();
1850     }
1851 
1852     return handle;
1853 }
1854 
1855 DllExport int
win32_stat(const char * path,Stat_t * sbuf)1856 win32_stat(const char *path, Stat_t *sbuf)
1857 {
1858     dTHX;
1859     BOOL        expect_dir = FALSE;
1860     int result;
1861     HANDLE handle;
1862     DWORD reparse_type = 0;
1863 
1864     path = PerlDir_mapA(path);
1865 
1866     handle =
1867         CreateFileA(path, FILE_READ_ATTRIBUTES,
1868                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1869                     NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1870     if (handle == INVALID_HANDLE_VALUE) {
1871         /* AF_UNIX sockets need to be opened as a reparse point, but
1872            that will also open symlinks rather than following them.
1873 
1874            There may be other reparse points that need similar
1875            treatment.
1876         */
1877         handle = S_follow_symlinks_to(aTHX_ path, &reparse_type);
1878         if (handle == INVALID_HANDLE_VALUE) {
1879             /* S_follow_symlinks_to() will set errno */
1880             return -1;
1881         }
1882     }
1883     if (handle != INVALID_HANDLE_VALUE) {
1884         result = win32_stat_low(handle, path, strlen(path), sbuf, reparse_type);
1885         CloseHandle(handle);
1886     }
1887     else {
1888         translate_to_errno();
1889         result = -1;
1890     }
1891 
1892     return result;
1893 }
1894 
1895 static void
translate_to_errno(void)1896 translate_to_errno(void)
1897 {
1898     /* This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for
1899        both permissions errors and if the source is a directory, while
1900        POSIX wants EACCES and EPERM respectively.
1901     */
1902     switch (GetLastError()) {
1903     case ERROR_BAD_NET_NAME:
1904     case ERROR_BAD_NETPATH:
1905     case ERROR_BAD_PATHNAME:
1906     case ERROR_FILE_NOT_FOUND:
1907     case ERROR_FILENAME_EXCED_RANGE:
1908     case ERROR_INVALID_DRIVE:
1909     case ERROR_PATH_NOT_FOUND:
1910       errno = ENOENT;
1911       break;
1912     case ERROR_ALREADY_EXISTS:
1913       errno = EEXIST;
1914       break;
1915     case ERROR_ACCESS_DENIED:
1916       errno = EACCES;
1917       break;
1918     case ERROR_PRIVILEGE_NOT_HELD:
1919       errno = EPERM;
1920       break;
1921     case ERROR_NOT_SAME_DEVICE:
1922       errno = EXDEV;
1923       break;
1924     case ERROR_DISK_FULL:
1925       errno = ENOSPC;
1926       break;
1927     case ERROR_NOT_ENOUGH_QUOTA:
1928       errno = EDQUOT;
1929       break;
1930     default:
1931       /* ERROR_INVALID_FUNCTION - eg. symlink on a FAT volume */
1932       errno = EINVAL;
1933       break;
1934     }
1935 }
1936 
1937 static BOOL
is_symlink(HANDLE h)1938 is_symlink(HANDLE h) {
1939     MY_REPARSE_DATA_BUFFER linkdata;
1940     const MY_SYMLINK_REPARSE_BUFFER * const sd =
1941         &linkdata.Data.SymbolicLinkReparseBuffer;
1942     DWORD linkdata_returned;
1943 
1944     if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &linkdata, sizeof(linkdata), &linkdata_returned, NULL)) {
1945         return FALSE;
1946     }
1947 
1948     if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.SymbolicLinkReparseBuffer.PathBuffer)
1949         || (linkdata.ReparseTag != IO_REPARSE_TAG_SYMLINK
1950             && linkdata.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)) {
1951         /* some other type of reparse point */
1952         return FALSE;
1953     }
1954 
1955     return TRUE;
1956 }
1957 
1958 static BOOL
is_symlink_name(const char * name)1959 is_symlink_name(const char *name) {
1960     HANDLE f = CreateFileA(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1961                            FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
1962     BOOL result;
1963 
1964     if (f == INVALID_HANDLE_VALUE) {
1965         return FALSE;
1966     }
1967     result = is_symlink(f);
1968     CloseHandle(f);
1969 
1970     return result;
1971 }
1972 
1973 static int
do_readlink_handle(HANDLE hlink,char * buf,size_t bufsiz,bool * is_symlink)1974 do_readlink_handle(HANDLE hlink, char *buf, size_t bufsiz, bool *is_symlink) {
1975     MY_REPARSE_DATA_BUFFER linkdata;
1976     DWORD linkdata_returned;
1977 
1978     if (is_symlink)
1979         *is_symlink = FALSE;
1980 
1981     if (!DeviceIoControl(hlink, FSCTL_GET_REPARSE_POINT, NULL, 0, &linkdata, sizeof(linkdata), &linkdata_returned, NULL)) {
1982         translate_to_errno();
1983         return -1;
1984     }
1985 
1986     int bytes_out;
1987     BOOL used_default;
1988     switch (linkdata.ReparseTag) {
1989     case IO_REPARSE_TAG_SYMLINK:
1990         {
1991             const MY_SYMLINK_REPARSE_BUFFER * const sd =
1992                 &linkdata.Data.SymbolicLinkReparseBuffer;
1993             if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.SymbolicLinkReparseBuffer.PathBuffer)) {
1994                 errno = EINVAL;
1995                 return -1;
1996             }
1997             bytes_out =
1998                 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1999                                     sd->PathBuffer + sd->PrintNameOffset/2,
2000                                     sd->PrintNameLength/2,
2001                                     buf, (int)bufsiz, NULL, &used_default);
2002             if (is_symlink)
2003                 *is_symlink = TRUE;
2004         }
2005         break;
2006     case IO_REPARSE_TAG_MOUNT_POINT:
2007         {
2008             const MY_MOUNT_POINT_REPARSE_BUFFER * const rd =
2009                 &linkdata.Data.MountPointReparseBuffer;
2010             if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.MountPointReparseBuffer.PathBuffer)) {
2011                 errno = EINVAL;
2012                 return -1;
2013             }
2014             bytes_out =
2015                 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
2016                                     rd->PathBuffer + rd->PrintNameOffset/2,
2017                                     rd->PrintNameLength/2,
2018                                     buf, (int)bufsiz, NULL, &used_default);
2019             if (is_symlink)
2020                 *is_symlink = TRUE;
2021         }
2022         break;
2023 
2024     default:
2025         errno = EINVAL;
2026         return -1;
2027     }
2028 
2029     if (bytes_out == 0 || used_default) {
2030         /* failed conversion from unicode to ANSI or otherwise failed */
2031         errno = EINVAL;
2032         return -1;
2033     }
2034 
2035     return bytes_out;
2036 }
2037 
2038 DllExport int
win32_readlink(const char * pathname,char * buf,size_t bufsiz)2039 win32_readlink(const char *pathname, char *buf, size_t bufsiz) {
2040     if (pathname == NULL || buf == NULL) {
2041         errno = EFAULT;
2042         return -1;
2043     }
2044     if (bufsiz <= 0) {
2045         errno = EINVAL;
2046         return -1;
2047     }
2048 
2049     DWORD fileattr = GetFileAttributes(pathname);
2050     if (fileattr == INVALID_FILE_ATTRIBUTES) {
2051         translate_to_errno();
2052         return -1;
2053     }
2054 
2055     if (!(fileattr & FILE_ATTRIBUTE_REPARSE_POINT)) {
2056         /* not a symbolic link */
2057         errno = EINVAL;
2058         return -1;
2059     }
2060 
2061     HANDLE hlink =
2062         CreateFileA(pathname, GENERIC_READ, 0, NULL, OPEN_EXISTING,
2063                     FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
2064     if (hlink == INVALID_HANDLE_VALUE) {
2065         translate_to_errno();
2066         return -1;
2067     }
2068     int bytes_out = do_readlink_handle(hlink, buf, bufsiz, NULL);
2069     CloseHandle(hlink);
2070     if (bytes_out < 0) {
2071         /* errno already set */
2072         return -1;
2073     }
2074 
2075     if ((size_t)bytes_out > bufsiz) {
2076         errno = EINVAL;
2077         return -1;
2078     }
2079 
2080     return bytes_out;
2081 }
2082 
2083 DllExport int
win32_lstat(const char * path,Stat_t * sbuf)2084 win32_lstat(const char *path, Stat_t *sbuf)
2085 {
2086     HANDLE f;
2087     int result;
2088     DWORD attr = GetFileAttributes(path); /* doesn't follow symlinks */
2089 
2090     if (attr == INVALID_FILE_ATTRIBUTES) {
2091         translate_to_errno();
2092         return -1;
2093     }
2094 
2095     if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
2096         return win32_stat(path, sbuf);
2097     }
2098 
2099     f = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
2100                            FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
2101     if (f == INVALID_HANDLE_VALUE) {
2102         translate_to_errno();
2103         return -1;
2104     }
2105     bool is_symlink;
2106     int size = do_readlink_handle(f, NULL, 0, &is_symlink);
2107     if (!is_symlink) {
2108         /* it isn't a symlink, fallback to normal stat */
2109         CloseHandle(f);
2110         return win32_stat(path, sbuf);
2111     }
2112     else if (size < 0) {
2113         /* some other error, errno already set */
2114         CloseHandle(f);
2115         return -1;
2116     }
2117     result = win32_stat_low(f, NULL, 0, sbuf, 0);
2118 
2119     if (result != -1){
2120         sbuf->st_mode = (sbuf->st_mode & ~_S_IFMT) | _S_IFLNK;
2121         sbuf->st_size = size;
2122     }
2123     CloseHandle(f);
2124 
2125     return result;
2126 }
2127 
2128 #define isSLASH(c) ((c) == '/' || (c) == '\\')
2129 #define SKIP_SLASHES(s) \
2130     STMT_START {				\
2131         while (*(s) && isSLASH(*(s)))		\
2132             ++(s);				\
2133     } STMT_END
2134 #define COPY_NONSLASHES(d,s) \
2135     STMT_START {				\
2136         while (*(s) && !isSLASH(*(s)))		\
2137             *(d)++ = *(s)++;			\
2138     } STMT_END
2139 
2140 /* Find the longname of a given path.  path is destructively modified.
2141  * It should have space for at least MAX_PATH characters. */
2142 DllExport char *
win32_longpath(char * path)2143 win32_longpath(char *path)
2144 {
2145     WIN32_FIND_DATA fdata;
2146     HANDLE fhand;
2147     char tmpbuf[MAX_PATH+1];
2148     char *tmpstart = tmpbuf;
2149     char *start = path;
2150     char sep;
2151     if (!path)
2152         return NULL;
2153 
2154     /* drive prefix */
2155     if (isALPHA(path[0]) && path[1] == ':') {
2156         start = path + 2;
2157         *tmpstart++ = path[0];
2158         *tmpstart++ = ':';
2159     }
2160     /* UNC prefix */
2161     else if (isSLASH(path[0]) && isSLASH(path[1])) {
2162         start = path + 2;
2163         *tmpstart++ = path[0];
2164         *tmpstart++ = path[1];
2165         SKIP_SLASHES(start);
2166         COPY_NONSLASHES(tmpstart,start);	/* copy machine name */
2167         if (*start) {
2168             *tmpstart++ = *start++;
2169             SKIP_SLASHES(start);
2170             COPY_NONSLASHES(tmpstart,start);	/* copy share name */
2171         }
2172     }
2173     *tmpstart = '\0';
2174     while (*start) {
2175         /* copy initial slash, if any */
2176         if (isSLASH(*start)) {
2177             *tmpstart++ = *start++;
2178             *tmpstart = '\0';
2179             SKIP_SLASHES(start);
2180         }
2181 
2182         /* FindFirstFile() expands "." and "..", so we need to pass
2183          * those through unmolested */
2184         if (*start == '.'
2185             && (!start[1] || isSLASH(start[1])
2186                 || (start[1] == '.' && (!start[2] || isSLASH(start[2])))))
2187         {
2188             COPY_NONSLASHES(tmpstart,start);	/* copy "." or ".." */
2189             *tmpstart = '\0';
2190             continue;
2191         }
2192 
2193         /* if this is the end, bust outta here */
2194         if (!*start)
2195             break;
2196 
2197         /* now we're at a non-slash; walk up to next slash */
2198         while (*start && !isSLASH(*start))
2199             ++start;
2200 
2201         /* stop and find full name of component */
2202         sep = *start;
2203         *start = '\0';
2204         fhand = FindFirstFile(path,&fdata);
2205         *start = sep;
2206         if (fhand != INVALID_HANDLE_VALUE) {
2207             STRLEN len = strlen(fdata.cFileName);
2208             if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) {
2209                 strcpy(tmpstart, fdata.cFileName);
2210                 tmpstart += len;
2211                 FindClose(fhand);
2212             }
2213             else {
2214                 FindClose(fhand);
2215                 errno = ERANGE;
2216                 return NULL;
2217             }
2218         }
2219         else {
2220             /* failed a step, just return without side effects */
2221             errno = EINVAL;
2222             return NULL;
2223         }
2224     }
2225     strcpy(path,tmpbuf);
2226     return path;
2227 }
2228 
2229 static void
out_of_memory(void)2230 out_of_memory(void)
2231 {
2232 
2233     if (PL_curinterp)
2234         croak_no_mem();
2235     exit(1);
2236 }
2237 
2238 void
win32_croak_not_implemented(const char * fname)2239 win32_croak_not_implemented(const char * fname)
2240 {
2241     PERL_ARGS_ASSERT_WIN32_CROAK_NOT_IMPLEMENTED;
2242 
2243     Perl_croak_nocontext("%s not implemented!\n", fname);
2244 }
2245 
2246 /* Converts a wide character (UTF-16) string to the Windows ANSI code page,
2247  * potentially using the system's default replacement character for any
2248  * unrepresentable characters. The caller must free() the returned string. */
2249 static char*
wstr_to_str(const wchar_t * wstr)2250 wstr_to_str(const wchar_t* wstr)
2251 {
2252     BOOL used_default = FALSE;
2253     size_t wlen = wcslen(wstr) + 1;
2254     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
2255                                    NULL, 0, NULL, NULL);
2256     char* str = (char*)malloc(len);
2257     if (!str)
2258         out_of_memory();
2259     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
2260                         str, len, NULL, &used_default);
2261     return str;
2262 }
2263 
2264 /* The win32_ansipath() function takes a Unicode filename and converts it
2265  * into the current Windows codepage. If some characters cannot be mapped,
2266  * then it will convert the short name instead.
2267  *
2268  * The buffer to the ansi pathname must be freed with win32_free() when it
2269  * is no longer needed.
2270  *
2271  * The argument to win32_ansipath() must exist before this function is
2272  * called; otherwise there is no way to determine the short path name.
2273  *
2274  * Ideas for future refinement:
2275  * - Only convert those segments of the path that are not in the current
2276  *   codepage, but leave the other segments in their long form.
2277  * - If the resulting name is longer than MAX_PATH, start converting
2278  *   additional path segments into short names until the full name
2279  *   is shorter than MAX_PATH.  Shorten the filename part last!
2280  */
2281 DllExport char *
win32_ansipath(const WCHAR * widename)2282 win32_ansipath(const WCHAR *widename)
2283 {
2284     char *name;
2285     BOOL use_default = FALSE;
2286     size_t widelen = wcslen(widename)+1;
2287     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
2288                                   NULL, 0, NULL, NULL);
2289     name = (char*)win32_malloc(len);
2290     if (!name)
2291         out_of_memory();
2292 
2293     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
2294                         name, len, NULL, &use_default);
2295     if (use_default) {
2296         DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
2297         if (shortlen) {
2298             WCHAR *shortname = (WCHAR*)win32_malloc(shortlen*sizeof(WCHAR));
2299             if (!shortname)
2300                 out_of_memory();
2301             shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
2302 
2303             len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
2304                                       NULL, 0, NULL, NULL);
2305             name = (char*)win32_realloc(name, len);
2306             if (!name)
2307                 out_of_memory();
2308             WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
2309                                 name, len, NULL, NULL);
2310             win32_free(shortname);
2311         }
2312     }
2313     return name;
2314 }
2315 
2316 /* the returned string must be freed with win32_freeenvironmentstrings which is
2317  * implemented as a macro
2318  * void win32_freeenvironmentstrings(void* block)
2319  */
2320 DllExport char *
win32_getenvironmentstrings(void)2321 win32_getenvironmentstrings(void)
2322 {
2323     LPWSTR lpWStr, lpWTmp;
2324     LPSTR lpStr, lpTmp;
2325     DWORD env_len, wenvstrings_len = 0, aenvstrings_len = 0;
2326 
2327     /* Get the process environment strings */
2328     lpWTmp = lpWStr = (LPWSTR) GetEnvironmentStringsW();
2329     for (wenvstrings_len = 1; *lpWTmp != '\0'; lpWTmp += env_len + 1) {
2330         env_len = wcslen(lpWTmp);
2331         /* calculate the size of the environment strings */
2332         wenvstrings_len += env_len + 1;
2333     }
2334 
2335     /* Get the number of bytes required to store the ACP encoded string */
2336     aenvstrings_len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
2337                                           lpWStr, wenvstrings_len, NULL, 0, NULL, NULL);
2338     lpTmp = lpStr = (char *)win32_calloc(aenvstrings_len, sizeof(char));
2339     if(!lpTmp)
2340         out_of_memory();
2341 
2342     /* Convert the string from UTF-16 encoding to ACP encoding */
2343     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpWStr, wenvstrings_len, lpStr,
2344                         aenvstrings_len, NULL, NULL);
2345 
2346     FreeEnvironmentStringsW(lpWStr);
2347 
2348     return(lpStr);
2349 }
2350 
2351 DllExport char *
win32_getenv(const char * name)2352 win32_getenv(const char *name)
2353 {
2354     dTHX;
2355     DWORD needlen;
2356     SV *curitem = NULL;
2357     DWORD last_err;
2358 
2359     needlen = GetEnvironmentVariableA(name,NULL,0);
2360     if (needlen != 0) {
2361         curitem = sv_2mortal(newSVpvs(""));
2362         do {
2363             SvGROW(curitem, needlen+1);
2364             needlen = GetEnvironmentVariableA(name,SvPVX(curitem),
2365                                               needlen);
2366         } while (needlen >= SvLEN(curitem));
2367         SvCUR_set(curitem, needlen);
2368     }
2369     else {
2370         last_err = GetLastError();
2371         if (last_err == ERROR_NOT_ENOUGH_MEMORY) {
2372             /* It appears the variable is in the env, but the Win32 API
2373                doesn't have a canned way of getting it.  So we fall back to
2374                grabbing the whole env and pulling this value out if possible */
2375             char *envv = GetEnvironmentStrings();
2376             char *cur = envv;
2377             STRLEN len;
2378             while (*cur) {
2379                 char *end = strchr(cur,'=');
2380                 if (end && end != cur) {
2381                     *end = '\0';
2382                     if (strEQ(cur,name)) {
2383                         curitem = sv_2mortal(newSVpv(end+1,0));
2384                         *end = '=';
2385                         break;
2386                     }
2387                     *end = '=';
2388                     cur = end + strlen(end+1)+2;
2389                 }
2390                 else if ((len = strlen(cur)))
2391                     cur += len+1;
2392             }
2393             FreeEnvironmentStrings(envv);
2394         }
2395 #ifndef WIN32_NO_REGISTRY
2396         else {
2397             /* last ditch: allow any environment variables that begin with 'PERL'
2398                to be obtained from the registry, if found there */
2399             if (strBEGINs(name, "PERL"))
2400                 (void)get_regstr(name, &curitem);
2401         }
2402 #endif
2403     }
2404     if (curitem && SvCUR(curitem))
2405         return SvPVX(curitem);
2406 
2407     return NULL;
2408 }
2409 
2410 DllExport int
win32_putenv(const char * name)2411 win32_putenv(const char *name)
2412 {
2413     char* curitem;
2414     char* val;
2415     int relval = -1;
2416 
2417     if (name) {
2418         curitem = (char *) win32_malloc(strlen(name)+1);
2419         strcpy(curitem, name);
2420         val = strchr(curitem, '=');
2421         if (val) {
2422             /* The sane way to deal with the environment.
2423              * Has these advantages over putenv() & co.:
2424              *  * enables us to store a truly empty value in the
2425              *    environment (like in UNIX).
2426              *  * we don't have to deal with RTL globals, bugs and leaks
2427              *    (specifically, see http://support.microsoft.com/kb/235601).
2428              *  * Much faster.
2429              * Why you may want to use the RTL environment handling
2430              * (previously enabled by USE_WIN32_RTL_ENV):
2431              *  * environ[] and RTL functions will not reflect changes,
2432              *    which might be an issue if extensions want to access
2433              *    the env. via RTL.  This cuts both ways, since RTL will
2434              *    not see changes made by extensions that call the Win32
2435              *    functions directly, either.
2436              * GSAR 97-06-07
2437              */
2438             *val++ = '\0';
2439             if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
2440                 relval = 0;
2441         }
2442         win32_free(curitem);
2443     }
2444     return relval;
2445 }
2446 
2447 static long
filetime_to_clock(PFILETIME ft)2448 filetime_to_clock(PFILETIME ft)
2449 {
2450     __int64 qw = ft->dwHighDateTime;
2451     qw <<= 32;
2452     qw |= ft->dwLowDateTime;
2453     qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
2454     return (long) qw;
2455 }
2456 
2457 DllExport int
win32_times(struct tms * timebuf)2458 win32_times(struct tms *timebuf)
2459 {
2460     FILETIME user;
2461     FILETIME kernel;
2462     FILETIME dummy;
2463     clock_t process_time_so_far = clock();
2464     if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
2465                         &kernel,&user)) {
2466         timebuf->tms_utime = filetime_to_clock(&user);
2467         timebuf->tms_stime = filetime_to_clock(&kernel);
2468         timebuf->tms_cutime = 0;
2469         timebuf->tms_cstime = 0;
2470     } else {
2471         /* That failed - e.g. Win95 fallback to clock() */
2472         timebuf->tms_utime = process_time_so_far;
2473         timebuf->tms_stime = 0;
2474         timebuf->tms_cutime = 0;
2475         timebuf->tms_cstime = 0;
2476     }
2477     return process_time_so_far;
2478 }
2479 
2480 static BOOL
filetime_from_time(PFILETIME pFileTime,time_t Time)2481 filetime_from_time(PFILETIME pFileTime, time_t Time)
2482 {
2483     struct tm *pt;
2484     SYSTEMTIME st;
2485     dTHX;
2486 
2487     GMTIME_LOCK;
2488     pt = gmtime(&Time);
2489     if (!pt) {
2490         GMTIME_UNLOCK;
2491         pFileTime->dwLowDateTime = 0;
2492         pFileTime->dwHighDateTime = 0;
2493         return FALSE;
2494     }
2495 
2496     st.wYear = pt->tm_year + 1900;
2497     st.wMonth = pt->tm_mon + 1;
2498     st.wDay = pt->tm_mday;
2499     st.wHour = pt->tm_hour;
2500     st.wMinute = pt->tm_min;
2501     st.wSecond = pt->tm_sec;
2502     st.wMilliseconds = 0;
2503 
2504     GMTIME_UNLOCK;
2505 
2506     if (!SystemTimeToFileTime(&st, pFileTime)) {
2507         pFileTime->dwLowDateTime = 0;
2508         pFileTime->dwHighDateTime = 0;
2509         return FALSE;
2510     }
2511 
2512     return TRUE;
2513 }
2514 
2515 DllExport int
win32_unlink(const char * filename)2516 win32_unlink(const char *filename)
2517 {
2518     dTHX;
2519     int ret;
2520     DWORD attrs;
2521 
2522     filename = PerlDir_mapA(filename);
2523     attrs = GetFileAttributesA(filename);
2524     if (attrs == 0xFFFFFFFF) {
2525         errno = ENOENT;
2526         return -1;
2527     }
2528     if (attrs & FILE_ATTRIBUTE_READONLY) {
2529         (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
2530         ret = unlink(filename);
2531         if (ret == -1)
2532             (void)SetFileAttributesA(filename, attrs);
2533     }
2534     else if ((attrs & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
2535         == (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)
2536              && is_symlink_name(filename)) {
2537         ret = rmdir(filename);
2538     }
2539     else {
2540         ret = unlink(filename);
2541     }
2542     return ret;
2543 }
2544 
2545 DllExport int
win32_utime(const char * filename,struct utimbuf * times)2546 win32_utime(const char *filename, struct utimbuf *times)
2547 {
2548     dTHX;
2549     HANDLE handle;
2550     FILETIME ftAccess;
2551     FILETIME ftWrite;
2552     struct utimbuf TimeBuffer;
2553     int rc = -1;
2554 
2555     filename = PerlDir_mapA(filename);
2556     /* This will (and should) still fail on readonly files */
2557     handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
2558                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2559                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
2560     if (handle == INVALID_HANDLE_VALUE) {
2561         translate_to_errno();
2562         return -1;
2563     }
2564 
2565     if (times == NULL) {
2566         times = &TimeBuffer;
2567         time(&times->actime);
2568         times->modtime = times->actime;
2569     }
2570 
2571     if (filetime_from_time(&ftAccess, times->actime) &&
2572         filetime_from_time(&ftWrite, times->modtime)) {
2573         if (SetFileTime(handle, NULL, &ftAccess, &ftWrite)) {
2574             rc = 0;
2575         }
2576         else {
2577             translate_to_errno();
2578         }
2579     }
2580     else {
2581         errno = EINVAL; /* bad time? */
2582     }
2583 
2584     CloseHandle(handle);
2585     return rc;
2586 }
2587 
2588 typedef union {
2589     unsigned __int64	ft_i64;
2590     FILETIME		ft_val;
2591 } FT_t;
2592 
2593 #ifdef __GNUC__
2594 #define Const64(x) x##LL
2595 #else
2596 #define Const64(x) x##i64
2597 #endif
2598 /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */
2599 #define EPOCH_BIAS  Const64(116444736000000000)
2600 
2601 /* NOTE: This does not compute the timezone info (doing so can be expensive,
2602  * and appears to be unsupported even by glibc) */
2603 DllExport int
win32_gettimeofday(struct timeval * tp,void * not_used)2604 win32_gettimeofday(struct timeval *tp, void *not_used)
2605 {
2606     FT_t ft;
2607 
2608     /* this returns time in 100-nanosecond units  (i.e. tens of usecs) */
2609     GetSystemTimeAsFileTime(&ft.ft_val);
2610 
2611     /* seconds since epoch */
2612     tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000));
2613 
2614     /* microseconds remaining */
2615     tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000));
2616 
2617     return 0;
2618 }
2619 
2620 DllExport int
win32_uname(struct utsname * name)2621 win32_uname(struct utsname *name)
2622 {
2623     struct hostent *hep;
2624     STRLEN nodemax = sizeof(name->nodename)-1;
2625 
2626     /* sysname */
2627     switch (g_osver.dwPlatformId) {
2628     case VER_PLATFORM_WIN32_WINDOWS:
2629         strcpy(name->sysname, "Windows");
2630         break;
2631     case VER_PLATFORM_WIN32_NT:
2632         strcpy(name->sysname, "Windows NT");
2633         break;
2634     case VER_PLATFORM_WIN32s:
2635         strcpy(name->sysname, "Win32s");
2636         break;
2637     default:
2638         strcpy(name->sysname, "Win32 Unknown");
2639         break;
2640     }
2641 
2642     /* release */
2643     sprintf(name->release, "%d.%d",
2644             g_osver.dwMajorVersion, g_osver.dwMinorVersion);
2645 
2646     /* version */
2647     sprintf(name->version, "Build %d",
2648             g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT
2649             ? g_osver.dwBuildNumber : (g_osver.dwBuildNumber & 0xffff));
2650     if (g_osver.szCSDVersion[0]) {
2651         char *buf = name->version + strlen(name->version);
2652         sprintf(buf, " (%s)", g_osver.szCSDVersion);
2653     }
2654 
2655     /* nodename */
2656     hep = win32_gethostbyname("localhost");
2657     if (hep) {
2658         STRLEN len = strlen(hep->h_name);
2659         if (len <= nodemax) {
2660             strcpy(name->nodename, hep->h_name);
2661         }
2662         else {
2663             strncpy(name->nodename, hep->h_name, nodemax);
2664             name->nodename[nodemax] = '\0';
2665         }
2666     }
2667     else {
2668         DWORD sz = nodemax;
2669         if (!GetComputerName(name->nodename, &sz))
2670             *name->nodename = '\0';
2671     }
2672 
2673     /* machine (architecture) */
2674     {
2675         SYSTEM_INFO info;
2676         DWORD procarch;
2677         const char *arch;
2678         GetSystemInfo(&info);
2679 
2680 #if (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION) && !defined(__MINGW_EXTENSION))
2681         procarch = info.u.s.wProcessorArchitecture;
2682 #else
2683         procarch = info.wProcessorArchitecture;
2684 #endif
2685         switch (procarch) {
2686         case PROCESSOR_ARCHITECTURE_INTEL:
2687             arch = "x86"; break;
2688         case PROCESSOR_ARCHITECTURE_IA64:
2689             arch = "ia64"; break;
2690         case PROCESSOR_ARCHITECTURE_AMD64:
2691             arch = "amd64"; break;
2692         case PROCESSOR_ARCHITECTURE_UNKNOWN:
2693             arch = "unknown"; break;
2694         default:
2695             sprintf(name->machine, "unknown(0x%x)", procarch);
2696             arch = name->machine;
2697             break;
2698         }
2699         if (name->machine != arch)
2700             strcpy(name->machine, arch);
2701     }
2702     return 0;
2703 }
2704 
2705 /* Timing related stuff */
2706 
2707 int
do_raise(pTHX_ int sig)2708 do_raise(pTHX_ int sig)
2709 {
2710     if (sig < SIG_SIZE) {
2711         Sighandler_t handler = w32_sighandler[sig];
2712         if (handler == SIG_IGN) {
2713             return 0;
2714         }
2715         else if (handler != SIG_DFL) {
2716             (*handler)(sig);
2717             return 0;
2718         }
2719         else {
2720             /* Choose correct default behaviour */
2721             switch (sig) {
2722 #ifdef SIGCLD
2723                 case SIGCLD:
2724 #endif
2725 #ifdef SIGCHLD
2726                 case SIGCHLD:
2727 #endif
2728                 case 0:
2729                     return 0;
2730                 case SIGTERM:
2731                 default:
2732                     break;
2733             }
2734         }
2735     }
2736     /* Tell caller to exit thread/process as appropriate */
2737     return 1;
2738 }
2739 
2740 void
sig_terminate(pTHX_ int sig)2741 sig_terminate(pTHX_ int sig)
2742 {
2743     Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
2744     /* exit() seems to be safe, my_exit() or die() is a problem in ^C
2745        thread
2746      */
2747     exit(sig);
2748 }
2749 
2750 DllExport int
win32_async_check(pTHX)2751 win32_async_check(pTHX)
2752 {
2753     MSG msg;
2754     HWND hwnd = w32_message_hwnd;
2755 
2756     /* Reset w32_poll_count before doing anything else, in case we dispatch
2757      * messages that end up calling back into perl */
2758     w32_poll_count = 0;
2759 
2760     if (hwnd != INVALID_HANDLE_VALUE) {
2761         /* Passing PeekMessage -1 as HWND (2nd arg) only gets PostThreadMessage() messages
2762         * and ignores window messages - should co-exist better with windows apps e.g. Tk
2763         */
2764         if (hwnd == NULL)
2765             hwnd = (HWND)-1;
2766 
2767         while (PeekMessage(&msg, hwnd, WM_TIMER,    WM_TIMER,    PM_REMOVE|PM_NOYIELD) ||
2768                PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD))
2769         {
2770             /* re-post a WM_QUIT message (we'll mark it as read later) */
2771             if(msg.message == WM_QUIT) {
2772                 PostQuitMessage((int)msg.wParam);
2773                 break;
2774             }
2775 
2776             if(!CallMsgFilter(&msg, MSGF_USER))
2777             {
2778                 TranslateMessage(&msg);
2779                 DispatchMessage(&msg);
2780             }
2781         }
2782     }
2783 
2784     /* Call PeekMessage() to mark all pending messages in the queue as "old".
2785      * This is necessary when we are being called by win32_msgwait() to
2786      * make sure MsgWaitForMultipleObjects() stops reporting the same waiting
2787      * message over and over.  An example how this can happen is when
2788      * Perl is calling win32_waitpid() inside a GUI application and the GUI
2789      * is generating messages before the process terminated.
2790      */
2791     PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD);
2792 
2793     /* Above or other stuff may have set a signal flag */
2794     if (PL_sig_pending)
2795         despatch_signals();
2796 
2797     return 1;
2798 }
2799 
2800 /* This function will not return until the timeout has elapsed, or until
2801  * one of the handles is ready. */
2802 DllExport DWORD
win32_msgwait(pTHX_ DWORD count,LPHANDLE handles,DWORD timeout,LPDWORD resultp)2803 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
2804 {
2805     /* We may need several goes at this - so compute when we stop */
2806     FT_t ticks = {0};
2807     unsigned __int64 endtime = timeout;
2808     if (timeout != INFINITE) {
2809         GetSystemTimeAsFileTime(&ticks.ft_val);
2810         ticks.ft_i64 /= 10000;
2811         endtime += ticks.ft_i64;
2812     }
2813     /* This was a race condition. Do not let a non INFINITE timeout to
2814      * MsgWaitForMultipleObjects roll under 0 creating a near
2815      * infinity/~(UINT32)0 timeout which will appear as a deadlock to the
2816      * user who did a CORE perl function with a non infinity timeout,
2817      * sleep for example.  This is 64 to 32 truncation minefield.
2818      *
2819      * This scenario can only be created if the timespan from the return of
2820      * MsgWaitForMultipleObjects to GetSystemTimeAsFileTime exceeds 1 ms. To
2821      * generate the scenario, manual breakpoints in a C debugger are required,
2822      * or a context switch occurred in win32_async_check in PeekMessage, or random
2823      * messages are delivered to the *thread* message queue of the Perl thread
2824      * from another process (msctf.dll doing IPC among its instances, VS debugger
2825      * causes msctf.dll to be loaded into Perl by kernel), see [perl #33096].
2826      */
2827     while (ticks.ft_i64 <= endtime) {
2828         /* if timeout's type is lengthened, remember to split 64b timeout
2829          * into multiple non-infinity runs of MWFMO */
2830         DWORD result = MsgWaitForMultipleObjects(count, handles, FALSE,
2831                                                 (DWORD)(endtime - ticks.ft_i64),
2832                                                 QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
2833         if (resultp)
2834            *resultp = result;
2835         if (result == WAIT_TIMEOUT) {
2836             /* Ran out of time - explicit return of zero to avoid -ve if we
2837                have scheduling issues
2838              */
2839             return 0;
2840         }
2841         if (timeout != INFINITE) {
2842             GetSystemTimeAsFileTime(&ticks.ft_val);
2843             ticks.ft_i64 /= 10000;
2844         }
2845         if (result == WAIT_OBJECT_0 + count) {
2846             /* Message has arrived - check it */
2847             (void)win32_async_check(aTHX);
2848 
2849             /* retry */
2850             if (ticks.ft_i64 > endtime)
2851                 endtime = ticks.ft_i64;
2852 
2853             continue;
2854         }
2855         else {
2856            /* Not timeout or message - one of handles is ready */
2857            break;
2858         }
2859     }
2860     /* If we are past the end say zero */
2861     if (!ticks.ft_i64 || ticks.ft_i64 > endtime)
2862         return 0;
2863     /* compute time left to wait */
2864     ticks.ft_i64 = endtime - ticks.ft_i64;
2865     /* if more ms than DWORD, then return max DWORD */
2866     return ticks.ft_i64 <= UINT_MAX ? (DWORD)ticks.ft_i64 : UINT_MAX;
2867 }
2868 
2869 int
win32_internal_wait(pTHX_ int * status,DWORD timeout)2870 win32_internal_wait(pTHX_ int *status, DWORD timeout)
2871 {
2872     /* XXX this wait emulation only knows about processes
2873      * spawned via win32_spawnvp(P_NOWAIT, ...).
2874      */
2875     int i, retval;
2876     DWORD exitcode, waitcode;
2877 
2878 #ifdef USE_ITHREADS
2879     if (w32_num_pseudo_children) {
2880         win32_msgwait(aTHX_ w32_num_pseudo_children, w32_pseudo_child_handles,
2881                       timeout, &waitcode);
2882         /* Time out here if there are no other children to wait for. */
2883         if (waitcode == WAIT_TIMEOUT) {
2884             if (!w32_num_children) {
2885                 return 0;
2886             }
2887         }
2888         else if (waitcode != WAIT_FAILED) {
2889             if (waitcode >= WAIT_ABANDONED_0
2890                 && waitcode < WAIT_ABANDONED_0 + w32_num_pseudo_children)
2891                 i = waitcode - WAIT_ABANDONED_0;
2892             else
2893                 i = waitcode - WAIT_OBJECT_0;
2894             if (GetExitCodeThread(w32_pseudo_child_handles[i], &exitcode)) {
2895                 *status = (int)(((U8) exitcode) << 8);
2896                 retval = (int)w32_pseudo_child_pids[i];
2897                 remove_dead_pseudo_process(i);
2898                 return -retval;
2899             }
2900         }
2901     }
2902 #endif
2903 
2904     if (!w32_num_children) {
2905         errno = ECHILD;
2906         return -1;
2907     }
2908 
2909     /* if a child exists, wait for it to die */
2910     win32_msgwait(aTHX_ w32_num_children, w32_child_handles, timeout, &waitcode);
2911     if (waitcode == WAIT_TIMEOUT) {
2912         return 0;
2913     }
2914     if (waitcode != WAIT_FAILED) {
2915         if (waitcode >= WAIT_ABANDONED_0
2916             && waitcode < WAIT_ABANDONED_0 + w32_num_children)
2917             i = waitcode - WAIT_ABANDONED_0;
2918         else
2919             i = waitcode - WAIT_OBJECT_0;
2920         if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
2921             *status = (int)(((U8) exitcode) << 8);
2922             retval = (int)w32_child_pids[i];
2923             remove_dead_process(i);
2924             return retval;
2925         }
2926     }
2927 
2928     errno = GetLastError();
2929     return -1;
2930 }
2931 
2932 DllExport int
win32_waitpid(int pid,int * status,int flags)2933 win32_waitpid(int pid, int *status, int flags)
2934 {
2935     dTHX;
2936     DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE;
2937     int retval = -1;
2938     long child;
2939     if (pid == -1)				/* XXX threadid == 1 ? */
2940         return win32_internal_wait(aTHX_ status, timeout);
2941 #ifdef USE_ITHREADS
2942     else if (pid < 0) {
2943         child = find_pseudo_pid(aTHX_ -pid);
2944         if (child >= 0) {
2945             HANDLE hThread = w32_pseudo_child_handles[child];
2946             DWORD waitcode;
2947             win32_msgwait(aTHX_ 1, &hThread, timeout, &waitcode);
2948             if (waitcode == WAIT_TIMEOUT) {
2949                 return 0;
2950             }
2951             else if (waitcode == WAIT_OBJECT_0) {
2952                 if (GetExitCodeThread(hThread, &waitcode)) {
2953                     *status = (int)(((U8) waitcode) << 8);
2954                     retval = (int)w32_pseudo_child_pids[child];
2955                     remove_dead_pseudo_process(child);
2956                     return -retval;
2957                 }
2958             }
2959             else
2960                 errno = ECHILD;
2961         }
2962     }
2963 #endif
2964     else {
2965         HANDLE hProcess;
2966         DWORD waitcode;
2967         child = find_pid(aTHX_ pid);
2968         if (child >= 0) {
2969             hProcess = w32_child_handles[child];
2970             win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2971             if (waitcode == WAIT_TIMEOUT) {
2972                 return 0;
2973             }
2974             else if (waitcode == WAIT_OBJECT_0) {
2975                 if (GetExitCodeProcess(hProcess, &waitcode)) {
2976                     *status = (int)(((U8) waitcode) << 8);
2977                     retval = (int)w32_child_pids[child];
2978                     remove_dead_process(child);
2979                     return retval;
2980                 }
2981             }
2982             else
2983                 errno = ECHILD;
2984         }
2985         else {
2986             hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
2987             if (hProcess) {
2988                 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2989                 if (waitcode == WAIT_TIMEOUT) {
2990                     CloseHandle(hProcess);
2991                     return 0;
2992                 }
2993                 else if (waitcode == WAIT_OBJECT_0) {
2994                     if (GetExitCodeProcess(hProcess, &waitcode)) {
2995                         *status = (int)(((U8) waitcode) << 8);
2996                         CloseHandle(hProcess);
2997                         return pid;
2998                     }
2999                 }
3000                 CloseHandle(hProcess);
3001             }
3002             else
3003                 errno = ECHILD;
3004         }
3005     }
3006     return retval >= 0 ? pid : retval;
3007 }
3008 
3009 DllExport int
win32_wait(int * status)3010 win32_wait(int *status)
3011 {
3012     dTHX;
3013     return win32_internal_wait(aTHX_ status, INFINITE);
3014 }
3015 
3016 DllExport unsigned int
win32_sleep(unsigned int t)3017 win32_sleep(unsigned int t)
3018 {
3019     dTHX;
3020     /* Win32 times are in ms so *1000 in and /1000 out */
3021     if (t > UINT_MAX / 1000) {
3022         Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
3023                         "sleep(%lu) too large", t);
3024     }
3025     return win32_msgwait(aTHX_ 0, NULL, t * 1000, NULL) / 1000;
3026 }
3027 
3028 DllExport int
win32_pause(void)3029 win32_pause(void)
3030 {
3031     dTHX;
3032     win32_msgwait(aTHX_ 0, NULL, INFINITE, NULL);
3033     return -1;
3034 }
3035 
3036 DllExport unsigned int
win32_alarm(unsigned int sec)3037 win32_alarm(unsigned int sec)
3038 {
3039     /*
3040      * the 'obvious' implementation is SetTimer() with a callback
3041      * which does whatever receiving SIGALRM would do
3042      * we cannot use SIGALRM even via raise() as it is not
3043      * one of the supported codes in <signal.h>
3044      */
3045     dTHX;
3046 
3047     if (w32_message_hwnd == INVALID_HANDLE_VALUE)
3048         w32_message_hwnd = win32_create_message_window();
3049 
3050     if (sec) {
3051         if (w32_message_hwnd == NULL)
3052             w32_timerid = SetTimer(NULL, w32_timerid, sec*1000, NULL);
3053         else {
3054             w32_timerid = 1;
3055             SetTimer(w32_message_hwnd, w32_timerid, sec*1000, NULL);
3056         }
3057     }
3058     else {
3059         if (w32_timerid) {
3060             KillTimer(w32_message_hwnd, w32_timerid);
3061             w32_timerid = 0;
3062         }
3063     }
3064     return 0;
3065 }
3066 
3067 extern char *	des_fcrypt(const char *txt, const char *salt, char *cbuf);
3068 
3069 DllExport char *
win32_crypt(const char * txt,const char * salt)3070 win32_crypt(const char *txt, const char *salt)
3071 {
3072     dTHX;
3073     return des_fcrypt(txt, salt, w32_crypt_buffer);
3074 }
3075 
3076 /* simulate flock by locking a range on the file */
3077 
3078 #define LK_LEN		0xffff0000
3079 
3080 DllExport int
win32_flock(int fd,int oper)3081 win32_flock(int fd, int oper)
3082 {
3083     OVERLAPPED o;
3084     int i = -1;
3085     HANDLE fh;
3086 
3087     fh = (HANDLE)_get_osfhandle(fd);
3088     if (fh == (HANDLE)-1)  /* _get_osfhandle() already sets errno to EBADF */
3089         return -1;
3090 
3091     memset(&o, 0, sizeof(o));
3092 
3093     switch(oper) {
3094     case LOCK_SH:		/* shared lock */
3095         if (LockFileEx(fh, 0, 0, LK_LEN, 0, &o))
3096             i = 0;
3097         break;
3098     case LOCK_EX:		/* exclusive lock */
3099         if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o))
3100             i = 0;
3101         break;
3102     case LOCK_SH|LOCK_NB:	/* non-blocking shared lock */
3103         if (LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o))
3104             i = 0;
3105         break;
3106     case LOCK_EX|LOCK_NB:	/* non-blocking exclusive lock */
3107         if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
3108                        0, LK_LEN, 0, &o))
3109             i = 0;
3110         break;
3111     case LOCK_UN:		/* unlock lock */
3112         if (UnlockFileEx(fh, 0, LK_LEN, 0, &o))
3113             i = 0;
3114         break;
3115     default:			/* unknown */
3116         errno = EINVAL;
3117         return -1;
3118     }
3119     if (i == -1) {
3120         if (GetLastError() == ERROR_LOCK_VIOLATION)
3121             errno = EWOULDBLOCK;
3122         else
3123             errno = EINVAL;
3124     }
3125     return i;
3126 }
3127 
3128 #undef LK_LEN
3129 
3130 extern int convert_wsa_error_to_errno(int wsaerr); /* in win32sck.c */
3131 
3132 /* Get the errno value corresponding to the given err. This function is not
3133  * intended to handle conversion of general GetLastError() codes. It only exists
3134  * to translate Windows sockets error codes from WSAGetLastError(). Such codes
3135  * used to be assigned to errno/$! in earlier versions of perl; this function is
3136  * used to catch any old Perl code which is still trying to assign such values
3137  * to $! and convert them to errno values instead.
3138  */
3139 int
win32_get_errno(int err)3140 win32_get_errno(int err)
3141 {
3142     return convert_wsa_error_to_errno(err);
3143 }
3144 
3145 /*
3146  *  redirected io subsystem for all XS modules
3147  *
3148  */
3149 
3150 DllExport int *
win32_errno(void)3151 win32_errno(void)
3152 {
3153     return (&errno);
3154 }
3155 
3156 DllExport char ***
win32_environ(void)3157 win32_environ(void)
3158 {
3159     return (&(_environ));
3160 }
3161 
3162 /* the rest are the remapped stdio routines */
3163 DllExport FILE *
win32_stderr(void)3164 win32_stderr(void)
3165 {
3166     return (stderr);
3167 }
3168 
3169 DllExport FILE *
win32_stdin(void)3170 win32_stdin(void)
3171 {
3172     return (stdin);
3173 }
3174 
3175 DllExport FILE *
win32_stdout(void)3176 win32_stdout(void)
3177 {
3178     return (stdout);
3179 }
3180 
3181 DllExport int
win32_ferror(FILE * fp)3182 win32_ferror(FILE *fp)
3183 {
3184     return (ferror(fp));
3185 }
3186 
3187 
3188 DllExport int
win32_feof(FILE * fp)3189 win32_feof(FILE *fp)
3190 {
3191     return (feof(fp));
3192 }
3193 
3194 #ifdef ERRNO_HAS_POSIX_SUPPLEMENT
3195 extern int convert_errno_to_wsa_error(int err); /* in win32sck.c */
3196 #endif
3197 
3198 /*
3199  * Since the errors returned by the socket error function
3200  * WSAGetLastError() are not known by the library routine strerror
3201  * we have to roll our own to cover the case of socket errors
3202  * that could not be converted to regular errno values by
3203  * get_last_socket_error() in win32/win32sck.c.
3204  */
3205 
3206 DllExport char *
win32_strerror(int e)3207 win32_strerror(int e)
3208 {
3209 #if !defined __MINGW32__      /* compiler intolerance */
3210     extern int sys_nerr;
3211 #endif
3212 
3213     if (e < 0 || e > sys_nerr) {
3214         dTHXa(NULL);
3215         if (e < 0)
3216             e = GetLastError();
3217 #ifdef ERRNO_HAS_POSIX_SUPPLEMENT
3218         /* VC10+ and some MinGW/gcc-4.8+ define a "POSIX supplement" of errno
3219          * values ranging from EADDRINUSE (100) to EWOULDBLOCK (140), but
3220          * sys_nerr is still 43 and strerror() returns "Unknown error" for them.
3221          * We must therefore still roll our own messages for these codes, and
3222          * additionally map them to corresponding Windows (sockets) error codes
3223          * first to avoid getting the wrong system message.
3224          */
3225         else if (inRANGE(e, EADDRINUSE, EWOULDBLOCK)) {
3226             e = convert_errno_to_wsa_error(e);
3227         }
3228 #endif
3229 
3230         aTHXa(PERL_GET_THX);
3231         if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
3232                          |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0,
3233                           w32_strerror_buffer, sizeof(w32_strerror_buffer),
3234                           NULL) == 0)
3235         {
3236             strcpy(w32_strerror_buffer, "Unknown Error");
3237         }
3238         return w32_strerror_buffer;
3239     }
3240 #undef strerror
3241     return strerror(e);
3242 #define strerror win32_strerror
3243 }
3244 
3245 DllExport void
win32_str_os_error(void * sv,DWORD dwErr)3246 win32_str_os_error(void *sv, DWORD dwErr)
3247 {
3248     DWORD dwLen;
3249     char *sMsg;
3250     dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
3251                           |FORMAT_MESSAGE_IGNORE_INSERTS
3252                           |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
3253                            dwErr, 0, (char *)&sMsg, 1, NULL);
3254     /* strip trailing whitespace and period */
3255     if (0 < dwLen) {
3256         do {
3257             --dwLen;	/* dwLen doesn't include trailing null */
3258         } while (0 < dwLen && isSPACE(sMsg[dwLen]));
3259         if ('.' != sMsg[dwLen])
3260             dwLen++;
3261         sMsg[dwLen] = '\0';
3262     }
3263     if (0 == dwLen) {
3264         sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
3265         if (sMsg)
3266             dwLen = sprintf(sMsg,
3267                             "Unknown error #0x%lX (lookup 0x%lX)",
3268                             dwErr, GetLastError());
3269     }
3270     if (sMsg) {
3271         dTHX;
3272         sv_setpvn((SV*)sv, sMsg, dwLen);
3273         LocalFree(sMsg);
3274     }
3275 }
3276 
3277 DllExport int
win32_fprintf(FILE * fp,const char * format,...)3278 win32_fprintf(FILE *fp, const char *format, ...)
3279 {
3280     va_list marker;
3281     va_start(marker, format);     /* Initialize variable arguments. */
3282 
3283     return (vfprintf(fp, format, marker));
3284 }
3285 
3286 DllExport int
win32_printf(const char * format,...)3287 win32_printf(const char *format, ...)
3288 {
3289     va_list marker;
3290     va_start(marker, format);     /* Initialize variable arguments. */
3291 
3292     return (vprintf(format, marker));
3293 }
3294 
3295 DllExport int
win32_vfprintf(FILE * fp,const char * format,va_list args)3296 win32_vfprintf(FILE *fp, const char *format, va_list args)
3297 {
3298     return (vfprintf(fp, format, args));
3299 }
3300 
3301 DllExport int
win32_vprintf(const char * format,va_list args)3302 win32_vprintf(const char *format, va_list args)
3303 {
3304     return (vprintf(format, args));
3305 }
3306 
3307 DllExport size_t
win32_fread(void * buf,size_t size,size_t count,FILE * fp)3308 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
3309 {
3310     return fread(buf, size, count, fp);
3311 }
3312 
3313 DllExport size_t
win32_fwrite(const void * buf,size_t size,size_t count,FILE * fp)3314 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
3315 {
3316     return fwrite(buf, size, count, fp);
3317 }
3318 
3319 #define MODE_SIZE 10
3320 
3321 DllExport FILE *
win32_fopen(const char * filename,const char * mode)3322 win32_fopen(const char *filename, const char *mode)
3323 {
3324     dTHXa(NULL);
3325     FILE *f;
3326 
3327     if (!*filename)
3328         return NULL;
3329 
3330     if (stricmp(filename, "/dev/null")==0)
3331         filename = "NUL";
3332 
3333     aTHXa(PERL_GET_THX);
3334     f = fopen(PerlDir_mapA(filename), mode);
3335     /* avoid buffering headaches for child processes */
3336     if (f && *mode == 'a')
3337         win32_fseek(f, 0, SEEK_END);
3338     return f;
3339 }
3340 
3341 DllExport FILE *
win32_fdopen(int handle,const char * mode)3342 win32_fdopen(int handle, const char *mode)
3343 {
3344     FILE *f;
3345     f = fdopen(handle, (char *) mode);
3346     /* avoid buffering headaches for child processes */
3347     if (f && *mode == 'a')
3348         win32_fseek(f, 0, SEEK_END);
3349     return f;
3350 }
3351 
3352 DllExport FILE *
win32_freopen(const char * path,const char * mode,FILE * stream)3353 win32_freopen(const char *path, const char *mode, FILE *stream)
3354 {
3355     dTHXa(NULL);
3356     if (stricmp(path, "/dev/null")==0)
3357         path = "NUL";
3358 
3359     aTHXa(PERL_GET_THX);
3360     return freopen(PerlDir_mapA(path), mode, stream);
3361 }
3362 
3363 DllExport int
win32_fclose(FILE * pf)3364 win32_fclose(FILE *pf)
3365 {
3366     return fclose(pf);
3367 }
3368 
3369 DllExport int
win32_fputs(const char * s,FILE * pf)3370 win32_fputs(const char *s,FILE *pf)
3371 {
3372     return fputs(s, pf);
3373 }
3374 
3375 DllExport int
win32_fputc(int c,FILE * pf)3376 win32_fputc(int c,FILE *pf)
3377 {
3378     return fputc(c,pf);
3379 }
3380 
3381 DllExport int
win32_ungetc(int c,FILE * pf)3382 win32_ungetc(int c,FILE *pf)
3383 {
3384     return ungetc(c,pf);
3385 }
3386 
3387 DllExport int
win32_getc(FILE * pf)3388 win32_getc(FILE *pf)
3389 {
3390     return getc(pf);
3391 }
3392 
3393 DllExport int
win32_fileno(FILE * pf)3394 win32_fileno(FILE *pf)
3395 {
3396     return fileno(pf);
3397 }
3398 
3399 DllExport void
win32_clearerr(FILE * pf)3400 win32_clearerr(FILE *pf)
3401 {
3402     clearerr(pf);
3403     return;
3404 }
3405 
3406 DllExport int
win32_fflush(FILE * pf)3407 win32_fflush(FILE *pf)
3408 {
3409     return fflush(pf);
3410 }
3411 
3412 DllExport Off_t
win32_ftell(FILE * pf)3413 win32_ftell(FILE *pf)
3414 {
3415     fpos_t pos;
3416     if (fgetpos(pf, &pos))
3417         return -1;
3418     return (Off_t)pos;
3419 }
3420 
3421 DllExport int
win32_fseek(FILE * pf,Off_t offset,int origin)3422 win32_fseek(FILE *pf, Off_t offset,int origin)
3423 {
3424     fpos_t pos;
3425     switch (origin) {
3426     case SEEK_CUR:
3427         if (fgetpos(pf, &pos))
3428             return -1;
3429         offset += pos;
3430         break;
3431     case SEEK_END:
3432         fseek(pf, 0, SEEK_END);
3433         pos = _telli64(fileno(pf));
3434         offset += pos;
3435         break;
3436     case SEEK_SET:
3437         break;
3438     default:
3439         errno = EINVAL;
3440         return -1;
3441     }
3442     return fsetpos(pf, &offset);
3443 }
3444 
3445 DllExport int
win32_fgetpos(FILE * pf,fpos_t * p)3446 win32_fgetpos(FILE *pf,fpos_t *p)
3447 {
3448     return fgetpos(pf, p);
3449 }
3450 
3451 DllExport int
win32_fsetpos(FILE * pf,const fpos_t * p)3452 win32_fsetpos(FILE *pf,const fpos_t *p)
3453 {
3454     return fsetpos(pf, p);
3455 }
3456 
3457 DllExport void
win32_rewind(FILE * pf)3458 win32_rewind(FILE *pf)
3459 {
3460     rewind(pf);
3461     return;
3462 }
3463 
3464 DllExport int
win32_tmpfd(void)3465 win32_tmpfd(void)
3466 {
3467     return win32_tmpfd_mode(0);
3468 }
3469 
3470 DllExport int
win32_tmpfd_mode(int mode)3471 win32_tmpfd_mode(int mode)
3472 {
3473     char prefix[MAX_PATH+1];
3474     char filename[MAX_PATH+1];
3475     DWORD len = GetTempPath(MAX_PATH, prefix);
3476     mode &= ~( O_ACCMODE | O_CREAT | O_EXCL );
3477     mode |= O_RDWR;
3478     if (len && len < MAX_PATH) {
3479         if (GetTempFileName(prefix, "plx", 0, filename)) {
3480             HANDLE fh = CreateFile(filename,
3481                                    DELETE | GENERIC_READ | GENERIC_WRITE,
3482                                    0,
3483                                    NULL,
3484                                    CREATE_ALWAYS,
3485                                    FILE_ATTRIBUTE_NORMAL
3486                                    | FILE_FLAG_DELETE_ON_CLOSE,
3487                                    NULL);
3488             if (fh != INVALID_HANDLE_VALUE) {
3489                 int fd = win32_open_osfhandle((intptr_t)fh, mode);
3490                 if (fd >= 0) {
3491                     PERL_DEB(dTHX;)
3492                     DEBUG_p(PerlIO_printf(Perl_debug_log,
3493                                           "Created tmpfile=%s\n",filename));
3494                     return fd;
3495                 }
3496             }
3497         }
3498     }
3499     return -1;
3500 }
3501 
3502 DllExport FILE*
win32_tmpfile(void)3503 win32_tmpfile(void)
3504 {
3505     int fd = win32_tmpfd();
3506     if (fd >= 0)
3507         return win32_fdopen(fd, "w+b");
3508     return NULL;
3509 }
3510 
3511 DllExport void
win32_abort(void)3512 win32_abort(void)
3513 {
3514     abort();
3515     return;
3516 }
3517 
3518 DllExport int
win32_fstat(int fd,Stat_t * sbufptr)3519 win32_fstat(int fd, Stat_t *sbufptr)
3520 {
3521     HANDLE handle = (HANDLE)win32_get_osfhandle(fd);
3522 
3523     return win32_stat_low(handle, NULL, 0, sbufptr, 0);
3524 }
3525 
3526 DllExport int
win32_pipe(int * pfd,unsigned int size,int mode)3527 win32_pipe(int *pfd, unsigned int size, int mode)
3528 {
3529     return _pipe(pfd, size, mode);
3530 }
3531 
3532 DllExport PerlIO*
win32_popenlist(const char * mode,IV narg,SV ** args)3533 win32_popenlist(const char *mode, IV narg, SV **args)
3534 {
3535     if (get_shell() < 0)
3536         return NULL;
3537 
3538     return do_popen(mode, NULL, narg, args);
3539 }
3540 
3541 STATIC PerlIO*
do_popen(const char * mode,const char * command,IV narg,SV ** args)3542 do_popen(const char *mode, const char *command, IV narg, SV **args) {
3543     int p[2];
3544     int handles[3];
3545     int parent, child;
3546     int stdfd;
3547     int ourmode;
3548     int childpid;
3549     DWORD nhandle;
3550     int lock_held = 0;
3551     const char **args_pvs = NULL;
3552 
3553     /* establish which ends read and write */
3554     if (strchr(mode,'w')) {
3555         stdfd = 0;		/* stdin */
3556         parent = 1;
3557         child = 0;
3558         nhandle = STD_INPUT_HANDLE;
3559     }
3560     else if (strchr(mode,'r')) {
3561         stdfd = 1;		/* stdout */
3562         parent = 0;
3563         child = 1;
3564         nhandle = STD_OUTPUT_HANDLE;
3565     }
3566     else
3567         return NULL;
3568 
3569     /* set the correct mode */
3570     if (strchr(mode,'b'))
3571         ourmode = O_BINARY;
3572     else if (strchr(mode,'t'))
3573         ourmode = O_TEXT;
3574     else
3575         ourmode = _fmode & (O_TEXT | O_BINARY);
3576 
3577     /* the child doesn't inherit handles */
3578     ourmode |= O_NOINHERIT;
3579 
3580     if (win32_pipe(p, 512, ourmode) == -1)
3581         return NULL;
3582 
3583     /* Previously this code redirected stdin/out temporarily so the
3584        child process inherited those handles, this caused race
3585        conditions when another thread was writing/reading those
3586        handles.
3587 
3588        To avoid that we just feed the handles to CreateProcess() so
3589        the handles are redirected only in the child.
3590      */
3591     handles[child] = p[child];
3592     handles[parent] = -1;
3593     handles[2] = -1;
3594 
3595     /* CreateProcess() requires inheritable handles */
3596     if (!SetHandleInformation((HANDLE)_get_osfhandle(p[child]), HANDLE_FLAG_INHERIT,
3597                               HANDLE_FLAG_INHERIT)) {
3598         goto cleanup;
3599     }
3600 
3601     /* start the child */
3602     {
3603         dTHX;
3604 
3605         if (command) {
3606             if ((childpid = do_spawn2_handles(aTHX_ command, EXECF_SPAWN_NOWAIT, handles)) == -1)
3607                 goto cleanup;
3608 
3609         }
3610         else {
3611             int i;
3612             const char *exe_name;
3613 
3614             Newx(args_pvs, narg + 1 + w32_perlshell_items, const char *);
3615             SAVEFREEPV(args_pvs);
3616             for (i = 0; i < narg; ++i)
3617                 args_pvs[i] = SvPV_nolen(args[i]);
3618             args_pvs[i] = NULL;
3619             exe_name = qualified_path(args_pvs[0], TRUE);
3620             if (!exe_name)
3621                 /* let CreateProcess() try to find it instead */
3622                 exe_name = args_pvs[0];
3623 
3624             if ((childpid = do_spawnvp_handles(P_NOWAIT, exe_name, args_pvs, handles)) == -1) {
3625                 goto cleanup;
3626             }
3627         }
3628 
3629         win32_close(p[child]);
3630 
3631         sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
3632 
3633         /* set process id so that it can be returned by perl's open() */
3634         PL_forkprocess = childpid;
3635     }
3636 
3637     /* we have an fd, return a file stream */
3638     return (PerlIO_fdopen(p[parent], (char *)mode));
3639 
3640 cleanup:
3641     /* we don't need to check for errors here */
3642     win32_close(p[0]);
3643     win32_close(p[1]);
3644 
3645     return (NULL);
3646 }
3647 
3648 /*
3649  * a popen() clone that respects PERL5SHELL
3650  *
3651  * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
3652  */
3653 
3654 DllExport PerlIO*
win32_popen(const char * command,const char * mode)3655 win32_popen(const char *command, const char *mode)
3656 {
3657 #ifdef USE_RTL_POPEN
3658     return _popen(command, mode);
3659 #else
3660     return do_popen(mode, command, 0, NULL);
3661 #endif /* USE_RTL_POPEN */
3662 }
3663 
3664 /*
3665  * pclose() clone
3666  */
3667 
3668 DllExport int
win32_pclose(PerlIO * pf)3669 win32_pclose(PerlIO *pf)
3670 {
3671 #ifdef USE_RTL_POPEN
3672     return _pclose(pf);
3673 #else
3674     dTHX;
3675     int childpid, status;
3676     SV *sv;
3677 
3678     sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE);
3679 
3680     if (SvIOK(sv))
3681         childpid = SvIVX(sv);
3682     else
3683         childpid = 0;
3684 
3685     if (!childpid) {
3686         errno = EBADF;
3687         return -1;
3688     }
3689 
3690 #ifdef USE_PERLIO
3691     PerlIO_close(pf);
3692 #else
3693     fclose(pf);
3694 #endif
3695     SvIVX(sv) = 0;
3696 
3697     if (win32_waitpid(childpid, &status, 0) == -1)
3698         return -1;
3699 
3700     return status;
3701 
3702 #endif /* USE_RTL_POPEN */
3703 }
3704 
3705 DllExport int
win32_link(const char * oldname,const char * newname)3706 win32_link(const char *oldname, const char *newname)
3707 {
3708     dTHXa(NULL);
3709     WCHAR wOldName[MAX_PATH+1];
3710     WCHAR wNewName[MAX_PATH+1];
3711 
3712     if (MultiByteToWideChar(CP_ACP, 0, oldname, -1, wOldName, MAX_PATH+1) &&
3713         MultiByteToWideChar(CP_ACP, 0, newname, -1, wNewName, MAX_PATH+1) &&
3714         ((aTHXa(PERL_GET_THX)), wcscpy(wOldName, PerlDir_mapW(wOldName)),
3715         CreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
3716     {
3717         return 0;
3718     }
3719     translate_to_errno();
3720     return -1;
3721 }
3722 
3723 typedef BOOLEAN (__stdcall *pCreateSymbolicLinkA_t)(LPCSTR, LPCSTR, DWORD);
3724 
3725 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
3726 #  define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
3727 #endif
3728 
3729 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
3730 #  define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
3731 #endif
3732 
3733 DllExport int
win32_symlink(const char * oldfile,const char * newfile)3734 win32_symlink(const char *oldfile, const char *newfile)
3735 {
3736     dTHX;
3737     size_t oldfile_len = strlen(oldfile);
3738     pCreateSymbolicLinkA_t pCreateSymbolicLinkA =
3739         (pCreateSymbolicLinkA_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkA");
3740     DWORD create_flags = 0;
3741 
3742     /* this flag can be used only on Windows 10 1703 or newer */
3743     if (g_osver.dwMajorVersion > 10 ||
3744         (g_osver.dwMajorVersion == 10 &&
3745          (g_osver.dwMinorVersion > 0 || g_osver.dwBuildNumber > 15063)))
3746     {
3747         create_flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
3748     }
3749 
3750     if (!pCreateSymbolicLinkA) {
3751         errno = ENOSYS;
3752         return -1;
3753     }
3754 
3755     /* oldfile might be relative and we don't want to change that,
3756        so don't map that.
3757     */
3758     newfile = PerlDir_mapA(newfile);
3759 
3760     if (strchr(oldfile, '/')) {
3761         /* Win32 (or perhaps NTFS) won't follow symlinks containing
3762            /, so replace any with \\
3763         */
3764         char *temp = savepv(oldfile);
3765         SAVEFREEPV(temp);
3766         char *p = temp;
3767         while (*p) {
3768             if (*p == '/') {
3769                 *p = '\\';
3770             }
3771             ++p;
3772         }
3773         *p = 0;
3774         oldfile = temp;
3775         oldfile_len = p - temp;
3776     }
3777 
3778     /* are we linking to a directory?
3779        CreateSymlinkA() needs to know if the target is a directory,
3780        If it looks like a directory name:
3781         - ends in slash
3782         - is just . or ..
3783         - ends in /. or /.. (with either slash)
3784         - is a simple drive letter
3785        assume it's a directory.
3786 
3787        Otherwise if the oldfile is relative we need to make a relative path
3788        based on the newfile to check if the target is a directory.
3789     */
3790     if ((oldfile_len >= 1 && isSLASH(oldfile[oldfile_len-1])) ||
3791         strEQ(oldfile, "..") ||
3792         strEQ(oldfile, ".") ||
3793         (isSLASH(oldfile[oldfile_len-2]) && oldfile[oldfile_len-1] == '.') ||
3794         strEQ(oldfile+oldfile_len-3, "\\..") ||
3795         (oldfile_len == 2 && oldfile[1] == ':')) {
3796         create_flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
3797     }
3798     else {
3799         DWORD dest_attr;
3800         const char *dest_path = oldfile;
3801         char szTargetName[MAX_PATH+1];
3802 
3803         if (oldfile_len >= 3 && oldfile[1] == ':') {
3804             /* relative to current directory on a drive, or absolute */
3805             /* dest_path = oldfile; already done */
3806         }
3807         else if (oldfile[0] != '\\') {
3808             size_t newfile_len = strlen(newfile);
3809             const char *last_slash = strrchr(newfile, '/');
3810             const char *last_bslash = strrchr(newfile, '\\');
3811             const char *end_dir = last_slash && last_bslash
3812                 ? ( last_slash > last_bslash ? last_slash : last_bslash)
3813                 : last_slash ? last_slash : last_bslash ? last_bslash : NULL;
3814 
3815             if (end_dir) {
3816                 if ((end_dir - newfile + 1) + oldfile_len > MAX_PATH) {
3817                     /* too long */
3818                     errno = EINVAL;
3819                     return -1;
3820                 }
3821 
3822                 memcpy(szTargetName, newfile, end_dir - newfile + 1);
3823                 strcpy(szTargetName + (end_dir - newfile + 1), oldfile);
3824                 dest_path = szTargetName;
3825             }
3826             else {
3827                 /* newpath is just a filename */
3828                 /* dest_path = oldfile; */
3829             }
3830         }
3831 
3832         dest_attr = GetFileAttributes(dest_path);
3833         if (dest_attr != (DWORD)-1 && (dest_attr & FILE_ATTRIBUTE_DIRECTORY)) {
3834             create_flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
3835         }
3836     }
3837 
3838     if (!pCreateSymbolicLinkA(newfile, oldfile, create_flags)) {
3839         translate_to_errno();
3840         return -1;
3841     }
3842 
3843     return 0;
3844 }
3845 
3846 DllExport int
win32_rename(const char * oname,const char * newname)3847 win32_rename(const char *oname, const char *newname)
3848 {
3849     char szOldName[MAX_PATH+1];
3850     BOOL bResult;
3851     DWORD dwFlags = MOVEFILE_COPY_ALLOWED;
3852     dTHX;
3853 
3854     if (stricmp(newname, oname))
3855         dwFlags |= MOVEFILE_REPLACE_EXISTING;
3856     strcpy(szOldName, PerlDir_mapA(oname));
3857 
3858     bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags);
3859     if (!bResult) {
3860         DWORD err = GetLastError();
3861         switch (err) {
3862         case ERROR_BAD_NET_NAME:
3863         case ERROR_BAD_NETPATH:
3864         case ERROR_BAD_PATHNAME:
3865         case ERROR_FILE_NOT_FOUND:
3866         case ERROR_FILENAME_EXCED_RANGE:
3867         case ERROR_INVALID_DRIVE:
3868         case ERROR_NO_MORE_FILES:
3869         case ERROR_PATH_NOT_FOUND:
3870             errno = ENOENT;
3871             break;
3872         case ERROR_DISK_FULL:
3873             errno = ENOSPC;
3874             break;
3875         case ERROR_NOT_ENOUGH_QUOTA:
3876             errno = EDQUOT;
3877             break;
3878         default:
3879             errno = EACCES;
3880             break;
3881         }
3882         return -1;
3883     }
3884     return 0;
3885 }
3886 
3887 DllExport int
win32_setmode(int fd,int mode)3888 win32_setmode(int fd, int mode)
3889 {
3890     return setmode(fd, mode);
3891 }
3892 
3893 DllExport int
win32_chsize(int fd,Off_t size)3894 win32_chsize(int fd, Off_t size)
3895 {
3896     int retval = 0;
3897     Off_t cur, end, extend;
3898 
3899     cur = win32_tell(fd);
3900     if (cur < 0)
3901         return -1;
3902     end = win32_lseek(fd, 0, SEEK_END);
3903     if (end < 0)
3904         return -1;
3905     extend = size - end;
3906     if (extend == 0) {
3907         /* do nothing */
3908     }
3909     else if (extend > 0) {
3910         /* must grow the file, padding with nulls */
3911         char b[4096];
3912         int oldmode = win32_setmode(fd, O_BINARY);
3913         size_t count;
3914         memset(b, '\0', sizeof(b));
3915         do {
3916             count = extend >= sizeof(b) ? sizeof(b) : (size_t)extend;
3917             count = win32_write(fd, b, count);
3918             if ((int)count < 0) {
3919                 retval = -1;
3920                 break;
3921             }
3922         } while ((extend -= count) > 0);
3923         win32_setmode(fd, oldmode);
3924     }
3925     else {
3926         /* shrink the file */
3927         win32_lseek(fd, size, SEEK_SET);
3928         if (!SetEndOfFile((HANDLE)_get_osfhandle(fd))) {
3929             errno = EACCES;
3930             retval = -1;
3931         }
3932     }
3933     win32_lseek(fd, cur, SEEK_SET);
3934     return retval;
3935 }
3936 
3937 DllExport Off_t
win32_lseek(int fd,Off_t offset,int origin)3938 win32_lseek(int fd, Off_t offset, int origin)
3939 {
3940     return _lseeki64(fd, offset, origin);
3941 }
3942 
3943 DllExport Off_t
win32_tell(int fd)3944 win32_tell(int fd)
3945 {
3946     return _telli64(fd);
3947 }
3948 
3949 DllExport int
win32_open(const char * path,int flag,...)3950 win32_open(const char *path, int flag, ...)
3951 {
3952     dTHXa(NULL);
3953     va_list ap;
3954     int pmode;
3955 
3956     va_start(ap, flag);
3957     pmode = va_arg(ap, int);
3958     va_end(ap);
3959 
3960     if (stricmp(path, "/dev/null")==0)
3961         path = "NUL";
3962 
3963     aTHXa(PERL_GET_THX);
3964     return open(PerlDir_mapA(path), flag, pmode);
3965 }
3966 
3967 DllExport int
win32_close(int fd)3968 win32_close(int fd)
3969 {
3970     return _close(fd);
3971 }
3972 
3973 DllExport int
win32_eof(int fd)3974 win32_eof(int fd)
3975 {
3976     return eof(fd);
3977 }
3978 
3979 DllExport int
win32_isatty(int fd)3980 win32_isatty(int fd)
3981 {
3982     /* The Microsoft isatty() function returns true for *all*
3983      * character mode devices, including "nul".  Our implementation
3984      * should only return true if the handle has a console buffer.
3985      */
3986     DWORD mode;
3987     HANDLE fh = (HANDLE)_get_osfhandle(fd);
3988     if (fh == (HANDLE)-1) {
3989         /* errno is already set to EBADF */
3990         return 0;
3991     }
3992 
3993     if (GetConsoleMode(fh, &mode))
3994         return 1;
3995 
3996     errno = ENOTTY;
3997     return 0;
3998 }
3999 
4000 DllExport int
win32_dup(int fd)4001 win32_dup(int fd)
4002 {
4003     return dup(fd);
4004 }
4005 
4006 DllExport int
win32_dup2(int fd1,int fd2)4007 win32_dup2(int fd1,int fd2)
4008 {
4009     return dup2(fd1,fd2);
4010 }
4011 
4012 static int
win32_read_console(int fd,U8 * buf,unsigned int cnt)4013 win32_read_console(int fd, U8 *buf, unsigned int cnt)
4014 {
4015     /* This function is a workaround for a bug in Windows:
4016      * https://github.com/microsoft/terminal/issues/4551
4017      * tl;dr: ReadFile() and ReadConsoleA() return garbage when reading
4018      * non-ASCII characters from the console with the 65001 codepage.
4019      */
4020     HANDLE h = (HANDLE)_get_osfhandle(fd);
4021     size_t left_to_read = cnt;
4022     DWORD mode;
4023 
4024     if (h == INVALID_HANDLE_VALUE) {
4025         errno = EBADF;
4026         return -1;
4027     }
4028 
4029     if (!GetConsoleMode(h, &mode)) {
4030         translate_to_errno();
4031         return -1;
4032     }
4033 
4034     while (left_to_read) {
4035         /* The purpose of converted_buf is to preserve partial UTF-8 (or of any
4036          * other multibyte encoding) code points between read() calls. Since
4037          * there's only one console, the buffer is global. It's needed because
4038          * ReadConsoleW() returns a string of UTF-16 code units and its result,
4039          * after conversion to the current console codepage, may not fit in the
4040          * return buffer.
4041          *
4042          * The buffer's size is 8 because it will contain at most two UTF-8 code
4043          * points.
4044          */
4045         static char converted_buf[8];
4046         static size_t converted_buf_len = 0;
4047         WCHAR wbuf[2];
4048         DWORD wbuf_len = 0, chars_read;
4049 
4050         if (converted_buf_len) {
4051             bool newline = 0;
4052             size_t to_write = MIN(converted_buf_len, left_to_read);
4053 
4054             /* Don't read anything if the *first* character is ^Z and
4055              * ENABLE_PROCESSED_INPUT is enabled. On some versions of Windows,
4056              * ReadFile() ignores ENABLE_PROCESSED_INPUT, but apparently it's a
4057              * bug: https://github.com/microsoft/terminal/issues/4958
4058              */
4059             if (left_to_read == cnt && (mode & ENABLE_PROCESSED_INPUT) &&
4060                 converted_buf[0] == 0x1a)
4061                  break;
4062 
4063             /* Are we returning a newline? */
4064             if (memchr(converted_buf, '\n', to_write))
4065                 newline = 1;
4066 
4067             memcpy(buf, converted_buf, to_write);
4068             buf += to_write;
4069 
4070             /* If there's anything left in converted_buf, move it to the
4071              * beginning of the buffer. */
4072             converted_buf_len -= to_write;
4073             if (converted_buf_len)
4074                 memmove(
4075                     converted_buf, converted_buf + to_write, converted_buf_len
4076                 );
4077 
4078             left_to_read -= to_write;
4079 
4080             /* With ENABLE_LINE_INPUT enabled, we stop reading after the first
4081              * newline, otherwise we stop reading after the first character. */
4082             if (!left_to_read || newline || (mode & ENABLE_LINE_INPUT) == 0)
4083                 break;
4084         }
4085 
4086         /* Reading one code unit at a time is inefficient, but since this code
4087          * is used only for the interactive console, that shouldn't matter. */
4088         if (!ReadConsoleW(h, wbuf, 1, &chars_read, 0)) {
4089             translate_to_errno();
4090             return -1;
4091         }
4092         if (!chars_read)
4093             break;
4094 
4095         ++wbuf_len;
4096 
4097         if (wbuf[0] >= 0xD800 && wbuf[0] <= 0xDBFF) {
4098             /* High surrogate, read one more code unit. */
4099             if (!ReadConsoleW(h, wbuf + 1, 1, &chars_read, 0)) {
4100                 translate_to_errno();
4101                 return -1;
4102             }
4103             if (chars_read)
4104                 ++wbuf_len;
4105         }
4106 
4107         converted_buf_len = WideCharToMultiByte(
4108             GetConsoleCP(), 0, wbuf, wbuf_len, converted_buf,
4109             sizeof(converted_buf), NULL, NULL
4110         );
4111         if (!converted_buf_len) {
4112             translate_to_errno();
4113             return -1;
4114         }
4115     }
4116 
4117     return cnt - left_to_read;
4118 }
4119 
4120 
4121 DllExport int
win32_read(int fd,void * buf,unsigned int cnt)4122 win32_read(int fd, void *buf, unsigned int cnt)
4123 {
4124     int ret;
4125     if (UNLIKELY(win32_isatty(fd) && GetConsoleCP() == 65001)) {
4126         MUTEX_LOCK(&win32_read_console_mutex);
4127         ret = win32_read_console(fd, (U8 *)buf, cnt);
4128         MUTEX_UNLOCK(&win32_read_console_mutex);
4129     }
4130     else
4131         ret = read(fd, buf, cnt);
4132 
4133     return ret;
4134 }
4135 
4136 DllExport int
win32_write(int fd,const void * buf,unsigned int cnt)4137 win32_write(int fd, const void *buf, unsigned int cnt)
4138 {
4139     return write(fd, buf, cnt);
4140 }
4141 
4142 DllExport int
win32_mkdir(const char * dir,int mode)4143 win32_mkdir(const char *dir, int mode)
4144 {
4145     dTHX;
4146     return mkdir(PerlDir_mapA(dir)); /* just ignore mode */
4147 }
4148 
4149 DllExport int
win32_rmdir(const char * dir)4150 win32_rmdir(const char *dir)
4151 {
4152     dTHX;
4153     return rmdir(PerlDir_mapA(dir));
4154 }
4155 
4156 DllExport int
win32_chdir(const char * dir)4157 win32_chdir(const char *dir)
4158 {
4159     if (!dir || !*dir) {
4160         errno = ENOENT;
4161         return -1;
4162     }
4163     return chdir(dir);
4164 }
4165 
4166 DllExport  int
win32_access(const char * path,int mode)4167 win32_access(const char *path, int mode)
4168 {
4169     dTHX;
4170     return access(PerlDir_mapA(path), mode);
4171 }
4172 
4173 DllExport  int
win32_chmod(const char * path,int mode)4174 win32_chmod(const char *path, int mode)
4175 {
4176     dTHX;
4177     return chmod(PerlDir_mapA(path), mode);
4178 }
4179 
4180 
4181 static char *
create_command_line(char * cname,STRLEN clen,const char * const * args)4182 create_command_line(char *cname, STRLEN clen, const char * const *args)
4183 {
4184     PERL_DEB(dTHX;)
4185     int index, argc;
4186     char *cmd, *ptr;
4187     const char *arg;
4188     STRLEN len = 0;
4189     bool bat_file = FALSE;
4190     bool cmd_shell = FALSE;
4191     bool dumb_shell = FALSE;
4192     bool extra_quotes = FALSE;
4193     bool quote_next = FALSE;
4194 
4195     if (!cname)
4196         cname = (char*)args[0];
4197 
4198     /* The NT cmd.exe shell has the following peculiarity that needs to be
4199      * worked around.  It strips a leading and trailing dquote when any
4200      * of the following is true:
4201      *    1. the /S switch was used
4202      *    2. there are more than two dquotes
4203      *    3. there is a special character from this set: &<>()@^|
4204      *    4. no whitespace characters within the two dquotes
4205      *    5. string between two dquotes isn't an executable file
4206      * To work around this, we always add a leading and trailing dquote
4207      * to the string, if the first argument is either "cmd.exe" or "cmd",
4208      * and there were at least two or more arguments passed to cmd.exe
4209      * (not including switches).
4210      * XXX the above rules (from "cmd /?") don't seem to be applied
4211      * always, making for the convolutions below :-(
4212      */
4213     if (cname) {
4214         if (!clen)
4215             clen = strlen(cname);
4216 
4217         if (clen > 4
4218             && (stricmp(&cname[clen-4], ".bat") == 0
4219                 || (stricmp(&cname[clen-4], ".cmd") == 0)))
4220         {
4221             bat_file = TRUE;
4222             len += 3;
4223         }
4224         else {
4225             char *exe = strrchr(cname, '/');
4226             char *exe2 = strrchr(cname, '\\');
4227             if (exe2 > exe)
4228                 exe = exe2;
4229             if (exe)
4230                 ++exe;
4231             else
4232                 exe = cname;
4233             if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
4234                 cmd_shell = TRUE;
4235                 len += 3;
4236             }
4237             else if (stricmp(exe, "command.com") == 0
4238                      || stricmp(exe, "command") == 0)
4239             {
4240                 dumb_shell = TRUE;
4241             }
4242         }
4243     }
4244 
4245     DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
4246     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
4247         STRLEN curlen = strlen(arg);
4248         if (!(arg[0] == '"' && arg[curlen-1] == '"'))
4249             len += 2;	/* assume quoting needed (worst case) */
4250         len += curlen + 1;
4251         DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
4252     }
4253     DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
4254 
4255     argc = index;
4256     Newx(cmd, len, char);
4257     ptr = cmd;
4258 
4259     if (bat_file) {
4260         *ptr++ = '"';
4261         extra_quotes = TRUE;
4262     }
4263 
4264     for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
4265         bool do_quote = 0;
4266         STRLEN curlen = strlen(arg);
4267 
4268         /* we want to protect empty arguments and ones with spaces with
4269          * dquotes, but only if they aren't already there */
4270         if (!dumb_shell) {
4271             if (!curlen) {
4272                 do_quote = 1;
4273             }
4274             else if (quote_next) {
4275                 /* see if it really is multiple arguments pretending to
4276                  * be one and force a set of quotes around it */
4277                 if (*find_next_space(arg))
4278                     do_quote = 1;
4279             }
4280             else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
4281                 STRLEN i = 0;
4282                 while (i < curlen) {
4283                     if (isSPACE(arg[i])) {
4284                         do_quote = 1;
4285                     }
4286                     else if (arg[i] == '"') {
4287                         do_quote = 0;
4288                         break;
4289                     }
4290                     i++;
4291                 }
4292             }
4293         }
4294 
4295         if (do_quote)
4296             *ptr++ = '"';
4297 
4298         strcpy(ptr, arg);
4299         ptr += curlen;
4300 
4301         if (do_quote)
4302             *ptr++ = '"';
4303 
4304         if (args[index+1])
4305             *ptr++ = ' ';
4306 
4307         if (!extra_quotes
4308             && cmd_shell
4309             && curlen >= 2
4310             && *arg  == '/'     /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
4311             && stricmp(arg+curlen-2, "/c") == 0)
4312         {
4313             /* is there a next argument? */
4314             if (args[index+1]) {
4315                 /* are there two or more next arguments? */
4316                 if (args[index+2]) {
4317                     *ptr++ = '"';
4318                     extra_quotes = TRUE;
4319                 }
4320                 else {
4321                     /* single argument, force quoting if it has spaces */
4322                     quote_next = TRUE;
4323                 }
4324             }
4325         }
4326     }
4327 
4328     if (extra_quotes)
4329         *ptr++ = '"';
4330 
4331     *ptr = '\0';
4332 
4333     return cmd;
4334 }
4335 
4336 static const char *exe_extensions[] =
4337   {
4338     ".exe", /* this must be first */
4339     ".cmd",
4340     ".bat"
4341   };
4342 
4343 static char *
qualified_path(const char * cmd,bool other_exts)4344 qualified_path(const char *cmd, bool other_exts)
4345 {
4346     char *pathstr;
4347     char *fullcmd, *curfullcmd;
4348     STRLEN cmdlen = 0;
4349     int has_slash = 0;
4350 
4351     if (!cmd)
4352         return NULL;
4353     fullcmd = (char*)cmd;
4354     while (*fullcmd) {
4355         if (*fullcmd == '/' || *fullcmd == '\\')
4356             has_slash++;
4357         fullcmd++;
4358         cmdlen++;
4359     }
4360 
4361     /* look in PATH */
4362     {
4363         dTHX;
4364         pathstr = PerlEnv_getenv("PATH");
4365     }
4366     /* worst case: PATH is a single directory; we need additional space
4367      * to append "/", ".exe" and trailing "\0" */
4368     Newx(fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char);
4369     curfullcmd = fullcmd;
4370 
4371     while (1) {
4372         DWORD res;
4373 
4374         /* start by appending the name to the current prefix */
4375         strcpy(curfullcmd, cmd);
4376         curfullcmd += cmdlen;
4377 
4378         /* if it doesn't end with '.', or has no extension, try adding
4379          * a trailing .exe first */
4380         if (cmd[cmdlen-1] != '.'
4381             && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
4382         {
4383             int i;
4384             /* first extension is .exe */
4385             int ext_limit = other_exts ? C_ARRAY_LENGTH(exe_extensions) : 1;
4386             for (i = 0; i < ext_limit; ++i) {
4387                 strcpy(curfullcmd, exe_extensions[i]);
4388                 res = GetFileAttributes(fullcmd);
4389                 if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
4390                     return fullcmd;
4391             }
4392 
4393             *curfullcmd = '\0';
4394         }
4395 
4396         /* that failed, try the bare name */
4397         res = GetFileAttributes(fullcmd);
4398         if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
4399             return fullcmd;
4400 
4401         /* quit if no other path exists, or if cmd already has path */
4402         if (!pathstr || !*pathstr || has_slash)
4403             break;
4404 
4405         /* skip leading semis */
4406         while (*pathstr == ';')
4407             pathstr++;
4408 
4409         /* build a new prefix from scratch */
4410         curfullcmd = fullcmd;
4411         while (*pathstr && *pathstr != ';') {
4412             if (*pathstr == '"') {	/* foo;"baz;etc";bar */
4413                 pathstr++;		/* skip initial '"' */
4414                 while (*pathstr && *pathstr != '"') {
4415                     *curfullcmd++ = *pathstr++;
4416                 }
4417                 if (*pathstr)
4418                     pathstr++;		/* skip trailing '"' */
4419             }
4420             else {
4421                 *curfullcmd++ = *pathstr++;
4422             }
4423         }
4424         if (*pathstr)
4425             pathstr++;			/* skip trailing semi */
4426         if (curfullcmd > fullcmd	/* append a dir separator */
4427             && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
4428         {
4429             *curfullcmd++ = '\\';
4430         }
4431     }
4432 
4433     Safefree(fullcmd);
4434     return NULL;
4435 }
4436 
4437 /* The following are just place holders.
4438  * Some hosts may provide and environment that the OS is
4439  * not tracking, therefore, these host must provide that
4440  * environment and the current directory to CreateProcess
4441  */
4442 
4443 DllExport void*
win32_get_childenv(void)4444 win32_get_childenv(void)
4445 {
4446     return NULL;
4447 }
4448 
4449 DllExport void
win32_free_childenv(void * d)4450 win32_free_childenv(void* d)
4451 {
4452 }
4453 
4454 DllExport void
win32_clearenv(void)4455 win32_clearenv(void)
4456 {
4457     char *envv = GetEnvironmentStrings();
4458     char *cur = envv;
4459     STRLEN len;
4460     while (*cur) {
4461         char *end = strchr(cur,'=');
4462         if (end && end != cur) {
4463             *end = '\0';
4464             SetEnvironmentVariable(cur, NULL);
4465             *end = '=';
4466             cur = end + strlen(end+1)+2;
4467         }
4468         else if ((len = strlen(cur)))
4469             cur += len+1;
4470     }
4471     FreeEnvironmentStrings(envv);
4472 }
4473 
4474 DllExport char*
win32_get_childdir(void)4475 win32_get_childdir(void)
4476 {
4477     char* ptr;
4478     char szfilename[MAX_PATH+1];
4479 
4480     GetCurrentDirectoryA(MAX_PATH+1, szfilename);
4481     Newx(ptr, strlen(szfilename)+1, char);
4482     strcpy(ptr, szfilename);
4483     return ptr;
4484 }
4485 
4486 DllExport void
win32_free_childdir(char * d)4487 win32_free_childdir(char* d)
4488 {
4489     Safefree(d);
4490 }
4491 
4492 
4493 /* XXX this needs to be made more compatible with the spawnvp()
4494  * provided by the various RTLs.  In particular, searching for
4495  * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
4496  * This doesn't significantly affect perl itself, because we
4497  * always invoke things using PERL5SHELL if a direct attempt to
4498  * spawn the executable fails.
4499  *
4500  * XXX splitting and rejoining the commandline between do_aspawn()
4501  * and win32_spawnvp() could also be avoided.
4502  */
4503 
4504 DllExport int
win32_spawnvp(int mode,const char * cmdname,const char * const * argv)4505 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
4506 {
4507 #ifdef USE_RTL_SPAWNVP
4508     return _spawnvp(mode, cmdname, (char * const *)argv);
4509 #else
4510     return do_spawnvp_handles(mode, cmdname, argv, NULL);
4511 #endif
4512 }
4513 
4514 static int
do_spawnvp_handles(int mode,const char * cmdname,const char * const * argv,const int * handles)4515 do_spawnvp_handles(int mode, const char *cmdname, const char *const *argv,
4516                 const int *handles) {
4517     dTHXa(NULL);
4518     int ret;
4519     void* env;
4520     char* dir;
4521     child_IO_table tbl;
4522     STARTUPINFO StartupInfo;
4523     PROCESS_INFORMATION ProcessInformation;
4524     DWORD create = 0;
4525     char *cmd;
4526     char *fullcmd = NULL;
4527     char *cname = (char *)cmdname;
4528     STRLEN clen = 0;
4529 
4530     if (cname) {
4531         clen = strlen(cname);
4532         /* if command name contains dquotes, must remove them */
4533         if (strchr(cname, '"')) {
4534             cmd = cname;
4535             Newx(cname,clen+1,char);
4536             clen = 0;
4537             while (*cmd) {
4538                 if (*cmd != '"') {
4539                     cname[clen] = *cmd;
4540                     ++clen;
4541                 }
4542                 ++cmd;
4543             }
4544             cname[clen] = '\0';
4545         }
4546     }
4547 
4548     cmd = create_command_line(cname, clen, argv);
4549 
4550     aTHXa(PERL_GET_THX);
4551     env = PerlEnv_get_childenv();
4552     dir = PerlEnv_get_childdir();
4553 
4554     switch(mode) {
4555     case P_NOWAIT:	/* asynch + remember result */
4556         if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
4557             errno = EAGAIN;
4558             ret = -1;
4559             goto RETVAL;
4560         }
4561         /* Create a new process group so we can use GenerateConsoleCtrlEvent()
4562          * in win32_kill()
4563          */
4564         create |= CREATE_NEW_PROCESS_GROUP;
4565         /* FALL THROUGH */
4566 
4567     case P_WAIT:	/* synchronous execution */
4568         break;
4569     default:		/* invalid mode */
4570         errno = EINVAL;
4571         ret = -1;
4572         goto RETVAL;
4573     }
4574 
4575     memset(&StartupInfo,0,sizeof(StartupInfo));
4576     StartupInfo.cb = sizeof(StartupInfo);
4577     memset(&tbl,0,sizeof(tbl));
4578     PerlEnv_get_child_IO(&tbl);
4579     StartupInfo.dwFlags		= tbl.dwFlags;
4580     StartupInfo.dwX		= tbl.dwX;
4581     StartupInfo.dwY		= tbl.dwY;
4582     StartupInfo.dwXSize		= tbl.dwXSize;
4583     StartupInfo.dwYSize		= tbl.dwYSize;
4584     StartupInfo.dwXCountChars	= tbl.dwXCountChars;
4585     StartupInfo.dwYCountChars	= tbl.dwYCountChars;
4586     StartupInfo.dwFillAttribute	= tbl.dwFillAttribute;
4587     StartupInfo.wShowWindow	= tbl.wShowWindow;
4588     StartupInfo.hStdInput	= handles && handles[0] != -1 ?
4589             (HANDLE)_get_osfhandle(handles[0]) : tbl.childStdIn;
4590     StartupInfo.hStdOutput	= handles && handles[1] != -1 ?
4591             (HANDLE)_get_osfhandle(handles[1]) : tbl.childStdOut;
4592     StartupInfo.hStdError	= handles && handles[2] != -1 ?
4593             (HANDLE)_get_osfhandle(handles[2]) : tbl.childStdErr;
4594     if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
4595         StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
4596         StartupInfo.hStdError == INVALID_HANDLE_VALUE)
4597     {
4598         create |= CREATE_NEW_CONSOLE;
4599     }
4600     else {
4601         StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4602     }
4603     if (w32_use_showwindow) {
4604         StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
4605         StartupInfo.wShowWindow = w32_showwindow;
4606     }
4607 
4608     DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
4609                           cname,cmd));
4610 RETRY:
4611     if (!CreateProcess(cname,		/* search PATH to find executable */
4612                        cmd,		/* executable, and its arguments */
4613                        NULL,		/* process attributes */
4614                        NULL,		/* thread attributes */
4615                        TRUE,		/* inherit handles */
4616                        create,		/* creation flags */
4617                        (LPVOID)env,	/* inherit environment */
4618                        dir,		/* inherit cwd */
4619                        &StartupInfo,
4620                        &ProcessInformation))
4621     {
4622         /* initial NULL argument to CreateProcess() does a PATH
4623          * search, but it always first looks in the directory
4624          * where the current process was started, which behavior
4625          * is undesirable for backward compatibility.  So we
4626          * jump through our own hoops by picking out the path
4627          * we really want it to use. */
4628         if (!fullcmd) {
4629             fullcmd = qualified_path(cname, FALSE);
4630             if (fullcmd) {
4631                 if (cname != cmdname)
4632                     Safefree(cname);
4633                 cname = fullcmd;
4634                 DEBUG_p(PerlIO_printf(Perl_debug_log,
4635                                       "Retrying [%s] with same args\n",
4636                                       cname));
4637                 goto RETRY;
4638             }
4639         }
4640         errno = ENOENT;
4641         ret = -1;
4642         goto RETVAL;
4643     }
4644 
4645     if (mode == P_NOWAIT) {
4646         /* asynchronous spawn -- store handle, return PID */
4647         ret = (int)ProcessInformation.dwProcessId;
4648 
4649         w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
4650         w32_child_pids[w32_num_children] = (DWORD)ret;
4651         ++w32_num_children;
4652     }
4653     else {
4654         DWORD status;
4655         win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
4656         /* FIXME: if msgwait returned due to message perhaps forward the
4657            "signal" to the process
4658          */
4659         GetExitCodeProcess(ProcessInformation.hProcess, &status);
4660         ret = (int)status;
4661         CloseHandle(ProcessInformation.hProcess);
4662     }
4663 
4664     CloseHandle(ProcessInformation.hThread);
4665 
4666 RETVAL:
4667     PerlEnv_free_childenv(env);
4668     PerlEnv_free_childdir(dir);
4669     Safefree(cmd);
4670     if (cname != cmdname)
4671         Safefree(cname);
4672     return ret;
4673 }
4674 
4675 DllExport int
win32_execv(const char * cmdname,const char * const * argv)4676 win32_execv(const char *cmdname, const char *const *argv)
4677 {
4678 #ifdef USE_ITHREADS
4679     dTHX;
4680     /* if this is a pseudo-forked child, we just want to spawn
4681      * the new program, and return */
4682     if (w32_pseudo_id)
4683         return _spawnv(P_WAIT, cmdname, argv);
4684 #endif
4685     return _execv(cmdname, argv);
4686 }
4687 
4688 DllExport int
win32_execvp(const char * cmdname,const char * const * argv)4689 win32_execvp(const char *cmdname, const char *const *argv)
4690 {
4691 #ifdef USE_ITHREADS
4692     dTHX;
4693     /* if this is a pseudo-forked child, we just want to spawn
4694      * the new program, and return */
4695     if (w32_pseudo_id) {
4696         int status = win32_spawnvp(P_WAIT, cmdname, (const char *const *)argv);
4697         if (status != -1) {
4698             my_exit(status);
4699             return 0;
4700         }
4701         else
4702             return status;
4703     }
4704 #endif
4705     return _execvp(cmdname, argv);
4706 }
4707 
4708 DllExport void
win32_perror(const char * str)4709 win32_perror(const char *str)
4710 {
4711     perror(str);
4712 }
4713 
4714 DllExport void
win32_setbuf(FILE * pf,char * buf)4715 win32_setbuf(FILE *pf, char *buf)
4716 {
4717     setbuf(pf, buf);
4718 }
4719 
4720 DllExport int
win32_setvbuf(FILE * pf,char * buf,int type,size_t size)4721 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
4722 {
4723     return setvbuf(pf, buf, type, size);
4724 }
4725 
4726 DllExport int
win32_flushall(void)4727 win32_flushall(void)
4728 {
4729     return flushall();
4730 }
4731 
4732 DllExport int
win32_fcloseall(void)4733 win32_fcloseall(void)
4734 {
4735     return fcloseall();
4736 }
4737 
4738 DllExport char*
win32_fgets(char * s,int n,FILE * pf)4739 win32_fgets(char *s, int n, FILE *pf)
4740 {
4741     return fgets(s, n, pf);
4742 }
4743 
4744 DllExport char*
win32_gets(char * s)4745 win32_gets(char *s)
4746 {
4747     return gets(s);
4748 }
4749 
4750 DllExport int
win32_fgetc(FILE * pf)4751 win32_fgetc(FILE *pf)
4752 {
4753     return fgetc(pf);
4754 }
4755 
4756 DllExport int
win32_putc(int c,FILE * pf)4757 win32_putc(int c, FILE *pf)
4758 {
4759     return putc(c,pf);
4760 }
4761 
4762 DllExport int
win32_puts(const char * s)4763 win32_puts(const char *s)
4764 {
4765     return puts(s);
4766 }
4767 
4768 DllExport int
win32_getchar(void)4769 win32_getchar(void)
4770 {
4771     return getchar();
4772 }
4773 
4774 DllExport int
win32_putchar(int c)4775 win32_putchar(int c)
4776 {
4777     return putchar(c);
4778 }
4779 
4780 #ifdef MYMALLOC
4781 
4782 #ifndef USE_PERL_SBRK
4783 
4784 static char *committed = NULL;		/* XXX threadead */
4785 static char *base      = NULL;		/* XXX threadead */
4786 static char *reserved  = NULL;		/* XXX threadead */
4787 static char *brk       = NULL;		/* XXX threadead */
4788 static DWORD pagesize  = 0;		/* XXX threadead */
4789 
4790 void *
sbrk(ptrdiff_t need)4791 sbrk(ptrdiff_t need)
4792 {
4793  void *result;
4794  if (!pagesize)
4795   {SYSTEM_INFO info;
4796    GetSystemInfo(&info);
4797    /* Pretend page size is larger so we don't perpetually
4798     * call the OS to commit just one page ...
4799     */
4800    pagesize = info.dwPageSize << 3;
4801   }
4802  if (brk+need >= reserved)
4803   {
4804    DWORD size = brk+need-reserved;
4805    char *addr;
4806    char *prev_committed = NULL;
4807    if (committed && reserved && committed < reserved)
4808     {
4809      /* Commit last of previous chunk cannot span allocations */
4810      addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
4811      if (addr)
4812       {
4813       /* Remember where we committed from in case we want to decommit later */
4814       prev_committed = committed;
4815       committed = reserved;
4816       }
4817     }
4818    /* Reserve some (more) space
4819     * Contiguous blocks give us greater efficiency, so reserve big blocks -
4820     * this is only address space not memory...
4821     * Note this is a little sneaky, 1st call passes NULL as reserved
4822     * so lets system choose where we start, subsequent calls pass
4823     * the old end address so ask for a contiguous block
4824     */
4825 sbrk_reserve:
4826    if (size < 64*1024*1024)
4827     size = 64*1024*1024;
4828    size = ((size + pagesize - 1) / pagesize) * pagesize;
4829    addr  = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
4830    if (addr)
4831     {
4832      reserved = addr+size;
4833      if (!base)
4834       base = addr;
4835      if (!committed)
4836       committed = base;
4837      if (!brk)
4838       brk = committed;
4839     }
4840    else if (reserved)
4841     {
4842       /* The existing block could not be extended far enough, so decommit
4843        * anything that was just committed above and start anew */
4844       if (prev_committed)
4845        {
4846        if (!VirtualFree(prev_committed,reserved-prev_committed,MEM_DECOMMIT))
4847         return (void *) -1;
4848        }
4849       reserved = base = committed = brk = NULL;
4850       size = need;
4851       goto sbrk_reserve;
4852     }
4853    else
4854     {
4855      return (void *) -1;
4856     }
4857   }
4858  result = brk;
4859  brk += need;
4860  if (brk > committed)
4861   {
4862    DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
4863    char *addr;
4864    if (committed+size > reserved)
4865     size = reserved-committed;
4866    addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
4867    if (addr)
4868     committed += size;
4869    else
4870     return (void *) -1;
4871   }
4872  return result;
4873 }
4874 
4875 #endif
4876 #endif
4877 
4878 DllExport void*
win32_malloc(size_t size)4879 win32_malloc(size_t size)
4880 {
4881     return malloc(size);
4882 }
4883 
4884 DllExport void*
win32_calloc(size_t numitems,size_t size)4885 win32_calloc(size_t numitems, size_t size)
4886 {
4887     return calloc(numitems,size);
4888 }
4889 
4890 DllExport void*
win32_realloc(void * block,size_t size)4891 win32_realloc(void *block, size_t size)
4892 {
4893     return realloc(block,size);
4894 }
4895 
4896 DllExport void
win32_free(void * block)4897 win32_free(void *block)
4898 {
4899     free(block);
4900 }
4901 
4902 
4903 DllExport int
win32_open_osfhandle(intptr_t handle,int flags)4904 win32_open_osfhandle(intptr_t handle, int flags)
4905 {
4906     return _open_osfhandle(handle, flags);
4907 }
4908 
4909 DllExport intptr_t
win32_get_osfhandle(int fd)4910 win32_get_osfhandle(int fd)
4911 {
4912     return (intptr_t)_get_osfhandle(fd);
4913 }
4914 
4915 DllExport FILE *
win32_fdupopen(FILE * pf)4916 win32_fdupopen(FILE *pf)
4917 {
4918     FILE* pfdup;
4919     fpos_t pos;
4920     char mode[3];
4921     int fileno = win32_dup(win32_fileno(pf));
4922 
4923     /* open the file in the same mode */
4924     if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RD) {
4925         mode[0] = 'r';
4926         mode[1] = 0;
4927     }
4928     else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_WR) {
4929         mode[0] = 'a';
4930         mode[1] = 0;
4931     }
4932     else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RW) {
4933         mode[0] = 'r';
4934         mode[1] = '+';
4935         mode[2] = 0;
4936     }
4937 
4938     /* it appears that the binmode is attached to the
4939      * file descriptor so binmode files will be handled
4940      * correctly
4941      */
4942     pfdup = win32_fdopen(fileno, mode);
4943 
4944     /* move the file pointer to the same position */
4945     if (!fgetpos(pf, &pos)) {
4946         fsetpos(pfdup, &pos);
4947     }
4948     return pfdup;
4949 }
4950 
4951 DllExport void*
win32_dynaload(const char * filename)4952 win32_dynaload(const char* filename)
4953 {
4954     dTHXa(NULL);
4955     char buf[MAX_PATH+1];
4956     const char *first;
4957 
4958     /* LoadLibrary() doesn't recognize forward slashes correctly,
4959      * so turn 'em back. */
4960     first = strchr(filename, '/');
4961     if (first) {
4962         STRLEN len = strlen(filename);
4963         if (len <= MAX_PATH) {
4964             strcpy(buf, filename);
4965             filename = &buf[first - filename];
4966             while (*filename) {
4967                 if (*filename == '/')
4968                     *(char*)filename = '\\';
4969                 ++filename;
4970             }
4971             filename = buf;
4972         }
4973     }
4974     aTHXa(PERL_GET_THX);
4975     return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
4976 }
4977 
XS(w32_SetChildShowWindow)4978 XS(w32_SetChildShowWindow)
4979 {
4980     dXSARGS;
4981     BOOL use_showwindow = w32_use_showwindow;
4982     /* use "unsigned short" because Perl has redefined "WORD" */
4983     unsigned short showwindow = w32_showwindow;
4984 
4985     if (items > 1)
4986         croak_xs_usage(cv, "[showwindow]");
4987 
4988     if (items == 0 || !SvOK(ST(0)))
4989         w32_use_showwindow = FALSE;
4990     else {
4991         w32_use_showwindow = TRUE;
4992         w32_showwindow = (unsigned short)SvIV(ST(0));
4993     }
4994 
4995     EXTEND(SP, 1);
4996     if (use_showwindow)
4997         ST(0) = sv_2mortal(newSViv(showwindow));
4998     else
4999         ST(0) = &PL_sv_undef;
5000     XSRETURN(1);
5001 }
5002 
5003 
5004 #ifdef PERL_IS_MINIPERL
5005 /* shelling out is much slower, full perl uses Win32.pm */
XS(w32_GetCwd)5006 XS(w32_GetCwd)
5007 {
5008     dXSARGS;
5009     /* Make the host for current directory */
5010     char* ptr = PerlEnv_get_childdir();
5011     /*
5012      * If ptr != Nullch
5013      *   then it worked, set PV valid,
5014      *   else return 'undef'
5015      */
5016     if (ptr) {
5017         SV *sv = sv_newmortal();
5018         sv_setpv(sv, ptr);
5019         PerlEnv_free_childdir(ptr);
5020 
5021 #ifndef INCOMPLETE_TAINTS
5022         SvTAINTED_on(sv);
5023 #endif
5024 
5025         ST(0) = sv;
5026         XSRETURN(1);
5027     }
5028     XSRETURN_UNDEF;
5029 }
5030 #endif
5031 
5032 void
Perl_init_os_extras(void)5033 Perl_init_os_extras(void)
5034 {
5035     dTHXa(NULL);
5036     const char *file = __FILE__;
5037 
5038     /* Initialize Win32CORE if it has been statically linked. */
5039 #ifndef PERL_IS_MINIPERL
5040     void (*pfn_init)(pTHX);
5041     HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
5042                                ? GetModuleHandle(NULL)
5043                                : w32_perldll_handle);
5044     pfn_init = (void (*)(pTHX))GetProcAddress(module, "init_Win32CORE");
5045     aTHXa(PERL_GET_THX);
5046     if (pfn_init)
5047         pfn_init(aTHX);
5048 #else
5049     aTHXa(PERL_GET_THX);
5050 #endif
5051 
5052     newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
5053 #ifdef PERL_IS_MINIPERL
5054     newXS("Win32::GetCwd", w32_GetCwd, file);
5055 #endif
5056 }
5057 
5058 void *
win32_signal_context(void)5059 win32_signal_context(void)
5060 {
5061     dTHX;
5062 #ifdef MULTIPLICITY
5063     if (!my_perl) {
5064         my_perl = PL_curinterp;
5065         PERL_SET_THX(my_perl);
5066     }
5067     return my_perl;
5068 #else
5069     return PL_curinterp;
5070 #endif
5071 }
5072 
5073 
5074 BOOL WINAPI
win32_ctrlhandler(DWORD dwCtrlType)5075 win32_ctrlhandler(DWORD dwCtrlType)
5076 {
5077 #ifdef MULTIPLICITY
5078     dTHXa(PERL_GET_SIG_CONTEXT);
5079 
5080     if (!my_perl)
5081         return FALSE;
5082 #endif
5083 
5084     switch(dwCtrlType) {
5085     case CTRL_CLOSE_EVENT:
5086      /*  A signal that the system sends to all processes attached to a console when
5087          the user closes the console (either by choosing the Close command from the
5088          console window's System menu, or by choosing the End Task command from the
5089          Task List
5090       */
5091         if (do_raise(aTHX_ 1))	      /* SIGHUP */
5092             sig_terminate(aTHX_ 1);
5093         return TRUE;
5094 
5095     case CTRL_C_EVENT:
5096         /*  A CTRL+c signal was received */
5097         if (do_raise(aTHX_ SIGINT))
5098             sig_terminate(aTHX_ SIGINT);
5099         return TRUE;
5100 
5101     case CTRL_BREAK_EVENT:
5102         /*  A CTRL+BREAK signal was received */
5103         if (do_raise(aTHX_ SIGBREAK))
5104             sig_terminate(aTHX_ SIGBREAK);
5105         return TRUE;
5106 
5107     case CTRL_LOGOFF_EVENT:
5108       /*  A signal that the system sends to all console processes when a user is logging
5109           off. This signal does not indicate which user is logging off, so no
5110           assumptions can be made.
5111        */
5112         break;
5113     case CTRL_SHUTDOWN_EVENT:
5114       /*  A signal that the system sends to all console processes when the system is
5115           shutting down.
5116        */
5117         if (do_raise(aTHX_ SIGTERM))
5118             sig_terminate(aTHX_ SIGTERM);
5119         return TRUE;
5120     default:
5121         break;
5122     }
5123     return FALSE;
5124 }
5125 
5126 
5127 #ifdef SET_INVALID_PARAMETER_HANDLER
5128 #  include <crtdbg.h>
5129 #endif
5130 
5131 static void
ansify_path(void)5132 ansify_path(void)
5133 {
5134     size_t len;
5135     char *ansi_path;
5136     WCHAR *wide_path;
5137     WCHAR *wide_dir;
5138 
5139     /* fetch Unicode version of PATH */
5140     len = 2000;
5141     wide_path = (WCHAR*)win32_malloc(len*sizeof(WCHAR));
5142     while (wide_path) {
5143         size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len);
5144         if (newlen == 0) {
5145             win32_free(wide_path);
5146             return;
5147         }
5148         if (newlen < len)
5149             break;
5150         len = newlen;
5151         wide_path = (WCHAR*)win32_realloc(wide_path, len*sizeof(WCHAR));
5152     }
5153     if (!wide_path)
5154         return;
5155 
5156     /* convert to ANSI pathnames */
5157     wide_dir = wide_path;
5158     ansi_path = NULL;
5159     while (wide_dir) {
5160         WCHAR *sep = wcschr(wide_dir, ';');
5161         char *ansi_dir;
5162         size_t ansi_len;
5163         size_t wide_len;
5164 
5165         if (sep)
5166             *sep++ = '\0';
5167 
5168         /* remove quotes around pathname */
5169         if (*wide_dir == '"')
5170             ++wide_dir;
5171         wide_len = wcslen(wide_dir);
5172         if (wide_len && wide_dir[wide_len-1] == '"')
5173             wide_dir[wide_len-1] = '\0';
5174 
5175         /* append ansi_dir to ansi_path */
5176         ansi_dir = win32_ansipath(wide_dir);
5177         ansi_len = strlen(ansi_dir);
5178         if (ansi_path) {
5179             size_t newlen = len + 1 + ansi_len;
5180             ansi_path = (char*)win32_realloc(ansi_path, newlen+1);
5181             if (!ansi_path)
5182                 break;
5183             ansi_path[len] = ';';
5184             memcpy(ansi_path+len+1, ansi_dir, ansi_len+1);
5185             len = newlen;
5186         }
5187         else {
5188             len = ansi_len;
5189             ansi_path = (char*)win32_malloc(5+len+1);
5190             if (!ansi_path)
5191                 break;
5192             memcpy(ansi_path, "PATH=", 5);
5193             memcpy(ansi_path+5, ansi_dir, len+1);
5194             len += 5;
5195         }
5196         win32_free(ansi_dir);
5197         wide_dir = sep;
5198     }
5199 
5200     if (ansi_path) {
5201         /* Update C RTL environ array.  This will only have full effect if
5202          * perl_parse() is later called with `environ` as the `env` argument.
5203          * Otherwise S_init_postdump_symbols() will overwrite PATH again.
5204          *
5205          * We do have to ansify() the PATH before Perl has been fully
5206          * initialized because S_find_script() uses the PATH when perl
5207          * is being invoked with the -S option.  This happens before %ENV
5208          * is initialized in S_init_postdump_symbols().
5209          *
5210          * XXX Is this a bug? Should S_find_script() use the environment
5211          * XXX passed in the `env` arg to parse_perl()?
5212          */
5213         putenv(ansi_path);
5214         /* Keep system environment in sync because S_init_postdump_symbols()
5215          * will not call mg_set() if it initializes %ENV from `environ`.
5216          */
5217         SetEnvironmentVariableA("PATH", ansi_path+5);
5218         win32_free(ansi_path);
5219     }
5220     win32_free(wide_path);
5221 }
5222 
5223 /* This hooks a function that is imported by the specified module. The hook is
5224  * local to that module. */
5225 static bool
win32_hook_imported_function_in_module(HMODULE module,LPCSTR fun_name,FARPROC hook_ptr)5226 win32_hook_imported_function_in_module(
5227     HMODULE module, LPCSTR fun_name, FARPROC hook_ptr
5228 )
5229 {
5230     ULONG_PTR image_base = (ULONG_PTR)module;
5231     PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)image_base;
5232     PIMAGE_NT_HEADERS nt_headers
5233         = (PIMAGE_NT_HEADERS)(image_base + dos_header->e_lfanew);
5234     PIMAGE_OPTIONAL_HEADER opt_header = &nt_headers->OptionalHeader;
5235 
5236     PIMAGE_DATA_DIRECTORY data_dir = opt_header->DataDirectory;
5237     DWORD data_dir_len = opt_header->NumberOfRvaAndSizes;
5238 
5239     BOOL is_idt_present = data_dir_len > IMAGE_DIRECTORY_ENTRY_IMPORT
5240         && data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0;
5241 
5242     if (!is_idt_present)
5243         return FALSE;
5244 
5245     BOOL found = FALSE;
5246 
5247     /* Import Directory Table */
5248     PIMAGE_IMPORT_DESCRIPTOR idt = (PIMAGE_IMPORT_DESCRIPTOR)(
5249         image_base + data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
5250     );
5251 
5252     for (; idt->Name != 0; ++idt) {
5253         /* Import Lookup Table */
5254         PIMAGE_THUNK_DATA ilt
5255             = (PIMAGE_THUNK_DATA)(image_base + idt->OriginalFirstThunk);
5256         /* Import Address Table */
5257         PIMAGE_THUNK_DATA iat
5258             = (PIMAGE_THUNK_DATA)(image_base + idt->FirstThunk);
5259 
5260         ULONG_PTR address_of_data;
5261         for (; address_of_data = ilt->u1.AddressOfData; ++ilt, ++iat) {
5262             /* Ordinal imports are quite rare, so skipping them will most likely
5263              * not cause any problems. */
5264             BOOL is_ordinal
5265                 = address_of_data >> ((sizeof(address_of_data) * 8) - 1);
5266 
5267             if (is_ordinal)
5268                 continue;
5269 
5270             LPCSTR name = (
5271                 (PIMAGE_IMPORT_BY_NAME)(image_base + address_of_data)
5272             )->Name;
5273 
5274             if (strEQ(name, fun_name)) {
5275                 DWORD old_protect = 0;
5276                 BOOL succ = VirtualProtect(
5277                     &iat->u1.Function, sizeof(iat->u1.Function), PAGE_READWRITE,
5278                     &old_protect
5279                 );
5280                 if (!succ)
5281                     return FALSE;
5282 
5283                 iat->u1.Function = (ULONG_PTR)hook_ptr;
5284                 found = TRUE;
5285 
5286                 VirtualProtect(
5287                     &iat->u1.Function, sizeof(iat->u1.Function), old_protect,
5288                     &old_protect
5289                 );
5290                 break;
5291             }
5292         }
5293     }
5294 
5295     return found;
5296 }
5297 
5298 typedef NTSTATUS (NTAPI *pNtQueryInformationFile_t)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, ULONG);
5299 pNtQueryInformationFile_t pNtQueryInformationFile = NULL;
5300 
5301 typedef BOOL (WINAPI *pCloseHandle)(HANDLE h);
5302 static pCloseHandle CloseHandle_orig;
5303 
5304 /* CloseHandle() that supports sockets. CRT uses mutexes during file operations,
5305  * so the lack of thread safety in this function isn't a problem. */
5306 static BOOL WINAPI
my_CloseHandle(HANDLE h)5307 my_CloseHandle(HANDLE h)
5308 {
5309     /* In theory, passing a non-socket handle to closesocket() is fine. It
5310      * should return a WSAENOTSOCK error, which is easy to recover from.
5311      * However, we should avoid doing that because it's not that simple in
5312      * practice. For instance, it can deadlock on a handle to a stuck pipe (see:
5313      * https://github.com/Perl/perl5/issues/19963).
5314      *
5315      * There's no foolproof way to tell if a handle is a socket (mostly because
5316      * of the non-IFS sockets), but in some cases we can tell if a handle
5317      * is definitely *not* a socket.
5318      */
5319 
5320     /* GetFileType() always returns FILE_TYPE_PIPE for sockets. */
5321     BOOL maybe_socket = (GetFileType(h) == FILE_TYPE_PIPE);
5322 
5323     if (maybe_socket && pNtQueryInformationFile) {
5324         IO_STATUS_BLOCK isb;
5325         struct {
5326             ULONG name_len;
5327             WCHAR name[100];
5328         } volume = {0};
5329 
5330         /* There are many ways to tell a named pipe from a socket, but almost
5331          * all of them can deadlock on a handle to a stuck pipe (like in the
5332          * bug ticket mentioned above). According to my tests,
5333          * FileVolumeNameInfomation is the only relevant function that doesn't
5334          * suffer from this problem.
5335          *
5336          * It's undocumented and it requires Windows 10, so on older systems
5337          * we always pass pipes to closesocket().
5338          */
5339         NTSTATUS s = pNtQueryInformationFile(
5340             h, &isb, &volume, sizeof(volume), 58 /* FileVolumeNameInformation */
5341         );
5342         if (NT_SUCCESS(s)) {
5343             maybe_socket = (_wcsnicmp(
5344                 volume.name, L"\\Device\\NamedPipe", C_ARRAY_LENGTH(volume.name)
5345             ) != 0);
5346         }
5347     }
5348 
5349     if (maybe_socket)
5350         if (closesocket((SOCKET)h) == 0)
5351             return TRUE;
5352         else if (WSAGetLastError() != WSAENOTSOCK)
5353             return FALSE;
5354 
5355     return CloseHandle_orig(h);
5356 }
5357 
5358 /* Hook CloseHandle() inside CRT so its functions like _close() or
5359  * _dup2() can close sockets properly. */
5360 static void
win32_hook_closehandle_in_crt()5361 win32_hook_closehandle_in_crt()
5362 {
5363     /* Get the handle to the CRT module basing on the address of _close()
5364      * function. */
5365     HMODULE crt_handle;
5366     BOOL succ = GetModuleHandleExA(
5367         GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
5368         | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)_close,
5369         &crt_handle
5370     );
5371     if (!succ)
5372         return;
5373 
5374     CloseHandle_orig = (pCloseHandle)GetProcAddress(
5375         GetModuleHandleA("kernel32.dll"), "CloseHandle"
5376     );
5377     if (!CloseHandle_orig)
5378         return;
5379 
5380     win32_hook_imported_function_in_module(
5381         crt_handle, "CloseHandle", (FARPROC)my_CloseHandle
5382     );
5383 
5384     pNtQueryInformationFile = (pNtQueryInformationFile_t)GetProcAddress(
5385         GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile"
5386     );
5387 }
5388 
5389 /* Remove the hook installed by win32_hook_closehandle_crt(). This is needed in
5390  * case the Perl DLL is unloaded, which would cause the hook become invalid.
5391  * This can happen in embedded Perls, for example in mod_perl. */
5392 static void
win32_unhook_closehandle_in_crt()5393 win32_unhook_closehandle_in_crt()
5394 {
5395     if (!CloseHandle_orig)
5396         return;
5397 
5398     HMODULE crt_handle;
5399     BOOL succ = GetModuleHandleExA(
5400         GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
5401         | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)_close,
5402         &crt_handle
5403     );
5404     if (!succ)
5405         return;
5406 
5407     win32_hook_imported_function_in_module(
5408         crt_handle, "CloseHandle", (FARPROC)CloseHandle_orig
5409     );
5410 
5411     CloseHandle_orig = NULL;
5412 }
5413 
5414 void
Perl_win32_init(int * argcp,char *** argvp)5415 Perl_win32_init(int *argcp, char ***argvp)
5416 {
5417 #ifdef SET_INVALID_PARAMETER_HANDLER
5418     _invalid_parameter_handler oldHandler, newHandler;
5419     newHandler = my_invalid_parameter_handler;
5420     oldHandler = _set_invalid_parameter_handler(newHandler);
5421     _CrtSetReportMode(_CRT_ASSERT, 0);
5422 #endif
5423     /* Disable floating point errors, Perl will trap the ones we
5424      * care about.  VC++ RTL defaults to switching these off
5425      * already, but some RTLs don't.  Since we don't
5426      * want to be at the vendor's whim on the default, we set
5427      * it explicitly here.
5428      */
5429 #if !defined(__GNUC__)
5430     _control87(MCW_EM, MCW_EM);
5431 #endif
5432     MALLOC_INIT;
5433 
5434     /* When the manifest resource requests Common-Controls v6 then
5435      * user32.dll no longer registers all the Windows classes used for
5436      * standard controls but leaves some of them to be registered by
5437      * comctl32.dll.  InitCommonControls() doesn't do anything but calling
5438      * it makes sure comctl32.dll gets loaded into the process and registers
5439      * the standard control classes.  Without this even normal Windows APIs
5440      * like MessageBox() can fail under some versions of Windows XP.
5441      */
5442     InitCommonControls();
5443 
5444     WSADATA wsadata;
5445     WSAStartup(MAKEWORD(2, 2), &wsadata);
5446 
5447     g_osver.dwOSVersionInfoSize = sizeof(g_osver);
5448     GetVersionEx(&g_osver);
5449 
5450     win32_hook_closehandle_in_crt();
5451 
5452     ansify_path();
5453 
5454 #ifndef WIN32_NO_REGISTRY
5455     {
5456         LONG retval;
5457         retval = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Perl", 0, KEY_READ, &HKCU_Perl_hnd);
5458         if (retval != ERROR_SUCCESS) {
5459             HKCU_Perl_hnd = NULL;
5460         }
5461         retval = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", 0, KEY_READ, &HKLM_Perl_hnd);
5462         if (retval != ERROR_SUCCESS) {
5463             HKLM_Perl_hnd = NULL;
5464         }
5465     }
5466 #endif
5467 
5468     {
5469         FILETIME ft;
5470         if (!SystemTimeToFileTime(&time_t_epoch_base_systemtime,
5471                                   &ft)) {
5472             fprintf(stderr, "panic: cannot convert base system time to filetime\n"); /* no interp */
5473             exit(1);
5474         }
5475         time_t_epoch_base_filetime.LowPart  = ft.dwLowDateTime;
5476         time_t_epoch_base_filetime.HighPart = ft.dwHighDateTime;
5477     }
5478 
5479     MUTEX_INIT(&win32_read_console_mutex);
5480 }
5481 
5482 void
Perl_win32_term(void)5483 Perl_win32_term(void)
5484 {
5485     HINTS_REFCNT_TERM;
5486     OP_REFCNT_TERM;
5487     PERLIO_TERM;
5488     MALLOC_TERM;
5489     LOCALE_TERM;
5490     ENV_TERM;
5491 #ifndef WIN32_NO_REGISTRY
5492     /* handles might be NULL, RegCloseKey then returns ERROR_INVALID_HANDLE
5493        but no point of checking and we can't die() at this point */
5494     RegCloseKey(HKLM_Perl_hnd);
5495     RegCloseKey(HKCU_Perl_hnd);
5496     /* the handles are in an undefined state until the next PERL_SYS_INIT3 */
5497 #endif
5498     win32_unhook_closehandle_in_crt();
5499 }
5500 
5501 void
win32_get_child_IO(child_IO_table * ptbl)5502 win32_get_child_IO(child_IO_table* ptbl)
5503 {
5504     ptbl->childStdIn	= GetStdHandle(STD_INPUT_HANDLE);
5505     ptbl->childStdOut	= GetStdHandle(STD_OUTPUT_HANDLE);
5506     ptbl->childStdErr	= GetStdHandle(STD_ERROR_HANDLE);
5507 }
5508 
5509 Sighandler_t
win32_signal(int sig,Sighandler_t subcode)5510 win32_signal(int sig, Sighandler_t subcode)
5511 {
5512     dTHXa(NULL);
5513     if (sig < SIG_SIZE) {
5514         int save_errno = errno;
5515         Sighandler_t result;
5516 #ifdef SET_INVALID_PARAMETER_HANDLER
5517         /* Silence our invalid parameter handler since we expect to make some
5518          * calls with invalid signal numbers giving a SIG_ERR result. */
5519         BOOL oldvalue = set_silent_invalid_parameter_handler(TRUE);
5520 #endif
5521         result = signal(sig, subcode);
5522 #ifdef SET_INVALID_PARAMETER_HANDLER
5523         set_silent_invalid_parameter_handler(oldvalue);
5524 #endif
5525         aTHXa(PERL_GET_THX);
5526         if (result == SIG_ERR) {
5527             result = w32_sighandler[sig];
5528             errno = save_errno;
5529         }
5530         w32_sighandler[sig] = subcode;
5531         return result;
5532     }
5533     else {
5534         errno = EINVAL;
5535         return SIG_ERR;
5536     }
5537 }
5538 
5539 /* The PerlMessageWindowClass's WindowProc */
5540 LRESULT CALLBACK
win32_message_window_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)5541 win32_message_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5542 {
5543     return win32_process_message(hwnd, msg, wParam, lParam) ?
5544         0 : DefWindowProc(hwnd, msg, wParam, lParam);
5545 }
5546 
5547 /* The real message handler. Can be called with
5548  * hwnd == NULL to process our thread messages. Returns TRUE for any messages
5549  * that it processes */
5550 static LRESULT
win32_process_message(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)5551 win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5552 {
5553     /* BEWARE. The context retrieved using dTHX; is the context of the
5554      * 'parent' thread during the CreateWindow() phase - i.e. for all messages
5555      * up to and including WM_CREATE.  If it ever happens that you need the
5556      * 'child' context before this, then it needs to be passed into
5557      * win32_create_message_window(), and passed to the WM_NCCREATE handler
5558      * from the lparam of CreateWindow().  It could then be stored/retrieved
5559      * using [GS]etWindowLongPtr(... GWLP_USERDATA ...), possibly eliminating
5560      * the dTHX calls here. */
5561     /* XXX For now it is assumed that the overhead of the dTHX; for what
5562      * are relativley infrequent code-paths, is better than the added
5563      * complexity of getting the correct context passed into
5564      * win32_create_message_window() */
5565     dTHX;
5566 
5567     switch(msg) {
5568 
5569 #ifdef USE_ITHREADS
5570         case WM_USER_MESSAGE: {
5571             long child = find_pseudo_pid(aTHX_ (int)wParam);
5572             if (child >= 0) {
5573                 w32_pseudo_child_message_hwnds[child] = (HWND)lParam;
5574                 return 1;
5575             }
5576             break;
5577         }
5578 #endif
5579 
5580         case WM_USER_KILL: {
5581             /* We use WM_USER_KILL to fake kill() with other signals */
5582             int sig = (int)wParam;
5583             if (do_raise(aTHX_ sig))
5584                 sig_terminate(aTHX_ sig);
5585 
5586             return 1;
5587         }
5588 
5589         case WM_TIMER: {
5590             /* alarm() is a one-shot but SetTimer() repeats so kill it */
5591             if (w32_timerid && w32_timerid==(UINT)wParam) {
5592                 KillTimer(w32_message_hwnd, w32_timerid);
5593                 w32_timerid=0;
5594 
5595                 /* Now fake a call to signal handler */
5596                 if (do_raise(aTHX_ 14))
5597                     sig_terminate(aTHX_ 14);
5598 
5599                 return 1;
5600             }
5601             break;
5602         }
5603 
5604         default:
5605             break;
5606 
5607     } /* switch */
5608 
5609     /* Above or other stuff may have set a signal flag, and we may not have
5610      * been called from win32_async_check() (e.g. some other GUI's message
5611      * loop.  BUT DON'T dispatch signals here: If someone has set a SIGALRM
5612      * handler that die's, and the message loop that calls here is wrapped
5613      * in an eval, then you may well end up with orphaned windows - signals
5614      * are dispatched by win32_async_check() */
5615 
5616     return 0;
5617 }
5618 
5619 void
win32_create_message_window_class(void)5620 win32_create_message_window_class(void)
5621 {
5622     /* create the window class for "message only" windows */
5623     WNDCLASS wc;
5624 
5625     Zero(&wc, 1, wc);
5626     wc.lpfnWndProc = win32_message_window_proc;
5627     wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);
5628     wc.lpszClassName = "PerlMessageWindowClass";
5629 
5630     /* second and subsequent calls will fail, but class
5631      * will already be registered */
5632     RegisterClass(&wc);
5633 }
5634 
5635 HWND
win32_create_message_window(void)5636 win32_create_message_window(void)
5637 {
5638     win32_create_message_window_class();
5639     return CreateWindow("PerlMessageWindowClass", "PerlMessageWindow",
5640                         0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
5641 }
5642 
5643 #ifdef HAVE_INTERP_INTERN
5644 
5645 static void
win32_csighandler(int sig)5646 win32_csighandler(int sig)
5647 {
5648 #if 0
5649     dTHXa(PERL_GET_SIG_CONTEXT);
5650     Perl_warn(aTHX_ "Got signal %d",sig);
5651 #endif
5652     /* Does nothing */
5653 }
5654 
5655 #if defined(__MINGW32__) && defined(__cplusplus)
5656 #define CAST_HWND__(x) (HWND__*)(x)
5657 #else
5658 #define CAST_HWND__(x) x
5659 #endif
5660 
5661 void
Perl_sys_intern_init(pTHX)5662 Perl_sys_intern_init(pTHX)
5663 {
5664     int i;
5665 
5666     w32_perlshell_tokens	= NULL;
5667     w32_perlshell_vec		= (char**)NULL;
5668     w32_perlshell_items		= 0;
5669     w32_fdpid			= newAV();
5670     Newx(w32_children, 1, child_tab);
5671     w32_num_children		= 0;
5672 #  ifdef USE_ITHREADS
5673     w32_pseudo_id		= 0;
5674     Newx(w32_pseudo_children, 1, pseudo_child_tab);
5675     w32_num_pseudo_children	= 0;
5676 #  endif
5677     w32_timerid                 = 0;
5678     w32_message_hwnd            = CAST_HWND__(INVALID_HANDLE_VALUE);
5679     w32_poll_count              = 0;
5680     for (i=0; i < SIG_SIZE; i++) {
5681         w32_sighandler[i] = SIG_DFL;
5682     }
5683 #  ifdef MULTIPLICITY
5684     if (my_perl == PL_curinterp) {
5685 #  else
5686     {
5687 #  endif
5688         /* Force C runtime signal stuff to set its console handler */
5689         signal(SIGINT,win32_csighandler);
5690         signal(SIGBREAK,win32_csighandler);
5691 
5692         /* We spawn asynchronous processes with the CREATE_NEW_PROCESS_GROUP
5693          * flag.  This has the side-effect of disabling Ctrl-C events in all
5694          * processes in this group.
5695          * We re-enable Ctrl-C handling by calling SetConsoleCtrlHandler()
5696          * with a NULL handler.
5697          */
5698         SetConsoleCtrlHandler(NULL,FALSE);
5699 
5700         /* Push our handler on top */
5701         SetConsoleCtrlHandler(win32_ctrlhandler,TRUE);
5702     }
5703 }
5704 
5705 void
5706 Perl_sys_intern_clear(pTHX)
5707 {
5708 
5709     Safefree(w32_perlshell_tokens);
5710     Safefree(w32_perlshell_vec);
5711     /* NOTE: w32_fdpid is freed by sv_clean_all() */
5712     Safefree(w32_children);
5713     if (w32_timerid) {
5714         KillTimer(w32_message_hwnd, w32_timerid);
5715         w32_timerid = 0;
5716     }
5717     if (w32_message_hwnd != NULL && w32_message_hwnd != INVALID_HANDLE_VALUE)
5718         DestroyWindow(w32_message_hwnd);
5719 #  ifdef MULTIPLICITY
5720     if (my_perl == PL_curinterp) {
5721 #  else
5722     {
5723 #  endif
5724         SetConsoleCtrlHandler(win32_ctrlhandler,FALSE);
5725     }
5726 #  ifdef USE_ITHREADS
5727     Safefree(w32_pseudo_children);
5728 #  endif
5729 }
5730 
5731 #  ifdef USE_ITHREADS
5732 
5733 void
5734 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
5735 {
5736     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
5737 
5738     dst->perlshell_tokens	= NULL;
5739     dst->perlshell_vec		= (char**)NULL;
5740     dst->perlshell_items	= 0;
5741     dst->fdpid			= newAV();
5742     Newxz(dst->children, 1, child_tab);
5743     dst->pseudo_id		= 0;
5744     Newxz(dst->pseudo_children, 1, pseudo_child_tab);
5745     dst->timerid                = 0;
5746     dst->message_hwnd		= CAST_HWND__(INVALID_HANDLE_VALUE);
5747     dst->poll_count             = 0;
5748     Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
5749 }
5750 #  endif /* USE_ITHREADS */
5751 #endif /* HAVE_INTERP_INTERN */
5752